diff --git a/firmware/platformio.ini b/firmware/platformio.ini index 9e8184b..d807cc1 100644 --- a/firmware/platformio.ini +++ b/firmware/platformio.ini @@ -20,8 +20,9 @@ monitor_flags = lib_deps = TFT_eSPI@2.3.59 fastled/FastLED @ ^3.4.0 - askuric/Simple FOC @ ^2.2 - infineon/TLV493D-Magnetic-Sensor @ ^1.0.3 + askuric/Simple FOC @ ^2.2 + infineon/TLV493D-Magnetic-Sensor @ ^1.0.3 + bxparks/AceButton @ ^1.9.1 build_flags = -Os diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp index 8a92251..5f05690 100644 --- a/firmware/src/main.cpp +++ b/firmware/src/main.cpp @@ -1,12 +1,15 @@ +#include #include -#include #include #include +#include #include "display_task.h" #include "motor_task.h" #include "tlv_sensor.h" +using namespace ace_button; + DisplayTask display_task = DisplayTask(1); MotorTask motor_task = MotorTask(0, display_task); diff --git a/firmware/src/motor_task.cpp b/firmware/src/motor_task.cpp index b062bc8..52d6dfd 100644 --- a/firmware/src/motor_task.cpp +++ b/firmware/src/motor_task.cpp @@ -3,13 +3,20 @@ #include "motor_task.h" #include "tlv_sensor.h" + template T CLAMP(const T& value, const T& low, const T& high) { return value < low ? low : (value > high ? high : value); } static const float DEAD_ZONE_DETENT_PERCENT = 0.2; -static const float DEAD_ZONE_RAD = 3 * _PI / 180; +static const float DEAD_ZONE_RAD = 1 * _PI / 180; + +static const float IDLE_VELOCITY_EWMA_ALPHA = 0.001; +static const float IDLE_VELOCITY_RAD_PER_SEC = 0.05; +static const uint32_t IDLE_CORRECTION_DELAY_MILLIS = 500; +static const float IDLE_CORRECTION_MAX_ANGLE_RAD = 5 * PI / 180; +static const float IDLE_CORRECTION_RATE_ALPHA = 0.0005; MotorTask::MotorTask(const uint8_t task_core, DisplayTask& display_task) : Task{"Motor", 8192, 1, task_core}, display_task_(display_task) { @@ -66,7 +73,7 @@ void MotorTask::run() { motor.useMonitoring(Serial); motor.monitor_downsample = 0; // disable monitor at first - optional - disableCore0WDT(); + // disableCore0WDT(); float current_detent_center = motor.shaft_angle; @@ -74,11 +81,35 @@ void MotorTask::run() { int max = 255; int value = 0; + float idle_check_velocity_ewma = 0; + uint32_t last_idle_start = 0; + uint32_t last_debug = 0; while (1) { motor.loopFOC(); + idle_check_velocity_ewma = motor.shaft_velocity * IDLE_VELOCITY_EWMA_ALPHA + idle_check_velocity_ewma * (1 - IDLE_VELOCITY_EWMA_ALPHA); + if (fabsf(idle_check_velocity_ewma) > IDLE_VELOCITY_RAD_PER_SEC) { + last_idle_start = 0; + } else { + if (last_idle_start == 0) { + last_idle_start = millis(); + } + } + + // If we are not moving and we're close to the center (but not exactly there), slowly adjust the centerpoint to match the current position + if (last_idle_start > 0 && millis() - last_idle_start > IDLE_CORRECTION_DELAY_MILLIS && fabsf(motor.shaft_angle - current_detent_center) < IDLE_CORRECTION_MAX_ANGLE_RAD) { + current_detent_center = motor.shaft_angle * IDLE_CORRECTION_RATE_ALPHA + current_detent_center * (1 - IDLE_CORRECTION_RATE_ALPHA); + if (millis() - last_debug > 100) { + last_debug = millis(); + Serial.print("Moving detent center. "); + Serial.print(current_detent_center); + Serial.print(" "); + Serial.println(motor.shaft_angle); + } + } + if (fabs(detents) < 0.01) { motor.move(0); display_task_.set_angle(motor.shaft_angle); @@ -86,11 +117,6 @@ void MotorTask::run() { } else { float detent_width = 2*PI/detents; - // if (millis() - last > 500) { - // last = millis(); - // current_detent_center += detent_width; - // } - float angle_to_detent_center = motor.shaft_angle - current_detent_center; if (angle_to_detent_center > detent_width * 1.2 && value > min) { current_detent_center += detent_width; @@ -102,15 +128,16 @@ void MotorTask::run() { value++; } - float dead_zone = CLAMP( + float dead_zone_adjustment = CLAMP( angle_to_detent_center, fmaxf(-detent_width*DEAD_ZONE_DETENT_PERCENT, -DEAD_ZONE_RAD), fminf(detent_width*DEAD_ZONE_DETENT_PERCENT, DEAD_ZONE_RAD)); if (fabsf(motor.shaft_velocity) > 20) { + // Don't apply torque if velocity is too high (helps avoid feedback loop) motor.move(0); } else { - motor.move(motor.PID_velocity(-angle_to_detent_center + dead_zone)); + motor.move(motor.PID_velocity(-angle_to_detent_center + dead_zone_adjustment)); } display_task_.set_angle(-value * PI / 180);