From ec45bdd2977ecddbf35aa5d312477b12224ee1f2 Mon Sep 17 00:00:00 2001 From: konelav Date: Sat, 20 Dec 2025 09:32:49 -0500 Subject: [PATCH 1/3] Make max_dc_calib coefficient (relative to maximum current) configurable via motor.config.max_dc_calib_koef (default value 0.1f) --- Firmware/MotorControl/motor.cpp | 2 +- Firmware/MotorControl/motor.hpp | 1 + Firmware/odrive-interface.yaml | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 7c76f5d56..83f088df6 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -329,7 +329,7 @@ bool Motor::setup() { // Clip all current control to actual usable range max_allowed_current_ = max_unity_gain_current * phase_current_rev_gain_; - max_dc_calib_ = 0.1f * max_allowed_current_; + max_dc_calib_ = config_.max_dc_calib_koef * max_allowed_current_; if (!gate_driver_.init()) return false; diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index 7091e2538..a4587ce41 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -48,6 +48,7 @@ class Motor : public ODriveIntf::MotorIntf { float I_leak_max = 0.1f; float dc_calib_tau = 0.2f; + float max_dc_calib_koef = 0.1f; // custom property setters Motor* parent = nullptr; diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 34cb09d06..72b5c2b9e 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -935,6 +935,10 @@ interfaces: Note that this feature is only works on devices with three current sensors (e.g. ODrive v4). dc_calib_tau: float32 + max_dc_calib_koef: + type: float32 + status: experimental + brief: Max current deviation from zero-level, relative to maximum allowed current ODrive.Oscilloscope: c_is_class: True From eca6f489ab23ce97ac6724b3a02cdba3ff613270 Mon Sep 17 00:00:00 2001 From: konelav Date: Sat, 20 Dec 2025 09:36:16 -0500 Subject: [PATCH 2/3] Add configurable `deadzone' for endpoints (both PWM and ADC). New attributes for ODrive.Endpoint: deadzone_min, deadzone_max, deadzone_value. Wors for `fractions' from 0.0 to 1.0 as follows: - when deadzone_min >= deadzone_max (default), result is linear interpolation between (0.0, min) and (1.0, max) (old behaviour); - when fraction < deadzone_min, result is linear interpolation between (0.0, min) and (deadzone_min, deadzone_value); - when fraction > deadzone_max, result is linear interpolation between (deadzone_max, deadzone_value) and (1.0, max); - otherwise, result is deadzone_value. --- Firmware/MotorControl/low_level.cpp | 9 +++++-- Firmware/MotorControl/odrive_main.h | 3 +++ Firmware/MotorControl/pwm_input.cpp | 10 ++++++-- Firmware/MotorControl/utils.cpp | 39 +++++++++++++++++++++++++++++ Firmware/MotorControl/utils.hpp | 5 ++++ Firmware/odrive-interface.yaml | 3 +++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 20f207a87..2f561cca1 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -387,8 +387,13 @@ void update_brake_current() { static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio) { - float fraction = get_adc_voltage(get_gpio(gpio)) / 3.3f; - float value = map->min + (fraction * (map->max - map->min)); + float fraction = get_adc_relative_voltage(get_gpio(gpio)); + float value = apply_deadzone(fraction, + 0.0, map->min, + 1.0, map->max, + map->deadzone_min, map->deadzone_value, + map->deadzone_max, map->deadzone_value + ); fibre::set_endpoint_from_float(map->endpoint, value); } diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index 4ddba244b..fd421af0c 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -57,6 +57,9 @@ struct PWMMapping_t { endpoint_ref_t endpoint = {0, 0}; float min = 0; float max = 0; + float deadzone_min = 0; + float deadzone_max = 0; + float deadzone_value = 0; }; // @brief general user configurable board configuration diff --git a/Firmware/MotorControl/pwm_input.cpp b/Firmware/MotorControl/pwm_input.cpp index 176b7eb23..6677e8536 100644 --- a/Firmware/MotorControl/pwm_input.cpp +++ b/Firmware/MotorControl/pwm_input.cpp @@ -1,4 +1,6 @@ +#include + #include "pwm_input.hpp" #include "odrive_main.h" @@ -39,8 +41,12 @@ void handle_pulse(int channel, uint32_t high_time) { if (high_time > PWM_MAX_HIGH_TIME) high_time = PWM_MAX_HIGH_TIME; float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME); - float value = odrv.config_.pwm_mappings[channel].min + - (fraction * (odrv.config_.pwm_mappings[channel].max - odrv.config_.pwm_mappings[channel].min)); + float value = apply_deadzone(fraction, + 0.0, odrv.config_.pwm_mappings[channel].min, + 1.0, odrv.config_.pwm_mappings[channel].max, + odrv.config_.pwm_mappings[channel].deadzone_min, odrv.config_.pwm_mappings[channel].deadzone_value, + odrv.config_.pwm_mappings[channel].deadzone_max, odrv.config_.pwm_mappings[channel].deadzone_value + ); fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[channel].endpoint, value); } diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index f41d9813b..9a72bd4cf 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -188,3 +188,42 @@ void delay_us(uint32_t us) } } +float apply_deadzone(float fraction, + float fraction_min, float output_min, + float fraction_max, float output_max, + float deadzone_min, float deadzone_min_output, + float deadzone_max, float deadzone_max_output) { + float x1, y1, x2, y2; + float x = fraction; + + if (fraction <= fraction_min) + return output_min; + else if (fraction >= fraction_max) + return output_max; + else if (!(fraction_min <= deadzone_min && deadzone_min < deadzone_max && deadzone_max <= fraction_max)) { + x1 = fraction_min; + x2 = fraction_max; + y1 = output_min; + y2 = output_max; + } + else if (x < deadzone_min) { + x1 = fraction_min; + x2 = deadzone_min; + y1 = output_min; + y2 = deadzone_min_output; + } + else if (x <= deadzone_max) { + x1 = deadzone_min; + x2 = deadzone_max; + y1 = deadzone_min_output; + y2 = deadzone_max_output; + } + else { + x1 = deadzone_max; + x2 = fraction_max; + y1 = deadzone_max_output; + y2 = output_max; + } + + return y1 + (x - x1) / (x2 - x1) * (y2 - y1); +} diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 67c1fceac..2438056f2 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -71,6 +71,11 @@ uint32_t timeout_to_deadline(uint32_t timeout_ms); int is_in_the_future(uint32_t time_ms); uint32_t micros(void); void delay_us(uint32_t us); +float apply_deadzone(float fraction, + float fraction_min, float output_min, + float fraction_max, float output_max, + float deadzone_min, float deadzone_min_output, + float deadzone_max, float deadzone_max_output); extern "C" { float our_arm_sin_f32(float x); diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 72b5c2b9e..887cc0753 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -432,6 +432,9 @@ interfaces: endpoint: endpoint_ref min: float32 max: float32 + deadzone_min: float32 + deadzone_max: float32 + deadzone_value: float32 ODrive.Axis: c_is_class: True From 6b6904d6dacd7d378467b616f4796a66fbf2858b Mon Sep 17 00:00:00 2001 From: konelav Date: Wed, 7 Jan 2026 15:24:19 -0500 Subject: [PATCH 3/3] deadzone renamed to deadband to be compatible with fw v0.6.12 --- Firmware/MotorControl/low_level.cpp | 7 ++-- Firmware/MotorControl/odrive_main.h | 7 ++-- Firmware/MotorControl/pwm_input.cpp | 7 ++-- Firmware/MotorControl/utils.cpp | 53 ++++++++++++++++------------- Firmware/MotorControl/utils.hpp | 11 +++--- Firmware/odrive-interface.yaml | 7 ++-- 6 files changed, 52 insertions(+), 40 deletions(-) diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 2f561cca1..d677198e5 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -388,11 +388,12 @@ void update_brake_current() { static void update_analog_endpoint(const struct PWMMapping_t *map, int gpio) { float fraction = get_adc_relative_voltage(get_gpio(gpio)); - float value = apply_deadzone(fraction, + float value = apply_deadband(fraction, 0.0, map->min, 1.0, map->max, - map->deadzone_min, map->deadzone_value, - map->deadzone_max, map->deadzone_value + map->deadband_enable, + map->deadband_start, map->deadband_level, + map->deadband_end, map->deadband_level ); fibre::set_endpoint_from_float(map->endpoint, value); } diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index fd421af0c..0243d8693 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -57,9 +57,10 @@ struct PWMMapping_t { endpoint_ref_t endpoint = {0, 0}; float min = 0; float max = 0; - float deadzone_min = 0; - float deadzone_max = 0; - float deadzone_value = 0; + bool deadband_enable = false; + float deadband_start = 0; + float deadband_end = 1; + float deadband_level = 0; }; // @brief general user configurable board configuration diff --git a/Firmware/MotorControl/pwm_input.cpp b/Firmware/MotorControl/pwm_input.cpp index 6677e8536..0a8cb6934 100644 --- a/Firmware/MotorControl/pwm_input.cpp +++ b/Firmware/MotorControl/pwm_input.cpp @@ -41,11 +41,12 @@ void handle_pulse(int channel, uint32_t high_time) { if (high_time > PWM_MAX_HIGH_TIME) high_time = PWM_MAX_HIGH_TIME; float fraction = (float)(high_time - PWM_MIN_HIGH_TIME) / (float)(PWM_MAX_HIGH_TIME - PWM_MIN_HIGH_TIME); - float value = apply_deadzone(fraction, + float value = apply_deadband(fraction, 0.0, odrv.config_.pwm_mappings[channel].min, 1.0, odrv.config_.pwm_mappings[channel].max, - odrv.config_.pwm_mappings[channel].deadzone_min, odrv.config_.pwm_mappings[channel].deadzone_value, - odrv.config_.pwm_mappings[channel].deadzone_max, odrv.config_.pwm_mappings[channel].deadzone_value + odrv.config_.pwm_mappings[channel].deadband_enable, + odrv.config_.pwm_mappings[channel].deadband_start, odrv.config_.pwm_mappings[channel].deadband_level, + odrv.config_.pwm_mappings[channel].deadband_end, odrv.config_.pwm_mappings[channel].deadband_level ); fibre::set_endpoint_from_float(odrv.config_.pwm_mappings[channel].endpoint, value); diff --git a/Firmware/MotorControl/utils.cpp b/Firmware/MotorControl/utils.cpp index 9a72bd4cf..ae3b6659a 100644 --- a/Firmware/MotorControl/utils.cpp +++ b/Firmware/MotorControl/utils.cpp @@ -188,42 +188,49 @@ void delay_us(uint32_t us) } } -float apply_deadzone(float fraction, - float fraction_min, float output_min, - float fraction_max, float output_max, - float deadzone_min, float deadzone_min_output, - float deadzone_max, float deadzone_max_output) { +float apply_deadband(float fraction, + float fraction_min, float level_min, + float fraction_max, float level_max, + bool deadband_enable, + float deadband_start, float deadband_start_level, + float deadband_end, float deadband_end_level) { float x1, y1, x2, y2; float x = fraction; if (fraction <= fraction_min) - return output_min; + return level_min; else if (fraction >= fraction_max) - return output_max; - else if (!(fraction_min <= deadzone_min && deadzone_min < deadzone_max && deadzone_max <= fraction_max)) { + return level_max; + else if (!(deadband_enable && + fraction_min <= deadband_start && + deadband_start <= deadband_end && + deadband_end <= fraction_max)) { x1 = fraction_min; x2 = fraction_max; - y1 = output_min; - y2 = output_max; + y1 = level_min; + y2 = level_max; } - else if (x < deadzone_min) { + else if (x < deadband_start) { x1 = fraction_min; - x2 = deadzone_min; - y1 = output_min; - y2 = deadzone_min_output; + x2 = deadband_start; + y1 = level_min; + y2 = deadband_start_level; } - else if (x <= deadzone_max) { - x1 = deadzone_min; - x2 = deadzone_max; - y1 = deadzone_min_output; - y2 = deadzone_max_output; + else if (x <= deadband_end) { + x1 = deadband_start; + x2 = deadband_end; + y1 = deadband_start_level; + y2 = deadband_end_level; } else { - x1 = deadzone_max; + x1 = deadband_end; x2 = fraction_max; - y1 = deadzone_max_output; - y2 = output_max; + y1 = deadband_end_level; + y2 = level_max; } - + + if (x1 >= x2) + return (y1 + y2) * 0.5f; + return y1 + (x - x1) / (x2 - x1) * (y2 - y1); } diff --git a/Firmware/MotorControl/utils.hpp b/Firmware/MotorControl/utils.hpp index 2438056f2..3b088efad 100644 --- a/Firmware/MotorControl/utils.hpp +++ b/Firmware/MotorControl/utils.hpp @@ -71,11 +71,12 @@ uint32_t timeout_to_deadline(uint32_t timeout_ms); int is_in_the_future(uint32_t time_ms); uint32_t micros(void); void delay_us(uint32_t us); -float apply_deadzone(float fraction, - float fraction_min, float output_min, - float fraction_max, float output_max, - float deadzone_min, float deadzone_min_output, - float deadzone_max, float deadzone_max_output); +float apply_deadband(float fraction, + float fraction_min, float level_min, + float fraction_max, float level_max, + bool deadband_enable, + float deadband_start, float deadband_start_level, + float deadband_end, float deadband_end_level); extern "C" { float our_arm_sin_f32(float x); diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 887cc0753..67985366d 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -432,9 +432,10 @@ interfaces: endpoint: endpoint_ref min: float32 max: float32 - deadzone_min: float32 - deadzone_max: float32 - deadzone_value: float32 + deadband_enable: bool + deadband_start: float32 + deadband_end: float32 + deadband_level: float32 ODrive.Axis: c_is_class: True