|
|
|
![]() |
|
|||||||
|
||||||||
Complete software release of BuzzXVIII software, written in LabVIEW. Also includes complete WPIlib LV rewrite and award-winning CulverDrive.
Complete software release of BuzzXVIII software, written in LabVIEW. Also includes complete WPIlib LV rewrite and award-winning CulverDrive.
Files:
buzz18_sw_postcmp.zip
-buzz18.lvproj main project file
-Folders contain entire robot code, including iterative BeeScript, BuzzLib, PalLib (WPIlib rewrite), and many other things
-Dashboard is included in project (we use the 'traditional' LV dashboard).
buzz_lib_sw_2013.zip
-Buzz lib (2013 inclusions)
-Simple Input Diagnostics - provides easy method for diagnosing simple analog sensor faults (fault high/low, out of range high/low, lack of movement or noise)
-IDG Trust Persuader - used with simple input diagnostics to 'persuade' an output to adhere to the limits set by simple input diagnostics.
-Simple Vref Diagnostics - Used with shorting jumper on adc7 (between 5v and analog pin), used to diagnose simple faults of 5v rail and provide ratiometric multiplier for ratiometric sensors.
-2-point simple analog scaling - Useful for 2-point linear interpolation in many cases, originally written to scale analog inputs
-Sharp IR - Scaling blocks for all three Sharp IR sensors sold by Sparkfun. Analog to cm values copied directly from datasheet and interpolated using multipoint linear interpolator (see below)
-2d interpolation - Interpolates a 2d curve from a prelookup and 1d array of Z points. Also variants to round down and round up to nearest Z point.
-3d interpolation - Interpolates a 3d map from two prelookups and a 2d array of Z points.
-Prelookup - Finds the index and fractional index from a x input and 1d array of X breakpoints. All breakpoints must be spaced least->greatest, there is no checking of this. Used for 2d and 3d interpolation, as it is the most computationally intense step a single prelookup can be shared by multiple interpolations who share the same axis breakpoints.
-Accel calculator - Calculates the acceleration of a velocity assuming 10ms iteration time. Really just finds current derivative, but used for acceleration of shooter wheel speed.
-Hysteresis - Threshold with hysteresis.
pal_lib_default.zip and buzz18_pal_lib.pdf:
-'pal_lib' WPIlib replacement for LabVIEW
-Heavily optimized architecture promotes execution efficienty, runtime performance determinism, and good embedded programming practices
-zip contains example 'default' program which maps several joysticks and buttons to motors, solenoids, and relays, and operates the compressor.
-See buzz18_pal_lib.pdf for complete write-up
-Also see buzz18_sw_postcmp for complete robot usage of library
buzz18_culverdrive.pdf and culverdrive_sw_release.zip:
-award-winning 'CulverDrive' drive algorithm design paper, including description of algorithm (Waterford 'Innovation In controls', Championship 'Quality')
-LabVIEW source code, direct from Buzz18 software release, plus required BuzzLib interpolation blocks
-C source code, used in simulation and intended for Vex targets (unfortunately it does not directly run on Vex because RobotC does support the C language very well).
buzz18_sw_postcmp.zip
buzz_lib_sw_2013.zip
buzz18_pal_lib.pdf
pal_lib_default.zip
buzz18_culverdrive.pdf
culverdrive_sw_release.zip
16-05-2013 20:55
apalrd
As promised, a complete release of Buzz18 software.
I am quite busy with school at this time, but I should finish the promised papers within the next week.
For now, the software zip is posted in CD Media.
16-05-2013 23:06
z_beeblebrox
Not having LabVIEW, I can't look at the code. However, a while ago, you mentioned that you used something other than your normal "Cheesy Drive" software for drivetrain control. What did you do?
16-05-2013 23:16
apalrd
Added file buzz_lib_sw_2013.zip (BuzzLib 2013)
New functionality from last year:
-Simple Vref Diagnostics - Used with shorting jumper on adc7 (between 5v and analog pin), used to diagnose simple faults of 5v rail and provide ratiometric multiplier for ratiometric sensors.
-Sharp IR - Scaling blocks for all three Sharp IR sensors sold by Sparkfun. Analog to cm values copied directly from datasheet and interpolated using multipoint linear interpolator (see below)
-2d interpolation - Interpolates a 2d curve from a prelookup and 1d array of Z points. Also variants to round down and round up to nearest Z point.
-3d interpolation - Interpolates a 3d map from two prelookups and a 2d array of Z points.
-Prelookup - Finds the index and fractional index from a x input and 1d array of X breakpoints. All breakpoints must be spaced least->greatest, there is no checking of this. Used for 2d and 3d interpolation, as it is the most computationally intense step a single prelookup can be shared by multiple interpolations who share the same axis breakpoints.
-Accel calculator - Calculates the acceleration of a velocity assuming 10ms iteration time. Really just finds current derivative, but used for acceleration of shooter wheel speed.
-Hysteresis - Threshold with hysteresis.
The 2d and 3d interpolation tables are the most interesting. For an example of their use, see the Sharp IR blocks in BuzzLib, as they do 2d interpolation in the most basic form.
16-05-2013 23:18
apalrd
|
Not having LabVIEW, I can't look at the code. However, a while ago, you mentioned that you used something other than your normal "Cheesy Drive" software for drivetrain control. What did you do?
|
20-05-2013 19:23
apalrd
Added new files:
buzz18_pal_lib.pdf
pal_lib_default.zip (LV example code, pal_lib code, and paper pdf)
A little bit about pal_lib:
-'pal_lib' WPIlib replacement for LabVIEW
-Replaces ENTIRE WPIlib LabVIEW with 30 VIs, contained in zip folder and inserted as a subfolder into your project.
-Heavily optimized architecture promotes execution efficienty, runtime performance determinism, and good embedded programming practices. Increases in execution efficiency are primarily done by reducing code size and removing unnecessary code, which significantly decreases compile and download times as an added benefit.
-FAST execution (all code currently executes at 10ms, for small or heavily optimized programs 5ms is possible). We could have run Buzz18 at 7ms iteration time, but ran at 10ms with ~55% CPU usage to leave overhead for LV probes and debugging.
-Supports basic commonly-used inputs, outputs, and control data. Large efficiency and code space gains are made by removing functionality that is not commonly used.
-Season-tested on Buzz18 (including five competitions and Einstein) at 10ms iteration time without any bugs EVER attributed to the library after initial library validation
-Extremely low CPU usage (initial validation tests provided initial functionality to default tank drive program with 1/2 CPU usage % at over 2x execution speed), library CPU usage does not increase with used functionality as the library is already handling all of the data it is capable of (e.g. adding another motor or encoder does not require any expensive VI calls and CPU usage of scaling function is inlined and negligible).
-zip contains example 'default' program which maps several joysticks, buttons, and limit switches to motors, solenoids, and relays, and operates the compressor. See comments in the code for exact mapping and IO pinouts. Entire example code is 4 VI's in addition to pal_lib
-Fully synchronous architecture forces efficient data-passing design, as all data can be passed via connectors. Use of global-visible data structures in Buzz18 further optimizes data-passing as all data is always available to all subsystems, any subsystem can change data without consequence to anything else, and order of operations is guaranteed (simplifies triggers significantly). Data structure architecture also simplifies dashboard programming, as all of the data structures are simply sent to the dashboard using the binary string method ('old-style', not NetworkTables) and the dashboard automatically has access to all key data.
-See buzz18_pal_lib.pdf for complete write-up
-Also see buzz18_sw_postcmp for complete robot usage of library
22-05-2013 16:29
apalrd
OK, finished the 'CulverDrive' paper and attached it with example code.
What is CulverDrive?
-Improvement on previous 'Cheesy' and 'Halo' style drive systems, which are already used by many top teams
-Utilizes Logitech F310's round joystick boundary to provide a virtual steering wheel effect, allowing the driver to gracefully navigate the field
-Utilizes a mathematical algorithm similar to the 'Cheesy Drive', the intent in design was to replicate the functionality of a 'Cheesy Drive' using a real steering wheel on a joystick.
-Award-Winning (Waterford 'Innovation In controls' and Championship 'Quality' specifically noted CulverDrive in judge comments)
What does the zip include?
-LabVIEW source code, direct from Buzz18 software release, plus required BuzzLib interpolation blocks (prelookup and interp_2d)
-C source code, used in simulation and intended for Vex targets and readable without LabVIEW. Unfortunately it does not directly run on Vex because RobotC does support the C language very well.
Does it require a Logitech F310?
-In short, yes. The Logitech F310 has a circular joystick boundary, which is key to the algorithm. It's also smoother to drive than a square of octagon.
-The Xbox 360 joystick has an octagonal boundary, which will not provide the desired effect nearly as cleanly. The Logitech DualAction has a square boundary, which will not work very well at all.
As always, feel free to ask questions regarding any of our software.
25-07-2013 01:19
Iaquinto.JoeHow do you open .lvsc files? Is it a tool included with Labview for FRC?
25-07-2013 09:13
apalrd
LVSC file are LabVIEW Statecharts.
The module is not included in the FRC release of LabVIEW. We arranged this with NI in return for feedback on it's use.
Since I didn't expect anyone to have Statchart, I generated documentation for all Statecharts using the internal documentation tool. Every statechart has a folder called <name>_files which contains a PNG image of the following:
-The statechart itself (Main Diagram0.png)
-The Inputs and Outputs clusters (Inputs0.png and Outputs0.png) - I didn't use the StateData but there is a cluster for that too
-The outer appearance of each block (I tried to name them reasonably, that should be in these images - #.png)
-The entry/exit action of the block (for states - entryaction#.png and exitaction#.png
-The guard action of the block (for transitions - guard#.png)
Statechart was a great tool to work with once we learned all the quirks (e.g. when you copy-paste a block, they share the same diagram and modifying one modifies the other). It definitely helped some of the more complex state-machines we had (the first and second revision gun feeder system was very complicated). It was also helpful when debugging - I could tell the mechanical and integration people working on that system exactly what it was trying to do and how it should react.
I also made some VI's to graph the input/output data which was very helpful in analyzing the strange behavior (if I do this and this and this, it jams with gate A open and gate B closed but the gun isn't on...)... But even with the automation there were still corner cases that weren't handled perfectly. But the bucket (and really simple code) had virtually none of them.
25-07-2013 11:23
JamesTerm|
That would be the 'CulverDrive', which we have a paper and example C code in-progress for.
The basic idea is to use the angle of the steering stick instead of the X axis of the steering stick as the primary steering input, when combined with a joystick with circular boundaries (such as the Logitech F310) this allows you to 'steer' the joystick around the boundary of the stick with finer steering resolution. |
25-07-2013 13:18
apalrd
|
That is brilliant! I am looking forward to reviewing this C example code!
![]() |
26-07-2013 14:56
Iaquinto.JoeWhat would you recommend if a team wanted to implement a Finite State Machine approach to their programming? Should we try to talk with NI about the statechart plugin, or implement state machines on our own?
26-07-2013 14:59
JamesTerm|
What would you recommend if a team wanted to implement a Finite State Machine approach to their programming? Should we try to talk with NI about the statechart plugin, or implement state machines on our own?
|
26-07-2013 15:45
Iaquinto.Joe|
Can you give an example of how you would like to use a finite state machine?
|
26-07-2013 16:16
apalrd
We use a lot of finite state machines for things that we sequence. We also use other control strategies for other things, including feedback controls, and lookup tables.
Our software modules are very large. They usually line up with the physical subsystem teams, although I threw the gun in with Roller because the integration between them was very critical and I wanted them to be designed together (there were 4 mechanical teams and 3 software modules).
We then design up a high-level design of the software (what each module is responsible for, what sensors and actuators it has command of, and what the interactions with other modules including HMI/Auton are). From that, we design each module.
The modules are split up into blocks, usually based on control of specific actuators or related groups of actuators. For example, drivetrain is split between drive motors and shift. Drive motors handles the drive motors, and shift handles the shifting. Drive motors is a passthrough function (control is unique to HMI/Auton, this is the only feature that is not implemented in it's module) and shifting is a lookup table. With servos in 2012, shifting was a finite-state machine that would peak and hold the servos for better shift quality and servo reliability (we melted servo or two before doing that).
So it dosen't make sense to design everything as an FSM. We really use a lot of small FSMs when we need to sequence actions at a high level (like the gun feed state machine or 2011 arm state machine), or deal with intermediate states at a medium to low level (like the peak-and-hold state machine).
All of the code runs in parallel (it all shares a single task, the software design strictly prevents blocking execution). The execution order is specified by how data is used by other modules.
I am not sure what NI's intentions are with the Statechart module.
26-07-2013 17:35
JamesTerm|
Example C code is in the zip (culverdrive_sw_release.zip). It isn't great, and isn't tested like the LV implementation, but should illustrate how the logic works.
|
#include "stdafx.h"
#include <math.h>
#undef __AlternateRawImplementation__
inline double sgn(double x)
{
return (x>=0)?1:-1;
}
const double PI=3.14159265358979323846;
const double PI2=PI*2.0;
const double PI_2=PI/2.0;
inline double DEG_2_RAD(double x)
{
return ((x)*PI/180.0);
}
inline double RAD_2_DEG(double x)
{
return ((x)*180.0/PI);
}
//INTERPOLATION CURVES (CONST DATA)
//Since these go past 90 I'm assuming it intended to steer past 90 on the wheel and filter to 115 from 1 to 0
const double c_radius_theta_x[] = {0, 90, 115, 180};
const double c_radius_theta_z[] = {1, 1, 0, 0};
//This table appears to have no effect since z is 1 all across
const double c_r_warp_x[] = {0, 45, 90, 135, 180};
const double c_r_warp_z[] = {1, 1, 1, 1, 1};
//These appear to only yield 1.0 from 115-170 degrees... I have not tested this alternate implementation
#ifdef __AlternateRawImplementation__
const double c_raw_theta_x[] = {0, 90, 115, 170, 180};
const double c_raw_theta_z[] = {0, 0, 1, 1, 0};
#endif
//CALIBRATIONS (CAL DATA)
double c_gain_radius = 1.2;
double c_gain_raw = 1.5;
//Note for Palardy size is now cardinal ;)
double interp_2d(double x,const double *x_tbl,const double *z_tbl, size_t size)
{
size_t num_cur = 0;
//num_cur index will point to the element that is closest to number in table without going over
while((x_tbl[num_cur] <= x) && (num_cur < (size-1)))
num_cur++;
//we assume x fits within range of table
assert(num_cur);
int num_last = num_cur - 1;
//Find fraction between num_cur and num_cur--
double x_frac;
{
const double lt = x_tbl[num_last];
const double gt = x_tbl[num_cur];
const double tmp = x - lt;
const double range = gt - lt;
x_frac = tmp / range;
//printf("Interpolation: Found X:%f between lt:%f and gt:%f (pts %d and %d) frac=%f",x,lt,gt,num_last,num_cur,x_frac);
}
//Find the two points on the z table
double z_out;
{
const double lt = z_tbl[num_last];
const double gt = z_tbl[num_cur];
const double range = gt - lt;
z_out = (range * x_frac) + lt;
//printf(" z: lt:%f gt:%f zout:%fn",lt,gt,z_out);
}
return z_out;
}
//This version of it clamps the range... but also will change any NAN cases to zero (This is a generic solution over _isnan() )
inline double limit_motor(double x)
{
//Clamp range
if (x>0.0)
{
if (x>1.0)
x=1.0;
}
else if (x<0.0)
{
if (x<-1.0)
x=-1.0;
}
else
{
x=0.0; //is nan case
assert(false); //Sanity check, This shouldn't happen with the simple math in this example
}
return x;
}
//Math function for Culver Drive
void culver_drive(double throttle, double wheelx, double wheely, bool quickturn, double &left, double &right)
{
//double theta = fabs(atan((wheely/wheelx)));
//I am confused as part of the code appears that we work only from a range of -90 - 90... while the tables go past 90
//The original implementation shows any down stick movement is the same as if it were up
//For this new replacement if we want to support values past 90 we can remove the fabs()... while this idea would support the real model
//of a steering wheel. I can see it being an undesirable effect (hence the filtering)... especially since there is the quick turn option
//Find arctan of wheel stick relative to vertical... note relative to vertical suggests that we swap the x and y components!
#if 0
const double theta_native = atan2(wheelx,fabs(wheely));
#else
const double theta_native = atan2(wheelx,-wheely); //I suspect we wanted to go past 90 based from the tables
#endif
//Test atan2 against atan... this test only succeeds if we use the fabs(wheely) version
#if 0
//note Palardy... there was a bug 59.29577... should be 57.29577
double test = fabs(atan((wheelx/wheely))); //if it is relative to vertical the x and y components need to be swapped
double sign_test = sgn(wheelx);
assert (fabs(theta - (test * sign_test))<1e-5); //check for equality within 5 decimal places
printf("%.2f %.2f %.2f %.2fn",RAD_2_DEG(theta),RAD_2_DEG(test * sign_test),wheelx,wheely);
#endif
// and turn it into degrees
//convert to absolute value and in degrees (grrrr should never solve code in degrees) ;)
const double sign = sgn(theta_native); //keep the sign to restore it later
const double theta = fabs(RAD_2_DEG(theta_native));
//as it stands this statement has no effect if c_r_warp_z has all 1's
assert(interp_2d(theta,c_r_warp_x,c_r_warp_z,_countof(c_r_warp_x)) == 1.0);
//Find the magnitude of the wheel stick
double r = sqrt(((wheely * wheely) + (wheelx * wheelx))) * interp_2d(theta,c_r_warp_x,c_r_warp_z,_countof(c_r_warp_x));
//Find the Radius Enablement curve
double radius_en = interp_2d(theta,c_radius_theta_x,c_radius_theta_z,_countof(c_radius_theta_x));
printf("%.2f, %.2f, %.2f, %.2f, %.2fn",theta * sign,r,radius_en,wheelx,wheely);
//Find the radius component based on r, theta, enablement curve, gain, throttle, sign
double radius = r * theta/90 * radius_en * c_gain_radius * throttle * sign;
//Find the raw component based on r, radius enablement curve, gain, sign
#ifndef __AlternateRawImplementation__
//double raw = r * radius_en * c_gain_raw * sign;
double raw = r * theta/90 * radius_en * c_gain_raw * sign; //this needs theta
#else
//Alternate raw implementation
double raw = r * interp_2d(theta, c_raw_theta_x, c_raw_theta_z, _countof(c_raw_theta_x)) * c_gain_raw * sign;
#endif
//Find Left and Right powers as sums
double tmpleft = throttle;
double tmpright = throttle;
//Comment out the entire IF/Else block for alternate raw mode
if(quickturn)
{
#if 1
tmpleft += raw;
tmpright -= raw;
#else
//Find the left and right powers as the sums - Use for Alternate Raw
tmpleft += radius + raw;
tmpright -= radius + raw;
#endif
}
else
{
tmpleft += radius;
tmpright -= radius;
}
//clamp value range to +-1
left = limit_motor(tmpleft);
right = limit_motor(tmpright);
}
//reciprocal
const double c_half_pi_reciprocal=1.0 / (PI_2); //its more efficient to multiply than to divide... this is around 0.6366...
const double c_taper_limit=DEG_2_RAD(115.0);
const double c_taper_length_recip=1.0 / (c_taper_limit-PI_2);
void culver_drive2(double throttle, double wheelx, double wheely, bool quickturn, double &left, double &right)
{
//Find arctan of wheel stick relative to vertical... note relative to vertical suggests that we swap the x and y components!
const double theta = atan2(wheelx,-wheely);
//Find the magnitude of the wheel stick
double r = sqrt(((wheely * wheely) + (wheelx * wheelx)));
//taper off past 90 - 115 using simple linear interpolation
double radius_filter=1.0;
if (fabs(theta)>PI_2)
{
const double abs_theta=fabs(theta);
if (abs_theta<c_taper_limit)
radius_filter=(1.0-((abs_theta-PI_2) * c_taper_length_recip));
else
radius_filter=0;
}
//convert to degrees for UI viewing
//printf("%.2f, %.2f, %.2f, %.2f, %.2fn",RAD_2_DEG(theta),r,radius_filter,wheelx,wheely);
//Find Left and Right powers as sums
double tmpleft = throttle;
double tmpright = throttle;
if(quickturn)
{
//Find the raw component based on r, radius filter curve, gain, sign
#ifndef __AlternateRawImplementation__
const double raw = r * theta*c_half_pi_reciprocal * radius_filter * c_gain_raw;
#else
//Alternate raw implementation
const double raw = r * interp_2d(theta, c_raw_theta_x, c_raw_theta_z, _countof(c_raw_theta_x)) * c_gain_raw);
#endif
printf("%.2f, %.2f, %.2f --> X(%.2f) Y(%.2f)n",r,RAD_2_DEG(theta),raw,wheelx,wheely);
//here is a separate alternate raw case
#if 1
tmpleft += raw;
tmpright -= raw;
#else
const double radius = r * theta*c_half_pi_reciprocal * radius_filter * c_gain_radius * throttle;
//Find the left and right powers as the sums - Use for Alternate Raw
tmpleft += radius + raw;
tmpright -= radius + raw;
#endif
}
else
{
//Find the radius component based on r, theta, filter curve, gain, throttle, sign
const double radius = r * theta*c_half_pi_reciprocal * radius_filter * c_gain_radius * throttle;
printf("%.2f, %.2f, %.2f --> X(%.2f) Y(%.2f)n",r,RAD_2_DEG(theta),radius,wheelx,wheely);
tmpleft += radius;
tmpright -= radius;
}
//clamp value range to +-1
left = limit_motor(tmpleft);
right = limit_motor(tmpright);
}
void main()
{
double theta=-PI;
const double NoIterations=100.0;
const double rho=PI2 / NoIterations; //compute step size... by dividing the interval by number of iterations
const bool DoRaw=false;
//do a sweep test full forward throttle non quick turn
while (theta<PI)
{
double Left,Right;
double x=sin(theta);
double y=-cos(theta);
//Test with weaker magnitude
#if 0
y*=0.5;
x*=0.5;
#endif
//Test with no Y
#if 0
y=0;
#endif
//swapped as 0 degrees if vertical alignment
//culver_drive(1.0,x,y,false,Left,Right);
culver_drive2(1.0,x,y,DoRaw,Left,Right);
theta+=rho;
}
printf("n");
}
26-07-2013 18:40
apalrd
The alternate vs normal raw mode is interesting. It was our method of removing the 'quick turn' switch, but we decided we liked the switch better.
In any case, the Radius component should be active from 0 to 90 degrees, plus a little for driver forgiveness. R allows us to return to 0 when the stick is centered, and the math works out such that you can drive it like a halo/cheesy drive if you want (although that's not quite ideal, the math works out the same). I am aware that above 90 slightly more turning power is possible, but I didn't really care. I also probably mistyped the radians to degrees, I actually used 180/Pi in the LV implementation and didn't precalculate it.
The initial ('alternate') raw implementation was designed to provide 'quick turn' functionality (steering without throttle for turn-in-place moves) by allowing the driver to move the stick in a fairly binary way around the bottom of the stick bounds (near +-180). It did not use theta at all (except for the enablement curves). We did not like this behavior, and decided to use the 'quick turn' button to switch.
It's interesting to see some SW design choices you made in your implementation.
26-07-2013 21:15
JamesTerm|
In any case, the Radius component should be active from 0 to 90 degrees, plus a little for driver forgiveness. I am aware that above 90 slightly more turning power is possible, but I didn't really care.
|
//Find arctan of wheel stick relative to vertical (i.e. swap the x and y components) const double theta = atan2(wheelx,fabs(wheely)); //Find the magnitude of the wheel stick double r = sqrt(((wheely * wheely) + (wheelx * wheelx)));
if (AnalogEvents)
{
//Now to use the attributes to tweak the value
//First evaluate dead zone range... if out of range subtract out the offset for no loss in precision
//The /(1.0-filter range) will restore the full range
double Temp=fabs(Value); //take out the sign... put it back in the end
Temp=(Temp>=key.FilterRange) ? Temp-key.FilterRange:0.0;
Temp=key.Multiplier*(Temp/(1.0-key.FilterRange)); //apply scale first then
if (key.CurveIntensity<=1.0)
Temp=key.CurveIntensity*pow(Temp,3) + (1.0-key.CurveIntensity)*Temp; //apply the curve intensity
else
Temp=pow(Temp,key.CurveIntensity); //apply the curve intensity
//Now to restore the sign
Value=(Value<0.0)?-Temp:Temp;
std::vector<std::string>::iterator pos;
for (pos = AnalogEvents->begin(); pos != AnalogEvents->end(); ++pos)
m_controlledEventMap->EventValue_Map[*pos].Fire(key.IsFlipped?-Value:Value);
}
|
R allows us to return to 0 when the stick is centered, and the math works out such that you can drive it like a halo/cheesy drive if you want (although that's not quite ideal, the math works out the same).
|
26-07-2013 21:27
apalrd
|
When you say halo/cheesy drive in this context... do you mean driving the steering without using the y component?
|
28-07-2013 07:59
JamesTermHi Palardy,
So I have started to integrate the Culver drive controls in our simulation and I have found some interesting findings:
When I started testing this, I found that not all circular controllers have the same diagonal intensity as the LogicTech F310. I tested the AirFlo which is also circular, but in testing, it reached full x and y intensity at the corners. I am not sure if there is a spec yet for identifying this, but it should probably be in a form that shows the full x and y intensity ratio (when diagonal), because the F310 I have tested if you normalize x and y readings and move the joystick to its full diagonal where they are equal...it is more than (sin(45) / 70.7...) it's around 80. I believe though that controllers like the AirFlo can still benefit from the Culver drive, because the faster speeds really shouldn't matter as much.
So using just the theta and magnitude and testing this with full y component magnitude. I put a UI feedback that shows what the value would have been if I just used the x component right beside the new value of (theta * r). It turns out they were equal (at first)! This means anyone can simply use halo drive code and control the wheel in the same manner to get the same effect. Now then in regards to the 80% normalized value I mentioned above... as you rotate the wheel closer to 90... the magnitude will actually exceed 1.0... to around 1.14... so as it gets closer and the magnitude increases you lose precision you would have had otherwise, but at these faster speeds it doesn't really matter.
So I have some good news though... I'd recommend you try the same tests if you can and see if you are getting the same results. If I scale the magnitude to around 0.875... this works out perfect where it starts with a magnitude of this (0.875) moving joystick to full Y with x centered... and then by the time it reaches the corner it will be around 1.01. With this you can finally achieve a greater precision for the slower speeds. The derivative of the distribution looks like a "soft" x^2 when compared testing against the x components raw value otherwise.
One other interesting note is that I tested the halo drive scenario (i.e. no Y component)... this turned out to lose more precision than otherwise, but not by much. That said, this solution cannot substitute the need to filter out the dead zone of the joystick, and it should be virtually the same amount as otherwise. This loss of precision could be a benefit for an alternate quick-turn solution. For our setup the workflow may be to use halo drive for most quick turns, and then the Culver wheel drive to fine tune the turns. On Monday I'll try to show a demo of this and these number findings on YouTube.
29-07-2013 15:04
JamesTermHere is a quick demo... I have changed how the magnitude is computed in this demo. It blends from 0.87 when theta is 0 to (1/(pi/2)) when theta is 90. Let me know what you think. 
http://youtu.be/NKd6inx9OH8
29-07-2013 20:41
BJCI really like this demo, it does a good job of explaining the jist of how "Culver drive" works. I'll leave the magnitude computation change assessing to Andrew.
If you look up at my name you'll see that the second one is "Culver." I just wanted to explain why this is named after me (I'm not egocentric, I promise.) Last summer I picked up a controller to drive the 2012 robot and was very disappointed by my inability to do arcing turns using Cheesy drive. Walking out of Chrysler that evening I explained to Andrew what I would rather have -- point the stick forward and move it around the way you would if you were driving a car with one hand on 12o'clock, keeping the throttle the same. Andrew coded it from there and after we tested it on a robot and liked it we were trying to think of a catchy name for it (WASPdrive was already in development and named at this point.) We couldn't think of anything and Andrew already had it named after me in the code at this point so, much to my displeasure, left it "Culver drive."
In any case, our driver really liked it this year and we'll probably be using it again next year.
Cheers, Bryan
30-07-2013 00:03
apalrd
The demo is interesting.
The question of how to deal with the quick turns comes up a lot, not just for Culverdrive but also for Cheesy vs Halo drives (I've usually defined them as the same algorithm, but the switch from including Throttle to not including throttle is done via a button in Cheesy and when throttle = 0 in Halo). One design logic is that the quick turn button is an additional button that can be optimized out, the other is that with the button it's possible to slow down to 0 speed while in an arc turn without suddenly spinning.
We initially used the design logic that the enablement curves would sum to 1 so as soon as the driver exceeded 95 degrees it would pull throttle out of the equation but the rest of the result would remain the same, to allow turning in place without the throttle. That is the 'alternate raw' implementation, which is actually the original. The current raw implementation is more similar to the original cheesy drive, and we just switch based on quick turn switch.
There's still more optimization we can do. I like the interp curve function because I can plugin a set of points and map the curve as I want, however nonlinear or strange it is, without finding a mathematical way to represent it, and it's not that inefficient for relatively small tables.
30-07-2013 12:15
magnetsThat's pretty cool. I like the idea of automatically switching to quick turn after the driver pushes the stick far enough to the side. This is similar to a team who used an actual steering wheel to control their robot one year!
How do your drivers feel about this setup? In the past we've tried some similar things (cheesydrive/right stick steering wheel), but our drivers preferred a tank drive setup, claiming that the cheesy drive was better for driving around an empty field, but when in pushing matches and having to line up, tank drive was better.
Also, do you know if anybody has come up with a way to use these style algorithms on swerve/meccanum, or with the sort of drive that 148 used in 2010 (I forget what it's called).
30-07-2013 14:55
apalrd
-Drivers did not complain. I personally loved it, and I drove a Halo style drive in 2012. High speed driving performance is WAY better than with a tank setup, and alighment is easier by optimizing it when the quick turn button is pressed.
--Driving with a steering wheel is not uncommon, we tried to emulate it with this, as we believe it is a superior HMI to a two-stick tank drive.
-Purely subjective driver analysis has shown that it's MUCH easier/faster to master the graceful high speed arc maneuvers we want with a culver or cheesy drive than the two-stick tank. We actually have a lot of code in our 2011 robot (two stick tank) to modify the inner wheels to arc nicely when the driver lets go of one stick to turn, but that code was quite a bit of work to develop fully and not a good solution, since it really makes driving three-state (both sticks same direction, one stick full one stick zero, both sticks opposite direction) and the driver bumps it on and off a lot for jerky turns.
-Judges loved it, noted 'Culver Drive' in championship award speech
-We used a Halo drive with a slide drive in Vex (what 148 in 2010 had, except without the droppable traction wheels and bump crossing ability), we just used the left stick for translation and right stick for rotation. Since the left/right wheels control steering, we simply ran a normal Halo drive and mapped the slide wheel to left X (left Y was throttle for left/right wheels, right X was wheel). This ran three Vex robots and all drivers loved it for that drivetrain, one was able to pick up some 'fancy' moves like circling around a point in front of him or spinning around off a defender extremely quickly. For a mecanum, I would find theta/R of both sticks, and use left theta for translation angle, left R for throttle, and right stick would determine the rotation as it does now. Then you would just have to solve the mecanum math and warp everything back into the +-1 range nicely.
30-07-2013 18:24
JamesTerm|
There's still more optimization we can do. I like the interp curve function because I can plugin a set of points and map the curve as I want, however nonlinear or strange it is, without finding a mathematical way to represent it, and it's not that inefficient for relatively small tables.
|

30-07-2013 21:47
JamesTerm|
We couldn't think of anything and Andrew already had it named after me in the code at this point so, much to my displeasure, left it "Culver drive."
|
30-07-2013 22:20
JamesTerm|
That's pretty cool. I like the idea of automatically switching to quick turn after the driver pushes the stick far enough to the side. This is similar to a team who used an actual steering wheel to control their robot one year!
Also, do you know if anybody has come up with a way to use these style algorithms on swerve/meccanum, or with the sort of drive that 148 used in 2010 (I forget what it's called). |
|
How do your drivers feel about this setup? In the past we've tried some similar things (cheesydrive/right stick steering wheel), but our drivers preferred a tank drive setup, claiming that the cheesy drive was better for driving around an empty field, but when in pushing matches and having to line up, tank drive was better.
|
31-07-2013 03:12
apalrd
|
Mechanical engineers (except for Jim Zondag) think I'm crazy for wanting to control velocity
|
31-07-2013 11:00
JamesTerm
|
-We do like velocity controls as well, but we haven't implemented them since 2011. I think that would be a logical improvement to this system.
-That said, a simple PI controller to speed does not have a fast enough response time for drivetrain usage. We did this in 2011 and turned the gains WAY up for fast response, and oscillations weren't very good, but it worked. -A better solution is one of the better speed control algorithms designed for shooter flywheel speed control (2012 was especially big on this, 2013 less). We ran a PI-FF algorithm with a feed forward equation (now we would just use a table), which is an improvement |
31-07-2013 11:16
apalrd
Those graphs definitely show improvement over the years.
I've always liked to feed forward as much as possible for best transient performance. The P-I-FF design feeds forward the steady state output (FF), corrects for steady state errors with I, and handles transients with P (what the D term in a 'normal' position PID would do). FF can be implemented as a math model, curve fit, or lookup/interpolation table.
I agree that mechanical engineers tend to solve problems mechanically, as a controls engineer I disagree with this approach.
01-08-2013 13:29
redengin33r|
How do your drivers feel about this setup? In the past we've tried some similar things (cheesydrive/right stick steering wheel), but our drivers preferred a tank drive setup, claiming that the cheesy drive was better for driving around an empty field, but when in pushing matches and having to line up, tank drive was better. |