Skip to content

Commit a24cfba

Browse files
Refactor solver core and switch to typed CDDP solutions (#159)
This PR refactors the shared CDDP solver internals to reduce duplication across the CLDDP, LogDDP, IPDDP, and MSIPDDP implementations. It also replaces the string-keyed CDDPSolution map with a typed solution struct and updates examples and tests to consume the typed API directly.
1 parent a23add7 commit a24cfba

69 files changed

Lines changed: 1980 additions & 4093 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ set(cddp_core_srcs
139139
src/cddp_core/boxqp.cpp
140140
src/cddp_core/qp_solver.cpp
141141
src/cddp_core/cddp_core.cpp
142+
src/cddp_core/cddp_solver_base.cpp
142143
src/cddp_core/clddp_solver.cpp
143144
src/cddp_core/logddp_solver.cpp
144145
src/cddp_core/ipddp_solver.cpp

examples/cddp_acrobot.cpp

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <thread>
2323
#include <numeric>
2424
#include "cddp.hpp"
25+
#include "cddp_example_utils.hpp"
2526
#include "dynamics_model/acrobot.hpp"
2627
#include "matplot/matplot.h"
2728

@@ -105,24 +106,18 @@ int main() {
105106

106107
// Solve
107108
cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::IPDDP);
108-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
109-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
110-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
109+
const auto& X_sol = solution.state_trajectory;
110+
const auto& U_sol = solution.control_trajectory;
111+
const auto& t_sol = solution.time_points;
111112

112113
// Create plot directory
113114
const std::string plotDirectory = "../results/acrobot";
114-
if (!fs::exists(plotDirectory)) {
115-
fs::create_directories(plotDirectory);
116-
}
115+
cddp::example::ensurePlotDir(plotDirectory);
117116

118117
// Extract solution data for animation
119-
std::vector<double> time_arr, theta1_arr, theta2_arr;
120-
121-
for (size_t i = 0; i < X_sol.size(); ++i) {
122-
time_arr.push_back(t_sol[i]);
123-
theta1_arr.push_back(X_sol[i](0));
124-
theta2_arr.push_back(X_sol[i](1));
125-
}
118+
const auto& time_arr = t_sol;
119+
auto theta1_arr = cddp::example::extractComponent(X_sol, 0);
120+
auto theta2_arr = cddp::example::extractComponent(X_sol, 1);
126121

127122
// --- Animation ---
128123
auto fig = figure();

examples/cddp_bicycle.cpp

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <thread>
66
#include <algorithm>
77
#include "cddp.hpp"
8+
#include "cddp_example_utils.hpp"
89
#include "matplot/matplot.h"
910

1011
namespace fs = std::filesystem;
@@ -66,37 +67,26 @@ int main()
6667
cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::CLDDP);
6768

6869
// Extract solution
69-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
70-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
71-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
70+
const auto& X_sol = solution.state_trajectory;
71+
const auto& U_sol = solution.control_trajectory;
72+
const auto& t_sol = solution.time_points;
7273

7374
// Create directory for saving plots
7475
const std::string plotDirectory = "../results/tests";
75-
if (!fs::exists(plotDirectory))
76-
{
77-
fs::create_directory(plotDirectory);
78-
}
76+
cddp::example::ensurePlotDir(plotDirectory);
7977

8078
// Create a directory for frame images.
8179
(void) std::system("mkdir -p frames");
8280

8381
// Extract trajectory data
84-
std::vector<double> x_arr, y_arr, theta_arr, v_arr;
85-
for (const auto &x : X_sol)
86-
{
87-
x_arr.push_back(x(0));
88-
y_arr.push_back(x(1));
89-
theta_arr.push_back(x(2));
90-
v_arr.push_back(x(3));
91-
}
82+
auto x_arr = cddp::example::extractComponent(X_sol, 0);
83+
auto y_arr = cddp::example::extractComponent(X_sol, 1);
84+
auto theta_arr = cddp::example::extractComponent(X_sol, 2);
85+
auto v_arr = cddp::example::extractComponent(X_sol, 3);
9286

9387
// Extract control inputs
94-
std::vector<double> acc_arr, steering_arr;
95-
for (const auto &u : U_sol)
96-
{
97-
acc_arr.push_back(u(0));
98-
steering_arr.push_back(u(1));
99-
}
88+
auto acc_arr = cddp::example::extractComponent(U_sol, 0);
89+
auto steering_arr = cddp::example::extractComponent(U_sol, 1);
10090

10191
// -----------------------------
10292
// Plot states and controls

examples/cddp_car.cpp

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <random>
2020
#include <cmath>
2121
#include "cddp.hpp"
22+
#include "cddp_example_utils.hpp"
2223
#include "matplot/matplot.h"
2324

2425
using namespace matplot;
@@ -251,17 +252,13 @@ int main()
251252
// ========================================
252253

253254
// Extract solution trajectories
254-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
255-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
256-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
255+
const auto& X_sol = solution.state_trajectory;
256+
const auto& U_sol = solution.control_trajectory;
257+
const auto& t_sol = solution.time_points;
257258

258259
// Prepare trajectory data
259-
std::vector<double> x_hist, y_hist;
260-
for (const auto &x : X_sol)
261-
{
262-
x_hist.push_back(x(0));
263-
y_hist.push_back(x(1));
264-
}
260+
auto x_hist = cddp::example::extractComponent(X_sol, 0);
261+
auto y_hist = cddp::example::extractComponent(X_sol, 1);
265262

266263
// Car dimensions.
267264
double car_length = 2.1;
@@ -275,10 +272,7 @@ int main()
275272

276273
// Create directory for saving plots
277274
const std::string plotDirectory = "../results/tests";
278-
if (!fs::exists(plotDirectory))
279-
{
280-
fs::create_directory(plotDirectory);
281-
}
275+
cddp::example::ensurePlotDir(plotDirectory);
282276

283277
// Create a directory for frame images.
284278
(void) std::system("mkdir -p frames");

examples/cddp_car_ipddp.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <map>
2222
#include <string>
2323
#include "cddp.hpp"
24+
#include "cddp_example_utils.hpp"
2425
#include "matplot/matplot.h"
2526

2627
using namespace matplot;
@@ -204,16 +205,13 @@ int main() {
204205
cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::MSIPDDP);
205206

206207
// Extract solution trajectories
207-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
208-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
209-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
208+
const auto& X_sol = solution.state_trajectory;
209+
const auto& U_sol = solution.control_trajectory;
210+
const auto& t_sol = solution.time_points;
210211

211212
// Prepare trajectory data for plotting
212-
std::vector<double> x_hist, y_hist;
213-
for (const auto& state : X_sol) {
214-
x_hist.push_back(state(0));
215-
y_hist.push_back(state(1));
216-
}
213+
auto x_hist = cddp::example::extractComponent(X_sol, 0);
214+
auto y_hist = cddp::example::extractComponent(X_sol, 1);
217215
// Car dimensions.
218216
double car_length = 2.1;
219217
double car_width = 0.9;
@@ -226,10 +224,7 @@ int main() {
226224

227225
// Create directory for saving plots
228226
const std::string plotDirectory = "../results/tests";
229-
if (!fs::exists(plotDirectory))
230-
{
231-
fs::create_directory(plotDirectory);
232-
}
227+
cddp::example::ensurePlotDir(plotDirectory);
233228

234229
// Create a directory for frame images.
235230
(void) std::system("mkdir -p frames");

examples/cddp_cartpole.cpp

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <string>
2121
#include <memory>
2222
#include "cddp.hpp"
23+
#include "cddp_example_utils.hpp"
2324
#include "matplot/matplot.h"
2425
#include <random>
2526

@@ -99,60 +100,30 @@ int main() {
99100
// std::make_unique<cddp::ControlConstraint>( control_lower_bound, control_upper_bound));
100101

101102
// Initial trajectory.
102-
std::vector<Eigen::VectorXd> X(horizon + 1, Eigen::VectorXd::Zero(state_dim));
103-
std::vector<Eigen::VectorXd> U(horizon, Eigen::VectorXd::Zero(control_dim));
104-
// Generate initial trajectory by constant initial state
105-
for (int i = 0; i < horizon + 1; ++i) {
106-
X[i] = initial_state;
107-
}
108-
// // Generate initial trajectory by random
109-
// for (int i = 0; i < horizon + 1; ++i) {
110-
// std::random_device rd;
111-
// std::mt19937 gen(rd());
112-
// std::uniform_real_distribution<double> dist(-0.025, 0.025);
113-
// X[i] = initial_state;
114-
// X[i](0) += dist(gen);
115-
// X[i](1) += dist(gen);
116-
// X[i](2) += dist(gen);
117-
// X[i](3) += dist(gen);
118-
// }
119-
// for (int i = 0; i < horizon; ++i) {
120-
// std::random_device rd;
121-
// std::mt19937 gen(rd());
122-
// std::uniform_real_distribution<double> dist(-0.005, 0.005);
123-
// U[i] = Eigen::VectorXd::Zero(control_dim);
124-
// U[i](0) += dist(gen);
125-
// }
103+
auto [X, U] = cddp::example::makeInitialTrajectory(initial_state, horizon, control_dim);
126104
cddp_solver.setInitialTrajectory(X, U);
127105

128106
// Solve.
129107
cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::IPDDP);
130-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
131-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
132-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
108+
const auto& X_sol = solution.state_trajectory;
109+
const auto& U_sol = solution.control_trajectory;
110+
const auto& t_sol = solution.time_points;
133111

134112
// Create plot directory.
135113
const std::string plotDirectory = "../results/tests";
136-
if (!fs::exists(plotDirectory)) {
137-
fs::create_directory(plotDirectory);
138-
}
114+
cddp::example::ensurePlotDir(plotDirectory);
139115

140116
// Create a directory for frame images.
141117
(void) std::system("mkdir -p frames");
142118

143119
// Extract solution data.
144-
std::vector<double> x_arr, x_dot_arr, theta_arr, theta_dot_arr, force_arr, time_arr, time_arr2;
145-
for (size_t i = 0; i < X_sol.size(); ++i) {
146-
time_arr.push_back(t_sol[i]);
147-
x_arr.push_back(X_sol[i](0));
148-
theta_arr.push_back(X_sol[i](1));
149-
x_dot_arr.push_back(X_sol[i](2));
150-
theta_dot_arr.push_back(X_sol[i](3));
151-
}
152-
for (size_t i = 0; i < U_sol.size(); ++i) {
153-
force_arr.push_back(U_sol[i](0));
154-
time_arr2.push_back(t_sol[i]);
155-
}
120+
auto x_arr = cddp::example::extractComponent(X_sol, 0);
121+
auto theta_arr = cddp::example::extractComponent(X_sol, 1);
122+
auto x_dot_arr = cddp::example::extractComponent(X_sol, 2);
123+
auto theta_dot_arr = cddp::example::extractComponent(X_sol, 3);
124+
auto force_arr = cddp::example::extractComponent(U_sol, 0);
125+
const auto& time_arr = t_sol;
126+
std::vector<double> time_arr2(t_sol.begin(), t_sol.begin() + U_sol.size());
156127

157128
// --- Plot static results (2x2 plots for state trajectories) ---
158129
auto fig1 = figure();

examples/cddp_forklift_ipddp.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <map>
2222
#include <string>
2323
#include "cddp.hpp"
24+
#include "cddp_example_utils.hpp"
2425
#include "dynamics_model/forklift.hpp"
2526
#include "matplot/matplot.h"
2627
#include <random>
@@ -246,16 +247,13 @@ int main() {
246247
cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::MSIPDDP);
247248

248249
// Extract solution trajectories
249-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
250-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
251-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
250+
const auto& X_sol = solution.state_trajectory;
251+
const auto& U_sol = solution.control_trajectory;
252+
const auto& t_sol = solution.time_points;
252253

253254
// Prepare trajectory data for plotting
254-
std::vector<double> x_hist, y_hist;
255-
for (const auto& state : X_sol) {
256-
x_hist.push_back(state(0));
257-
y_hist.push_back(state(1));
258-
}
255+
auto x_hist = cddp::example::extractComponent(X_sol, 0);
256+
auto y_hist = cddp::example::extractComponent(X_sol, 1);
259257

260258
// Forklift dimensions
261259
double forklift_length = 2.5; // Overall length
@@ -269,10 +267,7 @@ int main() {
269267

270268
// Create directory for saving plots
271269
const std::string plotDirectory = "../results/tests";
272-
if (!fs::exists(plotDirectory))
273-
{
274-
fs::create_directories(plotDirectory);
275-
}
270+
cddp::example::ensurePlotDir(plotDirectory);
276271

277272
// Animation loop: update plot for each time step and save frame
278273
for (size_t i = 0; i < X_sol.size(); ++i)

examples/cddp_hcw.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <Eigen/Dense>
2727

2828
#include "cddp.hpp"
29+
#include "cddp_example_utils.hpp"
2930
#include "matplot/matplot.h"
3031

3132
using namespace matplot;
@@ -146,22 +147,18 @@ int main() {
146147
cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::CLDDP);
147148

148149
// Extract solution and print result
149-
auto cost_sequence = std::any_cast<std::vector<double>>(solution.at("cost_trajectory"));
150-
double J_final = cost_sequence.back();
151-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
152-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
153-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
154-
150+
const auto& X_sol = solution.state_trajectory;
151+
const auto& U_sol = solution.control_trajectory;
152+
const auto& t_sol = solution.time_points;
153+
155154
std::cout << "\n[Result] CDDP solved." << std::endl;
156-
std::cout << "[Result] Final cost: " << J_final << std::endl;
155+
std::cout << "[Result] Final cost: " << solution.final_objective << std::endl;
157156
std::cout << "[Result] Final state: "
158157
<< X_sol.back().transpose() << std::endl;
159158

160159
// Create plot directory
161160
const std::string plotDirectory = "../results/tests";
162-
if (!std::filesystem::exists(plotDirectory)) {
163-
std::filesystem::create_directory(plotDirectory);
164-
}
161+
cddp::example::ensurePlotDir(plotDirectory);
165162

166163
// Extract state data arrays
167164
std::vector<double> x_arr, y_arr, z_arr, vx_arr, vy_arr, vz_arr, time_arr;

examples/cddp_lti_system.cpp

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <filesystem>
44
#include <random>
55
#include "cddp.hpp"
6+
#include "cddp_example_utils.hpp"
67

78
#include "matplot/matplot.h"
89
using namespace matplot;
@@ -147,29 +148,25 @@ int main() {
147148
// Alternatively: cddp::CDDPSolution solution = cddp_solver.solve(cddp::SolverType::LogDDP);
148149

149150
// Extract solution trajectories
150-
auto X_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("state_trajectory"));
151-
auto U_sol = std::any_cast<std::vector<Eigen::VectorXd>>(solution.at("control_trajectory"));
152-
auto t_sol = std::any_cast<std::vector<double>>(solution.at("time_points"));
153-
auto cost_sequence = std::any_cast<std::vector<double>>(solution.at("cost_trajectory"));
154-
auto iterations = std::any_cast<int>(solution.at("iterations"));
155-
auto converged = std::any_cast<bool>(solution.at("converged"));
156-
auto solve_time = std::any_cast<long long>(solution.at("solve_time"));
151+
const auto& X_sol = solution.state_trajectory;
152+
const auto& U_sol = solution.control_trajectory;
153+
const auto& t_sol = solution.time_points;
154+
auto iterations = solution.iterations_completed;
155+
bool converged = (solution.status_message == "OptimalSolutionFound" || solution.status_message == "AcceptableSolutionFound");
157156

158157
// Create directory for plots if it doesn't exist
159158
const std::string plotDirectory = "../results/tests";
160-
if (!fs::exists(plotDirectory)) {
161-
fs::create_directories(plotDirectory);
162-
}
159+
cddp::example::ensurePlotDir(plotDirectory);
163160

164161
// Plot state trajectories using the matplot API
165162
plotStateTrajectories(X_sol, plotDirectory);
166163

167164
// Print optimization statistics
168165
std::cout << "\nOptimization Results:" << std::endl;
169166
std::cout << "Iterations: " << iterations << std::endl;
170-
std::cout << "Final cost: " << cost_sequence.back() << std::endl;
167+
std::cout << "Final cost: " << solution.final_objective << std::endl;
171168
std::cout << "Converged: " << (converged ? "Yes" : "No") << std::endl;
172-
std::cout << "Solve time: " << solve_time << " microseconds" << std::endl;
169+
std::cout << "Solve time: " << solution.solve_time_ms << " ms" << std::endl;
173170

174171
return 0;
175172
}

0 commit comments

Comments
 (0)