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