Changing States

Hello Fellow ODrivers,

I am running into an issue where I cannot set my ODrive S1 to to Position Control mode from the Dashboard or in scripts. It keeps faulting to NOT_CALIBRATED even though I ran through all of the steps of the Apply & Calibrate page successfully.

I can run in Velocity and Torque Control modes but not in Position Control mode.

When I try to run from a python script I get the following errors when I dump_errors():

Exception: Axis failed to enter <AxisState.CLOSED_LOOP_CONTROL: 8>: axis0
  active_errors: no error
  disarm_reason: no error
  procedure_result: ProcedureResult.NOT_CALIBRATED
  last_drv_fault: none
internal issues: none

The axis I am trying to drive has a load encoder (absolute multi-turn RS485) and a commutator encoder (incremental - no index).

I had seen some other posts about similar problems but not exactly what I am seeing. If you have experienced a similar issue or can point me to a thread that resolves this issue I’d appreciate that.

Best,
Emi

Hi! My guess is that either you have your position reference frame set up weird, or you’re just not calibrating the encoder before entering closed loop. Would you be open to posting your python code, as well as your ODrive configuration (odrivetool backup-config config.json)?

Hey Solomon,

I used the GUI to apply my configs and calibrate (steps 1-6 of the Apply & Calibrate page in the GUI).

What is weird is that I ran through the same process with another motor and I was able to run that in closed loop mode.

Here is my (very rough please don’t judge :slight_smile: ) python code:

import math
import time

import odrive
from odrive.enums import AxisState, ControlMode, InputMode
from odrive.utils import dump_errors, request_state

# Find a connected ODrive (this will block until you connect one)
print("waiting for ODrive...")
odrv1 = odrive.find_sync(serial_number="XXXXXXXXXXXXXX")
print(f"found ODrive {odrv1._dev.serial_number}")


# clear errors on the odrive(s)
odrv1.clear_errors()

# Quick Calibration if needed
request_state(odrv1.axis0, AxisState.FULL_CALIBRATION_SEQUENCE)
request_state(odrv1.axis0, AxisState.ENCODER_OFFSET_CALIBRATION)



# Configure Current Limits
odrv1.axis0.config.I_bus_hard_min = -5
odrv1.axis0.config.I_bus_soft_min = -5
odrv1.axis0.config.I_bus_hard_max = 5
odrv1.axis0.config.I_bus_soft_max = 5
odrv1.axis0.controller.config.vel_limit = 100



# Enter closed loop control
odrv1.axis0.controller.config.input_mode = InputMode.PASSTHROUGH
odrv1.axis0.controller.config.control_mode = ControlMode.POSITION_CONTROL
request_state(odrv1.axis0, AxisState.CLOSED_LOOP_CONTROL)



# Step the Drives
step_delta = 1.0
timer_counter = 0
timer_delta = 5
max_loop_time = 20
try:
    p1 = odrv1.axis0.controller.input_pos
    t0 = time.monotonic()
    setpoint1 = p1

    while odrv1.axis0.current_state == AxisState.CLOSED_LOOP_CONTROL and timer_counter < max_loop_time:
        setpoint1 = setpoint1 + step_delta

        print(f"Set Point 1: {setpoint1}, Position 1: {odrv1.axis0.pos_estimate}\n\n")


        # # Position
        odrv1.axis0.controller.input_pos = setpoint1

        time.sleep(timer_delta)
        timer_counter = timer_counter + timer_delta

finally:
    request_state(odrv1.axis0, AxisState.IDLE)

# Show errors
dump_errors(odrv1)

I X’d out the serial number for this post.

Here is the config uploaded from the GUI:

