Making joysticks less sensitive using exponential functions

Hello everyone. I would like to share how I got my joysticks to be less sensitive for driver control.

Disclaimer: I know this has been explained before in previous topics, but I would like to explain how I did it. I want to emphasize that I am using split control with a 4-wheel drivetrain in this example.

First of all, a normal linear joystick function for a drivetrain looks a little bit like this:

This follows a linear line. If you press the joystick 20%, the motors will drive at 20%. The problem with this is when you are trying to do slight movements, you end up not being able to move without moving to much. It is too finicky for fine movements.

A fix to this is using exponential functions for joystick control.

This is the line 0.0001x³, a cubic line that shares the points (0,0), (100,100), and (-100,-100) with the line x. This line allows for slight movements when pressing the joystick lightly. For example, pressing the joystick 20% only spins the motor at 0.8%, but pressing 100% will still spin the motor at 100%.

You might see the benefit of implementing exponential functions now, but coding them into your drivetrain code is a different problem. Some versions of this have already been explained by other users such as @Sarknought

The difference here is that it doesn’t use split control and Sarknought makes the cubic line using two asymptotes, which is much harder to do.

Here is my code (split control with a 4-wheel drivetrain):

# x₁ = Left Motor X = [Controller] [3] position + [Controller] [1] position
        # xā‚‚ = Right Motor X = [Controller] [3] position - [Controller] [1] position
        # Sets each motor's velocity to 0.0001x³ (shown as 0.0001 * x * x * x)
        FrontLeftMotor.set_velocity((0.0001 * (((controller_1.axis3.position() + controller_1.axis1.position()) * (controller_1.axis3.position() + controller_1.axis1.position())) * (controller_1.axis3.position() + controller_1.axis1.position()))), PERCENT)
        BackLeftMotor.set_velocity((0.0001 * (((controller_1.axis3.position() + controller_1.axis1.position()) * (controller_1.axis3.position() + controller_1.axis1.position())) * (controller_1.axis3.position() + controller_1.axis1.position()))), PERCENT)
        FrontRightMotor.set_velocity((0.0001 * (((controller_1.axis3.position() - controller_1.axis1.position()) * (controller_1.axis3.position() - controller_1.axis1.position())) * (controller_1.axis3.position() - controller_1.axis1.position()))), PERCENT)
        BackRightMotor.set_velocity((0.0001 * (((controller_1.axis3.position() - controller_1.axis1.position()) * (controller_1.axis3.position() - controller_1.axis1.position())) * (controller_1.axis3.position() - controller_1.axis1.position()))), PERCENT)
        # Spins motor according to the velocity set
        FrontLeftMotor.spin(FORWARD)
        FrontLeftMotor.spin(FORWARD)
        FrontRightMotor.spin(FORWARD)
        FrontLeftMotor.spin(FORWARD)

This code is able to get the results we want from the cubic line, and is able to do that by multiplying the joystick% by the line 0.0001x³.

With the simplicity of this code, it doesn’t have to be in C++, in fact I was able to code it in VEXcode v5 Blocks. For newer teams this could be really helpful for drivers who want a little bit more control over their movement.

I hope this makes sense. If you have any questions, please ask.

Payne Stroud
Team 39232

21 Likes

I made something very similar last year and created a formula that can be used to adjust the exponential curve’s shape while keeping the endpoint of the line near 100 100 and -100 -100. Here is the graph if anyone wants to use it.

6 Likes

Yeah, you can also use x to the power of 5 or 7 and so on. It will just make the joysticks even less sensitive. Just know it gets to a point where it doesn’t work too well if you use too high of a power.

4 Likes

It looks like you are using arcade controls vs tank, so you may want to look at some past discussions on this topic that might be helpful:

6 Likes

Yeah, I made this code specifically for arcade controls. Exponential tank drive has already been explained before, and I personally like using split control.

3 Likes

Several ways to go about this. Can program a button to toggle between linear and non-linear rates for fine control.

That said… proper practice will erase the need for this. Set your deadzone for about 3% and treat the controller with care. No handling it with food residue on your hands…

2 Likes

Actually this is a really good idea. My team has different preferences for sensitivity and I think this could help.

4 Likes

You can also use even powers if you adjust your calculation to include one instance of the absolute value of the control signal.

3 Likes

I didn’t think about that! This probably works better than a cubed line.

3 Likes

When my kids want to try this, I usually recommend they try both squared and cubed… most go with the squared option.

2 Likes

Yeah, it seems to be the perfect middle ground between linear and cubic.

4 Likes

All on the right track… Remember the the square of x can be written as x**2.0… Of course x**1.0 = x. Try x**2.5. What happens if you go less than 1.0?? Don’t forget the sign. some say that the square of a negative number is positive (so definitely test this after built).
Also, a simple implementation would be Yo = f(Yi). Since you know your control, you know that it won’t go past 255 so Yi-max can’t be greater than 255.
Make Yo a function that only goes to 255. Do that by dividing Yi**2.0/(Yi-max**(2.0-1.0)). (aka,255). Now make 2.0 a variable Pow, and you get Yi**Pow/Yi-max**(Pow-1.0). All stays stable as you vary Pow, and never get more than 255 out of f(Yi)…
This is great for tractor, but Q.E.D. to make it for joystick… :innocent:

2 Likes

The square of a negative number is always positive.

4 Likes

All exponents that go below one don’t help at all. Decimal exponents below one actually hurt the sensitivity, and negative exponents make asymptotes that don’t touch the axes.

As you can see, none of these lines do anything, and they don’t intersect with points like (100,100) or (-100,-100) without multiplying it by a very specific (probably never ending) decimal. Negative exponents create asymptotes that don’t intersect with any axis or the origin point.

1 Like

Javaskrypt, I enjoy your enthusiasm for Math… I (also) was taught that negative square is positive. But the bothered by x=-3**1 is -3 and x=-3**3 is -27… So as you move from integer to real exponent, the troubles start… x=-3**2…01 result=-9.1…
I rationalize this by looking at this as (-1)*(3**2.01), (where -1 is really the sign of the (-3),
PS, exponent less than 1, but more than zero could be a solution looking for a problem… A race car driver may like the sensitivity at 260MPH, as he approaches the banks of the race course… Not all solutions for all problems… :innocent:

2 Likes

Yes, but this wouldn’t be considered squaring the number.

1 Like

Javaskrypt, your charts are ā€œtellingā€. Could you append y= Squared(2)? Sorry, this isn’t a function, but I don’t know if you consider 2**2 as the ā€œsquare functionā€. Also, the squareroot(2). Both charted in range to include {-9.0…9.0}.

I want to see if you get a fault on squareroot below 0 and if positive you get the plus and minus values. For square, I’m looking for the parabola, not a saddle point.

1 Like

What about incorporating ideas from vehicles… speed sensitive steering…

Need to add a term with the current motor velocity…

3 Likes

Drive algorithms are something I have worked on a lot this season. We have developed a little algorithm that makes it easier to drive.
Here it is, in pseudocode for those of you that don’t speak python: (we drive tank)

If the inputs are about the same, set them to the average of the inputs (make it easier to drive straight)

Else, if the inputs have a difference > 60, slow em down (slower turns)

Else, just give the direct joy value

2 Likes

I made a improved version, this one should work way better than this one did.

3 Likes