Build a Dyno

Another robot dinosaur? No, a dynamometer. A what? It’s a test machine to measure motor performance. It provides a load against the motor under test (MUT), varies the MUT’s speed, and measures the speed and torque. With a dyno you can determine the strength of each of your 393 motors, and find out how it’s geared without opening it up. There is a clamp to hold the 393 in place without screwing it down, saving time and wear when testing many motors.

This simple dyno uses gravity to provide the load and a VEX Shaft Encoder to measure position over time. From the position measurements you can calculate speed and torque. The encoder measures the angle from vertical in degrees. Speed is the change in angle divided by the change in time, scaled to revs per minute (RPM). Torque is the sine of the angle times the moment arm of the pendulum, scaled to Newton-meters (Nm). Moment arm is the weight of each piece of the pendulum times the distance from the pivot to the center of gravity.

Details to follow, including the RobotC source code.


Nice! An off season project for our teams is to test motor torque and speed in various combinations. This is a great way to accomplish this! Thanks for sharing!!

I was thinking about a motor tester of sorts as well, but applying an increasing load until the motor stalls, creating an index number for each motor which can be monitored/tracked throughout the years to determine when to take a motor out of service.

That was my initial thought too, but after working with the dyno, I found that the PTC interferes too soon with the motor characterization if you let it stall. What I have now repeatedly varies the load up to 0.39 Nm as the pendulum turns, keeping the PTC happy. The software then averages and extrapolates the torque-speed curve to find stall torque and no-load speed.

The dyno could be adapted to do what you suggest, so as to characterize the PTC as well.

Based on my method of “testing motors” after I have made repairs, which is sticking an axle and wheel into the motor and running it while feeling how strong it turns the wheel (or how easily it stalls), my totally non-reproducible and subjective method makes me think that PTC variation, based on how easy it is to stall some motors, is one of the biggest problems for our teams.

I agree. So there will likely be future development toward that goal.

I’ll post here soon what I have so far.

Here’s a sample run on a 393 geared for torque, at max power of 127. The horizontal axis is speed in RPM, and the vertical is torque in Nm. Blue points are the speed and torque values calculated as above. Red are supposedly corrected for inertia of the pendulum, since it is slowing and speeding as it turns. I was hoping the inertia correction would reduce the loopiness of the data, but I was careful with signs and they only got more loopy. It doesn’t seem to matter, though, because the best-fit lines (via linear regression) through both datasets are nearly identical.

Extrapolating the best-fit line to the vertical axis (the y-intercept) should give you the stall torque of the motor, that is, the torque at zero speed. Also, the horizontal crossing (the x-intercept) gives the no-load speed, that is, the speed at zero torque.

But is the torque-speed curve really linear? There does appear to be a slight downward curve, especially at negative torques (higher speeds). I think this is due to increased friction of the gears (both internal and external) at higher speeds, and friction generally goes by the square of speed. So I modified the linear regression to apply only to positive torques in hopes of minimizing the non-linearity. Still, using best-fit lines on curved data means that the stall torque and no-load speed calculations will be somewhat over-estimated. While not exact, they will be useful in comparing motors with one another.

I ran the finished dyno 3 times on each of 10 random motors. The spreadsheet shows the results. I am satisfied with the repeatability of better than 5% between runs on a given motor. It is easy to tell a torque-geared motor from a speed motor. I have not yet tried a turbo-geared motor. Here is a scatter plot of the 30 runs.
127.xls (86 KB)

I’m curious how the performance of a motor compares in forward and reverse.

Here’s the code:

#pragma config(Sensor, dgtl1, Encoder, sensorQuadEncoder)
#pragma config(Motor, port1, testMotor, tmotorNormal, openLoop)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

