Help with Joystick Axes and Sensitivity

This is our first year as a team for the FIRST competition, and needless to say, we are having some problems with the programming aspect of our robot.

I have been working with the user_routine.c file quite a bit lately, and my first task was to combine our two motors onto one joystick. The code we used was successful, but we believe that our axes are defined wrong. When we push the control forward, our robot turns left. When we push it right, it goes forward, etc. In short, our code must define forward and backward is as the x-axis, and lateral control as the y-axis. Though this could simply be solved by turning our joystick 90 degrees, we would rather solve the problem through our code.

Furthermore, our joystick control is hypersensitive. We realize that this is a common problem this year, but most of the discussions are geared toward programming veterans. We were able to code for a ‘turbo’ button, which was just an alteration of the scale of the joysticks, but are not fond of this idea. We have found several discussions on exponential or sine curves to define motor power, and are interested in pursuing this option, though we are unsure of how and where to implement it in our code.

My team and I appreciate any help that you can provide. We are noobies when it comes to programming, so a slightly ‘dumbed down’ version, or simply a file that we can compile and play around with would be greatly appreciated. If you have any questions or requests, feel free to leave them in a post, send me an e-mail, or contact me via AIM.

Thanks,
Bryan

It sounds like one of your drive motors is running backwards from the way your code intends. That can be solved by subtracting the pwm value from 254 before outputting it, but it is more appropriate to reverse the connections to the motor from the victor, so that “plus” always equals “forward”. (You might also have left and right swapped; you can figure that out later.)

On the other hand, maybe you do have your axes confused. The x axis is left-right joystick motion, and the y axis is forward-backward joystick motion. Swap x and y in your code if you’ve assumed otherwise.

Thanks a lot for the tip Alan, it solved my problem. Now I just have to figure out how to make my x-axis a little less sensitive. Thanks for your help!

Try this:


p1_y = (p1_y / 3) + ((127 / 3) * 2); //scale down Y axis
p1_x = (p1_x / 3) + ((127 / 3) * 2); //scale down X axis
 
left_pwm = Limit_Mix(2000 + p1_y - p1_x + 127); //limit pwm
right_pwm = Limit_Mix(2000 + p1_y + p1_x - 127); //limit pwm

pwm11 = left_pwm;
pwm12 = right_pwm;

If you want just a little more GO GO juice…do this:


p1_y = (p1_y / 2) + ((127 / 2) * 1); //scale down Y axis
p1_x = (p1_x / 2) + ((127 / 2) * 1); //scale down X axis
 
left_pwm = Limit_Mix(2000 + p1_y - p1_x + 127); //limit pwm
right_pwm = Limit_Mix(2000 + p1_y + p1_x - 127); //limit pwm

pwm11 = left_pwm;
pwm12 = right_pwm;

Hope it helps.

A good way to decrease the sensitivity feel of the joysticks is to output to the PWMs as a non-linear function of the joystick inputs.

For example, instead of:

PWM = joystick input (or pwm01 = p1_y or y = x)

Try:

PWM = 128/(1+127EXP(-kjoystick in))-1 (<- this is a logistic function)

(find a k value that works… probably around .075)

Of course it is not a good idea to have the RC evaluate this complex floating point expression every loop. Instead try using excel to generate a list of what you wish every joystick input values to map to in PWM outputs and save the excel file as a coma delimited list. You want a file that looks like:

“0”,”0”,”1”,”1”,”2”,”2”

Except instead of 0, 1, and 2 you want the values generated from your function. You also want either 127 or 244 numbers instead of six.

Creating a file in this format allows you do store this information in a ROM table on the RC:

[Please note that I have not actually tested this on the RC. I have no way to verify if it will actually work or not, but this bit of code does compile successfully. If someone notices a mistake please correct me.]

const rom unsigned char pwm_outputs[128] = {
#include “name_of_file.csv”
};

Now you can access the data you put in your name_of_file.cvs file as you would any other unsigned char array in your program. IE, pwm_output[1] would return “0” if your file looked like my example above does.
So, pwm01 = pwm_output[p1_y], will set pwm01 based on the excel table you generated.

This is the way I feel is best to decrease the sensitive feel of the joysticks. Another way I’ve heard suggested is to implement piecewise functions instead of a ROM table. Instead of a switch controlling a high and low sensitivity, the position of the joystick axis would. In other words, when the joystick is between 0 and 30 (in absolute difference from 127), the rate of change of the PWM output would be 2 PMW per joystick (a line of slope 2). From 30 to 60 you could use a steeper line (slope 2 maybe), and so on. This would allow your robot to feel less sensitive at low ranges but still reach full output at full joystick throw.

Greg

I used an output curve and deadzone system to get around my team’s joystick problems. Basically, instead of always setting the output to whatever the joystick reads (output = input;), you first want to round the value to neutral if it’s pretty close, to prevent “ghosting”, or off-center movement when the springs don’t pull the stick all the way back. This would be something like output = input; if(output < 127 + c && output > 127 - c) output = 127; where c is a value you experiment with (I think ours is set to 16 now). Then, for values that make it through this filter, you want to apply some algorithm to weigh the output.

There are a few different formulas you could try. If you want to think of it visually, consider a graph in the first quadrant of output vs input. The easiest function to use is a linear one, which would just be a simple straight line that cuts diagonally through the neutral point. This is what you already have, and doesn’t give you the best results. Another option is a sinusoidal or parabolic graph, that is concave down left of the neutral point, concave up to the right, and has a slope of zero in the middle. This way, if you move the joystick part way left or right, you don’t get much movement, but when you go all the way you still get full power.

It’s easier to deal with the math when the variables are signed chars (ranging from -128 to 127) instead of unsigned chars (0 to 255). You would read in the joystick value as a signed char, convert to unsigned and subtract 127, do all your calculations, then at the end add 127 and convert back to signed and map it out to pwm01 and the others. It’s up to you whether you want to complicate the variables this way, but it will simplify calculations later on.

If you do use signed chars, the formula for parabolic weighting is output = (int)input * input / 127; if(input < 0) output = -output;. For sinusoidal, it’s output = 127 * sin((float)input * PI / 256);. My teammates seem to like the second function better, but as GregT said, it is computational expensive to use floating point operations.

Thanks for all of the help everybody. I’m going to compile some new hex files and load them onto our robot on Monday. I’ll let you guys know how it works out. Thanks again!

Bryan