Optimizing Exponential Drive?

Months ago, I made a guide to implementing an exponential drive function in VCS C++. After learning a lot more C++ and switching to PROS, my last exponential function looked something like this:

#define JOYSTICK_DEADZONE 8

int getExpoValue(int joystickValue)
{
    int output = 0;
    // Ignore joystick input if it's too small
    if(abs(joystickValue) > JOYSTICK_DEADZONE){
      // Direction is either 1 or -1, based on joystick value
      int direction = abs(joystickValue) / joystickValue;
      // Plug joystick value into exponential function
      output = direction * (1.2 * pow(1.0356, abs(joystickValue)) - 1.2 + 0.2 * abs(joystickValue));
    }
    return output;
}

// The output is then used with whatever type of driver control layout (tank, arcade, etc.) you prefer
// A quick example of tank drive in opControl:

// Get joystick values
int leftPower = getExpoValue(controller.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y));
int rightPower = getExpoValue(controller.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y));
driveLeftMotor = leftPower;
driveRightMotor = rightPower;

I went into far too much detail about my exponential function in my previous guide, so I’ll make this explanation concise. Essentially, this function makes driving a lot more accurate. The voltage sent to the drive motors is not linearly related to how far a joystick is pushed, but is an exponential function, as seen in the following graph where x is how far you push the joystick and y is the motor voltage as a function of x.

Graph Labels
(Desmos is great :slight_smile: )

So the question remains, how can this be improved? Some people use a cubic function for their exponential drive. Although that works, cubic functions are more “shallow” near the origin, meaning that for x values less than ~50, you’ll barely get any voltage. Another idea is to use a pre-made exponential function, which might be available in some library, though I personally prefer making my own functions. I’m curious as to how other people approached this problem, and if there were any suggestions to my function.

15 Likes

I am curious how you found those “magic” numbers (1.2, 1.0356, 0.2) for the function. Aside from trial and error, it seems a bit un-intuitive. I like to use power functions, because it is straight-forward to visualize how their curvature changes as power changes. I explained these power functions a while back here. I explained my motivation for using these functions here. I encourage anyone to read how to use these functions, and try them out for themselves. I also enjoy graphing in Desmos, so I created some interactive graphs which visualize these functions. You can enable/disable the each of the graphs by pressing the circles to the right of the equations. You can change power of the curve using the n slider.
Pay attention to my note about even and odd values of n from the first link:

As you can see when n = 1 (blue), it actually results in the traditional output. Also note that they all intersect the points (0, 0) and (127, 127) . Please be aware that if you choose an even value for n , the output will be positive even when you push the joystick backwards! You can solve this by multiplying the sign of the raw joystick value and the output of your function. When n is an odd number, the direction of the output will work as expected. Remember there is no correct value of n , and it’s up to the driver’s preferences.

If you press the ‘play arrow’ next to the x_joystick you can see a circle follow the mapping curve. The radius of the circle shows the rate of change of the curve (its derivative). For reference, I have graphed OP’s equation in orange and [-127, 127] on the domain and range. I use motor.move() in PROS meaning [-127, 127]is the range for the joysticks and motors.

10 Likes

I totally agree, though it is easier to accept the constants with the visual graph. Nonetheless, your method of using power functions is much more clean and alterable to user preference.

This is something I also used in my function, where the int direction is just the sign of the joystick value.

In the post where you explained your motivation for creating your functions, you mention driver assists such as slowing down turns or equalizing speeds. I’m assuming that you used a tank drive layout, but for the former driver assist, did you make it such that if x_joystick was less than some threshold, the value of n would increase, thereby decreasing the requested motor speed?

Also, thank you for sharing your power functions! It’s really interesting to see different takes on the same problem, especially ones that are more efficient :slight_smile:

2 Likes

Thanks!

In response to:

Yes, we were using tank drive. If the left and right joysticks were within a certain range of each other, then both sides of the drive were given the same power. I know I could have used an arcade drive to make driving straight easy, but our driver wanted to use tank drive .

4 Likes