Hoverboard motor + AMT102 incremental encoder

Hello,

I spent a couple of hours on the discourse searching for a solution for my problem, but I am yet to find something.

I am trying to configure Odrive for a hoverboard motor, but I want to use an incremental encoder instead of the built-in HAL encoder.
I configured the motor according to the ODrive documentation (from git-hub tag fw-0.4.11), but for the encoder configuration I used:

  • odrv0.axis0.encoder.config.mode = ENCODER_MODE_INCREMENTAL
  • odrv0.axis0.encoder.config.cpr = 8192
  • odrv0.axis0.encoder.config.bandwidth = 1000

The calibration for the motor and the encoder are successful (the motor beeps during the motor calibration, and it spins during the encoder calibration), but when I try to test the velocity loop control, the motor does not spin.

Any suggestion what I did wrong, or what do I have to configure extra?
I would prefer not to update for now to the latest firmware, because my robot control SW relies on the FW 0.4 API.

I have the following setup:

  • ODrive HW v3.6.56
  • ODrive FW v0.4.11
  • 10S LiPo (42v) as power supply
  • 2 Ohm break resistor
  • AMT102 incremental encoder

After configuration I have the following config:
Controller stats:

config:
  control_mode: 2 (uint8)
  pos_gain: 1.0 (float)
  setpoints_in_cpr: False (bool)
  vel_gain: 0.019999999552965164 (float)
  vel_integrator_gain: 0.10000000149011612 (float)
  vel_limit: 1000.0 (float)
  vel_limit_tolerance: 1.2000000476837158 (float)
  vel_ramp_rate: 10000.0 (float)
current_setpoint: 0.0 (float)
error: 0 (uint8)
move_incremental(obj: object_ref, displacement: float, from_goal_point: bool)
move_to_pos(obj: object_ref, pos_setpoint: float)
pos_setpoint: 0.0 (float)
set_current_setpoint(obj: object_ref, current_setpoint: float)
set_pos_setpoint(obj: object_ref, pos_setpoint: float, vel_feed_forward: float, current_feed_forward: float)
set_vel_setpoint(obj: object_ref, vel_setpoint: float, current_feed_forward: float)
start_anticogging_calibration(obj: object_ref)
vel_integrator_current: 0.0 (float)
vel_ramp_enable: False (bool)
vel_ramp_target: 0.0 (float)
vel_setpoint: 120.0 (float)

Motor stats:

DC_calib_phB: -0.7780774235725403 (float)
DC_calib_phC: -0.34574565291404724 (float)
armed_state: 0 (uint8)
config:
  calibration_current: 10.0 (float)
  current_control_bandwidth: 100.0 (float)
  current_lim: 10.0 (float)
  current_lim_tolerance: 1.25 (float)
  direction: -1 (int32)
  inverter_temp_limit_lower: 100.0 (float)
  inverter_temp_limit_upper: 120.0 (float)
  motor_type: 0 (uint8)
  phase_inductance: 0.0004055439494550228 (float)
  phase_resistance: 0.22742202877998352 (float)
  pole_pairs: 15 (int32)
  pre_calibrated: True (bool)
  requested_current_range: 25.0 (float)
  resistance_calib_max_voltage: 4.0 (float)
current_control:
  I_measured_report_filter_k: 1.0 (float)
  Ibus: 0.0 (float)
  Id_measured: 0.0 (float)
  Iq_measured: 0.0 (float)
  Iq_setpoint: 0.0 (float)
  final_v_alpha: 0.0 (float)
  final_v_beta: 0.0 (float)
  i_gain: 22.742204666137695 (float)
  max_allowed_current: 30.375 (float)
  overcurrent_trip_level: 33.75 (float)
  p_gain: 0.04055439680814743 (float)
  v_current_control_integral_d: 0.0 (float)
  v_current_control_integral_q: 0.0 (float)
current_meas_phB: 0.05323946475982666 (float)
current_meas_phC: 0.0025625526905059814 (float)
error: 0 (uint16)
gate_driver:
  drv_fault: 0 (uint16)
get_inverter_temp(obj: object_ref) -> result: float
is_calibrated: True (bool)
phase_current_rev_gain: 0.012500000186264515 (float)
thermal_current_lim: 43.43867492675781 (float)

Encoder stats:

calib_scan_response: 0.0 (float)
config:
  bandwidth: 1000.0 (float)
  calib_range: 0.019999999552965164 (float)
  calib_scan_distance: 50.26548385620117 (float)
  calib_scan_omega: 12.566370964050293 (float)
  cpr: 8192 (int32)
  enable_phase_interpolation: True (bool)
  find_idx_on_lockin_only: False (bool)
  idx_search_unidirectional: False (bool)
  ignore_illegal_hall_state: False (bool)
  mode: 0 (uint8)
  offset: -2328 (int32)
  offset_float: -0.18335938453674316 (float)
  pre_calibrated: False (bool)
  use_index: False (bool)
  zero_count_on_find_idx: True (bool)
count_in_cpr: 0 (int32)
error: 0 (uint8)
hall_state: 0 (uint8)
index_found: False (bool)
interpolation: 0.5 (float)
is_ready: False (bool)
phase: 1.6584234237670898 (float)
pos_cpr: 0.0 (float)
pos_estimate: 0.0 (float)
set_linear_count(obj: object_ref, count: int32)
shadow_count: 0 (int32)
vel_estimate: 0.0 (float)


EDIT

I noticed that the odrv0.axis0.encoder.config.pre_calibrated is always False, even if I specifically set it to True.
From another post I found out that I need to use index for AMT102; so I set:

