Position goals not being reached?

Hi there - I’m trying to characterize the performance of some prototype robot joints that I’m designing and have noticed an “issue” with the ODrive not being able to successfully reach the goal position I’m giving it. It feels to me like this is a tuning issue, but it’s not obvious to me where I should be looking to further understand what I’m seeing. Any pointers would be greatly appreciated!

Just a little background – I’m using an ODrive v3.6 with 0.5.1 firmware. I’m driving small Flycat iRotor motors (pathetic little things, but very inexpensive). I’m controlling the ODrive over CAN from an Arduino Mega at the moment. I’ve gone through the tuning process for the motors and am using the following tuning parameters:

So, what I’m doing is playing with my latest joint iteration and it seems to be exhibiting symptoms of sticking. Although after doing a bunch of exercises with it I think what is really happing is it’s having issues positioning the motor to within the error I’ve specified (0.1 turns). If I bump (in my Arduino driver code) the allowable position error to 0.3 turns my tests complete as expected. If I run the following (from odrivetool):

start_liveplotter(lambda:[odrv0.axis1.encoder.pos_estimate, odrv0.axis1.controller.pos_setpoint])

I’m seeing that this joint is fairly consistently stopping movement about 0.2 (or slightly more) turns from the requested position. I’m guessing (but fairly sure) that what’s happening is, as the goal position is approached the vel is decreased to the point it can no longer move the motor. Looking at power supply current, I’m seeing general movement of the motor taking anywhere from about .5 to 1 amp. When the motor stops (about .2 from requested position) the draw remains at about .5 amps. If I then turn the motor by hand (which I can do) the amps drop as I get closer to the goal position. This tells me that the motor controller is still trying to achieve the goal, but is not pushing the motor hard enough to actually cause it to move.

If I add Iq_measured and Id_measured to the plot with the following:

start_liveplotter(lambda:[odrv0.axis1.encoder.pos_estimate, odrv0.axis1.controller.pos_setpoint, odrv0.axis1.motor.current_control.Iq_measured, odrv0.axis1.motor.current_control.Id_measured])

And then run through my characterization process (shown in the linked video)

I get the following liveplot:

It’s a very busy plot (sorry), but I think this confirms that the controller is trying to move the motor to the goal (Iq_measured is 5 amps), but is unable to reach it for some reason. Again, if I give it some help moving to the final position the Iq_measured drops to zero.

For clarity - what my “characterize” process is doing is first moving the joint to one extreme of the movement, waiting for it to get “close enough” (defined by an allowable error tolerance (0.3 turns in this case), then it moves through ten cycles, extending the joint 1/10th max extension, waits for the joint to get there, then back to zero, waits, then 2/10th max extension, etc. Until it gets to max extension on the 10th step. You can see that each step takes longer (which is to be expected, because the joint is moving further). It’s not really possible to see from this graph, but on none of the extensions does the joint ever actually get to the goal position, but on the last one it is fairly obvious that it’s about 0.2 turns shy of the correct position. And you can see that the controller is still pushing 5amps through the motor to try to close that last little gap.

So, why is the controller able to move the motor larger distances, but gets ‘hung up’ on the final little bit? Note, this is a full 1/5th to 1/4 of a turn of the motor, so I wouldn’t think it would be a cogging problem.

Thanks again for taking the time to read all this!

EDIT – meant to mention that I’m using the CAN SetInputPos cmd to initiate the movements (with VelFF and TorqueFF zero). The arduino is monitoring position with CAN getEncoderEstimates.EncoderPosEstimate. Controller Modes are CONTROL_MODE_POSITION_CONTROL, INPUT_MODE_PASSTHROUGH.

Right off the bat, because the ODrive uses a cascade control, if the velocity integrator is zero, then there is no position integrator and you will have steady state offset equal to the equivalent of a “normal” PID with

Kp = pos_gain * vel_gain

Set vel_integrator_gain to a non-zero value.

For general tuning, I would start by sending velocity step commands with pos_gain = 0 and tuning the vel gains to be nice and tight first, then tune the position gain.

So, why is the controller able to move the motor larger distances, but gets ‘hung up’ on the final little bit?

This is expected behaviour from control theory with “pure” proportional control. It’s called steady-state error.


Thanks for the pointers Wetmelon! It will likely take me a few days to investigate and get back with any progress. I really appreciate the help.

As suggested, the zero vel_integrator_gain was the problem. I’m sure no one was thinking otherwise, or losing sleep over it – but thought I should follow through and report that was the issue…

Thanks again Wetmelon for a quick and accurate diagnosis! Also, thanks for that video link… I ended up watching his whole series (26 or so videos) - lot’s of math that I haven’t touched in the last 35 years or so. It took a little to get back into the groove, but the video creator does a really great job at presenting the material. IMO well worth the time invested in watching them.

1 Like