No torque in torque mode

Try increasing that calibration_current to 20

same behaviourā€¦ Resistance out of rangeā€¦ Also tested it with 30A.

Try adjusting the values here https://docs.odriverobotics.com/api/odrive.motor.error

Yeah, thank you for that hint! Calibration completed successfully. I set resistance_calib_max_voltage = 6 because calibration_current is 10A and i rougly measured 0.5 ohm resistance. The result of the motor calibration:

phase_inductance = 7.907329563749954e-05 (float)
phase_resistance = 0.1924062967300415 (float)

Are these values in a plausible range? When i measured the resistance between each of the three phases, i measured 0.5 ohm with my cheap multimeter.

Unfortunately, now i have the torque mode problems again. When activating closed loop control with torque mode and input_torque = 0 everything is stable and nothing happens. When i now slightliy rotate the motor manually, immediately the following errors appear:

axis: Error(s):
    AXIS_ERROR_CONTROLLER_FAILED
  motor: Error(s):
    MOTOR_ERROR_CONTROL_DEADLINE_MISSED
  fet_thermistor: no error
  motor_thermistor: no error
  encoder: no error
  controller: Error(s):
    CONTROLLER_ERROR_OVERSPEED

The same happens as soo as i set input_torque = 1.

Increasing the vel_limit helped, but i am still wondering why the encoder measures such i high velocity, although the wheel is barely turning. I am assuming the reason could be the low resolution of the hall sensor?

As so can see here, when i turn the motor manually, i only get vel_estimates in multiples of around 3 (vel_estimate_counts is around 120). My CPR is 42, so ~120 / 42 = ~3. Does that mean my best resolution i can get of a velocity is 3 U/s?

Maybe phase_resistance is talking about the resistance of one phase, not two.
When you measure with your multimeter, you are measuring two phases in series (for a star-wound motor, which are the vast majority of motors)
In that case, you should expect to see 0.25 Ohm, and 0.198 is not so far off.

As for your velocity issueā€¦ Check if encoder.config.enable_phase_interpolation is set.

That is normal with hall effect sensors. The encoder estimator acts like a filter, so you can change odrv0.axis0.encoder.config.bandwidth to get better low speed behavior. The stock value is 1000.0f (radians/s I think? might be Hz), a lower value will give better low speed behavior.

The problem is - the ODrive will have executed many, many, many, control loop cycles between one hall effect count and the next one. Itā€™s basically like smacking a train car with a hammer to get it to move, but the train car is so light that it starts and stops between hammer-smacks. Bandwidth here is like train car weight.

1 Like

Has this issue been solved? I can not find the solution above. I am experiencing the same problem: torque control mode is not working.

I have an odrive v3.6 56V and a 25V LiPo battery as power source. Fully charged, odrv0.vbus_voltage = 26V.
The green LED is on, and the 3.3V and 5V signals from the board are OK.
22nF capacitors connected to the Hall sensor inputs of the board.

I am first following the Getting Started guide.
By accident I do have a hoverboard BLDC motor with internal Hall sensors, with the exact connections like in the guide. I am using the following commands to set axis1 into velocity control, which works fine:

odrv0.erase_configuration()

odrv0.config.enable_brake_resistor = False
odrv0.config.dc_max_negative_current = -1
odrv0.config.max_regen_current = 40

odrv0.axis1.motor.config.pole_pairs = 15
odrv0.axis1.motor.config.resistance_calib_max_voltage = 4
odrv0.axis1.motor.config.requested_current_range = 50
odrv0.axis1.motor.config.current_control_bandwidth = 100
odrv0.axis1.motor.config.torque_constant = 16 

odrv0.axis1.encoder.config.mode = ENCODER_MODE_HALL
odrv0.axis1.encoder.config.cpr = 90
odrv0.axis1.encoder.config.calib_scan_distance = 150

odrv0.axis1.encoder.config.bandwidth = 100
odrv0.axis1.controller.config.pos_gain = 1
odrv0.axis1.controller.config.vel_limit = 10  # 1000


odrv0.axis1.controller.config.vel_gain = 0.02 * odrv0.axis1.motor.config.torque_constant * odrv0.axis1.encoder.config.cpr
odrv0.axis1.controller.config.vel_integrator_gain = 0.1 * odrv0.axis1.motor.config.torque_constant * odrv0.axis1.encoder.config.cpr

