24C's Motor Control Value Remapping

Soon after the beginning of the Sack Attack competition season, Mr. James Pearman posted this thread which ended up showing the 2-wire motors’ “strange” logarithmic Motor Control Value-to-Motor Speed curve:

https://vexforum.com/attachment.php?attachmentid=6175&d=1337975715

This was the first time I had seen this, although our team had always somewhat noticed a difficulty in controlling our robots (specifically geared-up drives) at slower speeds. If you look at the graph, you can see that in order to get the motors to run at half-speed (63, or about 55 RPM), you would need to push the joystick less than a fifth of the way (24/127). You can also notice that after a Motor Control Value of about 88, the speed of the motor does not change hardly at all. This seemed very inefficient to me, and makes driving more difficult than it has to be. That is what originally gave me the idea to remap the motor control values in order to gain a near-linear graph.

For our first tournament in June, I simply took the graph, and utilized Mr. James’ test results to create an array of 128 values, remapping the (unsigned) joystick values inputted into it in order to get a linear motor speed control from 0 to 127. This proved useful, and did seem to help, although it was not perfect. One flaw was that the values used were based on 269 motors running without any load at all, while I was running 393 motors geared up and on a rather heavy competition robot. What I needed to do was create an array specifically tailored to our robot.

Because we really only needed this fine control on the drive, that was what I ended up doing testing on. I got a few foam tiles and created a square to run the robot on for testing, and ran a program that gave a Motor Control Value to all the motors on one side of the drive for about 5 seconds (with one additional second at the beginning to allow the motors to accelerate) before taking count of the value of the IME on that half of the drive, and calculating a speed in RPM for that Motor Control Value. It would then step to the next power value and repeat the process, for every value from 1 to 127. I preformed this test both from 1 to 127 and 127 to 1, on the left and right sides, monitoring the battery voltage along the way, to ensure that it did not drop very much (once it dropped to a certain voltage, I would pause the test and replace the battery).

I then took all the data I had collected and placed it into an Excel spreadsheet. Here is a graph I created with the data from 24C’s robot that we brought to the World Championship:

Using this data, I was able to create an array so that this curve would be as close to the red “Ideal” line on the graph as possible. I did this by first deciding that I did not want to let the drive begin moving until the joysticks reached above ±10 (to act as a threshold for the joystick springs). Then, I knew that the value for 11 would be the first Motor Control Value that consistently kept the robot moving. That value happened to be 21 for our robot, and the RPM at that power was ~15. I also knew that the value for 127 would be the fastest, which of course was 127, and the RPM at that power was just under 160 (the motors were internally geared for speed). Knowing those two points on the graph, I could then calculate every single other point for the ideal graph. I could get the ideal difference in RPM between each array value by simply taking (160-15)/(127-10), which equals 145/117, or ~1.239.

From that number, I could conclude that the value for 12 should be whatever Motor Control Value gave a speed closest to (15+1.239) = 16.239 RPM. I then continued doing that for 13, 14, etc., until I reached 127. I then had a completed array. Here is the array that I ended up creating for 24C:

// compensates for non-linearity of control value vs speed curve
const unsigned int TrueSpeed[128] =
{
  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  0, 21, 21, 21, 22, 22, 22, 23, 24, 24,
 25, 25, 25, 25, 26, 27, 27, 28, 28, 28,
 28, 29, 30, 30, 30, 31, 31, 32, 32, 32,
 33, 33, 34, 34, 35, 35, 35, 36, 36, 37,
 37, 37, 37, 38, 38, 39, 39, 39, 40, 40,
 41, 41, 42, 42, 43, 44, 44, 45, 45, 46,
 46, 47, 47, 48, 48, 49, 50, 50, 51, 52,
 52, 53, 54, 55, 56, 57, 57, 58, 59, 60,
 61, 62, 63, 64, 65, 66, 67, 67, 68, 70,
 71, 72, 72, 73, 74, 76, 77, 78, 79, 79,
 80, 81, 83, 84, 84, 86, 86, 87, 87, 88,
 88, 89, 89, 90, 90,127,127,127
};

Before I go into the strange jump from 90 to 127 at the end, I want you to take another look at the graph of the motor speeds:

