This documention is in the process of being updated for ODrive Pro


[0.6.1] - 2022-06-17


  • Improved error reporting

  • Encoder calibration no longer uses <axis>.config.motor.calibration_current as current but rather <axis>.config.calibration_lockin.current

  • Fixed unknown command on first ASCII protocol command after startup caused by a spurious byte in the RX buffer

  • Possibly fixed occasional UART TX hang that would occur during closed loop control

  • Fixed winusb.sys not automatically loading

  • Fixed various USB enumeration errors (string descriptors showing as ‘Љ’, ‘(error)’ or in wrong places and errors like “could not read langid”, “couldn’t open device” and “not available”)

  • Restored ability to disable CAN Simple messages by setting the interval to 0 (regression in 0.6.0)

  • Fixed wrong velocity used during second half of hall polarity calibration

  • Fixed homing getting stuck in ProcedureResult.BUSY

  • Fixed AxisState.FULL_CALIBRATION_SEQUENCE when using onboard encoder

  • Fixed FET temperature readings on ODrive Pro. The reported temperature was higher than the actual temperature:

    • reported 100°C => actual 103°C

    • reported 40°C => actual 29°C

    • actual = old_reported * 0.92 - 7.79

  • Fixed CAN bug where the ODrive would stop handling incoming messages after a large number of incoming messages.

  • Fixed PWM input on ODrive Pro

  • Increased default velocity limit from 2 turns/s to 10 turns/s. This also increases the default absolute velocity margin, leading to less overspeed errors on first use.

  • Decreased cutoff frequency of incremental encoder input filters


  • Landing page popup when ODrive is plugged in while Chrome browser is open

  • <axis>.config.index_search_at_target_vel_only option to make index search sensitive straight away or if it needs to get to target speed first.

  • SpiEncoder.delay to compensate for slow SPI encoders

  • Exposed more hall effect sensor state at HallEncoder.raw_hall_state, HallEncoder.Config.hall_polarity and HallEncoder.Config.hall_polarity_calibrated

  • Diagnostics variable <odrv>.can.n_restarts (a CAN restart isn’t necessarily an error - if the device is not on any bus the CAN interface will restart continuously)

  • Id,q feedforward terms <axis>.motor.input_id, <axis>.motor.input_iq

  • More tweak options in <axis>.config.motor: ff_pm_flux_linkage, torque_model_l_d, torque_model_l_q (see API reference for details)

  • Add timeout for calibration functions (e.g. when target current can’t be reached because it’s above the current limit)

  • CAN Simple protocol command MSG_GET_TEMPERATURE

  • CAN RX count (for debugging): <odrv>.can.n_rx


  • Increased maximum modulation depth from 80% to 100% (that means more max velocity for the same DC voltage).

  • Most variables in axis.config.motor require the axis to be in idle state to take effect.

  • Motor calibration is persistent by default on next save_configuration() (previously it required setting pre_calibrated)

  • At startup, the axis previously waited in UNDEFINED state for 2 seconds to wait for initialization to complete. Now it goes into IDLE state immediately. Use (axis.active_errors & ODriveError.INITIALIZING) == 0 to check for readiness.

  • Requesting an unknown state drops to IDLE without setting any error

  • <axis>.acim_estimator units changed

  • New LED behavior: NOT READY: Blue, READY: Cyan, RUNNING: Green, ERROR: Red

