Sleipnir C++ API
Loading...
Searching...
No Matches
OCPSolver.hpp
Go to the documentation of this file.
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <stdint.h>
6
7#include <chrono>
8#include <utility>
9
16
17namespace sleipnir {
18
28template <typename F, typename State, typename Input, typename Time>
30 auto halfdt = dt * 0.5;
31 State k1 = f(t0, x, u, dt);
32 State k2 = f(t0 + halfdt, x + k1 * halfdt, u, dt);
33 State k3 = f(t0 + halfdt, x + k2 * halfdt, u, dt);
34 State k4 = f(t0 + dt, x + k3 * dt, u, dt);
35
36 return x + (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (dt / 6.0);
37}
38
53
57enum class DynamicsType : uint8_t {
62};
63
67enum class TimestepMethod : uint8_t {
69 kFixed,
75};
76
104 public:
123 int numStates, int numInputs, std::chrono::duration<double> dt,
124 int numSteps,
126 const VariableMatrix& u)>
127 dynamics,
128 DynamicsType dynamicsType = DynamicsType::kExplicitODE,
129 TimestepMethod timestepMethod = TimestepMethod::kFixed,
130 TranscriptionMethod method = TranscriptionMethod::kDirectTranscription)
131 : OCPSolver{numStates,
132 numInputs,
133 dt,
134 numSteps,
135 [=]([[maybe_unused]] const VariableMatrix& t,
136 const VariableMatrix& x, const VariableMatrix& u,
137 [[maybe_unused]]
138 const VariableMatrix& dt) -> VariableMatrix {
139 return dynamics(x, u);
140 },
143 method} {}
144
163 int numStates, int numInputs, std::chrono::duration<double> dt,
164 int numSteps,
166 const VariableMatrix& u, const Variable& dt)>
167 dynamics,
168 DynamicsType dynamicsType = DynamicsType::kExplicitODE,
169 TimestepMethod timestepMethod = TimestepMethod::kFixed,
170 TranscriptionMethod method = TranscriptionMethod::kDirectTranscription)
171 : m_numStates{numStates},
172 m_numInputs{numInputs},
173 m_dt{dt},
174 m_numSteps{numSteps},
175 m_transcriptionMethod{method},
176 m_dynamicsType{dynamicsType},
177 m_dynamicsFunction{std::move(dynamics)},
178 m_timestepMethod{timestepMethod} {
179 // u is numSteps + 1 so that the final constraintFunction evaluation works
180 m_U = DecisionVariable(m_numInputs, m_numSteps + 1);
181
182 if (m_timestepMethod == TimestepMethod::kFixed) {
183 m_DT = VariableMatrix{1, m_numSteps + 1};
184 for (int i = 0; i < numSteps + 1; ++i) {
185 m_DT(0, i) = m_dt.count();
186 }
187 } else if (m_timestepMethod == TimestepMethod::kVariableSingle) {
188 Variable DT = DecisionVariable();
189 DT.SetValue(m_dt.count());
190
191 // Set the member variable matrix to track the decision variable
192 m_DT = VariableMatrix{1, m_numSteps + 1};
193 for (int i = 0; i < numSteps + 1; ++i) {
194 m_DT(0, i) = DT;
195 }
196 } else if (m_timestepMethod == TimestepMethod::kVariable) {
197 m_DT = DecisionVariable(1, m_numSteps + 1);
198 for (int i = 0; i < numSteps + 1; ++i) {
199 m_DT(0, i).SetValue(m_dt.count());
200 }
201 }
202
203 if (m_transcriptionMethod == TranscriptionMethod::kDirectTranscription) {
204 m_X = DecisionVariable(m_numStates, m_numSteps + 1);
205 ConstrainDirectTranscription();
206 } else if (m_transcriptionMethod ==
207 TranscriptionMethod::kDirectCollocation) {
208 m_X = DecisionVariable(m_numStates, m_numSteps + 1);
209 ConstrainDirectCollocation();
210 } else if (m_transcriptionMethod == TranscriptionMethod::kSingleShooting) {
211 // In single-shooting the states aren't decision variables, but instead
212 // depend on the input and previous states
213 m_X = VariableMatrix{m_numStates, m_numSteps + 1};
214 ConstrainSingleShooting();
215 }
216 }
217
223 template <typename T>
224 requires ScalarLike<T> || MatrixLike<T>
225 void ConstrainInitialState(const T& initialState) {
226 SubjectTo(InitialState() == initialState);
227 }
228
234 template <typename T>
235 requires ScalarLike<T> || MatrixLike<T>
236 void ConstrainFinalState(const T& finalState) {
237 SubjectTo(FinalState() == finalState);
238 }
239
249 const function_ref<void(const VariableMatrix& x, const VariableMatrix& u)>
250 callback) {
251 for (int i = 0; i < m_numSteps + 1; ++i) {
252 auto x = X().Col(i);
253 auto u = U().Col(i);
254 callback(x, u);
255 }
256 }
257
267 const function_ref<void(const Variable& t, const VariableMatrix& x,
268 const VariableMatrix& u, const Variable& dt)>
269 callback) {
270 Variable time = 0.0;
271
272 for (int i = 0; i < m_numSteps + 1; ++i) {
273 auto x = X().Col(i);
274 auto u = U().Col(i);
275 auto dt = DT()(0, i);
276 callback(time, x, u, dt);
277
278 time += dt;
279 }
280 }
281
288 template <typename T>
289 requires ScalarLike<T> || MatrixLike<T>
290 void SetLowerInputBound(const T& lowerBound) {
291 for (int i = 0; i < m_numSteps + 1; ++i) {
292 SubjectTo(U().Col(i) >= lowerBound);
293 }
294 }
295
302 template <typename T>
303 requires ScalarLike<T> || MatrixLike<T>
304 void SetUpperInputBound(const T& upperBound) {
305 for (int i = 0; i < m_numSteps + 1; ++i) {
306 SubjectTo(U().Col(i) <= upperBound);
307 }
308 }
309
315 void SetMinTimestep(std::chrono::duration<double> minTimestep) {
316 SubjectTo(DT() >= minTimestep.count());
317 }
318
324 void SetMaxTimestep(std::chrono::duration<double> maxTimestep) {
325 SubjectTo(DT() <= maxTimestep.count());
326 }
327
336 VariableMatrix& X() { return m_X; };
337
347 VariableMatrix& U() { return m_U; };
348
358 VariableMatrix& DT() { return m_DT; };
359
365 VariableMatrix InitialState() { return m_X.Col(0); }
366
372 VariableMatrix FinalState() { return m_X.Col(m_numSteps); }
373
374 private:
375 void ConstrainDirectCollocation() {
376 Assert(m_dynamicsType == DynamicsType::kExplicitODE);
377
378 Variable time = 0.0;
379
380 // Derivation at https://mec560sbu.github.io/2016/09/30/direct_collocation/
381 for (int i = 0; i < m_numSteps; ++i) {
382 Variable h = DT()(0, i);
383
384 auto& f = m_dynamicsFunction;
385
386 auto t_begin = time;
387 auto t_end = t_begin + h;
388
389 auto x_begin = X().Col(i);
390 auto x_end = X().Col(i + 1);
391
392 auto u_begin = U().Col(i);
393 auto u_end = U().Col(i + 1);
394
395 auto xdot_begin = f(t_begin, x_begin, u_begin, h);
396 auto xdot_end = f(t_end, x_end, u_end, h);
397 auto xdot_c =
398 -3 / (2 * h) * (x_begin - x_end) - 0.25 * (xdot_begin + xdot_end);
399
400 auto t_c = t_begin + 0.5 * h;
401 auto x_c = 0.5 * (x_begin + x_end) + h / 8 * (xdot_begin - xdot_end);
402 auto u_c = 0.5 * (u_begin + u_end);
403
404 SubjectTo(xdot_c == f(t_c, x_c, u_c, h));
405
406 time += h;
407 }
408 }
409
410 void ConstrainDirectTranscription() {
411 Variable time = 0.0;
412
413 for (int i = 0; i < m_numSteps; ++i) {
414 auto x_begin = X().Col(i);
415 auto x_end = X().Col(i + 1);
416 auto u = U().Col(i);
417 Variable dt = DT()(0, i);
418
419 if (m_dynamicsType == DynamicsType::kExplicitODE) {
420 SubjectTo(x_end == RK4<const decltype(m_dynamicsFunction)&,
421 VariableMatrix, VariableMatrix, Variable>(
422 m_dynamicsFunction, x_begin, u, time, dt));
423 } else if (m_dynamicsType == DynamicsType::kDiscrete) {
424 SubjectTo(x_end == m_dynamicsFunction(time, x_begin, u, dt));
425 }
426
427 time += dt;
428 }
429 }
430
431 void ConstrainSingleShooting() {
432 Variable time = 0.0;
433
434 for (int i = 0; i < m_numSteps; ++i) {
435 auto x_begin = X().Col(i);
436 auto x_end = X().Col(i + 1);
437 auto u = U().Col(i);
438 Variable dt = DT()(0, i);
439
440 if (m_dynamicsType == DynamicsType::kExplicitODE) {
441 x_end = RK4<const decltype(m_dynamicsFunction)&, VariableMatrix,
442 VariableMatrix, Variable>(m_dynamicsFunction, x_begin, u,
443 time, dt);
444 } else if (m_dynamicsType == DynamicsType::kDiscrete) {
445 x_end = m_dynamicsFunction(time, x_begin, u, dt);
446 }
447
448 time += dt;
449 }
450 }
451
452 int m_numStates;
453 int m_numInputs;
454 std::chrono::duration<double> m_dt;
455 int m_numSteps;
456 TranscriptionMethod m_transcriptionMethod;
457
458 DynamicsType m_dynamicsType;
459
460 function_ref<VariableMatrix(const Variable& t, const VariableMatrix& x,
461 const VariableMatrix& u, const Variable& dt)>
462 m_dynamicsFunction;
463
464 TimestepMethod m_timestepMethod;
465
466 VariableMatrix m_X;
467 VariableMatrix m_U;
468 VariableMatrix m_DT;
469};
470
471} // namespace sleipnir
#define Assert(condition)
Definition Assert.hpp:24
#define SLEIPNIR_DLLEXPORT
Definition SymbolExports.hpp:34
Definition OCPSolver.hpp:103
void ConstrainInitialState(const T &initialState)
Definition OCPSolver.hpp:225
void SetMinTimestep(std::chrono::duration< double > minTimestep)
Definition OCPSolver.hpp:315
VariableMatrix InitialState()
Definition OCPSolver.hpp:365
VariableMatrix & U()
Definition OCPSolver.hpp:347
void SetMaxTimestep(std::chrono::duration< double > maxTimestep)
Definition OCPSolver.hpp:324
void ForEachStep(const function_ref< void(const VariableMatrix &x, const VariableMatrix &u)> callback)
Definition OCPSolver.hpp:248
VariableMatrix & DT()
Definition OCPSolver.hpp:358
void ConstrainFinalState(const T &finalState)
Definition OCPSolver.hpp:236
void SetUpperInputBound(const T &upperBound)
Definition OCPSolver.hpp:304
VariableMatrix FinalState()
Definition OCPSolver.hpp:372
void ForEachStep(const function_ref< void(const Variable &t, const VariableMatrix &x, const VariableMatrix &u, const Variable &dt)> callback)
Definition OCPSolver.hpp:266
void SetLowerInputBound(const T &lowerBound)
Definition OCPSolver.hpp:290
OCPSolver(int numStates, int numInputs, std::chrono::duration< double > dt, int numSteps, function_ref< VariableMatrix(const VariableMatrix &x, const VariableMatrix &u)> dynamics, DynamicsType dynamicsType=DynamicsType::kExplicitODE, TimestepMethod timestepMethod=TimestepMethod::kFixed, TranscriptionMethod method=TranscriptionMethod::kDirectTranscription)
Definition OCPSolver.hpp:122
VariableMatrix & X()
Definition OCPSolver.hpp:336
OCPSolver(int numStates, int numInputs, std::chrono::duration< double > dt, int numSteps, function_ref< VariableMatrix(const Variable &t, const VariableMatrix &x, const VariableMatrix &u, const Variable &dt)> dynamics, DynamicsType dynamicsType=DynamicsType::kExplicitODE, TimestepMethod timestepMethod=TimestepMethod::kFixed, TranscriptionMethod method=TranscriptionMethod::kDirectTranscription)
Definition OCPSolver.hpp:162
Definition OptimizationProblem.hpp:51
Definition VariableMatrix.hpp:28
VariableBlock< VariableMatrix > Col(int col)
Definition VariableMatrix.hpp:505
Definition Variable.hpp:33
void SetValue(double value)
Definition Variable.hpp:92
Definition FunctionRef.hpp:17
Definition Concepts.hpp:28
Definition Concepts.hpp:12
Definition Expression.hpp:18
IntrusiveSharedPtr< T > AllocateIntrusiveShared(Alloc alloc, Args &&... args)
Definition IntrusiveSharedPtr.hpp:275
DynamicsType
Definition OCPSolver.hpp:57
@ kExplicitODE
The dynamics are a function in the form dx/dt = f(t, x, u).
@ kDiscrete
The dynamics are a function in the form xₖ₊₁ = f(t, xₖ, uₖ).
TranscriptionMethod
Definition OCPSolver.hpp:42
TimestepMethod
Definition OCPSolver.hpp:67
@ kFixed
The timestep is a fixed constant.
@ kVariable
The timesteps are allowed to vary as independent decision variables.
State RK4(F &&f, State x, Input u, Time t0, Time dt)
Definition OCPSolver.hpp:29
function_ref(R(*)(Args...)) -> function_ref< R(Args...)>