diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 0ab4cd1c..3bd00a00 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -35,7 +35,7 @@ bool Controller::select_encoder(size_t encoder_num) { Axis* ax = axes[encoder_num]; if (config_.setpoints_in_cpr) { pos_estimate_src_ = &ax->encoder_.pos_cpr_; - pos_wrap_src_ = &ax->encoder_.config_.cpr; + pos_wrap_src_ = &config_.circular_setpoint_range; } else { pos_estimate_src_ = &ax->encoder_.pos_estimate_; pos_wrap_src_ = nullptr; @@ -139,10 +139,9 @@ bool Controller::update(float* torque_setpoint_output) { } // TODO also enable circular deltas for 2nd order filter, etc. - if (pos_wrap_src_) { - float cpr = *pos_wrap_src_ * 2.0f * M_PI / ((float)axis_->encoder_.config_.cpr); + if (config_.setpoints_in_cpr) { // Keep pos setpoint from drifting - input_pos_ = fmodf_pos(input_pos_, cpr); + input_pos_ = fmodf_pos(input_pos_, config_.circular_setpoint_range); } // Update inputs @@ -234,13 +233,12 @@ bool Controller::update(float* torque_setpoint_output) { return false; } - if (pos_wrap_src_) { - float cpr = *pos_wrap_src_ * 2.0f * M_PI / ((float)axis_->encoder_.config_.cpr); + if (config_.setpoints_in_cpr) { // Keep pos setpoint from drifting - pos_setpoint_ = fmodf_pos(pos_setpoint_, cpr); + pos_setpoint_ = fmodf_pos(pos_setpoint_, config_.circular_setpoint_range); // Circular delta pos_err = pos_setpoint_ - *pos_estimate_src; - pos_err = wrap_pm(pos_err, 0.5f * cpr); + pos_err = wrap_pm(pos_err, 0.5f * config_.circular_setpoint_range); } else { pos_err = pos_setpoint_ - *pos_estimate_src; } diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index 6e2ef7f6..d866937c 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -30,6 +30,7 @@ public: float vel_ramp_rate = 2.0f * M_PI; // [(rad/s) / s] float torque_ramp_rate = 0.01f; // Nm / sec bool setpoints_in_cpr = false; + float circular_setpoint_range = 2.0f * M_PI; //circular space if setpoints_in_cpr is used for controller float inertia = 0.0f; // [A/(count/s^2)] float input_filter_bandwidth = 2.0f; // [1/s] float homing_speed = 2000.0f; // [counts/s] @@ -75,7 +76,7 @@ public: bool* pos_estimate_valid_src_ = nullptr; float* vel_estimate_src_ = nullptr; bool* vel_estimate_valid_src_ = nullptr; - int32_t* pos_wrap_src_ = nullptr; // enables circular position setpoints if not null. The value pointed to is the maximum position value. + float* pos_wrap_src_ = nullptr; // enables circular position setpoints if not null. The value pointed to is the maximum position value. float pos_setpoint_ = 0.0f; // [radians] float vel_setpoint_ = 0.0f; // [rad/s] @@ -95,6 +96,9 @@ public: bool anticogging_valid_ = false; + // custom setters + void set_input_pos(float value) { input_pos_ = value; input_pos_updated();} + }; #endif // __CONTROLLER_HPP diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 01ea8740..1dfa47de 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -522,17 +522,10 @@ bool Encoder::update() { snap_to_zero_vel = true; } -<<<<<<< HEAD //outputs from encoder for controller - pos_estimate_ = pos_estimate_counts_ * 2.0f * M_PI / config_.cpr; - vel_estimate_ = vel_estimate_counts_ * 2.0f * M_PI / config_.cpr; - pos_cpr_= pos_cpr_counts_ * 2.0f * M_PI / config_.cpr; -======= - //expose pos/vel estimates in radians for Controller - pos_est_rad_ = pos_estimate_ * 2.0f * M_PI / (float)config_.cpr; - vel_est_rad_ = vel_estimate_ * 2.0f * M_PI / (float)config_.cpr; - pos_cpr_rad_ = pos_cpr_ * 2.0f * M_PI / (float)config_.cpr; ->>>>>>> 4d920849a09d1e3368216f7786e9c1c9b9cb2ce3 + pos_estimate_ = pos_estimate_counts_ * 2.0f * M_PI / (float)config_.cpr; + vel_estimate_ = vel_estimate_counts_ * 2.0f * M_PI / (float)config_.cpr; + pos_cpr_= pos_cpr_counts_ * 2.0f * M_PI / (float)config_.cpr; //// run encoder count interpolation int32_t corrected_enc = count_in_cpr_ - config_.offset; diff --git a/docs/getting-started.md b/docs/getting-started.md index 913f2140..bdfe8d85 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -334,7 +334,7 @@ In the regular position mode, the `input_pos` would grow to a very large value a In this mode, the controller will try to track the position within only one turn of the motor. Specifically, `input_pos` is expected in the range `[0, 2*Pi]`. If the `input_pos` is incremented to outside this range (say via step/dir input), it is automatically wrapped around into the correct value. Note that in this mode `encoder.pos_cpr_` is used for feedback instead of `encoder.pos_estimate`. -If you try to increment the axis with a large step in one go that exceeds `Pi` steps, the motor will go to the same angle around the wrong way. This is also the case if there is a large disturbance. If you have an application where you would like to handle larger steps, you can use a virtual CPR for your encoder that is an integer times larger than your encoder's actual CPR. Set `encoder.config.cpr = N * your_enc_cpr`, where N is some integer. Choose N to give you an appropriate circular space for your application. `encoder.config.cpr` is automatically converted to radians internally. +If you try to increment the axis with a large step in one go that exceeds `Pi` steps, the motor will go to the same angle around the wrong way. This is also the case if there is a large disturbance. If you have an application where you would like to handle larger steps, you can use a virtual CPR for your encoder that is an integer times larger than your encoder's actual CPR. Set `encoder.config.cpr = N * 2.0 * Pi`, where N is some integer. Choose N to give you an appropriate circular space for your application. ### Velocity control Set `axis.controller.config.control_mode = CONTROL_MODE_VELOCITY_CONTROL`.
diff --git a/tools/odrive/tests/closed_loop_test.py b/tools/odrive/tests/closed_loop_test.py index 2bcfa9cf..ba452135 100644 --- a/tools/odrive/tests/closed_loop_test.py +++ b/tools/odrive/tests/closed_loop_test.py @@ -175,7 +175,7 @@ class TestRegenProtection(TestClosedLoopControlBase): with self.prepare(axis_ctx, motor_ctx, enc_ctx, logger): nominal_rps = 15.0 nominal_vel = 2.0 * pi * nominal_rps - max_current = 15.0 + max_current = 30.0 # Accept a bit of noise on Ibus axis_ctx.parent.handle.config.dc_max_negative_current = -0.5