odrv = odrv0
odrv.config.dc_bus_overvoltage_trip_level = 28.5
odrv.config.dc_bus_undervoltage_trip_level = 18
odrv.config.dc_max_positive_current = 5
odrv.config.dc_max_negative_current = -5
odrv.config.brake_resistor0.enable = False
odrv.axis0.config.motor.motor_type = MotorType.PMSM_CURRENT_CONTROL
odrv.axis0.config.motor.pole_pairs = 7
odrv.axis0.config.motor.torque_constant = 0.030629629629629628
odrv.axis0.config.motor.current_soft_max = 65
odrv.axis0.config.motor.current_hard_max = 85
odrv.axis0.config.motor.calibration_current = 10
odrv.axis0.config.motor.resistance_calib_max_voltage = 2
odrv.axis0.config.calibration_lockin.current = 10
odrv.axis0.motor.motor_thermistor.config.enabled = False
odrv.axis0.controller.config.control_mode = ControlMode.VELOCITY_CONTROL
odrv.axis0.controller.config.input_mode = InputMode.VEL_RAMP
odrv.axis0.controller.config.vel_limit = 2
odrv.axis0.controller.config.vel_limit_tolerance = 5
odrv.axis0.config.torque_soft_min = -math.inf
odrv.axis0.config.torque_soft_max = math.inf
odrv.axis0.trap_traj.config.accel_limit = 2
odrv.axis0.controller.config.vel_ramp_rate = 2
odrv.can.config.protocol = Protocol.NONE
odrv.axis0.config.enable_watchdog = False
odrv.axis0.config.load_encoder = EncoderId.RS485_ENCODER0
odrv.rs485_encoder_group0.config.mode = Rs485EncoderMode.AMT21_POLLING
odrv.inc_encoder0.config.enabled = True
odrv.axis0.config.commutation_encoder = EncoderId.INC_ENCODER0
odrv.inc_encoder0.config.cpr = 8192
odrv.axis0.commutation_mapper.config.use_index_gpio = False
odrv.config.enable_uart_a = False

Let me know if this is not the correct config you were asking for.

Best,
Emi

Thanks for all the info! In the future, no need to blank out the serial number – I get the paranoia, but the only thing that lets me do is know the production batch the ODrive came from :slight_smile: (and nobody else will know anything from it).

Some notes:

  1. Not related, but I notice you’re using Rs485EncoderMode.AMT21_POLLING for the load encoder. This implies a stock AMT21, which has a pretty significant firmware bug that really hurts performance at higher speeds (above a few hundred RPM). I’d recommend switching to the AMT21-B-V-OD from our shop, which has custom firmware that gives it much higher performance.
  2. Seems like you’re using the D5065 270KV from our shop, but your calibration_lockin.current is really low – you should bump this to around the motor’s max continuous current (about 40-45A) for more accurate calibration
  3. FULL_CALIBRATION_SEQUENCE does both MOTOR_CALIBRATION and ENCODER_OFFSET_CALIBRATION, so you don’t need to call both FULL_CALIBRATION_SEQUENCE and then ENCODER_OFFSET_CALIBRATION. In fact, you only need to call ENCODER_OFFSET_CALIBRATION, since the motor calibration (resistance+inductance) gets saved to the ODrive.
  4. This is the issue you’re having – request_state only switches the ODrive to that state, it doesn’t actually wait for the state to be done. As such, you’re requesting encoder offset calibration, but then immediately requesting closed loop control, so the encoder offset cal isn’t finished – hence the NOT_CALIBRATED error. My recommendation would be to use run_state() instead – this will set the new state and then wait for it to be complete.

So instead, you should have:

run_state(odrv1.axis0, AxisState.ENCODER_OFFSET_CALIBRATION)

Hey Solomon,

  1. We needed to use a larger diameter encoder so that is why we ended up with the AMT24. We may have been able to get away with using an adapter though. This is good to know for the next one. Luckily we are never running the absolute encoders over 50-100 RPM

  2. I used the default value given for the motor and I thought that would be all good. I will set this higher - probably try 20A and move up if that doesn’t work since I don’t want to fry the ODrive if it is too high. What is interesting is that I have calibrated another D5065 the exact same way I did for this one and it all worked exactly as I had intended. Weird that this one is acting up but this is still worth a shot.

  3. Excellent. I was doing the full calibration each time just to make sure I wasn’t missing anything. I was primarily using the GUI for this step.

  4. Ah that is good to know for my scripts. What is odd is that this happens even when I am using the GUI for calibration.

I will try changing the lock-in spin current for calibration and update my script and see where that gets me.

As always, thank you for the help!

When I ran the script with the suggested changes I got these errors after ENCODER_OFFSET_CALIBRATION was complete:

Just to reiterate, I am able to go into velocity and torque control modes, just not position control.

Likely something related to the position reference frames then. Could you share your config? odrivetool backup-config config.json, then you can paste the config.json here.

Also

I used the default value given for the motor and I thought that would be all good. I will set this higher - probably try 20A and move up if that doesn’t work since I don’t want to fry the ODrive if it is too high. What is interesting is that I have calibrated another D5065 the exact same way I did for this one and it all worked exactly as I had intended. Weird that this one is acting up but this is still worth a shot.