odrv0.save_configuration()  # includes a reboot

odrv0.axis1.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE
odrv0.axis1.motor.config.pre_calibrated = True
odrv0.axis1.encoder.config.pre_calibrated = True

odrv0.save_configuration()

During calibration the motor makes several noises and rotations in both directions, while doing the three calibrations (motor, Hall_polarity, and Encoder_offset)

Afterwards, odrv0.axis1.motor gives:

DC_calib_phA: 0.17508473992347717 (float)
DC_calib_phB: 0.08516408503055573 (float)
DC_calib_phC: -0.260256826877594 (float)
I_bus: 0.0 (float)
config:
  I_bus_hard_max: inf (float)
  I_bus_hard_min: -inf (float)
  I_leak_max: 0.10000000149011612 (float)
  R_wL_FF_enable: False (bool)
  acim_autoflux_attack_gain: 10.0 (float)
  acim_autoflux_decay_gain: 1.0 (float)
  acim_autoflux_enable: False (bool)
  acim_autoflux_min_Id: 10.0 (float)
  acim_gain_min_flux: 10.0 (float)
  bEMF_FF_enable: False (bool)
  calibration_current: 10.0 (float)
  current_control_bandwidth: 100.0 (float)
  current_lim: 10.0 (float)
  current_lim_margin: 8.0 (float)
  dc_calib_tau: 0.20000000298023224 (float)
  inverter_temp_limit_lower: 100.0 (float)
  inverter_temp_limit_upper: 120.0 (float)
  motor_type: 0 (uint8)
  phase_inductance: 0.0003803673607762903 (float)
  phase_resistance: 0.18802085518836975 (float)
  pole_pairs: 15 (int32)
  pre_calibrated: True (bool)
  requested_current_range: 50.0 (float)
  resistance_calib_max_voltage: 4.0 (float)
  torque_constant: 16.0 (float)
  torque_lim: inf (float)
current_control:
  I_measured_report_filter_k: 1.0 (float)
  Ialpha_measured: 0.0 (float)
  Ibeta_measured: 0.0 (float)
  Id_measured: 0.0 (float)
  Id_setpoint: 0.0 (float)
  Iq_measured: 0.0 (float)
  Iq_setpoint: 0.0 (float)
  Vd_setpoint: 0.0 (float)
  Vq_setpoint: 0.0 (float)
  final_v_alpha: 0.0 (float)
  final_v_beta: 0.0 (float)
  i_gain: 18.802085876464844 (float)
  p_gain: 0.038036737591028214 (float)
  phase: 0.0 (float)
  phase_vel: 0.0 (float)
  power: 0.0 (float)
  v_current_control_integral_d: 0.0 (float)
  v_current_control_integral_q: 0.0 (float)
current_meas_phA: -0.17588825523853302 (float)
current_meas_phB: -0.08383689075708389 (float)
current_meas_phC: 0.2596939504146576 (float)
effective_current_lim: 10.0 (float)
error: 0 (uint64)
fet_thermistor:
  config: ...
  temperature: 22.07196044921875 (float)
is_armed: False (bool)
is_calibrated: True (bool)
last_error_time: 0.0 (float)
max_allowed_current: 60.75 (float)
max_dc_calib: 6.075000286102295 (float)
motor_thermistor:
  config: ...
  temperature: 0.0 (float)
n_evt_current_measurement: 4043377 (uint32)
n_evt_pwm_update: 4043381 (uint32)
phase_current_rev_gain: 0.02500000037252903 (float)

odrv0.axis1.encoder gives:

calib_scan_response: 0.0 (float)
config:
  abs_spi_cs_gpio_pin: 1 (uint16)
  bandwidth: 100.0 (float)
  calib_range: 0.019999999552965164 (float)
  calib_scan_distance: 150.0 (float)
  calib_scan_omega: 12.566370964050293 (float)
  cpr: 90 (int32)
  direction: -1 (int32)
  enable_phase_interpolation: True (bool)
  find_idx_on_lockin_only: False (bool)
  hall_polarity: 0 (uint8)
  hall_polarity_calibrated: True (bool)
  ignore_illegal_hall_state: False (bool)
  index_offset: 0.0 (float)
  mode: 1 (uint16)
  phase_offset: -69 (int32)
  phase_offset_float: -0.47774702310562134 (float)
  pre_calibrated: True (bool)
  sincos_gpio_pin_cos: 4 (uint16)
  sincos_gpio_pin_sin: 3 (uint16)
  use_index: False (bool)
  use_index_offset: True (bool)