|*                              - Measure Torque and Speed with Encoders -                            *|
|*                                      ROBOTC on VEX 2.0 CORTEX                                      *|
|*                                                                                                    *|
|*    MOTORS & SENSORS:                                                                               *|
|*    *          [Name]              [Type]                [Description]                     *|
|*    Motor   - Port 1    testMotor           VEX 3-wire module     Motor under test                  *|
|*    Digital - Port 1,2  Encoder             VEX Shaft Encoder     Angle of weight                   *|

int a[200];

task main()
  wait1Msec(200);  // Delay
  SensorValue[Encoder] = 0;  //Clear Encoders
  int bb = nImmediateBatteryLevel;

  motor[testMotor] = 127;
  for (int i=0; i<160; i++)
    a* = SensorValue[Encoder];  // Sample Encoder
    wait1Msec(100);  // at 10 Hz
  motor[testMotor] = 0;

  float mom = 0.390;  // Nm
  float sumSp=0, sumS2=0, sumTq=0, sumST=0, num=0;
  writeDebugStream("sec deg RPM Nm\n");
  for (int i=8; i<158; i++)
    float sp = (a*-a*)*2.083;  // RPM
    float tq = sinDegrees(a*)*mom;  // Nm
    writeDebugStream("%3.1f %d %1.0f %5.3f\n", i*0.1, a*, sp, tq);
    if (tq>=0)  // Linear regression only on positive torques
      sumSp += sp;
      sumS2 += sp*sp;
      sumTq += tq;
      sumST += sp*tq;

  writeDebugStream("Batt: %4.2f - %4.2f\n", bb*0.001, nImmediateBatteryLevel*0.001);
  if (sumS2==0)
    writeDebugStream("No motion!\n");
    float m = (num*sumST-sumSp*sumTq)/(num*sumS2-sumSp*sumSp);  // Slope
    float b = (sumTq-m*sumSp)/num;  // Intercept
    writeDebugStream("Free speed: %2.0f RPM\n", -b/m);
    writeDebugStream("Load speed: %2.0f RPM at %4.2f Nm\n", (mom-b)/m, mom);
    writeDebugStream("Stall torque: %4.2f Nm\n", b);

First it clears the encoder. It is important to make sure the pendulum is hanging straight down before running, so as to get a good zero angle reference. Then it records the battery voltage and turns on the motor at full power. For 16 seconds it records the angle every tenth of a second, then turns off the motor.

Next it initializes the linear regression sums. It skips the startup readings and analyzes 150 data points. Speed is found at each time step by subtracting the angle at 0.2 seconds in the past from the angle at 0.2 seconds in the future, like a moving average over a 0.4 second period. This gives a speed resolution of about 2 RPM. Torque at each time step is the sine of the angle times the moment. For each step it writes time, angle, speed, and torque to the DebugStream window, and adds torque and speed to the regression sums if torque is positive.

Once all data are in, it calculates slope and y-intercept of the best fit line, y = mx + b . It then writes out the battery voltages before and after the run, the free (no-load) speed, the speed at maximum dyno load, and the extrapolated stall torque. The DebugStream window in RobotC allows you to save all this to a text file.

Please post any questions if something is unclear.

Good question. I have not got that far yet. When I accidentally plugged a motor in backwards, I got a bad result, probably because of the positive torque filter.

maybe you could add second motor mount with a reversing gear, so the motor could be moved from one station to the other for testing in both directions. Granted there would be a bit of extra friction, but it shouldn’t be much to change the test results.

Or just add a reverse run to the software?

On my cocktail* napkin sketch where I’m stretching a length of surgical tubing until the motor stalls, I have to mechanically reverse the motor. Yea, software would be easier.

*“diet coke” for the kids out there…

This is how the speed calculation is scaled:

S = ( delta A in degrees ) / ( delta T in seconds ) / ( 360 degrees / rev ) * ( 60 seconds / minute ) * ( 5 gear ratio)
  = ( A(T+0.2) - A(T-0.2) ) / 0.4 / 360 * 60 * 5 rev / minute
  = ( A(T+0.2) - A(T-0.2) ) * 2.083 RPM

