Hello all, I have two ElectroCraft MP24 motors that I’m trying to use to drive a robot in a barn. However, I haven’t been able to start them reliably after a power cycle by using the index channel.
These motors have a lot of resistance on them (the wheels literally have metal rods dragging on them to clean off manure as the robot moves), but I’ve been able to get them manually calibrated easily (i.e., axis.requested_state = AXIS_STATE_FULL_CALIBRATION_SEQUENCE
), after which they work swimmingly even under a lot of drag.
However, even with the wheels completely removed (i.e., hardly any resistance at all), I haven’t been able to start the motors reliably with axis.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH
per the skip encoder calibration with index channel tutorial on the ODrive website. Sometimes, I go through the process (odrv0.reboot()
, encoder search, axis.requested_state=AXIS_STATE_CLOSED_LOOP_CONTROL
) and the motor does work, but more often (6/8 times in my quick test) the motor just takes off (in the backwards direction) at a fast, consistent but random speed (i.e., sometimes it’s consistently fast, sometimes its consistently holy-crap-hit-the-kill-switch fast). Depending on fast it decides to go, it will sometimes trip an over-current or over-velocity error.
I’ve been fighting this long enough to create the following script, which takes the motor from odrv0.erase_configuration()
to what I’m trying to use:
import odrive
from odrive.enums import *
import time
from odrive.utils import dump_errors
import math
from fibre import ChannelBrokenException
def setup_sequence():
odrv = odrive.find_any()
axes = [odrv.axis0, odrv.axis1]
print("Configuring odrive...")
# Alternate theory for this step: It's possible to just save and load config from a file.
for axis in axes:
axis.controller.config.vel_limit = 50
axis.motor.config.pole_pairs = 2
axis.encoder.config.cpr = 4000
axis.motor.config.torque_constant = 1.350645
axis.motor.config.motor_type = MOTOR_TYPE_HIGH_CURRENT
axis.motor.config.resistance_calib_max_voltage = 10
axis.motor.config.calibration_current = 40
#Temporarily removed and set back to default
# axis.encoder.config.calib_scan_distance = (
# 33 * 2 * math.pi
# ) # 33 rotations, for one complete revolution of motor after gearbox plus one for good measure
axis.controller.config.vel_gain = 9 # I believe this could be raised to as high as 17 if needed. Was lowered to help with a glitchy motor.
axis.controller.config.vel_integrator_gain = 30
axis.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL
axis.motor.config.current_lim = 80
axis.motor.config.requested_current_range = 100 # Requires reboot
odrv.config.max_regen_current = 100
odrv.config.dc_max_negative_current = -100
odrv.config.brake_resistance = 0
for num, axis in enumerate(axes):
print("Running calibration on Axis #%d" % num)
axis.encoder.config.use_index = True
axis.requested_state = AXIS_STATE_MOTOR_CALIBRATION
while axis.current_state != AXIS_STATE_IDLE:
time.sleep(1)
if axis.error != 0:
print(
"Axis #%d failed initial calibration. See upcoming error dump for information"
% num
)
continue
axis.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH
while axis.current_state != AXIS_STATE_IDLE:
time.sleep(1)
axis.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION
while axis.current_state != AXIS_STATE_IDLE:
time.sleep(1)
axis.encoder.config.pre_calibrated = True
if not axis.encoder.config.pre_calibrated: # Verify it was accepted
print("Encoder calibration failed for axis #%d" % num)
continue
axis.motor.config.pre_calibrated = True
print("Dump of final odrive state:")
dump_errors(odrv)
odrv.save_configuration()
try:
odrv.reboot()
except ChannelBrokenException:
pass
print("As long as you didn't recieve any errors, odrive should be good to go")
if __name__ == "__main__":
setup_sequence()
Information from one of the axes:
Encoder:
error = 0x0000 (int)
is_ready = True (bool)
index_found = True (bool)
shadow_count = -1519 (int)
count_in_cpr = 2481 (int)
interpolation = 0.5 (float)
phase = 1.5009195804595947 (float)
pos_estimate = -0.3795000910758972 (float)
pos_estimate_counts = -1518.000244140625 (float)
pos_cpr = 0.620437502861023 (float)
pos_cpr_counts = 2481.75 (float)
pos_circular = 0.6212158799171448 (float)
hall_state = 2 (int)
vel_estimate = 0.0 (float)
vel_estimate_counts = 0.0 (float)
calib_scan_response = 0.0 (float)
pos_abs = 0 (int)
spi_error_rate = 0.0 (float)
config:
mode = 0 (int)
use_index = True (bool)
find_idx_on_lockin_only = False (bool)
abs_spi_cs_gpio_pin = 1 (int)
zero_count_on_find_idx = True (bool)
cpr = 4000 (int)
offset = -9996 (int)
pre_calibrated = True (bool)
offset_float = -0.25721877813339233 (float)
enable_phase_interpolation = True (bool)
bandwidth = 1000.0 (float)
calib_range = 0.019999999552965164 (float)
calib_scan_distance = 50.26548385620117 (float)
calib_scan_omega = 12.566370964050293 (float)
idx_search_unidirectional = False (bool)
ignore_illegal_hall_state = False (bool)
sincos_gpio_pin_sin = 3 (int)
sincos_gpio_pin_cos = 4 (int)
set_linear_count(count: int)
Motor:
error = 0x0000 (int)
armed_state = 0 (int)
is_calibrated = True (bool)
current_meas_phB = -0.0601348876953125 (float)
current_meas_phC = -0.13639980554580688 (float)
DC_calib_phB = -0.8263078927993774 (float)
DC_calib_phC = -0.5083249807357788 (float)
phase_current_rev_gain = 0.05000000074505806 (float)
effective_current_lim = 80.0 (float)
current_control:
p_gain = 0.7359321117401123 (float)
i_gain = 123.63776397705078 (float)
v_current_control_integral_d = -0.18157467246055603 (float)
v_current_control_integral_q = -1.3240957260131836 (float)
Ibus = 0.7657429575920105 (float)
final_v_alpha = 0.5790687203407288 (float)
final_v_beta = 1.2977954149246216 (float)
Id_setpoint = 0.0 (float)
Iq_setpoint = -10.0 (float)
Iq_measured = -10.034514427185059 (float)
Id_measured = 0.5472937822341919 (float)
I_measured_report_filter_k = 1.0 (float)
max_allowed_current = 121.5 (float)
overcurrent_trip_level = 135.0 (float)
acim_rotor_flux = 0.0 (float)
async_phase_vel = 0.0 (float)
async_phase_offset = 0.0 (float)
gate_driver:
drv_fault = 0 (int)
timing_log:
general = 60988 (int)
adc_cb_i = 4578 (int)
adc_cb_dc = 14786 (int)
meas_r = 10618 (int)
meas_l = 10758 (int)
enc_calib = 11170 (int)
idx_search = 24110 (int)
foc_voltage = 11114 (int)
foc_current = 11998 (int)
spi_start = 39252 (int)
sample_now = 12912 (int)
spi_end = 45269 (int)
config:
pre_calibrated = True (bool)
pole_pairs = 2 (int)
calibration_current = 40.0 (float)
resistance_calib_max_voltage = 10.0 (float)
phase_inductance = 0.0007359320879913867 (float)
phase_resistance = 0.12363775819540024 (float)
torque_constant = 1.3506449460983276 (float)
direction = -1 (int)
motor_type = 0 (int)
current_lim = 80.0 (float)
current_lim_margin = 8.0 (float)
torque_lim = inf (float)
inverter_temp_limit_lower = 100.0 (float)
inverter_temp_limit_upper = 120.0 (float)
requested_current_range = 100.0 (float)
current_control_bandwidth = 1000.0 (float)
acim_slip_velocity = 14.706000328063965 (float)
acim_gain_min_flux = 10.0 (float)
acim_autoflux_min_Id = 10.0 (float)
acim_autoflux_enable = False (bool)
acim_autoflux_attack_gain = 10.0 (float)
acim_autoflux_decay_gain = 1.0 (float)
Controller:
error = 0x0000 (int)
input_pos = -0.36994147300720215 (float)
input_vel = 3.0 (float)
input_torque = 0.0 (float)
pos_setpoint = -0.36994147300720215 (float)
vel_setpoint = 3.0 (float)
torque_setpoint = 0.0 (float)
trajectory_done = True (bool)
vel_integrator_torque = 3.645057565401714e-41 (float)
anticogging_valid = False (bool)
config:
gain_scheduling_width = 10.0 (float)
enable_vel_limit = True (bool)
enable_current_mode_vel_limit = True (bool)
enable_gain_scheduling = False (bool)
enable_overspeed_error = True (bool)
control_mode = 2 (int)
input_mode = 1 (int)
pos_gain = 20.0 (float)
vel_gain = 9.0 (float)
vel_integrator_gain = 30.0 (float)
vel_limit = 50.0 (float)
vel_limit_tolerance = 1.2000000476837158 (float)
vel_ramp_rate = 1.0 (float)
torque_ramp_rate = 0.009999999776482582 (float)
circular_setpoints = False (bool)
circular_setpoint_range = 1.0 (float)
homing_speed = 0.25 (float)
inertia = 0.0 (float)
axis_to_mirror = 255 (int)
mirror_ratio = 1.0 (float)
load_encoder_axis = 1 (int)
input_filter_bandwidth = 2.0 (float)
anticogging: ...
move_incremental(displacement: float, from_input_pos: bool)
start_anticogging_calibration()
Thank you for any guidance on this.