Notice how from a Motor Control Value of about 85 to 127, instead of consistently increasing like it had been, the speed jumps rather randomly up and down, and doesn’t really increase by very much as a whole. This was the main reason to just about get rid of this portion. Here is a comparison with the results when the same exact speed test was done using the array above:

As you can see, we were able to achieve near-perfect results, getting our array to map the values in order to create a very close to linear graph, right along the ideal line. We found this to be helpful in user control, allowing the driver to have great control of the robot’s drive at any speed.

If you have any more questions on how to do this, or have comments on it, please feel free to make a post below.

Thank you,

~Jordan

2 Likes

Thanks for sharing a nice practical example. This looks well done and sounds like you had good results with this approach.

Cheers Kb

I can vouch for how awesome this is. I love this remapping I used it all season and now its hard to drive a robot without it!

whoa I love it ! I’ll keep it in my tabs for the next time I’ll have to program my robot thanks a lot for sharing :):slight_smile:

Very clever. Did it take you very long to run all the tests?

We used a look up table like this on our robots last year. It made the robots easier to drive and made good PID controllers, which really want the output speed to be linear with power, possible.

Thank you for all of the positive feedback! Let me know if you do use this and are happy with/have questions about the results. As Kyle said, he did use this on his robot, and even just by using the numbers I had in 24C’s array was able to notice an improvement.

If you go through and do the test that I did, please do share the numbers you used for your array – perhaps we could figure out an “average” that teams could use if they did not want to perform the test themselves.

It did take a little while, but that was mainly because of the struggle I had in making sure the robot did not drive off of my small 2x2 tile square. I had the robot accelerating for 1 second, calculating for 5 seconds, then waiting for 3 seconds (to allow me to re-center the robot on the tiles in case of slipping). If you had a larger square setup, especially an entire 6x6 field, you shouldn’t have as much trouble, and ought to be able to get the test done using a 0.5 second acceleration, 3 second calculation, and 1 second deceleration. This could get the entire test done in under 10 minutes. You also do not need to perform the test forward and backward, or for both sides of the drive. What I found was that the numbers were very similar (as they should be).

This was another thing that I forgot to mention at the end. This also helps with autonomous, or any other implementation of motor acceleration/synchronization/speed control, for example in P or PID controllers. Thank you for adding that!

~Jordan

That’s good work Jordan, our team did use something similar during the Gateway season but pulled it out last year. For Sack Attack we did use a LUT as part of the PID control code, the motor drive value was linearized but this was calculated using a log function as part of initialization rather than having a fixed LUT. Here’s a comparison between your data and the curve I used in the PID code.

[ATTACH]7492[/ATTACH]

I did compare 393’s and 269’s at one point, they were pretty similar, but be aware that there is a small difference between how ports 1 and 10 work and the other 8 using the MC29s.

As background reading, also take a look at this thread that shows how the VEXpro gives a much more linear response than the cortex, it’s all down to the way the H-bridge circuit is driven. There has been lots of discussion of this issue in the FRC forums over the last couple of years, the recent Victor 888 that IFI launched is much more linear than the older 884. I though at one point last year we might have seen a new VEX motor controller using the same algorithm as the Victor, never materialized.

https://vexforum.com/t/vexpro-motor-speed-tests/21688/1&page=2

These experiments last year were the beginning of the work that became the motor current estimation and the subsequent SmartMotorLibrary that vamfun and I created.

https://vexforum.com/t/estimating-motor-current/21703/1&page=2

Another approach to this problem would be to use PID speed control of the motors, instead of the joystick value being sent directly is becomes a requested speed and then the PID loop decides what to send the motors. The ROBOTC 3.61 beta last year used this technique, it was good for autonomous control but not so much for driver control. I may have a look at doing this over the summer if time permits.
lut_comparison.jpg

Since each motor has different speed,the difference between the motors from both sides made our robot can’t forward straightly.So I also want to remap the output value.I was thinking about PTC and battary voltage.It maight be good to break into several steps.Also,do you just use a linear remapping,or use a cubic mapping?