Could also be just due to different load on the motor. No worries on frying the ODrive – it’ll self-limit current if it gets too hot! Even without a heatspreader plate or any sort of thermal management (just bare PCB), 20-30A is just fine.

Hey sorry for the delay!

{
  "can.config.baud_rate": 0,
  "can.config.data_baud_rate": 10000000,
  "can.config.tx_brs": 0,
  "can.config.protocol": 0,
  "config.enable_uart_a": false,
  "config.uart_a_baudrate": 115200,
  "config.usb_cdc_protocol": 3,
  "config.uart0_protocol": 3,
  "config.max_regen_current": 0.0,
  "config.dc_bus_undervoltage_trip_level": 18.0,
  "config.dc_bus_overvoltage_trip_level": 28.5,
  "config.dc_max_positive_current": 10.0,
  "config.dc_max_negative_current": -10.0,
  "config.user_config_0": 0,
  "config.user_config_1": 0,
  "config.user_config_2": 0,
  "config.user_config_3": 0,
  "config.user_config_4": 0,
  "config.user_config_5": 0,
  "config.user_config_6": 0,
  "config.user_config_7": 0,
  "config.gpio0_mode": 17,
  "config.gpio1_mode": 17,
  "config.gpio2_mode": 17,
  "config.gpio3_mode": 17,
  "config.gpio4_mode": 17,
  "config.gpio5_mode": 17,
  "config.gpio6_mode": 17,
  "config.gpio7_mode": 17,
  "config.gpio8_mode": 17,
  "config.gpio9_mode": 17,
  "config.gpio10_mode": 17,
  "config.gpio11_mode": 17,
  "config.gpio12_mode": 17,
  "config.gpio8_pwm_mapping.endpoint": null,
  "config.gpio8_pwm_mapping.min": 0.0,
  "config.gpio8_pwm_mapping.max": 0.0,
  "config.gpio1_analog_mapping.endpoint": null,
  "config.gpio1_analog_mapping.min": 0.0,
  "config.gpio1_analog_mapping.max": 0.0,
  "config.gpio11_analog_mapping.endpoint": null,
  "config.gpio11_analog_mapping.min": 0.0,
  "config.gpio11_analog_mapping.max": 0.0,
  "config.brake_resistor0.enable": false,
  "config.brake_resistor0.resistance": 0.0,
  "config.brake_resistor0.enable_dc_bus_voltage_feedback": false,
  "config.brake_resistor0.dc_bus_voltage_feedback_ramp_start": 51.0,
  "config.brake_resistor0.dc_bus_voltage_feedback_ramp_end": 53.0,
  "config.inverter0.current_soft_max": 80.0,
  "config.inverter0.current_hard_max": 96.0,
  "config.inverter0.temp_limit_lower": 83.95999908447266,
  "config.inverter0.temp_limit_upper": 103.11000061035156,
  "config.inverter0.mod_magn_max": 0.6781481504440308,
  "config.inverter0.shunt_conductance": 1999.9998779296875,
  "config.inverter0.drv_config": 9029553697166464,
  "axis0.config.startup_max_wait_for_ready": 3.0,
  "axis0.config.startup_motor_calibration": false,
  "axis0.config.startup_encoder_index_search": false,
  "axis0.config.startup_encoder_offset_calibration": false,
  "axis0.config.startup_closed_loop_control": false,
  "axis0.config.startup_homing": false,
  "axis0.config.init_torque": 0.0,
  "axis0.config.init_vel": 0.0,
  "axis0.config.init_pos": NaN,
  "axis0.config.enable_step_dir": false,
  "axis0.config.step_dir_always_on": false,
  "axis0.config.calib_range": 0.019999999552965164,
  "axis0.config.calib_scan_distance": 8.0,
  "axis0.config.calib_scan_vel": 2.0,
  "axis0.config.index_search_at_target_vel_only": false,
  "axis0.config.watchdog_timeout": 0.0,
  "axis0.config.enable_watchdog": false,
  "axis0.config.step_gpio_pin": 8,
  "axis0.config.dir_gpio_pin": 5,
  "axis0.config.error_gpio_pin": 6,
  "axis0.config.enable_error_gpio": false,
  "axis0.config.calibration_lockin.current": 40.0,
  "axis0.config.calibration_lockin.ramp_time": 0.4000000059604645,
  "axis0.config.calibration_lockin.ramp_distance": 0.5,
  "axis0.config.calibration_lockin.accel": 3.183098793029785,
  "axis0.config.calibration_lockin.vel": 6.36619758605957,
  "axis0.config.sensorless_ramp.initial_pos": 0.0,
  "axis0.config.sensorless_ramp.current": 10.0,
  "axis0.config.sensorless_ramp.ramp_time": 0.4000000059604645,
  "axis0.config.sensorless_ramp.ramp_distance": 0.5,
  "axis0.config.sensorless_ramp.accel": 31.83098793029785,
  "axis0.config.sensorless_ramp.vel": 63.6619758605957,
  "axis0.config.sensorless_ramp.finish_distance": 15.915493965148926,
  "axis0.config.sensorless_ramp.finish_on_vel": true,
  "axis0.config.sensorless_ramp.finish_on_distance": false,
  "axis0.config.general_lockin.initial_pos": 0.0,
  "axis0.config.general_lockin.current": 10.0,
  "axis0.config.general_lockin.ramp_time": 0.4000000059604645,
  "axis0.config.general_lockin.ramp_distance": 3.1415927410125732,
  "axis0.config.general_lockin.accel": 20.0,
  "axis0.config.general_lockin.vel": 40.0,
  "axis0.config.general_lockin.finish_distance": 100.0,
  "axis0.config.general_lockin.finish_on_vel": false,
  "axis0.config.general_lockin.finish_on_distance": false,
  "axis0.config.can.node_id": 63,
  "axis0.config.can.version_msg_rate_ms": 0,
  "axis0.config.can.heartbeat_msg_rate_ms": 100,
  "axis0.config.can.encoder_msg_rate_ms": 10,
  "axis0.config.can.iq_msg_rate_ms": 0,
  "axis0.config.can.error_msg_rate_ms": 0,
  "axis0.config.can.temperature_msg_rate_ms": 0,
  "axis0.config.can.bus_voltage_msg_rate_ms": 0,
  "axis0.config.can.torques_msg_rate_ms": 0,
  "axis0.config.can.powers_msg_rate_ms": 0,
  "axis0.config.can.input_vel_scale": 1000,
  "axis0.config.can.input_torque_scale": 1000,
  "axis0.config.load_encoder": 10,
  "axis0.config.commutation_encoder": 1,
  "axis0.config.encoder_bandwidth": 1000.0,
  "axis0.config.commutation_encoder_bandwidth": NaN,
  "axis0.config.I_bus_hard_min": -Infinity,
  "axis0.config.I_bus_hard_max": Infinity,
  "axis0.config.I_bus_soft_min": -Infinity,
  "axis0.config.I_bus_soft_max": Infinity,
  "axis0.config.P_bus_soft_min": -Infinity,
  "axis0.config.P_bus_soft_max": Infinity,
  "axis0.config.torque_soft_min": -Infinity,
  "axis0.config.torque_soft_max": Infinity,
  "axis0.config.motor.motor_type": 0,
  "axis0.config.motor.pole_pairs": 7,
  "axis0.config.motor.phase_resistance": 0.07713417708873749,
  "axis0.config.motor.phase_inductance": 2.0513876734185033e-05,
  "axis0.config.motor.phase_resistance_valid": true,
  "axis0.config.motor.phase_inductance_valid": true,
  "axis0.config.motor.torque_constant": 0.030629629269242287,
  "axis0.config.motor.direction": -1.0,
  "axis0.config.motor.current_control_bandwidth": 1000.0,
  "axis0.config.motor.wL_FF_enable": false,
  "axis0.config.motor.bEMF_FF_enable": false,
  "axis0.config.motor.dI_dt_FF_enable": false,
  "axis0.config.motor.ff_pm_flux_linkage": 0.0,
  "axis0.config.motor.ff_pm_flux_linkage_valid": false,
  "axis0.config.motor.motor_model_l_d": 0.0,
  "axis0.config.motor.motor_model_l_q": 0.0,
  "axis0.config.motor.motor_model_l_dq_valid": false,
  "axis0.config.motor.calibration_current": 10.0,
  "axis0.config.motor.resistance_calib_max_voltage": 2.0,
  "axis0.config.motor.current_soft_max": 65.0,
  "axis0.config.motor.current_hard_max": 85.0,
  "axis0.config.motor.current_slew_rate_limit": 10000.0,
  "axis0.config.motor.fw_enable": false,
  "axis0.config.motor.fw_mod_setpoint": 0.6103333234786987,
  "axis0.config.motor.fw_fb_bandwidth": 500.0,
  "axis0.config.motor.acim_gain_min_flux": 10.0,
  "axis0.config.motor.acim_autoflux_enable": false,
  "axis0.config.motor.acim_autoflux_min_Id": 10.0,
  "axis0.config.motor.acim_autoflux_attack_gain": 10.0,
  "axis0.config.motor.acim_autoflux_decay_gain": 1.0,
  "axis0.config.motor.acim_nominal_slip_vel": 2.3399999141693115,
  "axis0.config.motor.sensorless_observer_gain": 1000.0,
  "axis0.config.motor.sensorless_pll_bandwidth": 1000.0,
  "axis0.config.motor.sensorless_pm_flux_linkage": 0.0,
  "axis0.config.motor.sensorless_pm_flux_linkage_valid": false,
  "axis0.config.motor.power_torque_report_filter_bandwidth": 8000.0,
  "axis0.config.anticogging.enabled": false,
  "axis0.config.anticogging.max_torque": 0.15000000596046448,
  "axis0.config.anticogging.calib_start_vel": 1.0,
  "axis0.config.anticogging.calib_end_vel": 0.15000000596046448,
  "axis0.config.anticogging.calib_coarse_tuning_duration": 60.0,
  "axis0.config.anticogging.calib_fine_tuning_duration": 120.0,
  "axis0.config.anticogging.calib_fine_dist_scale": 1.0,
  "axis0.config.anticogging.calib_coarse_integrator_gain": 10.0,
  "axis0.config.anticogging.calib_bidirectional": true,
  "axis0.config.off_axis_k": 1.0,
  "axis0.config.off_axis_k_commutation": 1.0,
  "axis0.config.harmonic_compensation.calib_vel": 8.0,
  "axis0.config.harmonic_compensation.calib_settling_delay": 2.0,
  "axis0.config.harmonic_compensation.calib_turns": 8,
  "axis0.config.harmonic_compensation.cosx_coef": 0.0,
  "axis0.config.harmonic_compensation.sinx_coef": 0.0,
  "axis0.config.harmonic_compensation.cos2x_coef": 0.0,
  "axis0.config.harmonic_compensation.sin2x_coef": 0.0,
  "axis0.config.harmonic_compensation_commutation.calib_vel": 8.0,
  "axis0.config.harmonic_compensation_commutation.calib_settling_delay": 2.0,
  "axis0.config.harmonic_compensation_commutation.calib_turns": 8,
  "axis0.config.harmonic_compensation_commutation.cosx_coef": 0.0,
  "axis0.config.harmonic_compensation_commutation.sinx_coef": 0.0,
  "axis0.config.harmonic_compensation_commutation.cos2x_coef": 0.0,
  "axis0.config.harmonic_compensation_commutation.sin2x_coef": 0.0,
  "axis0.controller.config.enable_vel_limit": true,
  "axis0.controller.config.enable_torque_mode_vel_limit": true,
  "axis0.controller.config.enable_gain_scheduling": false,
  "axis0.controller.config.gain_scheduling_width": 0.0010000000474974513,
  "axis0.controller.config.enable_overspeed_error": true,
  "axis0.controller.config.control_mode": 2,
  "axis0.controller.config.input_mode": 2,
  "axis0.controller.config.pos_gain": 20.0,
  "axis0.controller.config.vel_gain": 0.1666666716337204,
  "axis0.controller.config.vel_integrator_gain": 0.3333333432674408,
  "axis0.controller.config.vel_integrator_limit": Infinity,
  "axis0.controller.config.vel_limit": 2.0,
  "axis0.controller.config.vel_limit_tolerance": 2.5,
  "axis0.controller.config.vel_ramp_rate": 2.0,
  "axis0.controller.config.torque_ramp_rate": 0.009999999776482582,
  "axis0.controller.config.circular_setpoints": false,
  "axis0.controller.config.circular_setpoint_range": 1.0,
  "axis0.controller.config.absolute_setpoints": false,
  "axis0.controller.config.use_commutation_vel": false,
  "axis0.controller.config.use_load_encoder_for_commutation_vel": false,
  "axis0.controller.config.commutation_vel_scale": 1.0,
  "axis0.controller.config.steps_per_circular_range": 1024,
  "axis0.controller.config.homing_speed": 0.25,
  "axis0.controller.config.inertia": 0.0,
  "axis0.controller.config.input_filter_bandwidth": 20.0,
  "axis0.controller.config.spinout_mechanical_power_bandwidth": 20.0,
  "axis0.controller.config.spinout_electrical_power_bandwidth": 20.0,
  "axis0.controller.config.spinout_mechanical_power_threshold": -10.0,
  "axis0.controller.config.spinout_electrical_power_threshold": 10.0,
  "axis0.trap_traj.config.vel_limit": 2.0,
  "axis0.trap_traj.config.accel_limit": 2.0,
  "axis0.trap_traj.config.decel_limit": 0.5,
  "axis0.min_endstop.config.gpio_num": 0,
  "axis0.min_endstop.config.enabled": false,
  "axis0.min_endstop.config.offset": 0.0,
  "axis0.min_endstop.config.is_active_high": false,
  "axis0.min_endstop.config.debounce_ms": 50,
  "axis0.max_endstop.config.gpio_num": 0,
  "axis0.max_endstop.config.enabled": false,
  "axis0.max_endstop.config.offset": 0.0,
  "axis0.max_endstop.config.is_active_high": false,
  "axis0.max_endstop.config.debounce_ms": 50,
  "axis0.enable_pin.config.gpio_num": 7,
  "axis0.enable_pin.config.enabled": false,
  "axis0.enable_pin.config.offset": 0.0,
  "axis0.enable_pin.config.is_active_high": false,
  "axis0.enable_pin.config.debounce_ms": 50,
  "axis0.mechanical_brake.config.gpio_num": 0,
  "axis0.mechanical_brake.config.is_active_low": true,
  "axis0.pos_vel_mapper.config.circular": false,
  "axis0.pos_vel_mapper.config.circular_output_range": 1.0,
  "axis0.pos_vel_mapper.config.scale": 1.0,
  "axis0.pos_vel_mapper.config.offset_valid": false,
  "axis0.pos_vel_mapper.config.offset": 0.0,
  "axis0.pos_vel_mapper.config.approx_init_pos_valid": false,
  "axis0.pos_vel_mapper.config.approx_init_pos": 0.0,
  "axis0.pos_vel_mapper.config.index_offset_valid": false,
  "axis0.pos_vel_mapper.config.index_offset": 0.0,
  "axis0.pos_vel_mapper.config.use_index_gpio": false,
  "axis0.pos_vel_mapper.config.passive_index_search": false,
  "axis0.pos_vel_mapper.config.index_gpio": 10,
  "axis0.pos_vel_mapper.config.use_endstop": false,
  "axis0.commutation_mapper.config.circular": true,
  "axis0.commutation_mapper.config.circular_output_range": 1.0,
  "axis0.commutation_mapper.config.scale": -7.0,
  "axis0.commutation_mapper.config.offset_valid": false,
  "axis0.commutation_mapper.config.offset": 0.0,
  "axis0.commutation_mapper.config.approx_init_pos_valid": false,
  "axis0.commutation_mapper.config.approx_init_pos": 0.0,
  "axis0.commutation_mapper.config.index_offset_valid": false,
  "axis0.commutation_mapper.config.index_offset": 0.0,
  "axis0.commutation_mapper.config.use_index_gpio": false,
  "axis0.commutation_mapper.config.passive_index_search": false,
  "axis0.commutation_mapper.config.index_gpio": 10,
  "axis0.commutation_mapper.config.use_endstop": false,
  "axis0.interpolator.config.dynamic": true,
  "axis0.motor.motor_thermistor.config.gpio_pin": 4,
  "axis0.motor.motor_thermistor.config.mode": 1,
  "axis0.motor.motor_thermistor.config.r_ref": 0.0,
  "axis0.motor.motor_thermistor.config.t_ref": 25.0,
  "axis0.motor.motor_thermistor.config.beta": 0.0,
  "axis0.motor.motor_thermistor.config.a": 3.9082999229431152,
  "axis0.motor.motor_thermistor.config.b": -0.0005774999735876918,
  "axis0.motor.motor_thermistor.config.temp_limit_lower": 100.0,
  "axis0.motor.motor_thermistor.config.temp_limit_upper": 120.0,
  "axis0.motor.motor_thermistor.config.enabled": false,
  "rs485_encoder_group0.config.mode": 1,
  "rs485_encoder_group1.config.mode": 0,
  "inc_encoder0.config.enabled": true,
  "inc_encoder0.config.filter": 0,
  "inc_encoder0.config.cpr": 8192,
  "hall_encoder0.config.enabled": false,
  "hall_encoder0.config.hall_polarity": 0,
  "hall_encoder0.config.hall_polarity_calibrated": false,
  "hall_encoder0.config.ignore_illegal_hall_state": false,
  "hall_encoder0.config.edges_calibrated": false,
  "hall_encoder0.config.edge0": NaN,
  "hall_encoder0.config.edge1": NaN,
  "hall_encoder0.config.edge2": NaN,
  "hall_encoder0.config.edge3": NaN,
  "hall_encoder0.config.edge4": NaN,
  "hall_encoder0.config.edge5": NaN,
  "spi_encoder0.config.ncs_gpio": 12,
  "spi_encoder0.config.mode": 0,
  "spi_encoder0.config.delay": 0.0,
  "spi_encoder0.config.max_error_rate": 0.004999999888241291,
  "spi_encoder0.config.baudrate": 1687500,
  "spi_encoder0.config.biss_c_bits": 18,
  "spi_encoder0.config.biss_c_multiturn_bits": 0,
  "spi_encoder1.config.ncs_gpio": 12,
  "spi_encoder1.config.mode": 0,
  "spi_encoder1.config.delay": 0.0,
  "spi_encoder1.config.max_error_rate": 0.004999999888241291,
  "spi_encoder1.config.baudrate": 1687500,
  "spi_encoder1.config.biss_c_bits": 18,
  "spi_encoder1.config.biss_c_multiturn_bits": 0
}

