Exponential Tray Programming

Does anyone have any sort of guide to programming motors to either speed up or slow down exponentially using RMS C++? Specifically using a potentiometer. We would like to try and implement something like this for our tray.

I use a potentiometer in conjunction with the motor’s internal encoder values. The potentiometer determines the stopping and starting positions, while the internal encoder’s value is mapped to a specific velocity. For example, if the tray is tilting upwards, and the internal encoder value is 400, then the velocity of the tray is 40. You can experiment with different continuous mathematical functions to find an appropriate “mapping” curve. I do this by representing the x value of the function by the internal encoder’s value and the y value by the output velocity.

So why not use the potentiometer to map the velocity of the motor? The potentiometer does not return perfectly linear values. Meaning a rotation from 30 degrees to 60 degrees could change the pot’s value by 150 while a rotation from 60 degrees to 90 degrees on the same pot could result in a change of 120. If the sensor was perfectly linear, then a 30 degree rotation of the sensor would change pot’s value by the same amount. See the jerks in potentiometer value below:

comparison_internal_pot

A potentiometer is really just an adjustable electrical resistor, so by turning it you are just changing the voltage that it detects. This method of measurement results in the slight error in the linearity of potentiometers. I tried to use a pot as the input of my mapping function, but as expected there were lots of random spikes due to its non-linearity. Considering you want the tray motor to move as smoothly as possible, using the pot values mapping the motor speeds is not feasible.

Another reason I use both sensors in conjunction is because the pot always measures the absolute position of the system however, the encoder value is only relative to the system’s position at the start of the program.

Understanding how each sensor works, and their strengths / limitations helped me determine the best way to implement each sensor to my advantage.

4 Likes

Okay, I get what you mean by how the motor itself is more accurate than the potentiometer, but I’m not sure as to how to implement this into my code. Like, how to use certain mathematical “exponential” equations for running a motor.

We currently have it set up to simply switch to a slower speed once it reaches a certain point, but we’ve never done any C++ programming to this degree, so we honestly don’t even know where to start.

I think you are looking for a proportional loop. You can find a lot more about it such as tuning by searching the forums for " proportional loop", “p-loop”, and “PID”.

You want to take your target position, then subtract it by your current position, and use the difference (or “error” as we like to call it) as your motor’s speed. So say you want to be at 20, but you are at 12, your error would be 8. Do the math again but with yourself closer to the target, you will see your error get smaller. So say your target is still 20, but you are now at 16, you’ll see your error be 4. So with that being the motor’s speed, you end up with a faster motor further away and a slower motor as it gets close to its target.

The problem with this error is that it isn’t proportional with your motor’s speed. This is where proportional comes into play. We multiply the resulting error by our proportional term (kP) so it is within the motor’s ranges.

5 Likes

So, when I’m targeting my position, should I use the position on the potentiometer or within the motor? I don’t know how to declare the position within the motor in RMS. How would I implement these equations within my code?

You should use the potentiometer, since its value doesn’t change after a reset. Your code would look something like this (assuming you only want it to tilt forward):

int target = whateverPotValue;
float kp = someSmallNum;
double error;
Void tiltP() {
  while (error < target){
   error = target - curPotValue;
   int power = error * kp;
   motor.spin(power, otherParams);
  }
  motor.stop();
}
2 Likes

Remember,

If that is too complicated then, just start with one sensor.

You might have misunderstood me. The potentiometer is more accurate because it “remembers” its position. The drawback is a jumpy output as you turn it. The internal encoder has a smooth output as you turn it, but due to slop in the system and, the inablity to “remember” is position after the program has stopped, it is less accurate for finding exact positions. That is why I use the potentiometer value to set just my end points, but the internal encoder value to calculate velocity as its moving.

As @PortalStorm4000 and @Anomalocaris said, you can use a proportional loop, but understand that is not “exponential” as you asked in the original function. If you graphed the output, you would get a straight downward sloping line.

Create a function which takes the input of the motor as a parameter, and do the math using that variable. For example, if you wanted the equation y = A*e^-B(x+C) + D:

float mapExponential(float input, float A, float B, float C, float D){
  float output = (A * pow(e, -B(x+C)) +D);
  return output;
}

Input would just be any sensor value, and A, B, C, and D are just transformations of the function. You can find their exact values by graphing them in desmos.

From the graph linked above, you can see I set a max velocity, a min velocity as well. Those are just the max velocity you want the tray to travel at and the min velocity to keep the tray pushing when it is near the target value. You would can make the tray move the max vel until a certain point (where the black line intersects the blue line ~ 100), then you could use the map function to exponentially decrease the speed. You should understand that I used arbitrary numbers for the x axis, and they should be numbers that match your desired sensor values. Also note that if your sensor value is negative you can flip the graph by changing the sign of B. If you do choose to test it like this, you should start with low velocities, just in case it does something you were not expecting.

3 Likes

No you wouldn’t; if this were the case, then we wouldn’t be recalculating error every iteration. A constant slope would mean that both kp and error stay the same the entire time. Instead, error progressively gets smaller (or larger if it overshoots) making it an exponential graph with an asymptote at target.

2 Likes

@Anomalocaris
A constant slope and constant error would have 0 slope. A downward sloping line has a constant slope which is mathematically the kP.

Using the equation:

And 6 discrete points, you can see that it is a:

As you can see the error gets smaller as it gets closer to the target, and it gets scaled by a factor of kP. Mathematically, a proportional relationship will never have asymptotes.

I encourage you to find the slope between each of those points. You will see that each slope has the same value of kP, which proves that it is a downward sloping line (with a constant slope). Further confirmation that there is no asymptote is shown by the final point which represents overshoot because it is greater than the target value.

1 Like

Your graph is subtracting the x value at each point from the y target, however, in this case, the error would be the y target - the current y position. The graph would look something like this:
p graph

1 Like

The equation you graphed is not a proportional relationship. Nor is it is the same equation you pseudo-coded in your previous post.

I should not have called the target value y_target, that makes it confusing – my mistake. To clarify, all the numbers on the x-axis represent the sensor values. This includes the current sensor value and the target sensor value. The numbers on the y-axis represent the output value of the calculations provided above. Based the code example you provided above, it would result in a proportional relationship. Which looks like this.

I can justify that my coordinates are correct because the input and target values are on the x-axis, and the error calculation (which gives the output value) is on the y - axis.

1 Like

Perhaps in that case the slope is constant. However, my graph is a representation of just the sensor value relative to the target value. This is a one dimensional situation, so me graphing it on a 2D plane makes x time. All of the values in the 2nd and 3rd quadrant are impossible because you can’t have negative time, leaving you with just the graph of how the current value is driven to the target value, assuming target is 100 and kp is 0.2. I think we just misunderstood that we were describing different graphs.

1 Like

What is the best way to determine my values to implement within this example. Like, how to properly determine my kp, error, and double error. I’m unaware of what kp is, however I have somewhat of an understanding to what error is. Sorry, I don’t have a ton of programming experience.

double error; is simply the declaration of error as a variable. error is the difference between your target and your current position. For instance, if your target is 3000 and your current value is 2750, your error is 250. kp is the constant for proportional, that you have to tune yourself. You want to start at a small number and progressively increase it to the point where your tray oscillates around the target, then decrease it until it no longer oscillates. You also probably want to change the condition of the while loop to be a range of values (i.e. while(error < 15 && error > 0)) since target is asymptotic meaning that a P loop will never truly drive the error to 0, so the loop will never break otherwise.

1 Like