46 velCostMat_.setZero();
49 double C = T * T * T / 6;
50 Eigen::Matrix<double, STATE_SIZE, STATE_SIZE> stateMatrix;
60 Eigen::Matrix<double, STATE_SIZE, INPUT_SIZE> inputMatrix;
70 Eigen::VectorXd biasVector = Eigen::VectorXd::Zero(
STATE_SIZE);
71 initState_ = Eigen::VectorXd::Zero(
STATE_SIZE);
72 previewSystem_ = std::make_shared<copra::PreviewSystem>(stateMatrix, inputMatrix, biasVector, initState_,
NB_STEPS);
73 LOG_SUCCESS(
"Initialized new ModelPredictiveControl solver");
78 if(config.has(
"weights"))
80 auto weights = config(
"weights");
89 using namespace mc_rtc::gui;
90 gui->addElement({
"Walking",
"CoM"},
91 ComboInput(
"MPC QP solver", {
"QuadProgDense",
"QLD"},
95 case copra::SolverFlag::QLD:
97 case copra::SolverFlag::QuadProgDense:
99 return "QuadProgDense";
102 [
this](
const std::string & solver) {
105 solver_ = copra::SolverFlag::QLD;
109 solver_ = copra::SolverFlag::QuadProgDense;
112 ArrayInput(
"MPC QP cost weights", {
"jerk",
"vel_x",
"vel_y",
"zmp"},
114 Eigen::VectorXd weights(4);
121 [
this](
const Eigen::VectorXd & weights) {
131 logger.addLogEntry(
"mpc_velRef", [
this]() -> Eigen::Vector2d {
return velRef_.head<2>(); });
132 logger.addLogEntry(
"mpc_weights_jerk", [
this]() {
return jerkWeight; });
133 logger.addLogEntry(
"mpc_weights_vel", [
this]() {
return velWeights; });
134 logger.addLogEntry(
"mpc_weights_zmp", [
this]() {
return zmpWeight; });
135 logger.addLogEntry(
"mpc_zmpRef", [
this]() -> Eigen::Vector2d {
return zmpRef_.head<2>(); });
136 logger.addLogEntry(
"perf_MPCBuildAndSolve", [
this]() {
return buildAndSolveTime_; });
137 logger.addLogEntry(
"perf_MPCSolve", [
this]() {
return solveTime_; });
141 double doubleSupportDuration,
142 double targetSupportDuration)
146 unsigned nbStepsSoFar = 0;
147 nbInitSupportSteps_ = std::min(static_cast<unsigned>(std::round(initSupportDuration / T)),
NB_STEPS - nbStepsSoFar);
148 nbStepsSoFar += nbInitSupportSteps_;
149 nbDoubleSupportSteps_ =
150 std::min(static_cast<unsigned>(std::round(doubleSupportDuration / T)),
NB_STEPS - nbStepsSoFar);
151 nbStepsSoFar += nbDoubleSupportSteps_;
152 nbTargetSupportSteps_ =
153 std::min(static_cast<unsigned>(std::round(targetSupportDuration / T)),
NB_STEPS - nbStepsSoFar);
154 nbStepsSoFar += nbTargetSupportSteps_;
155 if(nbTargetSupportSteps_ > 0)
157 nbNextDoubleSupportSteps_ =
NB_STEPS - nbStepsSoFar;
162 if(i < nbInitSupportSteps_ || (0 < i && i == nbInitSupportSteps_))
166 else if(i - nbInitSupportSteps_ < nbDoubleSupportSteps_)
170 else if(nbTargetSupportSteps_ > 0)
172 if(i - nbInitSupportSteps_ - nbDoubleSupportSteps_ <= nbTargetSupportSteps_)
176 else if(nbNextDoubleSupportSteps_ > 0)
192 void ModelPredictiveControl::computeZMPRef()
195 Eigen::Vector2d p_0 = initContact_.anklePos(sole_).head<2>();
196 Eigen::Vector2d p_1 = targetContact_.anklePos(sole_).head<2>();
197 Eigen::Vector2d p_2 = nextContact_.anklePos(sole_).head<2>();
198 if(nbTargetSupportSteps_ < 1)
200 p_1 = 0.5 * (initContact_.anklePos(sole_) + targetContact_.anklePos(sole_)).head<2>();
204 if(indexToHrep_[i] <= 1)
206 long j = i - nbInitSupportSteps_;
207 double x = (nbDoubleSupportSteps_ > 0) ? static_cast<double>(j) / nbDoubleSupportSteps_ : 0.;
208 x =
clamp(x, 0., 1.);
209 zmpRef_.segment<2>(2 * i) = (1. - x) * p_0 + x * p_1;
213 long j = i - nbInitSupportSteps_ - nbDoubleSupportSteps_ - nbTargetSupportSteps_;
214 double x = (nbNextDoubleSupportSteps_ > 0) ? static_cast<double>(j) / nbNextDoubleSupportSteps_ : 0;
215 x =
clamp(x, 0., 1.);
216 zmpRef_.segment<2>(2 * i) = (1. - x) * p_1 + x * p_2;
221 void ModelPredictiveControl::updateTerminalConstraint()
225 if(nbTargetSupportSteps_ < 1)
227 unsigned i = nbInitSupportSteps_ + nbDoubleSupportSteps_;
228 E_dcm.block<2, 6>(0, 6 * i) = dcmFromState_;
229 E_zmp.block<2, 6>(0, 6 * i) = zmpFromState_;
233 E_dcm.rightCols<6>() = dcmFromState_;
234 E_zmp.rightCols<6>() = zmpFromState_;
236 Eigen::Vector2d dcmTarget = zmpRef_.tail<2>();
237 Eigen::Vector2d zmpTarget = zmpRef_.tail<2>();
238 termDCMCons_ = std::make_shared<copra::TrajectoryConstraint>(E_dcm, dcmTarget,
false);
239 termZMPCons_ = std::make_shared<copra::TrajectoryConstraint>(E_zmp, zmpTarget,
false);
242 void ModelPredictiveControl::updateZMPConstraint()
244 hreps_[0] = initContact_.hrep();
245 hreps_[2] = targetContact_.hrep();
246 unsigned totalRows = 0;
249 unsigned hrepIndex = indexToHrep_[i];
250 if(hrepIndex % 2 == 0)
252 const auto & hrep = hreps_[hrepIndex];
253 totalRows +=
static_cast<unsigned>(hrep.first.rows());
257 Eigen::VectorXd b{totalRows};
262 unsigned hrepIndex = indexToHrep_[i];
263 if(hrepIndex % 2 == 0)
265 const auto & hrep = hreps_[indexToHrep_[i]];
266 unsigned consRows =
static_cast<unsigned>(hrep.first.rows());
268 b.segment(nextRow, consRows) = hrep.second;
272 zmpCons_ = std::make_shared<copra::TrajectoryConstraint>(A, b);
275 void ModelPredictiveControl::updateJerkCost()
277 Eigen::Matrix2d jerkMat = Eigen::Matrix2d::Identity();
278 Eigen::Vector2d jerkVec = Eigen::Vector2d::Zero();
279 jerkCost_ = std::make_shared<copra::ControlCost>(jerkMat, jerkVec);
283 void ModelPredictiveControl::updateVelCost()
286 const Eigen::Matrix3d & R_0 = initContact_.pose.rotation();
287 const Eigen::Matrix3d & R_1 = targetContact_.pose.rotation();
288 const Eigen::Matrix3d & R_2 = nextContact_.pose.rotation();
289 Eigen::Vector2d v_0 = initContact_.refVel.head<2>();
290 Eigen::Vector2d v_1 = targetContact_.refVel.head<2>();
291 Eigen::Vector2d v_2 = nextContact_.refVel.head<2>();
292 if(nbTargetSupportSteps_ < 1)
300 if(indexToHrep_[i] <= 1)
302 double w =
static_cast<double>(i) / (nbInitSupportSteps_ + nbDoubleSupportSteps_);
303 w =
clamp(w, 0., 1.);
304 R =
slerp(R_0, R_1, w).topLeftCorner<2, 2>();
305 v = (1. - w) * v_0 + w * v_1;
309 long i2 = i - nbInitSupportSteps_ - nbDoubleSupportSteps_;
310 double w =
static_cast<double>(i2) / (nbTargetSupportSteps_ + nbNextDoubleSupportSteps_);
311 w =
clamp(w, 0., 1.);
312 R =
slerp(R_1, R_2, w).topLeftCorner<2, 2>();
313 v = (1. - w) * v_1 + w * v_2;
316 velRef_.segment<2>(2 * i) = R * v;
318 velCost_ = std::make_shared<copra::TrajectoryCost>(velCostMat_, velRef_);
322 void ModelPredictiveControl::updateZMPCost()
324 zmpCost_ = std::make_shared<copra::TrajectoryCost>(zmpFromState_, zmpRef_);
326 zmpCost_->autoSpan();
331 using namespace std::chrono;
332 auto startTime = high_resolution_clock::now();
336 previewSystem_->xInit(initState_);
337 updateTerminalConstraint();
338 updateZMPConstraint();
343 copra::LMPC lmpc(previewSystem_, solver_);
344 lmpc.addConstraint(termDCMCons_);
345 lmpc.addConstraint(termZMPCons_);
346 lmpc.addConstraint(zmpCons_);
347 lmpc.addCost(jerkCost_);
348 lmpc.addCost(velCost_);
349 lmpc.addCost(zmpCost_);
351 bool solutionFound = lmpc.solve();
354 solution_.reset(
new Preview(lmpc.trajectory(), lmpc.control()));
358 LOG_ERROR(
"Model predictive control problem has no solution");
359 solution_.reset(
new Preview());
362 auto endTime = high_resolution_clock::now();
363 buildAndSolveTime_ = 1000. * duration_cast<duration<double>>(endTime - startTime).count();
364 solveTime_ = 1000. * lmpc.solveTime();
365 return solutionFound;
static constexpr unsigned INPUT_SIZE
Input is the 2D horizontal CoM jerk.
double clamp(double v, double vMin, double vMax)
Clamp a value in a given interval.
Eigen::Vector2d velWeights
Weights of CoM velocity tracking cost.
static constexpr unsigned NB_STEPS
Number of sampling steps.
double zmpWeight
Weight of reference ZMP tracking cost.
static EIGEN_MAKE_ALIGNED_OPERATOR_NEW constexpr double SAMPLING_PERIOD
Duration of each sampling step in [s].
ModelPredictiveControl()
Initialize new problem.
static constexpr unsigned STATE_SIZE
State is the 6D stacked vector of CoM positions, velocities and accelerations.
double jerkWeight
Weight of CoM jerk regularization cost.
void addLogEntries(mc_rtc::Logger &logger)
Log stabilizer entries.
void configure(const mc_rtc::Configuration &)
Read configuration from dictionary.
bool buildAndSolve()
Build and solve the model predictive control quadratic program.
Main controller namespace.
Solution of a model predictive control problem.
Eigen::Matrix3d slerp(const Eigen::Matrix3d &from, const Eigen::Matrix3d &to, double t)
Spherical linear interpolation between two rotation matrices.
void phaseDurations(double initSupportDuration, double doubleSupportDuration, double targetSupportDuration)
Set duration of the initial single-support phase.
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
Add GUI panel.