Okay I decided to run the calibration on this axis without load (or significantly less load) and the calibration worked!

I can now switch to position control so I think that the load must have been causing my issue.

For this axis, removing the load was fairly easy but for the others it will be a lot more difficult.

Is there a way to calibrate axis’ without having to disassemble my robot?

Great! That makes sense.

Is there a way to calibrate axis’ without having to disassemble my robot?

Very much depends. If you crank calibration_lockin.current it should help a lot. What sorts of loads are we talking about / what’s the robot?

The robot is a SCARA style robot similar to the YK-XG from Yamaha.

Our J1 has a 100:1 gear ration and J2 is at 20:1. For loads, do you want the moment of inertia of the associated linkages?

Ah gotcha. So you don’t have any “biased” loads in one direction or another, which is good – those are what throw off calibration (e.g. a robot arm joint that’s subject to gravity – more load torque in one direction than another). I’d say to set calibration_lockin.current to the aforementioned 40-45A, and then maybe increase calib_scan_distance to 16 or so to increase the distance the ODrive moves the motor for calibration (which can also help).

Cool cool.

I was trying to increase the lock-in current but I could only for up to 35A. I think my soft/hard current limits are too small because I kept getting overcurrent errors.

I will definitely try increasing the scan distance next week when I get back to the lab.

