Tinymovr – CAN Protocol, Improving Position Tracking

In the last post I covered improvements in the hardware side of Tinymovr R3.1 that resulted in a minor revision R3.2. This post will go over improvements on the Tinymovr firmware performed in the past couple months. 

CAN Bus Protocol

Implementation of CAN communication is now complete, and Tinymovr is fully operable through CAN. In the protocol I designed I’ve decided to stay as close as possible to the CANSimple protocol, adopted by the ODrive community, for compatibility reasons. Of course, it was not possible to fully conform to that protocol, as there are differences in hardware and features. However, in the approach I’ve taken I’ve tried to match the functionality of critical CAN commands (such as setting of states, setpoints and parameters). In addition, in order to stay compatible I’ve tried to extend the message content of some commands, instead of replacing; for instance, the State endpoint (0x03) returns an array that contains the error as a first element but also the controller state and control mode. If you are interested in the error only, you can just grab the first item in the returned tuple. 

Below is a table with the commands (endpoints) in more detail:

Byte Offset
State0x03RError (uint8)
State (uint8)
Ctrl Mode (uint8)

CAN Config0x05RCAN ID (uint8)
kbaud Rate (uint16)

Set CAN Config0x06WCAN ID (uint8)
kbaud Rate (uint16)

Set State0x07WState (uint8)
Control Mode (uint8)

Encoder Estimates0x09RPosition Estimate(float)
Velocity Estimate(float)

Set Position Setpoint0x0CWPosition Setpoint (float)
Velocity FF (int16)
Current FF (int16

Set Velocity Setpoint0x0DWVelocity Setpoint (float)
Current FF (float)

Set Current Setpoint0x0EWCurrent Setpoint (float)
Set Limits0x0FWVelocity Limit (float)
Current Limit (float)

Iq Estimate and Setpoint0x14RIq Estimate (float)
Iq Setpoint (float)

Limits0x15RVelocity Limit (float)

Vbus0x17RBus Voltage (float)
Position and Velocity Gains0x18RPosition Gain (float)
Velocity Gain (float)

Set Position and Velocity Gains0x19WPosition Gain (float)
Velocity Gain (float)

Device Info0x1ARDevice info register (uint32)
MCU Cycle Timings0x1BRBusy Cycles (uint16)
Total Cycles (uint16)

Save Config0x1CW
Erase Config0x1DW
Motor Info0x1ERMotor Calibrated (uint8)
Motor R (uint16)
Motor Pole Pairs (uint8)
Motor L (float)


The endpoints that are read (R) work by the client sending a request to transmit (RTR) and Tinymovr sending back the corresponding message. Note that this is an early protocol draft, and subject to change.

Position Tracking Improvements

The encoder/observer infrastructure has been vastly improved as well. Here, I’ve been dealing with the problem of diminishing floating point accuracy as the position setpoints reach large values, due to the limited 23-bit significand of single precision floats. This is an issue that has come up from time to time in various message boards, but afaik no concrete solution is available up till now.

To address this issue, there are two sub-problems that need to be addressed: The first problem concerns the estimation of the rotor angle in order to derive electrical angle and ensure accurate commutation. This is easily achieved by using a separate angle estimate that is wrapped to [0, 2pi). The second problem is a bit more complex and entails accurately tracking the rotor position value on the position axis. The issue here is that at large values the estimate of rotor position is increasingly quantized, due to the rapidly diminishing accuracy of floating point representation. Consider as an example the value 5000000 (5*10^6), which is a representable number in IEEE 754 single-precision floating point variable format. The next representable number, is 5000000.5. This is barely adequate to represent the observer rotor position estimate, and it only gets worse as the values increase. In tests that I did at such rotor positions, the effect due to quantization is that the velocity estimate has a greater variance than in values close to zero.

Read also:  Tinymovr: Dealing with an unexpected glitch

One way of fixing this is to use double-precision floats. While offering a greater high-precision position range, this is still not an ideal solution as it still suffers from diminishing precision in large values. The diagram below borrowed from Wikipedia illustrates precision for single and double precision IEEE754 floating point values:

It is evident that there is great variation in precision along the value axis. Accurate encoder positioning does not need more than a few decimal digits, however the floating point format “concerntrates” much precision to areas near zero.

Instead of using double precision floats, the solution I went ahead with is based on a composite scheme that divides the position axis into intervals. In this scheme, two values are used in order to represent a rotor position: one is a 32-bit integer , within which a single-precision float represents the offset of the motor from the middle of the interval. The diagram below gives an idea of the scheme:

Note that FP Precision values are estimates based on the previous diagram. This has vastly improved precision of estimated position tracking performance throughout the position axis. 

An important issue that this development addresses is with velocity controlled motors that are left running for a long time at high velocities, such as wheeled robots or spindles. With the original, single precision positioning scheme, variance of velocity estimates would increase the further away from zero the position gets, which would translate in more noisy motor behavior.

With the new positioning scheme, it should be possible to command any reasonable velocity value and not worry about tracking accuracy (since precision is mostly constant throughout the axis) or position overflow. As an illustrative figure, we may look at the time it takes for a motor position value to overflow at constant angular velocity. Let us consider an interval half-length of 10 * CPR (the MA702 sensor has a max resolution of 12,5 bits, which can be increased to 13bits through the observer, so 10 * 2^13 = 81920), and a rotational velocity of 10000 RPM. Under these assumptions, the time before position overflow would occur is:

81920 * 2^31 / 8192 / 10000 / 60 / 24 / 365 = 4.0857 years

So four years spinning at 10000 rpm with mostly constant multi-digit precision throughout the position axis. Yeah, bearings will have probably worn out to bits and shorted the electronics by that time 😀

Note that the above does not change the precision of commanded position setpoints; these still have single-precision accuracy, with all of it’s precision drawbacks. However, what the new scheme guarantees is that no matter what the setpoint is, it will be tracked accurately.

In addition, this currently does not affect the values returned via the CAN interface; these are still converted to float. However, I’m planning the addition of endpoints to access the precise position values.

Quadruped Robot

On other news: I’ve completed the quadruped robot construction and a series of tests is on the way. Below the robot in “initialization” pose:

The robot is 8DOF (so no holonomic motion yet), and carries 8x Tinymovr boards embedded in the legs. In it’s current state it has off-board power supply and control and no IMU. I’ll be gradually integrating those on-board over the next iterations. The robot weights just 1.8 kilograms (around 4 pounds), which is expected to rise to 1.9-2.0 kg together with a Raspberry pi and small battery.

That’s all for now. I’ve mentioned it a few times 😀 but eventually in the next post I’ll have more information on the availability of Tinymovr R3.2 boards (the latest revision that addresses some power supply issues outlined in the previous post), so stay tuned!

For more exciting experiments and tutorials, subscribe to our email:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.