cyqlone develop
Fast, parallel and vectorized solver for linear systems with optimal control structure.
Loading...
Searching...
No Matches
spring-mass.cpp
Go to the documentation of this file.
4#include <batmat/config.hpp>
5#include <guanaqo/eigen/view.hpp>
6
7#include <algorithm>
8#include <limits>
9#include <numeric>
10#include <random>
11#include <stdexcept>
12
13namespace CYQLONE_NS(cyqlone::qpalm::problems) {
14
16 using guanaqo::as_view;
17 const auto inf = std::numeric_limits<real_t>::infinity();
18
19 auto n_masses = static_cast<index_t>(p.masses.size());
20 const index_t ny_x = p.v_max > 0 ? 2 * n_masses : n_masses,
21 ny_x_N = p.v_max_f > 0 ? 2 * n_masses : n_masses;
22 LinearOCPStorage ocp{.dim{
23 .N_horiz = p.N_horiz,
24 .nx = 2 * n_masses, // position + velocity for each mass
25 .nu = p.n_actuators,
26 .ny = ny_x + p.n_actuators,
27 .ny_N = ny_x_N,
28 }};
29 auto [N, nx, nu, ny, ny_N] = ocp.dim;
30
31 // Continuous-time dynamics
32 eigen_mat A = eigen_mat::Zero(nx, nx), B = eigen_mat::Zero(nx, nu), b = eigen_mat::Zero(nx, 1);
33 for (Eigen::Index v = 0; v < n_masses; ++v) {
34 const auto km = p.k_spring / p.masses[v];
35 A(v, n_masses + v) = 1; // ṗ(t) = v(t)
36 A(n_masses + v, n_masses + v) = -p.friction; // v̇(t) = -ρ v(t) + ...
37 A(n_masses + v, v) = -2 * km; // ... - (2k/m) p(t) + ...
38 if (v > 0)
39 A(n_masses + v, v - 1) = km; // ... + (k/m) p_{v-1}(t)
40 if (v < n_masses - 1)
41 A(n_masses + v, v + 1) = km; // ... + (k/m) p_{v+1}(t)
42 else
43 b(n_masses + v, 0) = km * p.width; // Right wall force
44 }
45 switch (p.actuator_placement) {
47 for (Eigen::Index a = 0; a < p.n_actuators; ++a)
48 B(n_masses + a, a) = 1 / p.masses[a];
49 break;
51 std::mt19937 rng(p.seed);
52 std::vector<index_t> actuator_indices(n_masses);
53 std::ranges::iota(actuator_indices, index_t{});
54 std::shuffle(actuator_indices.begin(), actuator_indices.end(), rng);
55 for (Eigen::Index a = 0; a < p.n_actuators; ++a)
56 B(n_masses + actuator_indices[a], a) = 1 / p.masses[actuator_indices[a]];
57 } break;
59 std::mt19937 rng(p.seed);
60 std::vector<index_t> actuator_indices_l(n_masses), actuator_indices_r(n_masses);
61 std::ranges::iota(actuator_indices_l, index_t{});
62 std::ranges::iota(actuator_indices_r, index_t{});
63 std::shuffle(actuator_indices_l.begin(), actuator_indices_l.end(), rng);
64 std::shuffle(actuator_indices_r.begin(), actuator_indices_r.end(), rng);
65 for (Eigen::Index a = 0; a < p.n_actuators; ++a) {
66 B(n_masses + actuator_indices_l[a], a) = +1 / p.masses[actuator_indices_l[a]];
67 B(n_masses + actuator_indices_r[a], a) = -1 / p.masses[actuator_indices_r[a]];
68 }
69 } break;
71 for (Eigen::Index a = 0; a < p.n_actuators; ++a) {
72 if (a % 3 == 0) {
73 auto v_l = 2 * a, v_r = 2 * a + 1;
74 if (v_l < n_masses)
75 B(n_masses + v_l, a) = +1 / p.masses[v_l];
76 if (v_r < n_masses)
77 B(n_masses + v_r, a) = -1 / p.masses[v_r];
78 } else if (a % 3 == 1) {
79 auto v_l = 2 * a, v_r = 2 * a + 2;
80 if (v_l < n_masses)
81 B(n_masses + v_l, a) = +1 / p.masses[v_l];
82 if (v_r < n_masses)
83 B(n_masses + v_r, a) = -1 / p.masses[v_r];
84 } else {
85 auto v_l = 2 * a - 1, v_r = 2 * a + 1;
86 if (v_l < n_masses)
87 B(n_masses + v_l, a) = +1 / p.masses[v_l];
88 if (v_r < n_masses)
89 B(n_masses + v_r, a) = -1 / p.masses[v_r];
90 }
91 }
92 break;
93 default: throw std::invalid_argument("Invalid actuator placement");
94 }
95 auto Ts = p.T_horiz / static_cast<real_t>(p.N_horiz);
96 // ẋ = Ax + Bu + b → x[k+1] = Ad x[k] + Bd u[k] + bd
97 auto [Ad, Bd, bd] = discretize_zoh(A, B, b, Ts);
98
99 // Constraints rhs
100 ocp.b().set_constant(0);
101 ocp.b_min().set_constant(-inf);
102 ocp.b_max().set_constant(+inf);
103 auto x0 = ocp.b(0);
104 for (index_t v = 0; v < n_masses; ++v) // Initial positions
105 x0(v, 0) = static_cast<real_t>(v + 1) * p.width / static_cast<real_t>(n_masses + 1);
106
107 // Dynamics and constraint matrices
108 for (index_t i = 0; i < N; ++i) {
109 auto Ai = ocp.A(i), Bi = ocp.B(i), bi = ocp.b(i + 1), Ci = ocp.C(i), Di = ocp.D(i);
110 auto Qi = ocp.Q(i), Ri = ocp.R(i);
111 auto lbi = ocp.b_min(i), ubi = ocp.b_max(i);
112 for (index_t v = 0; v < n_masses; ++v) {
113 Ai = as_view(Ad);
114 Bi = as_view(Bd);
115 bi = as_view(bd);
116 Ci(v, v) = 1; // Measure displacement
117 lbi(v, 0) = x0(v, 0) + p.p_min;
118 ubi(v, 0) = x0(v, 0) + p.p_max;
119 if (p.v_max > 0) {
120 Ci(n_masses + v, n_masses + v) = 1; // Measure velocity
121 lbi(n_masses + v, 0) = -p.v_max;
122 ubi(n_masses + v, 0) = +p.v_max;
123 }
124 Qi(v, v) = p.q_pos;
125 Qi(n_masses + v, n_masses + v) = p.q_vel;
126 }
127 for (index_t a = 0; a < p.n_actuators; ++a) {
128 Di(ny_x + a, a) = 1; // Measure input
129 lbi(ny_x + a, 0) = -p.F_max;
130 ubi(ny_x + a, 0) = +p.F_max;
131 Ri(a, a) = p.r_act;
132 }
133 }
134 auto Ci = ocp.C(N), Qi = ocp.Q(N);
135 auto lbi = ocp.b_min(N), ubi = ocp.b_max(N);
136 for (index_t v = 0; v < n_masses; ++v) {
137 Ci(v, v) = 1; // Measure displacement
138 lbi(v, 0) = x0(v, 0) + p.p_min_f;
139 ubi(v, 0) = x0(v, 0) + p.p_max_f;
140 if (p.v_max_f > 0) {
141 Ci(n_masses + v, n_masses + v) = 1; // Measure velocity
142 lbi(n_masses + v, 0) = -p.v_max_f;
143 ubi(n_masses + v, 0) = +p.v_max_f;
144 }
145 Qi(v, v) = p.q_pos_f;
146 Qi(n_masses + v, n_masses + v) = p.q_vel_f;
147 }
148 std::vector<real_t> ref(nx + nu + nx);
149 for (index_t v = 0; v < n_masses; ++v) {
150 ref[v] = x0(v, 0); // position
151 ref[nx + nu + v] = x0(v, 0); // final position
152 }
153 reference_to_gradient(ocp, ref);
154
155 return {
156 .ocp = std::move(ocp),
157 .ref = std::move(ref),
158 };
159}
160
161} // namespace CYQLONE_NS(cyqlone::qpalm::problems)
#define CYQLONE_NS(ns)
Definition config.hpp:10
Conversion utilities for optimal control problems.
auto as_view(Eigen::DenseBase< Derived > &M, with_index_type_t< I >={})
void reference_to_gradient(const LinearOCPStorage &ocp, std::span< const real_t > ref, std::span< real_t > qr)
Simply computes the gradient of the quadratic cost , with , with the Hessian .
Definition conversion.cpp:9
SpringMassProblem spring_mass(SpringMassParams p)
Eigen::MatrixX< real_t > eigen_mat
Definition zoh.hpp:9
std::tuple< eigen_mat, eigen_mat > discretize_zoh(const Eigen::Ref< const eigen_mat > &A, const Eigen::Ref< const eigen_mat > &B, real_t Ts)
Definition zoh.hpp:11
constexpr index_t v
Storage for a linear-quadratic OCP of the form.
Definition ocp.hpp:37
guanaqo::MatrixView< real_t, index_t > B(index_t i)
Definition ocp.hpp:132
guanaqo::MatrixView< real_t, index_t > D(index_t i)
Definition ocp.hpp:111
guanaqo::MatrixView< real_t, index_t > b_min()
Definition ocp.hpp:267
guanaqo::MatrixView< real_t, index_t > b()
Definition ocp.hpp:251
guanaqo::MatrixView< real_t, index_t > R(index_t i)
Definition ocp.hpp:80
guanaqo::MatrixView< real_t, index_t > C(index_t i)
Definition ocp.hpp:106
guanaqo::MatrixView< real_t, index_t > Q(index_t i)
Definition ocp.hpp:75
guanaqo::MatrixView< real_t, index_t > A(index_t i)
Definition ocp.hpp:127
guanaqo::MatrixView< real_t, index_t > b_max()
Definition ocp.hpp:283
real_t r_act
scaling factor for cost terms for the actuators
real_t p_max_f
maximum displacement of each mass for the final stage
real_t F_max
maximum actuator force
real_t q_vel_f
scaling factor for terminal cost term for the velocity
real_t p_min
minimum displacement of each mass
real_t v_max
maximum velocity of each mass (zero = no limit)
real_t v_max_f
maximum velocity of each mass for the final stage
uint64_t seed
random seed for actuator placement
real_t p_min_f
minimum displacement of each mass for the final stage
real_t width
width of the setup (distance between the walls)
real_t q_vel
scaling factor for cost terms for the velocity
real_t k_spring
spring constant between masses
enum cyqlone::qpalm::problems::SpringMassParams::ActuatorPlacement actuator_placement
real_t q_pos_f
scaling factor for terminal cost term for the position
index_t n_actuators
number of actuators
real_t p_max
maximum displacement of each mass
index_t N_horiz
number of discretization steps
real_t q_pos
scaling factor for cost terms for the position