Not sure I understand the question, the LUT (lookup table) used by the 8888 code (red line on graph in post #8) was a power function calculated as follows.

// lookup table to linearize control
#define PIDLIB_LUT_SIZE     128
#define PIDLIB_LUT_FACTOR  20.0
#define PIDLIB_LUT_OFFSET    10
static  short   PidDriveLut[PIDLIB_LUT_SIZE];

void
PidControllerMakeLut()
{
    int   i;
    float x;

    for(i=0;i<PIDLIB_LUT_SIZE;i++)
        {
        // check for valid power base
        if( PIDLIB_LUT_FACTOR > 1 )
            {
            x = pow( PIDLIB_LUT_FACTOR, (float)i / (float)(PIDLIB_LUT_SIZE-1) );

            if(i >= (PIDLIB_LUT_OFFSET/2))
               PidDriveLut* = (((x - 1.0) / (PIDLIB_LUT_FACTOR - 1.0)) * (PIDLIB_LUT_SIZE-1-PIDLIB_LUT_OFFSET)) + PIDLIB_LUT_OFFSET;
            else
               PidDriveLut* = i * 2;
            }
        else
            {
            // Linear
            PidDriveLut* = i;
            }
        }
}

Thanks for your code! I was just thinking about the opreation of the test.Also,I was trying to find a good cubic function to apply in drive code.

The main reason i want to remap the output value is to make the robot forward straightly.I think that would be the fundamantal solution to the problem.

Best solution is to encode both left and right sides of the drive and then use closed loop control (PID or whatever) to make both sides run at the same speed. This assumes that any mechanical differences have already been removed. Sometimes you can also try and match motors before installing them, not all motors run at the same no load speed, also they often run at different speeds when running clockwise or anti-clockwise.

On the original post in the thread, how were the speeds calcuated? Are they end steady state free running speeds or as you are increasing the signal to get to the top speeds?

Did you have a series of tests that incremented the motor speed and then measured the end free speed for that PWM signal value, or is it measured along the way as you increased the control values along the way?

I am just a bit confused. I am wondering how the charts are to be read and why the free running speed would be lower as you increase the PWM value (near the top). I could see in an instant being lower but after a few seconds shouldn’t it be about the same?

I am not 100% sure what you are asking, but I’ll try to answer.

The speeds in my graph were calculated by running a program that took the following steps:

  1. i = 0.
  2. Set left drive motors to power of i.
  3. Wait 2 seconds for drive to accelerate.
  4. Reset left encoder.
  5. Wait 5 seconds.
  6. Read left encoder and calculate average RPM over the last 5 seconds.
  7. Set left drive motors to power of 0.
  8. Wait 3 seconds for drive to decelerate.
  9. i = i + 1.
  10. Go back to step 2.

Also, this was not a free speed test. This was with a load. I wanted the drive to have a perfectly linear speed graph that used an array tailored for our robot.

The reason that the speed is strange at high PWM signals is just because of the strange behavior of the Motor Controller 29 (and the internal motor controller inside the Cortex), and perhaps small changes in friction when I did my test. The strange behavior of the motor controllers is why I created this array for our robot.

~Jordan

Thanks. I was hoping this was the process.

It just seems odd to me that the speed at around 90 is greater than that of around 105. Just trying to wrap my brain around that.

I wonder how much effect do you think load/mass would have on the response curve and subsequent mapping of the response? Not such an importance in this game but sack attack had different characteristics for robots that carried lots of weight to the goal.

You would think max speed could be capped by the effect of the weight but would the response curve look different?

I think that the speed fluctuation at high motor power may have been caused by the drop in battery voltage as the test went on. I forget whether those specific results were from one of the tests where I replaced the battery any time it went below 8v, but I saw similar fluctuation before (and while) doing that.

I think that with increased weight, for the most part all of the speeds would simply be lowered. Because of this, the lowest power that still moves the robot would be higher, also. If one of the 24 teams performs this test, I will be sure to have them document what they did, and what their results were, and post them here. They have a much different drive than what 24C had at Worlds in Sack Attack.

~Jordan

There’s actually nothing strange about the MC29 or cortex motor control per se. At the pwm frequency used and with the type of control implemented, the non-linearity is exactly what would be expected. As Jordan said, his results near full power were most likely due to variations in battery voltage and/or slightly different conditions for wheel slip.

Looks cool!

This thread is from 2013 btw…

@rbenasutti Still relevant even in today’s competition though :stuck_out_tongue: