From cc0f083bf0b65c58f62ee3b75824c49bc94bc672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20H=C3=A4ggstr=C3=B6m?= Date: Thu, 13 Sep 2018 13:54:55 +0200 Subject: [PATCH 001/116] Poll analog inputs to drive a configured endpoint. --- Firmware/MotorControl/low_level.cpp | 31 +++++++++++++++++++++++- Firmware/MotorControl/low_level.h | 2 ++ Firmware/MotorControl/main.cpp | 2 ++ Firmware/MotorControl/odrive_main.h | 1 + Firmware/communication/communication.cpp | 7 ++++-- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index ffeeeadd..8d233188 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -724,4 +724,33 @@ void pwm_in_cb(int channel, uint32_t timestamp) { last_timestamp[gpio_num - 1] = timestamp; last_pin_state[gpio_num - 1] = current_pin_state; last_sample_valid[gpio_num - 1] = true; -} \ No newline at end of file +} + + +/* Analog speed control input */ + +static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio) +{ + float fraction = get_adc_voltage(get_gpio_port_by_pin(gpio), get_gpio_pin_by_pin(gpio)) / 3.3f; + float value = map->min + (fraction * (map->max - map->min)); + get_endpoint(map->endpoint)->set_from_float(value); +} + +static void analog_polling_thread(void *) +{ + while (true) { + for (int i = 0; i < GPIO_COUNT; i++) { + struct PWMMapping_t *map = &board_config.analog_mappings[i]; + + if (is_endpoint_ref_valid(map->endpoint)) + update_analog_endpoint(map, i + 1); + } + osDelay(200); + } +} + +void start_analog_thread() +{ + osThreadDef(thread_def, analog_polling_thread, osPriorityLow, 0, 4*512); + osThreadCreate(osThread(thread_def), NULL); +} diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index 4af6e3e0..e098c75f 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -53,6 +53,8 @@ void pwm_in_init(); void update_brake_current(); +void start_analog_thread(); + #ifdef __cplusplus } #endif diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 18b88433..4766cf54 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -208,6 +208,8 @@ int odrive_main(void) { axes[i]->start_thread(); } + start_analog_thread(); + system_stats_.fully_booted = true; return 0; } diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 4e4db160..00be19f0 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -75,6 +75,7 @@ struct BoardConfig_t { //make_protocol_definitions()), make_protocol_object("axis1", axes[1]->make_protocol_definitions()), make_protocol_object("can", can1_ctx.make_protocol_definitions()), From 36274b98e6832c6d008bca36f66e7634b60f396b Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 24 Sep 2018 21:17:13 -0400 Subject: [PATCH 002/116] Add move_incremental --- Firmware/MotorControl/controller.cpp | 10 ++++++++++ Firmware/MotorControl/controller.hpp | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index d7740a5e..7f9e5e51 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -48,6 +48,8 @@ void Controller::move_to_pos(float goal_point) { planned_move_end_time_ = axis_->trap_.planTrapezoidal(goal_point, pos_setpoint_, vel_setpoint_, axis_->trap_.config_.vel_limit, axis_->trap_.config_.accel_limit, axis_->trap_.config_.decel_limit); + + goal_point_ = goal_point; config_.control_mode = CTRL_MODE_PLANNED_MOVE_CONTROL; TrapTrajStep_t myTraj = axis_->trap_.evalTrapTraj(0.0f); pos_setpoint_ = myTraj.Y; @@ -57,6 +59,14 @@ void Controller::move_to_pos(float goal_point) { planned_move_timer_ = axis_->loop_counter_ * current_meas_period; } +void Controller::move_incremental(float displacement, bool from_goal_point = true){ + if(from_goal_point){ + move_to_pos(goal_point_ + displacement); + } else{ + move_to_pos(axis_->encoder_.pos_estimate_ + displacement); + } +} + 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) { diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index dd3f76a5..d7155871 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -35,6 +35,7 @@ public: // Trajectory-Planned control void move_to_pos(float goal_point); + void move_incremental(float displacement, bool from_goal_point); // TODO: make this more similar to other calibration loops void start_anticogging_calibration(); @@ -78,6 +79,8 @@ public: float planned_move_timer_ = 0.0f; float planned_move_end_time_ = 0.0f; + float goal_point_ = 0.0f; + // Communication protocol definitions auto make_protocol_definitions() { return make_protocol_member_list( @@ -102,6 +105,7 @@ public: make_protocol_function("set_current_setpoint", *this, &Controller::set_current_setpoint, "current_setpoint"), make_protocol_function("move_to_pos", *this, &Controller::move_to_pos, "pos_setpoint"), + make_protocol_function("move_incremental", *this, &Controller::move_incremental, "displacement", "from_goal_point"), make_protocol_function("start_anticogging_calibration", *this, &Controller::start_anticogging_calibration) ); } From 12651db3f2f23ee6be206019660192c9580a87ae Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 24 Sep 2018 21:21:49 -0400 Subject: [PATCH 003/116] Use pos_setpoint_ as "current position" in incremental moves --- Firmware/MotorControl/controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 7f9e5e51..a18938b8 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -63,7 +63,7 @@ void Controller::move_incremental(float displacement, bool from_goal_point = tru if(from_goal_point){ move_to_pos(goal_point_ + displacement); } else{ - move_to_pos(axis_->encoder_.pos_estimate_ + displacement); + move_to_pos(pos_setpoint_ + displacement); } } From a65212e2b7b975370c8427bed62786526fcf9b6c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 2 Oct 2018 00:44:42 -0700 Subject: [PATCH 004/116] move check_for_errors to inline --- Firmware/MotorControl/axis.cpp | 13 ------------- Firmware/MotorControl/axis.hpp | 14 +++++++++++++- Firmware/MotorControl/motor.cpp | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 4ff660fd..01892af1 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -96,19 +96,6 @@ void Axis::set_step_dir_enabled(bool enable) { } } -bool Axis::check_for_errors() { - // Maybe we should update this to only trigger on new errors? - // The danger with that is we could fail to bail on uncleared errors that still prevent - // correct opreation. - - // For now: we treat ERROR_INVALID_STATE in idle loop special, or we could never stay - // in idle after this kind of error. - if (current_state_ == AXIS_STATE_IDLE) - return (error_ & ~ERROR_INVALID_STATE) == ERROR_NONE; - else - return error_ == ERROR_NONE; -} - // @brief Do axis level checks and call subcomponent do_checks // Returns true if everything is ok. bool Axis::do_checks() { diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 6063ee3a..8306d1f5 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -80,9 +80,21 @@ public: bool check_PSU_brownout(); bool do_checks(); bool do_updates(); - bool check_for_errors(); float get_temp(); + bool inline check_for_errors() { + // Maybe we should update this to only trigger on new errors? + // The danger with that is we could fail to bail on uncleared errors that still prevent + // correct opreation. + + // For now: we treat ERROR_INVALID_STATE in idle loop special, or we could never stay + // in idle after this kind of error. + if (current_state_ == AXIS_STATE_IDLE) + return (error_ & ~ERROR_INVALID_STATE) == ERROR_NONE; + else + return error_ == ERROR_NONE; + } + // @brief Runs the specified update handler at the frequency of the current measurements. // // The loop runs until one of the following conditions: diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 5f518adb..ee71c98a 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -343,7 +343,7 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) { // Inverse park transform float mod_alpha = c * mod_d - s * mod_q; - float mod_beta = c * mod_q + s * mod_d; + 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; From ca83df61558aa063124475e0a701a7ba1c57b2bb Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 2 Oct 2018 01:35:32 -0700 Subject: [PATCH 005/116] can do lockin spin at constant speed --- Firmware/MotorControl/axis.cpp | 59 ++++++++++++++++++++++++---------- Firmware/MotorControl/axis.hpp | 33 +++++++++++-------- tools/odrive/enums.py | 7 ++-- 3 files changed, 65 insertions(+), 34 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 01892af1..43ee9ce2 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -132,13 +132,13 @@ float Axis::get_temp() { return horner_fma(normalized_voltage, thermistor_poly_coeffs, thermistor_num_coeffs); } -bool Axis::run_sensorless_spin_up() { - // Early Spin-up: spiral up current +bool Axis::run_lockin_spin() { + // Spiral up current for softer rotor lock-in float x = 0.0f; run_control_loop([&](){ - float phase = wrap_pm_pi(config_.ramp_up_distance * x); - float I_mag = config_.spin_up_current * x; - x += current_meas_period / config_.ramp_up_time; + float phase = wrap_pm_pi(config_.lockin_ramp_time * x); + float I_mag = config_.lockin_current * x; + x += current_meas_period / config_.lockin_ramp_time; if (!motor_.update(I_mag, phase)) return error_ |= ERROR_MOTOR_FAILED, false; return x < 1.0f; @@ -146,21 +146,38 @@ bool Axis::run_sensorless_spin_up() { if (error_ != ERROR_NONE) return false; - // Late Spin-up: accelerate - float vel = config_.ramp_up_distance / config_.ramp_up_time; - float phase = wrap_pm_pi(config_.ramp_up_distance); + // Accelerate + float distance = config_.lockin_ramp_time; + float phase = wrap_pm_pi(distance); + float vel = distance / config_.lockin_ramp_time; + bool vel_done = false; + bool dist_done = false; run_control_loop([&](){ - vel += config_.spin_up_acceleration * current_meas_period; + vel += config_.lockin_accel * current_meas_period; + distance += vel * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); - float I_mag = config_.spin_up_current; - if (!motor_.update(I_mag, phase)) + if (!motor_.update(config_.lockin_current, phase)) return error_ |= ERROR_MOTOR_FAILED, false; - return vel < config_.spin_up_target_vel; + vel_done = vel >= config_.lockin_vel; + dist_done = fabsf(distance) >= fabsf(config_.lockin_finish_distance); + if (config_.lockin_finish_on_distance) + return !vel_done && !dist_done; + else + return !vel_done; }); - // call to controller.reset() that happend when arming means that vel_setpoint - // is zeroed. So we make the setpoint the spinup target for smooth transition. - controller_.vel_setpoint_ = config_.spin_up_target_vel; + // Constant speed + if (config_.lockin_finish_on_distance) { + vel = config_.lockin_vel; + run_control_loop([&](){ + distance += vel * current_meas_period; + phase = wrap_pm_pi(phase + vel * current_meas_period); + if (!motor_.update(config_.lockin_current, phase)) + return error_ |= ERROR_MOTOR_FAILED, false; + dist_done = fabsf(distance) >= fabsf(config_.lockin_finish_distance); + return !dist_done; + }); + } return check_for_errors(); } @@ -282,10 +299,18 @@ void Axis::run_state_machine_loop() { status = encoder_.run_offset_calibration(); break; + case AXIS_STATE_LOCKIN_SPIN: + status = run_lockin_spin(); + break; + case AXIS_STATE_SENSORLESS_CONTROL: - status = run_sensorless_spin_up(); // TODO: restart if desired - if (status) + status = run_lockin_spin(); // TODO: restart if desired + if (status) { + // call to controller.reset() that happend when arming means that vel_setpoint + // is zeroed. So we make the setpoint the spinup target for smooth transition. + controller_.vel_setpoint_ = config_.lockin_vel; status = run_sensorless_control_loop(); + } break; case AXIS_STATE_CLOSED_LOOP_CONTROL: diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 8306d1f5..b264d1a1 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -31,9 +31,10 @@ public: AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3, // Date: Sun, 7 Oct 2018 23:30:24 -0700 Subject: [PATCH 006/116] hm something causes a hardfault --- Firmware/MotorControl/axis.cpp | 46 ++++++++++++++++++++-------------- Firmware/MotorControl/axis.hpp | 8 ++++-- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 43ee9ce2..2136ebcb 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -135,47 +135,55 @@ float Axis::get_temp() { bool Axis::run_lockin_spin() { // Spiral up current for softer rotor lock-in float x = 0.0f; - run_control_loop([&](){ + run_control_loop([&]() { float phase = wrap_pm_pi(config_.lockin_ramp_time * x); float I_mag = config_.lockin_current * x; x += current_meas_period / config_.lockin_ramp_time; if (!motor_.update(I_mag, phase)) - return error_ |= ERROR_MOTOR_FAILED, false; + return false; return x < 1.0f; }); if (error_ != ERROR_NONE) return false; - // Accelerate + // Spin states float distance = config_.lockin_ramp_time; float phase = wrap_pm_pi(distance); float vel = distance / config_.lockin_ramp_time; - bool vel_done = false; - bool dist_done = false; - run_control_loop([&](){ + + // Function of states to check if we are done + auto spin_done = [&](bool vel_override = false) -> bool { + bool done = false; + if (config_.lockin_finish_on_vel || vel_override) + done = done || vel >= config_.lockin_vel; + if (config_.lockin_finish_on_distance) + done = done || fabsf(distance) >= fabsf(config_.lockin_finish_distance); + if (config_.lockin_finish_on_enc_idx) + done = done || encoder_.index_found_; + return done; + }; + + // Accelerate + run_control_loop([&]() { vel += config_.lockin_accel * current_meas_period; distance += vel * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); + if (!motor_.update(config_.lockin_current, phase)) - return error_ |= ERROR_MOTOR_FAILED, false; - vel_done = vel >= config_.lockin_vel; - dist_done = fabsf(distance) >= fabsf(config_.lockin_finish_distance); - if (config_.lockin_finish_on_distance) - return !vel_done && !dist_done; - else - return !vel_done; + return false; + return !spin_done(true); //vel_override to go to next phase }); // Constant speed - if (config_.lockin_finish_on_distance) { - vel = config_.lockin_vel; - run_control_loop([&](){ + if (!spin_done()) { + vel = config_.lockin_vel; // reset to actual specified vel to avoid small integration error + run_control_loop([&]() { distance += vel * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); + if (!motor_.update(config_.lockin_current, phase)) - return error_ |= ERROR_MOTOR_FAILED, false; - dist_done = fabsf(distance) >= fabsf(config_.lockin_finish_distance); - return !dist_done; + return false; + return !spin_done(); }); } diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 7b4c7bf2..31dc1488 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -55,7 +55,9 @@ public: float lockin_ramp_distance = 4 * M_PI; // [rad] float lockin_accel = 400.0f; // [rad/s^2] float lockin_vel = 400.0f; // [rad/s] - bool lockin_finish_on_distance = false; // false: finish on target vel + bool lockin_finish_on_vel = false; + bool lockin_finish_on_distance = false; + bool lockin_finish_on_enc_idx = false; float lockin_finish_distance = 1000.0f; // [rad] }; @@ -196,8 +198,10 @@ public: make_protocol_property("lockin_ramp_distance", &config_.lockin_ramp_distance), make_protocol_property("lockin_accel", &config_.lockin_accel), make_protocol_property("lockin_vel", &config_.lockin_vel), + make_protocol_property("lockin_finish_distance", &config_.lockin_finish_distance), + make_protocol_property("lockin_finish_on_vel", &config_.lockin_finish_on_vel), make_protocol_property("lockin_finish_on_distance", &config_.lockin_finish_on_distance), - make_protocol_property("lockin_finish_distance", &config_.lockin_finish_distance) + make_protocol_property("lockin_finish_on_enc_idx", &config_.lockin_finish_on_enc_idx) ), make_protocol_function("get_temp", *this, &Axis::get_temp), make_protocol_object("motor", motor_.make_protocol_definitions()), From b967a5ba5d6c9a88f82146ac41ff1ad2470d971f Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 8 Oct 2018 01:32:13 -0700 Subject: [PATCH 007/116] add hard fault debug --- Firmware/Board/v3/Src/stm32f4xx_it.c | 34 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index 8acc96ee..7ee8eecb 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -88,22 +88,34 @@ void NMI_Handler(void) /* USER CODE END NonMaskableInt_IRQn 1 */ } +void get_regs(void** stack_ptr) { + void* volatile r0 __attribute__((unused)) = stack_ptr[0]; + void* volatile r1 __attribute__((unused)) = stack_ptr[1]; + void* volatile r2 __attribute__((unused)) = stack_ptr[2]; + void* volatile r3 __attribute__((unused)) = stack_ptr[3]; + + void* volatile r12 __attribute__((unused)) = stack_ptr[4]; + void* volatile lr __attribute__((unused)) = stack_ptr[5]; // Link register + void* volatile pc __attribute__((unused)) = stack_ptr[6]; // Program counter + void* volatile psr __attribute__((unused)) = stack_ptr[7]; // Program status register + + volatile bool stay_looping = true; + while(stay_looping); +} + /** * @brief This function handles Hard fault interrupt. */ +__attribute__((naked)) void HardFault_Handler(void) { - /* USER CODE BEGIN HardFault_IRQn 0 */ - - /* USER CODE END HardFault_IRQn 0 */ - while (1) - { - /* USER CODE BEGIN W1_HardFault_IRQn 0 */ - /* USER CODE END W1_HardFault_IRQn 0 */ - } - /* USER CODE BEGIN HardFault_IRQn 1 */ - - /* USER CODE END HardFault_IRQn 1 */ + __asm( + " tst lr, #4 \n\t" + " ite eq \n\t" + " mrseq r0, msp \n\t" + " mrsne r0, psp \n\t" + " b get_regs \n\t" + ); } /** From ac8691ec600a48a6c328be3ecb074fc504885610 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 8 Oct 2018 01:38:57 -0700 Subject: [PATCH 008/116] increase comms task stack space --- Firmware/communication/communication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index 622bdefb..0198e6c8 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -84,7 +84,7 @@ void init_communication(void) { printf("hi!\r\n"); // Start command handling thread - osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, 5000 /* in 32-bit words */); // TODO: fix stack issues + osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, 5500 /* in 32-bit words */); // TODO: fix stack issues comm_thread = osThreadCreate(osThread(task_cmd_parse), NULL); while (!endpoint_list_valid) From 6462651e00e036ffe08fdb6484ee2b96cccb2260 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 8 Oct 2018 01:46:24 -0700 Subject: [PATCH 009/116] generate disasembly in build --- Firmware/build.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firmware/build.lua b/Firmware/build.lua index ea792219..8962e403 100644 --- a/Firmware/build.lua +++ b/Firmware/build.lua @@ -99,6 +99,8 @@ function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) } -- display the size tup.frule{inputs={output_name..'.elf'}, command=prefix..'size %f'} + -- generate disassembly + tup.frule{inputs={output_name..'.elf'}, command=prefix..'objdump %f -dSC > %o', outputs={output_name..'.asm'}} -- create *.hex and *.bin output formats tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O ihex %f %o', outputs={output_name..'.hex'}} tup.frule{inputs={output_name..'.elf'}, command=prefix..'objcopy -O binary -S %f %o', outputs={output_name..'.bin'}} From 3a61b4a6accb64cfe712629b50b27cf07d6c45a1 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 8 Oct 2018 19:40:44 -0700 Subject: [PATCH 010/116] lockin with enc sense working --- Firmware/MotorControl/axis.cpp | 6 ++++-- Firmware/MotorControl/axis.hpp | 11 ++++++++++- Firmware/MotorControl/encoder.cpp | 2 ++ Firmware/MotorControl/encoder.hpp | 4 +++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 2136ebcb..276aa8ca 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -134,6 +134,7 @@ float Axis::get_temp() { bool Axis::run_lockin_spin() { // Spiral up current for softer rotor lock-in + lockin_state_ = LOCKIN_STATE_RAMP; float x = 0.0f; run_control_loop([&]() { float phase = wrap_pm_pi(config_.lockin_ramp_time * x); @@ -143,8 +144,6 @@ bool Axis::run_lockin_spin() { return false; return x < 1.0f; }); - if (error_ != ERROR_NONE) - return false; // Spin states float distance = config_.lockin_ramp_time; @@ -164,6 +163,7 @@ bool Axis::run_lockin_spin() { }; // Accelerate + lockin_state_ = LOCKIN_STATE_ACCELERATE; run_control_loop([&]() { vel += config_.lockin_accel * current_meas_period; distance += vel * current_meas_period; @@ -176,6 +176,7 @@ bool Axis::run_lockin_spin() { // Constant speed if (!spin_done()) { + lockin_state_ = LOCKIN_STATE_CONST_VEL; vel = config_.lockin_vel; // reset to actual specified vel to avoid small integration error run_control_loop([&]() { distance += vel * current_meas_period; @@ -187,6 +188,7 @@ bool Axis::run_lockin_spin() { }); } + lockin_state_ = LOCKIN_STATE_INACTIVE; return check_for_errors(); } diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 31dc1488..36edaab7 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -55,7 +55,7 @@ public: float lockin_ramp_distance = 4 * M_PI; // [rad] float lockin_accel = 400.0f; // [rad/s^2] float lockin_vel = 400.0f; // [rad/s] - bool lockin_finish_on_vel = false; + bool lockin_finish_on_vel = true; bool lockin_finish_on_distance = false; bool lockin_finish_on_enc_idx = false; float lockin_finish_distance = 1000.0f; // [rad] @@ -65,6 +65,13 @@ public: M_SIGNAL_PH_CURRENT_MEAS = 1u << 0 }; + enum LockinState_t { + LOCKIN_STATE_INACTIVE, + LOCKIN_STATE_RAMP, + LOCKIN_STATE_ACCELERATE, + LOCKIN_STATE_CONST_VEL, + }; + Axis(const AxisHardwareConfig_t& hw_config, Config_t& config, Encoder& encoder, @@ -176,6 +183,7 @@ public: State_t task_chain_[10] = { AXIS_STATE_UNDEFINED }; State_t& current_state_ = task_chain_[0]; uint32_t loop_counter_ = 0; + LockinState_t lockin_state_ = LOCKIN_STATE_INACTIVE; // Communication protocol definitions auto make_protocol_definitions() { @@ -185,6 +193,7 @@ public: make_protocol_ro_property("current_state", ¤t_state_), make_protocol_property("requested_state", &requested_state_), make_protocol_ro_property("loop_counter", &loop_counter_), + make_protocol_ro_property("lockin_state", &lockin_state_), make_protocol_object("config", make_protocol_property("startup_motor_calibration", &config_.startup_motor_calibration), make_protocol_property("startup_encoder_index_search", &config_.startup_encoder_index_search), diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index f8e1e530..45885c29 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -43,6 +43,8 @@ bool Encoder::do_checks(){ // TODO: disable interrupt once we found the index void Encoder::enc_index_cb() { if (config_.use_index && !index_found_) { + if (config_.find_idx_on_lockin && axis_->lockin_state_ != Axis::LOCKIN_STATE_CONST_VEL) + return; set_circular_count(0, false); set_linear_count(0); // Avoid position control transient after search if (config_.pre_calibrated) { diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 6eca0536..baa32ad0 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -36,6 +36,7 @@ public: float offset_float = 0.0f; // Sub-count phase alignment offset float calib_range = 0.02f; float bandwidth = 1000.0f; + bool find_idx_on_lockin = false; }; Encoder(const EncoderHardwareConfig_t& hw_config, @@ -104,7 +105,8 @@ public: make_protocol_property("offset_float", &config_.offset_float), make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), - make_protocol_property("calib_range", &config_.calib_range) + make_protocol_property("calib_range", &config_.calib_range), + make_protocol_property("find_idx_on_lockin", &config_.find_idx_on_lockin) ) ); } From b28f45383333ca8d74861798b50a7dd3342e78e8 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 8 Oct 2018 19:59:18 -0700 Subject: [PATCH 011/116] explicit state prerequisite checks --- Firmware/MotorControl/axis.cpp | 19 +++++++++++++------ Firmware/MotorControl/axis.hpp | 10 ++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 276aa8ca..05140c06 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -287,12 +287,6 @@ void Axis::run_state_machine_loop() { // Note that current_state is a reference to task_chain_[0] - // Validate the state before running it - if (current_state_ > AXIS_STATE_MOTOR_CALIBRATION && !motor_.is_calibrated_) - current_state_ = AXIS_STATE_UNDEFINED; - if (current_state_ > AXIS_STATE_ENCODER_OFFSET_CALIBRATION && !encoder_.is_ready_) - current_state_ = AXIS_STATE_UNDEFINED; - // Run the specified state // Handlers should exit if requested_state != AXIS_STATE_UNDEFINED bool status; @@ -302,18 +296,26 @@ void Axis::run_state_machine_loop() { break; case AXIS_STATE_ENCODER_INDEX_SEARCH: + if (!motor_.is_calibrated_) + goto invalid_state_label; status = encoder_.run_index_search(); break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: + if (!motor_.is_calibrated_) + goto invalid_state_label; status = encoder_.run_offset_calibration(); break; case AXIS_STATE_LOCKIN_SPIN: + if (!motor_.is_calibrated_) + goto invalid_state_label; status = run_lockin_spin(); break; case AXIS_STATE_SENSORLESS_CONTROL: + if (!motor_.is_calibrated_) + goto invalid_state_label; status = run_lockin_spin(); // TODO: restart if desired if (status) { // call to controller.reset() that happend when arming means that vel_setpoint @@ -324,6 +326,10 @@ void Axis::run_state_machine_loop() { break; case AXIS_STATE_CLOSED_LOOP_CONTROL: + if (!motor_.is_calibrated_) + goto invalid_state_label; + if (!encoder_.is_ready_) + goto invalid_state_label; status = run_closed_loop_control_loop(); break; @@ -333,6 +339,7 @@ void Axis::run_state_machine_loop() { break; default: + invalid_state_label: error_ |= ERROR_INVALID_STATE; status = false; // this will set the state to idle break; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 36edaab7..2755ba43 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -22,8 +22,6 @@ public: ERROR_POS_CTRL_DURING_SENSORLESS = 0x400, }; - // Warning: Do not reorder these enum values. - // The state machine uses ">" comparision on them. enum State_t { AXIS_STATE_UNDEFINED = 0, // Date: Wed, 10 Oct 2018 14:23:10 -0700 Subject: [PATCH 012/116] Lockin based index search working, clean out old search --- Firmware/MotorControl/axis.cpp | 38 +++++++++++++++++++------------ Firmware/MotorControl/axis.hpp | 10 ++++---- Firmware/MotorControl/encoder.cpp | 32 -------------------------- Firmware/MotorControl/encoder.hpp | 1 - tools/odrive/enums.py | 8 +++---- 5 files changed, 32 insertions(+), 57 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 05140c06..31e6a354 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -291,29 +291,37 @@ void Axis::run_state_machine_loop() { // Handlers should exit if requested_state != AXIS_STATE_UNDEFINED bool status; switch (current_state_) { - case AXIS_STATE_MOTOR_CALIBRATION: + case AXIS_STATE_MOTOR_CALIBRATION: { status = motor_.run_calibration(); - break; + } break; - case AXIS_STATE_ENCODER_INDEX_SEARCH: + case AXIS_STATE_ENCODER_INDEX_SEARCH: { if (!motor_.is_calibrated_) goto invalid_state_label; - status = encoder_.run_index_search(); - break; - case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: + encoder_.config_.use_index = true; + encoder_.index_found_ = false; + + bool orig_setting = config_.lockin_finish_on_enc_idx; + config_.lockin_finish_on_enc_idx = true; + status = run_lockin_spin(); + config_.lockin_finish_on_enc_idx = orig_setting; + // status = encoder_.run_index_search(); + } break; + + case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: { if (!motor_.is_calibrated_) goto invalid_state_label; status = encoder_.run_offset_calibration(); - break; + } break; - case AXIS_STATE_LOCKIN_SPIN: + case AXIS_STATE_LOCKIN_SPIN: { if (!motor_.is_calibrated_) goto invalid_state_label; status = run_lockin_spin(); - break; + } break; - case AXIS_STATE_SENSORLESS_CONTROL: + case AXIS_STATE_SENSORLESS_CONTROL: { if (!motor_.is_calibrated_) goto invalid_state_label; status = run_lockin_spin(); // TODO: restart if desired @@ -323,20 +331,20 @@ void Axis::run_state_machine_loop() { controller_.vel_setpoint_ = config_.lockin_vel; status = run_sensorless_control_loop(); } - break; + } break; - case AXIS_STATE_CLOSED_LOOP_CONTROL: + case AXIS_STATE_CLOSED_LOOP_CONTROL: { if (!motor_.is_calibrated_) goto invalid_state_label; if (!encoder_.is_ready_) goto invalid_state_label; status = run_closed_loop_control_loop(); - break; + } break; - case AXIS_STATE_IDLE: + case AXIS_STATE_IDLE: { run_idle_loop(); status = motor_.arm(); // done with idling - try to arm the motor - break; + } break; default: invalid_state_label: diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 2755ba43..909b0e66 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -50,13 +50,13 @@ public: // Spinup settings float lockin_current = 10.0f; // [A] float lockin_ramp_time = 0.4f; // [s] - float lockin_ramp_distance = 4 * M_PI; // [rad] - float lockin_accel = 400.0f; // [rad/s^2] - float lockin_vel = 400.0f; // [rad/s] - bool lockin_finish_on_vel = true; + float lockin_ramp_distance = 1 * M_PI; // [rad] + float lockin_accel = 10.0f; // [rad/s^2] + float lockin_vel = 100.0f; // [rad/s] + float lockin_finish_distance = 1000.0f; // [rad] + bool lockin_finish_on_vel = false; bool lockin_finish_on_distance = false; bool lockin_finish_on_enc_idx = false; - float lockin_finish_distance = 1000.0f; // [rad] }; enum thread_signals { diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 45885c29..781ea1d2 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -91,38 +91,6 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) { cpu_exit_critical(prim); } - -// @brief Slowly turns the motor in one direction until the -// encoder index is found. -// TODO: Do the scan with current, not voltage! -bool Encoder::run_index_search() { - float voltage_magnitude; - 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::MOTOR_TYPE_GIMBAL) - voltage_magnitude = axis_->motor_.config_.calibration_current; - else - return false; - - float omega = (float)(axis_->motor_.config_.direction) * config_.idx_search_speed; - - index_found_ = false; - float phase = 0.0f; - axis_->run_control_loop([&](){ - phase = wrap_pm_pi(phase + omega * current_meas_period); - - float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); - float v_beta = voltage_magnitude * our_arm_sin_f32(phase); - if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) - return false; // error set inside enqueue_voltage_timings - axis_->motor_.log_timing(Motor::TIMING_LOG_IDX_SEARCH); - - // continue until the index is found - return !index_found_; - }); - return true; -} - // @brief Turns the motor in one direction for a bit and then in the other // direction in order to find the offset between the electrical phase 0 // and the encoder state 0. diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index baa32ad0..ff0d82b7 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -51,7 +51,6 @@ public: void set_linear_count(int32_t count); void set_circular_count(int32_t count, bool update_offset); bool calib_enc_offset(float voltage_magnitude); - bool scan_for_enc_idx(float omega, float voltage_magnitude); bool run_index_search(); bool run_offset_calibration(); diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index ca73b315..677bcbed 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -7,10 +7,10 @@ AXIS_STATE_STARTUP_SEQUENCE = 2 AXIS_STATE_FULL_CALIBRATION_SEQUENCE = 3 AXIS_STATE_MOTOR_CALIBRATION = 4 AXIS_STATE_SENSORLESS_CONTROL = 5 -AXIS_STATE_LOCKIN_SPIN = 6 -AXIS_STATE_ENCODER_INDEX_SEARCH = 7 -AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 8 -AXIS_STATE_CLOSED_LOOP_CONTROL = 9 +AXIS_STATE_ENCODER_INDEX_SEARCH = 6 +AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7 +AXIS_STATE_CLOSED_LOOP_CONTROL = 8 +AXIS_STATE_LOCKIN_SPIN = 9 AXIS_ERROR_NONE = 0 AXIS_ERROR_INVALID_STATE = 1 From d7644daacaf5e58114bd7832da54850fcaf69e68 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 10 Oct 2018 15:35:41 -0700 Subject: [PATCH 013/116] make encoder dir find and overspeed check --- Firmware/MotorControl/axis.cpp | 42 +++++++++++++++++++++++++------ Firmware/MotorControl/axis.hpp | 1 + Firmware/MotorControl/encoder.cpp | 9 +++++++ Firmware/MotorControl/encoder.hpp | 7 +++++- Firmware/MotorControl/motor.hpp | 2 +- tools/odrive/enums.py | 1 + 6 files changed, 53 insertions(+), 9 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 31e6a354..457d0279 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -137,7 +137,7 @@ bool Axis::run_lockin_spin() { lockin_state_ = LOCKIN_STATE_RAMP; float x = 0.0f; run_control_loop([&]() { - float phase = wrap_pm_pi(config_.lockin_ramp_time * x); + float phase = wrap_pm_pi(config_.lockin_ramp_distance * x); float I_mag = config_.lockin_current * x; x += current_meas_period / config_.lockin_ramp_time; if (!motor_.update(I_mag, phase)) @@ -146,7 +146,7 @@ bool Axis::run_lockin_spin() { }); // Spin states - float distance = config_.lockin_ramp_time; + float distance = config_.lockin_ramp_distance; float phase = wrap_pm_pi(distance); float vel = distance / config_.lockin_ramp_time; @@ -154,7 +154,7 @@ bool Axis::run_lockin_spin() { auto spin_done = [&](bool vel_override = false) -> bool { bool done = false; if (config_.lockin_finish_on_vel || vel_override) - done = done || vel >= config_.lockin_vel; + done = done || fabsf(vel) >= fabsf(config_.lockin_vel); if (config_.lockin_finish_on_distance) done = done || fabsf(distance) >= fabsf(config_.lockin_finish_distance); if (config_.lockin_finish_on_enc_idx) @@ -298,7 +298,10 @@ void Axis::run_state_machine_loop() { case AXIS_STATE_ENCODER_INDEX_SEARCH: { if (!motor_.is_calibrated_) goto invalid_state_label; + if (encoder_.config_.idx_search_unidirectional && motor_.config_.direction==0) + goto invalid_state_label; + // TODO: move code body to function in Encoder encoder_.config_.use_index = true; encoder_.index_found_ = false; @@ -306,7 +309,32 @@ void Axis::run_state_machine_loop() { config_.lockin_finish_on_enc_idx = true; status = run_lockin_spin(); config_.lockin_finish_on_enc_idx = orig_setting; - // status = encoder_.run_index_search(); + } break; + + case AXIS_STATE_ENCODER_DIR_FIND: { + if (!motor_.is_calibrated_) + goto invalid_state_label; + + // TODO: move code body to function in Encoder + int32_t init_enc_val = encoder_.shadow_count_; + bool orig_setting = config_.lockin_finish_on_distance; + config_.lockin_finish_on_distance = true; + motor_.config_.direction = 1; // Must test spin forwards for direction detect logic + status = run_lockin_spin(); + config_.lockin_finish_on_distance = orig_setting; + + if (status) { + // Check response and direction + if (encoder_.shadow_count_ > init_enc_val + 8) { + // motor same dir as encoder + motor_.config_.direction = 1; + } else if (encoder_.shadow_count_ < init_enc_val - 8) { + // motor opposite dir as encoder + motor_.config_.direction = -1; + } else { + motor_.config_.direction = 0; + } + } } break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: { @@ -316,13 +344,13 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_LOCKIN_SPIN: { - if (!motor_.is_calibrated_) + if (!motor_.is_calibrated_ || motor_.config_.direction==0) goto invalid_state_label; status = run_lockin_spin(); } break; case AXIS_STATE_SENSORLESS_CONTROL: { - if (!motor_.is_calibrated_) + if (!motor_.is_calibrated_ || motor_.config_.direction==0) goto invalid_state_label; status = run_lockin_spin(); // TODO: restart if desired if (status) { @@ -334,7 +362,7 @@ void Axis::run_state_machine_loop() { } break; case AXIS_STATE_CLOSED_LOOP_CONTROL: { - if (!motor_.is_calibrated_) + if (!motor_.is_calibrated_ || motor_.config_.direction==0) goto invalid_state_label; if (!encoder_.is_ready_) goto invalid_state_label; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 909b0e66..3d89d3a0 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -33,6 +33,7 @@ public: AXIS_STATE_ENCODER_OFFSET_CALIBRATION = 7, // config_.overspeed_fault_ratio * axis_->controller_.config_.vel_limit) { + set_error(ERROR_OVERSPEED); + return false; + } + } + //// run encoder count interpolation int32_t corrected_enc = count_in_cpr_ - config_.offset; // if we are stopped, make sure we don't randomly drift diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index ff0d82b7..38889c12 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -15,6 +15,7 @@ public: ERROR_UNSUPPORTED_ENCODER_MODE = 0x08, ERROR_ILLEGAL_HALL_STATE = 0x10, ERROR_INDEX_NOT_FOUND_YET = 0x20, + ERROR_OVERSPEED = 0x40, }; enum Mode_t { @@ -36,7 +37,9 @@ public: float offset_float = 0.0f; // Sub-count phase alignment offset float calib_range = 0.02f; float bandwidth = 1000.0f; + float overspeed_fault_ratio = 1.2f; // ratio of vel_lim, 0.0f = disabled bool find_idx_on_lockin = false; + bool idx_search_unidirectional = false; }; Encoder(const EncoderHardwareConfig_t& hw_config, @@ -98,6 +101,7 @@ public: make_protocol_property("mode", &config_.mode), make_protocol_property("use_index", &config_.use_index), make_protocol_property("pre_calibrated", &config_.pre_calibrated), + make_protocol_property("overspeed_fault_ratio", &config_.overspeed_fault_ratio), make_protocol_property("idx_search_speed", &config_.idx_search_speed), make_protocol_property("cpr", &config_.cpr), make_protocol_property("offset", &config_.offset), @@ -105,7 +109,8 @@ public: make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), make_protocol_property("calib_range", &config_.calib_range), - make_protocol_property("find_idx_on_lockin", &config_.find_idx_on_lockin) + make_protocol_property("find_idx_on_lockin", &config_.find_idx_on_lockin), + make_protocol_property("idx_search_unidirectional", &config_.idx_search_unidirectional) ) ); } diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index ebfd3a12..b67d4874 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -58,7 +58,7 @@ public: float resistance_calib_max_voltage = 2.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 + int32_t direction = 0; // 1 or -1 (0 = unspecified) 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] diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 677bcbed..ec374bdc 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -11,6 +11,7 @@ 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_ERROR_NONE = 0 AXIS_ERROR_INVALID_STATE = 1 From 78bf4a732089c09b7d406111a587f24fa943900d Mon Sep 17 00:00:00 2001 From: samuelsadok Date: Fri, 30 Nov 2018 08:41:47 +0100 Subject: [PATCH 014/116] disarm motor if brake is disarmed --- Firmware/MotorControl/low_level.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index b40b3274..d1990a19 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -125,7 +125,7 @@ bool safety_critical_disarm_motor_pwm(Motor& motor) { void safety_critical_apply_motor_pwm_timings(Motor& motor, uint16_t timings[3]) { uint32_t mask = cpu_enter_critical(); if (!brake_resistor_armed) { - motor.armed_state_ = Motor::ARMED_STATE_ARMED; + motor.armed_state_ = Motor::ARMED_STATE_DISARMED; } motor.hw_config_.timer->Instance->CCR1 = timings[0]; @@ -713,4 +713,4 @@ void pwm_in_cb(int channel, uint32_t timestamp) { last_timestamp[gpio_num - 1] = timestamp; last_pin_state[gpio_num - 1] = current_pin_state; last_sample_valid[gpio_num - 1] = true; -} \ No newline at end of file +} From ff2bd83568ebb78796e259dcb17076295c49aea0 Mon Sep 17 00:00:00 2001 From: Mark Omo Date: Fri, 30 Nov 2018 13:40:57 -0700 Subject: [PATCH 015/116] Create License File Create License File --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..7b58d626 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Odrive + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 11592f9be6f27664dd9f0f80785d4750b360e95a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 6 Dec 2018 17:04:52 -0800 Subject: [PATCH 016/116] Update hoverboard.md --- docs/hoverboard.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/hoverboard.md b/docs/hoverboard.md index 2e9f6cf4..2851b516 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -18,6 +18,8 @@ You may wire the motor phases in any order into a motor connector on the ODrive, | Green | Z | | Black | GND | +Note: In order to ber compatible with encoder inputs, the ODrive doesn't have any filtering capacitors on the pins where the hall sensors connect. Therefore to get a reliable hall signal, it is recommended that you add some filter capacitors to these pins. You can see instructions [here](https://discourse.odriverobotics.com/t/encoder-error-error-illegal-hall-state/1047/7?u=madcowswe) + ### Hoverboard motor configuration Standard 6.5 inch hoverboard hub motors have 30 permanent magnet poles, and thus 15 pole pairs. If you have a different motor you need to count the magnets or have a reliable datasheet for this information. From 82665389241daa122d1e0171e1d13e09203e7267 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 6 Dec 2018 17:05:02 -0800 Subject: [PATCH 017/116] Update hoverboard.md --- docs/hoverboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hoverboard.md b/docs/hoverboard.md index 2851b516..1c0f0ce3 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -18,7 +18,7 @@ You may wire the motor phases in any order into a motor connector on the ODrive, | Green | Z | | Black | GND | -Note: In order to ber compatible with encoder inputs, the ODrive doesn't have any filtering capacitors on the pins where the hall sensors connect. Therefore to get a reliable hall signal, it is recommended that you add some filter capacitors to these pins. You can see instructions [here](https://discourse.odriverobotics.com/t/encoder-error-error-illegal-hall-state/1047/7?u=madcowswe) +Note: In order to ber compatible with encoder inputs, the ODrive doesn't have any filtering capacitors on the pins where the hall sensors connect. Therefore to get a reliable hall signal, it is recommended that you add some filter capacitors to these pins. You can see instructions [here](https://discourse.odriverobotics.com/t/encoder-error-error-illegal-hall-state/1047/7?u=madcowswe). ### Hoverboard motor configuration From 73bb86440d6fb48ac219a2b4af797bf10a70a9b5 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 9 Dec 2018 15:21:02 -0800 Subject: [PATCH 018/116] Update getting-started.md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index d9cc3dd6..8b81f657 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -40,7 +40,7 @@ permalink: / -* A power supply (12V-24V for the 24V board variant, 12V-48V for the 48V board variant). A battery is also fine. +* A power supply (12V-24V for the 24V board variant, 12V-48V for the 48V board variant). A battery is also fine. Some advice on choosing a power supply can be found [here](https://things-in-motion.blogspot.com/2018/12/how-to-select-right-power-source-for.html).
What voltage variant do I have?
On all ODrives shipped July 2018 or after have a silkscreen label clearly indicating the voltage variant. From 44152147d735a76ae7398f74ae6de0183feb9c37 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 17 Dec 2018 13:51:03 -0800 Subject: [PATCH 019/116] add ignore_illegal_hall_state --- Firmware/MotorControl/encoder.cpp | 6 ++++-- Firmware/MotorControl/encoder.hpp | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 8c0691f8..9b964f21 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -271,8 +271,10 @@ bool Encoder::update() { if (delta_enc > 3) delta_enc -= 6; } else { - set_error(ERROR_ILLEGAL_HALL_STATE); - return false; + if (!config_.ignore_illegal_hall_state) { + set_error(ERROR_ILLEGAL_HALL_STATE); + return false; + } } } break; diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 78f8f4f6..96e1a914 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -37,6 +37,7 @@ public: float offset_float = 0.0f; // Sub-count phase alignment offset float calib_range = 0.02f; float bandwidth = 1000.0f; + bool ignore_illegal_hall_state = false; }; Encoder(const EncoderHardwareConfig_t& hw_config, @@ -106,7 +107,8 @@ public: make_protocol_property("offset_float", &config_.offset_float), make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), - make_protocol_property("calib_range", &config_.calib_range) + make_protocol_property("calib_range", &config_.calib_range), + make_protocol_property("ignore_illegal_hall_state", &config_.ignore_illegal_hall_state) ) ); } From 6e34beba9fa88e13610e46c586869ccf3084da97 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 17 Dec 2018 14:19:55 -0800 Subject: [PATCH 020/116] increase analog polling to 100Hz --- Firmware/MotorControl/low_level.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 7748d6f4..e8fe62a4 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -734,7 +734,7 @@ static void analog_polling_thread(void *) if (is_endpoint_ref_valid(map->endpoint)) update_analog_endpoint(map, i + 1); } - osDelay(200); + osDelay(10); } } From 43bb11f89d3bd9137003d8a4600fff2539c35504 Mon Sep 17 00:00:00 2001 From: Svyatoslav Mishin Date: Fri, 28 Dec 2018 12:50:41 +0300 Subject: [PATCH 021/116] link to ArduinoLib was fixed --- docs/ascii-protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index 951ec214..51ff18ea 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -7,7 +7,7 @@ * **Windows:** Use the Zadig utility to set the ODrive's driver to "usbser". Windows will then make the device available as COM port. You can use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language * **Linux/macOS:** Run `/dev/tty*` to list all serial ports. The ODrive will show up as `/dev/ttyACM0` on Linux and `/dev/tty.usbmodem[...]` on macOS. Once you know the name, you can use `screen /dev/ttyACM0` (with the correct name) to send commands manually or open the device using your favorite programming language. Serial ports on Unix can be opened, written to and read from like a normal file. * **Via UART:** Connect the ODrive's TX (GPIO1) to your host's RX. Connect your ODrive's RX (GPIO2) to your host's TX. The logic level of the ODrive is 3.3V. - * **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODriveArduino) to talk to the ODrive. + * **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODrive/tree/master/Arduino/ODriveArduino) to talk to the ODrive. * **Windows/Linux/macOS:** You can use an FTDI USB-UART cable to connect to the ODrive. ## Command format From a676071cef1aca853a3ca067aeece14e074674a6 Mon Sep 17 00:00:00 2001 From: Jasper Galvin Date: Thu, 3 Jan 2019 18:51:21 +0800 Subject: [PATCH 022/116] Update getting-started.md I was trying to work out how to enable To enable Circular position control but could only find info on discord so added here --- docs/getting-started.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index 8b81f657..9408b23d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -288,6 +288,9 @@ Use the `move_to_pos` function to move to an absolute position: ``` ### Circular position control + +To enabel Circular position control 'axis.controller.config.setpoints_in_cpr = True' + This mode is useful for continuos incremental position movement. For example a robot rolling indefinitely, or an extruder motor or conveyor belt moving with controlled increments indefinitely. In the regular position mode, the `pos_setpoint` would grow to a very large value and would lose precision due to floating point rounding. From 7d99aff2fe9d1504f3b564b5b60478c6e49071c5 Mon Sep 17 00:00:00 2001 From: Pattinson Date: Thu, 3 Jan 2019 12:18:58 -0800 Subject: [PATCH 023/116] Update hyperlink to "ODrive Arduino Library" Update link to https://github.com/madcowswe/ODrive/tree/master/Arduino/ODriveArduino --- docs/ascii-protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index 951ec214..51ff18ea 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -7,7 +7,7 @@ * **Windows:** Use the Zadig utility to set the ODrive's driver to "usbser". Windows will then make the device available as COM port. You can use [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) to manually send commands or open the COM port using your favorite programming language * **Linux/macOS:** Run `/dev/tty*` to list all serial ports. The ODrive will show up as `/dev/ttyACM0` on Linux and `/dev/tty.usbmodem[...]` on macOS. Once you know the name, you can use `screen /dev/ttyACM0` (with the correct name) to send commands manually or open the device using your favorite programming language. Serial ports on Unix can be opened, written to and read from like a normal file. * **Via UART:** Connect the ODrive's TX (GPIO1) to your host's RX. Connect your ODrive's RX (GPIO2) to your host's TX. The logic level of the ODrive is 3.3V. - * **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODriveArduino) to talk to the ODrive. + * **Arduino:** You can use the [ODrive Arduino library](https://github.com/madcowswe/ODrive/tree/master/Arduino/ODriveArduino) to talk to the ODrive. * **Windows/Linux/macOS:** You can use an FTDI USB-UART cable to connect to the ODrive. ## Command format From 1627666cdfce8ee2f23b52dda03d100b374b633d Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 3 Jan 2019 17:00:26 -0800 Subject: [PATCH 024/116] Update getting-started.md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 9408b23d..e0991902 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -289,7 +289,7 @@ Use the `move_to_pos` function to move to an absolute position: ### Circular position control -To enabel Circular position control 'axis.controller.config.setpoints_in_cpr = True' +To enable Circular position control, set 'axis.controller.config.setpoints_in_cpr = True' This mode is useful for continuos incremental position movement. For example a robot rolling indefinitely, or an extruder motor or conveyor belt moving with controlled increments indefinitely. In the regular position mode, the `pos_setpoint` would grow to a very large value and would lose precision due to floating point rounding. From 68372a901e7fffcc03491cef7f97bfcc530e18bc Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 3 Jan 2019 18:15:09 -0800 Subject: [PATCH 025/116] add ascii q command --- CHANGELOG.md | 1 + Firmware/communication/ascii_protocol.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b001221..76f535d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors. +* `q` command to ascii protocol. It is like the old `p` command, but velocity and current mean limits, not feed-forward. # Releases ## [0.4.7] - 2018-11-28 diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 0981cc1d..5175ac49 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -102,6 +102,21 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& axes[motor_number]->controller_.set_pos_setpoint(pos_setpoint, vel_feed_forward, current_feed_forward); } + } else if (cmd[0] == 'q') { // position control with limits + unsigned motor_number; + float pos_setpoint, vel_limit, current_lim; + int numscan = sscanf(cmd, "q %u %f %f %f", &motor_number, &pos_setpoint, &vel_limit, ¤t_lim); + if (numscan < 4) { + 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 { + Axis* axis = axes[motor_number]; + axis->controller_.pos_setpoint_ = pos_setpoint; + axis->controller_.config_.vel_limit = vel_limit; + axis->motor_.config_.current_lim = current_lim; + } + } else if (cmd[0] == 'v') { // velocity control unsigned motor_number; float vel_setpoint, current_feed_forward; From 5cd64f2d4f184e4d65f57191cc33780f159cf850 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 3 Jan 2019 18:17:12 -0800 Subject: [PATCH 026/116] add current command to Arduino code --- Arduino/ODriveArduino/ODriveArduino.cpp | 4 ++++ Arduino/ODriveArduino/ODriveArduino.h | 1 + 2 files changed, 5 insertions(+) diff --git a/Arduino/ODriveArduino/ODriveArduino.cpp b/Arduino/ODriveArduino/ODriveArduino.cpp index 6e4a2748..64af8b2f 100644 --- a/Arduino/ODriveArduino/ODriveArduino.cpp +++ b/Arduino/ODriveArduino/ODriveArduino.cpp @@ -38,6 +38,10 @@ void ODriveArduino::SetVelocity(int motor_number, float velocity, float current_ serial_ << "v " << motor_number << " " << velocity << " " << current_feedforward << "\n"; } +void ODriveArduino::SetCurrent(int motor_number, float current) { + serial_ << "c" << motor_number << " " << current << "\n"; +} + float ODriveArduino::readFloat() { return readString().toFloat(); } diff --git a/Arduino/ODriveArduino/ODriveArduino.h b/Arduino/ODriveArduino/ODriveArduino.h index 524f5a59..1ebe1e33 100644 --- a/Arduino/ODriveArduino/ODriveArduino.h +++ b/Arduino/ODriveArduino/ODriveArduino.h @@ -26,6 +26,7 @@ public: void SetPosition(int motor_number, float position, float velocity_feedforward, float current_feedforward); void SetVelocity(int motor_number, float velocity); void SetVelocity(int motor_number, float velocity, float current_feedforward); + void SetCurrent(int motor_number, float current); // General params float readFloat(); From b1a0f2958edf4928067970b99c26f49290e035ad Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Fri, 4 Jan 2019 23:18:40 -0500 Subject: [PATCH 027/116] Add note re gcc 8.2.1 --- docs/developer-guide.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/developer-guide.md b/docs/developer-guide.md index ecf3e1ea..03afa0c8 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -80,7 +80,9 @@ __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) __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. +* [ARM compiler](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) + * __Note 1__: 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. + * __Note 2__: 8-2018-q4-major seems to have a bug on Windows. Please use 7-2018-q2-update. * [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. From 5143cc505561a135b28f124d287ed93c250eece4 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Thu, 10 Jan 2019 03:27:19 -0500 Subject: [PATCH 028/116] Fix CHANGELOG.md link --- docs/developer-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 03afa0c8..8083e16b 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -5,7 +5,7 @@ As such it assumes that you know things like how to use Git, what a compiler is, The official releases are maintained on the `master` branch. However since you are a developer, you are encouraged to use the `devel` branch, as it contains the latest features. -The project is under active development, so make sure to check the [Changelog](CHANGELOG.md) to keep track of updates. +The project is under active development, so make sure to check the [Changelog](../CHANGELOG.md) to keep track of updates. ### Table of contents From 5708465aea0ec5ef986e83fd7cc7e20a323cd25a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 10 Jan 2019 18:40:17 -0800 Subject: [PATCH 029/116] Update LICENSE.md --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 7b58d626..977309e4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Odrive +Copyright (c) 2016-2018 ODrive Robotics Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 7ad679c4494dec40d3aec17acdbaeb114dc471e2 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 10 Jan 2019 19:19:58 -0800 Subject: [PATCH 030/116] Update troubleshooting.md --- docs/troubleshooting.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index c05c7f92..a441ef95 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -32,6 +32,7 @@ The axis error may say that some other component has failed. Say it reports `ERR * Axis error flags defined [here](../Firmware/MotorControl/axis.hpp). * Motor error flags defined [here](../Firmware/MotorControl/motor.hpp). * Encoder error flags defined [here](../Firmware/MotorControl/encoder.hpp). +* Controller error flags defined [here](../Firmware/MotorControl/controller.hpp). * Sensorless estimator error flags defined [here](../Firmware/MotorControl/sensorless_estimator.hpp). ## Common Axis Errors From bf39862748217c0a62bee97404230ed84bb31f09 Mon Sep 17 00:00:00 2001 From: jusic Date: Sun, 13 Jan 2019 22:44:44 +0200 Subject: [PATCH 031/116] Update odrivetool.md, how to use dfu-util on macOS Add notes about using dfu-util on macOS. I successfully restored the firmware on macOS High Sierra to a ODrive 3.5 (48V) after a failed odrivetool dfu upload. The only caveat in these instructions is that I don't know any easy way to get objcopy on macOS, so I used a separate Ubuntu VM to convert the firmware .elf to a .bin. --- docs/odrivetool.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index 18dc7b0d..5a8d4358 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -135,9 +135,35 @@ In the Firmware directory, after finishing building the firmware: sudo dfu-util -a 0 -s 0x08000000 -D build/ODriveFirmware.bin ``` -#### MacOS -**This section needs more detail. Please consider adding detail if you got it to work.** -You may be able to use [dfu-util](http://dfu-util.sourceforge.net/) to upgrade the firmware. The command should be similar to the Linux instructions. +#### macOS + +First, you need a Ubuntu/Debian Linux (virtual) machine to convert the `.elf` file from into a `.bin` file that the `dfu-util` understands. Copy the latest `.elf` for your board from [here](https://github.com/madcowswe/ODrive/releases). Proceed to convert the binary (tested on Ubuntu 18.04): + +```text +$ sudo apt install binutils-arm-none-eabi +$ arm-none-eabi-objcopy -O binary ODriveFirmware_v3.5-48V.elf ODriveFirmware_v3.5-48V.bin +``` + +Back on the macOS machine (tested on macOS High Sierra), install `dfu-util`: + +```text +$ sudo port install dfu-util # via MacPorts; for HomeBrew use "brew install dfu-util" +``` + +Then, copy the new `.bin` firmware file to the macOS machine, find the correct device serial number to use. + +```text +$ dfu-util --list # list the DFU capable devices +[...] +Found DFU: [0483:df11] ver=2200, devnum=5, cfg=1, intf=0, path="20-2", alt=0, + name="@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg", serial="388237123123" +``` + +Finally, flash the firmware using the found serial number: + +```text +$ sudo dfu-util -S 388237123123 -a 0 -s 0x08000000 -D ODriveFirmware_v3.5-48V.bin +``` ## Flashing with an STLink From c17b61a10f3c99a542c51c8d8c6f08348a9d19af Mon Sep 17 00:00:00 2001 From: jaredairbusaerial <42249132+jaredairbusaerial@users.noreply.github.com> Date: Sun, 13 Jan 2019 17:36:22 -0500 Subject: [PATCH 032/116] Improved power connection procedure information --- docs/getting-started.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index e0991902..3e2ebf43 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -56,14 +56,19 @@ All non-power I/O is 3.3V output and 5V tolerant on input, on ODrive v3.3 and ne ### Wiring up the motors * Connect the motor phases into the 3-phase screw terminals. It is not recommended to use a clip-on connector such as an alligator clip, as this can cause issues with the phase resistance/inductance measurements. -* Connect the power source to the DC terminals. Make sure to pay attention to the polarity. -* Do not apply power just yet. ### Wiring up the encoders Connect the encoder(s) to J4. The A,B phases are required, and the Z (index pulse) is optional. The A,B and Z lines have 3.3k pull up resistors, for use with open-drain encoder outputs. For single ended push-pull signals with weak drive current (\<4mA), you may want to desolder the pull-ups. ![Image of ODrive all hooked up](https://docs.google.com/drawings/d/e/2PACX-1vTCD0P40Cd-wvD7Fl8UYEaxp3_UL81oI4qUVqrrCJPi6tkJeSs2rsffIXQRpdu6rNZs6-2mRKKYtILG/pub?w=1716&h=1281) +### Safety & Power UP +
+ Always think safety before powering up the ODrive if motors are attached. Consider what might happen if the motor spins as soon as power is applied. +
+* Unlike some devices, the ODrive does not recieve power over the USB port so the 24/48 volt power input is required even just to communicate with it using USB. It is ok to power up the ODrive before or after connecting the USB cable. +* To power up the ODrive, connect the power source to the DC terminals. Make sure to pay attention to the polarity. A small spark is normal. This is caused by the capacitors charging up. + ## Downloading and Installing Tools Most instructions in this guide refer to a utility called `odrivetool`, so you should install that first. From c04a61b621b2f4016f19b90d34e1704e777ab443 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 13 Jan 2019 17:15:47 -0800 Subject: [PATCH 033/116] Update odrivetool.md --- docs/odrivetool.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index 5a8d4358..c00d5bc3 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -137,20 +137,25 @@ sudo dfu-util -a 0 -s 0x08000000 -D build/ODriveFirmware.bin #### macOS -First, you need a Ubuntu/Debian Linux (virtual) machine to convert the `.elf` file from into a `.bin` file that the `dfu-util` understands. Copy the latest `.elf` for your board from [here](https://github.com/madcowswe/ODrive/releases). Proceed to convert the binary (tested on Ubuntu 18.04): +First, you need to install the arm development tools to copy the binary into the appropriate format. + +```text +$ brew cask install gcc-arm-embedded +``` + +Then convert the binary to .bin format ```text -$ sudo apt install binutils-arm-none-eabi $ arm-none-eabi-objcopy -O binary ODriveFirmware_v3.5-48V.elf ODriveFirmware_v3.5-48V.bin ``` -Back on the macOS machine (tested on macOS High Sierra), install `dfu-util`: +Install `dfu-util`: ```text $ sudo port install dfu-util # via MacPorts; for HomeBrew use "brew install dfu-util" ``` -Then, copy the new `.bin` firmware file to the macOS machine, find the correct device serial number to use. +Find the correct device serial number to use: ```text $ dfu-util --list # list the DFU capable devices From 74da2ae51455f3b9d6003b41adb4561a0c4cfa54 Mon Sep 17 00:00:00 2001 From: jaredairbusaerial <42249132+jaredairbusaerial@users.noreply.github.com> Date: Sun, 13 Jan 2019 21:10:26 -0500 Subject: [PATCH 034/116] Missing L in 'remote control' --- docs/hoverboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hoverboard.md b/docs/hoverboard.md index 1c0f0ce3..4ecaaf84 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -120,7 +120,7 @@ odrv0.axis0.requested_state = AXIS_STATE_IDLE Hopefully you got your motor to spin! Feel free to repeat all of the above for the other axis if appropriate. ### PWM input -If you want to drive your hoverboard wheels around with an RC remote contro you can use the [RC PWM input](interfaces.md#rc-pwm-input). There is more information in that link. +If you want to drive your hoverboard wheels around with an RC remote control you can use the [RC PWM input](interfaces.md#rc-pwm-input). There is more information in that link. Lets use GPIO 3/4 for the velocity inputs so that we don't have to disable UART. Then let's map the full stick range of these inputs to some suitable velocity setpoint range. We also have to reboot to activate the PWM input. From 17406fa8a5c4142226131b45ce7675660672ac95 Mon Sep 17 00:00:00 2001 From: jaredairbusaerial <42249132+jaredairbusaerial@users.noreply.github.com> Date: Sun, 13 Jan 2019 21:18:05 -0500 Subject: [PATCH 035/116] Added safety notice for PWM input. --- docs/hoverboard.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/hoverboard.md b/docs/hoverboard.md index 1c0f0ce3..9641ae6d 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -120,7 +120,7 @@ odrv0.axis0.requested_state = AXIS_STATE_IDLE Hopefully you got your motor to spin! Feel free to repeat all of the above for the other axis if appropriate. ### PWM input -If you want to drive your hoverboard wheels around with an RC remote contro you can use the [RC PWM input](interfaces.md#rc-pwm-input). There is more information in that link. +If you want to drive your hoverboard wheels around with an RC remote control you can use the [RC PWM input](interfaces.md#rc-pwm-input). There is more information in that link. Lets use GPIO 3/4 for the velocity inputs so that we don't have to disable UART. Then let's map the full stick range of these inputs to some suitable velocity setpoint range. We also have to reboot to activate the PWM input. @@ -161,6 +161,9 @@ odrv0.axis0.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL odrv0.axis1.requested_state = AXIS_STATE_CLOSED_LOOP_CONTROL ``` +### Safety +Be sure to setup the Failsafe feature on your RC Receiver so that if connection is lost between the remote and the receiver, the receiver outputs 0 and 0 for the velocity setpoint of both axes (or whatever is safest for your configuration). Also note that if the receiver turns off (loss of power, etc) or if the signal from the receiver to the ODrive is lost (wire comes unplugged, etc), the ODrive will continue the last commanded velocity setpoint. There is currently no timeout function in the ODrive for PWM inputs. + ### Automatic startup Try to reboot and then activate AXIS_STATE_CLOSED_LOOP_CONTROL on both axis. Check that everything is operational and works as expected. If so, you can now make the ODrive turn on the motor power automatically after booting. This is useful if you are going to be running the ODrive without a PC or other logic board. From 4c00ffc321858323b2c8134d4bb16cbbf8613c79 Mon Sep 17 00:00:00 2001 From: jaredairbusaerial <42249132+jaredairbusaerial@users.noreply.github.com> Date: Sun, 13 Jan 2019 21:21:31 -0500 Subject: [PATCH 036/116] Added safety notice for PWM input in interfaces doc --- docs/interfaces.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/interfaces.md b/docs/interfaces.md index ff0ec9ac..3c1cf39d 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -127,6 +127,8 @@ As an example, we'll configure GPIO4 to control the angle of axis 0. We want the ``` 5. With the ODrive powered off, connect the RC receiver ground to the ODrive's GND and one of the RC receiver signals to GPIO4. You may try to power the receiver from the ODrive's 5V supply if it doesn't draw too much power. Power up the the RC transmitter. You should now be able to control axis 0 from one of the RC sticks. +Be sure to setup the Failsafe feature on your RC Receiver so that if connection is lost between the remote and the receiver, the receiver outputs 0 for the velocity setpoint of both axes (or whatever is safest for your configuration). Also note that if the receiver turns off (loss of power, etc) or if the signal from the receiver to the ODrive is lost (wire comes unplugged, etc), the ODrive will continue the last commanded velocity setpoint. There is currently no timeout function in the ODrive for PWM inputs. + ## Ports Note: when you use an existing library you don't have to deal with the specifics described in this section. From 9b669ad623bb7e3d6b30d258cefb653d6f3b19da Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 14 Jan 2019 20:04:31 -0500 Subject: [PATCH 037/116] Fix Arduino 'c' command --- Arduino/ODriveArduino/ODriveArduino.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Arduino/ODriveArduino/ODriveArduino.cpp b/Arduino/ODriveArduino/ODriveArduino.cpp index 64af8b2f..2b2d6feb 100644 --- a/Arduino/ODriveArduino/ODriveArduino.cpp +++ b/Arduino/ODriveArduino/ODriveArduino.cpp @@ -39,7 +39,7 @@ void ODriveArduino::SetVelocity(int motor_number, float velocity, float current_ } void ODriveArduino::SetCurrent(int motor_number, float current) { - serial_ << "c" << motor_number << " " << current << "\n"; + serial_ << "c " << motor_number << " " << current << "\n"; } float ODriveArduino::readFloat() { From aedb65027b7e33a567993e1185f5dfac4e895937 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 14 Jan 2019 22:12:21 -0800 Subject: [PATCH 038/116] Update getting-started.md --- docs/getting-started.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/getting-started.md b/docs/getting-started.md index 3e2ebf43..39f86488 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -194,6 +194,7 @@ This is the resistance of the brake resistor. If you are not using it, you may s `odrv0.axis0.motor.config.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. _Note: this is not the same as the number of coils in the stator._ If you can't see them, try sliding a magnet around the rotor, and counting how many times it stops. This will be the number of **pole pairs**. If you use a magnetic piece of metal instead of a magnet, you will get the number of **magnet poles**. + `odrv0.axis0.motor.config.motor_type` This is the type of motor being used. Currently two types of motors are supported: High-current motors (`MOTOR_TYPE_HIGH_CURRENT`) and gimbal motors (`MOTOR_TYPE_GIMBAL`).
Which motor_type to choose?
From 5d7d6352886d05d3d4aeda6d2bece2baf0894472 Mon Sep 17 00:00:00 2001 From: Damien LaRocque Date: Tue, 15 Jan 2019 23:03:44 -0400 Subject: [PATCH 039/116] Change getting-started.md with trajectory control Trajectory Control commands weren't fully detailed. --- docs/getting-started.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 39f86488..1a346bd8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -261,6 +261,7 @@ You can also directly control the current of the motor, which is proportional to ### Trajectory control +Set `axis.controller.config.control_mode = CTRL_MODE_TRAJECTORY_CONTROL`.
This mode lets you smoothly accelerate, coast, and decelerate the axis from one position to another. With raw position control, the controller simply tries to go to the setpoint as quickly as possible. Using a trajectory lets you tune the feedback gains more aggressively to reject disturbance, while keeping smooth motion. ![Taptraj](TrapTrajPosVel.PNG)
@@ -295,7 +296,7 @@ Use the `move_to_pos` function to move to an absolute position: ### Circular position control -To enable Circular position control, set 'axis.controller.config.setpoints_in_cpr = True' +To enable Circular position control, set `axis.controller.config.setpoints_in_cpr = True` This mode is useful for continuos incremental position movement. For example a robot rolling indefinitely, or an extruder motor or conveyor belt moving with controlled increments indefinitely. In the regular position mode, the `pos_setpoint` would grow to a very large value and would lose precision due to floating point rounding. From 31562aca12b26d293be518bd9699cd774a2439f3 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 15 Jan 2019 19:44:07 -0800 Subject: [PATCH 040/116] add voltage magnitude clamping for gimbal motors in closed loop --- CHANGELOG.md | 1 + Firmware/MotorControl/controller.cpp | 7 ++++++- Firmware/MotorControl/motor.cpp | 1 - 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f535d2..57a6de10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors. * `q` command to ascii protocol. It is like the old `p` command, but velocity and current mean limits, not feed-forward. +* Voltage limit soft clamping instead of ERROR_MODULATION_MAGNITUDE in gimbal motor closed loop. # Releases ## [0.4.7] - 2018-11-28 diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 16aef47a..debca10d 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -183,8 +183,13 @@ bool Controller::update(float pos_estimate, float vel_estimate, float* current_s Iq += vel_integrator_current_; // Current limiting - float Ilim = std::min(axis_->motor_.config_.current_lim, axis_->motor_.current_control_.max_allowed_current); bool limited = false; + float Ilim = 0.0f; + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { + Ilim = std::min(axis_->motor_.config_.current_lim, 0.98f*one_by_sqrt3*vbus_voltage); + } else { + Ilim = std::min(axis_->motor_.config_.current_lim, axis_->motor_.current_control_.max_allowed_current); + } if (Iq > Ilim) { limited = true; Iq = Ilim; diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 7cee19b0..74ea20bc 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -282,7 +282,6 @@ bool Motor::enqueue_voltage_timings(float v_alpha, float v_beta) { return true; } -// TODO: This doesn't update brake current // We should probably make FOC Current call FOC Voltage to avoid duplication. bool Motor::FOC_voltage(float v_d, float v_q, float phase) { float c = our_arm_cos_f32(phase); From 077dd214b72666dab868a94e18093ea34baea5a7 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 15 Jan 2019 19:49:03 -0800 Subject: [PATCH 041/116] Update troubleshooting.md --- docs/troubleshooting.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index a441ef95..ef780745 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -74,6 +74,12 @@ In general, you need resistance_calib_max_voltage > calibration_current * phase_resistance`. ``` +* `ERROR_MODULATION_MAGNITUDE` + +The bus voltage was insufficent to push the requested current through the motor. Reduce `motor.config.calibration_current` and/or `motor.config.current_lim`, for errors at calibration-time and closed loop control respectively. + +For gimbal motors, it is recommended to set the calibration_current and current_lim to half your bus voltage, or less. + * `ERROR_DRV_FAULT = 0x0008` The ODrive v3.4 is known to have a hardware issue whereby the motors would stop operating From a43831e8d05230b2ada36a84aa5ce3c8e2f7d2e5 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 15 Jan 2019 19:50:32 -0800 Subject: [PATCH 042/116] Update troubleshooting.md --- docs/troubleshooting.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index ef780745..744d6eb3 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -74,12 +74,6 @@ In general, you need resistance_calib_max_voltage > calibration_current * phase_resistance`. ``` -* `ERROR_MODULATION_MAGNITUDE` - -The bus voltage was insufficent to push the requested current through the motor. Reduce `motor.config.calibration_current` and/or `motor.config.current_lim`, for errors at calibration-time and closed loop control respectively. - -For gimbal motors, it is recommended to set the calibration_current and current_lim to half your bus voltage, or less. - * `ERROR_DRV_FAULT = 0x0008` The ODrive v3.4 is known to have a hardware issue whereby the motors would stop operating @@ -91,6 +85,12 @@ power supply of the DRV8301 gate driver chips, thus tripping its under-voltage f To resolve this issue you can limit the M0 current to 40A. The lowest current at which the DRV fault was observed is 45A on one test motor and 50A on another test motor. Refer to [this post](https://discourse.odriverobotics.com/t/drv-fault-on-odrive-v3-4/558) for instructions for a hardware fix. +* `ERROR_MODULATION_MAGNITUDE = 0x0080` + +The bus voltage was insufficent to push the requested current through the motor. Reduce `motor.config.calibration_current` and/or `motor.config.current_lim`, for errors at calibration-time and closed loop control respectively. + +For gimbal motors, it is recommended to set the calibration_current and current_lim to half your bus voltage, or less. + ## Common Encoder Errors * `ERROR_CPR_OUT_OF_RANGE = 0x02` From 15983cc4f015719f962d9d90bef8fcb61327a9fe Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 16 Jan 2019 01:19:36 -0800 Subject: [PATCH 043/116] Thermal current limit with linear derating --- CHANGELOG.md | 1 + Firmware/MotorControl/axis.cpp | 6 ---- Firmware/MotorControl/axis.hpp | 2 -- Firmware/MotorControl/board_config_v3.h | 14 ++++----- Firmware/MotorControl/controller.cpp | 7 +---- Firmware/MotorControl/motor.cpp | 40 +++++++++++++++++++++++++ Firmware/MotorControl/motor.hpp | 13 +++++++- tools/odrive/utils.py | 2 +- 8 files changed, 62 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57a6de10..46feb061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Please add a note of your changes below this heading if you make a Pull Request. * `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors. * `q` command to ascii protocol. It is like the old `p` command, but velocity and current mean limits, not feed-forward. * Voltage limit soft clamping instead of ERROR_MODULATION_MAGNITUDE in gimbal motor closed loop. +* Thermal current limit with linear derating. # Releases ## [0.4.7] - 2018-11-28 diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index de631b2c..5b35d938 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -141,12 +141,6 @@ bool Axis::do_updates() { return check_for_errors(); } -float Axis::get_temp() { - float adc = adc_measurements_[hw_config_.thermistor_adc_ch]; - float normalized_voltage = adc / adc_full_scale; - return horner_fma(normalized_voltage, thermistor_poly_coeffs, thermistor_num_coeffs); -} - bool Axis::run_sensorless_spin_up() { // Early Spin-up: spiral up current float x = 0.0f; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index fe057569..a6b6bdae 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -86,7 +86,6 @@ public: bool check_PSU_brownout(); bool do_checks(); bool do_updates(); - float get_temp(); // True if there are no errors @@ -212,7 +211,6 @@ public: make_protocol_property("spin_up_acceleration", &config_.spin_up_acceleration), make_protocol_property("spin_up_target_vel", &config_.spin_up_target_vel) ), - make_protocol_function("get_temp", *this, &Axis::get_temp), make_protocol_object("motor", motor_.make_protocol_definitions()), make_protocol_object("controller", controller_.make_protocol_definitions()), make_protocol_object("encoder", encoder_.make_protocol_definitions()), diff --git a/Firmware/MotorControl/board_config_v3.h b/Firmware/MotorControl/board_config_v3.h index e865102d..58a8b3e2 100644 --- a/Firmware/MotorControl/board_config_v3.h +++ b/Firmware/MotorControl/board_config_v3.h @@ -23,7 +23,6 @@ typedef struct { uint16_t step_gpio_pin; uint16_t dir_gpio_pin; - size_t thermistor_adc_ch; osPriority thread_priority; } AxisHardwareConfig_t; @@ -42,6 +41,7 @@ typedef struct { TIM_HandleTypeDef* timer; uint16_t control_deadline; float shunt_conductance; + size_t inverter_thermistor_adc_ch; } MotorHardwareConfig_t; typedef struct { SPI_HandleTypeDef* spi; @@ -74,7 +74,6 @@ const BoardHardwareConfig_t hw_configs[2] = { { .axis_config = { .step_gpio_pin = 1, .dir_gpio_pin = 2, - .thermistor_adc_ch = 15, .thread_priority = (osPriority)(osPriorityHigh + (osPriority)1), }, .encoder_config = { @@ -92,6 +91,7 @@ const BoardHardwareConfig_t hw_configs[2] = { { .timer = &htim1, .control_deadline = TIM_1_8_PERIOD_CLOCKS, .shunt_conductance = 1.0f / SHUNT_RESISTANCE, //[S] + .inverter_thermistor_adc_ch = 15, }, .gate_driver_config = { .spi = &hspi3, @@ -112,11 +112,6 @@ const BoardHardwareConfig_t hw_configs[2] = { { #else .step_gpio_pin = 3, .dir_gpio_pin = 4, -#endif -#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 - .thermistor_adc_ch = 4, -#else - .thermistor_adc_ch = 1, #endif .thread_priority = osPriorityHigh, }, @@ -135,6 +130,11 @@ const BoardHardwareConfig_t hw_configs[2] = { { .timer = &htim8, .control_deadline = (3 * TIM_1_8_PERIOD_CLOCKS) / 2, .shunt_conductance = 1.0f / SHUNT_RESISTANCE, //[S] +#if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 + .inverter_thermistor_adc_ch = 4, +#else + .inverter_thermistor_adc_ch = 1, +#endif }, .gate_driver_config = { .spi = &hspi3, diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index debca10d..4e46c88b 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -184,12 +184,7 @@ bool Controller::update(float pos_estimate, float vel_estimate, float* current_s // Current limiting bool limited = false; - float Ilim = 0.0f; - if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { - Ilim = std::min(axis_->motor_.config_.current_lim, 0.98f*one_by_sqrt3*vbus_voltage); - } else { - Ilim = std::min(axis_->motor_.config_.current_lim, axis_->motor_.current_control_.max_allowed_current); - } + float Ilim = axis_->motor_.effective_current_lim(); if (Iq > Ilim) { limited = true; Iq = Ilim; diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 74ea20bc..82e53f39 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -143,14 +143,54 @@ void Motor::set_error(Motor::Error_t error){ update_brake_current(); } +float Motor::get_inverter_temp() { + float adc = adc_measurements_[hw_config_.inverter_thermistor_adc_ch]; + float normalized_voltage = adc / adc_full_scale; + return horner_fma(normalized_voltage, thermistor_poly_coeffs, thermistor_num_coeffs); +} + +bool Motor::update_thermal_limits() { + float fet_temp = get_inverter_temp(); + float temp_margin = config_.inverter_temp_limit_upper - fet_temp; + float derating_range = config_.inverter_temp_limit_upper - config_.inverter_temp_limit_lower; + thermal_current_lim_ = config_.current_lim * (temp_margin / derating_range); + if (!(thermal_current_lim_ >= 0.0f)) { //Funny polarity to also catch NaN + thermal_current_lim_ = 0.0f; + } + if (fet_temp > config_.inverter_temp_limit_upper + 5) { + set_error(ERROR_INVERTER_OVER_TEMP); + return false; + } + return true; +} + bool Motor::do_checks() { if (!check_DRV_fault()) { set_error(ERROR_DRV_FAULT); return false; } + if (!update_thermal_limits()) { + //error already set in function + return false; + } return true; } +float Motor::effective_current_lim() { + // Configured limit + float current_lim = config_.current_lim; + // Hardware limit + if (axis_->motor_.config_.motor_type == Motor::MOTOR_TYPE_GIMBAL) { + current_lim = std::min(current_lim, 0.98f*one_by_sqrt3*vbus_voltage); + } else { + current_lim = std::min(current_lim, axis_->motor_.current_control_.max_allowed_current); + } + // Thermal limit + current_lim = std::min(current_lim, thermal_current_lim_); + + return current_lim; +} + void Motor::log_timing(TimingLog_t log_idx) { static const uint16_t clocks_per_cnt = (uint16_t)((float)TIM_1_8_CLOCK_HZ / (float)TIM_APB1_CLOCK_HZ); uint16_t timing = clocks_per_cnt * htim13.Instance->CNT; // TODO: Use a hw_config diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index 2fe384e5..3f50f42c 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -21,7 +21,8 @@ public: ERROR_MODULATION_MAGNITUDE = 0x0080, ERROR_BRAKE_DEADTIME_VIOLATION = 0x0100, ERROR_UNEXPECTED_TIMER_CALLBACK = 0x0200, - ERROR_CURRENT_SENSE_SATURATION = 0x0400 + ERROR_CURRENT_SENSE_SATURATION = 0x0400, + ERROR_INVERTER_OVER_TEMP = 0x0800 }; enum MotorType_t { @@ -68,6 +69,8 @@ public: // Value used to compute shunt amplifier gains float requested_current_range = 60.0f; // [A] float current_control_bandwidth = 1000.0f; // [rad/s] + float inverter_temp_limit_lower = 100; + float inverter_temp_limit_upper = 120; }; enum TimingLog_t { @@ -106,6 +109,9 @@ public: bool check_DRV_fault(); void set_error(Error_t error); bool do_checks(); + float get_inverter_temp(); + bool update_thermal_limits(); + float effective_current_lim(); void log_timing(TimingLog_t log_idx); float phase_current_from_adcval(uint32_t ADCValue); bool measure_phase_resistance(float test_current, float max_voltage); @@ -159,6 +165,7 @@ public: }; DRV8301_FaultType_e drv_fault_ = DRV8301_FaultType_NoFault; DRV_SPI_8301_Vars_t gate_driver_regs_; //Local view of DRV registers (initialized by DRV8301_setup) + float thermal_current_lim_ = 10.0f; //[A] // Communication protocol definitions auto make_protocol_definitions() { @@ -171,6 +178,8 @@ public: make_protocol_property("DC_calib_phB", &DC_calib_.phB), make_protocol_property("DC_calib_phC", &DC_calib_.phC), make_protocol_property("phase_current_rev_gain", &phase_current_rev_gain_), + make_protocol_ro_property("thermal_current_lim", &thermal_current_lim_), + make_protocol_function("get_inverter_temp", *this, &Motor::get_inverter_temp), make_protocol_object("current_control", make_protocol_property("p_gain", ¤t_control_.p_gain), make_protocol_property("i_gain", ¤t_control_.i_gain), @@ -212,6 +221,8 @@ public: make_protocol_property("direction", &config_.direction), make_protocol_property("motor_type", &config_.motor_type), make_protocol_property("current_lim", &config_.current_lim), + make_protocol_property("inverter_temp_limit_lower", &config_.inverter_temp_limit_lower), + make_protocol_property("inverter_temp_limit_upper", &config_.inverter_temp_limit_upper), make_protocol_property("requested_current_range", &config_.requested_current_range), make_protocol_property("current_control_bandwidth", &config_.current_control_bandwidth, [](void* ctx) { static_cast(ctx)->update_current_controller_gains(); }, this) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index be8a5f4b..25a1cfb5 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -59,7 +59,7 @@ def dump_errors(odrv, clear=False): else: print(prefix + _VT100Colors['green'] + "no error" + _VT100Colors['default']) -data_rate = 100 +data_rate = 10 plot_rate = 10 num_samples = 1000 def start_liveplotter(get_var_callback): From d5614b73906edfbfe921204da54f36b4fd92951a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 16 Jan 2019 15:04:24 -0800 Subject: [PATCH 044/116] Update interfaces.md --- docs/interfaces.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interfaces.md b/docs/interfaces.md index 3c1cf39d..3f5a7dbd 100644 --- a/docs/interfaces.md +++ b/docs/interfaces.md @@ -33,6 +33,7 @@ The ODrive can be controlled over various ports and protocols. If you're comfort (*) ODrive v3.5 and later Notes: +* You must also connect GND between ODrive and your other board. * ODrive v3.3 and onward have 5V tolerant GPIO pins. * ODrive v3.5 and later have some noise supression filters on the default step/dir pins * You can change the step/dir pins using `axis.config._gpio_pin`. From aeb8d6abb89d8176c0fa933e5fee6af3dd56d0de Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 16 Jan 2019 15:05:02 -0800 Subject: [PATCH 045/116] Update ODriveArduinoTest.ino --- .../examples/ODriveArduinoTest/ODriveArduinoTest.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino b/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino index f948f7f6..1e835026 100644 --- a/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino +++ b/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino @@ -8,6 +8,7 @@ template<> inline Print& operator <<(Print &obj, float arg) { obj.print(a // Serial to the ODrive SoftwareSerial odrive_serial(8, 9); //RX (ODrive TX), TX (ODrive RX) +// Note: you must also connect GND on ODrive to GND on Arduino! // ODrive object ODriveArduino odrive(odrive_serial); From fcec31ab577c6da3aa84c66e530cb00f3313d1de Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 22 Jan 2019 19:43:12 -0800 Subject: [PATCH 046/116] add +CTRL_MODE_TRAJECTORY_CONTROL to enums.py --- tools/odrive/enums.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index 26e0f816..817e39e1 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -61,6 +61,7 @@ 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 ENCODER_MODE_INCREMENTAL = 0 ENCODER_MODE_HALL = 1 From eaaf4d14eab9d6541cf73d04bcba3a0a71075a57 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 24 Jan 2019 14:24:10 -0800 Subject: [PATCH 047/116] Update troubleshooting.md --- docs/troubleshooting.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 744d6eb3..369f9d39 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -71,7 +71,8 @@ Some motors will have a considerably different phase resistance and inductance t In general, you need ```text -resistance_calib_max_voltage > calibration_current * phase_resistance`. +resistance_calib_max_voltage > calibration_current * phase_resistance +resistance_calib_max_voltage < 0.5 * vbus_voltage ``` * `ERROR_DRV_FAULT = 0x0008` From 93286170927f37be0952cc1180d7d3f37766d407 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 24 Jan 2019 14:34:45 -0800 Subject: [PATCH 048/116] change inductance bounds --- Firmware/MotorControl/motor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 82e53f39..8f44fba9 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -276,7 +276,7 @@ bool Motor::measure_phase_inductance(float voltage_low, float voltage_high) { config_.phase_inductance = L; // TODO arbitrary values set for now - if (L < 1e-6f || L > 2500e-6f) + if (L < 4e-6f || L > 4000e-6f) return set_error(ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE), false; return true; } From 2f36e68b79ed914b4316cb2427e17df4e38c44bc Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Tue, 29 Jan 2019 22:25:51 -0500 Subject: [PATCH 049/116] double stack space for usb server thread --- Firmware/communication/interface_usb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/communication/interface_usb.cpp b/Firmware/communication/interface_usb.cpp index cd44c907..036a8203 100644 --- a/Firmware/communication/interface_usb.cpp +++ b/Firmware/communication/interface_usb.cpp @@ -177,6 +177,6 @@ void usb_rx_process_packet(uint8_t *buf, uint32_t len, uint8_t endpoint_pair) { void start_usb_server() { // Start USB communication thread - osThreadDef(usb_server_thread_def, usb_server_thread, osPriorityNormal, 0, 512); + osThreadDef(usb_server_thread_def, usb_server_thread, osPriorityNormal, 0, 1024); usb_thread = osThreadCreate(osThread(usb_server_thread_def), NULL); } From add0dd954cda6e93a7268df05a138f90df4b9156 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Tue, 29 Jan 2019 22:28:42 -0500 Subject: [PATCH 050/116] increase comms stack size again --- Firmware/communication/communication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index 625a3cf7..aaff052d 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -84,7 +84,7 @@ void init_communication(void) { printf("hi!\r\n"); // Start command handling thread - osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, 6000 /* in 32-bit words */); // TODO: fix stack issues + osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, 8000 /* in 32-bit words */); // TODO: fix stack issues comm_thread = osThreadCreate(osThread(task_cmd_parse), NULL); while (!endpoint_list_valid) From 301d0d807ee29e0886c21a958e8357dc55d3d373 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Tue, 29 Jan 2019 22:31:17 -0500 Subject: [PATCH 051/116] make stack overflows debuggable Now if you break on vApplicationStackOverflowHook in a debugger, you can see which thread overflowed. --- Firmware/MotorControl/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 4146850c..709381e4 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -97,7 +97,7 @@ void enter_dfu_mode() { extern "C" { int odrive_main(void); -void vApplicationStackOverflowHook(void) { +void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed portCHAR *pcTaskName) { for (;;); // TODO: safe action } void vApplicationIdleHook(void) { From 19f266c0fa10a52a48babd449cf92a208806e020 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 29 Jan 2019 19:34:28 -0800 Subject: [PATCH 052/116] update min inductance --- Firmware/MotorControl/motor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 8f44fba9..0a742e63 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -276,7 +276,7 @@ bool Motor::measure_phase_inductance(float voltage_low, float voltage_high) { config_.phase_inductance = L; // TODO arbitrary values set for now - if (L < 4e-6f || L > 4000e-6f) + if (L < 2e-6f || L > 4000e-6f) return set_error(ERROR_PHASE_INDUCTANCE_OUT_OF_RANGE), false; return true; } From 45582d046f369e3041ed1817f36bb15bcbbe4302 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 29 Jan 2019 19:37:56 -0800 Subject: [PATCH 053/116] Update tup PPA url in travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 99852c97..0337261e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ install: - export TUP_DIR=$HOME/dl/tup_0.7.5-0~16.04.york0_amd64 - export TUP_ARCHIVE=$HOME/dl/tup_0.7.5-0~16.04.york0_amd64.deb -- export TUP_URL=http://ppa.launchpad.net/jonathonf/tup/ubuntu/pool/main/t/tup/tup_0.7.5-0~16.04.york0_amd64.deb +- export TUP_URL=http://ppa.launchpad.net/jonathonf/tup/ubuntu/pool/main/t/tup/tup_0.7.8-2~16.04.york0_amd64.deb - if [ ! -e $TUP_DIR/bin/tup ]; then wget $TUP_URL -O $TUP_ARCHIVE; dpkg-deb -R $TUP_ARCHIVE $TUP_DIR; fi - export PATH=$PATH:$TUP_DIR/usr/bin From a169dd33f0f452b84e30af161330b75d1f66f36d Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 29 Jan 2019 23:17:27 -0800 Subject: [PATCH 054/116] add sincos encoder input (hacky) --- Firmware/MotorControl/encoder.cpp | 14 ++++++++++++++ Firmware/MotorControl/encoder.hpp | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 9b964f21..9b3acdec 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -277,6 +277,20 @@ bool Encoder::update() { } } } break; + + case MODE_SINCOS: { + float c = get_adc_voltage(GPIO_3_GPIO_Port, GPIO_3_Pin) / 3.3f; + float s = get_adc_voltage(GPIO_4_GPIO_Port, GPIO_4_Pin) / 3.3f; + + float phase = fast_atan2(s, c); + int fake_count = (int)(1000.0f * phase); + //CPR = 6283 = 2pi * 1k + + delta_enc = fake_count - count_in_cpr_; + delta_enc = mod(delta_enc, 6283); + if (delta_enc > 6283/2) + delta_enc -= 6283; + } break; default: { set_error(ERROR_UNSUPPORTED_ENCODER_MODE); diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 96e1a914..d8d279b1 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -19,7 +19,8 @@ public: enum Mode_t { MODE_INCREMENTAL, - MODE_HALL + MODE_HALL, + MODE_SINCOS }; struct Config_t { From 5392cb7ab3aea5d9501948e6d0a4fe98c84652d6 Mon Sep 17 00:00:00 2001 From: samuelsadok Date: Thu, 31 Jan 2019 01:12:02 +0100 Subject: [PATCH 055/116] fix tup build error --- Firmware/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/build.sh b/Firmware/build.sh index 1622fa69..8f3a0730 100755 --- a/Firmware/build.sh +++ b/Firmware/build.sh @@ -15,6 +15,7 @@ export CONFIG_STRICT=true rm -rdf build mkdir -p build env | grep ^CONFIG > tup.config +tup init tup generate ./tup_build.sh bash -xe ./tup_build.sh From 451e79519637fdcf33f220f7dae9a28b15e014ba Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Thu, 31 Jan 2019 20:41:46 +0100 Subject: [PATCH 056/116] Fix break -> brake --- docs/troubleshooting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 369f9d39..a357e1cc 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -49,9 +49,9 @@ You can monitor your PUS voltage using liveplotter in odrive tool by entering `s * `ERROR_DC_BUS_OVER_VOLTAGE = 0x04` -Confirm that you have a break resistor of the correct value connected securly and that `odrv0.config.brake_resistance` is set to the value of your break resistor. +Confirm that you have a brake resistor of the correct value connected securly and that `odrv0.config.brake_resistance` is set to the value of your brake resistor. -You can monitor your PUS voltage using liveplotter in odrive tool by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If during a move you see the voltage rise above your PSU's nominal set voltage then you have your break resistance set too low. This may happen if you are using long wires or small gauge wires to connect your break resistor to your odrive which will added extra resistance. This extra resistance needs to be accounted for to prevent this voltage spike. If you have checked all your connections you can also try increasing your break resistance by ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your break resistor value. +You can monitor your PUS voltage using liveplotter in odrive tool by entering `start_liveplotter(lambda: [odrv0.vbus_voltage])`. If during a move you see the voltage rise above your PSU's nominal set voltage then you have your brake resistance set too low. This may happen if you are using long wires or small gauge wires to connect your brake resistor to your odrive which will added extra resistance. This extra resistance needs to be accounted for to prevent this voltage spike. If you have checked all your connections you can also try increasing your brake resistance by ~ 0.01 Ohm at a time to a maximum of 0.05 greater than your brake resistor value. ## Common Motor Errors From c381f5f50e462101dd9d4be48e4ec4bb92b13265 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 31 Jan 2019 15:06:20 -0800 Subject: [PATCH 057/116] add save erase and reboot functions to ascii protocol --- Firmware/communication/ascii_protocol.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 5175ac49..380530d1 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -175,6 +175,13 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& respond(response_channel, use_checksum, "Firmware version: %d.%d.%d", FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_REVISION); respond(response_channel, use_checksum, "Serial number: %s", serial_number_str); + } else if (cmd[0] == 's'){ // Save config + save_configuration(); + } else if (cmd[0] == 'e'){ // Erase config + erase_configuration(); + } else if (cmd[0] == 'b'){ // Reboot + NVIC_SystemReset(); + } else if (cmd[0] == 'r') { // read property char name[MAX_LINE_LENGTH]; int numscan = sscanf(cmd, "r %" TO_STR(MAX_LINE_LENGTH) "s", name); From a86fb3e59b2df134b9ece10717ce100f40c9ba61 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 1 Feb 2019 21:01:45 -0800 Subject: [PATCH 058/116] add coherent sampling and 1.5 cycle pwm phase advance --- Firmware/MotorControl/axis.cpp | 9 ++++---- Firmware/MotorControl/encoder.cpp | 28 +++++++++++++++++++----- Firmware/MotorControl/encoder.hpp | 4 ++++ Firmware/MotorControl/low_level.cpp | 21 ++++++++++++++---- Firmware/MotorControl/motor.cpp | 34 +++++++++++++++++------------ Firmware/MotorControl/motor.hpp | 12 +++++++--- tools/odrive/utils.py | 1 + 7 files changed, 79 insertions(+), 30 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 5b35d938..8be43d15 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -148,7 +148,7 @@ bool Axis::run_sensorless_spin_up() { float phase = wrap_pm_pi(config_.ramp_up_distance * x); float I_mag = config_.spin_up_current * x; x += current_meas_period / config_.ramp_up_time; - if (!motor_.update(I_mag, phase)) + if (!motor_.update(I_mag, phase, 0.0f)) return error_ |= ERROR_MOTOR_FAILED, false; return x < 1.0f; }); @@ -162,7 +162,7 @@ bool Axis::run_sensorless_spin_up() { vel += config_.spin_up_acceleration * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); float I_mag = config_.spin_up_current; - if (!motor_.update(I_mag, phase)) + if (!motor_.update(I_mag, phase, vel)) return error_ |= ERROR_MOTOR_FAILED, false; return vel < config_.spin_up_target_vel; }); @@ -184,7 +184,7 @@ bool Axis::run_sensorless_control_loop() { float current_setpoint; if (!controller_.update(sensorless_estimator_.pll_pos_, sensorless_estimator_.vel_estimate_, ¤t_setpoint)) return error_ |= ERROR_CONTROLLER_FAILED, false; - if (!motor_.update(current_setpoint, sensorless_estimator_.phase_)) + if (!motor_.update(current_setpoint, sensorless_estimator_.phase_, sensorless_estimator_.vel_estimate_)) return false; // set_error should update axis.error_ return true; }); @@ -200,7 +200,8 @@ bool Axis::run_closed_loop_control_loop() { float current_setpoint; if (!controller_.update(encoder_.pos_estimate_, encoder_.vel_estimate_, ¤t_setpoint)) return error_ |= ERROR_CONTROLLER_FAILED, false; //TODO: Make controller.set_error - if (!motor_.update(current_setpoint, encoder_.phase_)) + float phase_vel = 2*M_PI * encoder_.vel_estimate_ / (float)encoder_.config_.cpr * motor_.config_.pole_pairs; + if (!motor_.update(current_setpoint, encoder_.phase_, phase_vel)) return false; // set_error should update axis.error_ return true; }); diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 9b3acdec..6a04dd4f 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -252,6 +252,27 @@ void Encoder::update_pll_gains() { } } +void Encoder::sample_now() { + switch (config_.mode) { + case MODE_INCREMENTAL: { + tim_cnt_sample_ = (int16_t)hw_config_.timer->Instance->CNT; + } break; + + case MODE_HALL: { + // do nothing: samples already captured in general GPIO capture + } break; + + case MODE_SINCOS: { + sincos_sample_s_ = get_adc_voltage(GPIO_3_GPIO_Port, GPIO_3_Pin) / 3.3f; + sincos_sample_c_ = get_adc_voltage(GPIO_4_GPIO_Port, GPIO_4_Pin) / 3.3f; + } break; + + default: { + set_error(ERROR_UNSUPPORTED_ENCODER_MODE); + } break; + } +} + bool Encoder::update() { // update internal encoder state. int32_t delta_enc = 0; @@ -259,7 +280,7 @@ bool Encoder::update() { case MODE_INCREMENTAL: { //TODO: use count_in_cpr_ instead as shadow_count_ can overflow //or use 64 bit - int16_t delta_enc_16 = (int16_t)hw_config_.timer->Instance->CNT - (int16_t)shadow_count_; + int16_t delta_enc_16 = (int16_t)tim_cnt_sample_ - (int16_t)shadow_count_; delta_enc = (int32_t)delta_enc_16; //sign extend } break; @@ -279,10 +300,7 @@ bool Encoder::update() { } break; case MODE_SINCOS: { - float c = get_adc_voltage(GPIO_3_GPIO_Port, GPIO_3_Pin) / 3.3f; - float s = get_adc_voltage(GPIO_4_GPIO_Port, GPIO_4_Pin) / 3.3f; - - float phase = fast_atan2(s, c); + float phase = fast_atan2(sincos_sample_s_, sincos_sample_c_); int fake_count = (int)(1000.0f * phase); //CPR = 6283 = 2pi * 1k diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index d8d279b1..1cda9800 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -57,6 +57,7 @@ public: bool run_index_search(); bool run_offset_calibration(); + void sample_now(); bool update(); void update_pll_gains(); @@ -78,8 +79,11 @@ public: float pll_kp_ = 0.0f; // [count/s / count] float pll_ki_ = 0.0f; // [(count/s^2) / count] + int16_t tim_cnt_sample_ = 0; // // Updated by low_level pwm_adc_cb uint8_t hall_state_ = 0x0; // bit[0] = HallA, .., bit[2] = HallC + float sincos_sample_s_ = 0.0f; + float sincos_sample_c_ = 0.0f; // Communication protocol definitions auto make_protocol_definitions() { diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index bf35c7a3..6125c99c 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -539,6 +539,7 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { axis.motor_.current_meas_.phC = current - axis.motor_.DC_calib_.phC; } // Prepare hall readings + // TODO move this to inside encoder update function decode_hall_samples(axis.encoder_, GPIO_port_samples[axis_num]); // Trigger axis thread axis.signal_current_meas(); @@ -553,18 +554,30 @@ void pwm_trig_adc_cb(ADC_HandleTypeDef* hadc, bool injected) { } void tim_update_cb(TIM_HandleTypeDef* htim) { - int portsamples_arr; + + // If the corresponding timer is counting up, we just sampled in SVM vector 0, i.e. real current + // If we are counting down, we just sampled in SVM vector 7, with zero current + bool counting_down = htim->Instance->CR1 & TIM_CR1_DIR; + if (counting_down) + return; + + int sample_ch; + Axis* axis; if (htim == &htim1) { - portsamples_arr = 0; + sample_ch = 0; + axis = axes[0]; } else if (htim == &htim8) { - portsamples_arr = 1; + sample_ch = 1; + axis = axes[1]; } else { low_level_fault(Motor::ERROR_UNEXPECTED_TIMER_CALLBACK); return; } + axis->encoder_.sample_now(); + for (int i = 0; i < num_GPIO; ++i) { - GPIO_port_samples[portsamples_arr][i] = GPIOs_to_samp[i]->IDR; + GPIO_port_samples[sample_ch][i] = GPIOs_to_samp[i]->IDR; } } diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 0a742e63..6fa01cc8 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -323,15 +323,15 @@ bool Motor::enqueue_voltage_timings(float v_alpha, float v_beta) { } // We should probably make FOC Current call FOC Voltage to avoid duplication. -bool Motor::FOC_voltage(float v_d, float v_q, float phase) { - float c = our_arm_cos_f32(phase); - float s = our_arm_sin_f32(phase); +bool Motor::FOC_voltage(float v_d, float v_q, float pwm_phase) { + float c = our_arm_cos_f32(pwm_phase); + float s = our_arm_sin_f32(pwm_phase); float v_alpha = c*v_d - s*v_q; float v_beta = c*v_q + s*v_d; return enqueue_voltage_timings(v_alpha, v_beta); } -bool Motor::FOC_current(float Id_des, float Iq_des, float phase) { +bool Motor::FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase) { // Syntactic sugar CurrentControl_t& ictrl = current_control_; @@ -349,11 +349,12 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) { float Ibeta = one_by_sqrt3 * (current_meas_.phB - current_meas_.phC); // Park transform - float c = our_arm_cos_f32(phase); - float s = our_arm_sin_f32(phase); - float Id = c * Ialpha + s * Ibeta; - float Iq = c * Ibeta - s * Ialpha; - ictrl.Iq_measured = Iq; + float c_I = our_arm_cos_f32(I_phase); + float s_I = our_arm_sin_f32(I_phase); + float Id = c_I * Ialpha + s_I * Ibeta; + float Iq = c_I * Ibeta - s_I * Ialpha; + ictrl.Iq_measured += ictrl.I_measured_report_filter_k * (Iq - ictrl.Iq_measured); + ictrl.Id_measured += ictrl.I_measured_report_filter_k * (Id - ictrl.Id_measured); // Current error float Ierr_d = Id_des - Id; @@ -387,8 +388,10 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) { 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; + float c_p = our_arm_cos_f32(pwm_phase); + float s_p = our_arm_sin_f32(pwm_phase); + float mod_alpha = c_p * mod_d - s_p * mod_q; + float mod_beta = c_p * mod_q + s_p * mod_d; // Report final applied voltage in stationary frame (for sensorles estimator) ictrl.final_v_alpha = mod_to_V * mod_alpha; @@ -403,19 +406,22 @@ bool Motor::FOC_current(float Id_des, float Iq_des, float phase) { } -bool Motor::update(float current_setpoint, float phase) { +bool Motor::update(float current_setpoint, float phase, float phase_vel) { current_setpoint *= config_.direction; phase *= config_.direction; + phase_vel *= config_.direction; + + float pwm_phase = phase + 1.5f * current_meas_period * phase_vel; // Execute current command // TODO: move this into the mot if (config_.motor_type == MOTOR_TYPE_HIGH_CURRENT) { - if(!FOC_current(0.0f, current_setpoint, phase)){ + if(!FOC_current(0.0f, current_setpoint, phase, pwm_phase)){ return false; } } else if (config_.motor_type == MOTOR_TYPE_GIMBAL) { //In gimbal motor mode, current is reinterptreted as voltage. - if(!FOC_voltage(0.0f, current_setpoint, phase)) + if(!FOC_voltage(0.0f, current_setpoint, pwm_phase)) return false; } else { set_error(ERROR_NOT_IMPLEMENTED_MOTOR_TYPE); diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index 3f50f42c..9742835e 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -47,6 +47,8 @@ public: float final_v_beta; // [V] float Iq_setpoint; // [A] float Iq_measured; // [A] + float Id_measured; // [A] + float I_measured_report_filter_k; float max_allowed_current; // [A] float overcurrent_trip_level; // [A] }; @@ -119,9 +121,9 @@ public: bool run_calibration(); bool enqueue_modulation_timings(float mod_alpha, float mod_beta); bool enqueue_voltage_timings(float v_alpha, float v_beta); - bool FOC_voltage(float v_d, float v_q, float phase); - bool FOC_current(float Id_des, float Iq_des, float phase); - bool update(float current_setpoint, float phase); + bool FOC_voltage(float v_d, float v_q, float pwm_phase); + bool FOC_current(float Id_des, float Iq_des, float I_phase, float pwm_phase); + bool update(float current_setpoint, float phase, float phase_vel); const MotorHardwareConfig_t& hw_config_; const GateDriverHardwareConfig_t gate_driver_config_; @@ -160,6 +162,8 @@ public: .final_v_beta = 0.0f, .Iq_setpoint = 0.0f, .Iq_measured = 0.0f, + .Id_measured = 0.0f, + .I_measured_report_filter_k = 1.0f, .max_allowed_current = 0.0f, .overcurrent_trip_level = 0.0f, }; @@ -190,6 +194,8 @@ public: make_protocol_property("final_v_beta", ¤t_control_.final_v_beta), make_protocol_property("Iq_setpoint", ¤t_control_.Iq_setpoint), make_protocol_property("Iq_measured", ¤t_control_.Iq_measured), + make_protocol_property("Id_measured", ¤t_control_.Id_measured), + make_protocol_property("I_measured_report_filter_k", ¤t_control_.I_measured_report_filter_k), make_protocol_ro_property("max_allowed_current", ¤t_control_.max_allowed_current), make_protocol_ro_property("overcurrent_trip_level", ¤t_control_.overcurrent_trip_level) ), diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 25a1cfb5..28d1d737 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -106,6 +106,7 @@ def start_liveplotter(get_var_callback): while not cancellation_token.is_set(): plt.clf() plt.plot(vals) + plt.legend(list(range(len(vals)))) fig.canvas.draw() fig.canvas.start_event_loop(1/plot_rate) From 8ad4ad92de25815a2f1389889d2719fc920ef7d9 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 5 Feb 2019 21:40:40 -0800 Subject: [PATCH 059/116] center sincos on 50pct --- Firmware/MotorControl/encoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 6a04dd4f..633778fa 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -263,8 +263,8 @@ void Encoder::sample_now() { } break; case MODE_SINCOS: { - sincos_sample_s_ = get_adc_voltage(GPIO_3_GPIO_Port, GPIO_3_Pin) / 3.3f; - sincos_sample_c_ = get_adc_voltage(GPIO_4_GPIO_Port, GPIO_4_Pin) / 3.3f; + sincos_sample_s_ = (get_adc_voltage(GPIO_3_GPIO_Port, GPIO_3_Pin) / 3.3f) - 0.5f; + sincos_sample_c_ = (get_adc_voltage(GPIO_4_GPIO_Port, GPIO_4_Pin) / 3.3f) - 0.5f; } break; default: { From b64b3b56252477d84d24e7f915c6ba62cf29c5db Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 8 Feb 2019 19:25:19 -0800 Subject: [PATCH 060/116] Update Gemfile.lock --- docs/Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 1c6d972e..860837ad 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -70,7 +70,7 @@ GEM listen (= 3.1.5) mercenary (~> 0.3) minima (= 2.4.0) - nokogiri (>= 1.8.1, < 2.0) + nokogiri (>= 1.8.5, < 2.0) rouge (= 2.2.1) terminal-table (~> 1.4) github-pages-health-check (1.4.0) @@ -81,7 +81,7 @@ GEM typhoeus (~> 1.3) html-pipeline (2.7.1) activesupport (>= 2) - nokogiri (>= 1.4) + nokogiri (>= 1.8.5) http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) @@ -207,7 +207,7 @@ GEM minitest (5.11.3) multipart-post (2.0.0) net-dns (0.8.0) - nokogiri (1.8.2) + nokogiri (>= 1.8.5) mini_portile2 (~> 2.3.0) octokit (4.8.0) sawyer (~> 0.8.0, >= 0.5.3) From 1e4b71b67bb2ba8ba0bd10135dd2db08f2d07def Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 9 Feb 2019 21:48:30 -0800 Subject: [PATCH 061/116] add ascii command f for feedback --- Firmware/communication/ascii_protocol.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 380530d1..3ac68499 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -155,6 +155,19 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& axes[motor_number]->controller_.move_to_pos(goal_point); } + } else if (cmd[0] == 'f') { // feedback + unsigned motor_number; + int numscan = sscanf(cmd, "f %u", &motor_number); + if (numscan < 1) { + 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 { + respond(response_channel, use_checksum, "%f %f", + (double)axes[motor_number]->encoder_.pos_estimate_, + (double)axes[motor_number]->encoder_.vel_estimate_); + } + } else if (cmd[0] == 'h') { // Help respond(response_channel, use_checksum, "Please see documentation for more details"); respond(response_channel, use_checksum, ""); From 4b998a8b64965bbf0968a3e2b75f156cdca6bb43 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 10 Feb 2019 20:12:32 -0800 Subject: [PATCH 062/116] add vel argument to constant speed spoolup --- Firmware/MotorControl/axis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 68edf0fb..baa6c2c6 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -191,7 +191,7 @@ bool Axis::run_lockin_spin() { distance += vel * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); - if (!motor_.update(config_.lockin_current, phase)) + if (!motor_.update(config_.lockin_current, phase, vel)) return false; return !spin_done(); }); From 7bb8b44fbcb04f3433d81e4b1bedc0ee6b55664d Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 10 Feb 2019 20:16:00 -0800 Subject: [PATCH 063/116] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46feb061..68d2d159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Please add a note of your changes below this heading if you make a Pull Request. * Voltage limit soft clamping instead of ERROR_MODULATION_MAGNITUDE in gimbal motor closed loop. * Thermal current limit with linear derating. +### Fixed +* Added required 1.5 cycle phase shift between ADC and PWM, lack thereof caused unstable current controller at high eRPM. + # Releases ## [0.4.7] - 2018-11-28 ### Added From bade43dc9cd3aa7cbd8f50d5db91f88465e52abf Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 10 Feb 2019 22:05:33 -0800 Subject: [PATCH 064/116] remove old overspeed check from encoder --- Firmware/MotorControl/encoder.cpp | 9 --------- Firmware/MotorControl/encoder.hpp | 3 --- 2 files changed, 12 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 7d0b8714..1425bf7d 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -309,15 +309,6 @@ bool Encoder::update() { snap_to_zero_vel = true; } - // Check overspeed fault - if (config_.overspeed_fault_ratio != 0.0f) { // 0.0f = disabled - // TODO: Use separate encoder, motor, controller vel_lim: take min - if (fabsf(vel_estimate_) > config_.overspeed_fault_ratio * axis_->controller_.config_.vel_limit) { - set_error(ERROR_OVERSPEED); - return false; - } - } - //// run encoder count interpolation int32_t corrected_enc = count_in_cpr_ - config_.offset; // if we are stopped, make sure we don't randomly drift diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index d01d4ed3..e25e6003 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -15,7 +15,6 @@ public: ERROR_UNSUPPORTED_ENCODER_MODE = 0x08, ERROR_ILLEGAL_HALL_STATE = 0x10, ERROR_INDEX_NOT_FOUND_YET = 0x20, - ERROR_OVERSPEED = 0x40, }; enum Mode_t { @@ -39,7 +38,6 @@ public: float offset_float = 0.0f; // Sub-count phase alignment offset float calib_range = 0.02f; float bandwidth = 1000.0f; - float overspeed_fault_ratio = 1.2f; // ratio of vel_lim, 0.0f = disabled bool find_idx_on_lockin = false; bool idx_search_unidirectional = false; bool ignore_illegal_hall_state = false; @@ -108,7 +106,6 @@ public: make_protocol_property("mode", &config_.mode), make_protocol_property("use_index", &config_.use_index), make_protocol_property("pre_calibrated", &config_.pre_calibrated), - make_protocol_property("overspeed_fault_ratio", &config_.overspeed_fault_ratio), make_protocol_property("idx_search_speed", &config_.idx_search_speed), make_protocol_property("zero_count_on_find_idx", &config_.zero_count_on_find_idx), make_protocol_property("cpr", &config_.cpr), From a735adb95ceb96b0cb3b89f539527ebd9f06eacd Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Feb 2019 15:13:30 -0800 Subject: [PATCH 065/116] rename var and add comments --- Firmware/MotorControl/encoder.cpp | 2 +- Firmware/MotorControl/encoder.hpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 1425bf7d..40be59e4 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -43,7 +43,7 @@ bool Encoder::do_checks(){ // TODO: disable interrupt once we found the index void Encoder::enc_index_cb() { if (config_.use_index && !index_found_) { - if (config_.find_idx_on_lockin && axis_->lockin_state_ != Axis::LOCKIN_STATE_CONST_VEL) + if (config_.find_idx_on_lockin_only && axis_->lockin_state_ != Axis::LOCKIN_STATE_CONST_VEL) return; set_circular_count(0, false); if (config_.zero_count_on_find_idx) diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index e25e6003..fa1ddeb3 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -36,11 +36,11 @@ public: int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, int32_t offset = 0; // Offset between encoder count and rotor electrical phase float offset_float = 0.0f; // Sub-count phase alignment offset - float calib_range = 0.02f; + float calib_range = 0.02f; // Accuracy required to pass encoder cpr check float bandwidth = 1000.0f; - bool find_idx_on_lockin = false; - bool idx_search_unidirectional = false; - bool ignore_illegal_hall_state = false; + bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state + bool idx_search_unidirectional = false; // Only allow index search in known direction + bool ignore_illegal_hall_state = false; // dont error on bad states like 000 or 111 }; Encoder(const EncoderHardwareConfig_t& hw_config, @@ -114,7 +114,7 @@ public: make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), make_protocol_property("calib_range", &config_.calib_range), - make_protocol_property("find_idx_on_lockin", &config_.find_idx_on_lockin), + make_protocol_property("find_idx_on_lockin_only", &config_.find_idx_on_lockin_only), make_protocol_property("idx_search_unidirectional", &config_.idx_search_unidirectional), make_protocol_property("ignore_illegal_hall_state", &config_.ignore_illegal_hall_state) ) From f17c96360fd547743e6546396c0ac8940ac676fe Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Feb 2019 17:32:48 -0800 Subject: [PATCH 066/116] push index and dir search into encoder object --- Firmware/MotorControl/axis.cpp | 60 +++++++++---------------------- Firmware/MotorControl/axis.hpp | 45 ++++++++++++----------- Firmware/MotorControl/encoder.cpp | 38 ++++++++++++++++++++ Firmware/MotorControl/encoder.hpp | 1 + 4 files changed, 81 insertions(+), 63 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index baa6c2c6..25034e22 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -146,27 +146,27 @@ bool Axis::run_lockin_spin() { lockin_state_ = LOCKIN_STATE_RAMP; float x = 0.0f; run_control_loop([&]() { - float phase = wrap_pm_pi(config_.lockin_ramp_distance * x); - float I_mag = config_.lockin_current * x; - x += current_meas_period / config_.lockin_ramp_time; + float phase = wrap_pm_pi(config_.lockin.ramp_distance * x); + float I_mag = config_.lockin.current * x; + x += current_meas_period / config_.lockin.ramp_time; if (!motor_.update(I_mag, phase, 0.0f)) return false; return x < 1.0f; }); // Spin states - float distance = config_.lockin_ramp_distance; + float distance = config_.lockin.ramp_distance; float phase = wrap_pm_pi(distance); - float vel = distance / config_.lockin_ramp_time; + float vel = distance / config_.lockin.ramp_time; // Function of states to check if we are done auto spin_done = [&](bool vel_override = false) -> bool { bool done = false; - if (config_.lockin_finish_on_vel || vel_override) - done = done || fabsf(vel) >= fabsf(config_.lockin_vel); - if (config_.lockin_finish_on_distance) - done = done || fabsf(distance) >= fabsf(config_.lockin_finish_distance); - if (config_.lockin_finish_on_enc_idx) + if (config_.lockin.finish_on_vel || vel_override) + done = done || fabsf(vel) >= fabsf(config_.lockin.vel); + if (config_.lockin.finish_on_distance) + done = done || fabsf(distance) >= fabsf(config_.lockin.finish_distance); + if (config_.lockin.finish_on_enc_idx) done = done || encoder_.index_found_; return done; }; @@ -174,11 +174,11 @@ bool Axis::run_lockin_spin() { // Accelerate lockin_state_ = LOCKIN_STATE_ACCELERATE; run_control_loop([&]() { - vel += config_.lockin_accel * current_meas_period; + vel += config_.lockin.accel * current_meas_period; distance += vel * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); - if (!motor_.update(config_.lockin_current, phase, vel)) + if (!motor_.update(config_.lockin.current, phase, vel)) return false; return !spin_done(true); //vel_override to go to next phase }); @@ -186,12 +186,12 @@ bool Axis::run_lockin_spin() { // Constant speed if (!spin_done()) { lockin_state_ = LOCKIN_STATE_CONST_VEL; - vel = config_.lockin_vel; // reset to actual specified vel to avoid small integration error + vel = config_.lockin.vel; // reset to actual specified vel to avoid small integration error run_control_loop([&]() { distance += vel * current_meas_period; phase = wrap_pm_pi(phase + vel * current_meas_period); - if (!motor_.update(config_.lockin_current, phase, vel)) + if (!motor_.update(config_.lockin.current, phase, vel)) return false; return !spin_done(); }); @@ -311,40 +311,14 @@ void Axis::run_state_machine_loop() { if (encoder_.config_.idx_search_unidirectional && motor_.config_.direction==0) goto invalid_state_label; - // TODO: move code body to function in Encoder - encoder_.config_.use_index = true; - encoder_.index_found_ = false; - - bool orig_setting = config_.lockin_finish_on_enc_idx; - config_.lockin_finish_on_enc_idx = true; - status = run_lockin_spin(); - config_.lockin_finish_on_enc_idx = orig_setting; + status = encoder_.run_index_search(); } break; case AXIS_STATE_ENCODER_DIR_FIND: { if (!motor_.is_calibrated_) goto invalid_state_label; - // TODO: move code body to function in Encoder - int32_t init_enc_val = encoder_.shadow_count_; - bool orig_setting = config_.lockin_finish_on_distance; - config_.lockin_finish_on_distance = true; - motor_.config_.direction = 1; // Must test spin forwards for direction detect logic - status = run_lockin_spin(); - config_.lockin_finish_on_distance = orig_setting; - - if (status) { - // Check response and direction - if (encoder_.shadow_count_ > init_enc_val + 8) { - // motor same dir as encoder - motor_.config_.direction = 1; - } else if (encoder_.shadow_count_ < init_enc_val - 8) { - // motor opposite dir as encoder - motor_.config_.direction = -1; - } else { - motor_.config_.direction = 0; - } - } + status = encoder_.run_direction_find(); } break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: { @@ -366,7 +340,7 @@ void Axis::run_state_machine_loop() { if (status) { // call to controller.reset() that happend when arming means that vel_setpoint // is zeroed. So we make the setpoint the spinup target for smooth transition. - controller_.vel_setpoint_ = config_.lockin_vel; + controller_.vel_setpoint_ = config_.lockin.vel; status = run_sensorless_control_loop(); } } break; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index ac1ffb3b..6904e79d 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -36,6 +36,18 @@ public: AXIS_STATE_ENCODER_DIR_FIND = 10, }; + struct LockinConfig_t { + float current = 10.0f; // [A] + float ramp_time = 0.4f; // [s] + float ramp_distance = 1 * M_PI; // [rad] + float accel = 10.0f; // [rad/s^2] + float vel = 100.0f; // [rad/s] + float finish_distance = 1000.0f; // [rad] + bool finish_on_vel = false; + bool finish_on_distance = false; + bool finish_on_enc_idx = false; + }; + struct Config_t { bool startup_motor_calibration = false; //(ctx)->decode_step_dir_pins(); }, this), make_protocol_property("dir_gpio_pin", &config_.dir_gpio_pin, - [](void* ctx) { static_cast(ctx)->decode_step_dir_pins(); }, this) + [](void* ctx) { static_cast(ctx)->decode_step_dir_pins(); }, this), + make_protocol_object("lockin", + make_protocol_property("current", &config_.lockin.current), + make_protocol_property("ramp_time", &config_.lockin.ramp_time), + make_protocol_property("ramp_distance", &config_.lockin.ramp_distance), + make_protocol_property("accel", &config_.lockin.accel), + make_protocol_property("vel", &config_.lockin.vel), + make_protocol_property("finish_distance", &config_.lockin.finish_distance), + make_protocol_property("finish_on_vel", &config_.lockin.finish_on_vel), + make_protocol_property("finish_on_distance", &config_.lockin.finish_on_distance), + make_protocol_property("finish_on_enc_idx", &config_.lockin.finish_on_enc_idx) + ) ), make_protocol_object("motor", motor_.make_protocol_definitions()), make_protocol_object("controller", controller_.make_protocol_definitions()), diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 40be59e4..64ad4042 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -92,6 +92,44 @@ void Encoder::set_circular_count(int32_t count, bool update_offset) { cpu_exit_critical(prim); } +bool Encoder::run_index_search() { + config_.use_index = true; + index_found_ = false; + if (!config_.idx_search_unidirectional && axis_->motor_.config_.direction == 0) { + axis_->motor_.config_.direction = 1; + } + + bool orig_finish_on_enc_idx = axis_->config_.lockin.finish_on_enc_idx; + axis_->config_.lockin.finish_on_enc_idx = true; + bool status = axis_->run_lockin_spin(); + axis_->config_.lockin.finish_on_enc_idx = orig_finish_on_enc_idx; + return status; +} + +bool Encoder::run_direction_find() { + int32_t init_enc_val = shadow_count_; + bool orig_finish_on_distance = axis_->config_.lockin.finish_on_distance; + axis_->config_.lockin.finish_on_distance = true; + axis_->motor_.config_.direction = 1; // Must test spin forwards for direction detect logic + bool status = axis_->run_lockin_spin(); + axis_->config_.lockin.finish_on_distance = orig_finish_on_distance; + + if (status) { + // Check response and direction + if (shadow_count_ > init_enc_val + 8) { + // motor same dir as encoder + axis_->motor_.config_.direction = 1; + } else if (shadow_count_ < init_enc_val - 8) { + // motor opposite dir as encoder + axis_->motor_.config_.direction = -1; + } else { + axis_->motor_.config_.direction = 0; + } + } + + return status; +} + // @brief Turns the motor in one direction for a bit and then in the other // direction in order to find the offset between the electrical phase 0 // and the encoder state 0. diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index fa1ddeb3..10f6a38f 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -57,6 +57,7 @@ public: bool calib_enc_offset(float voltage_magnitude); bool run_index_search(); + bool run_direction_find(); bool run_offset_calibration(); void sample_now(); bool update(); From c0bc2b97d5eef165fa3c8a86c978e1fb6685f0d6 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Feb 2019 17:35:52 -0800 Subject: [PATCH 067/116] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46feb061..95501471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Please add a note of your changes below this heading if you make a Pull Request. * Voltage limit soft clamping instead of ERROR_MODULATION_MAGNITUDE in gimbal motor closed loop. * Thermal current limit with linear derating. +### Changed +* Unified lockin drive modes. Current for index searching and encoder offset calibration now moved to axis.lockin.current. + # Releases ## [0.4.7] - 2018-11-28 ### Added From 81d9b338630b1699f2b9fc73cde4598e6f488fd3 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Feb 2019 17:54:56 -0800 Subject: [PATCH 068/116] add option to disable phase interpolation --- Firmware/MotorControl/encoder.cpp | 2 +- Firmware/MotorControl/encoder.hpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 6a04dd4f..294c3e0e 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -342,7 +342,7 @@ bool Encoder::update() { //// run encoder count interpolation int32_t corrected_enc = count_in_cpr_ - config_.offset; // if we are stopped, make sure we don't randomly drift - if (snap_to_zero_vel) { + if (snap_to_zero_vel || !config_.enable_phase_interpolation) { interpolation_ = 0.5f; // reset interpolation if encoder edge comes } else if (delta_enc > 0) { diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 1cda9800..2d2fc749 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -36,7 +36,8 @@ public: int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, int32_t offset = 0; // Offset between encoder count and rotor electrical phase float offset_float = 0.0f; // Sub-count phase alignment offset - float calib_range = 0.02f; + bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state + float calib_range = 0.02f; // Accuracy required to pass encoder cpr check float bandwidth = 1000.0f; bool ignore_illegal_hall_state = false; }; @@ -110,6 +111,7 @@ public: make_protocol_property("cpr", &config_.cpr), make_protocol_property("offset", &config_.offset), make_protocol_property("offset_float", &config_.offset_float), + make_protocol_property("enable_phase_interpolation", &config_.enable_phase_interpolation), make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), make_protocol_property("calib_range", &config_.calib_range), From c4c5aaac57d93402d341be0c0406aea29ea2a774 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Feb 2019 18:33:45 -0800 Subject: [PATCH 069/116] sincos is autoready --- Firmware/MotorControl/encoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 633778fa..d79e33de 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -9,7 +9,7 @@ Encoder::Encoder(const EncoderHardwareConfig_t& hw_config, { update_pll_gains(); - if (config.pre_calibrated && (config.mode == Encoder::MODE_HALL)) { + if (config.pre_calibrated && (config.mode == Encoder::MODE_HALL || config.mode == Encoder::MODE_SINCOS)) { is_ready_ = true; } } From e3785a2aa24a9079da460858fd02498747671987 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Feb 2019 18:42:22 -0800 Subject: [PATCH 070/116] make index only active when required --- Firmware/MotorControl/axis.cpp | 3 +++ Firmware/MotorControl/axis.hpp | 6 +++--- Firmware/MotorControl/encoder.cpp | 19 +++++++++++++++---- Firmware/MotorControl/encoder.hpp | 9 +++++---- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 25034e22..0ba614c3 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -183,6 +183,9 @@ bool Axis::run_lockin_spin() { return !spin_done(true); //vel_override to go to next phase }); + if (!encoder_.index_found_) + encoder_.set_idx_subscribe(true); + // Constant speed if (!spin_done()) { lockin_state_ = LOCKIN_STATE_CONST_VEL; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 6904e79d..dd8e88ef 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -40,9 +40,9 @@ public: float current = 10.0f; // [A] float ramp_time = 0.4f; // [s] float ramp_distance = 1 * M_PI; // [rad] - float accel = 10.0f; // [rad/s^2] - float vel = 100.0f; // [rad/s] - float finish_distance = 1000.0f; // [rad] + float accel = 20.0f; // [rad/s^2] + float vel = 40.0f; // [rad/s] + float finish_distance = 100.0f; // [rad] bool finish_on_vel = false; bool finish_on_distance = false; bool finish_on_enc_idx = false; diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 64ad4042..54a16a41 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -20,8 +20,7 @@ static void enc_index_cb_wrapper(void* ctx) { void Encoder::setup() { HAL_TIM_Encoder_Start(hw_config_.timer, TIM_CHANNEL_ALL); - GPIO_subscribe(hw_config_.index_port, hw_config_.index_pin, GPIO_NOPULL, - enc_index_cb_wrapper, this); + set_idx_subscribe(); } void Encoder::set_error(Error_t error) { @@ -43,8 +42,6 @@ bool Encoder::do_checks(){ // TODO: disable interrupt once we found the index void Encoder::enc_index_cb() { if (config_.use_index && !index_found_) { - if (config_.find_idx_on_lockin_only && axis_->lockin_state_ != Axis::LOCKIN_STATE_CONST_VEL) - return; set_circular_count(0, false); if (config_.zero_count_on_find_idx) set_linear_count(0); // Avoid position control transient after search @@ -58,6 +55,20 @@ void Encoder::enc_index_cb() { } index_found_ = true; } + + // Disable interrupt + GPIO_unsubscribe(hw_config_.index_port, hw_config_.index_pin); +} + +void Encoder::set_idx_subscribe(bool override_enable) { + if (override_enable || (config_.use_index && !config_.find_idx_on_lockin_only)) { + GPIO_subscribe(hw_config_.index_port, hw_config_.index_pin, GPIO_PULLDOWN, + enc_index_cb_wrapper, this); + } + + if (!config_.use_index || config_.find_idx_on_lockin_only) { + GPIO_unsubscribe(hw_config_.index_port, hw_config_.index_pin); + } } // Function that sets the current encoder count to a desired 32-bit value. diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index 10f6a38f..a18b4fa4 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -31,7 +31,6 @@ public: // be determined by run_offset_calibration. // In this case the encoder will enter ready // state as soon as the index is found. - float idx_search_speed = 10.0f; // [rad/s electrical] bool zero_count_on_find_idx = true; int32_t cpr = (2048 * 4); // Default resolution of CUI-AMT102 encoder, int32_t offset = 0; // Offset between encoder count and rotor electrical phase @@ -51,6 +50,7 @@ public: bool do_checks(); void enc_index_cb(); + void set_idx_subscribe(bool override_enable = false); void set_linear_count(int32_t count); void set_circular_count(int32_t count, bool update_offset); @@ -105,9 +105,11 @@ public: // make_protocol_property("pll_ki", &pll_ki_), make_protocol_object("config", make_protocol_property("mode", &config_.mode), - make_protocol_property("use_index", &config_.use_index), + make_protocol_property("use_index", &config_.use_index, + [](void* ctx) { static_cast(ctx)->set_idx_subscribe(); }, this), + make_protocol_property("find_idx_on_lockin_only", &config_.find_idx_on_lockin_only, + [](void* ctx) { static_cast(ctx)->set_idx_subscribe(); }, this), make_protocol_property("pre_calibrated", &config_.pre_calibrated), - make_protocol_property("idx_search_speed", &config_.idx_search_speed), make_protocol_property("zero_count_on_find_idx", &config_.zero_count_on_find_idx), make_protocol_property("cpr", &config_.cpr), make_protocol_property("offset", &config_.offset), @@ -115,7 +117,6 @@ public: make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), make_protocol_property("calib_range", &config_.calib_range), - make_protocol_property("find_idx_on_lockin_only", &config_.find_idx_on_lockin_only), make_protocol_property("idx_search_unidirectional", &config_.idx_search_unidirectional), make_protocol_property("ignore_illegal_hall_state", &config_.ignore_illegal_hall_state) ) From 40c16ef49499b421a108b8d994ebae83c35accbe Mon Sep 17 00:00:00 2001 From: csann <1936653+csann@users.noreply.github.com> Date: Fri, 22 Feb 2019 21:22:19 -0600 Subject: [PATCH 071/116] - Revise Mac bash commands to install arm-gcc-bin library. --- docs/developer-guide.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/developer-guide.md b/docs/developer-guide.md index 8083e16b..04671b48 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -70,7 +70,8 @@ sudo pacman -S tup #### Mac First install [Homebrew](https://brew.sh/). Then you can run these commands in Terminal: ```bash -brew cask install gcc-arm-embedded +brew tap osx-cross/arm +brew install arm-gcc-bin brew cask install osxfuse && brew install tup brew install openocd ``` From a253da299f9be1dd0d796bafc7b8a4ff1f680fbb Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 24 Feb 2019 22:42:03 +0100 Subject: [PATCH 072/116] Add TrapezoidalMove to Arduino library ("t" ASCII command) --- Arduino/ODriveArduino/ODriveArduino.cpp | 4 ++++ Arduino/ODriveArduino/ODriveArduino.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Arduino/ODriveArduino/ODriveArduino.cpp b/Arduino/ODriveArduino/ODriveArduino.cpp index 2b2d6feb..00fce19a 100644 --- a/Arduino/ODriveArduino/ODriveArduino.cpp +++ b/Arduino/ODriveArduino/ODriveArduino.cpp @@ -42,6 +42,10 @@ void ODriveArduino::SetCurrent(int motor_number, float current) { serial_ << "c " << motor_number << " " << current << "\n"; } +void ODriveArduino::TrapezoidalMove(int motor_number, float position){ + serial_ << "t " << motor_number << " " << position << "\n"; +} + float ODriveArduino::readFloat() { return readString().toFloat(); } diff --git a/Arduino/ODriveArduino/ODriveArduino.h b/Arduino/ODriveArduino/ODriveArduino.h index 1ebe1e33..86b3aaf1 100644 --- a/Arduino/ODriveArduino/ODriveArduino.h +++ b/Arduino/ODriveArduino/ODriveArduino.h @@ -27,7 +27,7 @@ public: void SetVelocity(int motor_number, float velocity); void SetVelocity(int motor_number, float velocity, float current_feedforward); void SetCurrent(int motor_number, float current); - + void TrapezoidalMove(int motor_number, float position); // General params float readFloat(); int32_t readInt(); From b03ec2e88cd8a29e2aee9cc73828596b9841805b Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 25 Feb 2019 20:00:14 -0800 Subject: [PATCH 073/116] update changelog, improve some ascii commands --- CHANGELOG.md | 8 ++++++- Firmware/communication/ascii_protocol.cpp | 27 +++++++++++++++-------- docs/ascii-protocol.md | 19 ++++++++++++++++ docs/getting-started.md | 13 +++++++++-- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d2d159..bab57274 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,13 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors. +* `f` command to ascii protocol to get encoder position and velocity feedback. * `q` command to ascii protocol. It is like the old `p` command, but velocity and current mean limits, not feed-forward. +* `ss`, `se`, `sr` commands to ascii protocol, for save config, erase config and reboot. +* `move_incremental` function for relative trajectory moves. +* `encoder.config.ignore_illegal_hall_state` option. +* `encoder.config.enable_phase_interpolation` option. Setting to false may reduce jerky pulsations at low speed when using hall sensor feedback. +* Analog input. Used the same way as the PWM input mappings. * Voltage limit soft clamping instead of ERROR_MODULATION_MAGNITUDE in gimbal motor closed loop. * Thermal current limit with linear derating. @@ -15,7 +21,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * Overspeed fault * Current sense saturation fault. -* Supress startup transients by sampling encoder estimate into position setpoint when entering closed loop control. +* Suppress startup transients by sampling encoder estimate into position setpoint when entering closed loop control. * Make step dir gpio pins configurable. * Configuration variable `encoder.config.zero_count_on_find_idx`, true by default. Set to false to leave the initial encoder count to be where the axis was at boot. * Circular position setpoint mode: position setpoints wrapped [0, cpr). Useful for infinite incremental position control. diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 3ac68499..8a0d3287 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -106,15 +106,17 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& unsigned motor_number; float pos_setpoint, vel_limit, current_lim; int numscan = sscanf(cmd, "q %u %f %f %f", &motor_number, &pos_setpoint, &vel_limit, ¤t_lim); - if (numscan < 4) { + 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 { Axis* axis = axes[motor_number]; axis->controller_.pos_setpoint_ = pos_setpoint; - axis->controller_.config_.vel_limit = vel_limit; - axis->motor_.config_.current_lim = current_lim; + if (numscan >= 3) + axis->controller_.config_.vel_limit = vel_limit; + if (numscan >= 4) + axis->motor_.config_.current_lim = current_lim; } } else if (cmd[0] == 'v') { // velocity control @@ -172,6 +174,7 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& respond(response_channel, use_checksum, "Please see documentation for more details"); respond(response_channel, use_checksum, ""); respond(response_channel, use_checksum, "Available commands syntax reference:"); + respond(response_channel, use_checksum, "Position: q axis pos vel-lim I-lim"); respond(response_channel, use_checksum, "Position: p axis pos vel-ff I-ff"); respond(response_channel, use_checksum, "Velocity: v axis vel I-ff"); respond(response_channel, use_checksum, "Current: c axis I"); @@ -179,6 +182,10 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& respond(response_channel, use_checksum, "Properties start at odrive root, such as axis0.requested_state"); respond(response_channel, use_checksum, "Read: r property"); respond(response_channel, use_checksum, "Write: w property value"); + respond(response_channel, use_checksum, ""); + respond(response_channel, use_checksum, "Save config: ss"); + respond(response_channel, use_checksum, "Erase config: se"); + respond(response_channel, use_checksum, "Reboot: sr"); } else if (cmd[0] == 'i'){ // Dump device info // respond(response_channel, use_checksum, "Signature: %#x", STM_ID_GetSignature()); @@ -188,12 +195,14 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& respond(response_channel, use_checksum, "Firmware version: %d.%d.%d", FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_REVISION); respond(response_channel, use_checksum, "Serial number: %s", serial_number_str); - } else if (cmd[0] == 's'){ // Save config - save_configuration(); - } else if (cmd[0] == 'e'){ // Erase config - erase_configuration(); - } else if (cmd[0] == 'b'){ // Reboot - NVIC_SystemReset(); + } else if (cmd[0] == 's'){ // System + if(cmd[1] == 's') { // Save config + save_configuration(); + } else if (cmd[1] == 'e'){ // Erase config + erase_configuration(); + } else if (cmd[1] == 'b'){ // Reboot + NVIC_SystemReset(); + } } else if (cmd[0] == 'r') { // read property char name[MAX_LINE_LENGTH]; diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index 51ff18ea..0c510fe6 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -37,6 +37,20 @@ Example: `t 0 -20000` For general moving around of the axis, this is the recommended command. #### Motor Position command +For basic use where you send one setpoint at at a time, use the `q` command. +If you have a realtime controller that is streaming setpoints and tracking a trajectory, use the `p` command. + +``` +q motor position velocity_lim current_lim +``` +* `q` for position +* `motor` is the motor number, `0` or `1`. +* `position` is the desired position, in encoder counts. +* `velocity_lim` is the velocity limit, in counts/s (optional). +* `current_lim` is the current limit, in A (optional). + +Example: `q 0 -20000 10000 10` + ``` p motor position velocity_ff current_ff ``` @@ -90,3 +104,8 @@ Not all parameters can be accessed via the ASCII protocol but at least all param * `property` name of the property, as seen in ODrive Tool * `value` text representation of the value to be written * Example: `w axis0.controller.pos_setpoint -123.456` + +#### System commands: +* `ss` - Save config +* `se` - Erase config +* `sr` - Reboot diff --git a/docs/getting-started.md b/docs/getting-started.md index 1a346bd8..982ec861 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -261,7 +261,7 @@ You can also directly control the current of the motor, which is proportional to ### Trajectory control -Set `axis.controller.config.control_mode = CTRL_MODE_TRAJECTORY_CONTROL`.
+While in position control mode, use the `move_to_pos` or `move_incremental` functions. See the **Usage** section for details
This mode lets you smoothly accelerate, coast, and decelerate the axis from one position to another. With raw position control, the controller simply tries to go to the setpoint as quickly as possible. Using a trajectory lets you tune the feedback gains more aggressively to reject disturbance, while keeping smooth motion. ![Taptraj](TrapTrajPosVel.PNG)
@@ -291,9 +291,18 @@ Keep in mind that you must still set your safety limits as before. I recommend #### Usage Use the `move_to_pos` function to move to an absolute position: ``` -..controller.move_to_pos() +..controller.move_to_pos(your_absolute_pos) ``` +Use the `move_incremental` function to move to a relative position. +To set the goal relative to the current actual position, use `from_goal_point = False` +To set the goal relative to the previous destination, use `from_goal_point = True` +``` +..controller.move_incremental(pos_increment, from_goal_point) +``` + +You can also execute a move with the [appropriate ascii command](ascii-protocol.md#motor-trajectory-command). + ### Circular position control To enable Circular position control, set `axis.controller.config.setpoints_in_cpr = True` From bcf3270a9c7091e97aa5647f62cf5c552e6182b8 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 25 Feb 2019 20:07:03 -0800 Subject: [PATCH 074/116] update changelog to released state --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab57274..71d320cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Unreleased Features Please add a note of your changes below this heading if you make a Pull Request. +# Releases +## [0.4.8] - 2019-02-25 ### Added * `dump_errors()` utility function in odrivetool to dump, decode and optionally clear errors. * `f` command to ascii protocol to get encoder position and velocity feedback. @@ -16,7 +18,6 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Fixed * Added required 1.5 cycle phase shift between ADC and PWM, lack thereof caused unstable current controller at high eRPM. -# Releases ## [0.4.7] - 2018-11-28 ### Added * Overspeed fault From cb7c0d842fd8f655ed41826647e4aed747ce9557 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 26 Feb 2019 20:52:05 -0800 Subject: [PATCH 075/116] fix dump errors axis mismatch --- tools/odrive/utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 28d1d737..e74376cf 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -32,9 +32,10 @@ class OperationAbortedException(Exception): pass def dump_errors(odrv, clear=False): - axes = [axis for name, axis in odrv._remote_attributes.items() if 'axis' in name] - for num, axis in enumerate(axes): - print('Axis{}:'.format(num)) + axes = [(name, axis) for name, axis in odrv._remote_attributes.items() if 'axis' in name] + axes.sort() + for name, axis in axes: + print(name) # Flatten axis and submodules # (name, remote_obj, errorcode) From 40f24fd30b60b0d81fe7670671b2fca1b98f4503 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 3 Mar 2019 15:45:10 -0800 Subject: [PATCH 076/116] Update odrivetool.md --- docs/odrivetool.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index c00d5bc3..52b648bb 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -116,6 +116,7 @@ You can use the DfuSe app from ST. 1. Force the ODrive into DFU mode, as per the instructions above "How to force DFU mode". 1. In the top left it should now be connected to "STM Device in DFU Mode". 1. If it doesn't appear, it may be because the driver is set to libusb by Zadig. We need to set it back to the original driver. Follow [these instructions](https://github.com/pbatard/libwdi/wiki/FAQ#Help_Zadig_replaced_the_driver_for_the_wrong_device_How_do_I_restore_it). + 2. If, after doing the above step, the ODrive still installs itself as a libusb device in Device Manager, you can try to delete the libusb driver (this is OK, since we can use Zadig to install it again). You can simply delete the file `C:\Windows\System32\drivers\libusb0.sys`. 1. In the bottom right section called "Upgrade or Verify Action" click the button "Choose...". 1. Locate the `ODriveFirmware.dfu` we made before. 1. Click button "Upgrade". From 42384f3ef9b8b7eeec8f3e91818a30673e16414b Mon Sep 17 00:00:00 2001 From: Paul Belanger Date: Wed, 6 Mar 2019 14:00:47 -0500 Subject: [PATCH 077/116] Initial implementation of comms watchdog - Added watchdog timeout property to Axis::Config_t: axis.config.watchdog_timeout - Added axis protocol function to feed watchdog timer: axis.watchdog_feed() - Axis::run_control_loop now checks for watchdog expiration. - Ascii protocol: add support for watchdog - The following ASCII commands now automatically update the watchdog: p, v, t, c, q - Added a 'u' command to update the watchdog of a motor without modifying setpoints. - Updated ascii protocol documentation to reflect new commands and effects. - Updated getting started guide to mention watchdog settings and functions in protocol. Please note: due to unavailability of hardware at this time, I have been unable to test this code on an Odrive. --- Firmware/MotorControl/axis.cpp | 33 +++++++++++++++++++++++ Firmware/MotorControl/axis.hpp | 22 +++++++++++++-- Firmware/communication/ascii_protocol.cpp | 29 +++++++++++++++++--- docs/ascii-protocol.md | 18 +++++++++++++ docs/getting-started.md | 12 +++++++++ 5 files changed, 108 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index 8be43d15..759d237b 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -28,6 +28,7 @@ Axis::Axis(const AxisHardwareConfig_t& hw_config, trap_.axis_ = this; decode_step_dir_pins(); + update_watchdog_settings(); } static void step_cb_wrapper(void* ctx) { @@ -88,6 +89,18 @@ void Axis::decode_step_dir_pins() { dir_pin_ = get_gpio_pin_by_pin(config_.dir_gpio_pin); } +// @brief: Setup the watchdog reset value from the configuration watchdog timeout interval. +void Axis::update_watchdog_settings() { + + if(config_.watchdog_timeout <= 0.0f) { // watchdog disabled + watchdog_reset_value_ = 0; + } else if(config_.watchdog_timeout >= UINT32_MAX / (current_meas_hz+1)) { //overflow! + watchdog_reset_value_ = UINT32_MAX; + } else { + watchdog_reset_value_ = static_cast(config_.watchdog_timeout * current_meas_hz); + } +} + // @brief (de)activates step/dir input void Axis::set_step_dir_active(bool active) { if (active) { @@ -141,6 +154,26 @@ bool Axis::do_updates() { return check_for_errors(); } +// @brief Feed the watchdog to prevent watchdog timeouts. +void Axis::watchdog_feed() { + watchdog_current_value_ = watchdog_reset_value_; +} + +// @brief Check the watchdog timer for expiration. Also sets the watchdog error bit if expired. +bool Axis::watchdog_check() { + // reset value = 0 means watchdog disabled. + if(watchdog_reset_value_ == 0) return true; + + // explicit check here to ensure that we don't underflow back to UINT32_MAX + if(watchdog_current_value_ > 0) { + watchdog_current_value_--; + return true; + } else { + error_ |= ERROR_WATCHDOG_TIMER_EXPIRED; + return false; + } +} + bool Axis::run_sensorless_spin_up() { // Early Spin-up: spiral up current float x = 0.0f; diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index a6b6bdae..759f4d8d 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -20,6 +20,7 @@ public: ERROR_ENCODER_FAILED = 0x100, // Go to encoder.hpp for information, check odrvX.axisX.encoder.error for error value ERROR_CONTROLLER_FAILED = 0x200, ERROR_POS_CTRL_DURING_SENSORLESS = 0x400, + ERROR_WATCHDOG_TIMER_EXPIRED = 0x800, }; // Warning: Do not reorder these enum values. @@ -47,6 +48,8 @@ public: // For M0 this has no effect if enable_uart is true float counts_per_step = 2.0f; + float watchdog_timeout = 0.0f; // [s] (0 disables watchdog) + // Defaults loaded from hw_config in load_configuration in main.cpp uint16_t step_gpio_pin = 0; uint16_t dir_gpio_pin = 0; @@ -79,6 +82,8 @@ public: void step_cb(); void set_step_dir_active(bool enable); void decode_step_dir_pins(); + void update_watchdog_settings(); + static void load_default_step_dir_pin_config( const AxisHardwareConfig_t& hw_config, Config_t* config); @@ -87,6 +92,9 @@ public: bool do_checks(); bool do_updates(); + void watchdog_feed(); + bool watchdog_check(); + // True if there are no errors bool inline check_for_errors() { @@ -121,8 +129,11 @@ public: // Update all estimators // Note: updates run even if checks fail bool updates_ok = do_updates(); + + // make sure the watchdog is being fed. + bool watchdog_ok = watchdog_check(); - if (!checks_ok || !updates_ok) { + if (!checks_ok || !updates_ok || !watchdog_ok) { // It's not useful to quit idle since that is the safe action // Also leaving idle would rearm the motors if (current_state_ != AXIS_STATE_IDLE) @@ -185,6 +196,10 @@ public: State_t& current_state_ = task_chain_[0]; uint32_t loop_counter_ = 0; + // watchdog + uint32_t watchdog_reset_value_ = 0; //computed from config_.watchdog_timeout in update_watchdog_settings() + uint32_t watchdog_current_value_= 0; + // Communication protocol definitions auto make_protocol_definitions() { return make_protocol_member_list( @@ -201,6 +216,8 @@ public: make_protocol_property("startup_sensorless_control", &config_.startup_sensorless_control), make_protocol_property("enable_step_dir", &config_.enable_step_dir), make_protocol_property("counts_per_step", &config_.counts_per_step), + make_protocol_property("watchdog_timeout", &config_.watchdog_timeout, + [](void* ctx) { static_cast(ctx)->update_watchdog_settings(); }, this), make_protocol_property("step_gpio_pin", &config_.step_gpio_pin, [](void* ctx) { static_cast(ctx)->decode_step_dir_pins(); }, this), make_protocol_property("dir_gpio_pin", &config_.dir_gpio_pin, @@ -215,7 +232,8 @@ public: 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("trap_traj", trap_.make_protocol_definitions()) + make_protocol_object("trap_traj", trap_.make_protocol_definitions()), + make_protocol_function("watchdog_feed", *this, &Axis::watchdog_feed) ); } }; diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index 8a0d3287..1e1c9ba4 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -99,7 +99,9 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& vel_feed_forward = 0.0f; if (numscan < 4) current_feed_forward = 0.0f; - axes[motor_number]->controller_.set_pos_setpoint(pos_setpoint, vel_feed_forward, current_feed_forward); + Axis* axis = axes[motor_number]; + axis->controller_.set_pos_setpoint(pos_setpoint, vel_feed_forward, current_feed_forward); + axis->watchdog_feed(); } } else if (cmd[0] == 'q') { // position control with limits @@ -117,6 +119,8 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& axis->controller_.config_.vel_limit = vel_limit; if (numscan >= 4) axis->motor_.config_.current_lim = current_lim; + + axis->watchdog_feed(); } } else if (cmd[0] == 'v') { // velocity control @@ -130,7 +134,9 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& } else { if (numscan < 3) current_feed_forward = 0.0f; - axes[motor_number]->controller_.set_vel_setpoint(vel_setpoint, current_feed_forward); + Axis* axis = axes[motor_number]; + axis->controller_.set_vel_setpoint(vel_setpoint, current_feed_forward); + axis->watchdog_feed(); } } else if (cmd[0] == 'c') { // current control @@ -142,7 +148,9 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& } else if (motor_number >= AXIS_COUNT) { respond(response_channel, use_checksum, "invalid motor %u", motor_number); } else { - axes[motor_number]->controller_.set_current_setpoint(current_setpoint); + Axis* axis = axes[motor_number]; + axis->controller_.set_current_setpoint(current_setpoint); + axis->watchdog_feed(); } } else if (cmd[0] == 't') { // trapezoidal trajectory @@ -154,7 +162,9 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& } 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); + Axis* axis = axes[motor_number]; + axis->controller_.move_to_pos(goal_point); + axis->watchdog_feed(); } } else if (cmd[0] == 'f') { // feedback @@ -240,6 +250,17 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& } } + }else if (cmd[0] == 'u') { // Update axis watchdog. + unsigned motor_number; + int numscan = sscanf(cmd, "u %u", &motor_number); + if(numscan < 1){ + 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]->watchdog_feed(); + } + } else if (cmd[0] != 0) { respond(response_channel, use_checksum, "unknown command"); } diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index 0c510fe6..d782aa79 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -36,6 +36,8 @@ Example: `t 0 -20000` For general moving around of the axis, this is the recommended command. +This command updates the watchdog timer for the motor. + #### Motor Position command For basic use where you send one setpoint at at a time, use the `q` command. If you have a realtime controller that is streaming setpoints and tracking a trajectory, use the `p` command. @@ -64,6 +66,7 @@ Example: `p 0 -20000 0 0` Note that if you don't know what feed-forward is or what it's used for, simply omit it. +This command updates the watchdog timer for the motor. #### Motor Velocity command ``` @@ -78,6 +81,8 @@ Example: `v 0 1000 0` Note that if you don't know what feed-forward is or what it's used for, simply omit it. +This command updates the watchdog timer for the motor. + #### Motor Current command ``` c motor current @@ -86,6 +91,19 @@ c motor current * `motor` is the motor number, `0` or `1`. * `current` is the desired current in A. +This command updates the watchdog timer for the motor. + + +#### Update motor watchdog +``` +u motor +``` +* `u` for /u/pdate. +* `motor` is the motor number, `0` or `1`. + +This command updates the watchdog timer for the motor, without changing any +setpoints. + #### Parameter reading/writing Not all parameters can be accessed via the ASCII protocol but at least all parameters with float and integer type are supported. diff --git a/docs/getting-started.md b/docs/getting-started.md index 982ec861..8060bbb3 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -331,6 +331,18 @@ You can now control the current with `axis.controller.current_setpoint = 3` [A]. *Note: There is no velocity limiting in current control mode. Make sure that you don't overrev the motor, or exceed the max speed for your encoder.* + +## Watchdog Timer +Each axis has a configurable watchdog timer that can stop the motors if the +control connection to the ODrive is interrupted. + +Each axis has a configurable watchdog timeout: `axis.config.watchdog_timeout`, +measured in seconds. A value of `0` disables the watchdog functionality. Any value +`> 0` will stop the motors if the watchdog has not been fed in the configured +time interval. + +The watchdog is fed using the `axis.watchdog_feed()` method of each axis. + ## What's next? You can now: * See what other [commands and parameters](commands.md) are available, including setting tuning parameters for better performance. From 412f7962f4e584ac3b06224ed21610be381b6e35 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 11 Mar 2019 14:35:19 -0700 Subject: [PATCH 078/116] reject precalibration unless encoder is ready --- Firmware/MotorControl/encoder.cpp | 30 ++++++++++++++++++------------ Firmware/MotorControl/encoder.hpp | 11 +++++++---- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 0cf13549..49605017 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -39,9 +39,8 @@ bool Encoder::do_checks(){ // Triggered when an encoder passes over the "Index" pin // TODO: only arm index edge interrupt when we know encoder has powered up // (maybe by attaching the interrupt on start search, synergistic with following) -// TODO: disable interrupt once we found the index void Encoder::enc_index_cb() { - if (config_.use_index && !index_found_) { + if (config_.use_index) { set_circular_count(0, false); if (config_.zero_count_on_find_idx) set_linear_count(0); // Avoid position control transient after search @@ -71,6 +70,23 @@ void Encoder::set_idx_subscribe(bool override_enable) { } } +void Encoder::update_pll_gains() { + pll_kp_ = 2.0f * config_.bandwidth; // basic conversion to discrete time + pll_ki_ = 0.25f * (pll_kp_ * pll_kp_); // Critically damped + + // Check that we don't get problems with discrete time approximation + if (!(current_meas_period * pll_kp_ < 1.0f)) { + set_error(ERROR_UNSTABLE_GAIN); + } +} + +void Encoder::check_pre_calibrated() { + if (!is_ready_) + config_.pre_calibrated = false; + if (config_.mode == MODE_INCREMENTAL && !index_found_) + config_.pre_calibrated = false; +} + // Function that sets the current encoder count to a desired 32-bit value. void Encoder::set_linear_count(int32_t count) { // Disable interrupts to make a critical section to avoid race condition @@ -261,16 +277,6 @@ static bool decode_hall(uint8_t hall_state, int32_t* hall_cnt) { } } -void Encoder::update_pll_gains() { - pll_kp_ = 2.0f * config_.bandwidth; // basic conversion to discrete time - pll_ki_ = 0.25f * (pll_kp_ * pll_kp_); // Critically damped - - // Check that we don't get problems with discrete time approximation - if (!(current_meas_period * pll_kp_ < 1.0f)) { - set_error(ERROR_UNSTABLE_GAIN); - } -} - void Encoder::sample_now() { switch (config_.mode) { case MODE_INCREMENTAL: { diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index dff98f28..b3a0e3bd 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -52,6 +52,8 @@ public: void enc_index_cb(); void set_idx_subscribe(bool override_enable = false); + void update_pll_gains(); + void check_pre_calibrated(); void set_linear_count(int32_t count); void set_circular_count(int32_t count, bool update_offset); @@ -63,7 +65,7 @@ public: void sample_now(); bool update(); - void update_pll_gains(); + const EncoderHardwareConfig_t& hw_config_; Config_t& config_; @@ -92,8 +94,8 @@ public: auto make_protocol_definitions() { return make_protocol_member_list( make_protocol_property("error", &error_), - make_protocol_ro_property("is_ready", &is_ready_), - make_protocol_ro_property("index_found", const_cast(&index_found_)), + make_protocol_property("is_ready", &is_ready_), + make_protocol_property("index_found", const_cast(&index_found_)), make_protocol_property("shadow_count", &shadow_count_), make_protocol_property("count_in_cpr", &count_in_cpr_), make_protocol_property("interpolation", &interpolation_), @@ -110,7 +112,8 @@ public: [](void* ctx) { static_cast(ctx)->set_idx_subscribe(); }, this), make_protocol_property("find_idx_on_lockin_only", &config_.find_idx_on_lockin_only, [](void* ctx) { static_cast(ctx)->set_idx_subscribe(); }, this), - make_protocol_property("pre_calibrated", &config_.pre_calibrated), + make_protocol_property("pre_calibrated", &config_.pre_calibrated, + [](void* ctx) { static_cast(ctx)->check_pre_calibrated(); }, this), make_protocol_property("zero_count_on_find_idx", &config_.zero_count_on_find_idx), make_protocol_property("cpr", &config_.cpr), make_protocol_property("offset", &config_.offset), From 3400d60fa347855376c7fc8c67a3ace721d59093 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 16 Mar 2019 00:30:32 +0100 Subject: [PATCH 079/116] Fix intellisense includePath, add flylint paths to workspace settings --- Firmware/.vscode/c_cpp_properties.json | 7 ++++--- ODrive_Workspace.code-workspace | 10 +++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 570c0f29..5e30003f 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -6,6 +6,7 @@ "${workspaceRoot}", "${workspaceRoot}/fibre/cpp/include/**", "${workspaceRoot}/MotorControl", + "${workspaceRoot}/communication", "${workspaceRoot}/Drivers/DRV8301", "${workspaceRoot}/Board/v3/Inc", "${workspaceRoot}/Board/v3/Drivers/CMSIS/Include", @@ -16,9 +17,7 @@ "${workspaceRoot}/Board/v3/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc", "${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", - "${ARM_GCC_ROOT}/arm-none-eabi/include/**", - "${ARM_GCC_ROOT}/lib/gcc/arm-none-eabi/**" + "${workspaceRoot}/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F" ], "defines": [ "STM32F405xx", @@ -49,6 +48,7 @@ "${workspaceRoot}", "${workspaceRoot}/fibre/cpp/include/**", "${workspaceRoot}/MotorControl", + "${workspaceRoot}/communication", "${workspaceRoot}/Drivers/DRV8301", "${workspaceRoot}/Board/v3/Inc", "${workspaceRoot}/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F", @@ -92,6 +92,7 @@ "${workspaceRoot}", "${workspaceRoot}/fibre/cpp/include/**", "${workspaceRoot}/MotorControl", + "${workspaceRoot}/communication", "${workspaceRoot}/Drivers/DRV8301", "${workspaceRoot}/Board/v3/Inc", "${workspaceRoot}/Board/v3/Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F", diff --git a/ODrive_Workspace.code-workspace b/ODrive_Workspace.code-workspace index d858dcc3..1670d342 100644 --- a/ODrive_Workspace.code-workspace +++ b/ODrive_Workspace.code-workspace @@ -11,6 +11,14 @@ } ], "settings": { + + "c-cpp-flylint.cppcheck.includePaths": [ + "${workspaceRoot}", + "${workspaceRoot}/fibre/cpp/include/fibre", + "${workspaceRoot}/communication", + "${workspaceRoot}/MotorControl", + ], + "files.associations": { "memory": "cpp", "utility": "cpp", @@ -47,5 +55,5 @@ "future": "cpp", "arm_math.h": "c" } - } + } From e7af78d2b02f0ae209bed453452f2e1a14a5318e Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 16 Mar 2019 00:35:08 +0100 Subject: [PATCH 080/116] Wrap gcc.exe in compilerPath with quotes --- Firmware/.vscode/c_cpp_properties.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 5e30003f..98ddcaba 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -38,7 +38,7 @@ ], "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", + "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" }, From 61632acce7205fd81855de3ba1019da415db9116 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 16 Mar 2019 23:30:45 +0100 Subject: [PATCH 081/116] Fix missing curly brace --- ODrive_Workspace.code-workspace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ODrive_Workspace.code-workspace b/ODrive_Workspace.code-workspace index 1670d342..65ed1095 100644 --- a/ODrive_Workspace.code-workspace +++ b/ODrive_Workspace.code-workspace @@ -55,5 +55,5 @@ "future": "cpp", "arm_math.h": "c" } - + } } From 37427fcdc201d57726eca5ec58dc4e8d87c50f7a Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:17:29 +0100 Subject: [PATCH 082/116] Fix protocol templates --- Firmware/fibre/cpp/include/fibre/protocol.hpp | 127 ++++-------------- 1 file changed, 23 insertions(+), 104 deletions(-) diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index 4b503e68..552dbd9e 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -84,59 +84,17 @@ typedef struct { template::value>> -inline size_t write_le(T value, uint8_t* buffer); +inline size_t write_le(T value, uint8_t* buffer){ + for(size_t i = 0; i < sizeof(value); ++i){ + buffer[i] = (value >> 8*i) & 0xff; + } + return sizeof(value); +} template -inline size_t read_le(T* value, const uint8_t* buffer); - -template<> -inline size_t write_le(bool value, uint8_t* buffer) { - buffer[0] = value ? 1 : 0; - return 1; -} - -template<> -inline size_t write_le(uint8_t value, uint8_t* buffer) { - buffer[0] = value; - return 1; -} - -template<> -inline size_t write_le(uint16_t value, uint8_t* buffer) { - buffer[0] = (value >> 0) & 0xff; - buffer[1] = (value >> 8) & 0xff; - return 2; -} - -template<> -inline size_t write_le(uint32_t value, uint8_t* buffer) { - buffer[0] = (value >> 0) & 0xff; - buffer[1] = (value >> 8) & 0xff; - buffer[2] = (value >> 16) & 0xff; - buffer[3] = (value >> 24) & 0xff; - return 4; -} - -template<> -inline size_t write_le(int32_t value, uint8_t* buffer) { - buffer[0] = (value >> 0) & 0xff; - buffer[1] = (value >> 8) & 0xff; - buffer[2] = (value >> 16) & 0xff; - buffer[3] = (value >> 24) & 0xff; - return 4; -} - -template<> -inline size_t write_le(uint64_t value, uint8_t* buffer) { - buffer[0] = (value >> 0) & 0xff; - buffer[1] = (value >> 8) & 0xff; - buffer[2] = (value >> 16) & 0xff; - buffer[3] = (value >> 24) & 0xff; - buffer[4] = (value >> 32) & 0xff; - buffer[5] = (value >> 40) & 0xff; - buffer[6] = (value >> 48) & 0xff; - buffer[7] = (value >> 56) & 0xff; - return 8; +typename std::enable_if_t::value, size_t> +write_le(T value, uint8_t* buffer) { + return write_le>(value, buffer); } template<> @@ -148,59 +106,12 @@ inline size_t write_le(float value, uint8_t* buffer) { } template -typename std::enable_if_t::value, size_t> -write_le(T value, uint8_t* buffer) { - return write_le>(value, buffer); -} - -template<> -inline size_t read_le(bool* value, const uint8_t* buffer) { - *value = buffer[0]; - return 1; -} - -template<> -inline size_t read_le(uint8_t* value, const uint8_t* buffer) { - *value = buffer[0]; - return 1; -} - -template<> -inline size_t read_le(uint16_t* value, const uint8_t* buffer) { - *value = (static_cast(buffer[0]) << 0) | - (static_cast(buffer[1]) << 8); - return 2; -} - -template<> -inline size_t read_le(int32_t* value, const uint8_t* buffer) { - *value = (static_cast(buffer[0]) << 0) | - (static_cast(buffer[1]) << 8) | - (static_cast(buffer[2]) << 16) | - (static_cast(buffer[3]) << 24); - return 4; -} - -template<> -inline size_t read_le(uint32_t* value, const uint8_t* buffer) { - *value = (static_cast(buffer[0]) << 0) | - (static_cast(buffer[1]) << 8) | - (static_cast(buffer[2]) << 16) | - (static_cast(buffer[3]) << 24); - return 4; -} - -template<> -inline size_t read_le(uint64_t* value, const uint8_t* buffer) { - *value = (static_cast(buffer[0]) << 0) | - (static_cast(buffer[1]) << 8) | - (static_cast(buffer[2]) << 16) | - (static_cast(buffer[3]) << 24) | - (static_cast(buffer[4]) << 32) | - (static_cast(buffer[5]) << 40) | - (static_cast(buffer[6]) << 48) | - (static_cast(buffer[7]) << 56); - return 8; +inline size_t read_le(T* value, const uint8_t* buffer){ + *value = static_cast(buffer[0]); + for(size_t i = 1; i < sizeof(*value); ++i){ + *value |= static_cast(buffer[i]) << i*8; + } + return sizeof(*value); } template<> @@ -499,6 +410,14 @@ inline constexpr const char* get_default_json_modifier() { return "\"type\":\"float\",\"access\":\"rw\""; } template<> +inline constexpr const char* get_default_json_modifier() { + return "\"type\":\"int64\",\"access\":\"r\""; +} +template<> +inline constexpr const char* get_default_json_modifier() { + return "\"type\":\"int64\",\"access\":\"rw\""; +} +template<> inline constexpr const char* get_default_json_modifier() { return "\"type\":\"uint64\",\"access\":\"r\""; } From 4d1e8152079f748975590264dfe69e129b91309d Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 16 Mar 2019 15:21:32 +0100 Subject: [PATCH 083/116] Fix include paths not being found by tup --- Firmware/build.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/build.lua b/Firmware/build.lua index 8962e403..bf1730a2 100644 --- a/Firmware/build.lua +++ b/Firmware/build.lua @@ -162,7 +162,7 @@ function build(args) outputs.includes = {} for _,inc in pairs(args.includes) do - table.insert(outputs.includes, tup.nodevariable(inc)) + table.insert(outputs.includes, inc) end if args.name != nil then all_packages[args.name] = outputs From d7a067e87dbc13708bb84628240b620a56e0d3c1 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 16 Mar 2019 15:23:29 +0100 Subject: [PATCH 084/116] Build objects to /obj folder --- Firmware/build.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/build.lua b/Firmware/build.lua index bf1730a2..d8a4a0f2 100644 --- a/Firmware/build.lua +++ b/Firmware/build.lua @@ -63,7 +63,7 @@ function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) inc_flags += "-I"..inc end -- todo: vary build directory - obj_file = builddir.."/"..src:gsub("/","_")..".o" + obj_file = builddir.."/obj/"..src:gsub("/","_")..".o" outputs.object_files += obj_file if gen_su_file then su_file = builddir.."/"..src:gsub("/","_")..".su" From 68259cd8f175026cfbdbbb2aeb6f8b187f6e9722 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sat, 16 Mar 2019 15:23:42 +0100 Subject: [PATCH 085/116] gcc_generic_compiler must be declared local --- Firmware/build.lua | 2 +- Firmware/fibre/tupfiles/build.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/build.lua b/Firmware/build.lua index d8a4a0f2..d4c7aad4 100644 --- a/Firmware/build.lua +++ b/Firmware/build.lua @@ -56,7 +56,7 @@ function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) compiler_flags += '-fstack-usage' end - gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) + local gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) -- convert include list to flags inc_flags = {} for _,inc in pairs(includes) do diff --git a/Firmware/fibre/tupfiles/build.lua b/Firmware/fibre/tupfiles/build.lua index 4a0ea105..8b4a4510 100644 --- a/Firmware/fibre/tupfiles/build.lua +++ b/Firmware/fibre/tupfiles/build.lua @@ -5,7 +5,7 @@ function GCCToolchain(prefix, builddir, compiler_flags, linker_flags) -- add some default compiler flags compiler_flags += '-fstack-usage' - gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) + local gcc_generic_compiler = function(compiler, compiler_flags, gen_su_file, src, flags, includes, outputs) -- resolve source path src = tostring(src) From b049e6f0ebcee8ed91dbde734f8ee607702aafb1 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:25:38 +0100 Subject: [PATCH 086/116] Add a config flag for debug builds. Build with -O2 by default --- Firmware/Tupfile.lua | 10 ++++++++-- Firmware/tup.config.default | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 94cb080e..2b1d5997 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -92,7 +92,12 @@ FLAGS += '-mfloat-abi=hard' FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} -- debug build -FLAGS += '-g -gdwarf-2' +if tup.getconfig("DEBUG") == "true" then + FLAGS += '-g -gdwarf-2' + OPT += '-Og' +else + OPT += '-O2' +end -- linker flags @@ -104,7 +109,8 @@ LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' -- common flags for ASM, C and C++ -OPT += '-Og' +-- OPT += '-Og' +-- OPT += '-O2' -- OPT += '-O0' OPT += '-ffast-math -fno-finite-math-only' tup.append_table(FLAGS, OPT) diff --git a/Firmware/tup.config.default b/Firmware/tup.config.default index 60d2500d..5c2c4822 100644 --- a/Firmware/tup.config.default +++ b/Firmware/tup.config.default @@ -3,6 +3,7 @@ #CONFIG_BOARD_VERSION=v3.5-24V CONFIG_USB_PROTOCOL=native CONFIG_UART_PROTOCOL=ascii +CONFIG_DEBUG=false # Uncomment this to error on compilation warnings #CONFIG_STRICT=true From 2e71e81dd12360ab89f3472f0a1f10a2afce7ba6 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:33:14 +0100 Subject: [PATCH 087/116] Call load_configuration before any threads start --- Firmware/Board/v3/Src/freertos.c | 3 +++ Firmware/MotorControl/main.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Firmware/Board/v3/Src/freertos.c b/Firmware/Board/v3/Src/freertos.c index 11154587..3c34849b 100644 --- a/Firmware/Board/v3/Src/freertos.c +++ b/Firmware/Board/v3/Src/freertos.c @@ -60,6 +60,7 @@ #include "usb_device.h" extern PCD_HandleTypeDef hpcd_USB_OTG_FS; int odrive_main(void); +int load_configuration(void); /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -187,6 +188,8 @@ void MX_FREERTOS_Init(void) { sem_usb_tx = osSemaphoreCreate(osSemaphore(sem_usb_tx), 1); init_deferred_interrupts(); + + load_configuration(); /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 7ffeb5e3..1a1f331d 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -45,7 +45,7 @@ void save_configuration(void) { } } -void load_configuration(void) { +extern "C" int load_configuration(void) { // Try to load configs if (NVM_init() || ConfigFormat::safe_load_config( @@ -71,6 +71,7 @@ void load_configuration(void) { } else { user_config_loaded_ = true; } + return user_config_loaded_; } void erase_configuration(void) { @@ -117,7 +118,6 @@ void vApplicationIdleHook(void) { int odrive_main(void) { // Load persistent configuration (or defaults) - load_configuration(); #if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 if (board_config.enable_i2c_instead_of_can) { From b9a23a118722d6dd19f1c9a328deaeea8c127945 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:38:29 +0100 Subject: [PATCH 088/116] Add a couple settings for cppcheck --- ODrive_Workspace.code-workspace | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ODrive_Workspace.code-workspace b/ODrive_Workspace.code-workspace index 65ed1095..3d990eaf 100644 --- a/ODrive_Workspace.code-workspace +++ b/ODrive_Workspace.code-workspace @@ -18,6 +18,8 @@ "${workspaceRoot}/communication", "${workspaceRoot}/MotorControl", ], + "c-cpp-flylint.cppcheck.platform": "avr8", + "c-cpp-flylint.cppcheck.standard": ["c99","c++14"], "files.associations": { "memory": "cpp", From b97a91ad94b288630a63426c9b31f20cd95719b2 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:42:47 +0100 Subject: [PATCH 089/116] Remove browse_path locations --- Firmware/.vscode/c_cpp_properties.json | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 98ddcaba..68884c82 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -28,16 +28,10 @@ "USB_PROTOCOL_NATIVE", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", - "__GNUC__" + "__GNUC__", + "__ODRIVE_MAIN_H" ], "intelliSenseMode": "clang-x64", - "browse": { - "path": [ - "${workspaceRoot}", - "${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" From b5f5b0396d5ce0f24c8bd32d4301254875bb8291 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:43:18 +0100 Subject: [PATCH 090/116] Declare single-argument constructors explicit --- Firmware/MotorControl/controller.hpp | 2 +- Firmware/MotorControl/sensorless_estimator.hpp | 2 +- Firmware/MotorControl/trapTraj.hpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index 7ffa25b6..020f34d0 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -34,7 +34,7 @@ public: bool setpoints_in_cpr = false; }; - Controller(Config_t& config); + explicit Controller(Config_t& config); void reset(); void set_error(Error_t error); diff --git a/Firmware/MotorControl/sensorless_estimator.hpp b/Firmware/MotorControl/sensorless_estimator.hpp index 6d15820f..719a3227 100644 --- a/Firmware/MotorControl/sensorless_estimator.hpp +++ b/Firmware/MotorControl/sensorless_estimator.hpp @@ -14,7 +14,7 @@ public: float pm_flux_linkage = 1.58e-3f; // [V / (rad/s)] { 5.51328895422 / ( * ) } }; - SensorlessEstimator(Config_t& config); + explicit SensorlessEstimator(Config_t& config); bool update(); diff --git a/Firmware/MotorControl/trapTraj.hpp b/Firmware/MotorControl/trapTraj.hpp index 42dac0ef..fe5f3fec 100644 --- a/Firmware/MotorControl/trapTraj.hpp +++ b/Firmware/MotorControl/trapTraj.hpp @@ -9,13 +9,14 @@ public: 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); + explicit TrapezoidalTrajectory(Config_t& config); bool planTrapezoidal(float Xf, float Xi, float Vi, float Vmax, float Amax, float Dmax); Step_t eval(float t); From 7fbbe2abfff910e4df18b76f79c14ee02b378f84 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:56:17 +0100 Subject: [PATCH 091/116] Use memcpy to put data into the buffer --- Firmware/fibre/cpp/include/fibre/protocol.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index 552dbd9e..ff3e6237 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -82,12 +82,11 @@ typedef struct { uint16_t endpoint_id; } endpoint_ref_t; +#include template::value>> inline size_t write_le(T value, uint8_t* buffer){ - for(size_t i = 0; i < sizeof(value); ++i){ - buffer[i] = (value >> 8*i) & 0xff; - } + std::memcpy(&buffer[0], &value, sizeof(value)); return sizeof(value); } @@ -118,6 +117,7 @@ template<> inline size_t read_le(float* value, const uint8_t* buffer) { static_assert(CHAR_BIT * sizeof(float) == 32, "32 bit floating point expected"); static_assert(std::numeric_limits::is_iec559, "IEEE 754 floating point expected"); + return read_le(reinterpret_cast(value), buffer); } From b8c144905beb94ee7106cec9923107020cf7e4f7 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 20:59:37 +0100 Subject: [PATCH 092/116] Use memcpy in read to copy data from buffer to value --- Firmware/fibre/cpp/include/fibre/protocol.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index ff3e6237..664ba20d 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -106,10 +106,7 @@ inline size_t write_le(float value, uint8_t* buffer) { template inline size_t read_le(T* value, const uint8_t* buffer){ - *value = static_cast(buffer[0]); - for(size_t i = 1; i < sizeof(*value); ++i){ - *value |= static_cast(buffer[i]) << i*8; - } + std::memcpy(value, buffer, sizeof(*value)); return sizeof(*value); } From b5609a8da0ea23be90c765c058871308aee3a266 Mon Sep 17 00:00:00 2001 From: Paul Guenette Date: Sun, 17 Mar 2019 21:02:40 +0100 Subject: [PATCH 093/116] Remove dumb define in intellisense --- Firmware/.vscode/c_cpp_properties.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 68884c82..4b93d26e 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -28,8 +28,7 @@ "USB_PROTOCOL_NATIVE", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", - "__GNUC__", - "__ODRIVE_MAIN_H" + "__GNUC__" ], "intelliSenseMode": "clang-x64", "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", From 901311b582cc6904c4ea76322a8df33d5b961ca6 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 18 Mar 2019 17:57:44 -0700 Subject: [PATCH 094/116] add ODrive v3.6 --- Firmware/.vscode/c_cpp_properties.json | 4 ++-- Firmware/Board/v3/Inc/main.h | 2 +- Firmware/MotorControl/odrive_main.h | 2 +- Firmware/Tupfile.lua | 8 ++++++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index 570c0f29..29d5fa77 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -24,8 +24,8 @@ "STM32F405xx", "USE_HAL_DRIVER", "HW_VERSION_MAJOR=3", - "HW_VERSION_MINOR=5", - "HW_VERSION_VOLTAGE=24", + "HW_VERSION_MINOR=6", + "HW_VERSION_VOLTAGE=56", "USB_PROTOCOL_NATIVE", "__weak=\"__attribute__((weak))\"", "__packed=\"__attribute__((__packed__))\"", diff --git a/Firmware/Board/v3/Inc/main.h b/Firmware/Board/v3/Inc/main.h index d41b19ee..bfd9888c 100644 --- a/Firmware/Board/v3/Inc/main.h +++ b/Firmware/Board/v3/Inc/main.h @@ -171,7 +171,7 @@ #define CURRENT_MEAS_PERIOD ( (float)2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1) / (float)TIM_1_8_CLOCK_HZ ) #define CURRENT_MEAS_HZ ( (float)(TIM_1_8_CLOCK_HZ) / (float)(2*TIM_1_8_PERIOD_CLOCKS*(TIM_1_8_RCR+1)) ) -#if HW_VERSION_VOLTAGE == 48 +#if HW_VERSION_VOLTAGE >= 48 #define VBUS_S_DIVIDER_RATIO 19.0f #define VBUS_OVERVOLTAGE_LEVEL 52.0f #elif HW_VERSION_VOLTAGE == 24 diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index df1bc14b..677fb996 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -77,7 +77,7 @@ struct BoardConfig_t { float brake_resistance = 0.47f; // [ohm] #endif float dc_bus_undervoltage_trip_level = 8.0f; // Date: Mon, 18 Mar 2019 18:36:08 -0700 Subject: [PATCH 095/116] update default OTP in makefile --- Firmware/Makefile | 4 ++-- tools/odrive/version.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Firmware/Makefile b/Firmware/Makefile index 82ae758c..263ba433 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -70,8 +70,8 @@ ifeq ($(OTP_CONFIRM),TRUE) -c 'mwb 0x1fff7801 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7802 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7803 3' -c 'sleep 10' \ - -c 'mwb 0x1fff7804 5' -c 'sleep 10' \ - -c 'mwb 0x1fff7805 48' -c 'sleep 10' \ + -c 'mwb 0x1fff7804 6' -c 'sleep 10' \ + -c 'mwb 0x1fff7805 56' -c 'sleep 10' \ -c 'reset run' \ -c exit @echo "OK" diff --git a/tools/odrive/version.py b/tools/odrive/version.py index 5a2827a3..64742ff0 100644 --- a/tools/odrive/version.py +++ b/tools/odrive/version.py @@ -70,6 +70,10 @@ if __name__ == '__main__': args = parser.parse_args() git_name, major, minor, revision, unreleased = get_version_from_git() + + #TODO temporary override to get around makefile editing for OTP + unreleased = False + print('Firmware version {}.{}.{}{} ({})'.format( major, minor, revision, '-dev' if unreleased else '', git_name)) From c399187edd3085732422bf05d74a3461ec43a5d9 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 18 Mar 2019 18:36:35 -0700 Subject: [PATCH 096/116] Revert "update default OTP in makefile" This reverts commit 2b6626a76d85c2bce06cf55e0c0d07630171d478. --- Firmware/Makefile | 4 ++-- tools/odrive/version.py | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Firmware/Makefile b/Firmware/Makefile index 263ba433..82ae758c 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -70,8 +70,8 @@ ifeq ($(OTP_CONFIRM),TRUE) -c 'mwb 0x1fff7801 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7802 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7803 3' -c 'sleep 10' \ - -c 'mwb 0x1fff7804 6' -c 'sleep 10' \ - -c 'mwb 0x1fff7805 56' -c 'sleep 10' \ + -c 'mwb 0x1fff7804 5' -c 'sleep 10' \ + -c 'mwb 0x1fff7805 48' -c 'sleep 10' \ -c 'reset run' \ -c exit @echo "OK" diff --git a/tools/odrive/version.py b/tools/odrive/version.py index 64742ff0..5a2827a3 100644 --- a/tools/odrive/version.py +++ b/tools/odrive/version.py @@ -70,10 +70,6 @@ if __name__ == '__main__': args = parser.parse_args() git_name, major, minor, revision, unreleased = get_version_from_git() - - #TODO temporary override to get around makefile editing for OTP - unreleased = False - print('Firmware version {}.{}.{}{} ({})'.format( major, minor, revision, '-dev' if unreleased else '', git_name)) From e7953744343dfa84f5bc848ef3511d600ef9b77f Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 18 Mar 2019 18:38:18 -0700 Subject: [PATCH 097/116] update default OTP in makefile --- Firmware/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/Makefile b/Firmware/Makefile index 82ae758c..263ba433 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -70,8 +70,8 @@ ifeq ($(OTP_CONFIRM),TRUE) -c 'mwb 0x1fff7801 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7802 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7803 3' -c 'sleep 10' \ - -c 'mwb 0x1fff7804 5' -c 'sleep 10' \ - -c 'mwb 0x1fff7805 48' -c 'sleep 10' \ + -c 'mwb 0x1fff7804 6' -c 'sleep 10' \ + -c 'mwb 0x1fff7805 56' -c 'sleep 10' \ -c 'reset run' \ -c exit @echo "OK" From e94230275eae8865b0aea40bdf16347297f4552c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 18 Mar 2019 21:09:46 -0700 Subject: [PATCH 098/116] add python side enum, fix instant timout on 1st iteration --- Firmware/MotorControl/axis.cpp | 3 +++ tools/odrive/enums.py | 1 + 2 files changed, 4 insertions(+) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index d20f292a..80987c00 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -99,6 +99,9 @@ void Axis::update_watchdog_settings() { } else { watchdog_reset_value_ = static_cast(config_.watchdog_timeout * current_meas_hz); } + + // Do a feed to avoid instant timeout + watchdog_feed(); } // @brief (de)activates step/dir input diff --git a/tools/odrive/enums.py b/tools/odrive/enums.py index fc5439bd..d4425a21 100644 --- a/tools/odrive/enums.py +++ b/tools/odrive/enums.py @@ -27,6 +27,7 @@ class errors: ERROR_ENCODER_FAILED = 0x100 # Go to encoder.hpp for information, check odrvX.axisX.encoder.error for error value ERROR_CONTROLLER_FAILED = 0x200 ERROR_POS_CTRL_DURING_SENSORLESS = 0x400 + ERROR_WATCHDOG_TIMER_EXPIRED = 0x800 class motor: ERROR_NONE = 0 From dc968c8f3d5ef7710c81b1765a0b932a82a2059a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 18 Mar 2019 21:12:51 -0700 Subject: [PATCH 099/116] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 975a62d9..e0d2f219 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Unreleased Features Please add a note of your changes below this heading if you make a Pull Request. +### Added +* Communication watchdog feature. + # Releases ## [0.4.8] - 2019-02-25 ### Added From 8695f1fc6e69ef8fdb9c02b2d5ddd279169ca2e7 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 19 Mar 2019 07:10:04 +0100 Subject: [PATCH 100/116] Apply comment suggestions from code review Co-Authored-By: Wetmelon --- Firmware/Board/v3/Src/freertos.c | 1 + Firmware/MotorControl/main.cpp | 1 - Firmware/fibre/cpp/include/fibre/protocol.hpp | 2 ++ 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/Board/v3/Src/freertos.c b/Firmware/Board/v3/Src/freertos.c index 3c34849b..ac6c6de9 100644 --- a/Firmware/Board/v3/Src/freertos.c +++ b/Firmware/Board/v3/Src/freertos.c @@ -189,6 +189,7 @@ void MX_FREERTOS_Init(void) { init_deferred_interrupts(); + // Load persistent configuration (or defaults) load_configuration(); /* USER CODE END RTOS_SEMAPHORES */ diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 1a1f331d..427792b3 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -117,7 +117,6 @@ void vApplicationIdleHook(void) { } int odrive_main(void) { - // Load persistent configuration (or defaults) #if HW_VERSION_MAJOR == 3 && HW_VERSION_MINOR >= 3 if (board_config.enable_i2c_instead_of_can) { diff --git a/Firmware/fibre/cpp/include/fibre/protocol.hpp b/Firmware/fibre/cpp/include/fibre/protocol.hpp index 664ba20d..498d5172 100644 --- a/Firmware/fibre/cpp/include/fibre/protocol.hpp +++ b/Firmware/fibre/cpp/include/fibre/protocol.hpp @@ -86,6 +86,7 @@ typedef struct { template::value>> inline size_t write_le(T value, uint8_t* buffer){ + //TODO: add static_assert that this is still a little endian machine std::memcpy(&buffer[0], &value, sizeof(value)); return sizeof(value); } @@ -106,6 +107,7 @@ inline size_t write_le(float value, uint8_t* buffer) { template inline size_t read_le(T* value, const uint8_t* buffer){ + // TODO: add static_assert that this is still a little endian machine std::memcpy(value, buffer, sizeof(*value)); return sizeof(*value); } From b8bcdb8fe36edc9059f09788aa38a9e6fd5e0f1c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 19 Mar 2019 14:13:51 -0700 Subject: [PATCH 101/116] reorder some stuff --- Firmware/Tupfile.lua | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 9f59f34f..994fd746 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -99,6 +99,13 @@ FLAGS += '-mfpu=fpv4-sp-d16' FLAGS += '-mfloat-abi=hard' FLAGS += { '-Wall', '-Wdouble-promotion', '-Wfloat-conversion', '-fdata-sections', '-ffunction-sections'} +-- linker flags +LDFLAGS += '-T'..boarddir..'/STM32F405RGTx_FLASH.ld' +LDFLAGS += '-L'..boarddir..'/Drivers/CMSIS/Lib' -- lib dir +LDFLAGS += '-lc -lm -lnosys -larm_cortexM4lf_math' -- libs +LDFLAGS += '-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' +LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' + -- debug build if tup.getconfig("DEBUG") == "true" then FLAGS += '-g -gdwarf-2' @@ -107,19 +114,7 @@ else OPT += '-O2' end - --- linker flags -LDFLAGS += '-T'..boarddir..'/STM32F405RGTx_FLASH.ld' -LDFLAGS += '-L'..boarddir..'/Drivers/CMSIS/Lib' -- lib dir -LDFLAGS += '-lc -lm -lnosys -larm_cortexM4lf_math' -- libs -LDFLAGS += '-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float -Wl,--cref -Wl,--gc-sections' -LDFLAGS += '-Wl,--undefined=uxTopUsedPriority' - - -- common flags for ASM, C and C++ --- OPT += '-Og' --- OPT += '-O2' --- OPT += '-O0' OPT += '-ffast-math -fno-finite-math-only' tup.append_table(FLAGS, OPT) tup.append_table(LDFLAGS, OPT) From 783ff659f6f744e9e7e8b5289168777f5364c311 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 20 Mar 2019 15:04:11 -0700 Subject: [PATCH 102/116] add set_linear_count function to encoder --- CHANGELOG.md | 1 + Firmware/MotorControl/encoder.hpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0d2f219..a6ec9cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * Communication watchdog feature. +* `encoder.set_linear_count(count)` function. # Releases ## [0.4.8] - 2019-02-25 diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index b3a0e3bd..ecc6d4f1 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -124,7 +124,8 @@ public: make_protocol_property("calib_range", &config_.calib_range), make_protocol_property("idx_search_unidirectional", &config_.idx_search_unidirectional), make_protocol_property("ignore_illegal_hall_state", &config_.ignore_illegal_hall_state) - ) + ), + make_protocol_function("set_linear_count", *this, &Encoder::set_linear_count, "count") ); } }; From 6520261fa66e97e9ee254e30264722c36b9ad0a2 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 22 Mar 2019 22:56:30 -0700 Subject: [PATCH 103/116] add feedback command to ascii docs --- docs/ascii-protocol.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index d782aa79..a0757e2f 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -93,6 +93,16 @@ c motor current This command updates the watchdog timer for the motor. +#### Request feedback +``` +f motor + +response: +pos vel +``` +* `f` for feedback +* `pos` is the encoder position in counts (float) +* `vel` is the encoder velocity in counts/s (float) #### Update motor watchdog ``` From e3a92f214d8fe2dd849f4250d151924ebc490f11 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 22 Mar 2019 22:56:30 -0700 Subject: [PATCH 104/116] add feedback command to ascii docs --- docs/ascii-protocol.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/ascii-protocol.md b/docs/ascii-protocol.md index 0c510fe6..3d20ba58 100644 --- a/docs/ascii-protocol.md +++ b/docs/ascii-protocol.md @@ -86,6 +86,29 @@ c motor current * `motor` is the motor number, `0` or `1`. * `current` is the desired current in A. +This command updates the watchdog timer for the motor. + +#### Request feedback +``` +f motor + +response: +pos vel +``` +* `f` for feedback +* `pos` is the encoder position in counts (float) +* `vel` is the encoder velocity in counts/s (float) + +#### Update motor watchdog +``` +u motor +``` +* `u` for /u/pdate. +* `motor` is the motor number, `0` or `1`. + +This command updates the watchdog timer for the motor, without changing any +setpoints. + #### Parameter reading/writing Not all parameters can be accessed via the ASCII protocol but at least all parameters with float and integer type are supported. From fd03abd4338c8bac53baa58a00049a13ecce6bc0 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 28 Mar 2019 15:24:05 -0700 Subject: [PATCH 105/116] Update commands.md --- docs/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index 32b7769d..d9a93def 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -79,7 +79,7 @@ An upcoming feature will enable automatic tuning. Until then, here is a rough tu * Back down `vel_gain` to 50% of the vibrating value. * Increase `pos_gain` by around 30% per iteration until you see some overshoot. * Back down `pos_gain` until you do not have overshoot anymore. -* The integrator is not easily tuned, nor is it strictly required. Tune at your own discretion. +* The integrator can be set to `2 * bandwidth * vel_gain`, where `bandwidth` is the overall resulting tracking bandwidth of your system. Say your tuning made it track commands with a settling time of 100ms: this means the bandwidth was 1/100ms or 10. In this case you should set the `vel_integrator_gain = 2.0 * 10 * vel_gain`. ## System monitoring commands From 1a2126385d515b4fc6e98d43d5d142d2db16173d Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 28 Mar 2019 15:25:12 -0700 Subject: [PATCH 106/116] Update commands.md --- docs/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index d9a93def..e164b884 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -79,7 +79,7 @@ An upcoming feature will enable automatic tuning. Until then, here is a rough tu * Back down `vel_gain` to 50% of the vibrating value. * Increase `pos_gain` by around 30% per iteration until you see some overshoot. * Back down `pos_gain` until you do not have overshoot anymore. -* The integrator can be set to `2 * bandwidth * vel_gain`, where `bandwidth` is the overall resulting tracking bandwidth of your system. Say your tuning made it track commands with a settling time of 100ms: this means the bandwidth was 1/100ms or 10. In this case you should set the `vel_integrator_gain = 2.0 * 10 * vel_gain`. +* The integrator can be set to `0.5 * bandwidth * vel_gain`, where `bandwidth` is the overall resulting tracking bandwidth of your system. Say your tuning made it track commands with a settling time of 100ms: this means the bandwidth was 1/100ms or 10. In this case you should set the `vel_integrator_gain = 0.5 * 10 * vel_gain`. ## System monitoring commands From 1761272cad99b66385c4e772286ac4abf9ac9da7 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 1 Apr 2019 21:22:27 -0700 Subject: [PATCH 107/116] add encoder offset calib debug var calib_scan_response --- Firmware/MotorControl/encoder.cpp | 14 ++++++-------- Firmware/MotorControl/encoder.hpp | 10 ++++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index d7235655..5a4fe833 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -163,9 +163,7 @@ bool Encoder::run_direction_find() { // TODO: Do the scan with current, not voltage! bool Encoder::run_offset_calibration() { static const float start_lock_duration = 1.0f; - static const float scan_omega = 4.0f * M_PI; - static const float scan_distance = 16.0f * M_PI; - static const int num_steps = (int)(scan_distance / scan_omega * (float)current_meas_hz); + static const int num_steps = (int)(config_.calib_scan_distance / config_.calib_scan_omega * (float)current_meas_hz); // Require index found if enabled if (config_.use_index && !index_found_) { @@ -202,7 +200,7 @@ bool Encoder::run_offset_calibration() { // scan forward i = 0; axis_->run_control_loop([&](){ - float phase = wrap_pm_pi(scan_distance * (float)i / (float)num_steps - scan_distance / 2.0f); + float phase = wrap_pm_pi(config_.calib_scan_distance * (float)i / (float)num_steps - config_.calib_scan_distance / 2.0f); float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); float v_beta = voltage_magnitude * our_arm_sin_f32(phase); if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) @@ -232,9 +230,9 @@ bool Encoder::run_offset_calibration() { //TODO avoid recomputing elec_rad_per_enc every time // Check CPR float elec_rad_per_enc = axis_->motor_.config_.pole_pairs * 2 * M_PI * (1.0f / (float)(config_.cpr)); - float expected_encoder_delta = scan_distance / elec_rad_per_enc; - float actual_encoder_delta_abs = fabsf(shadow_count_-init_enc_val); - if(fabsf(actual_encoder_delta_abs - expected_encoder_delta)/expected_encoder_delta > config_.calib_range) + float expected_encoder_delta = config_.calib_scan_distance / elec_rad_per_enc; + calib_scan_response_ = fabsf(shadow_count_-init_enc_val); + if(fabsf(calib_scan_response_ - expected_encoder_delta)/expected_encoder_delta > config_.calib_range) { set_error(ERROR_CPR_OUT_OF_RANGE); return false; @@ -243,7 +241,7 @@ bool Encoder::run_offset_calibration() { // scan backwards i = 0; axis_->run_control_loop([&](){ - float phase = wrap_pm_pi(-scan_distance * (float)i / (float)num_steps + scan_distance / 2.0f); + float phase = wrap_pm_pi(-config_.calib_scan_distance * (float)i / (float)num_steps + config_.calib_scan_distance / 2.0f); float v_alpha = voltage_magnitude * our_arm_cos_f32(phase); float v_beta = voltage_magnitude * our_arm_sin_f32(phase); if (!axis_->motor_.enqueue_voltage_timings(v_alpha, v_beta)) diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index ecc6d4f1..c2d32841 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -37,6 +37,8 @@ public: float offset_float = 0.0f; // Sub-count phase alignment offset bool enable_phase_interpolation = true; // Use velocity to interpolate inside the count state float calib_range = 0.02f; // Accuracy required to pass encoder cpr check + float calib_scan_distance = 16.0f * M_PI; // rad electrical + float calib_scan_omega = 4.0f * M_PI; // rad/s electrical float bandwidth = 1000.0f; bool find_idx_on_lockin_only = false; // Only be sensitive during lockin scan constant vel state bool idx_search_unidirectional = false; // Only allow index search in known direction @@ -83,6 +85,7 @@ public: float vel_estimate_ = 0.0f; // [count/s] float pll_kp_ = 0.0f; // [count/s / count] float pll_ki_ = 0.0f; // [(count/s^2) / count] + float calib_scan_response_ = 0.0f; // debug report from offset calib int16_t tim_cnt_sample_ = 0; // // Updated by low_level pwm_adc_cb @@ -99,11 +102,12 @@ public: make_protocol_property("shadow_count", &shadow_count_), make_protocol_property("count_in_cpr", &count_in_cpr_), make_protocol_property("interpolation", &interpolation_), - make_protocol_property("phase", &phase_), + make_protocol_ro_property("phase", &phase_), make_protocol_property("pos_estimate", &pos_estimate_), make_protocol_property("pos_cpr", &pos_cpr_), - make_protocol_property("hall_state", &hall_state_), + make_protocol_ro_property("hall_state", &hall_state_), make_protocol_property("vel_estimate", &vel_estimate_), + make_protocol_ro_property("calib_scan_response", &calib_scan_response_), // make_protocol_property("pll_kp", &pll_kp_), // make_protocol_property("pll_ki", &pll_ki_), make_protocol_object("config", @@ -122,6 +126,8 @@ public: make_protocol_property("bandwidth", &config_.bandwidth, [](void* ctx) { static_cast(ctx)->update_pll_gains(); }, this), make_protocol_property("calib_range", &config_.calib_range), + make_protocol_property("calib_scan_distance", &config_.calib_scan_distance), + make_protocol_property("calib_scan_omega", &config_.calib_scan_omega), make_protocol_property("idx_search_unidirectional", &config_.idx_search_unidirectional), make_protocol_property("ignore_illegal_hall_state", &config_.ignore_illegal_hall_state) ), From 163c2a7bad78fdfb6945cb3d8aa72449382f17a5 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 2 Apr 2019 21:38:29 -0700 Subject: [PATCH 108/116] update changelog --- CHANGELOG.md | 2 ++ tools/odrive/utils.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ec9cac..cae7884a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added * Communication watchdog feature. * `encoder.set_linear_count(count)` function. +* Configurable encoder offset calibration distance and speed:`calib_scan_distance` and `calib_scan_omega` +* Encoder offset calibration debug variable `calib_scan_response` # Releases ## [0.4.8] - 2019-02-25 diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index e74376cf..13fdfa9b 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -198,7 +198,7 @@ def setup_udev_rules(logger): if os.getuid() != 0: logger.warn("you should run this as root, otherwise it will probably not work") with open('/etc/udev/rules.d/91-odrive.rules', 'w') as file: - file.write('SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d3[0-9]", MODE="0666"\n') + file.write('SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d3[0-9]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"\n') subprocess.check_call(["udevadm", "control", "--reload-rules"]) subprocess.check_call(["udevadm", "trigger"]) logger.info('udev rules configured successfully') From 035c899776d19419f90438e96aca62c0a234034e Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 2 Apr 2019 21:40:13 -0700 Subject: [PATCH 109/116] make all errors to setting up udev rules during install non-fatal --- tools/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/setup.py b/tools/setup.py index b2cfeb74..ee76c438 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -95,7 +95,7 @@ if not creating_package: from fibre.utils import Logger try: odrive.utils.setup_udev_rules(Logger()) - except PermissionError: + except Exception: print("Warning: could not set up udev rules. Run `sudo odrivetool udev-setup` to try again.") try: From f750de0bbc4990cd46f4bf9834c0fd21ea0063b8 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 3 Apr 2019 20:23:26 -0700 Subject: [PATCH 110/116] move setup_udev_rules from utils to version to avoid importing heavy utils.py during install --- tools/odrive/utils.py | 11 ----------- tools/odrive/version.py | 13 +++++++++++++ tools/odrivetool | 2 +- tools/setup.py | 4 ++-- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 13fdfa9b..f5ce0c7f 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -192,17 +192,6 @@ def usb_burn_in_test(get_var_callback, cancellation_token): print("read {} values".format(i)) threading.Thread(target=fetch_data, daemon=True).start() -def setup_udev_rules(logger): - if platform.system() != 'Linux': - logger.error("This command only makes sense on Linux") - if os.getuid() != 0: - logger.warn("you should run this as root, otherwise it will probably not work") - with open('/etc/udev/rules.d/91-odrive.rules', 'w') as file: - file.write('SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d3[0-9]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"\n') - subprocess.check_call(["udevadm", "control", "--reload-rules"]) - subprocess.check_call(["udevadm", "trigger"]) - logger.info('udev rules configured successfully') - def yes_no_prompt(question, default=None): if default is None: question += " [y/n] " diff --git a/tools/odrive/version.py b/tools/odrive/version.py index 5a2827a3..a96e9b4e 100644 --- a/tools/odrive/version.py +++ b/tools/odrive/version.py @@ -3,6 +3,7 @@ import re import subprocess import os import sys +import platform def version_str_to_tuple(version_string): """ @@ -78,3 +79,15 @@ if __name__ == '__main__': args.output.write('#define FW_VERSION_MINOR {}\n'.format(minor)) args.output.write('#define FW_VERSION_REVISION {}\n'.format(revision)) args.output.write('#define FW_VERSION_UNRELEASED {}\n'.format(1 if unreleased else 0)) + +def setup_udev_rules(logger): + if platform.system() != 'Linux': + logger.error("This command only makes sense on Linux") + return + if os.getuid() != 0: + logger.warn("you should run this as root, otherwise it will probably not work") + with open('/etc/udev/rules.d/91-odrive.rules', 'w') as file: + file.write('SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d3[0-9]", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1"\n') + subprocess.check_call(["udevadm", "control", "--reload-rules"]) + subprocess.check_call(["udevadm", "trigger"]) + logger.info('udev rules configured successfully') diff --git a/tools/odrivetool b/tools/odrivetool index c6632e90..5c3af5b7 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -177,7 +177,7 @@ try: rate_test(my_odrive) elif args.command == 'udev-setup': - from odrive.utils import setup_udev_rules + from odrive.version import setup_udev_rules setup_udev_rules(logger) elif args.command == 'generate-code': diff --git a/tools/setup.py b/tools/setup.py index ee76c438..986d9d0d 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -91,10 +91,9 @@ if creating_package: if not creating_package: import platform if platform.system() == 'Linux': - import odrive.utils from fibre.utils import Logger try: - odrive.utils.setup_udev_rules(Logger()) + odrive.version.setup_udev_rules(Logger()) except Exception: print("Warning: could not set up udev rules. Run `sudo odrivetool udev-setup` to try again.") @@ -117,6 +116,7 @@ try: 'requests', # Used to by DFU to load firmware files 'IntelHex', # Used to by DFU to download firmware from github 'matplotlib', # Required to run the liveplotter + 'monotonic', # For compatibility with older python versions 'pywin32 >= 222; platform_system == "Windows"' # Required for fancy terminal features on Windows ], package_data={'': ['version.txt']}, From 7c9215a5db81c7d33234c79dee0d06f5099c0920 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 5 Apr 2019 19:11:38 -0700 Subject: [PATCH 111/116] fix instructions in sampler.py --- Firmware/sampler.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Firmware/sampler.py b/Firmware/sampler.py index 08dc126e..75ebbe55 100644 --- a/Firmware/sampler.py +++ b/Firmware/sampler.py @@ -1,9 +1,11 @@ #!/usr/bin/python2 # run openocd (0.9.0) with : -# $ openocd -f stlink-v2-1.cfg -f stm32f4x.cfg &> /dev/null" +# $ openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg &> /dev/null & # then run # $ python2 sampler.py path_to_myelf_with_symbols +# ctrl-c to stop sampling. +# To terminate the openocd session, enter command "fg" then do ctrl-c. import sys import time @@ -111,10 +113,10 @@ if __name__ == '__main__': cur = time.time() if cur - start > 1.0: - tmp = sorted(countmap.items(), key=operator.itemgetter(1), reverse=True) + tmp = sorted(countmap.items(), key=operator.itemgetter(1)) #, reverse=True) for k, v in tmp: - # print('{:05.2f}% {}'.format((v * 100.) / total, k)) - print('{:06.2f} clocks : {}'.format((v * 8192) / total, k)) + print('{:05.2f}% {}'.format((v * 100.) / total, k)) + # print('{:06.2f} clocks : {}'.format((v * 10500) / total, k)) start = cur print('{} Samples'.format(total)) print('') From 3484b10ecea031ff12873cec492818a01a98a54a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 12 Apr 2019 13:39:00 -0700 Subject: [PATCH 112/116] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cae7884a..ace68f99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Please add a note of your changes below this heading if you make a Pull Request. * Configurable encoder offset calibration distance and speed:`calib_scan_distance` and `calib_scan_omega` * Encoder offset calibration debug variable `calib_scan_response` +### Fixed +* Encoder index interrupts now disabled when not searching + # Releases ## [0.4.8] - 2019-02-25 ### Added From e3946f27e034946efeb4ce9e319c3afa98cec41e Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 15 Apr 2019 20:02:10 -0700 Subject: [PATCH 113/116] add setup_hall_as_index.py --- CHANGELOG.md | 1 + Firmware/MotorControl/encoder.cpp | 6 +-- tools/setup_hall_as_index.py | 86 +++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 tools/setup_hall_as_index.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cae7884a..a0e92806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Please add a note of your changes below this heading if you make a Pull Request. * `encoder.set_linear_count(count)` function. * Configurable encoder offset calibration distance and speed:`calib_scan_distance` and `calib_scan_omega` * Encoder offset calibration debug variable `calib_scan_response` +* Script to enable using a hall signal as index edge. # Releases ## [0.4.8] - 2019-02-25 diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 5a4fe833..95dc91f9 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -60,12 +60,10 @@ void Encoder::enc_index_cb() { } void Encoder::set_idx_subscribe(bool override_enable) { - if (override_enable || (config_.use_index && !config_.find_idx_on_lockin_only)) { + if (config_.use_index && (override_enable || !config_.find_idx_on_lockin_only)) { GPIO_subscribe(hw_config_.index_port, hw_config_.index_pin, GPIO_PULLDOWN, enc_index_cb_wrapper, this); - } - - if (!config_.use_index || config_.find_idx_on_lockin_only) { + } else if (!config_.use_index || config_.find_idx_on_lockin_only) { GPIO_unsubscribe(hw_config_.index_port, hw_config_.index_pin); } } diff --git a/tools/setup_hall_as_index.py b/tools/setup_hall_as_index.py new file mode 100644 index 00000000..87ec245f --- /dev/null +++ b/tools/setup_hall_as_index.py @@ -0,0 +1,86 @@ + +import odrive +from odrive.enums import * +import time + +print("Finding an odrive...") +odrv = odrive.find_any() + +axes = [odrv.axis0, odrv.axis1]; +# axes = [odrv.axis0]; + +print("Setting config...") +# Settings to protect battery +odrv.config.dc_bus_overvoltage_trip_level = 14.8 +odrv.config.dc_bus_undervoltage_trip_level = 8.0 +odrv.config.brake_resistance = 0 +for ax in axes: + ax.motor.config.requested_current_range = 25 + ax.motor.config.calibration_current = 10 + ax.motor.config.current_lim = 10 + ax.motor.config.resistance_calib_max_voltage = 4 + ax.motor.config.pole_pairs = 10 + + ax.encoder.config.cpr = 4096 + ax.encoder.config.use_index = True + ax.encoder.config.find_idx_on_lockin_only = True + ax.encoder.config.idx_search_unidirectional = True + + ax.controller.config.control_mode = CTRL_MODE_VELOCITY_CONTROL + ax.controller.config.vel_limit = 10000 + ax.controller.config.vel_gain = 0.002205736003816127 + ax.controller.config.vel_integrator_gain = 0.022057360038161278 + ax.controller.config.pos_gain = 26 + + ax.config.lockin.current = 10 + ax.config.lockin.vel = 15 + ax.config.lockin.accel = 10 + ax.config.lockin.finish_distance = 30 + +def wait_and_exit_on_error(ax): + while ax.current_state != AXIS_STATE_IDLE: + time.sleep(0.1) + if ax.error != errors.axis.ERROR_NONE: + odrive.utils.dump_errors(odrv) + quit() + +for axnum, ax in enumerate(axes): + print("Calibrating motor {}...".format(axnum)) + ax.requested_state = AXIS_STATE_MOTOR_CALIBRATION + wait_and_exit_on_error(ax) + + print("Checking motor {} direction...".format(axnum)) + ax.requested_state = AXIS_STATE_ENCODER_DIR_FIND + wait_and_exit_on_error(ax) + print(" Direction is {}".format(ax.motor.config.direction)) + + print("Searching for index on motor {}...".format(axnum)) + ax.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH + wait_and_exit_on_error(ax) + if (not ax.encoder.index_found): + print("Failed finding index! Quitting.") + quit() + + print("Calibrating encoder offset on motor {}...".format(axnum)) + ax.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION + wait_and_exit_on_error(ax) + if (not ax.encoder.is_ready): + print("Failed to calibrate encoder! Quitting") + quit() + + # If we get here there were no errors, so let's commit the values + ax.motor.config.pre_calibrated = True + ax.encoder.config.pre_calibrated = True + + # Uncomment this if you wish to automatically run index search and closed loop control on boot + # ax.config.startup_encoder_index_search = True + # ax.config.startup_closed_loop_control = True + +#Everything should be good to go here, so let's save and reboot +print("") +print("All operations successful!") +odrv.save_configuration() +try: + odrv.reboot() +except odrive.fibre.ChannelBrokenException: + pass From a26fca325cd9c0b96b07320a30c0cecb43a0afa9 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 15 Apr 2019 20:55:55 -0700 Subject: [PATCH 114/116] add option to flip index search direction --- tools/setup_hall_as_index.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/tools/setup_hall_as_index.py b/tools/setup_hall_as_index.py index 87ec245f..bc1ad866 100644 --- a/tools/setup_hall_as_index.py +++ b/tools/setup_hall_as_index.py @@ -1,13 +1,17 @@ import odrive +from odrive.utils import dump_errors from odrive.enums import * import time print("Finding an odrive...") odrv = odrive.find_any() -axes = [odrv.axis0, odrv.axis1]; -# axes = [odrv.axis0]; +# axes = [odrv.axis0, odrv.axis1]; +axes = [odrv.axis0]; + +flip_index_search_direction = False +save_and_reboot = False print("Setting config...") # Settings to protect battery @@ -33,6 +37,7 @@ for ax in axes: ax.controller.config.pos_gain = 26 ax.config.lockin.current = 10 + ax.config.lockin.ramp_distance = 3.14 ax.config.lockin.vel = 15 ax.config.lockin.accel = 10 ax.config.lockin.finish_distance = 30 @@ -41,8 +46,8 @@ def wait_and_exit_on_error(ax): while ax.current_state != AXIS_STATE_IDLE: time.sleep(0.1) if ax.error != errors.axis.ERROR_NONE: - odrive.utils.dump_errors(odrv) - quit() + dump_errors(odrv, True) + exit() for axnum, ax in enumerate(axes): print("Calibrating motor {}...".format(axnum)) @@ -54,19 +59,24 @@ for axnum, ax in enumerate(axes): wait_and_exit_on_error(ax) print(" Direction is {}".format(ax.motor.config.direction)) + if flip_index_search_direction: + ax.config.lockin.ramp_distance = -ax.config.lockin.ramp_distance + ax.config.lockin.vel = -ax.config.lockin.vel + ax.config.lockin.accel = -ax.config.lockin.accel + print("Searching for index on motor {}...".format(axnum)) ax.requested_state = AXIS_STATE_ENCODER_INDEX_SEARCH wait_and_exit_on_error(ax) if (not ax.encoder.index_found): print("Failed finding index! Quitting.") - quit() + exit() print("Calibrating encoder offset on motor {}...".format(axnum)) ax.requested_state = AXIS_STATE_ENCODER_OFFSET_CALIBRATION wait_and_exit_on_error(ax) if (not ax.encoder.is_ready): print("Failed to calibrate encoder! Quitting") - quit() + exit() # If we get here there were no errors, so let's commit the values ax.motor.config.pre_calibrated = True @@ -79,8 +89,9 @@ for axnum, ax in enumerate(axes): #Everything should be good to go here, so let's save and reboot print("") print("All operations successful!") -odrv.save_configuration() -try: - odrv.reboot() -except odrive.fibre.ChannelBrokenException: - pass +if save_and_reboot: + odrv.save_configuration() + try: + odrv.reboot() + except odrive.fibre.ChannelBrokenException: + pass From 9bb94e5157c64ce6ce27e8699deb5ce4c03da010 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 15 Apr 2019 20:57:35 -0700 Subject: [PATCH 115/116] save and reboot true by default --- tools/setup_hall_as_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/setup_hall_as_index.py b/tools/setup_hall_as_index.py index bc1ad866..c01de4ff 100644 --- a/tools/setup_hall_as_index.py +++ b/tools/setup_hall_as_index.py @@ -11,7 +11,7 @@ odrv = odrive.find_any() axes = [odrv.axis0]; flip_index_search_direction = False -save_and_reboot = False +save_and_reboot = True print("Setting config...") # Settings to protect battery From 7eb4f2a58448df771601e49df9f9264cd42bd17e Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 22 Apr 2019 20:44:08 -0700 Subject: [PATCH 116/116] add STM32 protection bits unlock command to makefile --- Firmware/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Firmware/Makefile b/Firmware/Makefile index 263ba433..91754f5a 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -40,6 +40,10 @@ erase: erase_config: $(OPENOCD) -c init -c reset\ halt -c flash\ erase_address\ 0x80C0000\ 0x40000 -c reset\ init -c reset\ run -c exit +# Sometimes the STM32 will get it's protection bits set for unknown reasons. Unlock it with this command +unlock: + $(OPENOCD) -c init -c reset\ halt -c stm32f2x\ unlock\ 0 + # The one-time programmable memory stores the board version # has the following format: # - OTP format version (0xFE: version 1)