This is how the moment arm is calculated, given the weight of a 35x5 steel c-channel as 0.678 lb, according to the VEX site, and the distances from the pivot to the center of each c-channel as 8.5" and 17":

M = 0.678 lb * ( 8.5 inch + 17 inch ) / ( 0.225 lb / Newton ) * ( 0.0254 meter / inch ) / ( 5 gear ratio )
  = 0.390 Nm

Thanks for all the hard work. That’s great detail.

I have a couple of questions and an embedded suggestion (power expander for increased input voltage resolution) and a request.

  1. Have you used this rig to characterize the output torque of a single motor over multiple power settings? If so, how does it look? Based on previous work by @jpearman and others, I would expect the motor output to stabilize at some input value significantly below 127. I think they saw that it stabilized at 90 or so. It would be interesting to see whether your measurements show something similar.
  2. Have you considered using a power expander to get more resolution in the battery voltage? It would require you to use two batteries (one just to power the cortex and one to power the motor through the power expander) but it would give you many more bits of accuracy in the battery voltage.
  3. Bringing together 1 and 2, have you considered measuring the effect of input voltage on the torque output? If you hooked a bench supply up to the power expander, you could more accurately measure the motor supply voltage. You could then characterize a single motor over a sweep of input voltages. Sounds worth doing, though I know that wasn’t your initial goal. And if you used a bench supply with digital control, you could use the cortex to drive the the voltage sweep.

Request: Could you post or attach an example of the raw output from the debugStream? That would be interesting to work with, I think.


  1. I have not yet. Yes, it would be interesting to correlate with previous work.

  2. I was not aware of that. Could the Cortex then be powered solely by USB?

  3. Good ideas. Sounds like a summer project for a team.

Attached are 3 runs from the same motor. (4.03 KB)

Here are some relevant threads for work @jpearman did in the past. For the motor speed mapping, look at “motor speed testing” and the extension of that work by @LegoMindstormsmaniac to create a nice look up table. Also, for torque/speed curves, check out the “rev2” thread for better info, the original thread for background, and the “estimating…” thread for good discussion. But all the discussion is good.

motor speed testing:

@LegoMindstormsmaniac’s work:




Unfortunately, no. It won’t drive output ports when powered by the USB or the 9v backup battery.

As to the increased resolution of the power expander, you can read about it on the second page of this document:

And below is an example task we wrote at EC3 to demonstrate a couple of things. The comments are wordy, because it was meant to explain things to the students.

task monitorPower()
    // declare some string variables to hold the formatted information
    string mainBattery;
    string auxBattery;

    // turn the integer value representing main battery millivolts into
    // a floating point value representing volts. Then, format the
    // the information for printing. The "%f" in the format string will be
    // replaced by the floating point number calculated by the division.

    sprintf(mainBattery," %f Volts",(float)nImmediateBatteryLevel/1000.0);

    // The power expander has a voltage sensor port. We plugged a
    // jumper wire (VEX extension cable) from the sensor port  on the
    // power expander to the analog sensor port # 2 on the Cortex.
    // The value read by the sensor must be divided by 270 in order to
    // convert it into volts. Also, we named the port "power". So:
    // turn the integer value from sensor port #2 into a floating point value
    // representing volts of the battery plugged into the power expander.
    // Then, format the information for printing. The "%f" in the format
    // string will be replaced by the floating point number calculated by the
    // division.

    sprintf(auxBattery," %f Volts",(float)SensorValue[power]/270.0);


    // wait 1 second before reading the volts again. Just because.


Yep, PTCs are, shall we say, sensitive.

Yes, my son showed me that some years ago and it got me thinking about building a dyno but I never got around to it until now. (BTW, my son presented Mr. Pearman with the VRC Mentor of the Year Award at Worlds 2015 (the year after our team The VEX Raptors won World Excellence).)

Thanks for those other links too. There’s certainly more work to do, and I hope this dyno can help. Its simple design is within reach of most teams and does not require expensive lab equipment.