Controller.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-2019, CNRS-UM LIRMM
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <mc_rbdyn/rpy_utils.h>
29 
32 
33 namespace lipm_walking
34 {
35 
36 Controller::Controller(std::shared_ptr<mc_rbdyn::RobotModule> robotModule,
37  double dt,
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, /* cutoff period = */ 0.01), netWrenchObs_(),
41  stabilizer_(controlRobot(), pendulum_, dt)
42 {
43  auto robotConfig = config("robot_models")(controlRobot().name());
44  auto planConfig = config("plans")(controlRobot().name());
45 
46  // Patch CoM height and step width in all plans
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)
54  {
55  auto plan = planConfig(p);
56  if(!plan.has("com_height"))
57  {
58  plan.add("com_height", comHeight);
59  }
60  }
61 
62  // Add upper-body tasks
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);
67  pelvisTask->orientation(pelvisOrientation_);
68  pelvisTask->stiffness(pelvisStiffness);
69  pelvisTask->weight(pelvisWeight);
70 
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);
76 
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_);
84  torsoTask->stiffness(torsoStiffness);
85  torsoTask->weight(torsoWeight);
86 
87  // Set half-sitting pose for posture task
88  const auto & halfSit = robotModule->stance();
89  const auto & refJointOrder = robot().refJointOrder();
90  for(unsigned i = 0; i < refJointOrder.size(); ++i)
91  {
92  if(robot().hasJoint(refJointOrder[i]))
93  {
94  halfSitPose[robot().jointIndexByName(refJointOrder[i])] = halfSit.at(refJointOrder[i]);
95  }
96  }
97 
98  // Read sole properties from robot model and configuration file
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");
106  sole_.leftAnkleOffset = X_lfc_lf.translation().head<2>();
107 
108  // Configure MPC solver
109  mpcConfig_ = config("mpc");
110  mpc_.sole(sole_);
111 
112  // Forward robot-specific settings to stabilizer configuration
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"));
118 
119  // Read footstep plans from configuration
120  planInterpolator.configure(planConfig);
121  planInterpolator.stepWidth(stepWidth);
122  std::string initialPlan = planInterpolator.availablePlans()[0];
123  config("initial_plan", initialPlan);
124  loadFootstepPlan(initialPlan);
125 
126  stabilizer_.reset(robots());
127  stabilizer_.sole(sole_);
128  stabilizer_.wrenchFaceMatrix(sole_);
129 
130  addLogEntries(logger());
131  mpc_.addLogEntries(logger());
132  stabilizer_.addLogEntries(logger());
133 
134  if(gui_)
135  {
136  addGUIElements(gui_);
137  mpc_.addGUIElements(gui_);
138  stabilizer_.addGUIElements(gui_);
139  }
140 
141  LOG_SUCCESS("LIPMWalking controller init done " << this)
142 }
143 
144 void Controller::addLogEntries(mc_rtc::Logger & logger)
145 {
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_; });
156  logger.addLogEntry("left_foot_ratio_measured", [this]() { return measuredLeftFootRatio(); });
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(); });
164  logger.addLogEntry("plan_double_support_duration", [this]() { return plan.doubleSupportDuration(); });
165  logger.addLogEntry("plan_final_dsp_duration", [this]() { return plan.finalDSPDuration(); });
166  logger.addLogEntry("plan_init_dsp_duration", [this]() { return plan.initDSPDuration(); });
167  logger.addLogEntry("plan_landing_duration", [this]() { return plan.landingDuration(); });
168  logger.addLogEntry("plan_landing_pitch", [this]() { return plan.landingPitch(); });
169  logger.addLogEntry("plan_ref_vel", [this]() { return plan.supportContact().refVel; });
170  logger.addLogEntry("plan_single_support_duration", [this]() { return plan.singleSupportDuration(); });
171  logger.addLogEntry("plan_swing_height", [this]() { return plan.swingHeight(); });
172  logger.addLogEntry("plan_takeoff_duration", [this]() { return plan.takeoffDuration(); });
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(); });
185 }
186 
187 void Controller::addGUIElements(std::shared_ptr<mc_rtc::gui::StateBuilder> gui)
188 {
189  using namespace mc_rtc::gui;
190 
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;
195 
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}}};
199 
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;
208 
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.;
216 
217  ArrowConfig netWrenchForceArrowConfig = pendulumForceArrowConfig;
218  netWrenchForceArrowConfig.color = COLORS.at('r');
219 
220  ArrowConfig refPendulumForceArrowConfig = pendulumForceArrowConfig;
221  refPendulumForceArrowConfig = COLORS.at('y');
222 
223  ForceConfig copForceConfig(COLORS.at('g'));
224  copForceConfig.start_point_scale = 0.02;
225  copForceConfig.end_point_scale = 0.;
226 
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());
233  return polygon;
234  };
235 
236  constexpr double COM_POINT_SIZE = 0.02;
237  constexpr double DCM_POINT_SIZE = 0.015;
238 
239  gui->addElement(
240  {"Markers", "CoM"},
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(); }));
247 
248  gui->addElement(
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;
255  const auto & contacts = plan.contacts();
256  for(unsigned i = 0; i < contacts.size(); i++)
257  {
258  auto & contact = contacts[i];
259  double supportDist = (contact.p() - supportContact().p()).norm();
260  double targetDist = (contact.p() - targetContact().p()).norm();
261  constexpr double SAME_CONTACT_DIST = 0.005;
262  // only display contact if it is not the support or target contact
263  if(supportDist > SAME_CONTACT_DIST && targetDist > SAME_CONTACT_DIST)
264  {
265  polygons.push_back(footStepPolygon(contact));
266  }
267  }
268  return polygons;
269  }));
270 
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();
279  }));
280 
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")); }));
292 
293  gui->addElement({"Walking", "Main"},
294  Button("# EMERGENCY STOP",
295  [this]() {
296  LOG_ERROR("EMERGENCY STOP!");
297  emergencyStop = true;
298  this->interrupt();
299  }),
300  Button("Reset", [this]() {
301  LOG_WARNING("Reset to Initial state");
302  this->resume("Initial");
303  }));
304 
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_);
309  plan.comHeight(height);
310  }),
311  NumberInput("Torso pitch [rad]", [this]() { return torsoPitch_; },
312  [this](double pitch) {
313  pitch = clamp(pitch, minTorsoPitch_, maxTorsoPitch_);
314  defaultTorsoPitch_ = pitch;
315  torsoPitch_ = pitch;
316  }));
317 
318  gui->addElement({"Walking", "Swing"}, Label("Plan name", [this]() { return plan.name; }),
319  NumberInput("Swing height [m]", [this]() { return plan.swingHeight(); },
320  [this](double height) { plan.swingHeight(height); }),
321  NumberInput("Takeoff duration [s]", [this]() { return plan.takeoffDuration(); },
322  [this](double duration) { plan.takeoffDuration(duration); }),
323  NumberInput("Takeoff pitch [rad]", [this]() { return plan.takeoffPitch(); },
324  [this](double pitch) { plan.takeoffPitch(pitch); }),
325  NumberInput("Landing duration [s]", [this]() { return plan.landingDuration(); },
326  [this](double duration) { plan.landingDuration(duration); }),
327  NumberInput("Landing pitch [rad]", [this]() { return plan.landingPitch(); },
328  [this](double pitch) { plan.landingPitch(pitch); }));
329 
330  gui->addElement({"Walking", "Timings"}, Label("Plan name", [this]() { return plan.name; }),
331  NumberInput("Initial DSP duration [s]", [this]() { return plan.initDSPDuration(); },
332  [this](double duration) { plan.initDSPDuration(duration); }),
333  NumberInput("SSP duration [s]", [this]() { return plan.singleSupportDuration(); },
334  [this](double duration) {
335  constexpr double T = ModelPredictiveControl::SAMPLING_PERIOD;
336  duration = std::round(duration / T) * T;
337  plan.singleSupportDuration(duration);
338  }),
339  NumberInput("DSP duration [s]", [this]() { return plan.doubleSupportDuration(); },
340  [this](double duration) {
341  constexpr double T = ModelPredictiveControl::SAMPLING_PERIOD;
342  duration = std::round(duration / T) * T;
343  plan.doubleSupportDuration(duration);
344  }),
345  NumberInput("Final DSP duration [s]", [this]() { return plan.finalDSPDuration(); },
346  [this](double duration) { plan.finalDSPDuration(duration); }));
347 }
348 
349 void Controller::reset(const mc_control::ControllerResetData & data)
350 {
351  mc_control::fsm::Controller::reset(data);
352  if(gui_)
353  {
354  gui_->removeCategory({"Contacts"});
355  }
356 }
357 
359 {
360  // (1) update floating-base transforms of both robot mbc's
361  auto X_0_fb = supportContact().robotTransform(controlRobot());
362  controlRobot().posW(X_0_fb);
363  controlRobot().velW(sva::MotionVecd::Zero());
364  realRobot().posW(X_0_fb);
365  realRobot().velW(sva::MotionVecd::Zero());
366 
367  // (2) update contact frames to coincide with surface ones
369 
370  // (3) reset solver tasks
371  postureTask->posture(halfSitPose);
372  solver().removeTask(pelvisTask);
373  solver().removeTask(torsoTask);
374  stabilizer_.reset(robots());
375 
376  // (4) reset controller attributes
377  leftFootRatioJumped_ = true;
378  leftFootRatio_ = 0.5;
379  nbMPCFailures_ = 0;
380  pauseWalking = false;
381  pauseWalkingRequested = false;
382 
383  Eigen::Vector3d controlCoM = controlRobot().com();
384  comVelFilter_.reset(controlCoM);
385  pendulum_.reset(controlCoM);
386 
387  // (5) reset floating-base observers
388  floatingBaseObs_.reset(controlRobot().posW());
389  floatingBaseObs_.leftFootRatio(leftFootRatio_);
390  floatingBaseObs_.run(realRobot());
391  updateRealFromKinematics(); // after leftFootRatio_ is initialized
392 
393  // (6) updates that depend on realCom_
394  netWrenchObs_.update(realRobot(), supportContact());
395  stabilizer_.updateState(realCom_, realComd_, netWrenchObs_.wrench(), leftFootRatio_);
396 
397  stopLogSegment();
398 }
399 
400 void Controller::leftFootRatio(double ratio)
401 {
402  double maxRatioVar = 1.5 * timeStep / plan.doubleSupportDuration();
403  if(std::abs(ratio - leftFootRatio_) > maxRatioVar)
404  {
405  LOG_WARNING("Left foot ratio jumped from " << leftFootRatio_ << " to " << ratio);
406  leftFootRatioJumped_ = true;
407  }
408  leftFootRatio_ = clamp(ratio, 0., 1., "leftFootRatio");
409 }
410 
412 {
413  if(emergencyStop)
414  {
415  return false;
416  }
418  {
420  }
421  if(!mc_control::fsm::Controller::running())
422  {
423  return mc_control::fsm::Controller::run();
424  }
425 
426  ctlTime_ += timeStep;
427 
429 
430  // update floating base estimate from IMU and joint encoder measurements
431  floatingBaseObs_.leftFootRatio(leftFootRatio_);
432  floatingBaseObs_.run(realRobot());
434 
435  // update pelvis and torso orientations based on the anchor frame estimate
436  // note that these two tasks contribute to stabilization because the anchor
437  // frame orientation changes when foot frames comply with the ground
438  // see https://github.com/stephane-caron/lipm_walking_controller/issues/32
439  sva::PTransformd X_0_a = floatingBaseObs_.getAnchorFrame(controlRobot());
440  pelvisOrientation_ = X_0_a.rotation();
441  pelvisTask->orientation(pelvisOrientation_);
442  torsoTask->orientation(mc_rbdyn::rpyToMat({0, torsoPitch_, 0}) * pelvisOrientation_);
443 
444  netWrenchObs_.update(realRobot(), supportContact());
445  stabilizer_.updateState(realCom_, realComd_, netWrenchObs_.wrench(), leftFootRatio_);
446 
447  bool ret = mc_control::fsm::Controller::run();
448  if(mc_control::fsm::Controller::running())
449  {
450  postureTask->posture(halfSitPose); // reset posture in case the FSM updated it
451  }
452  return ret;
453 }
454 
456 {
457  constexpr double MAX_HEIGHT_DIFF = 0.02; // [m]
458  if(pauseWalking)
459  {
460  LOG_WARNING("Already pausing, how did you get there?");
461  return;
462  }
463  else if(std::abs(supportContact().z() - targetContact().z()) > MAX_HEIGHT_DIFF)
464  {
465  if(!pauseWalkingRequested || verbose)
466  {
467  LOG_WARNING("Cannot pause on uneven ground, will pause later");
468  }
469  gui()->removeElement({"Walking", "Main"}, "Pause walking");
470  pauseWalkingRequested = true;
471  }
472  else if(pauseWalkingRequested)
473  {
474  LOG_WARNING("Pausing now that contacts are at same level");
475  pauseWalkingRequested = false;
476  pauseWalking = true;
477  }
478  else // (!pauseWalkingRequested)
479  {
480  gui()->removeElement({"Walking", "Main"}, "Pause walking");
481  pauseWalking = true;
482  }
483 }
484 
486 {
487  static bool isInTheAir = false;
488  constexpr double CONTACT_THRESHOLD = 30.; // [N]
489  double leftFootForce = realRobot().forceSensor("LeftFootForceSensor").force().z();
490  double rightFootForce = realRobot().forceSensor("RightFootForceSensor").force().z();
491  if(leftFootForce < CONTACT_THRESHOLD && rightFootForce < CONTACT_THRESHOLD)
492  {
493  if(!isInTheAir)
494  {
495  LOG_WARNING("Robot is in the air");
496  isInTheAir = true;
497  }
498  }
499  else
500  {
501  if(isInTheAir)
502  {
503  LOG_INFO("Robot is on the ground again");
504  isInTheAir = false;
505  }
506  }
507 }
508 
510 {
511  floatingBaseObs_.updateRobot(realRobot());
512  realCom_ = realRobot().com();
513  if(!leftFootRatioJumped_)
514  {
515  comVelFilter_.update(realCom_);
516  }
517  else // don't update velocity when CoM position jumped
518  {
519  comVelFilter_.updatePositionOnly(realCom_);
520  leftFootRatioJumped_ = false;
521  }
522  realComd_ = comVelFilter_.vel();
523 }
524 
525 void Controller::loadFootstepPlan(std::string name)
526 {
527  bool loadingNewPlan = (plan.name != name);
528  double initHeight = (plan.name.length() > 0) ? plan.supportContact().p().z() : 0.;
529  FootstepPlan defaultPlan = planInterpolator.getPlan(name);
530  if(loadingNewPlan)
531  {
532  plan = defaultPlan;
533  plan.name = name;
534  mpc_.configure(mpcConfig_);
535  if(!plan.mpcConfig.empty())
536  {
537  mpc_.configure(plan.mpcConfig);
538  }
539  }
540  else // only reload contacts
541  {
542  plan.resetContacts(defaultPlan.contacts());
543  }
544  plan.complete(sole_);
545  const sva::PTransformd & X_0_lf = controlRobot().surfacePose("LeftFootCenter");
546  const sva::PTransformd & X_0_rf = controlRobot().surfacePose("RightFootCenter");
547  plan.updateInitialTransform(X_0_lf, X_0_rf, initHeight);
549  planInterpolator.updateSupportPath(X_0_lf, X_0_rf);
550  plan.rewind();
551  torsoPitch_ = (plan.hasTorsoPitch()) ? plan.torsoPitch() : defaultTorsoPitch_;
552  if(loadingNewPlan)
553  {
554  LOG_INFO("Loaded footstep plan \"" << name << "\"");
555  }
556 }
557 
558 void Controller::startLogSegment(const std::string & label)
559 {
560  if(segmentName_.length() > 0)
561  {
562  stopLogSegment();
563  }
564  segmentName_ = "t_" + std::to_string(++nbLogSegments_).erase(0, 1) + "_" + label;
565  logger().addLogEntry(segmentName_, [this]() { return ctlTime_; });
566 }
567 
569 {
570  logger().removeLogEntry(segmentName_);
571  segmentName_ = "";
572 }
573 
575 {
576  mpc_.initState(pendulum());
577  mpc_.comHeight(plan.comHeight());
578  if(mpc_.buildAndSolve())
579  {
580  preview = mpc_.solution();
581  return true;
582  }
583  else
584  {
585  nbMPCFailures_++;
586  return false;
587  }
588 }
589 
590 } // namespace lipm_walking
591 
592 CONTROLLER_CONSTRUCTOR("LIPMWalking", lipm_walking::Controller)
bool pauseWalkingRequested
Has user clicked on the "Pause walking" button?
Definition: Controller.h:329
double measuredLeftFootRatio()
Estimate left foot pressure ratio from force sensors.
Definition: Controller.h:225
void updateSupportPath(const sva::PTransformd &X_0_lf, const sva::PTransformd &X_0_rf)
Update support path display.
const Eigen::Vector3d & p() const
Shorthand for position.
Definition: Contact.h:111
double clamp(double v, double vMin, double vMax)
Clamp a value in a given interval.
Definition: clamp.h:47
double finalDSPDuration() const
Get final double support phase duration.
Definition: FootstepPlan.h:164
double initDSPDuration() const
Get initial double support phase duration.
Definition: FootstepPlan.h:190
void wrenchFaceMatrix(const Sole &sole)
Update H-representation of contact wrench cones.
Definition: Stabilizer.h:247
PlanInterpolator planInterpolator
Footstep plan interpolator.
Definition: Controller.h:326
void addLogEntries(mc_rtc::Logger &logger)
Log stabilizer entries.
Definition: Stabilizer.cpp:76
double takeoffPitch() const
Get swing foot takeoff pitch angle.
Definition: FootstepPlan.h:382
std::shared_ptr< mc_tasks::OrientationTask > torsoTask
Torso orientation task.
Definition: Controller.h:332
bool emergencyStop
Emergency flag: if on, the controller stops doing anything.
Definition: Controller.h:327
EIGEN_MAKE_ALIGNED_OPERATOR_NEW Controller(std::shared_ptr< mc_rbdyn::RobotModule > robot, double dt, const mc_rtc::Configuration &config)
Initialize the controller.
Definition: Controller.cpp:36
void internalReset()
Reset robot to its initial (half-sitting) configuration.
Definition: Controller.cpp:358
void configure(const mc_rtc::Configuration &config)
Read configuration from dictionary.
double omega() const
Natural frequency.
Definition: Pendulum.h:144
const Eigen::Vector3d & comdd() const
CoM acceleration in the world frame.
Definition: Pendulum.h:128
sva::PTransformd robotTransform(const mc_rbdyn::Robot &robot) const
Compute floating base transform that puts the robot in contact.
Definition: Contact.h:304
double swingHeight() const
Default swing-foot height.
Definition: FootstepPlan.h:318
void updateInitialTransform(const sva::PTransformd &X_0_lf, const sva::PTransformd &X_0_rf, double initHeight)
Update plan coordinates from robot foot transforms.
const Eigen::Vector3d & comd() const
CoM velocity in the world frame.
Definition: Pendulum.h:120
mc_rbdyn::Robot & controlRobot()
Get control robot state.
Definition: Controller.h:175
mc_rbdyn::Robot & realRobot()
Get observed robot state.
Definition: Controller.h:279
std::vector< std::vector< double > > halfSitPose
Half-sit joint-angle configuration stored when the controller starts.
Definition: Controller.h:334
double comHeight() const
Default CoM height above ground contact.
Definition: FootstepPlan.h:122
Main controller class.
Definition: Controller.h:67
Eigen::Vector3d refVel
Desired CoM velocity when the robot is supporting itself on this contact.
Definition: Contact.h:314
void pauseWalkingCallback(bool verbose=false)
Callback function called by "Pause walking" button.
Definition: Controller.cpp:455
void reset(const mc_rbdyn::Robots &robots)
Reset CoM and foot CoP tasks.
Definition: Stabilizer.cpp:308
void sole(const Sole &sole)
Set model sole properties.
Definition: Stabilizer.h:218
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.
Definition: Controller.cpp:187
static EIGEN_MAKE_ALIGNED_OPERATOR_NEW constexpr double SAMPLING_PERIOD
Duration of each sampling step in [s].
bool updatePreview()
Update horizontal MPC preview.
Definition: Controller.cpp:574
void startLogSegment(const std::string &label)
Start new log segment.
Definition: Controller.cpp:558
const Contact & supportContact() const
Current support contact.
Definition: FootstepPlan.h:302
void rewind()
Rewind plan to the beginning.
Definition: FootstepPlan.h:420
FootstepPlan plan
Current footstep plan.
Definition: Controller.h:325
const Contact & targetContact()
Get current target contact.
Definition: Controller.h:319
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.
Definition: Stabilizer.cpp:127
Eigen::Vector3d dcm() const
Divergent component of motion.
Definition: Pendulum.h:136
const Contact & supportContact()
Get current support contact.
Definition: Controller.h:311
void addLogEntries(mc_rtc::Logger &logger)
Log controller entries.
Definition: Controller.cpp:144
double leftFootRatio()
Get fraction of total weight that should be sustained by the left foot.
Definition: Controller.h:217
void resetContacts(const std::vector< Contact > &contacts)
Reset contacts from another footstep plan.
Definition: FootstepPlan.h:278
const Eigen::Vector3d & zmp() const
Zero-tilting moment point.
Definition: Pendulum.h:155
EIGEN_MAKE_ALIGNED_OPERATOR_NEW Eigen::Vector2d leftAnkleOffset
Offset from center to ankle frames in the left sole frame.
Definition: Sole.h:42
void warnIfRobotIsInTheAir()
Log a warning message when robot is in the air.
Definition: Controller.cpp:485
mc_rtc::Configuration mpcConfig
Definition: FootstepPlan.h:426
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.
Definition: Controller.h:331
void addLogEntries(mc_rtc::Logger &logger)
Log stabilizer entries.
double singleSupportDuration() const
Default single-support duration.
Definition: FootstepPlan.h:286
void loadFootstepPlan(std::string name)
Load footstep plan from configuration.
Definition: Controller.cpp:525
FootstepPlan getPlan(std::string name)
Get contact plan.
double takeoffDuration() const
Get swing foot takeoff duration.
Definition: FootstepPlan.h:340
double stepWidth() const
Get step width.
void updateRealFromKinematics()
Update measured robot&#39;s floating base from kinematic observer.
Definition: Controller.cpp:509
Main controller namespace.
Definition: build.dox:1
bool pauseWalking
Is the pause-walking behavior engaged?
Definition: Controller.h:328
const Eigen::Vector3d & com() const
CoM position in the world frame.
Definition: Pendulum.h:112
std::vector< std::string > availablePlans() const
List available contact plans.
const sva::PTransformd & initPose()
Initial transform with respect to world frame.
Definition: FootstepPlan.h:208
virtual bool run() override
Main function of the controller, called at every control cycle.
Definition: Controller.cpp:411
EIGEN_MAKE_ALIGNED_OPERATOR_NEW void complete(const Sole &sole)
Complete contacts from sole parameters.
bool hasTorsoPitch() const
Does the plan provide a reference torso pitch?
Definition: FootstepPlan.h:172
double doubleSupportDuration() const
Default double-support duration.
Definition: FootstepPlan.h:146
Contacts wrap foot frames with extra info from the footstep plan.
Definition: Contact.h:58
void stopLogSegment()
Stop current log segment.
Definition: Controller.cpp:568
void sole(const Sole &sole)
Set model sole properties.
double landingPitch() const
Get swing foot landing pitch angle.
Definition: FootstepPlan.h:238
Eigen::Vector3d takeoffOffset() const
Get swing foot takeoff offset.
Definition: FootstepPlan.h:362
double torsoPitch() const
Reference torso pitch angle.
Definition: FootstepPlan.h:412
double landingDuration() const
Get swing foot landing ratio.
Definition: FootstepPlan.h:216
Pendulum & pendulum()
This getter is only used for consistency with the rest of mc_rtc.
Definition: Controller.h:263
void configure(const mc_rtc::Configuration &)
Read configuration from dictionary.
Definition: Stabilizer.cpp:245
void reset(const mc_control::ControllerResetData &data) override
Reset controller.
Definition: Controller.cpp:349
Sequence of footsteps with gait parameters.
Definition: FootstepPlan.h:41
const std::vector< Contact > & contacts() const
Reference to list of contacts.
Definition: FootstepPlan.h:138
std::shared_ptr< Preview > preview
Current solution trajectory from the walking pattern generator.
Definition: Controller.h:330
const sva::PTransformd & worldReference() const
Get walking target in world frame.
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.