count_in_cpr: 2 (int32)
delta_pos_cpr_counts: -5.605193857299268e-45 (float)
error: 0 (uint16)
hall_state: 2 (uint8)
index_found: False (bool)
interpolation: 0.5 (float)
is_ready: True (bool)
phase: 0.023302078247070312 (float)
pos_abs: 0 (int32)
pos_circular: 0.033057015389204025 (float)
pos_cpr_counts: 2.975128173828125 (float)
pos_estimate: 0.03305697813630104 (float)
pos_estimate_counts: 2.975128173828125 (float)
set_linear_count(obj: object_ref, count: int32)
shadow_count: 2 (int32)
spi_error_rate: 0.0 (float)
vel_estimate: 0.0 (float)
vel_estimate_counts: 0.0 (float)

odrv0.axis1 gives:

acim_estimator:
  config: ...
  phase_offset: 0.0 (float)
  rotor_flux: 0.0 (float)
  slip_vel: 0.0 (float)
  stator_phase: 0.0 (float)
  stator_phase_vel: 0.0 (float)
config:
  calibration_lockin: ...
  can: ...
  dir_gpio_pin: 8 (uint16)
  enable_sensorless_mode: False (bool)
  enable_step_dir: False (bool)
  enable_watchdog: False (bool)
  general_lockin: ...
  sensorless_ramp: ...
  startup_closed_loop_control: False (bool)
  startup_encoder_index_search: False (bool)
  startup_encoder_offset_calibration: False (bool)
  startup_homing: False (bool)
  startup_motor_calibration: False (bool)
  step_dir_always_on: False (bool)
  step_gpio_pin: 7 (uint16)
  watchdog_timeout: 0.0 (float)
controller:
  anticogging_valid: False (bool)
  autotuning: ...
  autotuning_phase: 0.0 (float)
  config: ...
  electrical_power: 0.0 (float)
  error: 0 (uint8)
  input_pos: 0.0 (float)
  input_torque: 0.0 (float)
  input_vel: 0.0 (float)
  last_error_time: 0.0 (float)
  mechanical_power: 0.0 (float)
  move_incremental(obj: object_ref, displacement: float, from_input_pos: bool)
  pos_setpoint: 0.0 (float)
  start_anticogging_calibration(obj: object_ref)
  torque_setpoint: 0.0 (float)
  trajectory_done: True (bool)
  vel_integrator_torque: 0.0 (float)
  vel_setpoint: 0.0 (float)
current_state: 1 (uint8)
encoder:
  calib_scan_response: 0.0 (float)
  config: ...
  count_in_cpr: 2 (int32)
  delta_pos_cpr_counts: -5.605193857299268e-45 (float)
  error: 0 (uint16)
  hall_state: 2 (uint8)
  index_found: False (bool)
  interpolation: 0.5 (float)
  is_ready: True (bool)
  phase: 0.023302078247070312 (float)
  pos_abs: 0 (int32)
  pos_circular: 0.033057015389204025 (float)
  pos_cpr_counts: 2.975128173828125 (float)
  pos_estimate: 0.03305697813630104 (float)
  pos_estimate_counts: 2.975128173828125 (float)
  set_linear_count(obj: object_ref, count: int32)
  shadow_count: 2 (int32)
  spi_error_rate: 0.0 (float)
  vel_estimate: 0.0 (float)
  vel_estimate_counts: 0.0 (float)
error: 0 (uint32)
is_homed: False (bool)
last_drv_fault: 0 (uint32)
max_endstop:
  config: ...
  endstop_state: False (bool)
mechanical_brake:
  config: ...
  engage(obj: object_ref)
  release(obj: object_ref)
min_endstop:
  config: ...
  endstop_state: False (bool)