Will keep you posted

Cool! If you still have ibus hard/soft min/max limits of -5/5, I’d say bump the I_bus_hard_max to maybe 2x of the soft max (and ditto with hard/soft min), it’s probably getting some brief transients that violate the hard max – you always need some margin there.

Okay after much testing here is what we found:

  • One ODrive was not configuring properly because of the load encoder protocol I had specified. I was using the advanced protocol instead of the original AMT21 one. Dumb mistake on my part but oh well

  • The other ODrive must have some issue with it because I swapped ODrives for this axis and it seemed to work just fine. It was able to jump into closed loop position control just fine. I am purchasing another ODrive to see if that fixes my issue once and for all.

Will keep update thread with my findings once I have more to share.

Interesting! Could you try doing odrivetool backup-config config.json on the working ODrive, then reinstalling the possibly broken one and doing odrivetool restore-config config.json? You’ll have to re-run motor+encoder calibration, but that should do a 1:1 parameter set copy otherwise.

sure!

How do I isolate the ODrive I want to backup-config and then how do I isolate the one I want to restore-config?

ODrivetool takes a --serial-number flag, e.g.

odrivetool --serial-number xyz backup-config config.json
odrivetool --serial-number abc restore-config config.json

1 Like

No luck unfortunately.

Will update when I get the new ODrive

Remind me the issue here – is it the overcurrent errors? Or something else?