Aidan's trueSpeed

This has been done a couple times before, however, I decided to do it myself for fun.

As many already know, the motor input from 0 to 127 is not linear to velocity but to torque instead. A couple attempts have been made to linearize this trend and called it TrueSpeed, as seen here.

Upon seeing this I set up a test bench with three brand new highspeed motors and three brand new quadratic encoders and ran the code attached in the zip file below called RPM Test. After running this test, I received the following data.

Columns A and B are the recorded data from the program. After I recorded this data, I went to the website “Polynomial Regression Data Fit” and pasted this data into the “Data Entry Area”, I ran the regression and increased the degree of the polynomial until I got to the 32nd degree polynomial, at this point I was happy with how the polynomial fit to the data as well as the 0.9668 correlation coefficient (r^2). I took the polynomial it gave me and reformatted it to Desmos language. After putting it into this Desmos, I restricted the domain to 127.

Now, to calculate the real linear values that should be used, I took the lowest point and the highest point of the graph, added them together, and then divided that by 127 to get a stepping resolution of 127 to create 127 equidistant points along the curve. I took the lowest point (1.593) and added the resolution times the variable z and recorded each value from 0 to 127. I put these values into column I of the google spreadsheet and then rounded them in column j. I then created an array of size 128 (0 to 127) and wrote out the values and got my final result:

const ubyte trueSpeed[128] = {
	 0,  3,  5,  7,  8,  9, 10, 10, 11, 11,
	11, 12, 12, 12, 12, 12, 12, 13, 13, 13,
	13, 14, 14, 14, 14, 14, 15, 15, 15, 15,
	16, 16, 16, 16, 16, 16, 17, 17, 17, 17,
	18, 18, 18, 18, 18, 19, 19, 19, 20, 20,
	20, 20, 21, 21, 21, 22, 22, 22, 22, 23,
	23, 23, 24, 24, 24, 24, 25, 25, 25, 26,
	26, 26, 27, 27, 27, 28, 28, 28, 29, 29,
	30, 30, 31, 31, 32, 32, 33, 33, 34, 35,
	35, 36, 37, 37, 38, 39, 39, 40, 41, 41,
	42, 43, 44, 45, 46, 46, 47, 48, 49, 50,
	51, 53, 54, 55, 57, 60, 62, 65, 69, 72,
	76, 80, 82, 85, 87, 91, 101, 127

Notice how the numbers steadily increase more and more, this is because of the close to logarithmic relation between the motor speed (0 - 127) and the RPM. I believe this is the most mathematically/statistically accurate way to linearize this relationship.

Let me know what you think and feel free to use my work,

First, I must congratulate you on the process you went through. It seems extensive and valid. Next, how does your array compare to the arrays of those who came before you? Also, just an idea: What if you took your values in your array and plotted a graph of it (I see you have already done this) and found the line of best fit. That way, you could just have an equation in a function that calculates the value you need.

I used an array instead of an equation because mapping an equation to the new values given by the array would not be 100% accurate just like the equation used to map to the original data. You would theoretically need a polynomial that is the degree of the number of data points you have or greater to “best” fit an equation to your set of data.

I opted to use an array instead of an equation to increase accuracy and decrease CPU load as a higher degree polynomial such as the 32nd-degree polynomial I used would not realistic given the number of times it would have to calculate in a loop without delay such as in the user control task.

From 24C/Jordan’s forum post, we calculated our data in the same manner. He however then took two points on the graph of the data he received (one low and one high) to create a linear line from these two points. This would be a linear regression or a first-degree polynomial regression analysis. He repeated this for each value from 1-127. I am not sure if this approach is valid however it seems valid to me. Statistically, I believe a regression of higher order is the best way to linearize this data. I have not tested either one in practice so I cannot attest to the experimental validity of either method, only theoretical.

Sorry for being really nooby on this stuff, but the data in your array, are those the numbers you should command the code to use if you want the true value of 0-127? Or are the values in the array the actual speed values of the motor at each command value from 0-127?

Sorry I have another noob question, how would you implement this into your programming (robotc) to make the motor use these values?

The values in the array from index 0 - 127 are the values that should be given to the motor at that motor value in order to linearize the original trend. So, for example, motor value 100 gets converted to the linearized value by referencing index 100 of the array which would return value 41.

You would use the array given and call the linearized motor speed like this:

motor[motor1] = trueSpeed[abs(motorValue)];

Since the joystick returns negative and positive values, you want to take the absolute value of the joystick input and just multiply it by -1 to get negative values, like this:

motor[motor1] = vexRT[Ch3] >= 0 ? trueSpeed[abs(vexRT[Ch3])] : -trueSpeed[abs(vexRT[Ch3])];
motor[motor2] = vexRT[Ch2] >= 0 ? trueSpeed[abs(vexRT[Ch2])] : -trueSpeed[abs(vexRT[Ch2])];

Thanks! So, just to clarify, if I wanted the motor to truly go at speed 60, I command it to go at speed 23?

Yes, if you want 0 - 127 to be linear to speed, then motor speed 60 would technically be motor speed 23 or index 60 of the array.

For those looking to implement the data, keep in mind these are without a load working against the motor.

Also, nice data collection and process Aidan.

Thank you! Also yes you are correct I forgot to mention this, this assumes that the motors are currently under no load as the test was performed with just a motor encoder and motor not on an actual robot. I will look into a way to easily transform the data based on the weight the motors are currently holding as theoretically adding load should proportionally change the equation.

Just a warning to anyone looking to use an int lookup-table or any large int array, my teams and many others have gotten mysterious “Slave CPU errors” — see this thread for more details:
EDIT: Using a short array instead should work, or a ubyte array as it’s implemented above

Does jpearman’s fix of changing the array to an array of shorts fix the issues? Also, would my use of ubyte fix the issues as well?

Although I haven’t tested it personally, I’ve heard that jpearman’s fix has worked for other teams, so I’d guess that ubyte would work as well (as the issue was with using a global int array).

Yeah while writing it I figured that having a large array of ints may be a problem so I used a ubyte to give it a smaller footprint.

So would this principle apply to a more arcade style control? I attempted it for a while but I kept getting errors in the array. Code below:

motor[D1] = vexRT[Ch3] + vexRT[Ch4] >= 0 ? trueSpeed[abs(vexRT[Ch3]) + (vexRT[Ch4])] : -trueSpeed[abs(vexRT[Ch3]) + (vexRT[Ch4])];
motor[D2] = vexRT[Ch3] - vexRT[Ch4] >= 0 ? trueSpeed[abs(vexRT[Ch3]) - (vexRT[Ch4])] : -trueSpeed[abs(vexRT[Ch2]) - (vexRT[Ch4])];

Those joysticks added together certainty get bigger than 127.

Fixed it lol, didn’t see it until now. Much thanks.

That would be great if you could generate the mappings for the motors under the load!

The easiest way to model dynamic load would be to drive one or a couple of disconnected 393 motors with your test motor. If you have an access to 5W 10-50 ohm resistors you can use them as an easy way to adjust the test load by connecting to the wires of the slave motor.

As you run the tests you may want to monitor the battery voltage. I would plug an external voltmeter into the adjacent motor port, because there were reported issues using nImmediateBatteryLevel.

The best voltage to conduct the tests would be around 7.5v when the battery is slightly discharged and chugging along the long, almost flat, slope in the middle:

Also, you may want to report the load in RPM drop from the idle speed at the same motor control power (or the percentage of the expected max idle speed).

Ideally, you would be able to come up with a set of mappings for 0%, 5%, 10%, 15%, … loads and a function that could interpolate between them when necessary.

The more load there is, the flatter the curve should become. Here is a graph from NbN season that I just posted in another thread:
X-Axis and column B is the desired RPM of the turbo motor under load (max idle angular velocity is expected to be 240 RPM)
Y-values for the blue line and column C is the motor control level that was necessary to achieve that velocity when motors are connected through MC29s. Red line and column D corresponds to ports 1 and 10 and column F is the ratio between the two.

As you can see at about 20% load, our test data shows almost linear dependency between motor control level and output speed in the region between 100 and 190 RPM.

20% or even higher momentary loads are ok, but for the best motor performance you, probably, want to keep the motors operating at 10%-15% load levels. That would be 90-85RPM for the speed or 1.5-2.5 for the output torque on the graph:

The above graph assumes nominal speed of 100 RPM and 10% load would be 10 RPM drop to 90. If you were commanding motors to run at 50 RPM when idle and then added similar load (same resistive torque) it would still result in the drop of approx 10 RPM (because delta RPM corresponds to delta voltage and therefore effective current and output torque). To avoid confusion I would count that 10 RPM drop as 10% load, compared to max idle 100 RPM velocity, and not vs commanded 50 RPM or actual 40 RPM.

Thank you! I use a DC power supply while doing all my test so that the voltage is kept constant throughout the entire duration of my testing.

Thank you for the data, I will definitely take this into account! This just makes me want an oscilloscope more and more. Hopefully after I finish my reserach i’ll make a website to generate an ideal mapping for the specs of each individual robot.