Standing.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 "Standing.h"
29 
32 
33 namespace lipm_walking
34 {
35 
36 namespace
37 {
38 constexpr double COM_STIFFNESS = 5.; // standing has CoM set-point task
39 }
40 
42 {
43  auto & ctl = controller();
44  auto & supportContact = ctl.supportContact();
45  auto & targetContact = ctl.targetContact();
46 
47  freeFootGain_ = 30.;
48  isMakingFootContact_ = false;
49  planChanged_ = false;
50  lastInterpolatorIter_ = ctl.planInterpolator.nbIter;
51  leftFootRatio_ = ctl.leftFootRatio();
52  releaseHeight_ = 0.05; // [m]
53  startWalking_ = false;
54  if(supportContact.surfaceName == "RightFootCenter")
55  {
56  leftFootContact_ = targetContact;
57  rightFootContact_ = supportContact;
58  }
59  else if(supportContact.surfaceName == "LeftFootCenter")
60  {
61  leftFootContact_ = supportContact;
62  rightFootContact_ = targetContact;
63  }
64  else
65  {
66  LOG_ERROR_AND_THROW(std::invalid_argument, "Unknown surface name: " << supportContact.surfaceName);
67  }
68 
69  if(ctl.isLastDSP())
70  {
71  ctl.loadFootstepPlan(ctl.plan.name);
72  }
73 
75  stabilizer().setContact(stabilizer().leftFootTask, leftFootContact_);
76  stabilizer().setContact(stabilizer().rightFootTask, rightFootContact_);
77  stabilizer().addTasks(ctl.solver());
78  ctl.solver().addTask(ctl.pelvisTask);
79  ctl.solver().addTask(ctl.torsoTask);
80 
81  updatePlan(ctl.plan.name);
82  updateTarget(leftFootRatio_);
83 
84  logger().addLogEntry("support_xmax",
85  [&ctl]() { return std::max(ctl.supportContact().xmax(), ctl.targetContact().xmax()); });
86  logger().addLogEntry("support_xmin",
87  [&ctl]() { return std::min(ctl.supportContact().xmin(), ctl.targetContact().xmin()); });
88  logger().addLogEntry("support_ymax",
89  [&ctl]() { return std::max(ctl.supportContact().ymax(), ctl.targetContact().ymax()); });
90  logger().addLogEntry("support_ymin",
91  [&ctl]() { return std::min(ctl.supportContact().ymin(), ctl.targetContact().ymin()); });
92  logger().addLogEntry("support_zmax",
93  [&ctl]() { return std::max(ctl.supportContact().zmax(), ctl.targetContact().zmax()); });
94  logger().addLogEntry("support_zmin",
95  [&ctl]() { return std::min(ctl.supportContact().zmin(), ctl.targetContact().zmin()); });
96  logger().addLogEntry("walking_phase", []() { return 3.; });
97  ctl.stopLogSegment();
98 
99  if(gui())
100  {
101  using namespace mc_rtc::gui;
102  gui()->removeElement({"Walking", "Main"}, "Pause walking");
103  gui()->addElement({"Walking", "Main"}, ComboInput("Footstep plan", ctl.planInterpolator.availablePlans(),
104  [&ctl]() { return ctl.plan.name; },
105  [this](const std::string & name) { updatePlan(name); }));
106  gui()->addElement({"Walking", "Main"}, Button((supportContact.id == 0) ? "Start walking" : "Resume walking",
107  [this]() { startWalking(); }));
108  gui()->addElement({"Standing"},
109  NumberInput("CoM target [0-1]", [this]() { return std::round(leftFootRatio_ * 10.) / 10.; },
110  [this](double leftFootRatio) { updateTarget(leftFootRatio); }),
111  NumberInput("Free foot gain", [this]() { return std::round(freeFootGain_); },
112  [this](double gain) { freeFootGain_ = clamp(gain, 5., 100.); }),
113  NumberInput("Release height [m]", [this]() { return std::round(releaseHeight_ * 100.) / 100.; },
114  [this](double height) { releaseHeight_ = clamp(height, 0., 0.25); }),
115  Label("Left foot force [N]",
116  [&ctl]() { return ctl.realRobot().forceSensor("LeftFootForceSensor").force().z(); }),
117  Label("Right foot force [N]",
118  [&ctl]() { return ctl.realRobot().forceSensor("RightFootForceSensor").force().z(); }),
119  Button("Go to left foot", [this]() { updateTarget(1.); }),
120  Button("Go to middle", [this]() { updateTarget(0.5); }),
121  Button("Go to right foot", [this]() { updateTarget(0.); }),
122  Button("Make left foot contact", [this]() { makeLeftFootContact(); }),
123  Button("Make right foot contact", [this]() { makeRightFootContact(); }),
124  Button("Release left foot", [this]() { releaseLeftFootContact(); }),
125  Button("Release right foot", [this]() { releaseRightFootContact(); }));
126  }
127 
128  runState(); // don't wait till next cycle to update reference and tasks
129 }
130 
132 {
133  auto & ctl = controller();
134 
135  stabilizer().removeTasks(ctl.solver());
136 
137  logger().removeLogEntry("support_xmax");
138  logger().removeLogEntry("support_xmin");
139  logger().removeLogEntry("support_ymax");
140  logger().removeLogEntry("support_ymin");
141  logger().removeLogEntry("support_zmax");
142  logger().removeLogEntry("support_zmin");
143  logger().removeLogEntry("walking_phase");
144 
145  if(gui())
146  {
147  gui()->removeCategory({"Standing"});
148  gui()->removeElement({"Walking", "Main"}, "Footstep plan");
149  gui()->removeElement({"Walking", "Main"}, "Gait");
150  gui()->removeElement({"Walking", "Main"}, "Go to middle");
151  gui()->removeElement({"Walking", "Main"}, "Resume walking");
152  gui()->removeElement({"Walking", "Main"}, "Start walking");
153  }
154 }
155 
157 {
159 
160  if(isMakingFootContact_)
161  {
162  auto & leftFootTask = stabilizer().leftFootTask;
163  auto & rightFootTask = stabilizer().rightFootTask;
164  bool isLeftFootSeekingContact = (leftFootTask->admittance().couple().x() < 1e-10);
165  bool isRightFootSeekingContact = (rightFootTask->admittance().couple().x() < 1e-10);
166  bool leftFootTouchdown = stabilizer().detectTouchdown(leftFootTask, leftFootContact_);
167  bool rightFootTouchdown = stabilizer().detectTouchdown(rightFootTask, rightFootContact_);
168  if(isLeftFootSeekingContact && leftFootTouchdown)
169  {
170  stabilizer().setContact(leftFootTask, leftFootContact_);
171  }
172  if(isRightFootSeekingContact && rightFootTouchdown)
173  {
174  stabilizer().setContact(rightFootTask, rightFootContact_);
175  }
176  isLeftFootSeekingContact = (leftFootTask->admittance().couple().x() < 1e-10);
177  isRightFootSeekingContact = (rightFootTask->admittance().couple().x() < 1e-10);
178  if(!isLeftFootSeekingContact && !isRightFootSeekingContact)
179  {
180  isMakingFootContact_ = false;
182  }
183  }
184 
185  auto & ctl = controller();
186  auto & pendulum = ctl.pendulum();
187 
188  Eigen::Vector3d comTarget = copTarget_ + Eigen::Vector3d{0., 0., ctl.plan.comHeight()};
189  const Eigen::Vector3d & com_i = pendulum.com();
190  const Eigen::Vector3d & comd_i = pendulum.comd();
191  const Eigen::Vector3d & cop_f = copTarget_;
192 
193  double K = COM_STIFFNESS;
194  double D = 2 * std::sqrt(K);
195  Eigen::Vector3d comdd = K * (comTarget - com_i) - D * comd_i;
196  Eigen::Vector3d n = ctl.supportContact().normal();
197  double lambda = n.dot(comdd - world::gravity) / n.dot(com_i - cop_f);
198  Eigen::Vector3d zmp = com_i + (world::gravity - comdd) / lambda;
199 
200  pendulum.integrateIPM(zmp, lambda, ctl.timeStep);
201  ctl.leftFootRatio(leftFootRatio_);
202  ctl.stabilizer().run();
203 }
204 
206 {
207  auto & ctl = controller();
208  if(ctl.planInterpolator.nbIter > lastInterpolatorIter_)
209  {
210  ctl.loadFootstepPlan(ctl.planInterpolator.customPlanName());
211  lastInterpolatorIter_ = ctl.planInterpolator.nbIter;
212  planChanged_ = true;
213  }
214  if(planChanged_)
215  {
216  if(gui())
217  {
218  gui()->removeElement({"Walking", "Main"}, "Resume walking");
219  gui()->removeElement({"Walking", "Main"}, "Start walking");
220  gui()->addElement({"Walking", "Main"}, mc_rtc::gui::Button("Start walking", [this]() { startWalking(); }));
221  }
222  planChanged_ = false;
223  }
224 }
225 
226 void states::Standing::updateTarget(double leftFootRatio)
227 {
228  auto & sole = controller().sole();
229  if(controller().stabilizer().contactState() != ContactState::DoubleSupport)
230  {
231  LOG_ERROR("Cannot update CoM target while in single support");
232  return;
233  }
234  leftFootRatio = clamp(leftFootRatio, 0., 1., "Standing target");
235  sva::PTransformd X_0_lfr =
236  sva::interpolate(rightFootContact_.anklePose(sole), leftFootContact_.anklePose(sole), leftFootRatio);
237  copTarget_ = X_0_lfr.translation();
238  leftFootRatio_ = leftFootRatio;
239 }
240 
241 void states::Standing::makeFootContact(std::shared_ptr<mc_tasks::force::CoPTask> footTask, const Contact & contact)
242 {
243  auto & stabilizer = controller().stabilizer();
244  if(footTask->admittance().couple().x() > 1e-10 || stabilizer.detectTouchdown(footTask, contact))
245  {
246  LOG_WARNING("Foot is already in contact");
247  return;
248  }
249  stabilizer.setSwingFoot(footTask);
250  stabilizer.seekTouchdown(footTask);
251  footTask->stiffness(freeFootGain_); // sets damping as well
252  footTask->targetPose(contact.pose);
253  isMakingFootContact_ = true;
254 }
255 
257 {
258  makeFootContact(controller().stabilizer().leftFootTask, leftFootContact_);
259 }
260 
262 {
263  makeFootContact(controller().stabilizer().rightFootTask, rightFootContact_);
264 }
265 
266 bool states::Standing::releaseFootContact(std::shared_ptr<mc_tasks::force::CoPTask> footTask)
267 {
268  constexpr double MAX_FOOT_RELEASE_FORCE = 50.; // [N]
269  auto & stabilizer = controller().stabilizer();
270  if(footTask->admittance().couple().x() < 1e-10)
271  {
272  LOG_WARNING("Foot contact is already released");
273  return false;
274  }
275  else if(footTask->measuredWrench().force().z() > MAX_FOOT_RELEASE_FORCE)
276  {
277  LOG_ERROR("Contact force is too high to release foot");
278  return false;
279  }
280  sva::PTransformd X_0_f = footTask->surfacePose();
281  sva::PTransformd X_f_t = Eigen::Vector3d{0., 0., releaseHeight_};
282  stabilizer.setSwingFoot(footTask);
283  footTask->stiffness(freeFootGain_); // sets damping as well
284  footTask->targetPose(X_f_t * X_0_f);
285  return true;
286 }
287 
289 {
290  if(releaseFootContact(controller().stabilizer().leftFootTask))
291  {
293  }
294 }
295 
297 {
298  if(releaseFootContact(controller().stabilizer().rightFootTask))
299  {
301  }
302 }
303 
305 {
306  auto & ctl = controller();
307 
308  if(isMakingFootContact_)
309  {
310  return false;
311  }
312  if(!startWalking_)
313  {
314  return false;
315  }
316 
317  ctl.mpc().contacts(ctl.supportContact(), ctl.targetContact(), ctl.nextContact());
318  ctl.mpc().phaseDurations(0., ctl.plan.initDSPDuration(), ctl.singleSupportDuration());
319  if(ctl.updatePreview())
320  {
321  ctl.nextDoubleSupportDuration(ctl.plan.initDSPDuration());
322  ctl.startLogSegment(ctl.plan.name);
323  output("DoubleSupport");
324  return true;
325  }
326  return false;
327 }
328 
330 {
331  auto & ctl = controller();
332  if(ctl.isLastSSP())
333  {
334  LOG_ERROR("No footstep in contact plan");
335  return;
336  }
337  startWalking_ = true;
338  gui()->addElement({"Walking", "Main"},
339  mc_rtc::gui::Button("Pause walking", [&ctl]() { ctl.pauseWalkingCallback(/* verbose = */ true); }));
340 }
341 
342 void states::Standing::updatePlan(const std::string & name)
343 {
344  auto & ctl = controller();
345  if(name.find("custom") != std::string::npos)
346  {
347  if(!ctl.planInterpolator.isShown)
348  {
349  ctl.planInterpolator.addGUIElements();
350  ctl.planInterpolator.isShown = true;
351  }
352  if(name.find("backward") != std::string::npos)
353  {
354  ctl.planInterpolator.restoreBackwardTarget();
355  }
356  else if(name.find("forward") != std::string::npos)
357  {
358  ctl.planInterpolator.restoreForwardTarget();
359  }
360  else if(name.find("lateral") != std::string::npos)
361  {
362  ctl.planInterpolator.restoreLateralTarget();
363  }
364  ctl.loadFootstepPlan(ctl.planInterpolator.customPlanName());
365  }
366  else // new plan is not custom
367  {
368  if(ctl.planInterpolator.isShown)
369  {
370  ctl.planInterpolator.removeGUIElements();
371  ctl.planInterpolator.isShown = false;
372  }
373  ctl.loadFootstepPlan(name);
374  }
375  planChanged_ = true;
376 }
377 
378 } // namespace lipm_walking
379 
380 EXPORT_SINGLE_STATE("Standing", lipm_walking::states::Standing)
double clamp(double v, double vMin, double vMax)
Clamp a value in a given interval.
Definition: clamp.h:47
ContactState contactState()
Get contact state.
Definition: Stabilizer.h:200
Pendulum & pendulum()
Get pendulum reference.
Definition: State.h:82
void seekTouchdown(std::shared_ptr< mc_tasks::force::CoPTask > footTask)
Configure foot task for contact seeking.
Definition: Stabilizer.cpp:412
Stabilizer & stabilizer()
This getter is only used for consistency with the rest of mc_rtc.
Definition: Controller.h:303
void addTasks(mc_solver::QPSolver &solver)
Add tasks to QP solver.
Definition: Stabilizer.cpp:358
Enable stabilizer and keep CoM at a reference position.
Definition: Standing.h:53
std::shared_ptr< mc_rtc::gui::StateBuilder > gui()
Get GUI handle.
Definition: State.h:62
void checkPlanUpdates()
Check for footstep plan updates.
Definition: Standing.cpp:205
const Eigen::Vector3d & comd() const
CoM velocity in the world frame.
Definition: Pendulum.h:120
Stabilizer & stabilizer()
Get stabilizer.
Definition: State.h:115
bool releaseFootContact(std::shared_ptr< mc_tasks::force::CoPTask > footTask)
Release foot contact.
Definition: Standing.cpp:266
bool detectTouchdown(const std::shared_ptr< mc_tasks::force::CoPTask > footTask, const Contact &contact)
Detect foot touchdown based on both force and distance.
Definition: Stabilizer.cpp:400
void startWalking()
Enable startWalking_ boolean and update GUI.
Definition: Standing.cpp:329
void makeLeftFootContact()
Make left foot contact.
Definition: Standing.cpp:256
std::shared_ptr< mc_tasks::force::CoPTask > leftFootTask
Left foot hybrid position/force control task.
Definition: Stabilizer.h:390
std::shared_ptr< mc_tasks::force::CoPTask > rightFootTask
Right foot hybrid position/force control task.
Definition: Stabilizer.h:391
void releaseRightFootContact()
Release right foot contact.
Definition: Standing.cpp:296
void integrateIPM(Eigen::Vector3d zmp, double lambda, double dt)
Integrate in floating-base inverted pendulum mode with constant inputs.
Definition: Pendulum.cpp:52
mc_rtc::Logger & logger()
Get logger.
Definition: State.h:72
void updatePlan(const std::string &name)
Change footstep plan.
Definition: Standing.cpp:342
const Eigen::Vector3d gravity
Definition: world.h:40
void updateTarget(double leftFootRatio)
Update target CoM and CoP.
Definition: Standing.cpp:226
void setContact(std::shared_ptr< mc_tasks::force::CoPTask > footTask, const Contact &contact)
Configure foot task for contact at a given location.
Definition: Stabilizer.cpp:372
Main controller namespace.
Definition: build.dox:1
void removeTasks(mc_solver::QPSolver &solver)
Remove tasks from QP solver.
Definition: Stabilizer.cpp:365
void setSwingFoot(std::shared_ptr< mc_tasks::force::CoPTask > footTask)
Configure foot task for swinging.
Definition: Stabilizer.cpp:393
const Eigen::Vector3d & com() const
CoM position in the world frame.
Definition: Pendulum.h:112
const Sole & sole() const
Get model sole properties.
Definition: Controller.h:295
void runState() override
Main state function, called if no transition at this cycle.
Definition: Standing.cpp:156
Contacts wrap foot frames with extra info from the footstep plan.
Definition: Contact.h:58
void makeRightFootContact()
Make right foot contact.
Definition: Standing.cpp:261
Controller & controller()
Get controller.
Definition: State.h:52
void makeFootContact(std::shared_ptr< mc_tasks::force::CoPTask > footTask, const Contact &contact)
Make foot contact.
Definition: Standing.cpp:241
void start() override
Start state.
Definition: Standing.cpp:41
bool checkTransitions() override
Check transitions at beginning of control cycle.
Definition: Standing.cpp:304
sva::PTransformd pose
Plücker transform of the contact in the inertial frame.
Definition: Contact.h:321
void releaseLeftFootContact()
Release left foot contact.
Definition: Standing.cpp:288
void teardown() override
Teardown state.
Definition: Standing.cpp:131