API Migration Guide

  • <odrv>.error removed. Use <axis>.active_errors and <axis>.disarm_reason instead.

  • <odrv>.n_issues removed. use <odrv>.issues.length instead.

  • <odrv>.config.error_gpio_pin moved to <axis>.config.error_gpio_pin

  • <axis>.motor.config moved to <axis>.config.motor

  • <axis>.motor.config.current_lim moved to <axis>.config.motor.current_soft_max and <odrv>.config.inverter0.current_soft_max.

  • <axis>.motor.config.current_lim_margin removed. Use <axis>.config.motor.current_hard_max and instead. A separate limit for the inverter was added under <odrv>.config.inverter0.current_hard_max and should usually not be modified.

  • <axis>.motor.config.pre_calibrated and <axis>.motor.is_calibrated replaced by <axis>.config.motor.phase_resistance_inductance_valid. This new variable goes to True automatically after successful motor calibration.

  • <axis>.motor.config.torque_lim replaced by <axis>.config.torque_soft_min and <axis>.config.torque_soft_max.

  • <axis>.motor.config.requested_current_range removed. It now gets internally set to 1.1 * min(<odrv>.config.inverter0.current_hard_max, <axis>.config.motor.current_hard_max).

  • <axis>.motor.current_control moved to <axis>.foc

  • <axis>.motor.is_armed moved to <axis>.is_armed

  • <axis>.motor.I_bus_hard_min and <axis>.motor.I_bus_hard_max moved to <axis>.I_bus_hard_min and <axis>.I_bus_hard_max

  • <axis>.motor.dc_calib_tau removed

  • <axis>.motor.last_error_time removed. Use <axis>.disarm_time instead.

  • <axis>.sensorless_estimator.config.observer_gain moved to <axis>.config.motor.sensorless_observer_gain

  • <axis>.sensorless_estimator.config.pll_bandwidth moved to <axis>.config.motor.sensorless_pll_bandwidth

  • <axis>.sensorless_estimator.config.pm_flux_linkage moved to <axis>.config.motor.sensorless_pm_flux_linkage and made optional by <axis>.config.motor.sensorless_pm_flux_linkage_valid (by default implied by <axis>.config.motor.pole_pairs and <axis>.config.motor.torque_constant)

  • Reorganized brake resistor API. On ODrive S1 all brake-resistor related variables got grouped together. On ODrive Pro (which supports no brake resistor) they got removed.

    • <odrv>.brake_resistor_armed moved to <odrv>.brake_resistor0.is_armed or removed

    • <odrv>.brake_resistor_saturated moved to <odrv>.brake_resistor0.is_saturated or removed

    • <odrv>.config.enable_brake_resistor moved to <odrv>.config.brake_resistor0.enable or removed

    • <odrv>.config.brake_resistance moved to <odrv>.config.brake_resistor0.resistance or removed

    • <odrv>.config.enable_dc_bus_overvoltage_ramp moved to <odrv>.config.brake_resistor0.enable_dc_bus_overvoltage_ramp or removed

    • <odrv>.config.dc_bus_overvoltage_ramp_start moved to <odrv>.config.brake_resistor0.dc_bus_overvoltage_ramp_start or removed

    • <odrv>.config.dc_bus_overvoltage_ramp_end moved to <odrv>.config.brake_resistor0.dc_bus_overvoltage_ramp_end or removed

  • <axis>.controller.status, <axis>.motor.error and <axis>.error removed. Replaced by axis.active_errors and axis.disarm_reason.

