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
50template <typename Scalar>
51class OCP : public Problem<Scalar> {
52 public:
70 OCP(int num_states, int num_inputs, std::chrono::duration<Scalar> dt,
71 int num_steps,
75 DynamicsType dynamics_type = DynamicsType::EXPLICIT_ODE,
76 TimestepMethod timestep_method = TimestepMethod::FIXED,
77 TranscriptionMethod transcription_method =
78 TranscriptionMethod::DIRECT_TRANSCRIPTION)
81 dt,
83 [=]([[maybe_unused]] const VariableMatrix<Scalar>& t,
84 const VariableMatrix<Scalar>& x,
85 const VariableMatrix<Scalar>& u,
87 -> VariableMatrix<Scalar> { return dynamics(x, u); },
91
109 OCP(int num_states, int num_inputs, std::chrono::duration<Scalar> dt,
110 int num_steps,
113 const VariableMatrix<Scalar>& u, const Variable<Scalar>& dt)>
114 dynamics,
115 DynamicsType dynamics_type = DynamicsType::EXPLICIT_ODE,
116 TimestepMethod timestep_method = TimestepMethod::FIXED,
117 TranscriptionMethod transcription_method =
118 TranscriptionMethod::DIRECT_TRANSCRIPTION)
119 : m_num_steps{num_steps},
120 m_dynamics{std::move(dynamics)},
121 m_dynamics_type{dynamics_type} {
122 // u is num_steps + 1 so that the final constraint function evaluation works
123 m_U = this->decision_variable(num_inputs, m_num_steps + 1);
124
125 if (timestep_method == TimestepMethod::FIXED) {
126 m_DT = VariableMatrix<Scalar>{1, m_num_steps + 1};
127 for (int i = 0; i < num_steps + 1; ++i) {
128 m_DT[0, i] = dt.count();
129 }
130 } else if (timestep_method == TimestepMethod::VARIABLE_SINGLE) {
131 Variable single_dt = this->decision_variable();
132 single_dt.set_value(dt.count());
133
134 // Set the member variable matrix to track the decision variable
135 m_DT = VariableMatrix<Scalar>{1, m_num_steps + 1};
136 for (int i = 0; i < num_steps + 1; ++i) {
137 m_DT[0, i] = single_dt;
138 }
139 } else if (timestep_method == TimestepMethod::VARIABLE) {
140 m_DT = this->decision_variable(1, m_num_steps + 1);
141 for (int i = 0; i < num_steps + 1; ++i) {
142 m_DT[0, i].set_value(dt.count());
143 }
144 }
145
146 if (transcription_method == TranscriptionMethod::DIRECT_TRANSCRIPTION) {
147 m_X = this->decision_variable(num_states, m_num_steps + 1);
148 constrain_direct_transcription();
149 } else if (transcription_method ==
150 TranscriptionMethod::DIRECT_COLLOCATION) {
151 m_X = this->decision_variable(num_states, m_num_steps + 1);
152 constrain_direct_collocation();
153 } else if (transcription_method == TranscriptionMethod::SINGLE_SHOOTING) {
154 // In single-shooting the states aren't decision variables, but instead
155 // depend on the input and previous states
156 m_X = VariableMatrix<Scalar>{num_states, m_num_steps + 1};
157 constrain_single_shooting();
158 }
159 }
160
166 template <typename T>
167 requires ScalarLike<T> || MatrixLike<T>
168 void constrain_initial_state(const T& initial_state) {
169 this->subject_to(this->initial_state() == initial_state);
170 }
171
177 template <typename T>
178 requires ScalarLike<T> || MatrixLike<T>
179 void constrain_final_state(const T& final_state) {
180 this->subject_to(this->final_state() == final_state);
181 }
182
193 callback) {
194 for (int i = 0; i < m_num_steps + 1; ++i) {
195 auto x = X().col(i);
196 auto u = U().col(i);
197 callback(x, u);
198 }
199 }
200
210 const function_ref<
211 void(const Variable<Scalar>& t, const VariableMatrix<Scalar>& x,
212 const VariableMatrix<Scalar>& u, const Variable<Scalar>& dt)>
213 callback) {
215
216 for (int i = 0; i < m_num_steps + 1; ++i) {
217 auto x = X().col(i);
218 auto u = U().col(i);
219 auto dt = this->dt()[0, i];
220 callback(time, x, u, dt);
221
222 time += dt;
223 }
224 }
225
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) >= lower_bound);
237 }
238 }
239
246 template <typename T>
247 requires ScalarLike<T> || MatrixLike<T>
249 for (int i = 0; i < m_num_steps + 1; ++i) {
250 this->subject_to(U().col(i) <= upper_bound);
251 }
252 }
253
259 void set_min_timestep(std::chrono::duration<Scalar> min_timestep) {
260 this->subject_to(dt() >= min_timestep.count());
261 }
262
268 void set_max_timestep(std::chrono::duration<Scalar> max_timestep) {
269 this->subject_to(dt() <= max_timestep.count());
270 }
271
280 VariableMatrix<Scalar>& X() { return m_X; }
281
291 VariableMatrix<Scalar>& U() { return m_U; }
292
302 VariableMatrix<Scalar>& dt() { return m_DT; }
303
310
316 VariableMatrix<Scalar> final_state() { return m_X.col(m_num_steps); }
317
318 private:
319 int m_num_steps;
320
323 const VariableMatrix<Scalar>& u, const Variable<Scalar>& dt)>
324 m_dynamics;
325 DynamicsType m_dynamics_type;
326
330
340 template <typename F, typename State, typename Input, typename Time>
341 State rk4(F&& f, State x, Input u, Time t0, Time dt) {
342 auto halfdt = dt * Scalar(0.5);
343 State k1 = f(t0, x, u, dt);
344 State k2 = f(t0 + halfdt, x + k1 * halfdt, u, dt);
345 State k3 = f(t0 + halfdt, x + k2 * halfdt, u, dt);
346 State k4 = f(t0 + dt, x + k3 * dt, u, dt);
347
348 return x + (k1 + k2 * Scalar(2) + k3 * Scalar(2) + k4) * (dt / Scalar(6));
349 }
350
354 void constrain_direct_collocation() {
355 slp_assert(m_dynamics_type == DynamicsType::EXPLICIT_ODE);
356
357 Variable<Scalar> time{0};
358
359 // Derivation at https://mec560sbu.github.io/2016/09/30/direct_collocation/
360 for (int i = 0; i < m_num_steps; ++i) {
361 Variable h = dt()[0, i];
362
363 auto& f = m_dynamics;
364
365 auto t_begin = time;
366 auto t_end = t_begin + h;
367
368 auto x_begin = X().col(i);
369 auto x_end = X().col(i + 1);
370
371 auto u_begin = U().col(i);
372 auto u_end = U().col(i + 1);
373
374 auto xdot_begin = f(t_begin, x_begin, u_begin, h);
375 auto xdot_end = f(t_end, x_end, u_end, h);
376 auto xdot_c = Scalar(-3) / (Scalar(2) * h) * (x_begin - x_end) -
377 Scalar(0.25) * (xdot_begin + xdot_end);
378
379 auto t_c = t_begin + Scalar(0.5) * h;
380 auto x_c = Scalar(0.5) * (x_begin + x_end) +
381 h / Scalar(8) * (xdot_begin - xdot_end);
382 auto u_c = Scalar(0.5) * (u_begin + u_end);
383
384 this->subject_to(xdot_c == f(t_c, x_c, u_c, h));
385
386 time += h;
387 }
388 }
389
393 void constrain_direct_transcription() {
394 Variable<Scalar> time{0};
395
396 for (int i = 0; i < m_num_steps; ++i) {
397 auto x_begin = X().col(i);
398 auto x_end = X().col(i + 1);
399 auto u = U().col(i);
400 Variable dt = this->dt()[0, i];
401
402 if (m_dynamics_type == DynamicsType::EXPLICIT_ODE) {
403 this->subject_to(
404 x_end == rk4<const decltype(m_dynamics)&, VariableMatrix<Scalar>,
405 VariableMatrix<Scalar>, Variable<Scalar>>(
406 m_dynamics, x_begin, u, time, dt));
407 } else if (m_dynamics_type == DynamicsType::DISCRETE) {
408 this->subject_to(x_end == m_dynamics(time, x_begin, u, dt));
409 }
410
411 time += dt;
412 }
413 }
414
418 void constrain_single_shooting() {
419 Variable<Scalar> time{0};
420
421 for (int i = 0; i < m_num_steps; ++i) {
422 auto x_begin = X().col(i);
423 auto x_end = X().col(i + 1);
424 auto u = U().col(i);
425 Variable dt = this->dt()[0, i];
426
427 if (m_dynamics_type == DynamicsType::EXPLICIT_ODE) {
428 x_end = rk4<const decltype(m_dynamics)&, VariableMatrix<Scalar>,
429 VariableMatrix<Scalar>, Variable<Scalar>>(
430 m_dynamics, x_begin, u, time, dt);
431 } else if (m_dynamics_type == DynamicsType::DISCRETE) {
432 x_end = m_dynamics(time, x_begin, u, dt);
433 }
434
435 time += dt;
436 }
437 }
438};
439
440extern template class EXPORT_TEMPLATE_DECLARE(SLEIPNIR_DLLEXPORT) OCP<double>;
441
442} // namespace slp
Definition intrusive_shared_ptr.hpp:29
Definition ocp.hpp:51
void constrain_initial_state(const T &initial_state)
Definition ocp.hpp:168
void constrain_final_state(const T &final_state)
Definition ocp.hpp:179
VariableMatrix< Scalar > & U()
Definition ocp.hpp:291
VariableMatrix< Scalar > initial_state()
Definition ocp.hpp:309
void set_min_timestep(std::chrono::duration< Scalar > min_timestep)
Definition ocp.hpp:259
VariableMatrix< Scalar > & X()
Definition ocp.hpp:280
void set_upper_input_bound(const T &upper_bound)
Definition ocp.hpp:248
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:109
void set_lower_input_bound(const T &lower_bound)
Definition ocp.hpp:234
void for_each_step(const function_ref< void(const VariableMatrix< Scalar > &x, const VariableMatrix< Scalar > &u)> callback)
Definition ocp.hpp:191
void set_max_timestep(std::chrono::duration< Scalar > max_timestep)
Definition ocp.hpp:268
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:70
VariableMatrix< Scalar > final_state()
Definition ocp.hpp:316
VariableMatrix< Scalar > & dt()
Definition ocp.hpp:302
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:209
Definition problem.hpp:66
Definition variable_matrix.hpp:35
VariableBlock< VariableMatrix > col(int col)
Definition variable_matrix.hpp:553
Definition variable.hpp:49
Definition function_ref.hpp:13
Definition concepts.hpp:18
Definition concepts.hpp:24