simple_casadi_mpc
GitHub
Loading...
Searching...
No Matches
simple_casadi_mpc.hpp
1#pragma once
2#include "casadi_utils.hpp"
3#include <Eigen/Dense>
4#include <casadi/casadi.hpp>
5#include <filesystem>
6#include <map>
7#include <memory>
8#include <numeric>
9#include <vector>
10
13
22template <class T, class DT>
23static T integrate_dynamics_forward_euler(DT dt, T x, T u, std::function<T(T, T)> dynamics) {
24 return x + dt * dynamics(x, u);
25}
26
29template <class T, class DT>
30static T integrate_dynamics_modified_euler(DT dt, T x, T u, std::function<T(T, T)> dynamics) {
31 T k1 = dynamics(x, u);
32 T k2 = dynamics(x + dt * k1, u);
33
34 return x + dt * (k1 + k2) / 2;
35}
36
39template <class T, class DT>
40static T integrate_dynamics_rk4(DT dt, T x, T u, std::function<T(T, T)> dynamics) {
41 T k1 = dynamics(x, u);
42 T k2 = dynamics(x + dt / 2 * k1, u);
43 T k3 = dynamics(x + dt / 2 * k2, u);
44 T k4 = dynamics(x + dt * k3, u);
45 return x + dt / 6 * (k1 + 2 * k2 + 2 * k3 + k4);
46}
47
57class Problem {
58public:
66
68 enum class ConstraintType {
69 Equality,
71 };
72
79 Problem(DynamicsType dyn_type, size_t _nx, size_t _nu, size_t _horizon, double _dt)
80 : Problem(dyn_type, _nx, _nu, _horizon, std::vector<double>(_horizon, _dt)) {}
81
92 Problem(DynamicsType dyn_type, size_t _nx, size_t _nu, size_t _horizon, std::vector<double> _dts)
93 : dyn_type_(dyn_type), nx_(_nx), nu_(_nu), horizon_(_horizon), dts_(std::move(_dts)) {
94 assert(dts_.size() == horizon_ && "dts.size() must equal horizon");
95 double inf = std::numeric_limits<double>::infinity();
96
97 Eigen::VectorXd uub = Eigen::VectorXd::Constant(nu(), inf);
98 Eigen::VectorXd ulb = -uub;
99 u_bounds_ = std::vector<LUbound>{horizon(), {ulb, uub}};
100
101 Eigen::VectorXd xub = Eigen::VectorXd::Constant(nx(), inf);
102 Eigen::VectorXd xlb = -xub;
103 x_bounds_ = std::vector<LUbound>{horizon(), {xlb, xub}};
104 }
105
116 virtual casadi::MX dynamics(casadi::MX x, casadi::MX u) = 0;
117
119 Eigen::VectorXd dynamics_eval(Eigen::VectorXd x, Eigen::VectorXd u) {
120 casadi::DM x_dm = casadi::DM::zeros(nx(), 1);
121 casadi::DM u_dm = casadi::DM::zeros(nu(), 1);
122 for (size_t i = 0; i < nx(); i++) {
123 x_dm(i) = x[i];
124 }
125 for (size_t i = 0; i < nu(); i++) {
126 u_dm(i) = u[i];
127 }
128 casadi::MX dx_mx = dynamics(x_dm, u_dm);
129 casadi::DM dx_dm = casadi::MX::evalf(dx_mx);
130 Eigen::VectorXd dx = casadi_utils::to_eigen(dx_dm);
131 return dx;
132 }
133
136 Eigen::VectorXd simulate(Eigen::VectorXd x0, Eigen::MatrixXd u) {
137 assert(dyn_type_ == DynamicsType::Discretized);
138 return dynamics_eval(x0, u);
139 }
140
146 Eigen::VectorXd simulate(Eigen::VectorXd x0, Eigen::MatrixXd u, double dt) {
147 assert(dyn_type_ != DynamicsType::Discretized);
148 auto dyn =
149 std::bind(&Problem::dynamics_eval, this, std::placeholders::_1, std::placeholders::_2);
150 switch (dyn_type_) {
152 return integrate_dynamics_forward_euler<Eigen::VectorXd>(dt, x0, u, dyn);
153 break;
155 return integrate_dynamics_modified_euler<Eigen::VectorXd>(dt, x0, u, dyn);
156 break;
158 return integrate_dynamics_rk4<Eigen::VectorXd>(dt, x0, u, dyn);
159 break;
161 break;
162 }
163 return x0;
164 }
165
172 void set_input_bound(Eigen::VectorXd lb, Eigen::VectorXd ub, int start = -1, int end = -1) {
173 std::tie(start, end) = index_range(start, end);
174 for (int i = start; i < end; i++) {
175 u_bounds_[i] = {lb, ub};
176 }
177 }
178
180 void set_input_lower_bound(Eigen::VectorXd lb, int start = -1, int end = -1) {
181 std::tie(start, end) = index_range(start, end);
182 for (int i = start; i < end; i++) {
183 u_bounds_[i].first = lb;
184 }
185 }
186
188 void set_input_upper_bound(Eigen::VectorXd ub, int start = -1, int end = -1) {
189 std::tie(start, end) = index_range(start, end);
190 for (int i = start; i < end; i++) {
191 u_bounds_[i].second = ub;
192 }
193 }
194
197 void set_state_bound(Eigen::VectorXd lb, Eigen::VectorXd ub, int start = -1, int end = -1) {
198 std::tie(start, end) = index_range(start, end);
199 for (int i = start; i < end; i++) {
200 x_bounds_[i] = {lb, ub};
201 }
202 }
203
205 void set_state_lower_bound(Eigen::VectorXd lb, int start = -1, int end = -1) {
206 std::tie(start, end) = index_range(start, end);
207 for (int i = start; i < end; i++) {
208 x_bounds_[i].first = lb;
209 }
210 }
211
213 void set_state_upper_bound(Eigen::VectorXd ub, int start = -1, int end = -1) {
214 std::tie(start, end) = index_range(start, end);
215 for (int i = start; i < end; i++) {
216 x_bounds_[i].second = ub;
217 }
218 }
219
225 std::function<casadi::MX(casadi::MX, casadi::MX)> constrinat) {
226 add_constraint_at(type, constrinat, /*start=*/-1, /*end=*/-1);
227 }
228
241 std::function<casadi::MX(casadi::MX, casadi::MX)> constraint, int start,
242 int end = -1) {
243 auto mask = stage_mask(start, end);
244 if (type == ConstraintType::Equality) {
245 equality_constraints_.push_back({constraint, std::move(mask)});
246 } else {
247 inequality_constraints_.push_back({constraint, std::move(mask)});
248 }
249 }
250
271 std::function<casadi::MX(casadi::MX, casadi::MX)> constraint,
272 double w1 = 1e3, double w2 = 0.0) {
273 add_soft_constraint_at(type, constraint, w1, w2, /*start=*/-1, /*end=*/-1);
274 }
275
282 std::function<casadi::MX(casadi::MX, casadi::MX)> constraint,
283 double w1, double w2, int start, int end = -1) {
284 soft_constraints_.push_back({type, constraint, w1, w2, stage_mask(start, end)});
285 }
286
291 virtual casadi::MX stage_cost(casadi::MX x, casadi::MX u, size_t k) {
292 (void)x;
293 (void)u;
294 (void)k;
295 return 0;
296 }
297
299 virtual casadi::MX terminal_cost(casadi::MX x) {
300 (void)x;
301 return 0;
302 }
303
304 DynamicsType dynamics_type() const { return dyn_type_; }
305 size_t nx() const { return nx_; }
306 size_t nu() const { return nu_; }
307 size_t horizon() const { return horizon_; }
308
311 double dt() const { return dts_.empty() ? 0.0 : dts_.front(); }
312
314 double dt(size_t k) const { return dts_.at(k); }
315
317 const std::vector<double> &dts() const { return dts_; }
318
320 bool has_uniform_dt() const {
321 if (dts_.size() <= 1)
322 return true;
323 for (size_t i = 1; i < dts_.size(); ++i) {
324 if (dts_[i] != dts_[0])
325 return false;
326 }
327 return true;
328 }
329
339 casadi::MX parameter(std::string name, size_t rows, size_t cols) {
340 auto param = casadi::MX::sym(name, rows, cols);
341 param_list_[name] = {param, casadi::DM::zeros(rows, cols)};
342 return param;
343 }
344
349 casadi::MX reference_trajectory(std::string name = "x_ref") {
350 return parameter(name, nx_, horizon_);
351 }
352
353private:
354 std::pair<int, int> index_range(int start, int end) {
355 if (start == -1 && end == -1) {
356 return {0, horizon_};
357 }
358 if (start != -1 && end == -1) {
359 return {start, start + 1};
360 }
361 return {start, end};
362 }
363
364 std::vector<bool> stage_mask(int start, int end) {
365 auto [s, e] = index_range(start, end);
366 std::vector<bool> mask(horizon_, false);
367 for (int k = s; k < e; ++k)
368 mask[k] = true;
369 return mask;
370 }
371
372 const DynamicsType dyn_type_;
373 const size_t nx_;
374 const size_t nu_;
375 const size_t horizon_;
376 const std::vector<double> dts_;
377
378 using ConstraintFunc = std::function<casadi::MX(casadi::MX, casadi::MX)>;
379
380 struct PathConstraint {
381 ConstraintFunc func;
382 std::vector<bool> stage_mask; // size == horizon; true at stages where the constraint is active.
383 };
384 std::vector<PathConstraint> equality_constraints_;
385 std::vector<PathConstraint> inequality_constraints_;
386
387 struct SoftConstraint {
388 ConstraintType type;
389 ConstraintFunc func;
390 double w1;
391 double w2;
392 std::vector<bool> stage_mask;
393 };
394 std::vector<SoftConstraint> soft_constraints_;
395
396 using LUbound = std::pair<Eigen::VectorXd, Eigen::VectorXd>;
397 std::vector<LUbound> u_bounds_;
398 std::vector<LUbound> x_bounds_;
399
400 struct MXDMPair {
401 casadi::MX mx;
402 casadi::DM dm;
403 };
404 std::map<std::string, MXDMPair> param_list_;
405
406 friend class MPC;
407 friend class JITMPC;
408 friend class CompiledMPC;
409};
410
417class MPC {
418public:
420 static casadi::Dict default_ipopt_config() {
421 casadi::Dict config = {{"calc_lam_p", true}, {"calc_lam_x", true},
422 {"ipopt.sb", "yes"}, {"ipopt.print_level", 0},
423 {"print_time", false}, {"ipopt.warm_start_init_point", "yes"},
424 {"expand", true}};
425 return config;
426 }
427
429 static casadi::Dict default_qpoases_config() {
430 casadi::Dict config = {
431 {"calc_lam_p", true},
432 {"calc_lam_x", true},
433 {"max_iter", 100},
434 {"print_header", false},
435 {"print_iteration", false},
436 {"print_status", false},
437 {"print_time", false},
438 {"qpsol", "qpoases"},
439 {"qpsol_options", casadi::Dict{{"enableRegularisation", true}, {"printLevel", "none"}}},
440 {"expand", true}};
441 return config;
442 }
443
445 static casadi::Dict default_fatrop_config() {
446 casadi::Dict config = {
447 {"calc_lam_p", true}, {"calc_lam_x", true},
448 {"expand", true}, {"print_time", false},
449 {"fatrop.print_level", 0}, {"fatrop.max_iter", 500},
450 {"fatrop.mu_init", 0.1}, {"structure_detection", "auto"},
451 {"fatrop.tol", 1e-6}, {"fatrop.tol_acceptable", 5e-3},
452 // {"debug", true},
453 };
454 return config;
455 }
456
461 static bool equality_required(const std::string &solver_name, const casadi::Dict &config) {
462 if (solver_name == "fatrop") {
463 auto it = config.find("structure_detection");
464 if (it != config.end() && it->second == "auto") {
465 return true;
466 }
467 }
468 return false;
469 }
470
480 template <class T>
481 MPC(std::shared_ptr<T> prob, std::string solver_name = "ipopt",
482 casadi::Dict config = default_ipopt_config())
483 : prob_(prob), solver_name_(solver_name), config_(config) {
484 using namespace casadi;
485 static_assert(std::is_base_of_v<Problem, T>, "prob must be based SimpleProb");
486
487 const size_t nx = prob_->nx();
488 const size_t nu = prob_->nu();
489 const size_t N = prob_->horizon();
490
491 // mapsum_stage_cost: build the stage-cost sum as a MapSum so first-order AD
492 // stays loop-shaped (smaller derivative graph).
493 // expand_inner_functions: SX-expand per-stage F/L/G before .map(N) so the
494 // inner per-stage AD operates on flat SX.
495 bool mapsum_stage_cost = true;
496 bool expand_inner_functions = true;
497 {
498 auto it = config_.find("mapsum_stage_cost");
499 if (it != config_.end()) {
500 mapsum_stage_cost = static_cast<bool>(it->second);
501 config_.erase(it);
502 }
503 it = config_.find("expand_inner_functions");
504 if (it != config_.end()) {
505 expand_inner_functions = static_cast<bool>(it->second);
506 config_.erase(it);
507 }
508 }
509
510 build_with_map(nx, nu, N, mapsum_stage_cost, expand_inner_functions);
511
512 if (expand_inner_functions) {
513 config_["expand"] = false;
514 }
515
516 if (equality_required(solver_name_, config_)) {
517 // Convert std::vector<bool> to std::vector<casadi_int> for CasADi
518 std::vector<casadi_int> equality_int(equality_.begin(), equality_.end());
519 config_["equality"] = equality_int;
520 }
521
522 build_solver();
523 }
524
535 virtual Eigen::VectorXd solve(Eigen::VectorXd x0,
536 casadi::DMDict new_param_list = casadi::DMDict()) {
537 using namespace casadi;
538
539 // Set new parameter
540 for (auto &[param_name, param] : new_param_list) {
541 prob_->param_list_[param_name].dm = param;
542 }
543
544 const size_t nx = prob_->nx();
545 const size_t nu = prob_->nu();
546
547 for (size_t l = 0; l < nx; l++) {
548 lbw_(l) = x0[l];
549 ubw_(l) = x0[l];
550 }
551
552 DMDict arg;
553 arg["x0"] = w0_;
554 arg["lbx"] = lbw_;
555 arg["ubx"] = ubw_;
556 arg["lbg"] = lbg_;
557 arg["ubg"] = ubg_;
558 arg["lam_x0"] = lam_x0_;
559 arg["lam_g0"] = lam_g0_;
560 param_vec_.clear();
561 param_vec_.reserve(prob_->param_list_.size());
562 for (auto &[param_name, param_pair] : prob_->param_list_) {
563 param_vec_.push_back(param_pair.dm);
564 }
565 arg["p"] = vertcat(param_vec_);
566 DMDict sol = solver_(arg);
567
568 w0_ = sol["x"];
569 lam_x0_ = sol["lam_x"];
570 lam_g0_ = sol["lam_g"];
571
572 Eigen::VectorXd opt_u(nu);
573 std::copy(w0_.ptr() + nx, w0_.ptr() + nx + nu, opt_u.data());
574
575 return opt_u;
576 }
577
579 casadi::MXDict casadi_prob() const { return casadi_prob_; }
581 const std::string &solver_name() const { return solver_name_; }
583 casadi::Dict solver_config() const { return config_; }
585 std::vector<casadi_int> equality_flags() const {
586 return std::vector<casadi_int>(equality_.begin(), equality_.end());
587 }
588
589protected:
590 std::shared_ptr<Problem> prob_;
591 std::string solver_name_;
592 casadi::Dict config_;
593 casadi::MXDict casadi_prob_;
594 casadi::Function solver_;
595 std::vector<casadi::MX> Xs = {};
596 std::vector<casadi::MX> Us = {};
597
598 casadi::DM lbw_;
599 casadi::DM ubw_;
600 casadi::DM lbg_;
601 casadi::DM ubg_;
602 std::vector<casadi::DM> param_vec_ = {};
603
604 std::vector<bool> equality_ = {}; // ダイナミクスと追加の制約が等式か不等式か
605
606 casadi::DM w0_;
607 casadi::DM lam_x0_;
608 casadi::DM lam_g0_;
609
610 // Build NLP with map (for expand=false, faster JIT compilation)
611 void build_with_map(size_t nx, size_t nu, casadi_int N, bool mapsum_stage_cost = true,
612 bool expand_inner_functions = true) {
613 using namespace casadi;
614 double inf = std::numeric_limits<double>::infinity(); // Make sure inf is defined
615
616 // 1. Symbolic variables - create individual variables for each stage
617 Xs.reserve(N + 1);
618 Us.reserve(N);
619 for (casadi_int i = 0; i < N; i++) {
620 Xs.push_back(MX::sym("X_" + std::to_string(i), nx, 1));
621 Us.push_back(MX::sym("U_" + std::to_string(i), nu, 1));
622 }
623 Xs.push_back(MX::sym("X_" + std::to_string(N), nx, 1));
624
625 // Create matrices for map operations
626 MX X = horzcat(Xs);
627 MX U = horzcat(Us);
628
629 MX x_k = MX::sym("x_k", nx);
630 MX u_k = MX::sym("u_k", nu);
631
632 // Collect all parameters
633 std::vector<MX> params_mx;
634 for (auto &[param_name, param_pair] : prob_->param_list_)
635 params_mx.push_back(param_pair.mx);
636
637 // 2. CasADi Functions for one step (unchanged)
638 // std::function<MX(MX, MX)> dynamics_func;
639 // ... same as your code ...
640 // For variable per-stage dt, dt_k_sym is plumbed through F as an extra
641 // input and bound to a (1, N) row of dts when calling F.map(N).
642 const bool variable_dt =
643 !prob_->has_uniform_dt() && prob_->dynamics_type() != Problem::DynamicsType::Discretized;
644 MX dt_k_sym = MX::sym("dt_k", 1, 1);
645 MX x_next;
646 switch (prob_->dynamics_type()) {
648 std::function<casadi::MX(casadi::MX, casadi::MX)> con_dyn =
649 std::bind(&Problem::dynamics, prob_.get(), std::placeholders::_1, std::placeholders::_2);
650 x_next = variable_dt
651 ? integrate_dynamics_forward_euler<casadi::MX>(dt_k_sym, x_k, u_k, con_dyn)
652 : integrate_dynamics_forward_euler<casadi::MX>(prob_->dt(), x_k, u_k, con_dyn);
653 break;
654 }
656 std::function<casadi::MX(casadi::MX, casadi::MX)> con_dyn =
657 std::bind(&Problem::dynamics, prob_.get(), std::placeholders::_1, std::placeholders::_2);
658 x_next = variable_dt
659 ? integrate_dynamics_modified_euler<casadi::MX>(dt_k_sym, x_k, u_k, con_dyn)
660 : integrate_dynamics_modified_euler<casadi::MX>(prob_->dt(), x_k, u_k, con_dyn);
661 break;
662 }
664 std::function<casadi::MX(casadi::MX, casadi::MX)> con_dyn =
665 std::bind(&Problem::dynamics, prob_.get(), std::placeholders::_1, std::placeholders::_2);
666 x_next = variable_dt ? integrate_dynamics_rk4<casadi::MX>(dt_k_sym, x_k, u_k, con_dyn)
667 : integrate_dynamics_rk4<casadi::MX>(prob_->dt(), x_k, u_k, con_dyn);
668 break;
669 }
671 x_next = prob_->dynamics(x_k, u_k);
672 break;
673 }
674 std::vector<MX> F_inputs = {x_k, u_k};
675 if (variable_dt)
676 F_inputs.push_back(dt_k_sym);
677 Function F("F_dynamics", F_inputs, {x_next});
678 if (expand_inner_functions)
679 F = F.expand(F.name(), {{"cse", true}});
680
681 std::vector<MX> L_inputs = {x_k, u_k};
682 L_inputs.insert(L_inputs.end(), params_mx.begin(), params_mx.end());
683 MX stage_cost = prob_->stage_cost(x_k, u_k, 0);
684 Function L("L_stage_cost", L_inputs, {stage_cost});
685 if (expand_inner_functions)
686 L = L.expand(L.name(), {{"cse", true}});
687
688 // 制約一覧
689 std::vector<MX> g_k_vec;
690 std::vector<casadi_int> equality_sizes; // size of each equality constraint (per stage)
691 std::vector<casadi_int> inequality_sizes;
692 equality_sizes.reserve(prob_->equality_constraints_.size());
693 inequality_sizes.reserve(prob_->inequality_constraints_.size());
694 for (auto &con : prob_->equality_constraints_) {
695 MX g_part = con.func(x_k, u_k);
696 equality_sizes.push_back(g_part.size1());
697 g_k_vec.push_back(g_part);
698 }
699 for (auto &con : prob_->inequality_constraints_) {
700 MX g_part = con.func(x_k, u_k);
701 inequality_sizes.push_back(g_part.size1());
702 g_k_vec.push_back(g_part);
703 }
704
705 // ソフト制約用のスラック変数を per-stage に確保し、対応する不等式を g_k に追加する。
706 // - 不等式 g(x,u) <= 0 -> g - s <= 0
707 // - 等式 h(x,u) = 0 -> h - s <= 0 かつ -h - s <= 0 (|h| <= s)
708 // s >= 0 は w 側の bound で強制し、ペナルティ w1*1^T s + 0.5*w2*s^T s をコストに加算する。
709 std::vector<casadi_int> soft_sizes;
710 soft_sizes.reserve(prob_->soft_constraints_.size());
711 for (auto &sc : prob_->soft_constraints_) {
712 soft_sizes.push_back(sc.func(x_k, u_k).size1());
713 }
714 const casadi_int n_s_total =
715 std::accumulate(soft_sizes.begin(), soft_sizes.end(), static_cast<casadi_int>(0));
716
717 std::vector<MX> Ss;
718 MX s_k;
719 if (n_s_total > 0) {
720 s_k = MX::sym("s_k", n_s_total, 1);
721 Ss.reserve(N);
722 for (casadi_int i = 0; i < N; ++i) {
723 Ss.push_back(MX::sym("S_" + std::to_string(i), n_s_total, 1));
724 }
725
726 casadi_int s_offset = 0;
727 for (size_t i = 0; i < prob_->soft_constraints_.size(); ++i) {
728 auto &sc = prob_->soft_constraints_[i];
729 const casadi_int m = soft_sizes[i];
730 MX s_part = s_k(Slice(s_offset, s_offset + m));
731 MX c_val = sc.func(x_k, u_k);
733 g_k_vec.push_back(c_val - s_part);
734 } else {
735 g_k_vec.push_back(c_val - s_part);
736 g_k_vec.push_back(-c_val - s_part);
737 }
738 s_offset += m;
739 }
740 }
741
742 MX g_k = vertcat(g_k_vec);
743
744 std::vector<MX> G_inputs = {x_k, u_k};
745 if (n_s_total > 0) {
746 G_inputs.push_back(s_k);
747 }
748 G_inputs.insert(G_inputs.end(), params_mx.begin(), params_mx.end());
749 Function G_constraints("G_constraints", G_inputs, {g_k});
750 if (expand_inner_functions)
751 G_constraints = G_constraints.expand(G_constraints.name(), {{"cse", true}});
752
753 // 3. Map application
754 std::vector<MX> F_map_inputs = {X(Slice(), Slice(0, N)), U};
755 if (variable_dt) {
756 // (1, N) row of per-stage dts, fed as the third input to F across stages.
757 F_map_inputs.push_back(DM(prob_->dts()).T());
758 }
759 MX X_next_cal = F.map(N)(F_map_inputs)[0];
760
761 // Stage cost: when mapsum_stage_cost is set, replace each per-stage param
762 // p (shape rows×N) by repmat(col_sym, 1, N) inside the user's stage_cost
763 // so p(:, k) collapses to a single column symbol; build the cost via
764 // MapSum. Verify k-independence by comparing the substituted expression at
765 // every k against k=0; on mismatch (k-dependent branching beyond param
766 // slicing), fall back to the per-stage loop.
767 bool mapsum_safe = mapsum_stage_cost;
768 std::vector<MX> col_syms;
769 std::vector<size_t> per_stage_idx;
770 MX cost_substituted;
771
772 if (mapsum_safe) {
773 for (size_t p = 0; p < params_mx.size(); ++p) {
774 if (params_mx[p].size2() == N) {
775 per_stage_idx.push_back(p);
776 col_syms.push_back(MX::sym(params_mx[p].name() + "_col", params_mx[p].size1(), 1));
777 }
778 }
779
780 std::vector<MX> per_stage_orig;
781 std::vector<MX> per_stage_magic;
782 per_stage_orig.reserve(per_stage_idx.size());
783 per_stage_magic.reserve(per_stage_idx.size());
784 for (size_t i = 0; i < per_stage_idx.size(); ++i) {
785 per_stage_orig.push_back(params_mx[per_stage_idx[i]]);
786 per_stage_magic.push_back(repmat(col_syms[i], 1, N));
787 }
788 cost_substituted =
789 per_stage_orig.empty()
790 ? stage_cost
791 : MX::substitute(std::vector<MX>{stage_cost}, per_stage_orig, per_stage_magic)[0];
792
793 for (casadi_int k = 1; k < N; ++k) {
794 MX cost_at_k = prob_->stage_cost(x_k, u_k, k);
795 MX cost_k_subst =
796 per_stage_orig.empty()
797 ? cost_at_k
798 : MX::substitute(std::vector<MX>{cost_at_k}, per_stage_orig, per_stage_magic)[0];
799 if (!MX::is_equal(cost_substituted, cost_k_subst, /*depth*/ 100)) {
800 casadi_warning("mapsum_stage_cost requested but stage_cost depends on k "
801 "beyond per-stage parameter slicing; falling back to "
802 "per-stage loop to preserve correctness.");
803 mapsum_safe = false;
804 break;
805 }
806 }
807 }
808
809 MX J_stage;
810 if (mapsum_safe) {
811 // Per-stage params -> col_syms (Map iterates columns); broadcast params
812 // are marked via reduce_in (Map repeats them).
813 std::vector<MX> L_subst_inputs = {x_k, u_k};
814 std::vector<casadi_int> reduce_in;
815 size_t next_col = 0;
816 for (size_t p = 0; p < params_mx.size(); ++p) {
817 casadi_int input_idx = static_cast<casadi_int>(2 + p);
818 bool is_per_stage = (next_col < per_stage_idx.size() && per_stage_idx[next_col] == p);
819 if (is_per_stage) {
820 L_subst_inputs.push_back(col_syms[next_col]);
821 ++next_col;
822 } else {
823 L_subst_inputs.push_back(params_mx[p]);
824 reduce_in.push_back(input_idx);
825 }
826 }
827 Function L_subst("L_stage_subst", L_subst_inputs, {cost_substituted});
828 if (expand_inner_functions)
829 L_subst = L_subst.expand(L_subst.name(), {{"cse", true}});
830
831 std::vector<casadi_int> reduce_out = {0};
832 Function L_mapsum = L_subst.map("L_stage_mapsum", "serial", N, reduce_in, reduce_out, Dict());
833 std::vector<MX> map_inputs = {X(Slice(), Slice(0, N)), U};
834 for (auto &param : params_mx) {
835 map_inputs.push_back(param);
836 }
837 J_stage = L_mapsum(map_inputs)[0];
838 } else {
839 std::vector<MX> stage_costs;
840 stage_costs.reserve(N);
841 for (casadi_int i = 0; i < N; ++i) {
842 std::vector<MX> stage_cost_inputs = {Xs[i], Us[i]};
843 stage_cost_inputs.insert(stage_cost_inputs.end(), params_mx.begin(), params_mx.end());
844 MX cost_i = prob_->stage_cost(Xs[i], Us[i], i);
845 Function L_i("L_stage_cost_" + std::to_string(i), stage_cost_inputs, {cost_i});
846 stage_costs.push_back(L_i(stage_cost_inputs)[0]);
847 }
848 J_stage = sum(vertcat(stage_costs));
849 }
850
851 // Terminal cost
852 MX terminal_val = prob_->terminal_cost(Xs[N]);
853
854 // ソフト制約のスラック変数に対するペナルティコスト
855 MX penalty_cost = 0;
856 if (n_s_total > 0) {
857 for (casadi_int k = 0; k < N; ++k) {
858 casadi_int off = 0;
859 for (size_t i = 0; i < prob_->soft_constraints_.size(); ++i) {
860 auto &sc = prob_->soft_constraints_[i];
861 const casadi_int m = soft_sizes[i];
862 MX sk_part = Ss[k](Slice(off, off + m));
863 penalty_cost = penalty_cost + sc.w1 * sum1(sk_part);
864 if (sc.w2 != 0.0) {
865 penalty_cost = penalty_cost + 0.5 * sc.w2 * dot(sk_part, sk_part);
866 }
867 off += m;
868 }
869 }
870 }
871
872 MX J = J_stage + terminal_val + penalty_cost;
873
874 // Path constraints
875 MX G_path;
876 if (!g_k.is_empty()) {
877 std::vector<MX> G_map_inputs = {X(Slice(), Slice(0, N)), U};
878 if (n_s_total > 0) {
879 G_map_inputs.push_back(horzcat(Ss));
880 }
881 for (auto &param : params_mx) {
882 G_map_inputs.push_back(repmat(param, 1, N));
883 }
884 G_path = G_constraints.map(N)(G_map_inputs)[0];
885 }
886
887 // 4. NLP construction
888 std::vector<MX> w_vec;
889 w_vec.reserve((n_s_total > 0 ? 3 : 2) * N + 1);
890 for (casadi_int i = 0; i < N; ++i) {
891 w_vec.push_back(Xs[i]);
892 w_vec.push_back(Us[i]);
893 if (n_s_total > 0) {
894 w_vec.push_back(Ss[i]);
895 }
896 }
897 w_vec.push_back(Xs[N]);
898 MX w = vertcat(w_vec);
899
900 std::vector<MX> g_vec;
901 g_vec.push_back(reshape(X(Slice(), Slice(1, N + 1)) - X_next_cal, nx * N, 1));
902 if (!g_k.is_empty()) {
903 g_vec.push_back(reshape(G_path, G_path.size1() * G_path.size2(), 1));
904 }
905
906 // --- [FIX] Build bounds in temporary double vectors first ---
907 std::vector<double> lbw_numeric, ubw_numeric, lbg_numeric, ubg_numeric;
908
909 auto &u_bounds = prob_->u_bounds_;
910 auto &x_bounds = prob_->x_bounds_;
911
912 // Bounds for w
913 for (casadi_int i = 0; i < N; ++i) {
914 if (i == 0) { // Dummy bounds for x_0 (will be overwritten by x0)
915 lbw_numeric.insert(lbw_numeric.end(), nx, 0.0);
916 ubw_numeric.insert(ubw_numeric.end(), nx, 0.0);
917 } else {
918 lbw_numeric.insert(lbw_numeric.end(), x_bounds[i - 1].first.data(),
919 x_bounds[i - 1].first.data() + nx);
920 ubw_numeric.insert(ubw_numeric.end(), x_bounds[i - 1].second.data(),
921 x_bounds[i - 1].second.data() + nx);
922 }
923 lbw_numeric.insert(lbw_numeric.end(), u_bounds[i].first.data(),
924 u_bounds[i].first.data() + nu);
925 ubw_numeric.insert(ubw_numeric.end(), u_bounds[i].second.data(),
926 u_bounds[i].second.data() + nu);
927 if (n_s_total > 0) {
928 // Slack bound is [0, inf] on stages where the soft constraint is
929 // active and [0, 0] (force s = 0) elsewhere.
930 for (size_t s_i = 0; s_i < prob_->soft_constraints_.size(); ++s_i) {
931 const casadi_int m = soft_sizes[s_i];
932 const bool active = prob_->soft_constraints_[s_i].stage_mask[i];
933 lbw_numeric.insert(lbw_numeric.end(), m, 0.0);
934 ubw_numeric.insert(ubw_numeric.end(), m, active ? inf : 0.0);
935 }
936 }
937 }
938 // Bounds for x_N
939 lbw_numeric.insert(lbw_numeric.end(), x_bounds[N - 1].first.data(),
940 x_bounds[N - 1].first.data() + nx);
941 ubw_numeric.insert(ubw_numeric.end(), x_bounds[N - 1].second.data(),
942 x_bounds[N - 1].second.data() + nx);
943
944 // Bounds for g
945 // Continuity constraints are all zero
946 lbg_numeric.insert(lbg_numeric.end(), nx * N, 0.0);
947 ubg_numeric.insert(ubg_numeric.end(), nx * N, 0.0);
948 equality_.insert(equality_.end(), nx * N, true);
949
950 // Path constraints bounds. Inactive stages of staged constraints get
951 // bounds set to [-inf, +inf] so the constraint is symbolically present
952 // (uniform map structure) but does not influence the solution.
953 for (casadi_int i = 0; i < N; ++i) {
954 for (size_t ci = 0; ci < prob_->equality_constraints_.size(); ++ci) {
955 auto &con = prob_->equality_constraints_[ci];
956 const casadi_int sz = equality_sizes[ci];
957 const bool active = con.stage_mask[i];
958 lbg_numeric.insert(lbg_numeric.end(), sz, active ? 0.0 : -inf);
959 ubg_numeric.insert(ubg_numeric.end(), sz, active ? 0.0 : inf);
960 equality_.insert(equality_.end(), sz, active);
961 }
962 for (size_t ci = 0; ci < prob_->inequality_constraints_.size(); ++ci) {
963 auto &con = prob_->inequality_constraints_[ci];
964 const casadi_int sz = inequality_sizes[ci];
965 const bool active = con.stage_mask[i];
966 lbg_numeric.insert(lbg_numeric.end(), sz, -inf);
967 ubg_numeric.insert(ubg_numeric.end(), sz, active ? 0.0 : inf);
968 equality_.insert(equality_.end(), sz, false);
969 }
970 // Soft path constraints (both forms are one-sided inequalities <= 0).
971 // On inactive stages the bound is relaxed to [-inf, +inf]; combined
972 // with the slack pinned to [0, 0] above, this nulls the constraint and
973 // its penalty contribution at those stages.
974 for (size_t s_i = 0; s_i < prob_->soft_constraints_.size(); ++s_i) {
975 auto &sc = prob_->soft_constraints_[s_i];
976 const casadi_int m = soft_sizes[s_i];
977 const casadi_int rows = (sc.type == Problem::ConstraintType::Inequality) ? m : 2 * m;
978 const bool active = sc.stage_mask[i];
979 lbg_numeric.insert(lbg_numeric.end(), rows, -inf);
980 ubg_numeric.insert(ubg_numeric.end(), rows, active ? 0.0 : inf);
981 equality_.insert(equality_.end(), rows, false);
982 }
983 }
984
985 // Assign from the temporary numeric vectors
986 lbw_ = casadi::DM(lbw_numeric);
987 ubw_ = casadi::DM(ubw_numeric);
988 lbg_ = casadi::DM(lbg_numeric);
989 ubg_ = casadi::DM(ubg_numeric);
990
991 MX g_all = vertcat(g_vec);
992 MX p_all = vertcat(params_mx);
993
994 casadi_prob_ = {{"x", w}, {"f", J}, {"g", g_all}, {"p", p_all}};
995
996 // Initialize w0_, lam_x0_, lam_g0_ for warm start
997 w0_ = DM::zeros(w.size1(), 1);
998 lam_x0_ = DM::zeros(w.size1(), 1);
999 lam_g0_ = DM::zeros(vertcat(g_vec).size1(), 1);
1000 }
1001
1002 virtual void build_solver() { solver_ = nlpsol("solver", solver_name_, casadi_prob_, config_); }
1003
1004private:
1005};
1006
1012class JITMPC : public MPC {
1013public:
1024 static casadi::Dict default_jit_options() {
1025 return casadi::Dict{
1026 {"compiler", "ccache gcc"},
1027 {"flags", "-O3 -march=native"},
1028 {"verbose", false},
1029 };
1030 }
1031
1039 template <class T>
1040 JITMPC(const std::string &prob_name, std::shared_ptr<T> prob, std::string solver_name = "ipopt",
1041 casadi::Dict config = MPC::default_ipopt_config(),
1042 casadi::Dict jit_options = JITMPC::default_jit_options(), const bool verbose = false)
1043 : MPC(prob, solver_name, config), prob_(prob), prob_name_(prob_name),
1044 jit_options_(std::move(jit_options)) {
1045 static_assert(std::is_base_of_v<Problem, T>, "prob must be based SimpleProb");
1046
1047 if (verbose)
1048 std::cout << "Generating and compiling optimized code..." << std::endl;
1049 generate_and_compile_code(prob_name);
1050 if (verbose)
1051 std::cout << "Code generation completed." << std::endl;
1052 }
1053
1055 Eigen::VectorXd solve(Eigen::VectorXd x0,
1056 casadi::DMDict new_param_list = casadi::DMDict()) override {
1057 using namespace casadi;
1058
1059 for (auto &[param_name, param] : new_param_list) {
1060 prob_->param_list_[param_name].dm = param;
1061 }
1062
1063 const size_t nx = prob_->nx();
1064 const size_t nu = prob_->nu();
1065
1066 for (size_t l = 0; l < nx; l++) {
1067 lbw_(l) = x0[l];
1068 ubw_(l) = x0[l];
1069 }
1070
1071 DMDict arg;
1072 arg["x0"] = w0_;
1073 arg["lbx"] = lbw_;
1074 arg["ubx"] = ubw_;
1075 arg["lbg"] = lbg_;
1076 arg["ubg"] = ubg_;
1077 arg["lam_x0"] = lam_x0_;
1078 arg["lam_g0"] = lam_g0_;
1079 param_vec_.clear();
1080 param_vec_.reserve(prob_->param_list_.size());
1081 for (auto &[param_name, param_pair] : prob_->param_list_) {
1082 param_vec_.push_back(param_pair.dm);
1083 }
1084 arg["p"] = vertcat(param_vec_);
1085 DMDict sol = compiled_solver_(arg);
1086
1087 w0_ = sol["x"];
1088 lam_x0_ = sol["lam_x"];
1089 lam_g0_ = sol["lam_g"];
1090
1091 Eigen::VectorXd opt_u(nu);
1092 std::copy(w0_.ptr() + nx, w0_.ptr() + nx + nu, opt_u.data());
1093
1094 return opt_u;
1095 }
1096
1097private:
1098 void generate_and_compile_code(const std::string &prob_name) {
1099 using namespace casadi;
1100
1101 Dict nlpsol_config = config_;
1102 nlpsol_config["jit"] = true;
1103 nlpsol_config["jit_options"] = jit_options_;
1104 nlpsol_config["jit_name"] = "jit_" + prob_name;
1105 nlpsol_config["jit_temp_suffix"] = false;
1106
1107 compiled_solver_ = nlpsol("compiled_solver", solver_name_, casadi_prob_, nlpsol_config);
1108 }
1109
1110 virtual void build_solver() override {
1111 // Do nothing, as solver will be built via JIT compilation
1112 }
1113
1114 casadi::Function compiled_solver_;
1115 std::shared_ptr<Problem> prob_;
1116 std::string prob_name_;
1117 casadi::Dict jit_options_;
1118};
1119
1127class CompiledMPC : public MPC {
1128public:
1137
1141 template <class T>
1142 CompiledMPC(const CompiledLibraryConfig &lib_config, std::shared_ptr<T> prob)
1143 : MPC(prob), prob_(prob), lib_config_(lib_config) {
1144 static_assert(std::is_base_of_v<Problem, T>, "prob must be based SimpleProb");
1145 load_compiled_solver();
1146 }
1147
1149 Eigen::VectorXd solve(Eigen::VectorXd x0,
1150 casadi::DMDict new_param_list = casadi::DMDict()) override {
1151 using namespace casadi;
1152
1153 for (auto &[param_name, param] : new_param_list) {
1154 prob_->param_list_[param_name].dm = param;
1155 }
1156
1157 const size_t nx = prob_->nx();
1158 const size_t nu = prob_->nu();
1159
1160 for (size_t l = 0; l < nx; l++) {
1161 lbw_(l) = x0[l];
1162 ubw_(l) = x0[l];
1163 }
1164
1165 DMDict arg;
1166 arg["x0"] = w0_;
1167 arg["lbx"] = lbw_;
1168 arg["ubx"] = ubw_;
1169 arg["lbg"] = lbg_;
1170 arg["ubg"] = ubg_;
1171 arg["lam_x0"] = lam_x0_;
1172 arg["lam_g0"] = lam_g0_;
1173 param_vec_.clear();
1174 param_vec_.reserve(prob_->param_list_.size());
1175 for (auto &[param_name, param_pair] : prob_->param_list_) {
1176 param_vec_.push_back(param_pair.dm);
1177 }
1178 arg["p"] = vertcat(param_vec_);
1179 DMDict sol = compiled_solver_(arg);
1180
1181 w0_ = sol["x"];
1182 lam_x0_ = sol["lam_x"];
1183 lam_g0_ = sol["lam_g"];
1184
1185 Eigen::VectorXd opt_u(nu);
1186 std::copy(w0_.ptr() + nx, w0_.ptr() + nx + nu, opt_u.data());
1187
1188 return opt_u;
1189 }
1190
1202 template <class T>
1203 static void generate_code(const std::string &export_solver_name, const std::string &export_dir,
1204 const std::string &solver_name = "ipopt",
1205 const casadi::Dict &solver_config = MPC::default_ipopt_config(),
1206 const casadi::Dict &codegen_options = {}) {
1207 static_assert(std::is_base_of_v<Problem, T>, "Problem type must inherit from Problem");
1208 namespace fs = std::filesystem;
1209 auto prob = std::make_shared<T>();
1210 MPC mpc(prob, solver_name, solver_config);
1211
1212 fs::path out_dir = fs::path(export_dir);
1213 fs::create_directories(out_dir);
1214 fs::path c_path = out_dir / (export_solver_name + ".c");
1215
1216 // Use the MPC-preprocessed config (equality flags applied, custom flags popped).
1217 casadi::Dict solver_cfg = mpc.solver_config();
1218
1219 casadi::Function solver =
1220 casadi::nlpsol(export_solver_name, solver_name, mpc.casadi_prob(), solver_cfg);
1221 casadi::Dict opts = codegen_options;
1222 if (opts.find("with_header") == opts.end())
1223 opts["with_header"] = true;
1224 casadi::CodeGenerator cg(export_solver_name, opts);
1225 cg.add(solver);
1226 cg.generate(out_dir.string() + "/");
1227 std::cout << "Generated solver source at: " << c_path << std::endl;
1228 }
1229
1230private:
1231 void load_compiled_solver() {
1232 compiled_solver_ =
1233 casadi::external(lib_config_.export_solver_name, lib_config_.shared_library_path);
1234 }
1235 virtual void build_solver() override {
1236 // Compiled solver is loaded externally; do not construct a CasADi solver here.
1237 }
1238
1239 casadi::Function compiled_solver_;
1240 std::shared_ptr<Problem> prob_;
1241 CompiledLibraryConfig lib_config_;
1242};
1243
1244} // namespace simple_casadi_mpc
MPC variant that loads an externally-compiled solver shared library.
Definition simple_casadi_mpc.hpp:1127
CompiledMPC(const CompiledLibraryConfig &lib_config, std::shared_ptr< T > prob)
Load the prebuilt solver from lib_config and bind it to prob.
Definition simple_casadi_mpc.hpp:1142
Eigen::VectorXd solve(Eigen::VectorXd x0, casadi::DMDict new_param_list=casadi::DMDict()) override
Solve the NLP at the current state and return the first optimal control.
Definition simple_casadi_mpc.hpp:1149
static void generate_code(const std::string &export_solver_name, const std::string &export_dir, const std::string &solver_name="ipopt", const casadi::Dict &solver_config=MPC::default_ipopt_config(), const casadi::Dict &codegen_options={})
Emit the C source for an AOT-compiled solver. Called from a codegen executable.
Definition simple_casadi_mpc.hpp:1203
MPC variant that JIT-compiles the solver during construction.
Definition simple_casadi_mpc.hpp:1012
JITMPC(const std::string &prob_name, std::shared_ptr< T > prob, std::string solver_name="ipopt", casadi::Dict config=MPC::default_ipopt_config(), casadi::Dict jit_options=JITMPC::default_jit_options(), const bool verbose=false)
Build the NLP and JIT-compile its solver in one step.
Definition simple_casadi_mpc.hpp:1040
Eigen::VectorXd solve(Eigen::VectorXd x0, casadi::DMDict new_param_list=casadi::DMDict()) override
Solve the NLP at the current state and return the first optimal control.
Definition simple_casadi_mpc.hpp:1055
static casadi::Dict default_jit_options()
Default JIT compile options (compiler / flags / verbose) passed to CasADi.
Definition simple_casadi_mpc.hpp:1024
Runtime MPC solver. Builds a CasADi NLP from a Problem and solves it on demand.
Definition simple_casadi_mpc.hpp:417
static casadi::Dict default_fatrop_config()
Reasonable defaults for the FATROP backend (auto structure detection).
Definition simple_casadi_mpc.hpp:445
static casadi::Dict default_ipopt_config()
Reasonable defaults for the IPOPT backend (silent, warm-start enabled).
Definition simple_casadi_mpc.hpp:420
casadi::MXDict casadi_prob() const
Symbolic NLP description {x, f, g, p} constructed from the Problem.
Definition simple_casadi_mpc.hpp:579
casadi::Dict solver_config() const
Effective config forwarded to nlpsol (after consuming simple-casadi-mpc keys).
Definition simple_casadi_mpc.hpp:583
static bool equality_required(const std::string &solver_name, const casadi::Dict &config)
Whether the chosen backend needs an equality flag vector in the config.
Definition simple_casadi_mpc.hpp:461
virtual Eigen::VectorXd solve(Eigen::VectorXd x0, casadi::DMDict new_param_list=casadi::DMDict())
Solve the NLP at the current state and return the first optimal control.
Definition simple_casadi_mpc.hpp:535
static casadi::Dict default_qpoases_config()
Reasonable defaults for SQP method with the qpOASES inner QP solver.
Definition simple_casadi_mpc.hpp:429
std::vector< casadi_int > equality_flags() const
Per-constraint flags marking equality (true) vs inequality (false).
Definition simple_casadi_mpc.hpp:585
MPC(std::shared_ptr< T > prob, std::string solver_name="ipopt", casadi::Dict config=default_ipopt_config())
Build the NLP from prob and create the underlying nlpsol.
Definition simple_casadi_mpc.hpp:481
const std::string & solver_name() const
Backend name passed to CasADi nlpsol.
Definition simple_casadi_mpc.hpp:581
Base class describing an MPC optimal control problem.
Definition simple_casadi_mpc.hpp:57
Eigen::VectorXd simulate(Eigen::VectorXd x0, Eigen::MatrixXd u)
Advance the plant one step using the user-provided discrete dynamics.
Definition simple_casadi_mpc.hpp:136
void set_input_lower_bound(Eigen::VectorXd lb, int start=-1, int end=-1)
Set only the lower bound on the input. See set_input_bound for start/end.
Definition simple_casadi_mpc.hpp:180
void add_constraint_at(ConstraintType type, std::function< casadi::MX(casadi::MX, casadi::MX)> constraint, int start, int end=-1)
Add a hard path constraint applied only at the specified stage range.
Definition simple_casadi_mpc.hpp:240
void add_soft_constraint(ConstraintType type, std::function< casadi::MX(casadi::MX, casadi::MX)> constraint, double w1=1e3, double w2=0.0)
Add a soft path constraint with per-stage non-negative slack.
Definition simple_casadi_mpc.hpp:270
DynamicsType dynamics_type() const
Discretization scheme.
Definition simple_casadi_mpc.hpp:304
void set_input_bound(Eigen::VectorXd lb, Eigen::VectorXd ub, int start=-1, int end=-1)
Set per-stage input bounds .
Definition simple_casadi_mpc.hpp:172
casadi::MX parameter(std::string name, size_t rows, size_t cols)
Declare a runtime-tunable symbolic parameter that can be passed to MPC::solve.
Definition simple_casadi_mpc.hpp:339
Eigen::VectorXd dynamics_eval(Eigen::VectorXd x, Eigen::VectorXd u)
Evaluate dynamics at numeric (x, u). Useful for plant simulation.
Definition simple_casadi_mpc.hpp:119
void add_soft_constraint_at(ConstraintType type, std::function< casadi::MX(casadi::MX, casadi::MX)> constraint, double w1, double w2, int start, int end=-1)
Add a soft path constraint applied only at the specified stage range.
Definition simple_casadi_mpc.hpp:281
void add_constraint(ConstraintType type, std::function< casadi::MX(casadi::MX, casadi::MX)> constrinat)
Add a hard path constraint applied at every stage.
Definition simple_casadi_mpc.hpp:224
casadi::MX reference_trajectory(std::string name="x_ref")
Convenience wrapper of parameter with shape .
Definition simple_casadi_mpc.hpp:349
ConstraintType
Constraint kind passed to add_constraint and add_soft_constraint.
Definition simple_casadi_mpc.hpp:68
Problem(DynamicsType dyn_type, size_t _nx, size_t _nu, size_t _horizon, double _dt)
Construct a problem with a fixed prediction horizon and uniform time step.
Definition simple_casadi_mpc.hpp:79
const std::vector< double > & dts() const
Read-only view of all per-stage time steps, length horizon().
Definition simple_casadi_mpc.hpp:317
void set_input_upper_bound(Eigen::VectorXd ub, int start=-1, int end=-1)
Set only the upper bound on the input. See set_input_bound for start/end.
Definition simple_casadi_mpc.hpp:188
DynamicsType
Discretization scheme used to advance the dynamics.
Definition simple_casadi_mpc.hpp:60
@ ContinuesForwardEuler
Continuous-time, integrated by forward Euler.
@ Discretized
User supplies x_{k+1} directly.
@ ContinuesModifiedEuler
Continuous-time, integrated by Heun's method.
@ ContinuesRK4
Continuous-time, integrated by classical RK4.
double dt(size_t k) const
Per-stage time step .
Definition simple_casadi_mpc.hpp:314
Problem(DynamicsType dyn_type, size_t _nx, size_t _nu, size_t _horizon, std::vector< double > _dts)
Construct a problem with a per-stage (variable) time step.
Definition simple_casadi_mpc.hpp:92
double dt() const
Step size at stage 0 (or the uniform step for problems built with the single-dt constructor)....
Definition simple_casadi_mpc.hpp:311
virtual casadi::MX stage_cost(casadi::MX x, casadi::MX u, size_t k)
Stage cost at step . Default returns 0.
Definition simple_casadi_mpc.hpp:291
size_t nx() const
State dimension.
Definition simple_casadi_mpc.hpp:305
size_t nu() const
Control dimension.
Definition simple_casadi_mpc.hpp:306
void set_state_lower_bound(Eigen::VectorXd lb, int start=-1, int end=-1)
Set only the lower bound on the state. See set_input_bound for start/end.
Definition simple_casadi_mpc.hpp:205
size_t horizon() const
Number of stages N.
Definition simple_casadi_mpc.hpp:307
Eigen::VectorXd simulate(Eigen::VectorXd x0, Eigen::MatrixXd u, double dt)
Advance the plant dt seconds using the configured continuous integrator.
Definition simple_casadi_mpc.hpp:146
void set_state_bound(Eigen::VectorXd lb, Eigen::VectorXd ub, int start=-1, int end=-1)
Set per-stage state bounds . Range semantics match set_input_bound.
Definition simple_casadi_mpc.hpp:197
bool has_uniform_dt() const
True iff every stage has the same dt.
Definition simple_casadi_mpc.hpp:320
void set_state_upper_bound(Eigen::VectorXd ub, int start=-1, int end=-1)
Set only the upper bound on the state. See set_input_bound for start/end.
Definition simple_casadi_mpc.hpp:213
virtual casadi::MX terminal_cost(casadi::MX x)
Terminal cost evaluated at . Default returns 0.
Definition simple_casadi_mpc.hpp:299
virtual casadi::MX dynamics(casadi::MX x, casadi::MX u)=0
Symbolic dynamics, must be overridden by the user.
Eigen::MatrixXd to_eigen(const casadi::DM &dm)
Convert a CasADi DM (column-major) into an Eigen MatrixXd.
Definition casadi_utils.hpp:14
Lightweight C++ utilities for building and solving MPC problems with CasADi.
Definition simple_casadi_mpc.hpp:12
Locator for an AOT-compiled solver shared library.
Definition simple_casadi_mpc.hpp:1133
std::string shared_library_path
Filesystem path to the .so / .dylib / .dll.
Definition simple_casadi_mpc.hpp:1135
std::string export_solver_name
CasADi function name embedded in the shared library.
Definition simple_casadi_mpc.hpp:1134