motor:
  DC_calib_phA: 0.16147330403327942 (float)
  DC_calib_phB: 0.0936511978507042 (float)
  DC_calib_phC: -0.25501498579978943 (float)
  I_bus: 0.0 (float)
  config: ...
  current_control: ...
  current_meas_phA: -0.1618221402168274 (float)
  current_meas_phB: -0.09373145550489426 (float)
  current_meas_phC: 0.255417138338089 (float)
  effective_current_lim: 10.0 (float)
  error: 0 (uint64)
  fet_thermistor: ...
  is_armed: False (bool)
  is_calibrated: True (bool)
  last_error_time: 0.0 (float)
  max_allowed_current: 60.75 (float)
  max_dc_calib: 6.075000286102295 (float)
  motor_thermistor: ...
  n_evt_current_measurement: 1350525 (uint32)
  n_evt_pwm_update: 1350529 (uint32)
  phase_current_rev_gain: 0.02500000037252903 (float)
requested_state: 0 (uint8)
sensorless_estimator:
  config: ...
  error: 0 (uint8)
  phase: 0.0 (float)
  phase_vel: 0.0 (float)
  pll_pos: 0.0 (float)
  vel_estimate: 0.0 (float)
step_dir_active: False (bool)
steps: 0 (int64)
task_times:
  acim_estimator_update: ...
  can_heartbeat: ...
  controller_update: ...
  current_controller_update: ...
  current_sense: ...
  dc_calib: ...
  encoder_update: ...
  endstop_update: ...
  motor_update: ...
  open_loop_controller_update: ...
  pwm_update: ...
  sensorless_estimator_update: ...
  thermistor_update: ...
trap_traj:
  config: ...
watchdog_feed(obj: object_ref)

Then I turn the volocity control on, this way:

odrv0.axis1.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL
odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis1.controller.input_vel = 2

And then the motor spins fine at 2 turns/sec continuously. I can hold the motor by hand to feel the torque increasing, upto a maximum current (odrv0.axis1.current_state) of 10A.

And then I try to put it in torque mode with the following commands:

odrv0.axis1.controller.input_vel = 0
odrv0.axis1.requested_state = AXIS_STATE_IDLE

odrv0.axis1.controller.config.pos_gain = 0
odrv0.axis1.controller.config.vel_gain = 0
odrv0.axis1.controller.config.vel_integrator_gain = 0
odrv0.axis1.controller.config.control_mode = CONTROL_MODE_TORQUE_CONTROL
odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL
odrv0.axis1.current_state  # It actually is in closed_loop (8)
odrv0.axis1.controller.input_torque = 0.1

Result: nothing happens, no sound, no movements, no spinning, no torque, no errors, I can freely rotate the motor.
I have repeated everything with axis0 and that gives the same result; no torque control (and velocity control OK).

Can anybody tell what I am missing please?

Ok right off the bat, this part is wrong:

odrv0.config.enable_brake_resistor = False
odrv0.config.dc_max_negative_current = -1
odrv0.config.max_regen_current = 40

That should be dc_max_negative_current = -41 (-40 - 1)

Check controller.config.enable_torque_mode_vel_limit = False, or increase the vel_limit. Also, double check your torque_constant (and 0.1Nm is not a lot of torque)

vel_limit = 10 was already included above, and I already tried enable_torque_mode_vel_limit=False, and now I also increased the dc_max_negative_current, which by the way I only increased because in velocity control I got an over-current error (even without any external load, just while accelerating and decelerating the motor).
And even at input_torque=1 nothing happens. No torque, rotation, or sound, whatsoever on either axis0 or axis1.
How to check the torque_constant? I just used what was written in the getting started and hoverboard guides.

I re-read the getting started guide. I decided to set odrv0.axis0.motor.config.torque_constant = 1 to enable setting torque in amps. that did the trick. It now starts turning at input_torque = 0.4. And if I increase it, then the torque (or revs) increase. Great, thank you.

1 Like

Googling, I found a nice website: Tutorial: How to measure the KV of a brushless motor ā€“ fishpepper.de. Other sites say that KV=16 seems to be an average value for these motors, so I still donā€™t understand why I didnā€™t get any torque with that.
Oh, correction. If I increase the input from a minimum of 0.4A (to get spinning) to 6Nm, then it is also working with torque_constant=KV=16.

1 Like