Merge branch 'devel' into Endstops

This commit is contained in:
Unknown committed 2018-09-27 20:42:30 -04:00
commit dc693ebd9c
36 files changed
+779 -273

No files matched your search

+17 -2
View File
@@ -2,10 +2,23 @@
Please add a note of your changes below this heading if you make a Pull Request.
# Unreleased
## Fixed
* Serious reliability issue with USB communication where packets on Native and the CDC interface would collide with each other.
### Added
* **Trapezoidal Trajectory Planner**
* -Wdouble-promotion warning to compilation
### Changed
* Make python tools compatible with python 2.7 (so it can be used with ROS)
* Threading API constructor can't take the daemon parameter, so all thread creation had to be expanded out.
* `TimeoutError` isn't defined, but it makes for more readable code, so I defined it as an OSError subclass.
* `ModuleNotFoundError` is replaced by the older ImportError.
* Print function imported from future
# Releases
## [0.4.4] - 2018-09-18
### Fixed
* Serious reliability issue with USB communication where packets on Native and the CDC interface would collide with each other.
## [0.4.3] - 2018-08-30
### Added
* `min_endstop` and `max_endstop` objects can be configured on GPIO
@@ -18,8 +31,10 @@ Please add a note of your changes below this heading if you make a Pull Request.
* Hardcoded maximum inductance now 2500 uH.
### Fixed
* Incorrect shifting of offset during index callback
* Once you got an axis error `ERROR_INVALID_STATE` you could never clear it
* Char to int conversion to read motornum on arduino example
* GPIO above #5 would not be used correctly in some cases
## [0.4.2] - 2018-08-04
### Added
+13 -16
View File
@@ -4,6 +4,7 @@
"name": "Win32",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/fibre/cpp/include/**",
"${workspaceRoot}/MotorControl",
"${workspaceRoot}/Drivers/DRV8301",
"${workspaceRoot}/Board/v3/Inc",
@@ -16,12 +17,8 @@
"${workspaceRoot}/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS",
"${workspaceRoot}/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/include",
"${workspaceRoot}/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F",
"c:/program files (x86)/gnu tools arm embedded/6 2017-q1-update/arm-none-eabi/include/c++/6.3.1",
"c:/program files (x86)/gnu tools arm embedded/6 2017-q1-update/arm-none-eabi/include/c++/6.3.1/arm-none-eabi",
"c:/program files (x86)/gnu tools arm embedded/6 2017-q1-update/arm-none-eabi/include/c++/6.3.1/backward",
"c:/program files (x86)/gnu tools arm embedded/6 2017-q1-update/lib/gcc/arm-none-eabi/6.3.1/include",
"c:/program files (x86)/gnu tools arm embedded/6 2017-q1-update/lib/gcc/arm-none-eabi/6.3.1/include-fixed",
"c:/program files (x86)/gnu tools arm embedded/6 2017-q1-update/arm-none-eabi/include"
"${ARM_GCC_ROOT}/arm-none-eabi/include/**",
"${ARM_GCC_ROOT}/lib/gcc/arm-none-eabi/**"
],
"defines": [
"STM32F405xx",
@@ -38,10 +35,11 @@
"browse": {
"path": [
"${workspaceRoot}",
"C:/Program Files (x86)/GNU Tools ARM Embedded"
"${ARM_GCC_ROOT}"
],
"limitSymbolsToIncludedHeaders": true
},
"compilerPath": "${ARM_GCC_ROOT}/bin/arm-none-eabi-gcc.exe -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float",
"cStandard": "c11",
"cppStandard": "c++14"
},
@@ -49,6 +47,7 @@
"name": "Linux",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/fibre/cpp/include/**",
"${workspaceRoot}/MotorControl",
"${workspaceRoot}/Drivers/DRV8301",
"${workspaceRoot}/Board/v3/Inc",
@@ -61,8 +60,8 @@
"${workspaceRoot}/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/Legacy",
"${workspaceRoot}/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include",
"${workspaceRoot}/Board/v3/Drivers/CMSIS/Include",
"/usr/lib/gcc/arm-none-eabi/4.9.3/include",
"/usr/lib/arm-none-eabi/include"
"${ARM_GCC_ROOT}/arm-none-eabi/include/**",
"${ARM_GCC_ROOT}/lib/gcc/arm-none-eabi/**"
],
"defines": [
"STM32F405xx",
@@ -78,8 +77,7 @@
"browse": {
"path": [
"${workspaceRoot}",
"/usr/lib/gcc/arm-none-eabi/4.9.3/include",
"/usr/lib/arm-none-eabi/include"
"${ARM_GCC_ROOT}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
@@ -92,6 +90,7 @@
"name": "Mac",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/fibre/cpp/include/**",
"${workspaceRoot}/MotorControl",
"${workspaceRoot}/Drivers/DRV8301",
"${workspaceRoot}/Board/v3/Inc",
@@ -104,10 +103,8 @@
"${workspaceRoot}/Board/v3/Drivers/STM32F4xx_HAL_Driver/Inc/Legacy",
"${workspaceRoot}/Board/v3/Drivers/CMSIS/Device/ST/STM32F4xx/Include",
"${workspaceRoot}/Board/v3/Drivers/CMSIS/Include",
"/usr/local/Caskroom/gcc-arm-embedded/6-2017-q2-update/gcc-arm-none-eabi-6-2017-q2-update/arm-none-eabi/include",
"/usr/local/Caskroom/gcc-arm-embedded/6-2017-q2-update/gcc-arm-none-eabi-6-2017-q2-update/arm-none-eabi/include/c++/6.3.1",
"/usr/local/Caskroom/gcc-arm-embedded/6-2017-q2-update/gcc-arm-none-eabi-6-2017-q2-update/arm-none-eabi/include/c++/6.3.1/arm-none-eabi",
"/usr/local/Caskroom/gcc-arm-embedded/6-2017-q2-update/gcc-arm-none-eabi-6-2017-q2-update/lib/gcc/arm-none-eabi/6.3.1/include"
"${ARM_GCC_ROOT}/arm-none-eabi/include/**",
"${ARM_GCC_ROOT}/lib/gcc/arm-none-eabi/**"
],
"defines": [
"STM32F405xx",
@@ -123,7 +120,7 @@
"browse": {
"path": [
"${workspaceRoot}",
"/usr/local/Caskroom/gcc-arm-embedded/"
"${ARM_GCC_ROOT}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016 Oskar Weigl (madcowswe)
Copyright (c) 2016-2018 Oskar Weigl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+5 -2
View File
@@ -7,11 +7,12 @@
#include "odrive_main.h"
Axis::Axis(const AxisHardwareConfig_t& hw_config,
AxisConfig_t& config,
Config_t& config,
Encoder& encoder,
SensorlessEstimator& sensorless_estimator,
Controller& controller,
Motor& motor,
TrapezoidalTrajectory& trap,
Endstop& min_endstop,
Endstop& max_endstop)
: hw_config_(hw_config),
@@ -20,6 +21,7 @@ Axis::Axis(const AxisHardwareConfig_t& hw_config,
sensorless_estimator_(sensorless_estimator),
controller_(controller),
motor_(motor),
trap_(trap),
min_endstop_(min_endstop),
max_endstop_(max_endstop)
{
@@ -27,6 +29,7 @@ Axis::Axis(const AxisHardwareConfig_t& hw_config,
sensorless_estimator_.axis_ = this;
controller_.axis_ = this;
motor_.axis_ = this;
trap_.axis_ = this;
min_endstop_.axis_ = this;
max_endstop_.axis_ = this;
}
@@ -189,7 +192,7 @@ bool Axis::run_sensorless_control_loop() {
set_step_dir_enabled(config_.enable_step_dir);
run_control_loop([this](){
if (controller_.config_.control_mode >= CTRL_MODE_POSITION_CONTROL)
if (controller_.config_.control_mode >= Controller::CTRL_MODE_POSITION_CONTROL)
return error_ |= ERROR_POS_CTRL_DURING_SENSORLESS, false;
// Note that all estimators are updated in the loop prefix in run_control_loop
+46 -42
View File
@@ -5,48 +5,13 @@
#error "This file should not be included directly. Include odrive_main.h instead."
#endif
// Warning: Do not reorder these enum values.
// The state machine uses ">" comparision on them.
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
AXIS_STATE_HOMING = 9 //<! run axis homing function
};
enum HomingState_t {
HOMING_STATE_IDLE,
HOMING_STATE_HOMING,
HOMING_STATE_MOVE_TO_ZERO
};
struct AxisConfig_t {
bool startup_motor_calibration = false; //<! run motor calibration at startup, skip otherwise
bool startup_encoder_index_search = false; //<! run encoder index search after startup, skip otherwise
// this only has an effect if encoder.config.use_index is also true
bool startup_encoder_offset_calibration = false; //<! run encoder offset calibration after startup, skip otherwise
bool startup_closed_loop_control = false; //<! enable closed loop control after calibration/startup
bool startup_sensorless_control = false; //<! enable sensorless control after calibration/startup
bool startup_homing = false; //<! enable homing after calibration/startup
bool enable_step_dir = false; //<! enable step/dir input after calibration
// For M0 this has no effect if enable_uart is true
float counts_per_step = 2.0f;
// Spinup settings
float ramp_up_time = 0.4f; // [s]
float ramp_up_distance = 4 * M_PI; // [rad]
float spin_up_current = 10.0f; // [A]
float spin_up_acceleration = 400.0f; // [rad/s^2]
float spin_up_target_vel = 400.0f; // [rad/s]
};
class Axis {
public:
enum Error_t {
@@ -66,16 +31,53 @@ public:
ERROR_MAX_ENDSTOP_PRESSED = 0x1000
};
// Warning: Do not reorder these enum values.
// The state machine uses ">" comparision on them.
enum State_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
AXIS_STATE_HOMING = 9 //<! run axis homing function
};
struct Config_t {
bool startup_motor_calibration = false; //<! run motor calibration at startup, skip otherwise
bool startup_encoder_index_search = false; //<! run encoder index search after startup, skip otherwise
// this only has an effect if encoder.config.use_index is also true
bool startup_encoder_offset_calibration = false; //<! run encoder offset calibration after startup, skip otherwise
bool startup_closed_loop_control = false; //<! enable closed loop control after calibration/startup
bool startup_sensorless_control = false; //<! enable sensorless control after calibration/startup
bool startup_homing = false; //<! enable homing after calibration/startup
bool enable_step_dir = false; //<! enable step/dir input after calibration
// For M0 this has no effect if enable_uart is true
float counts_per_step = 2.0f;
// Spinup settings
float ramp_up_time = 0.4f; // [s]
float ramp_up_distance = 4 * M_PI; // [rad]
float spin_up_current = 10.0f; // [A]
float spin_up_acceleration = 400.0f; // [rad/s^2]
float spin_up_target_vel = 400.0f; // [rad/s]
};
enum thread_signals {
M_SIGNAL_PH_CURRENT_MEAS = 1u << 0
};
Axis(const AxisHardwareConfig_t& hw_config,
AxisConfig_t& config,
Config_t& config,
Encoder& encoder,
SensorlessEstimator& sensorless_estimator,
Controller& controller,
Motor& motor,
TrapezoidalTrajectory& trap,
Endstop& min_endstop,
Endstop& max_endstop);
@@ -157,12 +159,13 @@ public:
void run_state_machine_loop();
const AxisHardwareConfig_t& hw_config_;
AxisConfig_t& config_;
Config_t& config_;
Encoder& encoder_;
SensorlessEstimator& sensorless_estimator_;
Controller& controller_;
Motor& motor_;
TrapezoidalTrajectory& trap_;
Endstop& min_endstop_;
Endstop& max_endstop_;
@@ -172,9 +175,9 @@ public:
// variables exposed on protocol
Error_t error_ = ERROR_NONE;
bool enable_step_dir_ = false; // auto enabled after calibration, based on config.enable_step_dir
AxisState_t requested_state_ = AXIS_STATE_STARTUP_SEQUENCE;
AxisState_t task_chain_[10] = { AXIS_STATE_UNDEFINED };
AxisState_t& current_state_ = task_chain_[0];
State_t requested_state_ = AXIS_STATE_STARTUP_SEQUENCE;
State_t task_chain_[10] = { AXIS_STATE_UNDEFINED };
State_t& current_state_ = task_chain_[0];
uint32_t loop_counter_ = 0;
HomingState_t homing_state_ = HOMING_STATE_IDLE;
@@ -206,9 +209,10 @@ public:
make_protocol_object("motor", motor_.make_protocol_definitions()),
make_protocol_object("controller", controller_.make_protocol_definitions()),
make_protocol_object("encoder", encoder_.make_protocol_definitions()),
make_protocol_object("sensorless_estimator", sensorless_estimator_.make_protocol_definitions()),
make_protocol_object("min_endstop", min_endstop_.make_protocol_definitions()),
make_protocol_object("max_endstop", max_endstop_.make_protocol_definitions()),
make_protocol_object("sensorless_estimator", sensorless_estimator_.make_protocol_definitions())
make_protocol_object("trap_traj", trap_.make_protocol_definitions())
);
}
};
+33 -3
View File
@@ -2,7 +2,7 @@
#include "odrive_main.h"
Controller::Controller(ControllerConfig_t& config) :
Controller::Controller(Config_t& config) :
config_(config)
{}
@@ -44,6 +44,15 @@ void Controller::set_current_setpoint(float current_setpoint) {
#endif
}
void Controller::move_to_pos(float goal_point) {
axis_->trap_.planTrapezoidal(goal_point, pos_setpoint_, vel_setpoint_,
axis_->trap_.config_.vel_limit,
axis_->trap_.config_.accel_limit,
axis_->trap_.config_.decel_limit);
traj_start_loop_count_ = axis_->loop_counter_;
config_.control_mode = CTRL_MODE_TRAJECTORY_CONTROL;
}
void Controller::start_anticogging_calibration() {
// Ensure the cogging map was correctly allocated earlier and that the motor is capable of calibrating
if (anticogging_.cogging_map != NULL && axis_->error_ == Axis::ERROR_NONE) {
@@ -94,7 +103,28 @@ bool Controller::anticogging_calibration(float pos_estimate, float vel_estimate)
bool Controller::update(float pos_estimate, float vel_estimate, float* current_setpoint_output) {
// Only runs if anticogging_.calib_anticogging is true; non-blocking
anticogging_calibration(pos_estimate, vel_estimate);
float anticogging_pos = pos_estimate;
// Trajectory control
if (config_.control_mode == CTRL_MODE_TRAJECTORY_CONTROL) {
// Note: uint32_t loop count delta is OK across overflow
// Beware of negative deltas, as they will not be well behaved due to uint!
float t = (axis_->loop_counter_ - traj_start_loop_count_) * current_meas_period;
if (t > axis_->trap_.Tf_) {
// Drop into position control mode when done to avoid problems on loop counter delta overflow
config_.control_mode = CTRL_MODE_POSITION_CONTROL;
// pos_setpoint already set by trajectory
vel_setpoint_ = 0.0f;
current_setpoint_ = 0.0f;
} else {
TrapezoidalTrajectory::Step_t traj_step = axis_->trap_.eval(t);
pos_setpoint_ = traj_step.Y;
vel_setpoint_ = traj_step.Yd;
current_setpoint_ = traj_step.Ydd * axis_->trap_.config_.A_per_css;
}
anticogging_pos = pos_setpoint_; // FF the position setpoint instead of the pos_estimate
}
// Position control
// TODO Decide if we want to use encoder or pll position here
float vel_des = vel_setpoint_;
@@ -115,7 +145,7 @@ bool Controller::update(float pos_estimate, float vel_estimate, float* current_s
// We get the current position and apply a current feed-forward
// ensuring that we handle negative encoder positions properly (-1 == motor->encoder.encoder_cpr - 1)
if (anticogging_.use_anticogging) {
Iq += anticogging_.cogging_map[mod(static_cast<int>(pos_estimate), axis_->encoder_.config_.cpr)];
Iq += anticogging_.cogging_map[mod(static_cast<int>(anticogging_pos), axis_->encoder_.config_.cpr)];
}
float v_err = vel_des - vel_estimate;
+26 -19
View File
@@ -5,34 +5,38 @@
#error "This file should not be included directly. Include odrive_main.h instead."
#endif
// Note: these should be sorted from lowest level of control to
// highest level of control, to allow "<" style comparisons.
typedef enum {
CTRL_MODE_VOLTAGE_CONTROL = 0,
CTRL_MODE_CURRENT_CONTROL = 1,
CTRL_MODE_VELOCITY_CONTROL = 2,
CTRL_MODE_POSITION_CONTROL = 3
} Motor_control_mode_t;
class Controller {
public:
// Note: these should be sorted from lowest level of control to
// highest level of control, to allow "<" style comparisons.
enum ControlMode_t{
CTRL_MODE_VOLTAGE_CONTROL = 0,
CTRL_MODE_CURRENT_CONTROL = 1,
CTRL_MODE_VELOCITY_CONTROL = 2,
CTRL_MODE_POSITION_CONTROL = 3,
CTRL_MODE_TRAJECTORY_CONTROL = 4
};
struct ControllerConfig_t {
Motor_control_mode_t control_mode = CTRL_MODE_POSITION_CONTROL; //see: Motor_control_mode_t
float pos_gain = 20.0f; // [(counts/s) / counts]
float vel_gain = 5.0f / 10000.0f; // [A/(counts/s)]
// float vel_gain = 5.0f / 200.0f, // [A/(rad/s)] <sensorless example>
float vel_integrator_gain = 10.0f / 10000.0f; // [A/(counts/s * s)]
float vel_limit = 20000.0f; // [counts/s]
struct Config_t {
ControlMode_t control_mode = CTRL_MODE_POSITION_CONTROL; //see: Motor_control_mode_t
float pos_gain = 20.0f; // [(counts/s) / counts]
float vel_gain = 5.0f / 10000.0f; // [A/(counts/s)]
// float vel_gain = 5.0f / 200.0f, // [A/(rad/s)] <sensorless example>
float vel_integrator_gain = 10.0f / 10000.0f; // [A/(counts/s * s)]
float vel_limit = 20000.0f; // [counts/s]
float homing_speed = 2000.0f; // [counts/s]
};
class Controller {
public:
Controller(ControllerConfig_t& config);
Controller(Config_t& config);
void reset();
void set_pos_setpoint(float pos_setpoint, float vel_feed_forward, float current_feed_forward);
void set_vel_setpoint(float vel_setpoint, float current_feed_forward);
void set_current_setpoint(float current_setpoint);
// Trajectory-Planned control
void move_to_pos(float goal_point);
bool home_axis();
// TODO: make this more similar to other calibration loops
@@ -41,7 +45,7 @@ public:
bool update(float pos_estimate, float vel_estimate, float* current_setpoint);
ControllerConfig_t& config_;
Config_t& config_;
Axis* axis_ = nullptr; // set by Axis constructor
// TODO: anticogging overhaul:
@@ -74,6 +78,8 @@ public:
float vel_integrator_current_ = 0.0f; // [A]
float current_setpoint_ = 0.0f; // [A]
uint32_t traj_start_loop_count_ = 0;
// Communication protocol definitions
auto make_protocol_definitions() {
return make_protocol_member_list(
@@ -96,6 +102,7 @@ public:
make_protocol_function("set_current_setpoint", *this, &Controller::set_current_setpoint,
"current_setpoint"),
make_protocol_function("start_anticogging_calibration", *this, &Controller::start_anticogging_calibration),
make_protocol_function("move_to_pos", *this, &Controller::move_to_pos, "goal_point"),
make_protocol_function("home_axis", *this, &Controller::home_axis)
);
}
+4 -4
View File
@@ -95,9 +95,9 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) {
// TODO: Do the scan with current, not voltage!
bool Encoder::run_index_search() {
float voltage_magnitude;
if (axis_->motor_.config_.motor_type == MOTOR_TYPE_HIGH_CURRENT)
if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_HIGH_CURRENT)
voltage_magnitude = axis_->motor_.config_.calibration_current * axis_->motor_.config_.phase_resistance;
else if (axis_->motor_.config_.motor_type == MOTOR_TYPE_GIMBAL)
else if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL)
voltage_magnitude = axis_->motor_.config_.calibration_current;
else
return false;
@@ -142,9 +142,9 @@ bool Encoder::run_offset_calibration() {
shadow_count_ = count_in_cpr_;
float voltage_magnitude;
if (axis_->motor_.config_.motor_type == MOTOR_TYPE_HIGH_CURRENT)
if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_HIGH_CURRENT)
voltage_magnitude = axis_->motor_.config_.calibration_current * axis_->motor_.config_.phase_resistance;
else if (axis_->motor_.config_.motor_type == MOTOR_TYPE_GIMBAL)
else if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL)
voltage_magnitude = axis_->motor_.config_.calibration_current;
else
return false;
+16 -10
View File
@@ -11,9 +11,10 @@
BoardConfig_t board_config;
Encoder::Config_t encoder_configs[AXIS_COUNT];
SensorlessEstimator::Config_t sensorless_configs[AXIS_COUNT];
ControllerConfig_t controller_configs[AXIS_COUNT];
MotorConfig_t motor_configs[AXIS_COUNT];
AxisConfig_t axis_configs[AXIS_COUNT];
Controller::Config_t controller_configs[AXIS_COUNT];
Motor::Config_t motor_configs[AXIS_COUNT];
Axis::Config_t axis_configs[AXIS_COUNT];
TrapezoidalTrajectory::Config_t trap_configs[AXIS_COUNT];
EndstopConfig_t min_endstop_configs[AXIS_COUNT];
EndstopConfig_t max_endstop_configs[AXIS_COUNT];
bool user_config_loaded_;
@@ -26,11 +27,12 @@ typedef Config<
BoardConfig_t,
Encoder::Config_t[AXIS_COUNT],
SensorlessEstimator::Config_t[AXIS_COUNT],
ControllerConfig_t[AXIS_COUNT],
MotorConfig_t[AXIS_COUNT],
Controller::Config_t[AXIS_COUNT],
Motor::Config_t[AXIS_COUNT],
TrapezoidalTrajectory::Config_t[AXIS_COUNT],
EndstopConfig_t[AXIS_COUNT],
EndstopConfig_t[AXIS_COUNT],
AxisConfig_t[AXIS_COUNT]> ConfigFormat;
Axis::Config_t[AXIS_COUNT]> ConfigFormat;
void save_configuration(void) {
if (ConfigFormat::safe_store_config(
@@ -39,6 +41,7 @@ void save_configuration(void) {
&sensorless_configs,
&controller_configs,
&motor_configs,
&trap_configs,
&min_endstop_configs,
&max_endstop_configs,
&axis_configs)) {
@@ -57,6 +60,7 @@ void load_configuration(void) {
&sensorless_configs,
&controller_configs,
&motor_configs,
&trap_configs,
&min_endstop_configs,
&max_endstop_configs,
&axis_configs)) {
@@ -65,11 +69,12 @@ void load_configuration(void) {
for (size_t i = 0; i < AXIS_COUNT; ++i) {
encoder_configs[i] = Encoder::Config_t();
sensorless_configs[i] = SensorlessEstimator::Config_t();
controller_configs[i] = ControllerConfig_t();
motor_configs[i] = MotorConfig_t();
controller_configs[i] = Controller::Config_t();
motor_configs[i] = Motor::Config_t();
trap_configs[i] = TrapezoidalTrajectory::Config_t();
axis_configs[i] = Axis::Config_t();
min_endstop_configs[i] = EndstopConfig_t();
max_endstop_configs[i] = EndstopConfig_t();
axis_configs[i] = AxisConfig_t();
}
} else {
user_config_loaded_ = true;
@@ -172,10 +177,11 @@ int odrive_main(void) {
Motor *motor = new Motor(hw_configs[i].motor_config,
hw_configs[i].gate_driver_config,
motor_configs[i]);
TrapezoidalTrajectory *trap = new TrapezoidalTrajectory(trap_configs[i]);
Endstop *min_endstop = new Endstop(min_endstop_configs[i]);
Endstop *max_endstop = new Endstop(max_endstop_configs[i]);
axes[i] = new Axis(hw_configs[i].axis_config, axis_configs[i],
*encoder, *sensorless_estimator, *controller, *motor, *min_endstop, *max_endstop);
*encoder, *sensorless_estimator, *controller, *motor, *trap, *min_endstop, *max_endstop);
}
// Start ADC for temperature measurements and user measurements
+15 -14
View File
@@ -6,8 +6,8 @@
Motor::Motor(const MotorHardwareConfig_t& hw_config,
const GateDriverHardwareConfig_t& gate_driver_config,
MotorConfig_t& config) :
const GateDriverHardwareConfig_t& gate_driver_config,
Config_t& config) :
hw_config_(hw_config),
gate_driver_config_(gate_driver_config),
config_(config),
@@ -298,10 +298,11 @@ bool Motor::FOC_voltage(float v_d, float v_q, float phase) {
}
bool Motor::FOC_current(float Id_des, float Iq_des, float phase) {
Current_control_t* ictrl = &current_control_;
// Syntactic sugar
CurrentControl_t& ictrl = current_control_;
// For Reporting
ictrl->Iq_setpoint = Iq_des;
ictrl.Iq_setpoint = Iq_des;
// Clarke transform
float Ialpha = -current_meas_.phB - current_meas_.phC;
@@ -312,7 +313,7 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) {
float s = arm_sin_f32(phase);
float Id = c * Ialpha + s * Ibeta;
float Iq = c * Ibeta - s * Ialpha;
ictrl->Iq_measured = Iq;
ictrl.Iq_measured = Iq;
// Current error
float Ierr_d = Id_des - Id;
@@ -320,8 +321,8 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) {
// TODO look into feed forward terms (esp omega, since PI pole maps to RL tau)
// Apply PI control
float Vd = ictrl->v_current_control_integral_d + Ierr_d * ictrl->p_gain;
float Vq = ictrl->v_current_control_integral_q + Ierr_q * ictrl->p_gain;
float Vd = ictrl.v_current_control_integral_d + Ierr_d * ictrl.p_gain;
float Vq = ictrl.v_current_control_integral_q + Ierr_q * ictrl.p_gain;
float mod_to_V = (2.0f / 3.0f) * vbus_voltage;
float V_to_mod = 1.0f / mod_to_V;
@@ -335,23 +336,23 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) {
mod_d *= mod_scalefactor;
mod_q *= mod_scalefactor;
// TODO make decayfactor configurable
ictrl->v_current_control_integral_d *= 0.99f;
ictrl->v_current_control_integral_q *= 0.99f;
ictrl.v_current_control_integral_d *= 0.99f;
ictrl.v_current_control_integral_q *= 0.99f;
} else {
ictrl->v_current_control_integral_d += Ierr_d * (ictrl->i_gain * current_meas_period);
ictrl->v_current_control_integral_q += Ierr_q * (ictrl->i_gain * current_meas_period);
ictrl.v_current_control_integral_d += Ierr_d * (ictrl.i_gain * current_meas_period);
ictrl.v_current_control_integral_q += Ierr_q * (ictrl.i_gain * current_meas_period);
}
// Compute estimated bus current
ictrl->Ibus = mod_d * Id + mod_q * Iq;
ictrl.Ibus = mod_d * Id + mod_q * Iq;
// Inverse park transform
float mod_alpha = c * mod_d - s * mod_q;
float mod_beta = c * mod_q + s * mod_d;
// Report final applied voltage in stationary frame (for sensorles estimator)
ictrl->final_v_alpha = mod_to_V * mod_alpha;
ictrl->final_v_beta = mod_to_V * mod_beta;
ictrl.final_v_alpha = mod_to_V * mod_alpha;
ictrl.final_v_beta = mod_to_V * mod_beta;
// Apply SVM
if (!enqueue_modulation_timings(mod_alpha, mod_beta))
+48 -48
View File
@@ -7,51 +7,6 @@
#include "drv8301.h"
typedef enum {
MOTOR_TYPE_HIGH_CURRENT = 0,
// MOTOR_TYPE_LOW_CURRENT = 1, //Not yet implemented
MOTOR_TYPE_GIMBAL = 2
} Motor_type_t;
typedef struct {
float phB;
float phC;
} Iph_BC_t;
typedef struct {
float p_gain; // [V/A]
float i_gain; // [V/As]
float v_current_control_integral_d; // [V]
float v_current_control_integral_q; // [V]
float Ibus; // DC bus current [A]
// Voltage applied at end of cycle:
float final_v_alpha; // [V]
float final_v_beta; // [V]
float Iq_setpoint;
float Iq_measured;
float max_allowed_current;
} Current_control_t;
// NOTE: for gimbal motors, all units of A are instead V.
// example: vel_gain is [V/(count/s)] instead of [A/(count/s)]
// example: current_lim and calibration_current will instead determine the maximum voltage applied to the motor.
typedef struct {
bool pre_calibrated = false; // can be set to true to indicate that all values here are valid
int32_t pole_pairs = 7;
float calibration_current = 10.0f; // [A]
float resistance_calib_max_voltage = 1.0f; // [V] - You may need to increase this if this voltage isn't sufficient to drive calibration_current through the motor.
float phase_inductance = 0.0f; // to be set by measure_phase_inductance
float phase_resistance = 0.0f; // to be set by measure_phase_resistance
int32_t direction = 1; // 1 or -1
Motor_type_t motor_type = MOTOR_TYPE_HIGH_CURRENT;
// Read out max_allowed_current to see max supported value for current_lim.
// float current_lim = 70.0f; //[A]
float current_lim = 10.0f; //[A]
// Value used to compute shunt amplifier gains
float requested_current_range = 70.0f; // [A]
float current_control_bandwidth = 1000.0f; // [rad/s]
} MotorConfig_t;
class Motor {
public:
enum Error_t {
@@ -68,6 +23,51 @@ public:
ERROR_UNEXPECTED_TIMER_CALLBACK = 0x0200
};
enum MotorType_t {
MOTOR_TYPE_HIGH_CURRENT = 0,
// MOTOR_TYPE_LOW_CURRENT = 1, //Not yet implemented
MOTOR_TYPE_GIMBAL = 2
};
struct Iph_BC_t {
float phB;
float phC;
};
struct CurrentControl_t{
float p_gain; // [V/A]
float i_gain; // [V/As]
float v_current_control_integral_d; // [V]
float v_current_control_integral_q; // [V]
float Ibus; // DC bus current [A]
// Voltage applied at end of cycle:
float final_v_alpha; // [V]
float final_v_beta; // [V]
float Iq_setpoint;
float Iq_measured;
float max_allowed_current;
};
// NOTE: for gimbal motors, all units of A are instead V.
// example: vel_gain is [V/(count/s)] instead of [A/(count/s)]
// example: current_lim and calibration_current will instead determine the maximum voltage applied to the motor.
struct Config_t {
bool pre_calibrated = false; // can be set to true to indicate that all values here are valid
int32_t pole_pairs = 7;
float calibration_current = 10.0f; // [A]
float resistance_calib_max_voltage = 1.0f; // [V] - You may need to increase this if this voltage isn't sufficient to drive calibration_current through the motor.
float phase_inductance = 0.0f; // to be set by measure_phase_inductance
float phase_resistance = 0.0f; // to be set by measure_phase_resistance
int32_t direction = 1; // 1 or -1
MotorType_t motor_type = MOTOR_TYPE_HIGH_CURRENT;
// Read out max_allowed_current to see max supported value for current_lim.
// float current_lim = 70.0f; //[A]
float current_lim = 10.0f; //[A]
// Value used to compute shunt amplifier gains
float requested_current_range = 70.0f; // [A]
float current_control_bandwidth = 1000.0f; // [rad/s]
};
enum TimingLog_t {
TIMING_LOG_GENERAL,
TIMING_LOG_ADC_CB_I,
@@ -90,7 +90,7 @@ public:
Motor(const MotorHardwareConfig_t& hw_config,
const GateDriverHardwareConfig_t& gate_driver_config,
MotorConfig_t& config);
Config_t& config);
bool arm();
void disarm();
@@ -119,7 +119,7 @@ public:
const MotorHardwareConfig_t& hw_config_;
const GateDriverHardwareConfig_t gate_driver_config_;
MotorConfig_t& config_;
Config_t& config_;
Axis* axis_ = nullptr; // set by Axis constructor
//private:
@@ -144,7 +144,7 @@ public:
Iph_BC_t current_meas_ = {0.0f, 0.0f};
Iph_BC_t DC_calib_ = {0.0f, 0.0f};
float phase_current_rev_gain_ = 0.0f; // Reverse gain for ADC to Amps (to be set by DRV8301_setup)
Current_control_t current_control_ = {
CurrentControl_t current_control_ = {
.p_gain = 0.0f, // [V/A] should be auto set after resistance and inductance measurement
.i_gain = 0.0f, // [V/As] should be auto set after resistance and inductance measurement
.v_current_control_integral_d = 0.0f,
+1
View File
@@ -109,6 +109,7 @@ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast<ENUMTYPE>(~static_c
#include <sensorless_estimator.hpp>
#include <controller.hpp>
#include <motor.hpp>
#include <trapTraj.hpp>
#include <endstop.hpp>
#include <axis.hpp>
#include <communication/communication.h>
+94
View File
@@ -0,0 +1,94 @@
#include <math.h>
#include "odrive_main.h"
#include "utils.h"
// A sign function where input 0 has positive sign (not 0)
float sign_hard(float val) {
return (std::signbit(val)) ? -1.0f : 1.0f;
}
// Symbol Description
// Ta, Tv and Td Duration of the stages of the AL profile
// Xi and Vi Adapted initial conditions for the AL profile
// Xf Position set-point
// s Direction (sign) of the trajectory
// Vmax, Amax, Dmax and jmax Kinematic bounds
// Ar, Dr and Vr Reached values of acceleration and velocity
TrapezoidalTrajectory::TrapezoidalTrajectory(Config_t& config) : config_(config) {}
bool TrapezoidalTrajectory::planTrapezoidal(float Xf, float Xi, float Vi,
float Vmax, float Amax, float Dmax) {
float dX = Xf - Xi; // Distance to travel
float stop_dist = (Vi * Vi) / (2.0f * Dmax); // Minimum stopping distance
float dXstop = std::copysign(stop_dist, Vi); // Minimum stopping displacement
float s = sign_hard(dX - dXstop); // Sign of coast velocity (if any)
Ar_ = s * Amax; // Maximum Acceleration (signed)
Dr_ = -s * Dmax; // Maximum Deceleration (signed)
Vr_ = s * Vmax; // Maximum Velocity (signed)
// If we start with a speed faster than cruising, then we need to decel instead of accel
// aka "double deceleration move" in the paper
if ((s * Vi) > (s * Vr_)) {
Ar_ = -s * Amax;
}
// Time to accel/decel to/from Vr (cruise speed)
Ta_ = (Vr_ - Vi) / Ar_;
Td_ = -Vr_ / Dr_;
// Integral of velocity ramps over the full accel and decel times to get
// minimum displacement required to reach cuising speed
float dXmin = 0.5f*Ta_*(Vr_ + Vi) + 0.5f*Td_*Vr_;
// Are we displacing enough to reach cruising speed?
if (s*dX < s*dXmin) {
// Short move (triangle profile)
Vr_ = s * sqrtf((Dr_*SQ(Vi) + 2*Ar_*Dr_*dX) / (Dr_ - Ar_));
Ta_ = std::max(0.0f, (Vr_ - Vi) / Ar_);
Td_ = std::max(0.0f, -Vr_ / Dr_);
Tv_ = 0.0f;
} else {
// Long move (trapezoidal profile)
Tv_ = (dX - dXmin) / Vr_;
}
// Fill in the rest of the values used at evaluation-time
Tf_ = Ta_ + Tv_ + Td_;
Xi_ = Xi;
Xf_ = Xf;
Vi_ = Vi;
yAccel_ = Xi + Vi*Ta_ + 0.5f*Ar_*SQ(Ta_); // pos at end of accel phase
return true;
}
TrapezoidalTrajectory::Step_t TrapezoidalTrajectory::eval(float t) {
Step_t trajStep;
if (t < 0.0f) { // Initial Condition
trajStep.Y = Xi_;
trajStep.Yd = Vi_;
trajStep.Ydd = 0.0f;
} else if (t < Ta_) { // Accelerating
trajStep.Y = Xi_ + Vi_*t + 0.5f*Ar_*SQ(t);
trajStep.Yd = Vi_ + Ar_*t;
trajStep.Ydd = Ar_;
} else if (t < Ta_ + Tv_) { // Coasting
trajStep.Y = yAccel_ + Vr_*(t - Ta_);
trajStep.Yd = Vr_;
trajStep.Ydd = 0.0f;
} else if (t < Tf_) { // Deceleration
float td = t - Tf_;
trajStep.Y = Xf_ + 0.5f*Dr_*SQ(td);
trajStep.Yd = Dr_*td;
trajStep.Ydd = Dr_;
} else if (t >= Tf_) { // Final Condition
trajStep.Y = Xf_;
trajStep.Yd = 0.0f;
trajStep.Ydd = 0.0f;
} else {
// TODO: report error here
}
return trajStep;
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef _TRAP_TRAJ_H
#define _TRAP_TRAJ_H
class TrapezoidalTrajectory {
public:
struct Config_t {
float vel_limit = 20000.0f; // [count/s]
float accel_limit = 5000.0f; // [count/s^2]
float decel_limit = 5000.0f; // [count/s^2]
float A_per_css = 0.0f; // [A/(count/s^2)]
};
struct Step_t {
float Y;
float Yd;
float Ydd;
};
TrapezoidalTrajectory(Config_t& config);
bool planTrapezoidal(float Xf, float Xi, float Vi,
float Vmax, float Amax, float Dmax);
Step_t eval(float t);
auto make_protocol_definitions() {
return make_protocol_member_list(
make_protocol_object("config",
make_protocol_property("vel_limit", &config_.vel_limit),
make_protocol_property("accel_limit", &config_.accel_limit),
make_protocol_property("decel_limit", &config_.decel_limit),
make_protocol_property("A_per_css", &config_.A_per_css)
)
);
}
Axis* axis_ = nullptr; // set by Axis constructor
Config_t& config_;
float Xi_;
float Xf_;
float Vi_;
float Ar_;
float Vr_;
float Dr_;
float Ta_;
float Tv_;
float Td_;
float Tf_;
float yAccel_;
};
#endif
+2
View File
@@ -63,6 +63,8 @@ extern "C" {
#define MACRO_MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MACRO_MIN(x, y) (((x) < (y)) ? (x) : (y))
#define SQ(x) ((x) * (x))
static const float one_by_sqrt3 = 0.57735026919f;
static const float two_by_sqrt3 = 1.15470053838f;
static const float sqrt3_by_2 = 0.86602540378f;
+2 -1
View File
@@ -89,7 +89,7 @@ FLAGS += '-mthumb'
FLAGS += '-mcpu=cortex-m4'
FLAGS += '-mfpu=fpv4-sp-d16'
FLAGS += '-mfloat-abi=hard'
FLAGS += { '-Wall', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'}
FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'}
-- debug build
FLAGS += '-g -gdwarf-2'
@@ -156,6 +156,7 @@ build{
'MotorControl/endstop.cpp',
'MotorControl/controller.cpp',
'MotorControl/sensorless_estimator.cpp',
'MotorControl/trapTraj.cpp',
'MotorControl/main.cpp',
'communication/communication.cpp',
'communication/ascii_protocol.cpp',
+12
View File
@@ -128,6 +128,18 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink&
axes[motor_number]->controller_.set_current_setpoint(current_setpoint);
}
} else if (cmd[0] == 't') { // trapezoidal trajectory
unsigned motor_number;
float goal_point;
int numscan = sscanf(cmd, "t %u %f", &motor_number, &goal_point);
if (numscan < 2) {
respond(response_channel, use_checksum, "invalid command format");
} else if (motor_number >= AXIS_COUNT) {
respond(response_channel, use_checksum, "invalid motor %u", motor_number);
} else {
axes[motor_number]->controller_.move_to_pos(goal_point);
}
} else if (cmd[0] == 'h') { // Help
respond(response_channel, use_checksum, "Please see documentation for more details");
respond(response_channel, use_checksum, "");
+17 -6
View File
@@ -592,10 +592,10 @@ private:
template<typename T>
struct format_traits_t;
template<> struct format_traits_t<float> { using type = void;
static constexpr const char * fmt = "%f";
static constexpr const char * fmtp = "%f";
};
// template<> struct format_traits_t<float> { using type = void;
// static constexpr const char * fmt = "%f";
// static constexpr const char * fmtp = "%f";
// };
template<> struct format_traits_t<int32_t> { using type = void;
static constexpr const char * fmt = "%ld";
static constexpr const char * fmtp = "%ld";
@@ -626,8 +626,15 @@ static bool to_string(const T& value, char * buffer, size_t length, int) {
snprintf(buffer, length, format_traits_t<T>::fmtp, value);
return true;
}
// Special case for float because printf promotes float to double, and we get warnings
template<typename T>
static bool to_string(const float& value, char * buffer, size_t length, int) {
snprintf(buffer, length, "%f", (double)value);
return true;
}
template<typename T>
//__attribute__((__unused__))
static bool to_string(const bool& value, char * buffer, size_t length, int) {
buffer[0] = value ? '1' : '0';
buffer[1] = 0;
@@ -642,7 +649,11 @@ template<typename T, typename = typename format_traits_t<T>::type>
static bool from_string(const char * buffer, size_t length, T* property, int) {
return sscanf(buffer, format_traits_t<T>::fmt, property) == 1;
}
//__attribute__((__unused__))
// Special case for float because printf promotes float to double, and we get warnings
template<typename T>
static bool from_string(const char * buffer, size_t length, float* property, int) {
return sscanf(buffer, "%f", property) == 1;
}
template<typename T>
static bool from_string(const char * buffer, size_t length, bool* property, int) {
int val;
+1 -1
View File
@@ -1,5 +1,5 @@
from .discovery import find_any, find_all
from .utils import Event, Logger
from .utils import Event, Logger, TimeoutError
from .protocol import ChannelBrokenException, ChannelDamagedException
from .shell import launch_shell
+9 -8
View File
@@ -11,7 +11,7 @@ import fibre.protocol
import fibre.utils
import fibre.remote_object
from fibre.utils import Event, Logger
from fibre.protocol import ChannelBrokenException
from fibre.protocol import ChannelBrokenException, TimeoutError
# Load all installed transport layers
@@ -20,25 +20,25 @@ channel_types = {}
try:
import fibre.usbbulk_transport
channel_types['usb'] = fibre.usbbulk_transport.discover_channels
except ModuleNotFoundError:
except ImportError:
pass
try:
import fibre.serial_transport
channel_types['serial'] = fibre.serial_transport.discover_channels
except ModuleNotFoundError:
except ImportError:
pass
try:
import fibre.tcp_transport
channel_types['tcp'] = fibre.tcp_transport.discover_channels
except ModuleNotFoundError:
except ImportError:
pass
try:
import fibre.udp_transport
channel_types['udp'] = fibre.udp_transport.discover_channels
except ModuleNotFoundError:
except ImportError:
pass
def noprint(text):
@@ -102,9 +102,10 @@ def find_all(path, serial_number,
prefix = search_spec.split(':')[0]
the_rest = ':'.join(search_spec.split(':')[1:])
if prefix in channel_types:
threading.Thread(target=channel_types[prefix],
args=(the_rest, serial_number, did_discover_channel, search_cancellation_token, channel_termination_token, logger),
daemon=True).start()
t = threading.Thread(target=channel_types[prefix],
args=(the_rest, serial_number, did_discover_channel, search_cancellation_token, channel_termination_token, logger))
t.daemon = True
t.start()
else:
raise Exception("Invalid path spec \"{}\"".format(search_spec))
+4 -3
View File
@@ -6,7 +6,7 @@ import sys
import threading
import traceback
#import fibre.utils
from fibre.utils import Event, wait_any
from fibre.utils import Event, wait_any, TimeoutError
import abc
if sys.version_info >= (3, 4):
@@ -65,7 +65,6 @@ def calc_crc16(remainder, value):
#print(hex(calc_crc8(0x12, [1, 2, 3, 4, 5, 0x10, 0x13, 0x37])))
#print(hex(calc_crc16(0xfeef, [1, 2, 3, 4, 5, 0x10, 0x13, 0x37])))
class DeviceInitException(Exception):
pass
@@ -256,7 +255,9 @@ class Channel(PacketSink):
self._logger.debug("receiver thread is exiting: " + traceback.format_exc())
finally:
self._channel_broken.set()
threading.Thread(target=receiver_thread, daemon=True).start()
t = threading.Thread(target=receiver_thread)
t.daemon = True
t.start()
def remote_endpoint_operation(self, endpoint_id, input, expect_ack, output_length):
if input is None:
@@ -10,6 +10,7 @@ import traceback
import serial
import serial.tools.list_ports
import fibre
from fibre.utils import TimeoutError
# TODO: make this customizable
DEFAULT_BAUDRATE = 115200
+1 -1
View File
@@ -4,7 +4,7 @@ import socket
import time
import traceback
import fibre.protocol
from fibre.utils import wait_any
from fibre.utils import wait_any, TimeoutError
def noprint(x):
pass
@@ -8,6 +8,7 @@ import time
import fibre.protocol
import traceback
import platform
from fibre.utils import TimeoutError
# Currently we identify fibre-enabled devices by VID,PID
# TODO: identify by USB descriptors
+11 -2
View File
@@ -12,10 +12,16 @@ try:
# TODO: we should win32console anyway so we could just omit colorama
import colorama
colorama.init()
except ModuleNotFoundError:
except ImportError:
print("Could not init terminal features.")
sys.stdout.flush()
pass
if sys.version_info < (3, 3):
class TimeoutError(Exception):
pass
else:
TimeoutError = TimeoutError
def get_serial_number_str(device):
if hasattr(device, 'serial_number'):
@@ -92,7 +98,10 @@ class Event():
def delayed_trigger():
if not self.wait(timeout=timeout):
self.set()
threading.Thread(target=delayed_trigger, daemon=True).start()
threading.Thread(target=delayed_trigger)
t.daemon = True
t.start()
def wait_any(timeout=None, *events):
"""
+12
View File
@@ -24,6 +24,18 @@ command *42 ; comment [new line character]
## Command Reference
#### Motor trajectory command
```
t motor destination
```
* `t` for trajectory
* `motor` is the motor number, `0` or `1`.
* `destination` is the goal position, in encoder counts.
Example: `t 0 -20000`
For general moving around of the axis, this is the recommended command.
#### Motor Position command
```
p motor position velocity_ff current_ff
+1
View File
@@ -16,6 +16,7 @@ Before doing the VSCode setup, make sure you've installed all of your [prerequis
* Include Autocomplete
* Path Autocomplete
* Auto Comment Blocks
1. Create an environment variable named `ARM_GCC_ROOT` whose value is the location of the `GNU Arm Embedded Toolchain` (.e.g `C:\Program Files (x86)\GNU Tools Arm Embedded\7 2018-q2-update`) that you installed in the prerequisites section of the developer's guide.
1. Restart VSCode
1. Open the VSCode Workspace file, which is located in the root of the ODrive repository. It is called `ODrive_Workspace.code-workspace`. The first time you open it, VSCode will install some dependencies. If it fails, you may need to [change your proxy settings](https://code.visualstudio.com/docs/getstarted/settings).
+2 -2
View File
@@ -32,7 +32,7 @@ The recommended tools for ODrive development are:
* **make**: Used to invoke tup
* **Tup**: The build system used to invoke the compile commands
* **ARM GNU Compiler**: For cross-comiling code
* **ARM GNU Compiler**: For cross-compiling code
* **ARM GDB**: For debugging the code and stepping through on the device
* **OpenOCD**: For flashing the ODrive with the STLink/v2 programmer
* **Python**: For running the Python tools
@@ -80,7 +80,7 @@ __Note__: make sure these programs are not only installed but also added to your
Some instructions in this document may assume that you're using a bash command prompt, such as the Windows 10 built-in bash or [Git](https://git-scm.com/download/win) bash.
* [ARM compiler](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads)
* [ARM compiler](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) __Note__: After installing, create an environment variable named `ARM_GCC_ROOT` whose value is the path you installed to. e.g. `C:\Program Files (x86)\GNU Tools Arm Embedded\7 2018-q2-update`. This variable is used to locate include files for the c/c++ Visual Studio Code extension.
* [Tup](http://gittup.org/tup/index.html)
* [Make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm)
* [OpenOCD](http://gnuarmeclipse.github.io/openocd/install/). Also follow the instructions on the ST-LINK/V2 drivers.
+72 -51
View File
File diff suppressed because it is too large. Load diff
+7 -29
View File
@@ -143,36 +143,14 @@ This procedure is only necessary for ODrive v3.4 or earlier. You will need an ST
Power up the ODrive.
4. Open up a terminal and navigate to the directory where the firmware is.
5. Run the following command (replace `ODriveFirmware_v3.4-24V.elf` with the name of your firmware file):
```
~/Downloads $ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c init -c reset\ halt -c flash\ write_image\ erase\ ODriveFirmware_v3.4-24V.elf -c reset\ run -c exit
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v17 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.236027
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
adapter speed: 2000 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08009224 msp: 0x20020000
auto erase enabled
Info : device id = 0x10076413
Info : flash size = 1024kbytes
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x20000046 msp: 0x20020000
Warn : no flash bank found for address 10000000
```
openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg -c init -c "reset halt" -c "flash write_image erase ODriveFirmware_v3.4-24V.elf" -c "reset run" -c exit
```
If everything worked correctly, you should see something similar to this towards the end of the printout:
```
wrote 262144 bytes from file ODriveFirmware_v3.4-24V.elf in 10.194110s (25.113 KiB/s)
adapter speed: 2000 kHz
```
```
If something doesn't work, make sure `openocd` is in your `PATH` variable, check that the wires are connected properly and try with elevated privileges.
File diff suppressed because it is too large. Load diff
+6 -2
View File
@@ -3,6 +3,7 @@
Tool for flashing .hex files to the ODrive via the STM built-in USB DFU mode.
"""
from __future__ import print_function
import argparse
import sys
import time
@@ -206,7 +207,7 @@ def show_deferred_message(message, cancellation_token):
time.sleep(1)
if not cancellation_token.is_set():
print(message)
t = threading.Thread(target=show_message_thread, args=(message, cancellation_token), daemon=True)
t = threading.Thread(target=show_message_thread, args=(message, cancellation_token))
t.daemon = True
t.start()
@@ -436,7 +437,10 @@ def launch_dfu(args, logger, cancellation_token):
def find_device_in_dfu_mode_thread():
devices[0] = find_device_in_dfu_mode(serial_number, find_odrive_cancellation_token)
find_odrive_cancellation_token.set()
threading.Thread(target=find_device_in_dfu_mode_thread, daemon=True).start()
t = threading.Thread(target=find_device_in_dfu_mode_thread)
t.daemon = True
t.start()
# Scan for ODrives not in DFU mode
# We only scan on USB because DFU is only implemented over USB
+1 -1
View File
@@ -1,4 +1,4 @@
from __future__ import print_function
import subprocess
import shlex
import math
+10 -3
View File
@@ -1,3 +1,4 @@
from __future__ import print_function
import sys
import time
@@ -12,7 +13,7 @@ try:
import win32console
import colorama
colorama.init()
except ModuleNotFoundError:
except ImportError:
print("Could not init terminal features.")
print("Refer to install instructions at http://docs.odriverobotics.com/#downloading-and-installing-tools")
sys.stdout.flush()
@@ -72,8 +73,14 @@ def start_liveplotter(get_var_callback):
fig.canvas.draw()
fig.canvas.start_event_loop(1/plot_rate)
threading.Thread(target=fetch_data, daemon=True).start()
threading.Thread(target=plot_data, daemon=True).start()
fetch_t = threading.Thread(target=fetch_data)
fetch_t.daemon = True
fetch_t.start()
plot_t = threading.Thread(target=plot_data)
plot_t.daemon = True
plot_t.start()
return cancellation_token;
#plot_data()
+2 -1
View File
@@ -35,7 +35,8 @@ def for_all_parallel(objects, get_name, callback):
# Start a thread for each element in the list
all_threads = []
for element in objects:
thread = threading.Thread(target=run_callback, args=(element,), daemon=True)
thread = threading.Thread(target=run_callback, args=(element,))
thread.daemon = True
thread.start()
all_threads.append(thread)
+8 -1
View File
@@ -48,6 +48,10 @@ from setuptools import setup
import os
import sys
if sys.version_info < (3, 3):
import exceptions
PermissionError = exceptions.OSError
creating_package = "sdist" in sys.argv
# Load version from Git tag
@@ -78,7 +82,10 @@ if creating_package:
fibre_link = os.path.join(os.path.dirname(
os.path.realpath(__file__)), "fibre")
if not os.path.exists(fibre_link):
os.symlink(fibre_src, fibre_link, True)
if sys.version_info > (3, 3):
os.symlink(fibre_src, fibre_link, target_is_directory=True)
else:
os.symlink(fibre_src, fibre_link)
# TODO: find a better place for this
if not creating_package: