28 #include <mc_rtc/gui.h>
48 velCostMat_.setZero();
51 double C = T * T * T / 6;
52 Eigen::Matrix<double, STATE_SIZE, STATE_SIZE> stateMatrix;
62 Eigen::Matrix<double, STATE_SIZE, INPUT_SIZE> inputMatrix;
72 Eigen::VectorXd biasVector = Eigen::VectorXd::Zero(
STATE_SIZE);
73 initState_ = Eigen::VectorXd::Zero(
STATE_SIZE);
74 previewSystem_ = std::make_shared<copra::PreviewSystem>(stateMatrix, inputMatrix, biasVector, initState_,
NB_STEPS);
75 mc_rtc::log::success(
"Initialized new ModelPredictiveControl solver");
80 if(config.has(
"weights"))
82 auto weights = config(
"weights");
92 gui->addElement({
"Walking",
"CoM"},
94 "MPC QP solver", {
"QuadProgDense",
"QLD"},
95 [
this]() -> std::string
99 case copra::SolverFlag::QLD:
101 case copra::SolverFlag::QuadProgDense:
103 return "QuadProgDense";
106 [
this](
const std::string & solver)
110 solver_ = copra::SolverFlag::QLD;
114 solver_ = copra::SolverFlag::QuadProgDense;
118 "MPC QP cost weights", {
"jerk",
"vel_x",
"vel_y",
"zmp"},
121 Eigen::VectorXd weights(4);
128 [
this](
const Eigen::VectorXd & weights)
139 logger.addLogEntry(
"mpc_velRef", [
this]() -> Eigen::Vector2d {
return velRef_.head<2>(); });
140 logger.addLogEntry(
"mpc_weights_jerk", [
this]() {
return jerkWeight; });
141 logger.addLogEntry(
"mpc_weights_vel", [
this]() {
return velWeights; });
142 logger.addLogEntry(
"mpc_weights_zmp", [
this]() {
return zmpWeight; });
143 logger.addLogEntry(
"mpc_zmpRef", [
this]() -> Eigen::Vector2d {
return zmpRef_.head<2>(); });
144 logger.addLogEntry(
"perf_MPCBuildAndSolve", [
this]() {
return buildAndSolveTime_; });
145 logger.addLogEntry(
"perf_MPCSolve", [
this]() {
return solveTime_; });
149 double doubleSupportDuration,
150 double targetSupportDuration)
154 unsigned nbStepsSoFar = 0;
155 nbInitSupportSteps_ = std::min(
static_cast<unsigned>(std::round(initSupportDuration / T)),
NB_STEPS - nbStepsSoFar);
156 nbStepsSoFar += nbInitSupportSteps_;
157 nbDoubleSupportSteps_ =
158 std::min(
static_cast<unsigned>(std::round(doubleSupportDuration / T)),
NB_STEPS - nbStepsSoFar);
159 nbStepsSoFar += nbDoubleSupportSteps_;
160 nbTargetSupportSteps_ =
161 std::min(
static_cast<unsigned>(std::round(targetSupportDuration / T)),
NB_STEPS - nbStepsSoFar);
162 nbStepsSoFar += nbTargetSupportSteps_;
163 if(nbTargetSupportSteps_ > 0)
165 nbNextDoubleSupportSteps_ =
NB_STEPS - nbStepsSoFar;
170 if(i < nbInitSupportSteps_ || (0 < i && i == nbInitSupportSteps_))
174 else if(i - nbInitSupportSteps_ < nbDoubleSupportSteps_)
178 else if(nbTargetSupportSteps_ > 0)
180 if(i - nbInitSupportSteps_ - nbDoubleSupportSteps_ <= nbTargetSupportSteps_)
184 else if(nbNextDoubleSupportSteps_ > 0)
200 void ModelPredictiveControl::computeZMPRef()
203 Eigen::Vector2d p_0 = initContact_.
anklePos(sole_).head<2>();
204 Eigen::Vector2d p_1 = targetContact_.
anklePos(sole_).head<2>();
205 Eigen::Vector2d p_2 = nextContact_.
anklePos(sole_).head<2>();
206 if(nbTargetSupportSteps_ < 1)
208 p_1 = 0.5 * (initContact_.
anklePos(sole_) + targetContact_.
anklePos(sole_)).head<2>();
212 if(indexToHrep_[i] <= 1)
214 long j = i - nbInitSupportSteps_;
215 double x = (nbDoubleSupportSteps_ > 0) ?
static_cast<double>(j) / nbDoubleSupportSteps_ : 0.;
216 x =
clamp(x, 0., 1.);
217 zmpRef_.segment<2>(2 * i) = (1. - x) * p_0 + x * p_1;
221 long j = i - nbInitSupportSteps_ - nbDoubleSupportSteps_ - nbTargetSupportSteps_;
222 double x = (nbNextDoubleSupportSteps_ > 0) ?
static_cast<double>(j) / nbNextDoubleSupportSteps_ : 0;
223 x =
clamp(x, 0., 1.);
224 zmpRef_.segment<2>(2 * i) = (1. - x) * p_1 + x * p_2;
229 void ModelPredictiveControl::updateTerminalConstraint()
233 if(nbTargetSupportSteps_ < 1)
235 unsigned i = nbInitSupportSteps_ + nbDoubleSupportSteps_;
236 E_dcm.block<2, 6>(0, 6 * i) = dcmFromState_;
237 E_zmp.block<2, 6>(0, 6 * i) = zmpFromState_;
241 E_dcm.rightCols<6>() = dcmFromState_;
242 E_zmp.rightCols<6>() = zmpFromState_;
244 Eigen::Vector2d dcmTarget = zmpRef_.tail<2>();
245 Eigen::Vector2d zmpTarget = zmpRef_.tail<2>();
246 termDCMCons_ = std::make_shared<copra::TrajectoryConstraint>(E_dcm, dcmTarget,
false);
247 termZMPCons_ = std::make_shared<copra::TrajectoryConstraint>(E_zmp, zmpTarget,
false);
250 void ModelPredictiveControl::updateZMPConstraint()
252 hreps_[0] = initContact_.
hrep();
253 hreps_[2] = targetContact_.
hrep();
254 unsigned totalRows = 0;
257 unsigned hrepIndex = indexToHrep_[i];
258 if(hrepIndex % 2 == 0)
260 const auto & hrep = hreps_[hrepIndex];
261 totalRows +=
static_cast<unsigned>(hrep.first.rows());
265 Eigen::VectorXd b{totalRows};
270 unsigned hrepIndex = indexToHrep_[i];
271 if(hrepIndex % 2 == 0)
273 const auto & hrep = hreps_[indexToHrep_[i]];
274 unsigned consRows =
static_cast<unsigned>(hrep.first.rows());
276 b.segment(nextRow, consRows) = hrep.second;
280 zmpCons_ = std::make_shared<copra::TrajectoryConstraint>(A, b);
283 void ModelPredictiveControl::updateJerkCost()
285 Eigen::Matrix2d jerkMat = Eigen::Matrix2d::Identity();
286 Eigen::Vector2d jerkVec = Eigen::Vector2d::Zero();
287 jerkCost_ = std::make_shared<copra::ControlCost>(jerkMat, jerkVec);
291 void ModelPredictiveControl::updateVelCost()
294 const Eigen::Matrix3d & R_0 = initContact_.
pose.rotation();
295 const Eigen::Matrix3d & R_1 = targetContact_.
pose.rotation();
296 const Eigen::Matrix3d & R_2 = nextContact_.
pose.rotation();
297 Eigen::Vector2d v_0 = initContact_.
refVel.head<2>();
298 Eigen::Vector2d v_1 = targetContact_.
refVel.head<2>();
299 Eigen::Vector2d v_2 = nextContact_.
refVel.head<2>();
300 if(nbTargetSupportSteps_ < 1)
308 if(indexToHrep_[i] <= 1)
310 double w =
static_cast<double>(i) / (nbInitSupportSteps_ + nbDoubleSupportSteps_);
311 w =
clamp(w, 0., 1.);
312 R =
slerp(R_0, R_1, w).topLeftCorner<2, 2>();
313 v = (1. - w) * v_0 + w * v_1;
317 long i2 = i - nbInitSupportSteps_ - nbDoubleSupportSteps_;
318 double w =
static_cast<double>(i2) / (nbTargetSupportSteps_ + nbNextDoubleSupportSteps_);
319 w =
clamp(w, 0., 1.);
320 R =
slerp(R_1, R_2, w).topLeftCorner<2, 2>();
322 v = (1. - w) * v_1 + w * v_2;
325 velRef_.segment<2>(2 * i) = R * v;
327 velCost_ = std::make_shared<copra::TrajectoryCost>(velCostMat_, velRef_);
331 void ModelPredictiveControl::updateZMPCost()
333 zmpCost_ = std::make_shared<copra::TrajectoryCost>(zmpFromState_, zmpRef_);
335 zmpCost_->autoSpan();
340 using namespace std::chrono;
341 auto startTime = high_resolution_clock::now();
345 previewSystem_->xInit(initState_);
346 updateTerminalConstraint();
347 updateZMPConstraint();
352 copra::LMPC lmpc(previewSystem_, solver_);
353 lmpc.addConstraint(termDCMCons_);
354 lmpc.addConstraint(termZMPCons_);
355 lmpc.addConstraint(zmpCons_);
356 lmpc.addCost(jerkCost_);
357 lmpc.addCost(velCost_);
358 lmpc.addCost(zmpCost_);
360 bool solutionFound = lmpc.solve();
363 solution_.reset(
new Preview(lmpc.trajectory(), lmpc.control()));
367 mc_rtc::log::error(
"Model predictive control problem has no solution");
368 solution_.reset(
new Preview());
371 auto endTime = high_resolution_clock::now();
372 buildAndSolveTime_ = 1000. * duration_cast<duration<double>>(endTime - startTime).count();
373 solveTime_ = 1000. * lmpc.solveTime();
374 return solutionFound;
double clamp(double v, double vMin, double vMax)
Eigen::Matrix3d slerp(const Eigen::Matrix3d &from, const Eigen::Matrix3d &to, double t)
void addLogEntries(mc_rtc::Logger &logger)
void addGUIElements(std::shared_ptr< mc_rtc::gui::StateBuilder > gui)
static constexpr EIGEN_MAKE_ALIGNED_OPERATOR_NEW double SAMPLING_PERIOD
static constexpr unsigned INPUT_SIZE
void configure(const mc_rtc::Configuration &)
void phaseDurations(double initSupportDuration, double doubleSupportDuration, double targetSupportDuration)
Eigen::Vector2d velWeights
static constexpr unsigned NB_STEPS
static constexpr unsigned STATE_SIZE