-
Notifications
You must be signed in to change notification settings - Fork 21
Add balance control to foot controller #1636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8c4b04e
81d0ef8
ce18c71
03f379f
a11951d
6225e4c
f58e41c
2f2c6dd
52915e4
ac4cd4c
b5b09e3
c1c2044
15eba7a
9a9f422
ec81ae6
f0d6e51
54b4553
5643738
afd07af
c85ff2d
5854297
673cae9
b9c2469
4275d2f
a26dba1
0130383
83faabb
6c8debe
3c8bdd4
e3ae2db
5b60b01
f1ab699
a985dc3
4cc793f
f95126b
15d70b8
fc75123
aca031e
b8de13a
6e6f463
1e83b46
bc119ea
bbca430
a6b16a5
c92df0c
a440ed0
4e84fdb
503db3c
2e29ca3
c13206c
2b87c51
157ec44
e37d9fe
00387b5
56279ed
984fde5
fd48bc3
ac217c5
1ff551c
f529f37
d8d91a5
59dc37a
76c7845
592ae12
ca8dfc8
e80ee44
22bb93e
5fbaa98
a5d3656
c8dec0d
62d2866
cbb9d84
06e62fb
48c0f51
8139e98
a219074
9301db7
a052baf
58130ef
1569ed1
0aa77cc
82fd60a
4d1d1d3
e0e95a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,10 @@ | |
| #ifndef MODULE_ACTUATION_FOOTCONTROLLER_HPP | ||
| #define MODULE_ACTUATION_FOOTCONTROLLER_HPP | ||
|
|
||
| #include <Eigen/Core> | ||
| #include <Eigen/Geometry> | ||
| #include <algorithm> | ||
| #include <fmt/format.h> | ||
| #include <nuclear> | ||
|
|
||
| #include "extension/Behaviour.hpp" | ||
|
|
@@ -37,6 +40,8 @@ | |
|
|
||
| #include "utility/input/LimbID.hpp" | ||
| #include "utility/input/ServoID.hpp" | ||
| #include "utility/math/comparison.hpp" | ||
| #include "utility/math/euler.hpp" | ||
| #include "utility/nusight/NUhelpers.hpp" | ||
|
|
||
| #define TORQUE_ENABLED 100 | ||
|
|
@@ -47,11 +52,18 @@ namespace module::actuation { | |
| using message::input::Sensors; | ||
|
|
||
| using utility::input::LimbID; | ||
| using utility::math::euler::mat_to_rpy_intrinsic; | ||
| using utility::math::euler::rpy_intrinsic_to_mat; | ||
| using utility::nusight::graph; | ||
|
|
||
| /// @brief Empty struct used to trigger the setting of servo gains. | ||
| struct SetGains {}; | ||
|
|
||
| class FootController : public ::extension::behaviour::BehaviourReactor { | ||
| public: | ||
| /// @brief Called by the powerplant to build and setup the FootController reactor. | ||
| explicit FootController(std::unique_ptr<NUClear::Environment> environment); | ||
|
|
||
| private: | ||
| /// @brief Stores configuration values | ||
| struct Config { | ||
|
|
@@ -79,8 +91,36 @@ namespace module::actuation { | |
| std::chrono::seconds set_gain_delay = std::chrono::seconds(0); | ||
| /// @brief Desired gains for each servo | ||
| std::map<std::string, double> desired_gains{}; | ||
|
|
||
| /// @brief Proportional gain for torso orientation roll correction | ||
| double roll_p_gain = 0.0; | ||
| /// @brief Proportional gain for torso orientation pitch correction | ||
| double pitch_p_gain = 0.0; | ||
| /// @brief Integral gain for torso orientation roll correction | ||
| double roll_i_gain = 0.0; | ||
| /// @brief Integral gain for torso orientation pitch correction | ||
| double pitch_i_gain = 0.0; | ||
| /// @brief Antiwindup max I error | ||
| double max_i_error = 0.0; | ||
| /// @brief Derivative gain for torso orientation roll correction | ||
| double roll_d_gain = 0.0; | ||
| /// @brief Derivative gain for torso orientation pitch correction | ||
| double pitch_d_gain = 0.0; | ||
| /// @brief Maximum allowed pitch error before disabling correction | ||
| double max_pitch_error = 0.0; | ||
| /// @brief Maximum allowed roll error before disabling correction | ||
| double max_roll_error = 0.0; | ||
| } cfg; | ||
|
|
||
| /// @brief Accumulates the integral of the roll error over time for use in PID control. | ||
| double integral_roll_error = 0; | ||
| /// @brief Accumulates the integral of the pitch error over time for use in PID control. | ||
| double integral_pitch_error = 0; | ||
|
|
||
| /// @brief Stores the timestamp of the last PID control update, used to compute time deltas for integral and | ||
| /// derivative calculations. | ||
| NUClear::clock::time_point last_update_time{}; | ||
|
|
||
| /// @brief Foot controller logic | ||
| template <typename FootControlTask, typename IKTask> | ||
| void control_foot(const FootControlTask& foot_control_task, | ||
|
|
@@ -110,13 +150,127 @@ namespace module::actuation { | |
| else { | ||
| throw std::runtime_error("Invalid mode"); | ||
| } | ||
| } | ||
|
|
||
| public: | ||
| /// @brief Called by the powerplant to build and setup the FootController reactor. | ||
| explicit FootController(std::unique_ptr<NUClear::Environment> environment); | ||
| // Add correction to the torso orientation if enabled | ||
| if (foot_control_task.correction_enabled) { | ||
| // Hwt quaternion | ||
| Eigen::Quaterniond Hwt_quat(sensors.Htw.inverse().linear()); | ||
|
|
||
| // Get fused roll and pitch | ||
| Eigen::Vector3d rpy = mat_to_rpy_intrinsic(Hwt_quat.toRotationMatrix()); | ||
| double fused_roll = rpy.x(); | ||
| double fused_pitch = rpy.y(); | ||
|
|
||
| if (log_level <= DEBUG) { | ||
| // Graph the correction being applied | ||
| emit(graph("Balance/Actual_Roll", fused_roll)); | ||
| emit(graph("Balance/Actual_Pitch", fused_pitch)); | ||
| } | ||
|
|
||
| // Get the desired roll and pitch | ||
| Eigen::Quaterniond Hft_quat(ik_task->Htf.inverse().linear()); | ||
| rpy = mat_to_rpy_intrinsic(Hft_quat.toRotationMatrix()); | ||
| double desired_roll = rpy.x(); | ||
| double desired_pitch = rpy.y(); | ||
|
|
||
| if (log_level <= DEBUG) { | ||
| // Graph desired orientation (before PID correction) | ||
| emit(graph("Balance/Desired_Roll_Original", desired_roll)); | ||
| emit(graph("Balance/Desired_Pitch_Original", desired_pitch)); | ||
| } | ||
|
|
||
| // Compute the error between the desired torso orientation and the actual torso orientation | ||
| auto roll_error = desired_roll - fused_roll; | ||
| auto pitch_error = desired_pitch - fused_pitch; | ||
|
|
||
| if (log_level <= DEBUG) { | ||
| // Graph the errors | ||
| emit(graph("Balance/Roll_Error", roll_error)); | ||
| emit(graph("Balance/Pitch_Error", pitch_error)); | ||
| } | ||
|
|
||
| auto dt = | ||
| std::chrono::duration_cast<std::chrono::duration<double>>(NUClear::clock::now() - last_update_time) | ||
| .count(); | ||
| last_update_time = NUClear::clock::now(); | ||
|
|
||
| // P control | ||
| auto clamped_roll_error = utility::math::clamp(-cfg.max_roll_error, roll_error, cfg.max_roll_error); | ||
| auto clamped_pitch_error = utility::math::clamp(-cfg.max_pitch_error, pitch_error, cfg.max_pitch_error); | ||
| desired_roll += cfg.roll_p_gain * clamped_roll_error; | ||
| desired_pitch += cfg.pitch_p_gain * clamped_pitch_error; | ||
|
|
||
| if (log_level <= DEBUG) { | ||
| if (roll_error > cfg.max_roll_error || roll_error < -cfg.max_roll_error) { | ||
| // If the error is too large, we are probably falling over and should pause applying control. | ||
| log<DEBUG>(fmt::format("Balance correction disabled due to large ROLL error: roll_error = {}", | ||
| roll_error)); | ||
| log<DEBUG>(fmt::format("Clamping roll error to: {}", clamped_roll_error)); | ||
| } | ||
|
|
||
| if (pitch_error > cfg.max_pitch_error || pitch_error < -cfg.max_pitch_error) { | ||
| // If the error is too large, we are probably falling over and should pause applying control. | ||
| log<DEBUG>(fmt::format("Balance correction disabled due to large error: pitch_error = {}", | ||
| pitch_error)); | ||
| log<DEBUG>(fmt::format("Clamping pitch error to: {}", clamped_pitch_error)); | ||
| } | ||
| } | ||
|
|
||
| // Graph P Correction | ||
| if (log_level <= DEBUG) { | ||
| emit(graph("Balance/P_Roll_Correction", cfg.roll_p_gain * roll_error)); | ||
| emit(graph("Balance/P_Pitch_Correction", cfg.pitch_p_gain * pitch_error)); | ||
| } | ||
|
|
||
| // I control | ||
| integral_roll_error += roll_error * dt; | ||
| integral_pitch_error += pitch_error * dt; | ||
|
|
||
| // Graph the integral correction | ||
| if (log_level <= DEBUG) { | ||
| emit(graph("Balance/I_Roll_Correction", cfg.roll_i_gain * integral_roll_error)); | ||
| emit(graph("Balance/I_Pitch_Correction", cfg.pitch_i_gain * integral_pitch_error)); | ||
| } | ||
|
|
||
| // Anti windup | ||
| integral_roll_error = std::max(std::min(integral_roll_error, cfg.max_i_error), -cfg.max_i_error); | ||
| integral_pitch_error = std::max(std::min(integral_pitch_error, cfg.max_i_error), -cfg.max_i_error); | ||
|
|
||
| desired_roll += cfg.roll_i_gain * integral_roll_error; | ||
| desired_pitch += cfg.pitch_i_gain * integral_pitch_error; | ||
|
|
||
| // D control using gyroscope values | ||
| auto roll_rate = sensors.gyroscope.x(); | ||
| auto pitch_rate = sensors.gyroscope.y(); | ||
| desired_roll -= cfg.roll_d_gain * roll_rate; | ||
| desired_pitch -= cfg.pitch_d_gain * pitch_rate; | ||
|
|
||
| if (log_level <= DEBUG) { | ||
| // Graph D corrections and rates | ||
| emit(graph("Balance/D_Roll_Correction", -cfg.roll_d_gain * roll_rate)); | ||
| emit(graph("Balance/D_Pitch_Correction", -cfg.pitch_d_gain * pitch_rate)); | ||
| emit(graph("Balance/Roll_Rate", roll_rate)); | ||
| emit(graph("Balance/Pitch_Rate", pitch_rate)); | ||
|
|
||
| // Graph final corrected desired values | ||
| emit(graph("Balance/Desired_Roll_Final", desired_roll)); | ||
| emit(graph("Balance/Desired_Pitch_Final", desired_pitch)); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be good to apply a slew rate limit to these, just as a safety precaution. so basically just add a limit on the maximum change in desired pitch and roll per second, just so that if we get bad sensor data (like a faulty gyro reading) this doesn't blow up |
||
|
|
||
| double desired_yaw = mat_to_rpy_intrinsic(Hft_quat.toRotationMatrix()).z(); | ||
|
|
||
| // Compute desired orientation: yaw * fused_roll_pitch | ||
| Eigen::Matrix3d desired_Rft = Eigen::AngleAxisd(desired_yaw, Eigen::Vector3d::UnitZ()) | ||
| * rpy_intrinsic_to_mat(Eigen::Vector3d(desired_roll, desired_pitch, 0.0)); | ||
| Eigen::Isometry3d Htf_corrected = foot_control_task.Htf; | ||
| Htf_corrected.linear() = desired_Rft.transpose(); | ||
|
|
||
| ik_task->Htf = Htf_corrected; | ||
| } | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| } // namespace module::actuation | ||
|
|
||
| #endif // MODULE_ACTUATION_FOOTCONTROLLER_HPP | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -37,3 +37,6 @@ arms: | |||||
| left_shoulder_roll: 0.35 | ||||||
| right_elbow: -0.7 | ||||||
| left_elbow: -0.7 | ||||||
|
|
||||||
| # Use balance control. NOTE: correction_enabled must be true in FootController config | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| use_balance_control: false | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -36,3 +36,6 @@ arms: | |||||
| left_shoulder_roll: 0.2 | ||||||
| right_elbow: -0.7 | ||||||
| left_elbow: -0.7 | ||||||
|
|
||||||
| # Use balance control. NOTE: correction_enabled must be true in FootController config | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or maybe the comment isn't needed anymore |
||||||
| use_balance_control: false | ||||||
Uh oh!
There was an error while loading. Please reload this page.