Skip to content

Commit

Permalink
Merge pull request odriverobotics#583 from aarondls/arduino-improvements
Browse files Browse the repository at this point in the history
Arduino improvements
  • Loading branch information
samuelsadok authored Jul 13, 2021
2 parents c17f27c + 1bb5992 commit cedc4c5
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 20 deletions.
9 changes: 7 additions & 2 deletions Arduino/ODriveArduino/ODriveArduino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,24 @@ void ODriveArduino::SetCurrent(int motor_number, float current) {
serial_ << "c " << motor_number << " " << current << "\n";
}

void ODriveArduino::TrapezoidalMove(int motor_number, float position){
void ODriveArduino::TrapezoidalMove(int motor_number, float position) {
serial_ << "t " << motor_number << " " << position << "\n";
}

float ODriveArduino::readFloat() {
return readString().toFloat();
}

float ODriveArduino::GetVelocity(int motor_number){
float ODriveArduino::GetVelocity(int motor_number) {
serial_<< "r axis" << motor_number << ".encoder.vel_estimate\n";
return ODriveArduino::readFloat();
}

float ODriveArduino::GetPosition(int motor_number) {
serial_ << "r axis" << motor_number << ".encoder.pos_estimate\n";
return ODriveArduino::readFloat();
}

int32_t ODriveArduino::readInt() {
return readString().toInt();
}
Expand Down
14 changes: 2 additions & 12 deletions Arduino/ODriveArduino/ODriveArduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,10 @@
#define ODriveArduino_h

#include "Arduino.h"
#include "ODriveEnums.h"

class ODriveArduino {
public:
enum AxisState_t {
AXIS_STATE_UNDEFINED = 0, //<! will fall through to idle
AXIS_STATE_IDLE = 1, //<! disable PWM and do nothing
AXIS_STATE_STARTUP_SEQUENCE = 2, //<! the actual sequence is defined by the config.startup_... flags
AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3, //<! run all calibration procedures, then idle
AXIS_STATE_MOTOR_CALIBRATION = 4, //<! run motor calibration
AXIS_STATE_SENSORLESS_CONTROL = 5, //<! run sensorless control
AXIS_STATE_ENCODER_INDEX_SEARCH = 6, //<! run encoder index search
AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7, //<! run encoder offset calibration
AXIS_STATE_CLOSED_LOOP_CONTROL = 8 //<! run closed loop control
};

ODriveArduino(Stream& serial);

// Commands
Expand All @@ -30,6 +19,7 @@ class ODriveArduino {
void TrapezoidalMove(int motor_number, float position);
// Getters
float GetVelocity(int motor_number);
float GetPosition(int motor_number);
// General params
float readFloat();
int32_t readInt();
Expand Down
199 changes: 199 additions & 0 deletions Arduino/ODriveArduino/ODriveEnums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@

#ifndef ODriveEnums_h
#define ODriveEnums_h

/* TODO: This file is dangerous because the enums could potentially change between API versions. Should transmit as part of the JSON.
** To regenerate this file, nagivate to the top level of the ODrive repository and run:
** python Firmware/interface_generator_stub.py --definitions Firmware/odrive-interface.yaml --template tools/arduino_enums_template.j2 --output Arduino/ODriveArduino/ODriveEnums.h
*/

// ODrive.GpioMode
enum GpioMode {
GPIO_MODE_DIGITAL = 0,
GPIO_MODE_DIGITAL_PULL_UP = 1,
GPIO_MODE_DIGITAL_PULL_DOWN = 2,
GPIO_MODE_ANALOG_IN = 3,
GPIO_MODE_UART_A = 4,
GPIO_MODE_UART_B = 5,
GPIO_MODE_UART_C = 6,
GPIO_MODE_CAN_A = 7,
GPIO_MODE_I2C_A = 8,
GPIO_MODE_SPI_A = 9,
GPIO_MODE_PWM = 10,
GPIO_MODE_ENC0 = 11,
GPIO_MODE_ENC1 = 12,
GPIO_MODE_ENC2 = 13,
GPIO_MODE_MECH_BRAKE = 14,
GPIO_MODE_STATUS = 15,
};

// ODrive.StreamProtocolType
enum StreamProtocolType {
STREAM_PROTOCOL_TYPE_FIBRE = 0,
STREAM_PROTOCOL_TYPE_ASCII = 1,
STREAM_PROTOCOL_TYPE_STDOUT = 2,
STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT = 3,
};

// ODrive.Can.Protocol
enum Protocol {
PROTOCOL_SIMPLE = 0x00000001,
};

// ODrive.Axis.AxisState
enum AxisState {
AXIS_STATE_UNDEFINED = 0,
AXIS_STATE_IDLE = 1,
AXIS_STATE_STARTUP_SEQUENCE = 2,
AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3,
AXIS_STATE_MOTOR_CALIBRATION = 4,
AXIS_STATE_ENCODER_INDEX_SEARCH = 6,
AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7,
AXIS_STATE_CLOSED_LOOP_CONTROL = 8,
AXIS_STATE_LOCKIN_SPIN = 9,
AXIS_STATE_ENCODER_DIR_FIND = 10,
AXIS_STATE_HOMING = 11,
AXIS_STATE_ENCODER_HALL_POLARITY_CALIBRATION = 12,
AXIS_STATE_ENCODER_HALL_PHASE_CALIBRATION = 13,
};

// ODrive.Encoder.Mode
enum EncoderMode {
ENCODER_MODE_INCREMENTAL = 0,
ENCODER_MODE_HALL = 1,
ENCODER_MODE_SINCOS = 2,
ENCODER_MODE_SPI_ABS_CUI = 256,
ENCODER_MODE_SPI_ABS_AMS = 257,
ENCODER_MODE_SPI_ABS_AEAT = 258,
ENCODER_MODE_SPI_ABS_RLS = 259,
ENCODER_MODE_SPI_ABS_MA732 = 260,
};

// ODrive.Controller.ControlMode
enum ControlMode {
CONTROL_MODE_VOLTAGE_CONTROL = 0,
CONTROL_MODE_TORQUE_CONTROL = 1,
CONTROL_MODE_VELOCITY_CONTROL = 2,
CONTROL_MODE_POSITION_CONTROL = 3,
};

// ODrive.Controller.InputMode
enum InputMode {
INPUT_MODE_INACTIVE = 0,
INPUT_MODE_PASSTHROUGH = 1,
INPUT_MODE_VEL_RAMP = 2,
INPUT_MODE_POS_FILTER = 3,
INPUT_MODE_MIX_CHANNELS = 4,
INPUT_MODE_TRAP_TRAJ = 5,
INPUT_MODE_TORQUE_RAMP = 6,
INPUT_MODE_MIRROR = 7,
INPUT_MODE_TUNING = 8,
};

// ODrive.Motor.MotorType
enum MotorType {
MOTOR_TYPE_HIGH_CURRENT = 0,
MOTOR_TYPE_GIMBAL = 2,
MOTOR_TYPE_ACIM = 3,
};

// ODrive.Error
enum ODriveError {
ODRIVE_ERROR_NONE = 0x00000000,
ODRIVE_ERROR_CONTROL_ITERATION_MISSED = 0x00000001,
ODRIVE_ERROR_DC_BUS_UNDER_VOLTAGE = 0x00000002,
ODRIVE_ERROR_DC_BUS_OVER_VOLTAGE = 0x00000004,
ODRIVE_ERROR_DC_BUS_OVER_REGEN_CURRENT = 0x00000008,
ODRIVE_ERROR_DC_BUS_OVER_CURRENT = 0x00000010,
ODRIVE_ERROR_BRAKE_DEADTIME_VIOLATION = 0x00000020,
ODRIVE_ERROR_BRAKE_DUTY_CYCLE_NAN = 0x00000040,
ODRIVE_ERROR_INVALID_BRAKE_RESISTANCE = 0x00000080,
};

// ODrive.Can.Error
enum CanError {
CAN_ERROR_NONE = 0x00000000,
CAN_ERROR_DUPLICATE_CAN_IDS = 0x00000001,
};

// ODrive.Axis.Error
enum AxisError {
AXIS_ERROR_NONE = 0x00000000,
AXIS_ERROR_INVALID_STATE = 0x00000001,
AXIS_ERROR_WATCHDOG_TIMER_EXPIRED = 0x00000800,
AXIS_ERROR_MIN_ENDSTOP_PRESSED = 0x00001000,
AXIS_ERROR_MAX_ENDSTOP_PRESSED = 0x00002000,
AXIS_ERROR_ESTOP_REQUESTED = 0x00004000,
AXIS_ERROR_HOMING_WITHOUT_ENDSTOP = 0x00020000,
AXIS_ERROR_OVER_TEMP = 0x00040000,
AXIS_ERROR_UNKNOWN_POSITION = 0x00080000,
};

// ODrive.Motor.Error
enum MotorError {
MOTOR_ERROR_NONE = 0x00000000,
MOTOR_ERROR_PHASE_RESISTANCE_OUT_OF_RANGE = 0x00000001,
MOTOR_ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE = 0x00000002,
MOTOR_ERROR_DRV_FAULT = 0x00000008,
MOTOR_ERROR_CONTROL_DEADLINE_MISSED = 0x00000010,
MOTOR_ERROR_MODULATION_MAGNITUDE = 0x00000080,
MOTOR_ERROR_CURRENT_SENSE_SATURATION = 0x00000400,
MOTOR_ERROR_CURRENT_LIMIT_VIOLATION = 0x00001000,
MOTOR_ERROR_MODULATION_IS_NAN = 0x00010000,
MOTOR_ERROR_MOTOR_THERMISTOR_OVER_TEMP = 0x00020000,
MOTOR_ERROR_FET_THERMISTOR_OVER_TEMP = 0x00040000,
MOTOR_ERROR_TIMER_UPDATE_MISSED = 0x00080000,
MOTOR_ERROR_CURRENT_MEASUREMENT_UNAVAILABLE = 0x00100000,
MOTOR_ERROR_CONTROLLER_FAILED = 0x00200000,
MOTOR_ERROR_I_BUS_OUT_OF_RANGE = 0x00400000,
MOTOR_ERROR_BRAKE_RESISTOR_DISARMED = 0x00800000,
MOTOR_ERROR_SYSTEM_LEVEL = 0x01000000,
MOTOR_ERROR_BAD_TIMING = 0x02000000,
MOTOR_ERROR_UNKNOWN_PHASE_ESTIMATE = 0x04000000,
MOTOR_ERROR_UNKNOWN_PHASE_VEL = 0x08000000,
MOTOR_ERROR_UNKNOWN_TORQUE = 0x10000000,
MOTOR_ERROR_UNKNOWN_CURRENT_COMMAND = 0x20000000,
MOTOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x40000000,
MOTOR_ERROR_UNKNOWN_VBUS_VOLTAGE = 0x80000000,
MOTOR_ERROR_UNKNOWN_VOLTAGE_COMMAND = 0x100000000,
MOTOR_ERROR_UNKNOWN_GAINS = 0x200000000,
MOTOR_ERROR_CONTROLLER_INITIALIZING = 0x400000000,
MOTOR_ERROR_UNBALANCED_PHASES = 0x800000000,
};

// ODrive.Controller.Error
enum ControllerError {
CONTROLLER_ERROR_NONE = 0x00000000,
CONTROLLER_ERROR_OVERSPEED = 0x00000001,
CONTROLLER_ERROR_INVALID_INPUT_MODE = 0x00000002,
CONTROLLER_ERROR_UNSTABLE_GAIN = 0x00000004,
CONTROLLER_ERROR_INVALID_MIRROR_AXIS = 0x00000008,
CONTROLLER_ERROR_INVALID_LOAD_ENCODER = 0x00000010,
CONTROLLER_ERROR_INVALID_ESTIMATE = 0x00000020,
CONTROLLER_ERROR_INVALID_CIRCULAR_RANGE = 0x00000040,
CONTROLLER_ERROR_SPINOUT_DETECTED = 0x00000080,
};

// ODrive.Encoder.Error
enum EncoderError {
ENCODER_ERROR_NONE = 0x00000000,
ENCODER_ERROR_UNSTABLE_GAIN = 0x00000001,
ENCODER_ERROR_CPR_POLEPAIRS_MISMATCH = 0x00000002,
ENCODER_ERROR_NO_RESPONSE = 0x00000004,
ENCODER_ERROR_UNSUPPORTED_ENCODER_MODE = 0x00000008,
ENCODER_ERROR_ILLEGAL_HALL_STATE = 0x00000010,
ENCODER_ERROR_INDEX_NOT_FOUND_YET = 0x00000020,
ENCODER_ERROR_ABS_SPI_TIMEOUT = 0x00000040,
ENCODER_ERROR_ABS_SPI_COM_FAIL = 0x00000080,
ENCODER_ERROR_ABS_SPI_NOT_READY = 0x00000100,
ENCODER_ERROR_HALL_NOT_CALIBRATED_YET = 0x00000200,
};

// ODrive.SensorlessEstimator.Error
enum SensorlessEstimatorError {
SENSORLESS_ESTIMATOR_ERROR_NONE = 0x00000000,
SENSORLESS_ESTIMATOR_ERROR_UNSTABLE_GAIN = 0x00000001,
SENSORLESS_ESTIMATOR_ERROR_UNKNOWN_CURRENT_MEASUREMENT = 0x00000002,
};

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ void loop() {
int motornum = c-'0';
int requested_state;

requested_state = ODriveArduino::AXIS_STATE_MOTOR_CALIBRATION;
requested_state = AXIS_STATE_MOTOR_CALIBRATION;
Serial << "Axis" << c << ": Requesting state " << requested_state << '\n';
if(!odrive.run_state(motornum, requested_state, true)) return;

requested_state = ODriveArduino::AXIS_STATE_ENCODER_OFFSET_CALIBRATION;
requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION;
Serial << "Axis" << c << ": Requesting state " << requested_state << '\n';
if(!odrive.run_state(motornum, requested_state, true, 25.0f)) return;

requested_state = ODriveArduino::AXIS_STATE_CLOSED_LOOP_CONTROL;
requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL;
Serial << "Axis" << c << ": Requesting state " << requested_state << '\n';
if(!odrive.run_state(motornum, requested_state, false /*don't wait*/)) return;
}
Expand Down Expand Up @@ -112,8 +112,7 @@ void loop() {
unsigned long start = millis();
while(millis() - start < duration) {
for (int motor = 0; motor < 2; ++motor) {
odrive_serial << "r axis" << motor << ".encoder.pos_estimate\n";
Serial << odrive.readFloat() << '\t';
Serial << odrive.GetPosition(motor) << '\t';
}
Serial << '\n';
}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Please add a note of your changes below this heading if you make a Pull Request.
* Added torque mirroring to INPUT_MODE_MIRROR
* `mechanical_power_bandwidth`, `electrical_power_bandwidth`, `spinout_electrical_power_threshold`, `spinout_mechanical_power_threshold` added to `controller.config` for spinout detection.
* `mechanical_power` and `electrical_power` added to `controller`.
* Added autogenerated enums header file [ODriveEnums.h](../Arduino/ODriveArduino/ODriveEnums.h) for Arduino use. Created Jinja template and edited Makefile to autogenerate it. Reflected change in Dockerfile and added note in developer-guide markdown file for updating ODriveEnums.h alongside enums.py.
* Added GetPosition member function in ODriveArduino class to complement existing GetVelocity, SetVelocity, and SetPosition functions.

### Changed
* Step/dir performance improved! Dual axis step rates up to 250kHz have been tested
Expand Down Expand Up @@ -71,6 +73,8 @@ Please add a note of your changes below this heading if you make a Pull Request.
* Added `torque_mirror_ratio` and use it to feed-forward `controller_.torque_output` in `INPUT_MODE_MIRROR`
* Accumulate integer steps in step/dir to avoid float precision errors
* Circular setpoint mode must be enabled when the step/dir interface is used.
* Replaced inline enum in ODriveArduino class by including new autogenerated ODriveEnums.h header file.
* Changed the example ODriveArduinoTest.ino file to reflect the new GetPosition member function. Also removed the scope resolution operator to access the enums as it can now be accessed from the global namespace.

### API Migration Notes
* `axis.config.turns_per_step` changed to `axis.controller.config.steps_per_circular_range`
Expand Down
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ CMD \
--definitions odrive-interface.yaml \
--template ../tools/enums_template.j2 \
--output ../tools/odrive/enums.py && \
python interface_generator_stub.py \
--definitions odrive-interface.yaml \
--template ../tools/arduino_enums_template.j2 \
--output ../Arduino/ODriveArduino/ODriveEnums.h && \
# Hack around Tup's dependency on FUSE
tup init && \
tup generate build.sh && \
Expand Down
1 change: 1 addition & 0 deletions Firmware/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ all:
@$(PY_CMD) ../tools/odrive/version.py --output autogen/version.c
@tup --quiet -no-environ-check
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/enums_template.j2 --output ../tools/odrive/enums.py
@$(PY_CMD) interface_generator_stub.py --definitions odrive-interface.yaml --template ../tools/arduino_enums_template.j2 --output ../Arduino/ODriveArduino/ODriveEnums.h

# Copy libfibre files to odrivetool if they were built
@ ! test -f "fibre-cpp/build/libfibre-linux-amd64.so" || cp fibre-cpp/build/libfibre-linux-amd64.so ../tools/odrive/pyfibre/fibre/
Expand Down
2 changes: 1 addition & 1 deletion docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,4 @@ When filing a PR please go through this checklist:
- Also, for each removed/moved/renamed API item use your IDE's search feature to search for occurrences of this name. Update the places you found (this will usually be documentation and test scripts).
- If you added things to `odrive-interface.yaml` make sure the new things have decent documentation in the YAML file. We don't expect 100% coverage but use good sense of what to document.
- Make sure your PR doesn't contain spurious changes that unnecessarily add or remove whitespace. These add noise and make the reviewer's lifes harder.
- If you changed any enums in `odrive-interface.yaml`, make sure you update [enums.py](../tools/odrive/enums.py). The file includes instructions on how to do this. Check the diff to verify that none of the existing enumerators changed their value.
- If you changed any enums in `odrive-interface.yaml`, make sure you update [enums.py](../tools/odrive/enums.py) and [ODriveEnums.h](../Arduino/ODriveArduino/ODriveEnums.h). The file includes instructions on how to do this. Check the diff to verify that none of the existing enumerators changed their value.
23 changes: 23 additions & 0 deletions tools/arduino_enums_template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

#ifndef ODriveEnums_h
#define ODriveEnums_h

/* TODO: This file is dangerous because the enums could potentially change between API versions. Should transmit as part of the JSON.
** To regenerate this file, nagivate to the top level of the ODrive repository and run:
** python Firmware/interface_generator_stub.py --definitions Firmware/odrive-interface.yaml --template tools/arduino_enums_template.j2 --output Arduino/ODriveArduino/ODriveEnums.h
*/

[%- for _, enum in value_types.items() %]
[%- if enum.is_enum %]

// [[enum.fullname]]
enum [[(enum.parent.name if enum.name in ['Error', 'Mode'] else '') + enum.name ]] {
[%- for k, value in enum['values'].items() %]
[[((((enum.parent.name if enum.name in ['Error', 'Mode'] else '') + enum.name) | to_macro_case) + "_" + (k | to_macro_case)).ljust(40)]] = [% if enum.is_flags %]0x[['%08x' | format(value.value)]][% else %][[value.value]][% endif %],
[%- endfor %]
};
[%- endif %]
[%- endfor %]

#endif

0 comments on commit cedc4c5

Please sign in to comment.