[0.6.0] - 2022-02-21


  • Fixed CAN command MSG_GET_IQ to return Iq setpoint and Iq measurement as documented

  • Fixed SPI hang during high CPU load

  • Make current limit active in lockin spin

  • Fixed violation of velocity ramp limit when increasing the velocity limit in InputMode.VEL_RAMP mode while input_vel is higher than the old velocity limit


  • Added support for ODrive Pro

  • Added motor.resistance_calibration_I_beta for debugging of MOTOR_UNBALANCED_PHASES error

  • Added bursty sine stimulus (InputMode.TUNING)

  • issues count n_issues to indicated unexpected issues (probably bugs)

  • Added controller.config.use_commutation_vel

  • Added 8kHz logging (“oscilloscope”) of arbitrary memory locations (internal use only)

  • Added firmware commit hash hex(<odrv>.commit_hash)

  • Make DRV config parameters configurable (internal use only)

  • odrivetool: Added experimental machine-centric configuration system. See example_config, status(example_config), apply(example_config) and calibrate(example_config).


  • Changing the CAN node ID now requires a reboot to take effect.

  • Reboot on hard fault (n_issues will init to 1 after reboot)

  • Anticogging temporarily not available

  • Encoder object split into multiple objects (hardware-dependent components, estimator, interpolator, commutation mapper and load mapper). See API Migration Notes below.

  • Hall effect innovation residual size can be non-uniform

  • when an illegal hall state is encountered, rather than using the last hall state to derive a measurement residual, the measurement residual is returned as 0

  • Hall calibration runs in both directions

  • On transient SPI errors, the encoder estimator is left in prediction mode rather than correcting towards the last known measurement

  • SPI error rate low pass filter gain increased from 1 to 50 so the error clears faster (~100ms) when the encoder is connected

  • Snap-to-zero-velocity is no longer supported for SPI encoders because it was not sensible for most SPI encoders and it saves the user from specifying CPR for SPI encoders.

  • Interpolator is not limited to equisized steps

  • Encoder offset calibration only checks at the end if the index was found, so offset calibration and index search can happen in one.

  • Encoder offset calibration uses least squares line fit

  • When absolute setpoints are used (controller.config.absolute_setpoints), position control is not allowed before homing.

  • The CAN messages MSG_GET_ENCODER_ERROR (0x004), MSG_GET_ENCODER_COUNT (0x00a) and MSG_GET_SENSORLESS_ESTIMATES (0x015) got removed

  • Allow responses on CAN Simple if DLC == 0

  • odrivetool: Changed enum naming convention from AXIS_STATE_CLOSED_LOOP_CONTROL to AxisState.CLOSED_LOOP_CONTROL

API Migration Notes

  • <axis>.encoder was removed

    • Instead of <axis>.encoder.config.mode use <axis>.config.load_encoder and <axis>.config.commutation_encoder. To set the SPI encoder type, use <odrv>.spi_encoderX.config.mode.

    • Instead of <axis>.encoder.pos_estimate/<axis>.encoder.pos_circular use <axis>.load_mapper.pos_rel or <axis>.load_mapper.pos_abs and make sure <axis>.controller.config.circular_setpoints is set correctly.

    • Instead of <axis>.encoder.vel_estimate use <axis>.load_mapper.vel.

    • Instead of <axis>.encoder.set_linear_count() use <axis>.load_mapper.set_abs_pos().

    • Instead of <axis>.encoder.config.bandwidth use <axis>.config.encoder_bandwidth.

    • Encoder type specific variables and config can be accessed via <odrv>.inc_encoder0, <odrv>.inc_encoder1, <odrv>.spi_encoder0, <odrv>.spi_encoder1, <odrv>.hall_encoder0, <odrv>.hall_encoder1.

    • <axis>.encoder.config.calib_range moved to <axis>.config.calib_range

    • <axis>.encoder.config.calib_scan_distance moved to <axis>.config.calib_scan_distance

    • <axis>.encoder.config.calib_scan_omega replaced by <axis>.config.calib_scan_vel (changed units)

    • Instead of <axis>.encoder.error use <odrv>.spi_encoder0.status (or similar) for runtime (active) errors and <axis>.procedure_result after calibration for calibration errors.

  • <axis>.config.general_lockin.finish_on_enc_idx was removed without replacement.

  • Motor error codes that can only occur during motor calibration were removed. Inspect <axis>.procedure_result instead.

  • <axis>.controller.error was replaced by <axis>.controller.status which has a different type

  • <axis>.controller.anticogging_valid was removed without replacement.

  • <axis>.controller.load_encoder_axis was removed. Use <axis>.config.load_encoder instead.

  • <axis>.controller.last_error_time was removed. Use <axis>.disarm_time instead.

  • <axis>.sensorless_estimator.vel_estimate was removed. Use <axis>.sensorless_estimator.phase_vel instead (different unit).

[0.5.4] and older

See here for older releases.