35 inline double floorn(
double x,
int n)
37 return floor(pow(10, n) * x) / pow(10, n);
42 using namespace mc_rtc::gui;
45 {
"Markers",
"Footsteps"},
46 Trajectory(
"Support_Path", [
this]() ->
const std::vector<Eigen::Vector3d> & {
return supportPathDisplay_; }),
47 XYTheta(
"World target [m, rad]",
48 [
this]() -> Eigen::VectorXd {
49 Eigen::Vector3d targetLocal;
50 targetLocal << targetPose_.pos(), 0.;
51 Eigen::Matrix3d rotLocal = mc_rbdyn::rpyToMat({0., 0., targetPose_.theta});
52 sva::PTransformd targetWorld = sva::PTransformd(rotLocal, targetLocal) * worldReference_;
53 double thetaWorld = mc_rbdyn::rpyFromMat(targetWorld.rotation()).z();
54 Eigen::VectorXd vec(4);
55 vec <<
floorn(targetWorld.translation().x(), 4),
floorn(targetWorld.translation().y(), 4),
56 floorn(thetaWorld, 4), worldReference_.translation().z();
59 [
this](
const Eigen::VectorXd & desired) { updateWorldTarget_(desired.head<3>()); }));
62 {
"Walking",
"Footsteps"}, Label(
"Plan name", [
this]() {
return customPlan_.name; }),
63 ComboInput(
"Gait", {
"Walk",
"Shuffle",
"Turn"}, [
this]() {
return gait(); },
64 [
this](
const std::string & dir) {
gait(dir); }),
65 ComboInput(
"Lead foot", {
"Left",
"Right"}, [
this]() {
return (startWithRightFootstep_) ?
"Right" :
"Left"; },
66 [
this](
const std::string & footName) {
67 startWithRightFootstep_ = (footName ==
"Right");
70 NumberInput(
"Desired step angle [deg]", [
this]() {
return desiredStepAngle_ * 180. / M_PI; },
71 [
this](
double angleDeg) {
75 NumberInput(
"Desired step length [m]", [
this]() {
return desiredStepLength_; },
76 [
this](
double length) {
77 bool isLateral = (customPlan_.name ==
"custom_lateral");
82 NumberInput(
"Extra step width [m]", [
this]() {
return extraStepWidth_; },
83 [
this](
double width) {
87 NumberInput(
"Initial tangent [deg]", [
this]() {
return -initPose_.theta * 180. / M_PI; },
88 [
this](
double angle) {
89 initPose_.theta =
clamp(-angle * M_PI / 180., -2 * M_PI, 2 * M_PI);
92 NumberInput(
"Scale initial tangent", [
this]() {
return supportPath_.extraInitVelScaling(); },
94 supportPath_.extraInitVelScaling(s);
97 NumberInput(
"Scale target tangent", [
this]() {
return supportPath_.extraTargetVelScaling(); },
99 supportPath_.extraTargetVelScaling(s);
102 ArrayInput(
"Walk target from current", {
"x [m]",
"y [m]",
"theta [deg]"},
103 [
this]() {
return targetPose_.vectorDegrees(); },
104 [
this](
const Eigen::Vector3d & desired) {
105 updateLocalTarget_(
SE2d(desired.x(), desired.y(), desired.z() * M_PI / 180.));
108 Label(
"Number of steps", [
this]() {
return nbFootsteps_; }),
109 Label(
"Step angle [deg]", [
this]() {
return std::round(stepAngle_ * 180. / M_PI * 10.) / 10.; }),
110 Label(
"Step length [m]", [
this]() {
return std::round(stepLength_ * 1000.) / 1000.; }),
111 Label(
"Total length [m]", [
this]() {
return std::round(supportPath_.arcLength(0., 1.) * 1000.) / 1000.; }));
116 gui_->removeCategory({
"Walking",
"Footsteps"});
117 gui_->removeElement({
"Markers",
"Footsteps"},
"Support_Path");
118 gui_->removeElement({
"Markers",
"Footsteps"},
"World target [m, rad]");
121 void PlanInterpolator::updateWorldTarget_(
const Eigen::Vector3d & desired)
123 Eigen::Vector3d posWorld;
124 posWorld << desired(0), desired(1), worldReference_.translation().z();
125 Eigen::Matrix3d rotWorld = mc_rbdyn::rpyToMat({0., 0., desired(2)});
126 sva::PTransformd X_desired(rotWorld, posWorld);
128 sva::PTransformd targetLocal = X_desired * worldReference_.inv();
129 double thetaLocal = mc_rbdyn::rpyFromMat(targetLocal.rotation()).z();
130 const Eigen::Vector3d & posLocal = targetLocal.translation();
131 updateLocalTarget_(
SE2d(posLocal.x(), posLocal.y(), thetaLocal));
135 void PlanInterpolator::updateLocalTarget_(
const SE2d & target)
145 double absX = std::abs(targetPose_.x);
146 double absY = std::abs(targetPose_.y);
160 for(
auto name : {
"custom_backward",
"custom_forward",
"custom_lateral"})
162 if(!plans_.has(name))
164 LOG_ERROR(
"[PlanInterpolator] Configuration lacks \"" << name <<
"\" plan, skipping...");
167 customPlan_ = plans_(name);
168 const Contact & leftFoot = customPlan_.contacts()[1];
169 const Contact & rightFoot = customPlan_.contacts()[0];
172 LOG_ERROR(
"Wrong initial foothold order in \"" << name <<
"\" plan");
174 sva::PTransformd X_0_mid = sva::interpolate(leftFoot.
pose, rightFoot.
pose, 0.5);
175 if(!X_0_mid.rotation().isIdentity() || X_0_mid.translation().norm() > 1e-4)
177 LOG_ERROR(
"Invalid X_0_mid(\"" << name <<
"\") = " << X_0_mid);
182 desiredStepLength_ = plans_(
"custom_forward")(
"step_length");
183 customPlan_ = plans_(
"custom_forward");
184 customPlan_.name =
"custom_forward";
190 if(targetPose_.pos().norm() < 2e-3)
205 double dx = targetPose_.x;
206 if(dx < 0. && customPlan_.name !=
"custom_backward")
208 desiredStepLength_ = plans_(
"custom_backward")(
"step_length");
211 else if(dx >= 0. && customPlan_.name !=
"custom_forward")
213 desiredStepLength_ = plans_(
"custom_forward")(
"step_length");
220 if(customPlan_.name !=
"custom_lateral")
222 desiredStepLength_ = plans_(
"custom_lateral")(
"step_length");
229 if(customPlan_.name !=
"custom_lateral")
231 desiredStepLength_ = plans_(
"custom_lateral")(
"step_length");
239 void PlanInterpolator::runWalking_()
241 bool goingBackward = (targetPose_.pos().x() < 0.);
244 customPlan_ = plans_(
"custom_backward");
245 customPlan_.name =
"custom_backward";
246 supportPath_.reset(initPose_.pos(), -initPose_.ori(), targetPose_.pos(), -targetPose_.ori());
247 lastBackwardTarget_ = targetPose_;
251 customPlan_ = plans_(
"custom_forward");
252 customPlan_.name =
"custom_forward";
253 supportPath_.reset(initPose_.pos(), initPose_.ori(), targetPose_.pos(), targetPose_.ori());
254 lastForwardTarget_ = targetPose_;
256 if(!startWithRightFootstep_)
258 customPlan_.swapFirstTwoContacts();
261 double T = customPlan_.doubleSupportDuration() + customPlan_.singleSupportDuration();
262 double totalLength = supportPath_.arcLength(0., 1.);
263 unsigned nbInnerSteps =
static_cast<unsigned>(std::max(0., std::round(totalLength / desiredStepLength_) - 1));
264 double maxStepLength = 1.1 * desiredStepLength_;
265 if(totalLength / (nbInnerSteps + 1) > maxStepLength)
269 double innerStepLength = totalLength / (nbInnerSteps + 1);
270 double innerVel = (goingBackward ? -1. : +1.) * innerStepLength / T;
271 double outerStepLength;
275 outerStepLength = 0.5 * innerStepLength;
276 outerVel = 0.5 * innerVel;
278 stepLength_ = innerStepLength;
282 outerStepLength = totalLength;
285 stepLength_ = totalLength;
289 bool isRightFootstep = startWithRightFootstep_;
290 unsigned nbFinalSteps = 0;
291 double freeLength = outerStepLength;
292 double curVel = outerVel;
293 while(nbFinalSteps < 2)
295 double length = freeLength;
296 double curStepWidth = stepWidth_ + extraStepWidth_;
297 if(length > totalLength - outerStepLength - 1e-3)
301 if(length >= totalLength - 1e-3)
303 curStepWidth = stepWidth_;
305 length = totalLength;
309 double t = supportPath_.arcLengthInverse(0., length);
310 Eigen::Vector2d supportPoint = supportPath_.pos(t);
311 Eigen::Vector2d supportTangent = supportPath_.tangent(t);
314 supportTangent *= -1.;
316 Eigen::Vector2d supportNormal = {-supportTangent.y(), supportTangent.x()};
317 double sign = (isRightFootstep) ? -1. : +1.;
318 double dy = 0.5 * sign * curStepWidth;
319 double theta = atan2(supportTangent.y(), supportTangent.x());
320 Eigen::Vector2d p = supportPoint + dy * supportNormal;
321 SE2d stepPose = {p.
x(), p.y(), theta};
325 contact.
refVel = curVel * Eigen::Vector3d{supportTangent.x(), supportTangent.y(), 0.};
326 contact.
surfaceName = (isRightFootstep) ?
"RightFootCenter" :
"LeftFootCenter";
327 customPlan_.appendContact(contact);
328 isRightFootstep = !isRightFootstep;
330 freeLength += innerStepLength;
336 if(nbFootsteps_ - 3 != nbInnerSteps)
338 LOG_ERROR(
"[PlanInterpolator] Footstep count check failed");
340 if(std::abs(2 * outerStepLength + nbInnerSteps * innerStepLength - totalLength) > 1e-4)
342 LOG_ERROR(
"[PlanInterpolator] Total length check failed");
347 if(nbFootsteps_ != 2)
349 LOG_ERROR(
"[PlanInterpolator] Footstep count check failed");
351 if(std::abs(outerStepLength - totalLength) > 1e-4)
353 LOG_ERROR(
"[PlanInterpolator] Total length check failed");
358 void PlanInterpolator::runShuffling_()
360 customPlan_ = plans_(
"custom_lateral");
361 customPlan_.name =
"custom_lateral";
362 lastLateralTarget_ = targetPose_;
363 supportPath_.reset(initPose_.pos(), initPose_.ori(), targetPose_.pos(), targetPose_.ori());
365 std::string leadFootSurfaceName, followFootSurfaceName;
366 bool goingToTheRight = (targetPose_.pos().y() < 0.);
369 leadFootSurfaceName =
"RightFootCenter";
370 followFootSurfaceName =
"LeftFootCenter";
371 startWithRightFootstep_ =
true;
375 leadFootSurfaceName =
"LeftFootCenter";
376 followFootSurfaceName =
"RightFootCenter";
377 customPlan_.swapFirstTwoContacts();
378 startWithRightFootstep_ =
false;
381 double totalLength = supportPath_.arcLength(0., 1.);
382 double nbSteps = std::max(1., std::round(totalLength / desiredStepLength_));
383 double maxStepLength = 1.1 * desiredStepLength_;
384 if(totalLength / nbSteps > maxStepLength)
388 double stepLength = totalLength / nbSteps;
391 for(
unsigned i = 1; i <= nbSteps; i++)
393 double curStepWidth = stepWidth_;
396 curStepWidth += extraStepWidth_;
398 double t = supportPath_.arcLengthInverse(0., i * stepLength);
399 Eigen::Vector2d supportPoint = supportPath_.pos(t);
400 double theta = t * targetPose_.theta;
401 Eigen::Vector2d lateralVec = {-std::sin(theta), std::cos(theta)};
402 lateralVec *= (goingToTheRight ? -1. : +1.) * 0.5 * curStepWidth;
403 Eigen::Vector2d leadPos = supportPoint + lateralVec;
404 Eigen::Vector2d followPos = supportPoint - lateralVec;
405 SE2d leadStepPose = {leadPos.
x(), leadPos.y(), theta};
406 SE2d followStepPose = {followPos.
x(), followPos.y(), theta};
413 customPlan_.appendContact(leadStep);
414 customPlan_.appendContact(followStep);
419 stepLength_ = stepLength;
422 void PlanInterpolator::runTurning_()
424 customPlan_ = plans_(
"custom_lateral");
425 customPlan_.name =
"custom_lateral";
426 supportPath_.reset(initPose_.pos(), initPose_.ori(), targetPose_.pos(), targetPose_.ori());
428 std::string leadFootSurfaceName, followFootSurfaceName;
429 bool goingToTheRight = (targetPose_.theta < 0.);
432 leadFootSurfaceName =
"RightFootCenter";
433 followFootSurfaceName =
"LeftFootCenter";
434 startWithRightFootstep_ =
true;
438 leadFootSurfaceName =
"LeftFootCenter";
439 followFootSurfaceName =
"RightFootCenter";
440 customPlan_.swapFirstTwoContacts();
441 startWithRightFootstep_ =
false;
444 double totalAngle = std::abs(targetPose_.theta);
445 double nbSteps = std::max(1., std::round(totalAngle / desiredStepAngle_));
446 double maxStepAngle = 1.0 * desiredStepAngle_;
447 if(totalAngle / nbSteps > maxStepAngle)
451 double stepAngle = totalAngle / nbSteps;
454 for(
unsigned i = 1; i <= nbSteps; i++)
456 double curStepWidth = stepWidth_;
459 curStepWidth += extraStepWidth_;
461 double theta = (goingToTheRight ? -1. : +1.) * i * stepAngle;
462 Eigen::Vector2d supportPoint = targetPose_.pos();
463 Eigen::Vector2d lateralVec = {-std::sin(theta), std::cos(theta)};
464 lateralVec *= (goingToTheRight ? -1. : +1.) * 0.5 * curStepWidth;
465 Eigen::Vector2d leadPos = supportPoint + lateralVec;
466 Eigen::Vector2d followPos = supportPoint - lateralVec;
467 SE2d leadStepPose = {leadPos.
x(), leadPos.y(), theta};
468 SE2d followStepPose = {followPos.
x(), followPos.y(), theta};
475 customPlan_.appendContact(leadStep);
476 customPlan_.appendContact(followStep);
480 stepAngle_ = stepAngle;
486 constexpr
double PATH_STEP = 0.05;
487 sva::PTransformd X_0_mid = sva::interpolate(X_0_lf, X_0_rf, 0.5);
488 supportPathDisplay_.clear();
489 for(
double s = 0.; s <= 1.; s += PATH_STEP)
491 Eigen::Vector2d p = supportPath_.pos(s);
492 SE2d pose = {p.
x(), p.y(), 0.};
493 sva::PTransformd X_0_p = pose * X_0_mid;
494 supportPathDisplay_.push_back(X_0_p.translation());
498 void PlanInterpolator::restoreDefaults()
501 initPose_.theta = 0.;
502 startWithRightFootstep_ =
true;
503 supportPath_.extraInitVelScaling(1.);
504 supportPath_.extraTargetVelScaling(1.);
void removeGUIElements()
Remove GUI panel.
void addGUIElements()
Add GUI panel.
void updateSupportPath(const sva::PTransformd &X_0_lf, const sva::PTransformd &X_0_rf)
Update support path display.
std::string gait() const
Get current gait as string.
double clamp(double v, double vMin, double vMax)
Clamp a value in a given interval.
static constexpr double MAX_SAGITTAL_STEP_LENGTH
static EIGEN_MAKE_ALIGNED_OPERATOR_NEW constexpr double DEFAULT_EXTRA_STEP_WIDTH
void configure(const mc_rtc::Configuration &config)
Read configuration from dictionary.
Eigen::Vector2d pos() const
Get 2D position.
static constexpr double IN_PLACE_EXTRA_STEP_WIDTH
sva::PTransformd asPTransform() const
Convert to Plucker transform.
unsigned nbIter
Number of times the interpolator was called.
static constexpr double MAX_EXTRA_STEP_WIDTH
Main controller namespace.
double x
First translation coordinate.
void run()
Generate a new footstep plan.
double theta
Orientation coordinate.
double floorn(double x, int n)
void suggestGait()
Suggest gait based on local target coordinates.
static constexpr double IN_PLACE_MAX_STEP_ANGLE
static constexpr double MIN_STEP_LENGTH
static constexpr double MAX_LATERAL_STEP_LENGTH