Adding/subtracting two encoder inputs to control a single motor

Hi all,

I’m wondering if it’s possible to use position inputs from two encoders to generate the error signal for a single motor (i.e. by addition/subtraction of two encoder inputs)

The reason for this is that I am designing a robot arm, and want to have encoders mounted directly to each joint axis (i.e. not directly to the motors), but two of my joints (J5 and J6) are coupled with a differential drive arrangement.
The result is that if you drive both motors with the same direction/speed, only J5 rotates, and if you drive the motors in opposite directions, only J6 rotates.
This produces the following relationship: (units are degrees)

J5/6 Motor A Error = (J5 Encoder Error + J6 Encoder Error)/2
J5/6 Motor B Error = (J5 Encoder Error - J6 Encoder Error)/2

Maybe the stock firmware isn’t capable of this, but I don’t mind modifying the firmware to enable this functionality. I am really just wondering if this is possible.

Best regards,

I’ve had a look at low_level.c, and found control_motor_loop and the error computation line:

float pos_err = motor->pos_setpoint - motor->encoder.pll_pos;

I can add another encoder to the motor_t type in low_level.h, then maybe add another enum into the motor_t type to allow selection of encoder 1, encoder 2, or the sum or difference of the encoders, then a case statement to select between the different error computation lines.

I’m not really understanding the actual reference to the encoder input. Is it the encoder timers &htim3 and &htim4?
Just not sure how the encoder inputs work.

Are there any other interactions that I’m missing here?

Thanks in advance.


using coreXY to run a CNC machine has a similar effect, if you run the motors in one direction you get vertical movement, if you move in opposite directions you get horizontal movement.

There are two ways to handle this situation

  1. know how much each motor has moved, and calculate the sum/difference of them to figure the position

  2. know the movement and reverse the calculation to figure out how much each motor has moved.

the vast majority of people take the first approach, it works with open-loop steppers as well as closed loop devices.

you are wanting to do the second, the firmware doesn’t support that currently

Thanks for your post David.
You’ve pretty much captured my predicament.

I was tossing up between putting the encoders (scenario 1) on the motors, and putting the encoders on the joints (scenario 2).

I could easily put the encoders on the motors, but then there is some unknown belt slop/play that goes unaccounted for. It may only be worth 0.5mm of movement in the robot’s end effector, but I would really like to avoid this loss of precision if possible. It might mean the difference between overall positioning repeatability of 0.6mm (scenario 1), and 0.1mm (scenario 2), which would be significant. And the improvement comes at almost no cost (other than a few hours of my time coding).

Regarding the firmware, I guess I would also have to modify the motor electrical angle calculation code to factor in the new kinematics as well? And also the homing code (which might be a little tricky).


I think scenario 2 is okay in general, the belt slop projected onto the motors electrical angle just has to be better than 20 degrees electrical, but I think that’s easy to achieve. The main issue is to restore the electrical angle calibration. On a robot arm you don’t want to do the calibration sequence on every startup, and deriving the absolute motor angle from joint angles on startup will be your main challenge I suspect.

Actually on ODrive v3.5 I have made available CH1 and CH2 of both TIM2 and TIM5 on the GPIO pins. I can’t make any promises that there won’t be something in the way, because we haven’t tested this yet, but I suspect we will be able to accommodate 2 encoders per axis. Maybe this will solve your issues?

Another way to deal with this is to get a sensored (Hall sensors) motor, and read the values at startup to initialise the commutation.

Oskar, do you know if the CUI absolute encoders are actually absolute (i.e. retain position between despite power loss) or if they’re called absolute because they output position instead of a quadrature output (i.e. they keep track of and output position information over SPI/RS485 etc, but still lose their position on power loss)?

I understand the ODrive only takes quadrature inputs, but it’s interesting to me nonetheless. It would be nice if a future version of the robot could run immediately on power-up without any kind of initialisation sequence.

The electrical angle of the motor will have no direct correlation to the encoder position, you will have to compute back what an encoder attached to the motor would read and treat that as the angle.

Hi David,

Are you David Lang of OpenROV fame? Did you come to Mini Maker Faire Sydney a few years ago? If so, there’s a good chance we met.

Yes, I’m aware that in such a mechanism, the position of any one encoder cannot be used to determine the electrical angle of the motor, but if you have both encoder positions and motor angle offsets (discovered during calibration), you should be able to compute electrical angle for both motors for any combination of encoder positions.

I think that’s what you’re saying right?

Thanks for your help.


Absolute encoders in this case means that they can recover their absolute angle even after power loss. They can’t however count turns while powered off. That is, with these encoders on the motors you can energize the system and have full operation without any other movement at startup, but if you have multiple motor turns in your joint range, it is ambigous where the joint is.

The best solution I’ve seen to this problem is to 1. have absolute encoder at the motor, 2. have an inexpensive potentiometer at the joint to resolve the integer turn ambiguity at the motor. This will let your arm be ready to go immediately on startup.

If you need the encoder at the output of the joint, I’d suggest to use something like an AS5311 and a potentiometer together (pot to solve the integer cycle ambiguity), and another absolute or index pulse encoder at the motor. The AS5047P is inexpensive an compact and a good choice for an absolute encoder for the motor. In this dual encoder configuration you can do position feedback on the joint encoder, and velocity feedback on the motor encoder, to get you the best of both worlds: good joint output positioning, and only having to do velocity control on the near-side of the belt compliance.

Thanks for the reply Oskar. I did read it immediately after you posted, but completely forgot to reply. My apologies.

As for the AS5311:
I like the magnetic strip idea, but the AMT102’s hollow-shaft mount is probably ideal, as I typically have a lot of space left on the shaft, and it’s just simplest to install, as there’s no extra fabrication required to support the AMT102.

Do you recommend the AS5311 and AS5047P because they are cheaper than 2x AMT102?

I take this back: I realized that TIM2 is taken already by the brake resistor PWM. I can change the topology of the brake half-H bridge to just low-side switching and freewheel diode, possibly on ODrive v3.6. But for now, yeah we can’t do dual encoders, at least not on both axes.

To answer your question: I just like the AS5311 together with the 128 pole magnet ring, it has very high resolution (16 bit).

I hadn’t seen the magnet ring before… looks really tidy. In fact, do you think it might be more compact (along the shaft axis) than an AMT102?

Now that I think of it, the off-axis design might actually make it assembly/disassembly easier than using the AMT102.

10 posts were merged into an existing topic: Where to source magnetic encoder and ring?