diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b09cb376338018e783562875a289915301171dc..c546d4b470c3e80a23b8e05d725a47c7ab2c5bfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ set(${PROJECT_NAME}_HEADERS include/qrw/WbcWrapper.hpp include/qrw/Params.hpp include/qrw/Estimator.hpp + include/qrw/ComplementaryFilter.hpp include/qrw/Joystick.hpp include/qrw/Filter.hpp include/qrw/Controller.hpp @@ -89,6 +90,7 @@ set(${PROJECT_NAME}_SOURCES src/WbcWrapper.cpp src/Params.cpp src/Estimator.cpp + src/ComplementaryFilter.cpp src/Joystick.cpp src/Filter.cpp src/Controller.cpp diff --git a/include/qrw/ComplementaryFilter.hpp b/include/qrw/ComplementaryFilter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b12fb47c84f617d7d8bf4555da3280841ad91a95 --- /dev/null +++ b/include/qrw/ComplementaryFilter.hpp @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \brief This is the header for Estimator and ComplementaryFilter classes +/// +/// \details These classes estimate the state of the robot based on sensor measurements +/// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef COMPLEMENTARY_FILTER_H_INCLUDED +#define COMPLEMENTARY_FILTER_H_INCLUDED + +#include "qrw/Types.h" + +class ComplementaryFilter { + public: + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Constructor + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + ComplementaryFilter(); + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Constructor witht initialization + /// + /// \param[in] dt Time step of the complementary filter + /// \param[in] HighPass Initial value for the high pass filter + /// \param[in] LowPass Initial value for the low pass filter + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + ComplementaryFilter(double dt, Vector3 HighPass, Vector3 LowPass); + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + ~ComplementaryFilter() {} // Empty destructor + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Initialize + /// + /// \param[in] dt Time step of the complementary filter + /// \param[in] HighPass Initial value for the high pass filter + /// \param[in] LowPass Initial value for the low pass filter + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + void initialize(double dt, Vector3 HighPass, Vector3 LowPass); + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Compute the filtered output of the complementary filter + /// + /// \param[in] x Quantity handled by the filter + /// \param[in] dx Derivative of the quantity + /// \param[in] alpha Filtering coefficient between x and dx quantities + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + Vector3 compute(Vector3 const& x, Vector3 const& dx, Vector3 const& alpha); + + Vector3 getX() { return x_; } // Get the input quantity + Vector3 getDx() { return dx_; } // Get the derivative of the input quantity + Vector3 getHighPass() { return HighPass_; } // Get the high-passed internal quantity + Vector3 getLowPass() { return LowPass_; } // Get the low-passed internal quantity + Vector3 getAlpha() { return alpha_; } // Get the alpha coefficient of the filter + Vector3 getFilteredX() { return filteredX_; } // Get the filtered output + + private: + double dt_; // Time step of the complementary filter + Vector3 HighPass_; // Initial value for the high pass filter + Vector3 LowPass_; // Initial value for the low pass filter + Vector3 alpha_; // Filtering coefficient between x and dx quantities + Vector3 x_; // Quantity to filter + Vector3 dx_; // Quantity to filter derivative's + Vector3 filteredX_; // Filtered output +}; + +#endif // COMPLEMENTARY_FILTER_H_INCLUDED diff --git a/include/qrw/Controller.hpp b/include/qrw/Controller.hpp index 959e1970ecb917d63c230584ed1a996d4bdd6be8..6a61828ee8b38406cc6cc806fbf032fb7b4ab860 100644 --- a/include/qrw/Controller.hpp +++ b/include/qrw/Controller.hpp @@ -12,11 +12,6 @@ #include <odri_control_interface/robot.hpp> #include <boost/interprocess/managed_shared_memory.hpp> #include "qrw/FakeRobot.hpp" -#include <cmath> -#include <fstream> -#include <iostream> -#include <string> -#include "qrw/Types.h" #include "qrw/Params.hpp" #include "qrw/Joystick.hpp" #include "qrw/Estimator.hpp" diff --git a/include/qrw/Estimator.hpp b/include/qrw/Estimator.hpp index d3f328f32a9676abfb3936daa9d648de543a8f01..d8c014f233fc205aaebff5c387c3dec768e9c4a8 100644 --- a/include/qrw/Estimator.hpp +++ b/include/qrw/Estimator.hpp @@ -9,93 +9,100 @@ #ifndef ESTIMATOR_H_INCLUDED #define ESTIMATOR_H_INCLUDED -#include "qrw/Gait.hpp" -#include "qrw/Params.hpp" -#include "qrw/Types.h" -#include "pinocchio/math/rpy.hpp" -#include "pinocchio/spatial/se3.hpp" -#include "pinocchio/algorithm/frames.hpp" -#include "pinocchio/multibody/model.hpp" -#include "pinocchio/multibody/data.hpp" -#include "pinocchio/parsers/urdf.hpp" -#include "pinocchio/algorithm/compute-all-terms.hpp" #include <deque> -class ComplementaryFilter { +#include "pinocchio/multibody/data.hpp" +#include "pinocchio/multibody/model.hpp" +#include "pinocchio/spatial/se3.hpp" +#include "qrw/ComplementaryFilter.hpp" +#include "qrw/Params.hpp" + +class Estimator { public: //////////////////////////////////////////////////////////////////////////////////////////////// /// /// \brief Constructor /// //////////////////////////////////////////////////////////////////////////////////////////////// - ComplementaryFilter(); - - //////////////////////////////////////////////////////////////////////////////////////////////// - /// - /// \brief Destructor - /// - //////////////////////////////////////////////////////////////////////////////////////////////// - ~ComplementaryFilter() {} // Empty destructor - - //////////////////////////////////////////////////////////////////////////////////////////////// - /// - /// \brief Initialize - /// - /// \param[in] dt Time step of the complementary filter - /// \param[in] HP_x Initial value for the high pass filter - /// \param[in] LP_x Initial value for the low pass filter - /// - //////////////////////////////////////////////////////////////////////////////////////////////// - void initialize(double dt, Vector3 HP_x, Vector3 LP_x); + Estimator(); //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Compute the filtered output of the complementary filter + /// \brief Initialize with given data /// - /// \param[in] x Quantity handled by the filter - /// \param[in] dx Derivative of the quantity - /// \param[in] alpha Filtering coefficient between x and dx quantities + /// \param[in] params Object that stores parameters /// //////////////////////////////////////////////////////////////////////////////////////////////// - Vector3 compute(Vector3 const& x, Vector3 const& dx, Vector3 const& alpha); - - Vector3 getX() { return x_; } // Get the input quantity - Vector3 getDX() { return dx_; } // Get the derivative of the input quantity - Vector3 getHpX() { return HP_x_; } // Get the high-passed internal quantity - Vector3 getLpX() { return LP_x_; } // Get the low-passed internal quantity - Vector3 getAlpha() { return alpha_; } // Get the alpha coefficient of the filter - Vector3 getFiltX() { return filt_x_; } // Get the filtered output - - private: - double dt_; - Vector3 x_, dx_, HP_x_, LP_x_, alpha_, filt_x_; -}; + void initialize(Params& params); -class Estimator { - public: //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Constructor + /// \brief Destructor. /// //////////////////////////////////////////////////////////////////////////////////////////////// - Estimator(); + ~Estimator() {} // Empty destructor //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Initialize with given data + /// \brief Run one iteration of the estimator to get the position and velocity states of the robot /// - /// \param[in] params Object that stores parameters + /// \param[in] gait Gait matrix that stores current and future contact status of the feet + /// \param[in] goals Target positions of the four feet + /// \param[in] baseLinearAcceleration Linear acceleration of the IMU (gravity compensated) + /// \param[in] baseAngularVelocity Angular velocity of the IMU + /// \param[in] baseOrientation Quaternion orientation of the IMU + /// \param[in] q_mes Position of the 12 actuators + /// \param[in] v_mes Velocity of the 12 actuators + /// \param[in] perfectPosition Position of the robot in world frame + /// \param[in] b_perfectVelocity Velocity of the robot in base frame /// //////////////////////////////////////////////////////////////////////////////////////////////// - void initialize(Params& params); + void run(MatrixN const& gait, MatrixN const& goals, VectorN const& baseLinearAcceleration, + VectorN const& baseAngularVelocity, VectorN const& baseOrientation, VectorN const& q_mes, + VectorN const& v_mes, VectorN const& perfectPosition, Vector3 const& b_perfectVelocity); //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Destructor. + /// \brief Update state vectors of the robot (q and v) + /// Update transformation matrices between world and horizontal frames + /// + /// \param[in] joystick_v_ref Reference velocity from the joystick /// //////////////////////////////////////////////////////////////////////////////////////////////// - ~Estimator() {} // Empty destructor + void updateReferenceState(VectorN const& vRef); + + VectorN getQEstimate() { return qEstimate_; } + VectorN getVEstimate() { return vEstimate_; } + VectorN getVSecurity() { return vSecurity_; } + VectorN getFeetStatus() { return feetStatus_; } + MatrixN getFeetTargets() { return feetTargets_; } + Vector3 getBaseVelocityFK() { return baseVelocityFK_; } + Vector3 getBasePositionFK() { return basePositionFK_; } + Vector3 getFeetPositionBarycenter() { return feetPositionBarycenter_; } + Vector3 getBBaseVelocity() { return b_baseVelocity_; } + + Vector3 getFilterVelX() { return velocityFilter_.getX(); } + Vector3 getFilterVelDX() { return velocityFilter_.getDx(); } + Vector3 getFilterVelAlpha() { return velocityFilter_.getAlpha(); } + Vector3 getFilterVelFiltX() { return velocityFilter_.getFilteredX(); } + + Vector3 getFilterPosX() { return positionFilter_.getX(); } + Vector3 getFilterPosDX() { return positionFilter_.getDx(); } + Vector3 getFilterPosAlpha() { return positionFilter_.getAlpha(); } + Vector3 getFilterPosFiltX() { return positionFilter_.getFilteredX(); } + + VectorN getQReference() { return qRef_; } + VectorN getVReference() { return vRef_; } + VectorN getBaseVelRef() { return baseVelRef_; } + VectorN getBaseAccRef() { return baseAccRef_; } + VectorN getHV() { return h_v_; } + VectorN getVFiltered() { return vFiltered_; } + VectorN getHVFiltered() { return h_vFiltered_; } + Matrix3 getoRh() { return oRh_; } + Matrix3 gethRb() { return hRb_; } + Vector3 getoTh() { return oTh_; } + private: //////////////////////////////////////////////////////////////////////////////////////////////// /// /// \brief Retrieve and update IMU data @@ -105,27 +112,36 @@ class Estimator { /// \param[in] baseOrientation Euler orientation of the IMU /// //////////////////////////////////////////////////////////////////////////////////////////////// - void get_data_IMU(Vector3 const& baseLinearAcceleration, Vector3 const& baseAngularVelocity, - Vector3 const& baseOrientation, VectorN const& q_perfect); + void updateIMUData(Vector3 const& baseLinearAcceleration, Vector3 const& baseAngularVelocity, + Vector3 const& baseOrientation, VectorN const& perfectPosition); //////////////////////////////////////////////////////////////////////////////////////////////// /// /// \brief Retrieve and update position and velocity of the 12 actuators /// - /// \param[in] q_mes Position of the 12 actuators - /// \param[in] v_mes Velocity of the 12 actuators + /// \param[in] q Position of the 12 actuators + /// \param[in] v Velocity of the 12 actuators /// //////////////////////////////////////////////////////////////////////////////////////////////// - void get_data_joints(Vector12 const& q_mes, Vector12 const& v_mes); + void updateJointData(Vector12 const& q, Vector12 const& v); //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Estimate base position and velocity using Forward Kinematics + /// \brief Update the feet relative data + /// \details update feetStatus_, feetTargets_, feetStancePhaseDuration_ and phaseRemainingDuration_ /// - /// \param[in] feet_status Contact status of the four feet + /// \param[in] gait Gait matrix that stores current and future contact status of the feet + /// \param[in] feetTargets Target positions of the four feet /// //////////////////////////////////////////////////////////////////////////////////////////////// - void get_data_FK(Eigen::Matrix<double, 1, 4> const& feet_status); + void updatFeetStatus(MatrixN const& gait, MatrixN const& feetTargets); + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Estimate base position and velocity using Forward Kinematics + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + void updateForwardKinematics(); //////////////////////////////////////////////////////////////////////////////////////////////// /// @@ -135,155 +151,116 @@ class Estimator { /// \param[in] goals Target positions of the four feet /// //////////////////////////////////////////////////////////////////////////////////////////////// - void get_xyz_feet(Eigen::Matrix<double, 1, 4> const& feet_status, Matrix34 const& goals); + void computeFeetPositionBarycenter(); //////////////////////////////////////////////////////////////////////////////////////////////// /// /// \brief Estimate the velocity of the base with forward kinematics using a contact point that /// is supposed immobile in world frame /// - /// \param[in] contactFrameId Frame ID of the contact point + /// \param[in] contactFrameId Frame ID of the contact foot /// //////////////////////////////////////////////////////////////////////////////////////////////// - Vector3 BaseVelocityFromKinAndIMU(int contactFrameId); + Vector3 computeBaseVelocityFromFoot(int footId); //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Run one iteration of the estimator to get the position and velocity states of the robot + /// \brief Estimate the position of the base with forward kinematics using a contact point that + /// is supposed immobile in world frame /// - /// \param[in] gait Gait matrix that stores current and future contact status of the feet - /// \param[in] goals Target positions of the four feet - /// \param[in] baseLinearAcceleration Linear acceleration of the IMU (gravity compensated) - /// \param[in] baseAngularVelocity Angular velocity of the IMU - /// \param[in] baseOrientation Quaternion orientation of the IMU - /// \param[in] q_mes Position of the 12 actuators - /// \param[in] v_mes Velocity of the 12 actuators - /// \param[in] q_perfect Position of the robot in PyBullet simulator (only for simulation) - /// \param[in] b_baseVel_perfect Velocity of the robot in PyBullet simulator (only for simulation) + /// \param[in] footId Frame ID of the contact foot /// //////////////////////////////////////////////////////////////////////////////////////////////// - void run_filter(MatrixN const& gait, MatrixN const& goals, VectorN const& baseLinearAcceleration, - VectorN const& baseAngularVelocity, VectorN const& baseOrientation, VectorN const& q_mes, - VectorN const& v_mes, VectorN const& q_perfect, Vector3 const& b_baseVel_perfect); + Vector3 computeBasePositionFromFoot(int footId); //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Security check to verify that measured positions, velocities and required torques - /// are not above defined tresholds - /// - /// \param[in] tau_ff Feedforward torques outputted by the whole body control for the PD+ + /// \brief Compute the alpha coefficient for the complementary filter + /// \return alpha /// //////////////////////////////////////////////////////////////////////////////////////////////// - int security_check(VectorN const& tau_ff); + double computeAlphaVelocity(); //////////////////////////////////////////////////////////////////////////////////////////////// /// - /// \brief Update state vectors of the robot (q and v) - /// Update transformation matrices between world and horizontal frames + /// \brief Estimates the velocity vector + /// \details The complementary filter combines data from the FK and the IMU acceleration data /// - /// \param[in] joystick_v_ref Reference velocity from the joystick - /// \param[in] gait Gait object + /// \param[in] b_perfectVelocity Perfect velocity of the base in the base frame /// //////////////////////////////////////////////////////////////////////////////////////////////// - void updateState(VectorN const& joystick_v_ref, Gait& gait); - - VectorN getQFilt() { return q_filt_dyn_; } - VectorN getVFilt() { return v_filt_dyn_; } - VectorN getVSecu() { return v_secu_dyn_; } - VectorN getVFiltBis() { return v_filt_bis_; } - Vector3 getRPY() { return IMU_RPY_; } - MatrixN getFeetStatus() { return feet_status_; } - MatrixN getFeetGoals() { return feet_goals_; } - Vector3 getFKLinVel() { return FK_lin_vel_; } - Vector3 getFKXYZ() { return FK_xyz_; } - Vector3 getXYZMeanFeet() { return xyz_mean_feet_; } - Vector3 getFiltLinVel() { return b_filt_lin_vel_; } - - Vector3 getFilterVelX() { return filter_xyz_vel_.getX(); } - Vector3 getFilterVelDX() { return filter_xyz_vel_.getDX(); } - Vector3 getFilterVelAlpha() { return filter_xyz_vel_.getAlpha(); } - Vector3 getFilterVelFiltX() { return filter_xyz_vel_.getFiltX(); } + void estimateVelocity(Vector3 const& b_perfectVelocity); - Vector3 getFilterPosX() { return filter_xyz_pos_.getX(); } - Vector3 getFilterPosDX() { return filter_xyz_pos_.getDX(); } - Vector3 getFilterPosAlpha() { return filter_xyz_pos_.getAlpha(); } - Vector3 getFilterPosFiltX() { return filter_xyz_pos_.getFiltX(); } - - VectorN getQUpdated() { return q_up_; } - VectorN getVUpdated() { return v_up_; } - VectorN getVRef() { return v_ref_; } - VectorN getARef() { return a_ref_; } - VectorN getHV() { return h_v_; } - VectorN getHVWindowed() { return h_v_windowed_; } - Matrix3 getoRb() { return oRb_; } - Matrix3 getoRh() { return oRh_; } - Matrix3 gethRb() { return hRb_; } - Vector3 getoTh() { return oTh_; } - double getYawEstim() { return yaw_estim_; } - - private: - ComplementaryFilter filter_xyz_pos_; // Complementary filter for base position - ComplementaryFilter filter_xyz_vel_; // Complementary filter for base velocity - - double dt_wbc; // Time step of the estimator - double alpha_secu_; // Low pass coefficient for the outputted filtered velocity for security check - double offset_yaw_IMU_; // Yaw orientation of the IMU at startup - bool perfect_estimator; // Enable perfect estimator (directly from the PyBullet simulation) - bool solo3D; // Perfect estimator including yaw angle - int N_SIMULATION; // Number of loops before the control ends - int k_log_; // Number of time the estimator has been called - - Vector3 IMU_lin_acc_; // Linear acceleration of the IMU (gravity compensated) - Vector3 IMU_ang_vel_; // Angular velocity of the IMU - Vector3 IMU_RPY_; // Roll Pitch Yaw orientation of the IMU - Matrix3 oRb_; // Rotation between base and world frame - pinocchio::SE3::Quaternion IMU_ang_pos_; // Quaternion orientation of the IMU - Vector12 actuators_pos_; // Measured positions of actuators - Vector12 actuators_vel_; // Measured velocities of actuators - - Vector19 q_FK_; // Configuration vector for Forward Kinematics - Vector18 v_FK_; // Velocity vector for Forward Kinematics - MatrixN feet_status_; // Contact status of the four feet - MatrixN feet_goals_; // Target positions of the four feet - Vector3 FK_lin_vel_; // Base linear velocity estimated by Forward Kinematics - Vector3 FK_xyz_; // Base position estimated by Forward Kinematics - Vector3 b_filt_lin_vel_; // Filtered estimated velocity at center base (base frame) - - Vector3 xyz_mean_feet_; // Barycenter of feet in contact - - Eigen::Matrix<double, 1, 4> k_since_contact_; // Number of loops during which each foot has been in contact - int feet_indexes_[4] = {10, 18, 26, 34}; // Frame indexes of the four feet - - pinocchio::Model model_, model_for_xyz_; // Pinocchio models for frame computations and forward kinematics - pinocchio::Data data_, data_for_xyz_; // Pinocchio datas for frame computations and forward kinematics - - Vector19 q_filt_; // Filtered output configuration - Vector18 v_filt_; // Filtered output velocity - Vector12 v_secu_; // Filtered output velocity for security check - - VectorN q_filt_dyn_; // Dynamic size version of q_filt_ - VectorN v_filt_dyn_; // Dynamic size version of v_filt_ - VectorN v_secu_dyn_; // Dynamic size version of v_secu_ - - pinocchio::SE3 _1Mi_; // Transform between the base frame and the IMU frame - - Vector12 q_security_; // Position limits for the actuators above which safety controller is triggered - - // For updateState function - VectorN q_up_; // Configuration vector in ideal world frame - VectorN v_up_; // Velocity vector in ideal world frame - VectorN v_ref_; // Reference velocity vector - VectorN a_ref_; // Reference acceleration vector - VectorN h_v_; // Velocity vector in horizontal frame - Matrix3 oRh_; // Rotation between horizontal and world frame - Matrix3 hRb_; // Rotation between base and horizontal frame - Vector3 oTh_; // Translation between horizontal and world frame - double yaw_estim_; // Yaw angle in perfect world + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Estimates the configuration vector + /// \details The complementary filter combines data from the FK and the estimated velocity + /// + /// \param[in] perfectPosition Perfect position of the base in the world frame + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + void estimatePosition(Vector3 const& perfectPosition); - // Filter the base velocity with an averaging window to remove oscillations at multiples of gait frequency - int N_queue_; // Number of samples in the averaging window - VectorN v_filt_bis_; // Base velocity (in base frame) filtered by averaging window - VectorN h_v_windowed_; // Base velocity (in horizontal frame) filtered by averaging window - std::deque<double> vx_queue_, vy_queue_, vz_queue_, wR_queue_, wP_queue_, wY_queue_; // Queues that hold samples + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Filter the estimated velocity over a moving window + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + void filterVelocity(); + + bool perfectEstimator_; // Enable perfect estimator (directly from the PyBullet simulation) + bool solo3D_; // Perfect estimator including yaw angle + double dt_; // Time step of the estimator + bool initialized_; // Is intiialized after the first update of the IMU + Vector4 feetFrames_; // Frame indexes of the four feet + double footRadius_; // radius of a foot + Vector3 alphaPos_; // Alpha coeefficient for the position complementary filter + double alphaVelMax_; // Maximum alpha value for the velocity complementary filter + double alphaVelMin_; // Minimum alpha value for the velocity complementary filter + double alphaSecurity_; // Low pass coefficient for the outputted filtered velocity for security check + + pinocchio::SE3 b_M_IMU_; // Transform between the base frame and the IMU frame + double IMUYawOffset_; // Yaw orientation of the IMU at startup + Vector3 IMULinearAcceleration_; // Linear acceleration of the IMU (gravity compensated) + Vector3 IMUAngularVelocity_; // Angular velocity of the IMU + Vector3 IMURpy_; // Roll Pitch Yaw orientation of the IMU + pinocchio::SE3::Quaternion IMUQuat_; // Quaternion orientation of the IMU + + Vector12 qActuators_; // Measured positions of actuators + Vector12 vActuators_; // Measured velocities of actuators + + int phaseRemainingDuration_; // Number of iterations left for the current gait phase + Vector4 feetStancePhaseDuration_; // Number of loops during which each foot has been in contact + Vector4 feetStatus_; // Contact status of the four feet + Matrix34 feetTargets_; // Target positions of the four feet + + pinocchio::Model velocityModel_, positionModel_; // Pinocchio models for frame computations and forward kinematics + pinocchio::Data velocityData_, positionData_; // Pinocchio datas for frame computations and forward kinematics + Vector19 q_FK_; // Configuration vector for Forward Kinematics + Vector18 v_FK_; // Velocity vector for Forward Kinematics + Vector3 baseVelocityFK_; // Base linear velocity estimated by Forward Kinematics + Vector3 basePositionFK_; // Base position estimated by Forward Kinematics + Vector3 b_baseVelocity_; // Filtered estimated velocity at center base (base frame) + Vector3 feetPositionBarycenter_; // Barycenter of feet in contact + + ComplementaryFilter positionFilter_; // Complementary filter for base position + ComplementaryFilter velocityFilter_; // Complementary filter for base velocity + Vector19 qEstimate_; // Filtered output configuration + Vector18 vEstimate_; // Filtered output velocity + Vector12 vSecurity_; // Filtered output velocity for security check + + int windowSize_; // Number of samples in the averaging window + Vector6 vFiltered_; // Base velocity (in base frame) filtered by averaging window + std::deque<double> vx_queue_, vy_queue_, vz_queue_; // Queues that hold samples + + Vector18 qRef_; // Configuration vector in ideal world frame + Vector18 vRef_; // Velocity vector in ideal world frame + Vector6 baseVelRef_; // Reference velocity vector + Vector6 baseAccRef_; // Reference acceleration vector + Matrix3 oRh_; // Rotation between horizontal and world frame + Matrix3 hRb_; // Rotation between base and horizontal frame + Vector3 oTh_; // Translation between horizontal and world frame + Vector6 h_v_; // Velocity vector in horizontal frame + Vector6 h_vFiltered_; // Base velocity (in horizontal frame) filtered by averaging window }; #endif // ESTIMATOR_H_INCLUDED diff --git a/python/Estimator.cpp b/python/Estimator.cpp index 2a71eed6d49be2e4c3211254bd7f4c0919161b09..4095a258b0282080489146fe81f6d0919e524187 100644 --- a/python/Estimator.cpp +++ b/python/Estimator.cpp @@ -9,41 +9,37 @@ struct EstimatorVisitor : public bp::def_visitor<EstimatorVisitor<Estimator>> { cl.def(bp::init<>(bp::arg(""), "Default constructor.")) .def("initialize", &Estimator::initialize, bp::args("params"), "Initialize Estimator from Python.\n") - .def("security_check", &Estimator::security_check, bp::args("tau_ff"), "Run security check.\n") - .def("updateState", &Estimator::updateState, bp::args("joystick_v_ref", "gait"), "Update robot state.\n") + .def("update_reference_state", &Estimator::updateReferenceState, bp::args("v_ref"), "Update robot state.\n") - .def("getQFilt", &Estimator::getQFilt, "Get filtered configuration.\n") - .def("getVFilt", &Estimator::getVFilt, "Get filtered velocity.\n") - .def("getVSecu", &Estimator::getVSecu, "Get filtered velocity for security check.\n") - .def("getVFiltBis", &Estimator::getVFiltBis, "Get filtered velocity.\n") - .def("getRPY", &Estimator::getRPY, "Get Roll Pitch Yaw.\n") - .def("getFeetStatus", &Estimator::getFeetStatus, "") - .def("getFeetGoals", &Estimator::getFeetGoals, "") - .def("getFKLinVel", &Estimator::getFKLinVel, "") - .def("getFKXYZ", &Estimator::getFKXYZ, "") - .def("getXYZMeanFeet", &Estimator::getXYZMeanFeet, "") - .def("getFiltLinVel", &Estimator::getFiltLinVel, "") - .def("getFilterVelX", &Estimator::getFilterVelX, "") - .def("getFilterVelDX", &Estimator::getFilterVelDX, "") - .def("getFilterVelAlpha", &Estimator::getFilterVelAlpha, "") - .def("getFilterVelFiltX", &Estimator::getFilterVelFiltX, "") - .def("getFilterPosX", &Estimator::getFilterPosX, "") - .def("getFilterPosDX", &Estimator::getFilterPosDX, "") - .def("getFilterPosAlpha", &Estimator::getFilterPosAlpha, "") - .def("getFilterPosFiltX", &Estimator::getFilterPosFiltX, "") - .def("getQUpdated", &Estimator::getQUpdated, "") - .def("getVUpdated", &Estimator::getVUpdated, "") - .def("getVRef", &Estimator::getVRef, "") - .def("getARef", &Estimator::getARef, "") - .def("getHV", &Estimator::getHV, "") - .def("getHVWindowed", &Estimator::getHVWindowed, "") - .def("getoRb", &Estimator::getoRb, "") - .def("getoRh", &Estimator::getoRh, "") - .def("gethRb", &Estimator::gethRb, "") - .def("getoTh", &Estimator::getoTh, "") - .def("getYawEstim", &Estimator::getYawEstim, "") + .def("get_q_estimate", &Estimator::getQEstimate, "Get filtered configuration.\n") + .def("get_v_estimate", &Estimator::getVEstimate, "Get filtered velocity.\n") + .def("get_v_security", &Estimator::getVSecurity, "Get filtered velocity for security check.\n") + .def("get_feet_status", &Estimator::getFeetStatus, "") + .def("get_feet_targets", &Estimator::getFeetTargets, "") + .def("get_base_velocity_FK", &Estimator::getBaseVelocityFK, "") + .def("get_base_position_FK", &Estimator::getBasePositionFK, "") + .def("get_feet_position_barycenter", &Estimator::getFeetPositionBarycenter, "") + .def("get_b_base_velocity", &Estimator::getBBaseVelocity, "") + .def("get_filter_vel_X", &Estimator::getFilterVelX, "") + .def("get_filter_vel_DX", &Estimator::getFilterVelDX, "") + .def("get_filter_vel_Alpha", &Estimator::getFilterVelAlpha, "") + .def("get_filter_vel_FiltX", &Estimator::getFilterVelFiltX, "") + .def("get_filter_pos_X", &Estimator::getFilterPosX, "") + .def("get_filter_pos_DX", &Estimator::getFilterPosDX, "") + .def("get_filter_pos_Alpha", &Estimator::getFilterPosAlpha, "") + .def("get_filter_pos_FiltX", &Estimator::getFilterPosFiltX, "") + .def("get_q_reference", &Estimator::getQReference, "") + .def("get_v_reference", &Estimator::getVReference, "") + .def("get_base_vel_ref", &Estimator::getBaseVelRef, "") + .def("get_base_acc_ref", &Estimator::getBaseAccRef, "") + .def("get_h_v", &Estimator::getHV, "") + .def("get_v_filtered", &Estimator::getVFiltered, "Get filtered velocity.\n") + .def("get_h_v_filtered", &Estimator::getHVFiltered, "") + .def("get_oRh", &Estimator::getoRh, "") + .def("get_hRb", &Estimator::gethRb, "") + .def("get_oTh", &Estimator::getoTh, "") - .def("run_filter", &Estimator::run_filter, + .def("run", &Estimator::run, bp::args("gait", "goals", "baseLinearAcceleration", "baseAngularVelocity", "baseOrientation", "q_mes", "v_mes", "dummyPos", "b_baseVel"), "Run Estimator from Python.\n"); diff --git a/scripts/Controller.py b/scripts/Controller.py index ebf10c930db941ac1e0cb2e49d25e978aaf5e226..8ea72b260f8be4c764752b9b42ad42a0f3c8def5 100644 --- a/scripts/Controller.py +++ b/scripts/Controller.py @@ -85,6 +85,8 @@ class Controller: # Parameters definition # ######################################################################## + self.q_security = np.array([1.2, 2.1, 3.14, 1.2, 2.1, 3.14, 1.2, 2.1, 3.14, 1.2, 2.1, 3.14]) + # Init joint torques to correct shape self.jointTorques = np.zeros((12, 1)) @@ -288,32 +290,32 @@ class Controller: self.last_b_vel = b_baseVel_perfect self.n_nan = 0 - self.estimator.run_filter(self.gait.getCurrentGait(), - self.footTrajectoryGenerator.getFootPosition(), - device.imu.linear_acceleration, - device.imu.gyroscope, - device.imu.attitude_euler, - device.joints.positions, - device.joints.velocities, - q_perfect, b_baseVel_perfect) + self.estimator.run(self.gait.getCurrentGait(), + self.footTrajectoryGenerator.getFootPosition(), + device.imu.linear_acceleration, + device.imu.gyroscope, + device.imu.attitude_euler, + device.joints.positions, + device.joints.velocities, + q_perfect, b_baseVel_perfect) # Update state vectors of the robot (q and v) + transformation matrices between world and horizontal frames - self.estimator.updateState(self.joystick.getVRef(), self.gait) - oRh = self.estimator.getoRh() - hRb = self.estimator.gethRb() - oTh = self.estimator.getoTh().reshape((3, 1)) - self.a_ref[:6, 0] = self.estimator.getARef() - self.v_ref[:6, 0] = self.estimator.getVRef() - self.h_v[:6, 0] = self.estimator.getHV() - self.h_v_windowed[:6, 0] = self.estimator.getHVWindowed() + self.estimator.update_reference_state(self.joystick.getVRef()) + oRh = self.estimator.get_oRh() + hRb = self.estimator.get_hRb() + oTh = self.estimator.get_oTh().reshape((3, 1)) + self.a_ref[:6, 0] = self.estimator.get_base_acc_ref() + self.v_ref[:6, 0] = self.estimator.get_base_vel_ref() + self.h_v[:6, 0] = self.estimator.get_h_v() + self.h_v_windowed[:6, 0] = self.estimator.get_h_v_filtered() if self.solo3D: - self.q[:3, 0] = self.estimator.getQFilt()[:3] - self.q[6:, 0] = self.estimator.getQFilt()[7:] - self.q[3:6] = quaternionToRPY(self.estimator.getQFilt()[3:7]) + self.q[:3, 0] = self.estimator.get_q_estimate()[:3] + self.q[6:, 0] = self.estimator.get_q_estimate()[7:] + self.q[3:6] = quaternionToRPY(self.estimator.get_q_estimate()[3:7]) else: - self.q[:, 0] = self.estimator.getQUpdated() - self.v[:, 0] = self.estimator.getVUpdated() - self.yaw_estim = self.estimator.getYawEstim() + self.q[:, 0] = self.estimator.get_q_reference() + self.v[:, 0] = self.estimator.get_v_reference() + self.yaw_estim = self.q[5, 0] # Quantities go through a 1st order low pass filter with fc = 15 Hz (avoid >25Hz foldback) self.q_filter[:6, 0] = self.filter_mpc_q.filter(self.q[:6, 0], True) @@ -422,7 +424,7 @@ class Controller: self.q_wbc[6:, 0] = self.wbcWrapper.qdes[:] # Update velocity vector for wbc - self.dq_wbc[:6, 0] = self.estimator.getVFilt()[:6] # Â Velocities in base frame (not horizontal frame!) + self.dq_wbc[:6, 0] = self.estimator.get_v_estimate()[:6] # Â Velocities in base frame (not horizontal frame!) self.dq_wbc[6:, 0] = self.wbcWrapper.vdes[:] # with reference angular velocities of previous loop # Feet command position, velocity and acceleration in base frame @@ -567,19 +569,23 @@ class Controller: """ Check if the command is fine and set the command to zero in case of error """ + if not (self.error or self.joystick.getStop()): - error_flag = self.estimator.security_check(self.wbcWrapper.tau_ff) - if (error_flag != 0): + if (np.abs(self.estimator.get_q_estimate()[7:]) > self.q_security).any(): + print("-- POSITION LIMIT ERROR --") + print(self.estimator.get_q_estimate()[7:]) + print(np.abs(self.estimator.get_q_estimate()[7:]) > self.q_security) + self.error = True + elif (np.abs(self.estimator.get_v_security()) > 100.).any(): + print("-- VELOCITY TOO HIGH ERROR --") + print(self.estimator.get_v_security()) + print(np.abs(self.estimator.get_v_security()) > 100.) + self.error = True + elif (np.abs(self.wbcWrapper.tau_ff) > 8.0).any(): + print("-- FEEDFORWARD TORQUES TOO HIGH ERROR --") + print(self.wbcWrapper.tau_ff) + print(np.abs(self.wbcWrapper.tau_ff) > 8.0) self.error = True - if (error_flag == 1): - print("-- POSITION LIMIT ERROR --") - print(self.estimator.getQFilt()[7:]) - elif (error_flag == 2): - print("-- VELOCITY TOO HIGH ERROR --") - print(self.estimator.getVSecu()) - else: - print("-- FEEDFORWARD TORQUES TOO HIGH ERROR --") - print(self.wbcWrapper.tau_ff) def clamp(self, num, min_value=None, max_value=None): clamped = False diff --git a/scripts/LoggerControl.py b/scripts/LoggerControl.py index a29d3ca122099fafc22c92d2ee6291e356d97a2a..667173c9822137bbe4918ed8e7925489725cad1b 100644 --- a/scripts/LoggerControl.py +++ b/scripts/LoggerControl.py @@ -139,31 +139,31 @@ class LoggerControl(): self.joy_v_ref[self.i] = joystick.getVRef() # Logging from estimator - self.esti_feet_status[self.i] = estimator.getFeetStatus() - self.esti_feet_goals[self.i] = estimator.getFeetGoals() - self.esti_q_filt[self.i] = estimator.getQFilt() - self.esti_q_up[self.i] = estimator.getQUpdated() - self.esti_v_filt[self.i] = estimator.getVFilt() - self.esti_v_filt_bis[self.i, :6] = estimator.getVFiltBis() - self.esti_v_up[self.i] = estimator.getVUpdated() - self.esti_v_ref[self.i] = estimator.getVRef() - self.esti_v_secu[self.i] = estimator.getVSecu() - self.esti_a_ref[self.i] = estimator.getARef() - - self.esti_FK_lin_vel[self.i] = estimator.getFKLinVel() - self.esti_FK_xyz[self.i] = estimator.getFKXYZ() - self.esti_xyz_mean_feet[self.i] = estimator.getXYZMeanFeet() - self.esti_filt_lin_vel[self.i] = estimator.getFiltLinVel() - - self.esti_HP_x[self.i] = estimator.getFilterVelX() - self.esti_HP_dx[self.i] = estimator.getFilterVelDX() - self.esti_HP_alpha[self.i] = estimator.getFilterVelAlpha() - self.esti_HP_filt_x[self.i] = estimator.getFilterVelFiltX() - - self.esti_LP_x[self.i] = estimator.getFilterPosX() - self.esti_LP_dx[self.i] = estimator.getFilterPosDX() - self.esti_LP_alpha[self.i] = estimator.getFilterPosAlpha() - self.esti_LP_filt_x[self.i] = estimator.getFilterPosFiltX() + self.esti_feet_status[self.i] = estimator.get_feet_status() + self.esti_feet_goals[self.i] = estimator.get_feet_targets() + self.esti_q_filt[self.i] = estimator.get_q_estimate() + self.esti_q_up[self.i] = estimator.get_q_reference() + self.esti_v_filt[self.i] = estimator.get_v_estimate() + self.esti_v_filt_bis[self.i, :6] = estimator.get_v_filtered() + self.esti_v_up[self.i] = estimator.get_v_reference() + self.esti_v_ref[self.i] = estimator.get_base_vel_ref() + self.esti_v_secu[self.i] = estimator.get_v_security() + self.esti_a_ref[self.i] = estimator.get_base_acc_ref() + + self.esti_FK_lin_vel[self.i] = estimator.get_base_velocity_FK() + self.esti_FK_xyz[self.i] = estimator.get_base_position_FK() + self.esti_xyz_mean_feet[self.i] = estimator.get_feet_position_barycenter() + self.esti_filt_lin_vel[self.i] = estimator.get_b_base_velocity() + + self.esti_HP_x[self.i] = estimator.get_filter_vel_X() + self.esti_HP_dx[self.i] = estimator.get_filter_vel_DX() + self.esti_HP_alpha[self.i] = estimator.get_filter_vel_Alpha() + self.esti_HP_filt_x[self.i] = estimator.get_filter_vel_FiltX() + + self.esti_LP_x[self.i] = estimator.get_filter_pos_X() + self.esti_LP_dx[self.i] = estimator.get_filter_pos_DX() + self.esti_LP_alpha[self.i] = estimator.get_filter_pos_Alpha() + self.esti_LP_filt_x[self.i] = estimator.get_filter_pos_FiltX() # Logging from the main loop self.loop_o_q[self.i] = controller.q[:, 0] diff --git a/src/ComplementaryFilter.cpp b/src/ComplementaryFilter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64190ddd98d9fd988a4c46362761d5416f7d0e11 --- /dev/null +++ b/src/ComplementaryFilter.cpp @@ -0,0 +1,37 @@ +#include "qrw/ComplementaryFilter.hpp" + +ComplementaryFilter::ComplementaryFilter() + : dt_(0.), + HighPass_(Vector3::Zero()), + LowPass_(Vector3::Zero()), + alpha_(Vector3::Zero()), + x_(Vector3::Zero()), + dx_(Vector3::Zero()), + filteredX_(Vector3::Zero()) {} + +ComplementaryFilter::ComplementaryFilter(double dt, Vector3 HighPass, Vector3 LowPass) + : dt_(dt), + HighPass_(HighPass), + LowPass_(LowPass), + alpha_(Vector3::Zero()), + x_(Vector3::Zero()), + dx_(Vector3::Zero()), + filteredX_(Vector3::Zero()) {} + +void ComplementaryFilter::initialize(double dt, Vector3 HighPass, Vector3 LowPass) { + dt_ = dt; + HighPass_ = HighPass; + LowPass_ = LowPass; +} + +Vector3 ComplementaryFilter::compute(Vector3 const& x, Vector3 const& dx, Vector3 const& alpha) { + alpha_ = alpha; + x_ = x; + dx_ = dx; + + HighPass_ = alpha.cwiseProduct(HighPass_ + dx_ * dt_); + LowPass_ = alpha.cwiseProduct(LowPass_) + (Vector3::Ones() - alpha).cwiseProduct(x_); + filteredX_ = HighPass_ + LowPass_; + + return filteredX_; +} \ No newline at end of file diff --git a/src/Controller.cpp b/src/Controller.cpp index bb5810ce887c3bc95e10e3dc7ee7588c9a99c0d0..e9b787c478fad0094d7ca3190309862bcc74a5f2 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -1,7 +1,7 @@ #include "qrw/Controller.hpp" -#include "pinocchio/math/rpy.hpp" #include "pinocchio/algorithm/crba.hpp" +#include "pinocchio/math/rpy.hpp" Controller::Controller() : P(Vector12::Zero()), @@ -61,27 +61,26 @@ void Controller::compute(FakeRobot* robot) { joystick.update_v_ref(k, params_->velID, gait.getIsStatic()); // Process state estimator - estimator.run_filter(gait.getCurrentGait(), footTrajectoryGenerator.getFootPosition(), - robot->imu->GetLinearAcceleration(), robot->imu->GetGyroscope(), robot->imu->GetAttitudeEuler(), - robot->joints->GetPositions(), robot->joints->GetVelocities(), Vector3::Zero(), - Vector3::Zero()); + estimator.run(gait.getCurrentGait(), footTrajectoryGenerator.getFootPosition(), robot->imu->GetLinearAcceleration(), + robot->imu->GetGyroscope(), robot->imu->GetAttitudeEuler(), robot->joints->GetPositions(), + robot->joints->GetVelocities(), Vector3::Zero(), Vector3::Zero()); // Update state vectors of the robot (q and v) + transformation matrices between world and horizontal frames - estimator.updateState(joystick.getVRef(), gait); + estimator.updateReferenceState(joystick.getVRef()); // Update gait gait.updateGait(k, k_mpc, joystick.getJoystickCode()); // Quantities go through a 1st order low pass filter with fc = 15 Hz (avoid >25Hz foldback) - q_filt_mpc.head(6) = filter_mpc_q.filter(estimator.getQUpdated().head(6), true); - q_filt_mpc.tail(12) = estimator.getQUpdated().tail(12); + q_filt_mpc.head(6) = filter_mpc_q.filter(estimator.getQReference().head(6), true); + q_filt_mpc.tail(12) = estimator.getQReference().tail(12); h_v_filt_mpc = filter_mpc_v.filter(estimator.getHV().head(6), false); - vref_filt_mpc = filter_mpc_vref.filter(estimator.getVRef().head(6), false); + vref_filt_mpc = filter_mpc_vref.filter(estimator.getBaseVelRef().head(6), false); // Compute target footstep based on current and reference velocities o_targetFootstep = footstepPlanner.updateFootsteps( - (k % k_mpc == 0) && (k != 0), static_cast<int>(k_mpc - (k % k_mpc)), estimator.getQUpdated().head(18), - estimator.getHVWindowed().head(6), estimator.getVRef().head(6)); + (k % k_mpc == 0) && (k != 0), static_cast<int>(k_mpc - (k % k_mpc)), estimator.getQReference().head(18), + estimator.getHVFiltered().head(6), estimator.getBaseVelRef().head(6)); // Run state planner (outputs the reference trajectory of the base) statePlanner.computeReferenceStates(q_filt_mpc.head(6), h_v_filt_mpc, vref_filt_mpc); @@ -132,7 +131,7 @@ void Controller::compute(FakeRobot* robot) { q_wbc.tail(12) = wbcWrapper.get_qdes(); // with reference angular positions of previous loop // Update velocity vector for wbc - dq_wbc.head(6) = estimator.getVFilt().head(6); // Velocities in base frame (not horizontal frame!) + dq_wbc.head(6) = estimator.getVEstimate().head(6); // Velocities in base frame (not horizontal frame!) dq_wbc.tail(12) = wbcWrapper.get_vdes(); // with reference angular velocities of previous loop // Desired position, orientation and velocities of the base @@ -245,16 +244,32 @@ void Controller::init_robot(Params& params) { } void Controller::security_check() { + Vector12 q_security_ = (Vector3(1.2, 2.1, 3.14)).replicate<4, 1>(); + + if (((estimator.getQEstimate().tail(12).cwiseAbs()).array() > q_security_.array()).any()) { + std::cout << "Position limit error " + << ((estimator.getQEstimate().tail(12).cwiseAbs()).array() > q_security_.array()).transpose() << std::endl; + error_flag = 1; + } else if (((estimator.getVSecurity().cwiseAbs()).array() > 100.0).any()) { + std::cout << "Velocity limit error " << ((estimator.getVSecurity().cwiseAbs()).array() > 100.0).transpose() + << std::endl; + error_flag = 2; + } else if (((tau_ff.cwiseAbs()).array() > 8.0).any()) { + std::cout << "Feedforward limit error " << ((tau_ff.cwiseAbs()).array() > 8.0).transpose() << std::endl; + error_flag = 3; + } else { + error_flag = 0; + } + if (error_flag == 0 && !error) { - error_flag = estimator.security_check(tau_ff); // Check position, velocity and feedforward torque limits if (error_flag != 0) { error = true; switch (error_flag) { case 1: // Out of position limits - error_value = estimator.getQFilt().tail(12) * 180 / 3.1415; + error_value = estimator.getQEstimate().tail(12) * 180 / 3.1415; break; case 2: // Out of velocity limits - error_value = estimator.getVSecu(); + error_value = estimator.getVSecurity(); break; default: // Out of torques limits error_value = tau_ff; diff --git a/src/Estimator.cpp b/src/Estimator.cpp index cffeeabcaeee4bb33931fa408d0b6d7f2aac5e50..7485d1841f472e6b3961c6cc34d7557fe5dbe930 100644 --- a/src/Estimator.cpp +++ b/src/Estimator.cpp @@ -1,482 +1,283 @@ -#include <example-robot-data/path.hpp> - #include "qrw/Estimator.hpp" -//////////////////////////////////// -// Complementary filter functions -//////////////////////////////////// - -ComplementaryFilter::ComplementaryFilter() - : x_(Vector3::Zero()), - dx_(Vector3::Zero()), - HP_x_(Vector3::Zero()), - LP_x_(Vector3::Zero()), - alpha_(Vector3::Zero()), - filt_x_(Vector3::Zero()) {} - -void ComplementaryFilter::initialize(double dt, Vector3 HP_x, Vector3 LP_x) { - dt_ = dt; - HP_x_ = HP_x; - LP_x_ = LP_x; -} - -Vector3 ComplementaryFilter::compute(Vector3 const& x, Vector3 const& dx, Vector3 const& alpha) { - // For logging - x_ = x; - dx_ = dx; - alpha_ = alpha; - - // Process high pass filter - HP_x_ = alpha.cwiseProduct(HP_x_ + dx_ * dt_); - - // Process low pass filter - LP_x_ = alpha.cwiseProduct(LP_x_) + (Vector3::Ones() - alpha).cwiseProduct(x_); - - // Add both to get the filtered output - filt_x_ = HP_x_ + LP_x_; - - return filt_x_; -} +#include <example-robot-data/path.hpp> -///////////////////////// -// Estimator functions -///////////////////////// +#include "pinocchio/algorithm/compute-all-terms.hpp" +#include "pinocchio/algorithm/frames.hpp" +#include "pinocchio/math/rpy.hpp" +#include "pinocchio/parsers/urdf.hpp" Estimator::Estimator() - : dt_wbc(0.0), - alpha_secu_(0.0), - offset_yaw_IMU_(0.0), - perfect_estimator(false), - solo3D(false), - N_SIMULATION(0), - k_log_(0), - IMU_lin_acc_(Vector3::Zero()), - IMU_ang_vel_(Vector3::Zero()), - IMU_RPY_(Vector3::Zero()), - oRb_(Matrix3::Identity()), - IMU_ang_pos_(pinocchio::SE3::Quaternion(1.0, 0.0, 0.0, 0.0)), - actuators_pos_(Vector12::Zero()), - actuators_vel_(Vector12::Zero()), + : perfectEstimator_(false), + solo3D_(false), + dt_(0.0), + initialized_(false), + feetFrames_(Vector4::Zero()), + footRadius_(0.155), + alphaPos_({0.995, 0.995, 0.9}), + alphaVelMax_(1.), + alphaVelMin_(0.97), + alphaSecurity_(0.), + IMUYawOffset_(0.), + IMULinearAcceleration_(Vector3::Zero()), + IMUAngularVelocity_(Vector3::Zero()), + IMURpy_(Vector3::Zero()), + IMUQuat_(pinocchio::SE3::Quaternion(1.0, 0.0, 0.0, 0.0)), + qActuators_(Vector12::Zero()), + vActuators_(Vector12::Zero()), + phaseRemainingDuration_(0), + feetStancePhaseDuration_(Vector4::Zero()), + feetStatus_(Vector4::Zero()), + feetTargets_(Matrix34::Zero()), q_FK_(Vector19::Zero()), v_FK_(Vector18::Zero()), - feet_status_(MatrixN::Zero(1, 4)), - feet_goals_(MatrixN::Zero(3, 4)), - FK_lin_vel_(Vector3::Zero()), - FK_xyz_(Vector3::Zero()), - b_filt_lin_vel_(Vector3::Zero()), - xyz_mean_feet_(Vector3::Zero()), - k_since_contact_(Eigen::Matrix<double, 1, 4>::Zero()), - q_filt_(Vector19::Zero()), - v_filt_(Vector18::Zero()), - v_secu_(Vector12::Zero()), - q_filt_dyn_(VectorN::Zero(19, 1)), - v_filt_dyn_(VectorN::Zero(18, 1)), - v_secu_dyn_(VectorN::Zero(12, 1)), - q_up_(VectorN::Zero(18)), - v_up_(VectorN::Zero(18)), - v_ref_(VectorN::Zero(6)), - a_ref_(VectorN::Zero(6)), - h_v_(VectorN::Zero(6)), + baseVelocityFK_(Vector3::Zero()), + basePositionFK_(Vector3::Zero()), + b_baseVelocity_(Vector3::Zero()), + feetPositionBarycenter_(Vector3::Zero()), + qEstimate_(Vector19::Zero()), + vEstimate_(Vector18::Zero()), + vSecurity_(Vector12::Zero()), + windowSize_(0), + vFiltered_(Vector6::Zero()), + qRef_(Vector18::Zero()), + vRef_(Vector18::Zero()), + baseVelRef_(Vector6::Zero()), + baseAccRef_(Vector6::Zero()), oRh_(Matrix3::Identity()), hRb_(Matrix3::Identity()), oTh_(Vector3::Zero()), - yaw_estim_(0.0), - N_queue_(0), - v_filt_bis_(VectorN::Zero(6, 1)), - h_v_windowed_(VectorN::Zero(6, 1)) {} + h_v_(Vector6::Zero()), + h_vFiltered_(Vector6::Zero()) { + b_M_IMU_ = pinocchio::SE3(pinocchio::SE3::Quaternion(1.0, 0.0, 0.0, 0.0), Vector3(0.1163, 0.0, 0.02)); + q_FK_(6) = 1.0; + qEstimate_(6) = 1.0; +} void Estimator::initialize(Params& params) { - dt_wbc = params.dt_wbc; - N_SIMULATION = params.N_SIMULATION; - perfect_estimator = params.perfect_estimator; - solo3D = params.solo3D; + dt_ = params.dt_wbc; + perfectEstimator_ = params.perfect_estimator; + solo3D_ = params.solo3D; // Filtering estimated linear velocity - int k_mpc = static_cast<int>(std::round(params.dt_mpc / params.dt_wbc)); - N_queue_ = static_cast<int>(k_mpc * params.gait.rows() / params.N_periods); - vx_queue_.resize(N_queue_, 0.0); // List full of 0.0 - vy_queue_.resize(N_queue_, 0.0); // List full of 0.0 - vz_queue_.resize(N_queue_, 0.0); // List full of 0.0 - wR_queue_.resize(N_queue_, 0.0); // List full of 0.0 - wP_queue_.resize(N_queue_, 0.0); // List full of 0.0 - wY_queue_.resize(N_queue_, 0.0); // List full of 0.0 + int k_mpc = (int)(std::round(params.dt_mpc / params.dt_wbc)); + windowSize_ = (int)(k_mpc * params.gait.rows() / params.N_periods); + vx_queue_.resize(windowSize_, 0.0); // List full of 0.0 + vy_queue_.resize(windowSize_, 0.0); // List full of 0.0 + vz_queue_.resize(windowSize_, 0.0); // List full of 0.0 // Filtering velocities used for security checks - double fc = 6.0; // Cut frequency - double y = 1 - std::cos(2 * M_PI * fc * dt_wbc); - alpha_secu_ = -y + std::sqrt(y * y + 2 * y); - - FK_xyz_(2, 0) = params.h_ref; - - filter_xyz_vel_.initialize(dt_wbc, Vector3::Zero(), Vector3::Zero()); - filter_xyz_pos_.initialize(dt_wbc, Vector3::Zero(), FK_xyz_); + double fc = 6.0; + double y = 1 - std::cos(2 * M_PI * 6. * dt_); + alphaSecurity_ = -y + std::sqrt(y * y + 2 * y); + + // Initialize Quantities + basePositionFK_(2) = params.h_ref; + velocityFilter_.initialize(dt_, Vector3::Zero(), Vector3::Zero()); + positionFilter_.initialize(dt_, Vector3::Zero(), basePositionFK_); + qRef_(2, 0) = params.h_ref; + qRef_.tail(12) = Vector12(params.q_init.data()); + + // Initialize Pinocchio + const std::string filename = std::string(EXAMPLE_ROBOT_DATA_MODEL_DIR "/solo_description/robots/solo12.urdf"); + pinocchio::urdf::buildModel(filename, pinocchio::JointModelFreeFlyer(), velocityModel_, false); + pinocchio::urdf::buildModel(filename, pinocchio::JointModelFreeFlyer(), positionModel_, false); + velocityData_ = pinocchio::Data(velocityModel_); + positionData_ = pinocchio::Data(positionModel_); + pinocchio::computeAllTerms(velocityModel_, velocityData_, qEstimate_, vEstimate_); + pinocchio::computeAllTerms(positionModel_, positionData_, qEstimate_, vEstimate_); + feetFrames_ << (int)positionModel_.getFrameId("FL_FOOT"), (int)positionModel_.getFrameId("FR_FOOT"), + (int)positionModel_.getFrameId("HL_FOOT"), (int)positionModel_.getFrameId("HR_FOOT"); +} - _1Mi_ = pinocchio::SE3(pinocchio::SE3::Quaternion(1.0, 0.0, 0.0, 0.0), Vector3(0.1163, 0.0, 0.02)); +void Estimator::run(MatrixN const& gait, MatrixN const& feetTargets, VectorN const& baseLinearAcceleration, + VectorN const& baseAngularVelocity, VectorN const& baseOrientation, VectorN const& q, + VectorN const& v, VectorN const& perfectPosition, Vector3 const& b_perfectVelocity) { + updatFeetStatus(gait, feetTargets); + updateIMUData(baseLinearAcceleration, baseAngularVelocity, baseOrientation, perfectPosition); + updateJointData(q, v); - q_security_ = (Vector3(1.2, 2.1, 3.14)).replicate<4, 1>(); -// q_security_ = (Vector3(M_PI * 0.4, M_PI * 80 / 180, M_PI)).replicate<4, 1>(); + updateForwardKinematics(); + computeFeetPositionBarycenter(); - q_FK_(6, 0) = 1.0; // Last term of the quaternion - q_filt_(6, 0) = 1.0; // Last term of the quaternion - q_filt_dyn_(6, 0) = 1.0; // Last term of the quaternion + estimateVelocity(b_perfectVelocity); + estimatePosition(perfectPosition.head(3)); - q_up_(2, 0) = params.h_ref; // Reference height - q_up_.tail(12) = Vector12(params.q_init.data()); // Actuator initial positions + filterVelocity(); - // Path to the robot URDF - const std::string filename = - std::string(EXAMPLE_ROBOT_DATA_MODEL_DIR "/solo_description/robots/solo12.urdf"); + vSecurity_ = (1 - alphaSecurity_) * vActuators_ + alphaSecurity_ * vSecurity_; +} - // Build model from urdf - pinocchio::urdf::buildModel(filename, pinocchio::JointModelFreeFlyer(), model_, false); - pinocchio::urdf::buildModel(filename, pinocchio::JointModelFreeFlyer(), model_for_xyz_, false); +void Estimator::updateReferenceState(VectorN const& vRef) { + // Update reference acceleration and velocities + Matrix3 Rz = pinocchio::rpy::rpyToMatrix(0., 0., -baseVelRef_[5] * dt_); + baseAccRef_.head(3) = (vRef.head(3) - Rz * baseVelRef_.head(3)) / dt_; + baseAccRef_.tail(3) = (vRef.tail(3) - Rz * baseVelRef_.tail(3)) / dt_; + baseVelRef_ = vRef; - // Construct data from model - data_ = pinocchio::Data(model_); - data_for_xyz_ = pinocchio::Data(model_for_xyz_); + // Update position and velocity state vectors + Rz = pinocchio::rpy::rpyToMatrix(0., 0., qRef_[5]); + vRef_.head(2) = Rz.topLeftCorner(2, 2) * baseVelRef_.head(2); + vRef_[5] = baseVelRef_[5]; + vRef_.tail(12) = vActuators_; + + qRef_.head(2) += vRef_.head(2) * dt_; + qRef_[2] = qEstimate_[2]; + qRef_.segment(3, 2) = IMURpy_.head(2); + qRef_[5] += baseVelRef_[5] * dt_; + qRef_.tail(12) = qActuators_; + + // Transformation matrices + hRb_ = pinocchio::rpy::rpyToMatrix(IMURpy_[0], IMURpy_[1], 0.); + oRh_ = pinocchio::rpy::rpyToMatrix(0., 0., qRef_[5]); + oTh_.head(2) = qRef_.head(2); - // Update all the quantities of the model - pinocchio::computeAllTerms(model_, data_, q_filt_, v_filt_); - pinocchio::computeAllTerms(model_for_xyz_, data_for_xyz_, q_filt_, v_filt_); + // Express estimated velocity and filtered estimated velocity in horizontal frame + h_v_.head(3) = hRb_ * vEstimate_.head(3); + h_v_.tail(3) = hRb_ * vEstimate_.segment(3, 3); + h_vFiltered_.head(3) = hRb_ * vFiltered_.head(3); + h_vFiltered_.tail(3) = hRb_ * vFiltered_.tail(3); } -void Estimator::get_data_IMU(Vector3 const& baseLinearAcceleration, Vector3 const& baseAngularVelocity, - Vector3 const& baseOrientation, VectorN const& q_perfect) { - // Linear acceleration of the trunk (base frame) - IMU_lin_acc_ = baseLinearAcceleration; - - // Angular velocity of the trunk (base frame) - IMU_ang_vel_ = baseAngularVelocity; +void Estimator::updatFeetStatus(MatrixN const& gait, MatrixN const& feetTargets) { + feetStatus_ = gait.row(0); + feetTargets_ = feetTargets; - // Angular position of the trunk (local frame) - IMU_RPY_ = baseOrientation; + feetStancePhaseDuration_ += feetStatus_; + feetStancePhaseDuration_ = feetStancePhaseDuration_.cwiseProduct(feetStatus_); - if (k_log_ <= 1) { - offset_yaw_IMU_ = IMU_RPY_(2, 0); + phaseRemainingDuration_ = 1; + while (feetStatus_.isApprox((Vector4)gait.row(phaseRemainingDuration_))) { + phaseRemainingDuration_++; } - IMU_RPY_(2, 0) -= offset_yaw_IMU_; // Remove initial offset of IMU - - if (solo3D) { - IMU_RPY_.tail(1) = q_perfect.tail(1); // Yaw angle from motion capture - } - - IMU_ang_pos_ = - pinocchio::SE3::Quaternion(pinocchio::rpy::rpyToMatrix(IMU_RPY_(0, 0), IMU_RPY_(1, 0), IMU_RPY_(2, 0))); - // Above could be commented since IMU_ang_pos yaw is not used anywhere and instead - // replace by: IMU_ang_pos_ = baseOrientation_ } -void Estimator::get_data_joints(Vector12 const& q_mes, Vector12 const& v_mes) { - actuators_pos_ = q_mes; - actuators_vel_ = v_mes; -} +void Estimator::updateIMUData(Vector3 const& baseLinearAcceleration, Vector3 const& baseAngularVelocity, + Vector3 const& baseOrientation, VectorN const& perfectPosition) { + IMULinearAcceleration_ = baseLinearAcceleration; + IMUAngularVelocity_ = baseAngularVelocity; + IMURpy_ = baseOrientation; -void Estimator::get_data_FK(Eigen::Matrix<double, 1, 4> const& feet_status) { - // Update estimator FK model - q_FK_.tail(12) = actuators_pos_; // Position of actuators - v_FK_.tail(12) = actuators_vel_; // Velocity of actuators - // Position and orientation of the base remain at 0 - // Linear and angular velocities of the base remain at 0 - - // Update model used for the forward kinematics - q_FK_.block(3, 0, 4, 1) << 0.0, 0.0, 0.0, 1.0; - pinocchio::forwardKinematics(model_, data_, q_FK_, v_FK_); - // pin.updateFramePlacements(self.model, self.data) - - // Update model used for the forward geometry - q_FK_.block(3, 0, 4, 1) = IMU_ang_pos_.coeffs(); - pinocchio::forwardKinematics(model_for_xyz_, data_for_xyz_, q_FK_); - - // Get estimated velocity from updated model - int cpt = 0; - Vector3 vel_est = Vector3::Zero(); - Vector3 xyz_est = Vector3::Zero(); - for (int j = 0; j < 4; j++) { - // Consider only feet in contact + Security margin after the contact switch - if (feet_status(0, j) == 1.0 && k_since_contact_[j] >= 16) { - // Estimated velocity of the base using the considered foot - Vector3 vel_estimated_baseframe = BaseVelocityFromKinAndIMU(feet_indexes_[j]); - - // Estimated position of the base using the considered foot - pinocchio::updateFramePlacement(model_for_xyz_, data_for_xyz_, feet_indexes_[j]); - Vector3 xyz_estimated = -data_for_xyz_.oMf[feet_indexes_[j]].translation(); - - // Logging - // self.log_v_est[:, i, self.k_log] = vel_estimated_baseframe[0:3, 0] - // self.log_h_est[i, self.k_log] = xyz_estimated[2] - - // Increment counter and add estimated quantities to the storage variables - cpt++; - vel_est += vel_estimated_baseframe; // Linear velocity - xyz_est += xyz_estimated; // Position - - double r_foot = 0.0155; // 31mm of diameter on meshlab - if (j <= 1) { - vel_est(0, 0) += r_foot * (actuators_vel_(1 + 3 * j, 0) - actuators_vel_(2 + 3 * j, 0)); - } else { - vel_est(0, 0) += r_foot * (actuators_vel_(1 + 3 * j, 0) + actuators_vel_(2 + 3 * j, 0)); - } - } + if (!initialized_) { + IMUYawOffset_ = IMURpy_(2); + initialized_ = true; } + IMURpy_(2) -= IMUYawOffset_; - // If at least one foot is in contact, we do the average of feet results - if (cpt > 0) { - FK_lin_vel_ = vel_est / cpt; - FK_xyz_ = xyz_est / cpt; + if (solo3D_) { + IMURpy_.tail(1) = perfectPosition.tail(1); } + + IMUQuat_ = pinocchio::SE3::Quaternion(pinocchio::rpy::rpyToMatrix(IMURpy_(0), IMURpy_(1), IMURpy_(2))); } -void Estimator::get_xyz_feet(Eigen::Matrix<double, 1, 4> const& feet_status, Matrix34 const& goals) { - int cpt = 0; - Vector3 xyz_feet = Vector3::Zero(); +void Estimator::updateJointData(Vector12 const& q, Vector12 const& v) { + qActuators_ = q; + vActuators_ = v; +} - // Consider only feet in contact - for (int j = 0; j < 4; j++) { - if (feet_status(0, j) == 1.0) { - cpt++; - xyz_feet += goals.col(j); +void Estimator::updateForwardKinematics() { + q_FK_.tail(12) = qActuators_; + v_FK_.tail(12) = vActuators_; + q_FK_.segment(3, 4) << 0., 0., 0., 1.; + pinocchio::forwardKinematics(velocityModel_, velocityData_, q_FK_, v_FK_); + + q_FK_.segment(3, 4) = IMUQuat_.coeffs(); + pinocchio::forwardKinematics(positionModel_, positionData_, q_FK_); + + int nContactFeet = 0; + Vector3 baseVelocityEstimate = Vector3::Zero(); + Vector3 basePositionEstimate = Vector3::Zero(); + for (int foot = 0; foot < 4; foot++) { + if (feetStatus_(foot) == 1. && feetStancePhaseDuration_[foot] >= 16) { + baseVelocityEstimate += computeBaseVelocityFromFoot(foot); + basePositionEstimate += computeBasePositionFromFoot(foot); + nContactFeet++; } } - // If at least one foot is in contact, we do the average of feet results - if (cpt > 0) { - xyz_mean_feet_ = xyz_feet / cpt; + if (nContactFeet > 0) { + baseVelocityFK_ = baseVelocityEstimate / nContactFeet; + basePositionFK_ = basePositionEstimate / nContactFeet; } } -Vector3 Estimator::BaseVelocityFromKinAndIMU(int contactFrameId) { - Vector3 frameVelocity = pinocchio::getFrameVelocity(model_, data_, contactFrameId, pinocchio::LOCAL).linear(); - pinocchio::updateFramePlacement(model_, data_, contactFrameId); - - // Angular velocity of the base wrt the world in the base frame (Gyroscope) - Vector3 _1w01 = IMU_ang_vel_; - // Linear velocity of the foot wrt the base in the foot frame - Vector3 _Fv1F = frameVelocity; - // Level arm between the base and the foot - Vector3 _1F = data_.oMf[contactFrameId].translation(); - // Orientation of the foot wrt the base - Matrix3 _1RF = data_.oMf[contactFrameId].rotation(); - // Linear velocity of the base wrt world in the base frame - Vector3 _1v01 = _1F.cross(_1w01) - _1RF * _Fv1F; - - // IMU and base frames have the same orientation - // _iv0i = _1v01 + self.cross3(self._1Mi.translation.ravel(), _1w01.ravel()) - - return _1v01; -} - -void Estimator::run_filter(MatrixN const& gait, MatrixN const& goals, VectorN const& baseLinearAcceleration, - VectorN const& baseAngularVelocity, VectorN const& baseOrientation, VectorN const& q_mes, - VectorN const& v_mes, VectorN const& q_perfect, Vector3 const& b_baseVel_perfect) { - feet_status_ = gait.block(0, 0, 1, 4); - feet_goals_ = goals; - - int remaining_steps = 1; // Remaining MPC steps for the current gait phase - while ((gait.block(0, 0, 1, 4)).isApprox(gait.row(remaining_steps))) { - remaining_steps++; - } - - // Update IMU data - get_data_IMU(baseLinearAcceleration, baseAngularVelocity, baseOrientation, q_perfect); - - // Angular position of the trunk - Vector4 filt_ang_pos = IMU_ang_pos_.coeffs(); - - // Angular velocity of the trunk - Vector3 filt_ang_vel = IMU_ang_vel_; - - // Update joints data - get_data_joints(q_mes, v_mes); +Vector3 Estimator::computeBaseVelocityFromFoot(int footId) { + pinocchio::updateFramePlacement(velocityModel_, velocityData_, feetFrames_[footId]); + pinocchio::SE3 contactFrame = velocityData_.oMf[feetFrames_[footId]]; + Vector3 frameVelocity = + pinocchio::getFrameVelocity(velocityModel_, velocityData_, feetFrames_[footId], pinocchio::LOCAL).linear(); - // Update nb of iterations since contact - k_since_contact_ += feet_status_; // Increment feet in stance phase - k_since_contact_ = k_since_contact_.cwiseProduct(feet_status_); // Reset feet in swing phase - - // Update forward kinematics data - get_data_FK(feet_status_); - - // Update forward geometry data - get_xyz_feet(feet_status_, goals); - - // Tune alpha depending on the state of the gait (close to contact switch or not) - double a = std::ceil(k_since_contact_.maxCoeff() * 0.1) - 1; - double b = static_cast<double>(remaining_steps); - const double n = 1; // Nb of steps of margin around contact switch + return contactFrame.translation().cross(IMUAngularVelocity_) - contactFrame.rotation() * frameVelocity; +} - const double v_max = 1.00; // Maximum alpha value - const double v_min = 0.97; // Minimum alpha value - double c = ((a + b) - 2 * n) * 0.5; - double alpha = 0.0; - if (a <= (n - 1) || b <= n) { // If we are close from contact switch - alpha = v_max; // Only trust IMU data - } else { - alpha = v_min + (v_max - v_min) * std::abs(c - (a - n)) / c; - // self.alpha = 0.997 - } +Vector3 Estimator::computeBasePositionFromFoot(int footId) { + pinocchio::updateFramePlacement(positionModel_, positionData_, feetFrames_[footId]); + Vector3 basePosition = -positionData_.oMf[feetFrames_[footId]].translation(); + basePosition(0) += footRadius_ * (vActuators_(1 + 3 * footId) + vActuators_(2 + 3 * footId)); - // Use cascade of complementary filters + return basePosition; +} - // Base velocity estimated by FK, estimated by motion capture - if (solo3D) { - FK_lin_vel_ = b_baseVel_perfect; +void Estimator::computeFeetPositionBarycenter() { + int nContactFeet = 0; + Vector3 feetPositions = Vector3::Zero(); + for (int j = 0; j < 4; j++) { + if (feetStatus_(j) == 1.) { + feetPositions += feetTargets_.col(j); + nContactFeet++; + } } + if (nContactFeet > 0) feetPositionBarycenter_ = feetPositions / nContactFeet; +} - // Rotation matrix to go from base frame to world frame - Matrix3 oRb = IMU_ang_pos_.toRotationMatrix(); - - // Get FK estimated velocity at IMU location (base frame) - Vector3 cross_product = (_1Mi_.translation()).cross(IMU_ang_vel_); - Vector3 i_FK_lin_vel = FK_lin_vel_ + cross_product; - - // Get FK estimated velocity at IMU location (world frame) - Vector3 oi_FK_lin_vel = oRb * i_FK_lin_vel; - - // Integration of IMU acc at IMU location (world frame) - Vector3 oi_filt_lin_vel = filter_xyz_vel_.compute(oi_FK_lin_vel, oRb * IMU_lin_acc_, alpha * Vector3::Ones()); +double Estimator::computeAlphaVelocity() { + double a = std::ceil(feetStancePhaseDuration_.maxCoeff() * 0.1) - 1; + double b = static_cast<double>(phaseRemainingDuration_); + double c = ((a + b) - 2) * 0.5; + if (a <= 0 || b <= 1) + return alphaVelMax_; + else + return alphaVelMin_ + (alphaVelMax_ - alphaVelMin_) * std::abs(c - (a - 1)) / c; +} - // Filtered estimated velocity at IMU location (base frame) - Vector3 i_filt_lin_vel = oRb.transpose() * oi_filt_lin_vel; +void Estimator::estimateVelocity(Vector3 const& b_perfectVelocity) { + Vector3 alpha = Vector3::Ones() * computeAlphaVelocity(); + Matrix3 oRb = IMUQuat_.toRotationMatrix(); + Vector3 bTi = (b_M_IMU_.translation()).cross(IMUAngularVelocity_); - // Filtered estimated velocity at center base (base frame) - b_filt_lin_vel_ = i_filt_lin_vel - cross_product; + // At IMU location in world frame + Vector3 oi_baseVelocityFK = solo3D_ ? oRb * (b_perfectVelocity + bTi) : oRb * (baseVelocityFK_ + bTi); + Vector3 oi_baseVelocity = velocityFilter_.compute(oi_baseVelocityFK, oRb * IMULinearAcceleration_, alpha); - // Filtered estimated velocity at center base (world frame) - Vector3 ob_filt_lin_vel = oRb * b_filt_lin_vel_; + // At base location in base frame + b_baseVelocity_ = oRb.transpose() * oi_baseVelocity - bTi; - // Position of the center of the base from FGeometry and filtered velocity (world frame) - Vector3 filt_lin_pos = Vector3::Zero(); + vEstimate_.head(3) = perfectEstimator_ ? b_perfectVelocity : b_baseVelocity_; + vEstimate_.segment(3, 3) = IMUAngularVelocity_; + vEstimate_.tail(12) = vActuators_; +} - if (solo3D) { - Vector3 offset_ = Vector3::Zero(); - // offset_.tail(3) << -0.0155; - filt_lin_pos = filter_xyz_pos_.compute(q_perfect.head(3) - offset_, ob_filt_lin_vel, Vector3(0.995, 0.995, 0.9)); - } else { - filt_lin_pos = filter_xyz_pos_.compute(FK_xyz_ + xyz_mean_feet_, ob_filt_lin_vel, Vector3(0.995, 0.995, 0.9)); - } +void Estimator::estimatePosition(Vector3 const& perfectPosition) { + Matrix3 oRb = IMUQuat_.toRotationMatrix(); - // Output filtered position vector (19 x 1) - q_filt_.head(3) = filt_lin_pos; - if (perfect_estimator || solo3D) { - q_filt_(2, 0) = q_perfect(2, 0); // Minus feet radius - } - q_filt_.block(3, 0, 4, 1) = filt_ang_pos; - q_filt_.tail(12) = actuators_pos_; // Actuators pos are already directly from PyBullet + Vector3 basePosition = solo3D_ ? perfectPosition : (basePositionFK_ + feetPositionBarycenter_); + qEstimate_.head(3) = positionFilter_.compute(basePosition, oRb * b_baseVelocity_, alphaPos_); - // Output filtered velocity vector (18 x 1) - // Linear velocities directly from PyBullet if perfect estimator - v_filt_.head(3) = perfect_estimator ? b_baseVel_perfect : b_filt_lin_vel_; - v_filt_.block(3, 0, 3, 1) = filt_ang_vel; // Angular velocities are already directly from PyBullet - v_filt_.tail(12) = actuators_vel_; // Actuators velocities are already directly from PyBullet + if (perfectEstimator_ || solo3D_) qEstimate_(2) = perfectPosition(2); + qEstimate_.segment(3, 4) = IMUQuat_.coeffs(); + qEstimate_.tail(12) = qActuators_; +} +void Estimator::filterVelocity() { + vFiltered_ = vEstimate_.head(6); vx_queue_.pop_back(); vy_queue_.pop_back(); vz_queue_.pop_back(); - vx_queue_.push_front(perfect_estimator ? b_baseVel_perfect(0) : b_filt_lin_vel_(0)); - vy_queue_.push_front(perfect_estimator ? b_baseVel_perfect(1) : b_filt_lin_vel_(1)); - vz_queue_.push_front(perfect_estimator ? b_baseVel_perfect(2) : b_filt_lin_vel_(2)); - v_filt_bis_(0) = std::accumulate(vx_queue_.begin(), vx_queue_.end(), 0.0) / N_queue_; - v_filt_bis_(1) = std::accumulate(vy_queue_.begin(), vy_queue_.end(), 0.0) / N_queue_; - v_filt_bis_(2) = std::accumulate(vz_queue_.begin(), vz_queue_.end(), 0.0) / N_queue_; - /* - wR_queue_.pop_back(); - wP_queue_.pop_back(); - wY_queue_.pop_back(); - wR_queue_.push_front(filt_ang_vel(0)); - wP_queue_.push_front(filt_ang_vel(1)); - wY_queue_.push_front(filt_ang_vel(2)); - v_filt_bis_(3) = std::accumulate(wR_queue_.begin(), wR_queue_.end(), 0.0) / N_queue_; - v_filt_bis_(4) = std::accumulate(wP_queue_.begin(), wP_queue_.end(), 0.0) / N_queue_; - v_filt_bis_(5) = std::accumulate(wY_queue_.begin(), wY_queue_.end(), 0.0) / N_queue_;*/ - v_filt_bis_.tail(3) = filt_ang_vel; // No filtering for angular velocity - ////// - - // Update model used for the forward kinematics - /*pin.forwardKinematics(self.model, self.data, q_up__filt, self.v_filt) - pin.updateFramePlacements(self.model, self.data) - - z_min = 100 - for i in (np.where(feet_status == 1))[0]: // Consider only feet in contact - // Estimated position of the base using the considered foot - framePlacement = pin.updateFramePlacement(self.model, self.data, self.indexes[i]) - z_min = np.min((framePlacement.translation[2], z_min)) - q_up__filt[2, 0] -= z_min*/ - - ////// - - // Output filtered actuators velocity for security checks - v_secu_ = (1 - alpha_secu_) * actuators_vel_ + alpha_secu_ * v_secu_; - - // Copy data to dynamic sized matrices since Python converters for big sized fixed matrices do not exist - // TODO: Find a way to cast a fixed size eigen matrix as dynamic size to remove the need for those variables - q_filt_dyn_ = q_filt_; - v_filt_dyn_ = v_filt_; - v_secu_dyn_ = v_secu_; - - // Increment iteration counter - k_log_++; -} - -int Estimator::security_check(VectorN const& tau_ff) { - if (((q_filt_.tail(12).cwiseAbs()).array() > q_security_.array()).any()) { // Test position limits - std::cout << "Position limit error " << ((q_filt_.tail(12).cwiseAbs()).array() > q_security_.array()).transpose() << std::endl; - return 1; - } else if (((v_secu_.cwiseAbs()).array() > 100.0).any()) { // Test velocity limits - std::cout << "Velocity limit error " << ((v_secu_.cwiseAbs()).array() > 100.0).transpose() << std::endl; - return 2; - } else if (((tau_ff.cwiseAbs()).array() > 8.0).any()) { // Test feedforward torques limits - std::cout << "feedforward limit error " << ((tau_ff.cwiseAbs()).array() > 8.0).transpose() << std::endl; - return 3; - } - return 0; -} - -void Estimator::updateState(VectorN const& joystick_v_ref, Gait& gait) { - // TODO: Joystick velocity given in base frame and not in horizontal frame (case of non flat ground) - - // Update reference acceleration vector - a_ref_.head(3) = - (joystick_v_ref.head(3) - pinocchio::rpy::rpyToMatrix(0.0, 0.0, -v_ref_[5] * dt_wbc) * v_ref_.head(3)) / dt_wbc; - a_ref_.tail(3) = - (joystick_v_ref.tail(3) - pinocchio::rpy::rpyToMatrix(0.0, 0.0, -v_ref_[5] * dt_wbc) * v_ref_.tail(3)) / dt_wbc; - - // Update reference velocity vector - v_ref_.head(3) = joystick_v_ref.head(3); - v_ref_.tail(3) = joystick_v_ref.tail(3); - - // Update position and velocity state vectors - - // Integration to get evolution of perfect x, y and yaw - Matrix2 Ryaw; - Ryaw << cos(yaw_estim_), -sin(yaw_estim_), sin(yaw_estim_), cos(yaw_estim_); - v_up_.head(2) = Ryaw * v_ref_.head(2); - q_up_.head(2) = q_up_.head(2) + v_up_.head(2) * dt_wbc; - - // Mix perfect x and y with height measurement - q_up_[2] = q_filt_dyn_[2]; - - // Mix perfect yaw with pitch and roll measurements - v_up_[5] = v_ref_[5]; - yaw_estim_ += v_ref_[5] * dt_wbc; - q_up_.block(3, 0, 3, 1) << IMU_RPY_[0], IMU_RPY_[1], yaw_estim_; - - // Transformation matrices between world and base frames - oRb_ = pinocchio::rpy::rpyToMatrix(IMU_RPY_(0, 0), IMU_RPY_(1, 0), yaw_estim_); - - // Actuators measurements - q_up_.tail(12) = q_filt_dyn_.tail(12); - v_up_.tail(12) = v_filt_dyn_.tail(12); - - // Velocities are the one estimated by the estimator - hRb_ = pinocchio::rpy::rpyToMatrix(IMU_RPY_[0], IMU_RPY_[1], 0.0); - - // Express estimated velocity and filtered estimated velocity in horizontal frame - h_v_.head(3) = hRb_ * v_filt_.block(0, 0, 3, 1); - h_v_.tail(3) = hRb_ * v_filt_.block(3, 0, 3, 1); - h_v_windowed_.head(3) = hRb_ * v_filt_bis_.block(0, 0, 3, 1); - h_v_windowed_.tail(3) = hRb_ * v_filt_bis_.block(3, 0, 3, 1); - - // Transformation matrices between world and horizontal frames - oRh_ = Matrix3::Identity(); - oRh_.block(0, 0, 2, 2) << cos(yaw_estim_), -sin(yaw_estim_), sin(yaw_estim_), cos(yaw_estim_); - oTh_ << q_up_[0], q_up_[1], 0.0; + vx_queue_.push_front(vEstimate_(0)); + vy_queue_.push_front(vEstimate_(1)); + vz_queue_.push_front(vEstimate_(2)); + vFiltered_(0) = std::accumulate(vx_queue_.begin(), vx_queue_.end(), 0.) / windowSize_; + vFiltered_(1) = std::accumulate(vy_queue_.begin(), vy_queue_.end(), 0.) / windowSize_; + vFiltered_(2) = std::accumulate(vz_queue_.begin(), vz_queue_.end(), 0.) / windowSize_; }