odrivetool Setup
GUI Wizard
Checkout our GUI Wizard for an enhanced graphical setup experience.
Important
Make sure you have odrivetool installed before continuing (pip install --upgrade odrive
)
Configure Power Supply
Use this configuration if you’re powering the ODrive from a power supply that plugs into the grid.
Note
Most AC/DC power supplies do not accept reverse current, make sure any regenerated power is effectively dissipated.
Please adapt the ???
placeholders below.
dc_bus_overvoltage_trip_level
is the overvoltage fault level and should be set to a value that will protect it from overvoltage damage. For instance if your power supply outputs 24V a reasonable value is 30.
dc_max_positive_current
is the overcurrent fault level and should be set to a value that will protect your power source from overcurrent damage.
dc_max_negative_current
is the regenerated current fault level, it is a negative value.
odrv0.config.dc_bus_overvoltage_trip_level
= ???odrv0.config.dc_max_positive_current
= ???odrv0.config.dc_max_negative_current
= -0.01
Additionally, on ODrive S1 if you have a brake resistor connected:
odrv0.config.brake_resistor0.resistance
= ??? # resistance in Ohmsodrv0.config.brake_resistor0.enable
= Trueodrv0.clear_errors()
The following configuration assumes a battery that has an allowable voltage range of 3.3V to 4.25V. These are standard values for LiPo batteries, however you may want to change them if your battery has different ratings. Please adapt each ??? according to your battery.
# Voltage Limits bat_n_cells = ??? # number of cells in seriesodrv0.config.dc_bus_undervoltage_trip_level
= 3.3 * bat_n_cellsodrv0.config.dc_bus_overvoltage_trip_level
= 4.25 * bat_n_cells # Current Limitsodrv0.config.dc_max_positive_current
= ??? # max battery discharge currentodrv0.config.dc_max_negative_current
= - ??? # max battery charging current
Note
The maximum battery discharge and charging currents can be calculated from the batteries capacity and C ratings,
Motor Configuration
We only list configurations for the most commonly used motors here. If you’re using a different motor, you can use these scenarios as reference and read up on the documentation of the settings to adapt them for your motor.
odrv0.axis0.config.motor.motor_type
= MotorType.HIGH_CURRENTodrv0.axis0.config.motor.pole_pairs
= 7odrv0.axis0.config.motor.torque_constant
= 8.27 / 270odrv0.axis0.requested_state
= AxisState.MOTOR_CALIBRATION # [wait for end of motor beep]odrv0.save_configuration()
set_motor_thermistor_coeffs(odrv0.axis0, Rload=1000, R_25=10000, Beta=3435, Tmin=-10, Tmax=150, thermistor_bottom=True) # thermistor must be connectedodrv0.axis0.motor_thermistor.config.enabled
= Trueodrv0.save_configuration()
odrv0.axis0.config.motor.motor_type
= MotorType.HIGH_CURRENTodrv0.axis0.config.motor.pole_pairs
= 7odrv0.axis0.config.motor.torque_constant
= 8.27 / 150odrv0.axis0.requested_state
= AxisState.MOTOR_CALIBRATION # [wait for end of motor beep]odrv0.save_configuration()
# Thermistor config is not currently supported in the GUI set_motor_thermistor_coeffs(odrv0.axis0, Rload=1000, R_25=10000, Beta=3435, Tmin=-10, Tmax=150, thermistor_bottom=True) # thermistor must be connectedodrv0.axis0.motor_thermistor.config.enabled
= Trueodrv0.save_configuration()
# [motor parameters]odrv0.axis0.config.motor.motor_type
= MotorType.HIGH_CURRENTodrv0.axis0.config.motor.pole_pairs
= 15odrv0.axis0.config.motor.torque_constant
= 8.27 / 8.7 # [system config]odrv0.config.dc_max_negative_current
= -10odrv0.axis0.config.motor.calibration_current
= 4odrv0.axis0.config.calibration_lockin.current
= 4odrv0.axis0.config.motor.resistance_calib_max_voltage
= 10odrv0.axis0.config.motor.current_control_bandwidth
= 100odrv0.save_configuration()
# [calibration]odrv0.axis0.requested_state
= AxisState.MOTOR_CALIBRATIONodrv0.save_configuration()
# Thermistor config is not currently supported in the GUI set_motor_thermistor_coeffs(odrv0.axis0, Rload=1000, R_25=20000, Beta=3950, Tmin=-10, Tmax=150, thermistor_bottom=True) # thermistor must be connectedodrv0.axis0.motor_thermistor.config.enabled
= Trueodrv0.save_configuration()
Gimbal motors are motors that run at very small currents and relatively high voltages.
If 100’s of mA of current noise is “large” for you, and you do not intend to spin the motor very fast \(Ω * L << R\), and the motor is fairly large resistance (1 ohm or larger), it’s a gimbal motor. Example: Turnigy HD 5208 Brushless Gimbal Motor.
If 100’s of mA of current noise is “small” for you, it’s not a gimbal motor. Example.
The following example is for the Turnigy HD 5208 Brushless Gimbal Motor:
odrv0.axis0.config.motor.motor_type
= MotorType.GIMBALodrv0.axis0.config.motor.pole_pairs
= 7odrv0.axis0.config.motor.torque_constant
= 8.27 / 31odrv0.axis0.config.motor.phase_resistance
= 10odrv0.axis0.config.motor.phase_resistance_valid
= Trueodrv0.save_configuration()
Each of the following parameters must be set to configure the ODrive, all values should be found in the motors datasheet or otherwise provided by the manufacturer.
odrv0.axis0.config.motor.motor_type
Unless you are using a gimbal motor, this parameter should be set to
MotorType.HIGH_CURRENT
.
odrv0.axis0.config.motor.pole_pairs
This is the number of magnet poles in the rotor, divided by two. To find this, you can simply count the number of permanent magnets in the rotor, if you can see them. This is not the same as the number of coils in the stator.
odrv0.axis0.config.motor.torque_constant
If the kV rating is provided, then this should be set to 8.27 / <kV>, otherwise set this to 1. Setting this to 1 means the units of torque will be in [A] instead of [Nm]
odrv0.axis0.config.motor.calibration_current
(used for motor calibration)The recommended value to start with is half of the continuous current rating of the motor.
odrv0.axis0.config.motor.resistance_calib_max_voltage
The default is typically fine for most high current motors. For higher resistance motors you may need to increase this to pass the motor calibration. The maximum value allowed is half of the bus voltage.
odrv0.axis0.config.calibration_lockin.current
(used for encoder calibration)The recommended value to start with is also half of the continuous current rating of the motor.
Enable the motor thermistor. Please see the Thermistors page for setup.
Thermistor config is not currently supported in the GUI
odrv0.axis0.requested_state
to AxisState.MOTOR_CALIBRATION
.odrv0.save_configuration()
.
Hardcoding Motor Calibration (advanced users only)
If the phase resistance and phase inductance are known, motor calibration can be bypassed by setting
* odrv0.axis0.config.motor.phase_resistance
* odrv0.axis0.config.motor.phase_resistance
to their corresponding values, and setting both
* odrv0.axis0.config.motor.phase_resistance_valid
* odrv0.axis0.config.motor.phase_resistance_valid
to True.
Note
the motor will not spin unless all of these values are set and the configuration is saved.
Warning
If you change the motor, make sure to re-run the motor calibration.
What’s the point of motor calibration?
Motor calibration measures your motor’s electrical properties (namely phase resistance and phase inductance). It uses this information to set its current controller gains.
If using motor thermistor Please see the Thermistors page for setup.
Setting the Limits
Current limit
odrv0.axis0.config.motor.current_soft_max
= 10 # [A] (adapt this to your motor)odrv0.axis0.config.motor.current_hard_max
= 18 # [A] (should be more than soft max)
It is recommended to set the soft max to something quite weak to start with, but strong enough to overcome friction in your system with a decent margin. Once you have built confidence in the stability of the control and strength of your mechanical setup, you can increase these. For high current motors you need to turn this up to get high performance, and for low current motors such as the BotWheel you need to use something that’s lower than the examples shown above.
The recommended maximum for current_soft_max
is the continuous current rating of your motor if you are not using motor thermistor temperature feedback, and the peak current rating of your motor if you are using it.
The value for current_hard_max
is a fault trip level and should be set to a level above current_soft_max
.
The appropriate level is a tradeoff between getting nuisance faults especially during high accelerations, and ability to catch unstable current control situations.
The recommended maximum is the current your motor can handle for 50ms.
Note
The motor current and the current drawn from the power supply is not the same in general. You should not look at the power supply current to see what is going on with the motor current.
Ok, so tell me how it actually works then…
The current in the motor is only connected to the current in the power supply sometimes and other times it just cycles out of one phase and back in the other. This is what the modulation magnitude is (sometimes people call this duty cycle, but that’s a bit confusing because we use SVM not straight PWM). When the modulation magnitude is 0, the average voltage seen across the motor phases is 0, and the motor current is never connected to the power supply. When the magnitude is 100%, it is always connected, and at 50% it’s connected half the time, and cycled in just the motor half the time.
The largest effect on modulation magnitude is speed. There are other smaller factors, but in general: if the motor is still it’s not unreasonable to have 50A in the motor from 5A on the power supply. When the motor is spinning close to top speed, the power supply current and the motor current will be somewhat close to each other.
Velocity limit
odrv0.axis0.controller.config.vel_limit = 2 # [turn/s]
The motor will be limited to this speed. Again the default value is quite slow.
Temperature limits
odrv0.axis0.motor_thermistor.config.temp_limit_lower
= 100 # °C (adapt this to your motor)odrv0.axis0.motor_thermistor.config.temp_limit_upper
= 120 # °C (adapt this to your motor)
Please see Over Temperature Current Limiting for more information.
Encoder Configuration
Note
The rotor must be allowed to rotate without any biased load during encoder calibration. That means mass and weak friction loads are fine, but gravity or spring loads are not okay. Heavier loads are not advised, it is recommended that you lift any wheeled robot off the ground.
The following commands assume you have the encoder connected to the Motor I/O connector.
Connecting the A, B phases is mandatory, the Z (index pulse) is optional.
odrv0.inc_encoder0.config.cpr = 8192
odrv0.inc_encoder0.config.enabled = True
odrv0.axis0.config.load_encoder = EncoderId.INC_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.INC_ENCODER0
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]
After each subsequent reboot you need to re-run odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
before you can activate position/velocity control.
The following commands assume you have the encoder connected to the Motor I/O connector. The Z (index) signal must also be connected.
odrv0.inc_encoder0.config.cpr = 8192
odrv0.inc_encoder0.config.enabled = True
odrv0.axis0.config.load_encoder = EncoderId.INC_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.INC_ENCODER0
odrv0.config.gpio7_mode = GpioMode.DIGITAL
odrv0.axis0.commutation_mapper.config.index_gpio = 7
odrv0.axis0.commutation_mapper.config.use_index_gpio = True
odrv0.config.gpio7_mode = GpioMode.DIGITAL
# if you don't use absolute position setpoints you can skip pos_vel_mapper configuration
odrv0.axis0.pos_vel_mapper.config.index_gpio = 7
odrv0.axis0.pos_vel_mapper.config.use_index_gpio = True
odrv0.axis0.pos_vel_mapper.config.index_offset = 0
odrv0.axis0.pos_vel_mapper.config.index_offset_valid = True
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_INDEX_SEARCH
# [wait for motor to stop]
odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_INDEX_SEARCH
# [wait for motor to stop]
After each subsequent reboot you need to re-run odrv0.axis0.requested_state = AxisState.ENCODER_INDEX_SEARCH
before you can activate position/velocity control.
AMT212B-V-OD and AMT212B-V require different settings.
If your part number is AMT212B-V use Rs485EncoderMode.AMT21_POLLING
.
If your part number is AMT212B-V-OD use Rs485EncoderMode.AMT21_EVENT_DRIVEN
.
odrv0.rs485_encoder_group0.config.mode = [SEE_ABOVE]
odrv0.axis0.config.load_encoder = EncoderId.RS485_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.RS485_ENCODER0
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]
odrv0.save_configuration()
odrv0.rs485_encoder_group0.config.mode = Rs485EncoderMode.ODRIVE_OA1
odrv0.axis0.config.load_encoder = EncoderId.RS485_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.RS485_ENCODER0
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]
odrv0.save_configuration()
Note
It is recommended that you lift any wheeled robot off the ground for this step.
odrv0.axis0.config.encoder_bandwidth = 100
odrv0.hall_encoder0.config.enabled = True
odrv0.axis0.config.load_encoder = EncoderId.HALL_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.HALL_ENCODER0
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_HALL_POLARITY_CALIBRATION
# [wait for motor to stop]
odrv0.axis0.requested_state = AxisState.ENCODER_HALL_PHASE_CALIBRATION
# [wait for motor to stop]
The BotWheel has an integrated hall effect encoder, and an incremental encoder which will be used as the commutation and load encoders respectively.
Enable and Configure
# [configure commutation encoder]odrv0.axis0.config.encoder_bandwidth
= 100odrv0.hall_encoder0.config.enabled
= Trueodrv0.axis0.config.commutation_encoder
=EncoderId.HALL_ENCODER0
# [configure load encoder]odrv0.inc_encoder0.config.cpr
= 3200odrv0.inc_encoder0.config.enabled
= Trueodrv0.axis0.config.load_encoder
=EncoderId.INC_ENCODER0
odrv0.save_configuration()
Calibration
Note
It is recommended that you lift any wheeled robot off the ground for this step.
# [CALIBRATE COMMUTATION ENCODER]odrv0.axis0.requested_state
=AxisState.ENCODER_HALL_POLARITY_CALIBRATION
# [wait for motor to stop]odrv0.axis0.requested_state
=AxisState.ENCODER_HALL_PHASE_CALIBRATION
# [wait for motor to stop] # [CALIBRATE LOAD ENCODER]odrv0.axis0.requested_state
=AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]odrv0.save_configuration()
Use of the onboard encoder requires a diametric neodymium magnet mounted to the end of the motor shaft. For general purpose use we recommend the 8996 by Radial Magnets, if there is a chance that the magnet could get hot we suggest using the 9049 variant. If neither of these are suitable, any diametric neodymium magnet with a diameter ≥ 4 mm and thickness ≥2.5 mm will work.
When mounting, a 1 mm gap between the onboard encoder and the magnet is ideal.
odrv0.axis0.config.load_encoder = EncoderId.ONBOARD_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.ONBOARD_ENCODER0
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]
All of the SPI encoders are configured the same way, although as the user you must choose which SPI mode you want to use. The options are
For more specific information about each of these encoders, please see ODrive Encoder Guide.
You will have to connect one of the GPIO
odrv0.spi_encoder0.config.mode = SpiEncoderMode.SEE_ABOVE
odrv0.spi_encoder0.config.ncs_gpio = 17 # Pin 7 on J14 (MISC IO)
odrv0.axis0.config.load_encoder = EncoderId.SPI_ENCODER0
odrv0.axis0.config.commutation_encoder = EncoderId.SPI_ENCODER0
odrv0.save_configuration()
# [wait for ODrive to reboot]
odrv0.axis0.requested_state = AxisState.ENCODER_OFFSET_CALIBRATION
# [wait for motor to stop]
Please see setting up sensorless mode.
What’s the point of encoder calibration?
Encoder offset calibration measures the offset between the motor’s electrical phase and the encoder position. This is needed so the ODrive knows in which orientation to generate a stator magnetic field depending on rotor orientation.
Help, something isn’t working!
Check the encoder wiring and that the encoder is firmly connected to the motor. Check the value of dump_errors(odrv0)
and then refer to the error code documentation for details.
Once you understand the error and have fixed its cause, you may clear the error state with odrv0.clear_errors()
Enter and retry.
Motor doesn’t move?
This could be because of high inertia or strong friction or cogging torque. Try increasing odrv0.axis0.config.motor.calibration_current
[A] in steps of around 50%.
Do not go higher than half of the maximum continuous current rating of your motor.
If the motor still doesn’t move you may need to change something mechanically to reduce load torque.
Viewing Encoder Feedback
Position and velocity feedback from the load_encoder
can be found by entering
odrv0.axis0.pos_estimate # or odrv0.axis0.vel_estimate
Position Control
Warning
Make sure you have a good mechanical connection between the encoder and the motor, slip can cause disastrous oscillations or runaway.
Enter closed loop control mode by setting
odrv0.axis0.requested_state
toAxisState.CLOSED_LOOP_CONTROL
.From now on the ODrive will try to hold the motor’s position. If you try to turn it by hand, it will fight you gently. That is unless you bump up
odrv0.axis0.config.motor.current_soft_max
, in which case it will fight you more fiercely. If the motor begins to vibrate either immediately or after being disturbed you will need to lower the controller gains.Send the motor a new position setpoint by setting
odrv0.axis0.controller.input_pos
to 1.The units are in turns.
At this point you will probably want to properly tune the motor controller in order to maximize system performance.
Other Control Modes
The default control mode is unfiltered position control relative to the startup position of the axis. You may wish to use a controlled trajectory instead. Or you may wish to control position in a circular frame to allow continuous rotation forever without growing the numeric value of the setpoint too large.
You may also wish to control velocity (directly or with a ramping filter). You can also directly control the torque of the motor.
For this and more, refer to Control Modes.
What’s Next?
You can now:
Properly tune the motor controller to unlock the full potential of the ODrive.
See what other commands and parameters are available, in order to better control the ODrive.
Control the ODrive from your own program or hook it up to an existing system through one of it’s interfaces.
See how you can improve the behavior during the startup procedure.
If you have any issues or any questions please get in touch. The ODrive Community warmly welcomes you.