Desensitizing Joysticks

You might want to stay away from recursive functions because they can take up a lot of memory while running making the code less reliable.

Hold on a sec folks…

I’m assuming before you’ve done any of this, you’ve gone through and recalibrated the Victors with the joysticks you plan on using, right?

Right?

The option to recalibration is really just there because not all joysticks are created equal. The output from one stick to another varies, espically from model to model. By calibrating each victor to the joystick you use, you’ll get a smooth, consistent output from it, instead of either:

A: Peaking early (I’ve seen victors putting out %100 at only half stick), which leads to ultra sensitive sticks

or

B: Never peaking. Which leads to speed and power you never get, just because your stick doesn’t let you move the pot that extra bit.

Honestly, calibrate the Victors before you do anything else. I suspect that a great deal of the trouble teams are having with the new sticks is because the victors factory calibration is off from were they ‘want’ to be with the new sticks.

Check your documentation for the procedure. It takes 30 seconds, and can do only good.

-Andy A.

Compared to last year’s, the new sticks are more seensitive. I think it’s because the centering springs aren’t as powerful.

I think the Victors would mask that effect quite nicely, because they have their own deadband around 127.

I have found that the Victors do indeed have their own deadband of about +/- 7 that I didn’t like, so I wrote code to get around it. I chose my own deadband (+/-2 for example). First I calculate my (smooth) transfer function assuming the Victor has no deadband. Then, when the value is inside MY deadband, I set the output to the center value of the Victor (determined either by experimentation (which is what I did) or by calibration of the Victor (better).

When the value is above my deadband, I add a fixed offset to the value. When it is below my deadband, I subtract a fixed offset. This way the output “jumps” from dead to the first value that causes the Victor to do something. I actually did the process on absolute values and restored the sign at the end.

BTW, I have found that working with signed ints saves a lot of headaches. But you need to be careful when converting back to unsigned char that the value is in the range 0-255!

int speed;  // signed motor speed; negative means backwards
int absoluteSpeed; // always positive
int direction; // +1 or -1 (sign of speed)

#define ABS( x )  (x) > 0 ? (x) : (-x)
#define SIGN( x ) (x) > 0 ? (+1) : (-1)
#define VICTOR_CENTER 131
#define VICTOR_DEADBAND 7
#define MY_DEADBAND 2

// ... ( magic transfer function that sets 'speed')

// work around Victor's deadband:
//
absoluteSpeed = ABS ( speed );
direction     = SIGN( speed );

if ( absoluteSpeed > MY_DEADBAND )
  absoluteSpeed += VICTOR_DEADBAND;
else
  absoluteSpeed = 0;

// restore sign and add offset.  (Be careful to keep this in range of 0-255!)
pwm13 = (unsigned char) clipInt((absoluteSpeed * direction) + VICTOR_CENTER, 0, 255);

The function clipInt() is:

int clipInt( int in, int low, int high )
{
  if ( in > high )
    return high;
  else if ( in < low )
    return low;
  else
    return in;
}

CAUTION: I wrote all this code manually here (not cut and pasted), so it may have bugs!

I know that people have said what to do, but, I’m new to this and do not understand how to modify the sensitivity. Could anyone please post up the code to desensitize the joysticks and where to put it. Thanks! :smiley:

there is no specific “the code” - there a plenty of ways to do this. however, I have included an example below - it maps the joystick values to an approximation of an x-squared curve. it’s not perfect, and i would probably do it differently if i were putting a ton of effort into it, but this is for an example. it should still be functional though.


const rom signed char JOYSTICK_SMOOTHING[128] = 
{
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8,
8, 9, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 17, 17,
18, 19, 20, 20, 21, 22, 23, 24, 25, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 45, 46, 48, 49,
50, 51, 53, 54, 55, 56, 58, 59, 61, 62, 63, 65, 66, 68, 69, 71,
72, 74, 75, 77, 78, 80, 81, 83, 85, 86, 88, 89, 91, 93, 95, 96,
98, 100, 102, 103, 105, 107, 109, 111, 113, 114, 116, 118, 120, 122, 124, 126
};

unsigned char Get_Desensitized_Output(unsigned char input)
{
    if(input > 127)
    {
        return ((unsigned char) (128 + JOYSTICK_SMOOTHING[input - 127]));
    {
    else
    {
        return ((unsigned char) (127 - JOYSTICK_SMOOTHING[127 - input]));
    }
}

That’s a lot of numbers. :slight_smile: I like it though.

yeah - it’s a lookup table… gonna have a lot of numbers any way you work it. save you the trouble of typing them in, though :slight_smile:

We are using an array of values.
First we worked out in excel a parabolic equation for all the values from -127 to +127 (with zero as neutral)
Then input the adjusted speed into the array.
It was decided to have the parabola reach linear at about 1/4 throttle/joystick movement.

unsigned int throttle_curve[50] = {value0,value1,…};

then we write a quick little function to take the motor speed in and transform it.

unsigned int adjust_throttle(unsigned int speed_in)
{
int speed_out;
if (speed_in > 104 && speed_in < 150)
speed_out = throttle_curve[speed_in - 104];
else
speed_out = speed_in;
return speed_out;
}

call it like this.

p1y = adjust_throttle(p1y);

The nice part of this setup is that you just adjust array values to flatten or sharpen the curve.

Phil

Wouldn’t a hyperbola work? not the regular kind; try Y=sqrt(R^2+X^2)-R. basically, close to zero, it’s a curve, but farther away, it’s close to linear. Excel that and try it. But it won’t reach maximum value ever. A coefficient would be necesary. R is the value at zero.

Take out the set screw in the joystick. It should spin like the wheel on the robobt.

I didn’t break it!

plenty of mathematical forms work, just pick one.

I am new to Chief Delphi forum but recommend that you add one more
bit of functionality - a rate limiting function. The exponential curve via
table lookup solves one problem but not the problem of quick changes
in torque. If you use both methods simultaneously, it can help to
solve range/resolution issues associated with Joystick input to pwm0x output,
and torque/speed transitions from positive/negative to negative/positive.

Looking back on this post, the reason we did this was because our joysticks didn’t stay exactly centered and you would have to re-adjust the trim 85 billion times in a match if we used those joysticks.

im not a fluent programmer. im learning java in class. but our programmer wrote in a zeroing button on the control board after the joysicks being a pain and never going back to zero. big mistake, the joysticks hold a value, i forget what it is, and after a while the values become off due to the mechanics of the stick. The zero button made the robot very hard to drive,if you zero it while its not PERFECT, the handling falls apart.

just sharing an experience

In programmer terms? Do you mean a loop to change the speed only so fast?

My team had a similr problem. To correct it I wrote some code that would center the joysticks when a button was pressed. It would consider the current position of the joystick the center. It worked by finding the difference between true center and that position and always adding it to the output for the motors. It worked well on our robot and should not be to hard to edit for your robot.

P.S. If you would like the code PM me. The program was a little rushed so it was a tad buggy, nothing too major, but I’m sure with a little work they can be worked out.

It seems the problem is big enough to cause some serious thread resurrection.

Here was Team 188’s solution to sensitive joysticks for 2004 courtesy Carol, Honson, Tristan and myself:


temp_p1_long = (signed long)p1_y - 128;
pwm01 = (unsigned char)(((temp_p1_long * temp_p1_long * temp_p1_long)  >> 14) + 128);

temp_p2_long = (signed long)p2_y - 128;
pwm02 = (unsigned char)(((temp_p2_long * temp_p2_long * temp_p2_long)  >> 14) + 128);

make sure you declare temp_p1_long and temp_p2_long as signed long beforehand.

In a nutshell, it’s a cubic transfer function that makes the robot accelerate exponentially when you push the joystick. There’s a nice wide area to operate slowly, yet full speed is still there when you push it all the way.

We spent a few days testing different transfer functions, and this one worked out the best.

It’s pretty CPU intensive (multiplying long integers), and a look-up table would be faster, but try it for a quick and dirty way to get things working smoothly. You can generate a look-up based on the code later on.

Cheers!

-SlimBoJones…

Absolutely…our team also realized the joysticks provided by FIRST proportionalize poor handling…Don’t know if you’d had a look at the white papers on this website yet but team 1382’s written one about the advantages of converting the linear curve of the joystick into an exponential one (and includes source code). I guarantee it’ll give you some ideas…
Link:
http://www.chiefdelphi.com/forums/papers.php?s=&action=single&paperid=280