Sleipnir C++ API
Loading...
Searching...
No Matches
ocp.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <stdint.h>
6
7#include <chrono>
8#include <utility>
9
10#include "sleipnir/autodiff/variable.hpp"
11#include "sleipnir/autodiff/variable_matrix.hpp"
12#include "sleipnir/optimization/ocp/dynamics_type.hpp"
13#include "sleipnir/optimization/ocp/timestep_method.hpp"
14#include "sleipnir/optimization/ocp/transcription_method.hpp"
15#include "sleipnir/optimization/problem.hpp"
16#include "sleipnir/util/assert.hpp"
17#include "sleipnir/util/concepts.hpp"
18#include "sleipnir/util/function_ref.hpp"
19#include "sleipnir/util/symbol_exports.hpp"
20
21namespace slp {
22
49template <typename Scalar>
50class OCP : public Problem<Scalar> {
51 public:
69 OCP(int num_states, int num_inputs, std::chrono::duration<Scalar> dt,
70 int num_steps,
74 DynamicsType dynamics_type = DynamicsType::EXPLICIT_ODE,
75 TimestepMethod timestep_method = TimestepMethod::FIXED,
76 TranscriptionMethod transcription_method =
77 TranscriptionMethod::DIRECT_TRANSCRIPTION)
80 dt,
82 [=]([[maybe_unused]] const VariableMatrix<Scalar>& t,
83 const VariableMatrix<Scalar>& x,
84 const VariableMatrix<Scalar>& u,
86 -> VariableMatrix<Scalar> { return dynamics(x, u); },
90
108 OCP(int num_states, int num_inputs, std::chrono::duration<Scalar> dt,
109 int num_steps,
112 const VariableMatrix<Scalar>& u, const Variable<Scalar>& dt)>
113 dynamics,
114 DynamicsType dynamics_type = DynamicsType::EXPLICIT_ODE,
115 TimestepMethod timestep_method = TimestepMethod::FIXED,
116 TranscriptionMethod transcription_method =
117 TranscriptionMethod::DIRECT_TRANSCRIPTION)
118 : m_num_steps{num_steps},
119 m_dynamics{std::move(dynamics)},
120 m_dynamics_type{dynamics_type} {
121 // u is num_steps + 1 so that the final constraint function evaluation works
122 m_U = this->decision_variable(num_inputs, m_num_steps + 1);
123
124 if (timestep_method == TimestepMethod::FIXED) {
125 m_DT = VariableMatrix<Scalar>{1, m_num_steps + 1};
126 for (int i = 0; i < num_steps + 1; ++i) {
127 m_DT[0, i] = dt.count();
128 }
129 } else if (timestep_method == TimestepMethod::VARIABLE_SINGLE) {
130 Variable single_dt = this->decision_variable();
131 single_dt.set_value(dt.count());
132
133 // Set the member variable matrix to track the decision variable
134 m_DT = VariableMatrix<Scalar>{1, m_num_steps + 1};
135 for (int i = 0; i < num_steps + 1; ++i) {
136 m_DT[0, i] = single_dt;
137 }
138 } else if (timestep_method == TimestepMethod::VARIABLE) {
139 m_DT = this->decision_variable(1, m_num_steps + 1);
140 for (int i = 0; i < num_steps + 1; ++i) {
141 m_DT[0, i].set_value(dt.count());
142 }
143 }
144
145 if (transcription_method == TranscriptionMethod::DIRECT_TRANSCRIPTION) {
146 m_X = this->decision_variable(num_states, m_num_steps + 1);
147 constrain_direct_transcription();
148 } else if (transcription_method ==
149 TranscriptionMethod::DIRECT_COLLOCATION) {
150 m_X = this->decision_variable(num_states, m_num_steps + 1);
151 constrain_direct_collocation();
152 } else if (transcription_method == TranscriptionMethod::SINGLE_SHOOTING) {
153 // In single-shooting the states aren't decision variables, but instead
154 // depend on the input and previous states
155 m_X = VariableMatrix<Scalar>{num_states, m_num_steps + 1};
156 constrain_single_shooting();
157 }
158 }
159
163 template <typename T>
164 requires ScalarLike<T> || MatrixLike<T>
165 void constrain_initial_state(const T& initial_state) {
166 this->subject_to(this->initial_state() == initial_state);
167 }
168
172 template <typename T>
173 requires ScalarLike<T> || MatrixLike<T>
174 void constrain_final_state(const T& final_state) {
175 this->subject_to(this->final_state() == final_state);
176 }
177
186 callback) {
187 for (int i = 0; i < m_num_steps + 1; ++i) {
188 auto x = X().col(i);
189 auto u = U().col(i);
190 callback(x, u);
191 }
192 }
193
201 const function_ref<
202 void(const Variable<Scalar>& t, const VariableMatrix<Scalar>& x,
203 const VariableMatrix<Scalar>& u, const Variable<Scalar>& dt)>
204 callback) {
206
207 for (int i = 0; i < m_num_steps + 1; ++i) {
208 auto x = X().col(i);
209 auto u = U().col(i);
210 auto dt = this->dt()[0, i];
211 callback(time, x, u, dt);
212
213 time += dt;
214 }
215 }
216
221 template <typename T>
222 requires ScalarLike<T> || MatrixLike<T>
224 for (int i = 0; i < m_num_steps + 1; ++i) {
225 this->subject_to(U().col(i) >= lower_bound);
226 }
227 }
228
233 template <typename T>
234 requires ScalarLike<T> || MatrixLike<T>
236 for (int i = 0; i < m_num_steps + 1; ++i) {
237 this->subject_to(U().col(i) <= upper_bound);
238 }
239 }
240
244 void set_min_timestep(std::chrono::duration<Scalar> min_timestep) {
245 this->subject_to(dt() >= min_timestep.count());
246 }
247
251 void set_max_timestep(std::chrono::duration<Scalar> max_timestep) {
252 this->subject_to(dt() <= max_timestep.count());
253 }
254
261 VariableMatrix<Scalar>& X() { return m_X; }
262
270 VariableMatrix<Scalar>& U() { return m_U; }
271
279 VariableMatrix<Scalar>& dt() { return m_DT; }
280
285
289 VariableMatrix<Scalar> final_state() { return m_X.col(m_num_steps); }
290
291 private:
292 int m_num_steps;
293
296 const VariableMatrix<Scalar>& u, const Variable<Scalar>& dt)>
297 m_dynamics;
298 DynamicsType m_dynamics_type;
299
303
311 template <typename F, typename State, typename Input, typename Time>
312 State rk4(F&& f, State x, Input u, Time t0, Time dt) {
313 auto halfdt = dt * Scalar(0.5);
314 State k1 = f(t0, x, u, dt);
315 State k2 = f(t0 + halfdt, x + k1 * halfdt, u, dt);
316 State k3 = f(t0 + halfdt, x + k2 * halfdt, u, dt);
317 State k4 = f(t0 + dt, x + k3 * dt, u, dt);
318
319 return x + (k1 + k2 * Scalar(2) + k3 * Scalar(2) + k4) * (dt / Scalar(6));
320 }
321
323 void constrain_direct_collocation() {
324 slp_assert(m_dynamics_type == DynamicsType::EXPLICIT_ODE);
325
326 Variable<Scalar> time{0};
327
328 // Derivation at https://mec560sbu.github.io/2016/09/30/direct_collocation/
329 for (int i = 0; i < m_num_steps; ++i) {
330 Variable h = dt()[0, i];
331
332 auto& f = m_dynamics;
333
334 auto t_begin = time;
335 auto t_end = t_begin + h;
336
337 auto x_begin = X().col(i);
338 auto x_end = X().col(i + 1);
339
340 auto u_begin = U().col(i);
341 auto u_end = U().col(i + 1);
342
343 auto xdot_begin = f(t_begin, x_begin, u_begin, h);
344 auto xdot_end = f(t_end, x_end, u_end, h);
345 auto xdot_c = Scalar(-3) / (Scalar(2) * h) * (x_begin - x_end) -
346 Scalar(0.25) * (xdot_begin + xdot_end);
347
348 auto t_c = t_begin + Scalar(0.5) * h;
349 auto x_c = Scalar(0.5) * (x_begin + x_end) +
350 h / Scalar(8) * (xdot_begin - xdot_end);
351 auto u_c = Scalar(0.5) * (u_begin + u_end);
352
353 this->subject_to(xdot_c == f(t_c, x_c, u_c, h));
354
355 time += h;
356 }
357 }
358
360 void constrain_direct_transcription() {
361 Variable<Scalar> time{0};
362
363 for (int i = 0; i < m_num_steps; ++i) {
364 auto x_begin = X().col(i);
365 auto x_end = X().col(i + 1);
366 auto u = U().col(i);
367 Variable dt = this->dt()[0, i];
368
369 if (m_dynamics_type == DynamicsType::EXPLICIT_ODE) {
370 this->subject_to(
371 x_end == rk4<const decltype(m_dynamics)&, VariableMatrix<Scalar>,
372 VariableMatrix<Scalar>, Variable<Scalar>>(
373 m_dynamics, x_begin, u, time, dt));
374 } else if (m_dynamics_type == DynamicsType::DISCRETE) {
375 this->subject_to(x_end == m_dynamics(time, x_begin, u, dt));
376 }
377
378 time += dt;
379 }
380 }
381
383 void constrain_single_shooting() {
384 Variable<Scalar> time{0};
385
386 for (int i = 0; i < m_num_steps; ++i) {
387 auto x_begin = X().col(i);
388 auto x_end = X().col(i + 1);
389 auto u = U().col(i);
390 Variable dt = this->dt()[0, i];
391
392 if (m_dynamics_type == DynamicsType::EXPLICIT_ODE) {
393 x_end = rk4<const decltype(m_dynamics)&, VariableMatrix<Scalar>,
394 VariableMatrix<Scalar>, Variable<Scalar>>(
395 m_dynamics, x_begin, u, time, dt);
396 } else if (m_dynamics_type == DynamicsType::DISCRETE) {
397 x_end = m_dynamics(time, x_begin, u, dt);
398 }
399
400 time += dt;
401 }
402 }
403};
404
405extern template class EXPORT_TEMPLATE_DECLARE(SLEIPNIR_DLLEXPORT) OCP<double>;
406
407} // namespace slp
Definition intrusive_shared_ptr.hpp:27
Definition ocp.hpp:50
void constrain_initial_state(const T &initial_state)
Definition ocp.hpp:165
void constrain_final_state(const T &final_state)
Definition ocp.hpp:174
VariableMatrix< Scalar > & U()
Definition ocp.hpp:270
VariableMatrix< Scalar > initial_state()
Definition ocp.hpp:284
void set_min_timestep(std::chrono::duration< Scalar > min_timestep)
Definition ocp.hpp:244
VariableMatrix< Scalar > & X()
Definition ocp.hpp:261
void set_upper_input_bound(const T &upper_bound)
Definition ocp.hpp:235
OCP(int num_states, int num_inputs, std::chrono::duration< Scalar > dt, int num_steps, function_ref< VariableMatrix< Scalar >(const Variable< Scalar > &t, const VariableMatrix< Scalar > &x, const VariableMatrix< Scalar > &u, const Variable< Scalar > &dt)> dynamics, DynamicsType dynamics_type=DynamicsType::EXPLICIT_ODE, TimestepMethod timestep_method=TimestepMethod::FIXED, TranscriptionMethod transcription_method=TranscriptionMethod::DIRECT_TRANSCRIPTION)
Definition ocp.hpp:108
void set_lower_input_bound(const T &lower_bound)
Definition ocp.hpp:223
void for_each_step(const function_ref< void(const VariableMatrix< Scalar > &x, const VariableMatrix< Scalar > &u)> callback)
Definition ocp.hpp:184
void set_max_timestep(std::chrono::duration< Scalar > max_timestep)
Definition ocp.hpp:251
OCP(int num_states, int num_inputs, std::chrono::duration< Scalar > dt, int num_steps, function_ref< VariableMatrix< Scalar >(const VariableMatrix< Scalar > &x, const VariableMatrix< Scalar > &u)> dynamics, DynamicsType dynamics_type=DynamicsType::EXPLICIT_ODE, TimestepMethod timestep_method=TimestepMethod::FIXED, TranscriptionMethod transcription_method=TranscriptionMethod::DIRECT_TRANSCRIPTION)
Definition ocp.hpp:69
VariableMatrix< Scalar > final_state()
Definition ocp.hpp:289
VariableMatrix< Scalar > & dt()
Definition ocp.hpp:279
void for_each_step(const function_ref< void(const Variable< Scalar > &t, const VariableMatrix< Scalar > &x, const VariableMatrix< Scalar > &u, const Variable< Scalar > &dt)> callback)
Definition ocp.hpp:200
Definition problem.hpp:71
Definition variable_matrix.hpp:37
VariableBlock< VariableMatrix > col(int col)
Definition variable_matrix.hpp:486
Definition variable.hpp:55
Definition function_ref.hpp:13
Definition concepts.hpp:18
Definition concepts.hpp:24