Tuning a hoverboard type hub motor with external encoder

hw: 3.6
sw: 0.5.1
motor: hoverboard style w/ 15 pole pairs (nominal 36V, used at ~24V)

We are trying to use a Hoverboard motor (6 inch, 15 pole pairs) in a differential drive robotics project. Since accuracy was a concern we added an official 8192 cpr CUI AMT102 encoder to the drive axis.

Using velocity control, the problem is when we tune the control loop to be stable without load, the motor torque will lag along when we put down the robot and apply load. On the other hand when we tune the parameters to work under load, the motors are visibly or audibly oscillating (practically forever) when the load is not present, for example the wheels are temporarily not touching the ground.

The main parameters we experimented with are the following:

  • Velocity gain
  • Velocity integrator gain
  • Current bandwidth
  • Encoder bandwidth

As stated in a forum post, in the odrive’s cascaded control loop there should be a 3-10x difference between the loop bandwidths, the inside one being the highest. During our testing we couldn’t move the motors when the current control bandwidth exceeded ~200; But the encoder bandwidth could be increased up to ~3000. Increasing the encoder bandwidth made the motor oscillation frequency higher.

We also tried the anticogging feature, but that didn’t help either.

There are two types of errors we get frequently: ERROR_CURRENT_MEASUREMENT_TIMEOUT
ERROR_CONTROL_DEADLINE_MISSED

Tuned to best work without load but lags when there is load:

(!) => tune_params(odrv, pos_gain = 0, vel_gain = 7, vel_integrator_gain = 20)
axis.controller.config.pos_gain = pos_gain
axis.controller.config.vel_gain = vel_gain
axis.controller.config.vel_integrator_gain = vel_integrator_gain
axis.motor.config.pole_pairs = 15
axis.motor.config.resistance_calib_max_voltage = 10
axis.motor.config.calibration_current = 5
axis.motor.config.requested_current_range = 25
axis.motor.config.current_control_bandwidth = 100
axis.motor.config.torque_constant = 8.27 / 10
axis.motor.config.torque_lim = 9999999999
axis.encoder.config.mode = ENCODER_MODE_INCREMENTAL
axis.encoder.config.cpr = 8192
axis.encoder.config.calib_scan_distance = math.pi * 4 * 12
(!) => axis.encoder.config.bandwidth = 300
axis.config.calibration_lockin.vel = 10
axis.controller.config.vel_limit = 100
axis.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL

Tuned to best work with load but oscillates when there is no load:

(!) => tune_params(odrv, pos_gain = 0, vel_gain = 30, vel_integrator_gain = 150)
axis.controller.config.pos_gain = pos_gain
axis.controller.config.vel_gain = vel_gain
axis.controller.config.vel_integrator_gain = vel_integrator_gain
axis.motor.config.pole_pairs = 15
axis.motor.config.resistance_calib_max_voltage = 10
axis.motor.config.calibration_current = 5
axis.motor.config.requested_current_range = 25
axis.motor.config.current_control_bandwidth = 100
axis.motor.config.torque_constant = 8.27 / 10
axis.motor.config.torque_lim = 9999999999
axis.encoder.config.mode = ENCODER_MODE_INCREMENTAL
axis.encoder.config.cpr = 8192
axis.encoder.config.calib_scan_distance = math.pi * 4 * 12
(!) => axis.encoder.config.bandwidth = 300
axis.config.calibration_lockin.vel = 10
axis.controller.config.vel_limit = 100
axis.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL

What parameters are we missing? How can we have a stable system with suitable torque?

Here you can take a look at our testing data with different gain and bandwidth values. (You can open it after download.)

Cheers