From ee334b055a11adcdf3e78e427c2bf1de3144506c Mon Sep 17 00:00:00 2001 From: paleziart <paleziart@laas.fr> Date: Tue, 13 Apr 2021 17:02:24 +0200 Subject: [PATCH] New StatePlanner class independent from Planner to generate the CoM trajectory --- CMakeLists.txt | 2 + include/qrw/FootstepPlanner.hpp | 21 +------- include/qrw/Planner.hpp | 5 +- include/qrw/StatePlanner.hpp | 75 ++++++++++++++++++++++++++ python/gepadd.cpp | 41 ++++++++++++-- scripts/Controller.py | 15 ++++-- scripts/Planner.py | 5 +- src/FootstepPlanner.cpp | 84 +---------------------------- src/Planner.cpp | 7 ++- src/StatePlanner.cpp | 96 +++++++++++++++++++++++++++++++++ 10 files changed, 232 insertions(+), 119 deletions(-) create mode 100644 include/qrw/StatePlanner.hpp create mode 100644 src/StatePlanner.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c70d0cf..76f43b84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ set(${PROJECT_NAME}_HEADERS include/qrw/Gait.hpp include/qrw/FootTrajectoryGenerator.hpp include/qrw/FootstepPlanner.hpp + include/qrw/StatePlanner.hpp include/qrw/Types.h include/qrw/InvKin.hpp include/qrw/QPWBC.hpp @@ -63,6 +64,7 @@ set(${PROJECT_NAME}_SOURCES src/Gait.cpp src/FootTrajectoryGenerator.cpp src/FootstepPlanner.cpp + src/StatePlanner.cpp src/Planner.cpp src/InvKin.cpp src/QPWBC.cpp diff --git a/include/qrw/FootstepPlanner.hpp b/include/qrw/FootstepPlanner.hpp index e0c6babe..bafb666f 100644 --- a/include/qrw/FootstepPlanner.hpp +++ b/include/qrw/FootstepPlanner.hpp @@ -59,8 +59,7 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////// MatrixN computeTargetFootstep(VectorN const& q, Vector6 const& v, - Vector6 const& b_vref, - double const z_average); + Vector6 const& b_vref); //////////////////////////////////////////////////////////////////////////////////////////////// /// @@ -73,7 +72,7 @@ public: void rollGait(int const k, int const k_mpc); - MatrixN getXReference(); + // MatrixN getXReference(); MatrixN getFootsteps(); MatrixN getTargetFootsteps(); @@ -103,22 +102,6 @@ private: //////////////////////////////////////////////////////////////////////////////////////////////// void compute_next_footstep(int i, int j); - //////////////////////////////////////////////////////////////////////////////////////////////// - /// - /// \brief Compute the reference trajectory of the CoM for each time step of the - /// predition horizon. The ouput is a matrix of size 12 by (N+1) with N the number - /// of time steps in the gait cycle (T_gait/dt) and 12 the position, orientation, - /// linear velocity and angular velocity vertically stacked. The first column contains - /// the current state while the remaining N columns contains the desired future states. - /// - /// \param[in] q current position vector of the flying base in world frame (linear and angular stacked) - /// \param[in] v current velocity vector of the flying base in world frame (linear and angular stacked) - /// \param[in] vref desired velocity vector of the flying base in world frame (linear and angular stacked) - /// \param[in] z_average average height of feet currently in stance phase - /// - //////////////////////////////////////////////////////////////////////////////////////////////// - int getRefStates(VectorN const& q, Vector6 const& v, Vector6 const& vref, double z_average); - //////////////////////////////////////////////////////////////////////////////////////////////// /// /// \brief Update desired location of footsteps using information coming from the footsteps planner diff --git a/include/qrw/Planner.hpp b/include/qrw/Planner.hpp index 2d634b7c..8ed8f312 100644 --- a/include/qrw/Planner.hpp +++ b/include/qrw/Planner.hpp @@ -68,14 +68,12 @@ public: /// \param[in] q current position vector of the flying base in world frame (linear and angular stacked) /// \param[in] v current velocity vector of the flying base in world frame (linear and angular stacked) /// \param[in] b_vref desired velocity vector of the flying base in base frame (linear and angular stacked) - /// \param[in] z_average average height of feet currently in stance phase /// //////////////////////////////////////////////////////////////////////////////////////////////// void run_planner(int const k, VectorN const& q, Vector6 const& v, - Vector6 const& b_vref, - double const z_average); + Vector6 const& b_vref); //////////////////////////////////////////////////////////////////////////////////////////////// /// @@ -102,7 +100,6 @@ public: void setGait(MatrixN const& gaitMatrix); // Accessors (to retrieve C data from Python) - MatrixN get_xref(); MatrixN get_fsteps(); MatrixN get_gait(); Matrix3N get_goals(); diff --git a/include/qrw/StatePlanner.hpp b/include/qrw/StatePlanner.hpp new file mode 100644 index 00000000..0ddd0f0b --- /dev/null +++ b/include/qrw/StatePlanner.hpp @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \brief This is the header for StatePlanner class +/// +/// \details Planner that outputs the reference trajectory of the base based on the reference +/// velocity given by the user and the current position/velocity of the base +/// +////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef STATEPLANNER_H_INCLUDED +#define STATEPLANNER_H_INCLUDED + +#include "pinocchio/math/rpy.hpp" +#include "qrw/Types.h" + + +class StatePlanner +{ +public: + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Empty constructor + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + StatePlanner(); + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Destructor. + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + ~StatePlanner() {} + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Initializer + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + void initialize(double dt_in, double T_mpc_in, double h_ref_in); + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// \brief Compute the reference trajectory of the CoM for each time step of the + /// predition horizon. The ouput is a matrix of size 12 by (N+1) with N the number + /// of time steps in the gait cycle (T_gait/dt) and 12 the position, orientation, + /// linear velocity and angular velocity vertically stacked. The first column contains + /// the current state while the remaining N columns contains the desired future states. + /// + /// \param[in] q current position vector of the flying base in world frame (linear and angular stacked) + /// \param[in] v current velocity vector of the flying base in world frame (linear and angular stacked) + /// \param[in] vref desired velocity vector of the flying base in world frame (linear and angular stacked) + /// \param[in] z_average average height of feet currently in stance phase + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + void computeRefStates(VectorN const& q, Vector6 const& v, Vector6 const& vref, double z_average); + + MatrixN getXReference() { return xref_; } + +private: + double dt_; // Time step of the contact sequence (time step of the MPC) + double T_mpc_; // MPC period (prediction horizon) + double h_ref_; // Reference height for the trunk + int n_steps_; // Number of time steps in the prediction horizon + + Vector3 RPY_; // To store roll, pitch and yaw angles + + // Reference trajectory matrix of size 12 by (1 + N) with the current state of + // the robot in column 0 and the N steps of the prediction horizon in the others + MatrixN xref_; + + VectorN dt_vector_; // Vector containing all time steps in the prediction horizon + +}; + +#endif // STATEPLANNER_H_INCLUDED diff --git a/python/gepadd.cpp b/python/gepadd.cpp index 25c3baad..475d69c2 100644 --- a/python/gepadd.cpp +++ b/python/gepadd.cpp @@ -2,6 +2,7 @@ #include "qrw/InvKin.hpp" #include "qrw/MPC.hpp" #include "qrw/Planner.hpp" +#include "qrw/StatePlanner.hpp" #include "qrw/QPWBC.hpp" #include <boost/python.hpp> @@ -37,7 +38,9 @@ struct MPCPythonVisitor : public bp::def_visitor<MPCPythonVisitor<MPC>> void exposeMPC() { MPCPythonVisitor<MPC>::expose(); } - +///////////////////////////////// +/// Binding Planner class +///////////////////////////////// template <typename Planner> struct PlannerPythonVisitor : public bp::def_visitor<PlannerPythonVisitor<Planner>> { @@ -50,7 +53,6 @@ struct PlannerPythonVisitor : public bp::def_visitor<PlannerPythonVisitor<Planne "fsteps_in", "shoulders positions"), "Constructor with parameters.")) - .def("get_xref", &Planner::get_xref, "Get xref matrix.\n") .def("get_fsteps", &Planner::get_fsteps, "Get fsteps matrix.\n") .def("get_gait", &Planner::get_gait, "Get gait matrix.\n") .def("get_goals", &Planner::get_goals, "Get position goals matrix.\n") @@ -58,7 +60,7 @@ struct PlannerPythonVisitor : public bp::def_visitor<PlannerPythonVisitor<Planne .def("get_agoals", &Planner::get_agoals, "Get acceleration goals matrix.\n") // Run Planner from Python - .def("run_planner", &Planner::run_planner, bp::args("k", "q", "v", "b_vref", "z_average"), + .def("run_planner", &Planner::run_planner, bp::args("k", "q", "v", "b_vref"), "Run Planner from Python.\n") // Update gait matrix from Python @@ -79,7 +81,39 @@ struct PlannerPythonVisitor : public bp::def_visitor<PlannerPythonVisitor<Planne }; void exposePlanner() { PlannerPythonVisitor<Planner>::expose(); } +///////////////////////////////// +/// Binding StatePlanner class +///////////////////////////////// +template <typename StatePlanner> +struct StatePlannerPythonVisitor : public bp::def_visitor<StatePlannerPythonVisitor<StatePlanner>> +{ + template <class PyClassStatePlanner> + void visit(PyClassStatePlanner& cl) const + { + cl.def(bp::init<>(bp::arg(""), "Default constructor.")) + + .def("getXReference", &StatePlanner::getXReference, "Get xref matrix.\n") + + .def("initialize", &StatePlanner::initialize, bp::args("dt_in", "T_mpc_in", "h_ref_in"), + "Initialize StatePlanner from Python.\n") + + // Run StatePlanner from Python + .def("computeRefStates", &StatePlanner::computeRefStates, bp::args("q", "v", "b_vref", "z_average"), + "Run StatePlanner from Python.\n"); + } + + static void expose() + { + bp::class_<StatePlanner>("StatePlanner", bp::no_init).def(StatePlannerPythonVisitor<StatePlanner>()); + + ENABLE_SPECIFIC_MATRIX_TYPE(MatrixN); + } +}; +void exposeStatePlanner() { StatePlannerPythonVisitor<StatePlanner>::expose(); } +///////////////////////////////// +/// Binding InvKin class +///////////////////////////////// template <typename InvKin> struct InvKinPythonVisitor : public bp::def_visitor<InvKinPythonVisitor<InvKin>> { @@ -144,6 +178,7 @@ BOOST_PYTHON_MODULE(libquadruped_reactive_walking) exposeMPC(); exposePlanner(); + exposeStatePlanner(); exposeInvKin(); exposeQPWBC(); } \ No newline at end of file diff --git a/scripts/Controller.py b/scripts/Controller.py index cab2d170..3efb0aee 100644 --- a/scripts/Controller.py +++ b/scripts/Controller.py @@ -10,6 +10,7 @@ import pybullet as pyb from Planner import PyPlanner import pinocchio as pin from solopython.utils.viewerClient import viewerClient, NonBlockingViewerFromRobot +import libquadruped_reactive_walking as lqrw class Result: """Object to store the result of the control loop @@ -116,6 +117,9 @@ class Controller: self.planner = PyPlanner(dt_mpc, dt_wbc, T_gait, T_mpc, k_mpc, on_solo8, h_ref, self.fsteps_init) + self.statePlanner = lqrw.StatePlanner() + self.statePlanner.initialize(dt_mpc, T_mpc, h_ref) + # Wrapper that makes the link with the solver that you want to use for the MPC # First argument to True to have PA's MPC, to False to have Thomas's MPC self.enable_multiprocessing = True @@ -211,11 +215,16 @@ class Controller: self.planner.updateGait(self.k, self.k_mpc, self.q[0:7, 0:1], self.joystick) # Run planner - self.planner.run_planner(self.k, self.k_mpc, self.q[0:7, 0:1], - self.v[0:6, 0:1].copy(), self.joystick.v_ref, self.q_estim[2, 0], 0.0) + self.planner.run_planner(self.k, self.q[0:7, 0:1], self.v[0:6, 0:1].copy(), self.joystick.v_ref) + + # Run state planner (outputs the reference trajectory of the CoM / base) + self.statePlanner.computeRefStates(self.q[0:7, 0:1], self.v[0:6, 0:1].copy(), self.joystick.v_ref, 0.0) + # Result can be retrieved with self.statePlanner.getXReference() + self.planner.xref = self.statePlanner.getXReference() + t_planner = time.time() - self.planner.setGait(self.planner.gait) + # self.planner.setGait(self.planner.gait) # Process MPC once every k_mpc iterations of TSID if (self.k % self.k_mpc) == 0: diff --git a/scripts/Planner.py b/scripts/Planner.py index d93e2dd5..10e3240e 100644 --- a/scripts/Planner.py +++ b/scripts/Planner.py @@ -32,12 +32,11 @@ class PyPlanner: shoulders[1, :] = [0.14695, -0.14695, 0.14695, -0.14695] self.planner = la.Planner(dt, dt_tsid, T_gait, T_mpc, k_mpc, h_ref, fsteps_init, shoulders) - def run_planner(self, k, k_mpc, q, v, b_vref, h_estim, z_average): + def run_planner(self, k, q, v, b_vref): # Run C++ planner - self.planner.run_planner(k, q, v, b_vref, np.double(z_average)) + self.planner.run_planner(k, q, v, b_vref) # Retrieve data from C++ planner - self.xref = self.planner.get_xref() self.fsteps = self.planner.get_fsteps() self.gait = self.planner.get_gait() self.goals = self.planner.get_goals() diff --git a/src/FootstepPlanner.cpp b/src/FootstepPlanner.cpp index ecfeafec..71ae61d5 100644 --- a/src/FootstepPlanner.cpp +++ b/src/FootstepPlanner.cpp @@ -159,81 +159,6 @@ void FootstepPlanner::compute_next_footstep(int i, int j) nextFootstep_.row(2) = Vector4::Zero().transpose(); } -int FootstepPlanner::getRefStates(VectorN const& q, Vector6 const& v, Vector6 const& vref, double z_average) -{ - VectorN dt_vector = VectorN::LinSpaced(n_steps, dt, T_mpc); - - // Update yaw and yaw velocity - xref.block(5, 1, 1, n_steps) = vref(5) * dt_vector.transpose(); - for (int i = 0; i < n_steps; i++) - { - xref(11, 1 + i) = vref(5); - } - - // Update x and y velocities taking into account the rotation of the base over the prediction horizon - for (int i = 0; i < n_steps; i++) - { - xref(6, 1 + i) = vref(0) * std::cos(xref(5, 1 + i)) - vref(1) * std::sin(xref(5, 1 + i)); - xref(7, 1 + i) = vref(0) * std::sin(xref(5, 1 + i)) + vref(1) * std::cos(xref(5, 1 + i)); - } - - // Update x and y depending on x and y velocities (cumulative sum) - if (vref(5) != 0) - { - for (int i = 0; i < n_steps; i++) - { - xref(0, 1 + i) = (vref(0) * std::sin(vref(5) * dt_vector(i)) + vref(1) * (std::cos(vref(5) * dt_vector(i)) - 1.0)) / vref(5); - xref(1, 1 + i) = (vref(1) * std::sin(vref(5) * dt_vector(i)) - vref(0) * (std::cos(vref(5) * dt_vector(i)) - 1.0)) / vref(5); - } - } - else - { - for (int i = 0; i < n_steps; i++) - { - xref(0, 1 + i) = vref(0) * dt_vector(i); - xref(1, 1 + i) = vref(1) * dt_vector(i); - } - } - - for (int i = 0; i < n_steps; i++) - { - xref(5, 1 + i) += RPY(2); - xref(2, 1 + i) = h_ref + z_average; - xref(8, 1 + i) = 0.0; - } - - // No need to update Z velocity as the reference is always 0 - // No need to update roll and roll velocity as the reference is always 0 for those - // No need to update pitch and pitch velocity as the reference is always 0 for those - - // Update the current state - xref.block(0, 0, 3, 1) = q.head(3); - xref.block(3, 0, 3, 1) = RPY; - xref.block(6, 0, 3, 1) = v.head(3); - xref.block(9, 0, 3, 1) = v.tail(3); - - for (int i = 0; i < n_steps; i++) - { - xref(0, 1 + i) += xref(0, 0); - xref(1, 1 + i) += xref(1, 0); - } - - if (gait_->getIsStatic()) - { - Vector19 q_static = gait_->getQStatic(); - Eigen::Quaterniond quat(q_static(6, 0), q_static(3, 0), q_static(4, 0), q_static(5, 0)); // w, x, y, z - RPY << pinocchio::rpy::matrixToRpy(quat.toRotationMatrix()); - - for (int i = 0; i < n_steps; i++) - { - xref.block(0, 1 + i, 3, 1) = q_static.block(0, 0, 3, 1); - xref.block(3, 1 + i, 3, 1) = RPY; - } - } - - return 0; -} - void FootstepPlanner::update_target_footsteps() { for (int i = 0; i < 4; i++) @@ -249,8 +174,7 @@ void FootstepPlanner::update_target_footsteps() MatrixN FootstepPlanner::computeTargetFootstep(VectorN const& q, Vector6 const& v, - Vector6 const& b_vref, - double const z_average) + Vector6 const& b_vref) { // Get the reference velocity in world frame (given in base frame) Eigen::Quaterniond quat(q(6), q(3), q(4), q(5)); // w, x, y, z @@ -267,11 +191,6 @@ MatrixN FootstepPlanner::computeTargetFootstep(VectorN const& q, // Compute the desired location of footsteps over the prediction horizon compute_footsteps(q, v, vref); - - // Get the reference trajectory for the MPC - getRefStates(q, v, vref, z_average); - - // Update desired location of footsteps on the ground update_target_footsteps(); return targetFootstep_; @@ -284,7 +203,6 @@ void FootstepPlanner::rollGait(int const k, gait_->roll(k, footsteps_[1], currentFootstep_); } -MatrixN FootstepPlanner::getXReference() { return xref; } MatrixN FootstepPlanner::getFootsteps() { return vectorToMatrix(footsteps_); } MatrixN FootstepPlanner::getTargetFootsteps() { return targetFootstep_; } diff --git a/src/Planner.cpp b/src/Planner.cpp index 32e36be1..3d75bb15 100644 --- a/src/Planner.cpp +++ b/src/Planner.cpp @@ -27,11 +27,11 @@ Planner::Planner(double dt_in, void Planner::run_planner(int const k, VectorN const& q, Vector6 const& v, - Vector6 const& b_vref, - double const z_average) + Vector6 const& b_vref) { - targetFootstep_ = footstepPlanner_.computeTargetFootstep(q, v, b_vref, z_average); + targetFootstep_ = footstepPlanner_.computeTargetFootstep(q, v, b_vref); fooTrajectoryGenerator_.update(k, targetFootstep_); + } void Planner::updateGait(int const k, @@ -48,7 +48,6 @@ void Planner::setGait(MatrixN const& gaitMatrix) gait_->setGait(gaitMatrix); } -MatrixN Planner::get_xref() { return footstepPlanner_.getXReference(); } MatrixN Planner::get_fsteps() { return footstepPlanner_.getFootsteps(); } MatrixN Planner::get_gait() { return gait_->getCurrentGait(); } Matrix3N Planner::get_goals() { return fooTrajectoryGenerator_.getFootPosition(); } diff --git a/src/StatePlanner.cpp b/src/StatePlanner.cpp new file mode 100644 index 00000000..613deb3a --- /dev/null +++ b/src/StatePlanner.cpp @@ -0,0 +1,96 @@ +#include "qrw/StatePlanner.hpp" + +StatePlanner::StatePlanner() + : dt_(0.0) + , T_mpc_(0.0) + , h_ref_(0.0) + , n_steps_(0) + , RPY_(Vector3::Zero()) +{ + // Empty +} + +void StatePlanner::initialize(double dt_in, double T_mpc_in, double h_ref_in) +{ + dt_ = dt_in; + T_mpc_ = T_mpc_in; + h_ref_ = h_ref_in; + n_steps_ = (int)std::lround(T_mpc_in / dt_in); + xref_ = MatrixN::Zero(12, 1 + n_steps_); + dt_vector_ = VectorN::LinSpaced(n_steps_, dt_, T_mpc_); +} + +void StatePlanner::computeRefStates(VectorN const& q, Vector6 const& v, Vector6 const& vref, double z_average) +{ + Eigen::Quaterniond quat(q(6), q(3), q(4), q(5)); // w, x, y, z + RPY_ << pinocchio::rpy::matrixToRpy(quat.toRotationMatrix()); + + // Update yaw and yaw velocity + xref_.block(5, 1, 1, n_steps_) = vref(5) * dt_vector_.transpose(); + for (int i = 0; i < n_steps_; i++) + { + xref_(11, 1 + i) = vref(5); + } + + // Update x and y velocities taking into account the rotation of the base over the prediction horizon + for (int i = 0; i < n_steps_; i++) + { + xref_(6, 1 + i) = vref(0) * std::cos(xref_(5, 1 + i)) - vref(1) * std::sin(xref_(5, 1 + i)); + xref_(7, 1 + i) = vref(0) * std::sin(xref_(5, 1 + i)) + vref(1) * std::cos(xref_(5, 1 + i)); + } + + // Update x and y depending on x and y velocities (cumulative sum) + if (vref(5) != 0) + { + for (int i = 0; i < n_steps_; i++) + { + xref_(0, 1 + i) = (vref(0) * std::sin(vref(5) * dt_vector_(i)) + vref(1) * (std::cos(vref(5) * dt_vector_(i)) - 1.0)) / vref(5); + xref_(1, 1 + i) = (vref(1) * std::sin(vref(5) * dt_vector_(i)) - vref(0) * (std::cos(vref(5) * dt_vector_(i)) - 1.0)) / vref(5); + } + } + else + { + for (int i = 0; i < n_steps_; i++) + { + xref_(0, 1 + i) = vref(0) * dt_vector_(i); + xref_(1, 1 + i) = vref(1) * dt_vector_(i); + } + } + + for (int i = 0; i < n_steps_; i++) + { + xref_(5, 1 + i) += RPY_(2); + xref_(2, 1 + i) = h_ref_ + z_average; + xref_(8, 1 + i) = 0.0; + } + + // No need to update Z velocity as the reference is always 0 + // No need to update roll and roll velocity as the reference is always 0 for those + // No need to update pitch and pitch velocity as the reference is always 0 for those + + // Update the current state + xref_.block(0, 0, 3, 1) = q.head(3); + xref_.block(3, 0, 3, 1) = RPY_; + xref_.block(6, 0, 3, 1) = v.head(3); + xref_.block(9, 0, 3, 1) = v.tail(3); + + for (int i = 0; i < n_steps_; i++) + { + xref_(0, 1 + i) += xref_(0, 0); + xref_(1, 1 + i) += xref_(1, 0); + } + + /*if (gait_->getIsStatic()) + { + Vector19 q_static = gait_->getQStatic(); + Eigen::Quaterniond quatStatic(q_static(6, 0), q_static(3, 0), q_static(4, 0), q_static(5, 0)); // w, x, y, z + RPY_ << pinocchio::rpy::matrixToRpy(quatStatic.toRotationMatrix()); + + for (int i = 0; i < n_steps_; i++) + { + xref_.block(0, 1 + i, 3, 1) = q_static.block(0, 0, 3, 1); + xref_.block(3, 1 + i, 3, 1) = RPY_; + } + }*/ + +} -- GitLab