28 #include <mc_rbdyn/rpy_utils.h> 38 const mc_rtc::Configuration & config)
39 : mc_control::fsm::
Controller(robotModule, dt, config), planInterpolator(gui()), halfSitPose(controlRobot().mbc().q),
40 floatingBaseObs_(controlRobot()), comVelFilter_(dt, 0.01), netWrenchObs_(),
41 stabilizer_(controlRobot(), pendulum_, dt)
43 auto robotConfig = config(
"robot_models")(
controlRobot().name());
44 auto planConfig = config(
"plans")(
controlRobot().name());
47 std::vector<std::string> plans = planConfig.keys();
48 double comHeight = robotConfig(
"com")(
"height");
49 maxCoMHeight_ = robotConfig(
"com")(
"max_height");
50 minCoMHeight_ = robotConfig(
"com")(
"min_height");
51 maxTorsoPitch_ = robotConfig(
"torso")(
"max_pitch");
52 minTorsoPitch_ = robotConfig(
"torso")(
"min_pitch");
53 for(
const auto & p : plans)
55 auto plan = planConfig(p);
56 if(!
plan.has(
"com_height"))
58 plan.add(
"com_height", comHeight);
63 double pelvisStiffness = config(
"tasks")(
"pelvis")(
"stiffness");
64 double pelvisWeight = config(
"tasks")(
"pelvis")(
"weight");
65 std::string pelvisBodyName = robot().mb().body(0).
name();
66 pelvisTask = std::make_shared<mc_tasks::OrientationTask>(pelvisBodyName, robots(), 0);
71 double postureStiffness = config(
"tasks")(
"posture")(
"stiffness");
72 double postureWeight = config(
"tasks")(
"posture")(
"weight");
73 postureTask = getPostureTask(robot().name());
74 postureTask->stiffness(postureStiffness);
75 postureTask->weight(postureWeight);
77 std::string torsoName = robotConfig(
"torso")(
"name");
78 robotConfig(
"torso")(
"pitch", defaultTorsoPitch_);
79 double torsoStiffness = config(
"tasks")(
"torso")(
"stiffness");
80 double torsoWeight = config(
"tasks")(
"torso")(
"weight");
81 torsoPitch_ = defaultTorsoPitch_;
82 torsoTask = std::make_shared<mc_tasks::OrientationTask>(torsoName, robots(), 0);
83 torsoTask->orientation(mc_rbdyn::rpyToMat({0, torsoPitch_, 0}) * pelvisOrientation_);
88 const auto & halfSit = robotModule->stance();
89 const auto & refJointOrder = robot().refJointOrder();
90 for(
unsigned i = 0; i < refJointOrder.size(); ++i)
92 if(robot().hasJoint(refJointOrder[i]))
94 halfSitPose[robot().jointIndexByName(refJointOrder[i])] = halfSit.at(refJointOrder[i]);
99 sva::PTransformd X_0_lfc =
controlRobot().surfacePose(
"LeftFootCenter");
100 sva::PTransformd X_0_rfc =
controlRobot().surfacePose(
"RightFootCenter");
101 sva::PTransformd X_0_lf =
controlRobot().surfacePose(
"LeftFoot");
102 sva::PTransformd X_lfc_lf = X_0_lf * X_0_lfc.inv();
103 sva::PTransformd X_rfc_lfc = X_0_lfc * X_0_rfc.inv();
104 double stepWidth = X_rfc_lfc.translation().y();
105 sole_ = robotConfig(
"sole");
109 mpcConfig_ = config(
"mpc");
113 std::vector<std::string> comActiveJoints = robotConfig(
"com")(
"active_joints");
114 config(
"stabilizer").add(
"admittance", robotConfig(
"admittance"));
115 config(
"stabilizer").add(
"dcm_tracking", robotConfig(
"dcm_tracking"));
116 config(
"stabilizer")(
"tasks")(
"com").add(
"active_joints", comActiveJoints);
117 stabilizer_.
configure(config(
"stabilizer"));
123 config(
"initial_plan", initialPlan);
126 stabilizer_.
reset(robots());
127 stabilizer_.
sole(sole_);
141 LOG_SUCCESS(
"LIPMWalking controller init done " <<
this)
146 logger.addLogEntry(
"controlRobot_LeftFoot", [
this]() {
return controlRobot().surfacePose(
"LeftFoot"); });
147 logger.addLogEntry(
"controlRobot_LeftFootCenter", [
this]() {
return controlRobot().surfacePose(
"LeftFootCenter"); });
148 logger.addLogEntry(
"controlRobot_RightFoot", [
this]() {
return controlRobot().surfacePose(
"RightFoot"); });
149 logger.addLogEntry(
"controlRobot_RightFootCenter",
150 [
this]() {
return controlRobot().surfacePose(
"RightFootCenter"); });
151 logger.addLogEntry(
"controlRobot_com", [
this]() {
return controlRobot().com(); });
152 logger.addLogEntry(
"controlRobot_comd", [
this]() {
return controlRobot().comVelocity(); });
153 logger.addLogEntry(
"controlRobot_posW", [
this]() {
return controlRobot().posW(); });
154 logger.addLogEntry(
"mpc_failures", [
this]() {
return nbMPCFailures_; });
155 logger.addLogEntry(
"left_foot_ratio", [
this]() {
return leftFootRatio_; });
157 logger.addLogEntry(
"pendulum_com", [
this]() {
return pendulum_.
com(); });
158 logger.addLogEntry(
"pendulum_comd", [
this]() {
return pendulum_.
comd(); });
159 logger.addLogEntry(
"pendulum_comdd", [
this]() {
return pendulum_.
comdd(); });
160 logger.addLogEntry(
"pendulum_dcm", [
this]() {
return pendulum_.
dcm(); });
161 logger.addLogEntry(
"pendulum_omega", [
this]() {
return pendulum_.
omega(); });
162 logger.addLogEntry(
"pendulum_zmp", [
this]() {
return pendulum_.
zmp(); });
163 logger.addLogEntry(
"plan_com_height", [
this]() {
return plan.
comHeight(); });
168 logger.addLogEntry(
"plan_landing_pitch", [
this]() {
return plan.
landingPitch(); });
171 logger.addLogEntry(
"plan_swing_height", [
this]() {
return plan.
swingHeight(); });
173 logger.addLogEntry(
"plan_takeoff_offset", [
this]() {
return plan.
takeoffOffset(); });
174 logger.addLogEntry(
"plan_takeoff_pitch", [
this]() {
return plan.
takeoffPitch(); });
175 logger.addLogEntry(
"realRobot_LeftFoot", [
this]() {
return realRobot().surfacePose(
"LeftFoot"); });
176 logger.addLogEntry(
"realRobot_LeftFootCenter", [
this]() {
return realRobot().surfacePose(
"LeftFootCenter"); });
177 logger.addLogEntry(
"realRobot_RightFoot", [
this]() {
return realRobot().surfacePose(
"RightFoot"); });
178 logger.addLogEntry(
"realRobot_RightFootCenter", [
this]() {
return realRobot().surfacePose(
"RightFootCenter"); });
179 logger.addLogEntry(
"realRobot_com", [
this]() {
return realCom_; });
180 logger.addLogEntry(
"realRobot_comd", [
this]() {
return realComd_; });
181 logger.addLogEntry(
"realRobot_dcm", [
this]() -> Eigen::Vector3d {
return realCom_ + realComd_ / pendulum_.
omega(); });
182 logger.addLogEntry(
"realRobot_posW", [
this]() {
return realRobot().posW(); });
183 logger.addLogEntry(
"realRobot_wrench", [
this]() {
return netWrenchObs_.
wrench(); });
184 logger.addLogEntry(
"realRobot_zmp", [
this]() {
return netWrenchObs_.
zmp(); });
189 using namespace mc_rtc::gui;
191 constexpr
double ARROW_HEAD_DIAM = 0.015;
192 constexpr
double ARROW_HEAD_LEN = 0.05;
193 constexpr
double ARROW_SHAFT_DIAM = 0.015;
194 constexpr
double FORCE_SCALE = 0.0015;
196 const std::map<char, Color> COLORS = {{
'r', Color{1.0, 0.0, 0.0}}, {
'g', Color{0.0, 1.0, 0.0}},
197 {
'b', Color{0.0, 0.0, 1.0}}, {
'y', Color{1.0, 0.5, 0.0}},
198 {
'c', Color{0.0, 0.5, 1.0}}, {
'm', Color{1.0, 0.0, 0.5}}};
200 ArrowConfig pendulumArrowConfig;
201 pendulumArrowConfig.color = COLORS.at(
'y');
202 pendulumArrowConfig.end_point_scale = 0.02;
203 pendulumArrowConfig.head_diam = .1 * ARROW_HEAD_DIAM;
204 pendulumArrowConfig.head_len = .1 * ARROW_HEAD_LEN;
205 pendulumArrowConfig.scale = 1.;
206 pendulumArrowConfig.shaft_diam = .1 * ARROW_SHAFT_DIAM;
207 pendulumArrowConfig.start_point_scale = 0.02;
209 ArrowConfig pendulumForceArrowConfig;
210 pendulumForceArrowConfig.shaft_diam = 1 * ARROW_SHAFT_DIAM;
211 pendulumForceArrowConfig.head_diam = 1 * ARROW_HEAD_DIAM;
212 pendulumForceArrowConfig.head_len = 1 * ARROW_HEAD_LEN;
213 pendulumForceArrowConfig.scale = 1.;
214 pendulumForceArrowConfig.start_point_scale = 0.02;
215 pendulumForceArrowConfig.end_point_scale = 0.;
217 ArrowConfig netWrenchForceArrowConfig = pendulumForceArrowConfig;
218 netWrenchForceArrowConfig.color = COLORS.at(
'r');
220 ArrowConfig refPendulumForceArrowConfig = pendulumForceArrowConfig;
221 refPendulumForceArrowConfig = COLORS.at(
'y');
223 ForceConfig copForceConfig(COLORS.at(
'g'));
224 copForceConfig.start_point_scale = 0.02;
225 copForceConfig.end_point_scale = 0.;
227 auto footStepPolygon = [](
const Contact & contact) {
228 std::vector<Eigen::Vector3d> polygon;
229 polygon.push_back(contact.vertex0());
230 polygon.push_back(contact.vertex1());
231 polygon.push_back(contact.vertex2());
232 polygon.push_back(contact.vertex3());
236 constexpr
double COM_POINT_SIZE = 0.02;
237 constexpr
double DCM_POINT_SIZE = 0.015;
241 Arrow(
"Pendulum_CoM", pendulumArrowConfig, [
this]() -> Eigen::Vector3d {
return this->pendulum_.zmp(); },
242 [
this]() -> Eigen::Vector3d {
return this->pendulum_.com(); }),
243 Point3D(
"Measured_CoM", PointConfig(COLORS.at(
'g'), COM_POINT_SIZE), [
this]() {
return realCom_; }),
244 Point3D(
"Pendulum_DCM", PointConfig(COLORS.at(
'y'), DCM_POINT_SIZE), [
this]() {
return pendulum_.dcm(); }),
245 Point3D(
"Measured_DCM", PointConfig(COLORS.at(
'g'), DCM_POINT_SIZE),
246 [
this]() -> Eigen::Vector3d {
return realCom_ + realComd_ /
pendulum().
omega(); }));
249 {
"Markers",
"Footsteps"},
250 Polygon(
"SupportContact", COLORS.at(
'g'),
251 [
this, footStepPolygon]() {
return footStepPolygon(
supportContact()); }),
252 Polygon(
"TargetContact", COLORS.at(
'b'), [
this, footStepPolygon]() {
return footStepPolygon(
targetContact()); }),
253 Polygon(
"FootstepPlan", COLORS.at(
'b'), [
this, footStepPolygon]() {
254 std::vector<std::vector<Eigen::Vector3d>> polygons;
256 for(
unsigned i = 0; i < contacts.size(); i++)
258 auto & contact = contacts[i];
261 constexpr
double SAME_CONTACT_DIST = 0.005;
263 if(supportDist > SAME_CONTACT_DIST && targetDist > SAME_CONTACT_DIST)
265 polygons.push_back(footStepPolygon(contact));
271 gui->addElement({
"Markers",
"Net wrench"},
272 Point3D(
"Stabilizer_ZMP", PointConfig(COLORS.at(
'm'), 0.02), [
this]() {
return stabilizer_.zmp(); }),
273 Point3D(
"Measured_ZMP", PointConfig(COLORS.at(
'r'), 0.02),
274 [
this]() -> Eigen::Vector3d {
return netWrenchObs_.zmp(); }),
275 Arrow(
"Measured_ZMPForce", netWrenchForceArrowConfig,
276 [
this]() -> Eigen::Vector3d {
return this->netWrenchObs_.zmp(); },
277 [
this, FORCE_SCALE]() -> Eigen::Vector3d {
278 return netWrenchObs_.zmp() + FORCE_SCALE * netWrenchObs_.wrench().force();
281 gui->addElement({
"Markers",
"Foot wrenches"},
282 Point3D(
"Stabilizer_LeftCoP", PointConfig(COLORS.at(
'm'), 0.01),
283 [
this]() {
return stabilizer_.leftFootTask->targetCoPW(); }),
284 Force(
"Measured_LeftCoPForce", copForceConfig,
285 [
this]() {
return this->robot().surfaceWrench(
"LeftFootCenter"); },
286 [
this]() {
return sva::PTransformd(this->robot().copW(
"LeftFootCenter")); }),
287 Point3D(
"Stabilizer_RightCoP", PointConfig(COLORS.at(
'm'), 0.01),
288 [
this]() {
return stabilizer_.rightFootTask->targetCoPW(); }),
289 Force(
"Measured_RightCoPForce", copForceConfig,
290 [
this]() {
return this->robot().surfaceWrench(
"RightFootCenter"); },
291 [
this]() {
return sva::PTransformd(this->robot().copW(
"RightFootCenter")); }));
293 gui->addElement({
"Walking",
"Main"},
294 Button(
"# EMERGENCY STOP",
296 LOG_ERROR(
"EMERGENCY STOP!");
300 Button(
"Reset", [
this]() {
301 LOG_WARNING(
"Reset to Initial state");
302 this->resume(
"Initial");
305 gui->addElement({
"Walking",
"CoM"}, Label(
"Plan name", [
this]() {
return plan.
name; }),
306 NumberInput(
"CoM height [m]", [
this]() {
return plan.
comHeight(); },
307 [
this](
double height) {
308 height =
clamp(height, minCoMHeight_, maxCoMHeight_);
311 NumberInput(
"Torso pitch [rad]", [
this]() {
return torsoPitch_; },
312 [
this](
double pitch) {
313 pitch =
clamp(pitch, minTorsoPitch_, maxTorsoPitch_);
314 defaultTorsoPitch_ = pitch;
318 gui->addElement({
"Walking",
"Swing"}, Label(
"Plan name", [
this]() {
return plan.
name; }),
319 NumberInput(
"Swing height [m]", [
this]() {
return plan.
swingHeight(); },
323 NumberInput(
"Takeoff pitch [rad]", [
this]() {
return plan.
takeoffPitch(); },
327 NumberInput(
"Landing pitch [rad]", [
this]() {
return plan.
landingPitch(); },
330 gui->addElement({
"Walking",
"Timings"}, Label(
"Plan name", [
this]() {
return plan.
name; }),
334 [
this](
double duration) {
336 duration = std::round(duration / T) * T;
340 [
this](
double duration) {
342 duration = std::round(duration / T) * T;
351 mc_control::fsm::Controller::reset(data);
354 gui_->removeCategory({
"Contacts"});
365 realRobot().velW(sva::MotionVecd::Zero());
374 stabilizer_.reset(robots());
377 leftFootRatioJumped_ =
true;
378 leftFootRatio_ = 0.5;
384 comVelFilter_.reset(controlCoM);
385 pendulum_.reset(controlCoM);
389 floatingBaseObs_.leftFootRatio(leftFootRatio_);
395 stabilizer_.updateState(realCom_, realComd_, netWrenchObs_.wrench(), leftFootRatio_);
403 if(std::abs(ratio - leftFootRatio_) > maxRatioVar)
405 LOG_WARNING(
"Left foot ratio jumped from " << leftFootRatio_ <<
" to " << ratio);
406 leftFootRatioJumped_ =
true;
408 leftFootRatio_ =
clamp(ratio, 0., 1.,
"leftFootRatio");
421 if(!mc_control::fsm::Controller::running())
423 return mc_control::fsm::Controller::run();
426 ctlTime_ += timeStep;
431 floatingBaseObs_.leftFootRatio(leftFootRatio_);
439 sva::PTransformd X_0_a = floatingBaseObs_.getAnchorFrame(
controlRobot());
440 pelvisOrientation_ = X_0_a.rotation();
442 torsoTask->orientation(mc_rbdyn::rpyToMat({0, torsoPitch_, 0}) * pelvisOrientation_);
445 stabilizer_.updateState(realCom_, realComd_, netWrenchObs_.wrench(), leftFootRatio_);
447 bool ret = mc_control::fsm::Controller::run();
448 if(mc_control::fsm::Controller::running())
457 constexpr
double MAX_HEIGHT_DIFF = 0.02;
460 LOG_WARNING(
"Already pausing, how did you get there?");
467 LOG_WARNING(
"Cannot pause on uneven ground, will pause later");
469 gui()->removeElement({
"Walking",
"Main"},
"Pause walking");
474 LOG_WARNING(
"Pausing now that contacts are at same level");
480 gui()->removeElement({
"Walking",
"Main"},
"Pause walking");
487 static bool isInTheAir =
false;
488 constexpr
double CONTACT_THRESHOLD = 30.;
489 double leftFootForce =
realRobot().forceSensor(
"LeftFootForceSensor").force().z();
490 double rightFootForce =
realRobot().forceSensor(
"RightFootForceSensor").force().z();
491 if(leftFootForce < CONTACT_THRESHOLD && rightFootForce < CONTACT_THRESHOLD)
495 LOG_WARNING(
"Robot is in the air");
503 LOG_INFO(
"Robot is on the ground again");
511 floatingBaseObs_.updateRobot(
realRobot());
513 if(!leftFootRatioJumped_)
515 comVelFilter_.update(realCom_);
519 comVelFilter_.updatePositionOnly(realCom_);
520 leftFootRatioJumped_ =
false;
522 realComd_ = comVelFilter_.vel();
527 bool loadingNewPlan = (
plan.
name != name);
534 mpc_.configure(mpcConfig_);
545 const sva::PTransformd & X_0_lf =
controlRobot().surfacePose(
"LeftFootCenter");
546 const sva::PTransformd & X_0_rf =
controlRobot().surfacePose(
"RightFootCenter");
554 LOG_INFO(
"Loaded footstep plan \"" << name <<
"\"");
560 if(segmentName_.length() > 0)
564 segmentName_ =
"t_" + std::to_string(++nbLogSegments_).erase(0, 1) +
"_" + label;
565 logger().addLogEntry(segmentName_, [
this]() {
return ctlTime_; });
570 logger().removeLogEntry(segmentName_);
578 if(mpc_.buildAndSolve())
bool pauseWalkingRequested
Has user clicked on the "Pause walking" button?
double measuredLeftFootRatio()
Estimate left foot pressure ratio from force sensors.
void updateSupportPath(const sva::PTransformd &X_0_lf, const sva::PTransformd &X_0_rf)
Update support path display.
double clamp(double v, double vMin, double vMax)
Clamp a value in a given interval.
void wrenchFaceMatrix(const Sole &sole)
Update H-representation of contact wrench cones.
PlanInterpolator planInterpolator
Footstep plan interpolator.
void addLogEntries(mc_rtc::Logger &logger)
Log stabilizer entries.
std::shared_ptr< mc_tasks::OrientationTask > torsoTask
Torso orientation task.
bool emergencyStop
Emergency flag: if on, the controller stops doing anything.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW Controller(std::shared_ptr< mc_rbdyn::RobotModule > robot, double dt, const mc_rtc::Configuration &config)
Initialize the controller.
void internalReset()
Reset robot to its initial (half-sitting) configuration.
void configure(const mc_rtc::Configuration &config)
Read configuration from dictionary.
double omega() const
Natural frequency.
const Eigen::Vector3d & comdd() const
CoM acceleration in the world frame.
const Eigen::Vector3d & comd() const
CoM velocity in the world frame.
mc_rbdyn::Robot & controlRobot()
Get control robot state.
mc_rbdyn::Robot & realRobot()
Get observed robot state.
std::vector< std::vector< double > > halfSitPose
Half-sit joint-angle configuration stored when the controller starts.
void pauseWalkingCallback(bool verbose=false)
Callback function called by "Pause walking" button.
void reset(const mc_rbdyn::Robots &robots)
Reset CoM and foot CoP tasks.
void sole(const Sole &sole)
Set model sole properties.
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.
static EIGEN_MAKE_ALIGNED_OPERATOR_NEW constexpr double SAMPLING_PERIOD
Duration of each sampling step in [s].
bool updatePreview()
Update horizontal MPC preview.
void startLogSegment(const std::string &label)
Start new log segment.
FootstepPlan plan
Current footstep plan.
const Contact & targetContact()
Get current target contact.
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.
Eigen::Vector3d dcm() const
Divergent component of motion.
const Contact & supportContact()
Get current support contact.
void addLogEntries(mc_rtc::Logger &logger)
Log controller entries.
double leftFootRatio()
Get fraction of total weight that should be sustained by the left foot.
const Eigen::Vector3d & zmp() const
Zero-tilting moment point.
EIGEN_MAKE_ALIGNED_OPERATOR_NEW Eigen::Vector2d leftAnkleOffset
Offset from center to ankle frames in the left sole frame.
void warnIfRobotIsInTheAir()
Log a warning message when robot is in the air.
const Eigen::Vector3d & zmp()
Zero-tilting moment point in the latest contact frame.
const sva::ForceVecd & wrench()
Net contact wrench.
std::shared_ptr< mc_tasks::OrientationTask > pelvisTask
Pelvis orientation task.
void addLogEntries(mc_rtc::Logger &logger)
Log stabilizer entries.
void loadFootstepPlan(std::string name)
Load footstep plan from configuration.
FootstepPlan getPlan(std::string name)
Get contact plan.
double stepWidth() const
Get step width.
void updateRealFromKinematics()
Update measured robot's floating base from kinematic observer.
Main controller namespace.
bool pauseWalking
Is the pause-walking behavior engaged?
const Eigen::Vector3d & com() const
CoM position in the world frame.
std::vector< std::string > availablePlans() const
List available contact plans.
virtual bool run() override
Main function of the controller, called at every control cycle.
void stopLogSegment()
Stop current log segment.
void sole(const Sole &sole)
Set model sole properties.
Pendulum & pendulum()
This getter is only used for consistency with the rest of mc_rtc.
void configure(const mc_rtc::Configuration &)
Read configuration from dictionary.
void reset(const mc_control::ControllerResetData &data) override
Reset controller.
std::shared_ptr< Preview > preview
Current solution trajectory from the walking pattern generator.
const sva::PTransformd & worldReference() const
Get walking target in world frame.
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.