odrv0.axis0.encoder.config.use_index = True
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH
# I can notice here the motor moves until the index is found. Also idnex_found = True
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
# I can notice that the motor rotates again to perform the calibration
odrv0.axis0.encoder.config.pre_calibrated = True
# After this, the pre_calibrated variable is set to True and is persistent after reset
odrv0.save_configuration()
odrv0.reboot()

But when I try again:

odrv0.axis0.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis0.controller.vel_setpoint = 10

The motor moves a bit (3 degrees) but then it abruptly stops, and I see that the motor has the error 16 (ERROR_CONTROL_DEADLINE_MISSED)

Motor stats

DC_calib_phB: -0.7711188197135925 (float)
DC_calib_phC: -0.3776278793811798 (float)
armed_state: 0 (uint8)
config:
  calibration_current: 10.0 (float)
  current_control_bandwidth: 100.0 (float)
  current_lim: 10.0 (float)
  current_lim_tolerance: 1.25 (float)
  direction: -1 (int32)
  inverter_temp_limit_lower: 100.0 (float)
  inverter_temp_limit_upper: 120.0 (float)
  motor_type: 0 (uint8)
  phase_inductance: 0.0003835245734080672 (float)
  phase_resistance: 0.2256225347518921 (float)
  pole_pairs: 15 (int32)
  pre_calibrated: True (bool)
  requested_current_range: 25.0 (float)
  resistance_calib_max_voltage: 4.0 (float)
current_control:
  I_measured_report_filter_k: 1.0 (float)
  Ibus: 0.28113868832588196 (float)
  Id_measured: -0.9875475168228149 (float)
  Iq_measured: 3.9099690914154053 (float)
  Iq_setpoint: 6.422600746154785 (float)
  final_v_alpha: 1.7034990787506104 (float)
  final_v_beta: 0.9837055802345276 (float)
  i_gain: 22.562253952026367 (float)
  max_allowed_current: 30.375 (float)
  overcurrent_trip_level: 33.75 (float)
  p_gain: 0.03835245594382286 (float)
  v_current_control_integral_d: 0.11580724269151688 (float)
  v_current_control_integral_q: 1.8720605373382568 (float)
current_meas_phB: -0.014534950256347656 (float)
current_meas_phC: -0.00539824366569519 (float)
error: 16 (uint16)
gate_driver:
  drv_fault: 0 (uint16)
get_inverter_temp(obj: object_ref) -> result: float
is_calibrated: True (bool)
phase_current_rev_gain: 0.012500000186264515 (float)
thermal_current_lim: 44.876373291015625 (float)

Any ideas?

Please send the set of command you used to calibrate and move the motor.

Erase config
Set motor pole pairs
Full calibration sequence
Controller config control mode = velocity
Increase velocity limit
Enter closed loop control
Run motor

Hello @Wetmelon,

Here are the commands I used for configuration and test:

odrv0.erase_configuration()
odrv0.reboot()
# Motor Configuration
odrv0.axis0.motor.config.pole_pairs = 15
odrv0.axis0.motor.config.resistance_calib_max_voltage = 4
odrv0.axis0.motor.config.requested_current_range = 25
odrv0.axis0.motor.config.current_control_bandwidth = 100
# Encoder configuration
odrv0.axis0.encoder.config.mode = ENCODER_MODE_INCREMENTAL
odrv0.axis0.encoder.config.cpr = 8192
odrv0.axis0.encoder.config.use_index = True
# Controller configuration
odrv0.axis0.controller.config.pos_gain = 1
odrv0.axis0.controller.config.vel_gain = 0.02
odrv0.axis0.controller.config.vel_integrator_gain = 0.1
odrv0.axis0.controller.config.vel_limit = 1000
odrv0.axis0.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL

odrv0.save_configuration()
odrv0.reboot()

# Motor Calibtration
odrv0.axis0.requested_state = AXIS_STATE_MOTOR_CALIBRATION
odrv0.axis0.motor.config.pre_calibrated = True

# Encoder Calibration
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH
odrv0.axis0.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
odrv0.axis0.encoder.config.pre_calibrated = True
odrv0.axis0.config.startup_encoder_index_search = True

odrv0.save_configuration()
odrv0.reboot()

# Test Motor
odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis0.controller.vel_setpoint = 10

After I set the vel_setpoint to 10, and read odrv0.axis0.motor.error I get the error code 16

If I dump errors I get the following output:

In [12]: dump_errors(odrv0)
system: not found
axis0
  axis: Error(s):
    UNKNOWN ERROR: 0x00000200
  motor: Error(s):
    MOTOR_ERROR_CONTROL_DEADLINE_MISSED
  sensorless_estimator: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_OVERSPEED

It seems there is also a CONTROLLER_ERROR_OVERSPEED, can this be caused by a misconfiguration of the controller PID?

Hmm, in the old firmware since you’re still in Counts, you need to increase vel_limit quite a bit. 1000 counts is 1/8 of a turn per second. And a command of 10 is … 1/80? Of a turn per second

@Wetmelon, I tried with higher vel_limit values of 10 000 and even 100 000, and I can definitely see a difference; now the motor starts shaking.
I also played a bit with vel_gain and vel_integrator_gain but without success.
I got desperate and updated to the firmware 0.5.4, but with this version the motor turns for about 3-4 seconds and then it stops.
When I switch back to built in HAL sensors, the motor turns smoothly, so it is for sure some misconfiguration related to the encoder.