Skip to content

Commit 1bdaafb

Browse files
casadi examples and sqp solver created (#78)
* Add casadi baselines for cartpole and unicycle * acado car parking result * Modify ipopt scripts * Update sqp_core and test sqp_core * Add sqp_unicycle
1 parent 7f1d5e6 commit 1bdaafb

File tree

20 files changed

+1738
-1132
lines changed

20 files changed

+1738
-1132
lines changed

CMakeLists.txt

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ set(CMAKE_BUILD_TYPE "Release") # Set the build type to Release by default
2929
option(CDDP_CPP_BUILD_TESTS "Whether to build tests." ON)
3030

3131
# SQP Configuration
32-
option(CDDP_CPP_SQP "Whether to use SQP solver" OFF)
32+
option(CDDP_CPP_SQP "Whether to use SQP solver" ON)
3333

3434
# CasADi Configuration
35-
option(CDDP_CPP_CASADI "Whether to use CasADi" OFF)
35+
option(CDDP_CPP_CASADI "Whether to use CasADi" ON)
36+
37+
# Acado Configuration
38+
option(CDDP_CPP_ACADO "Whether to use Acado" OFF)
3639

3740
# Gurobi Configuration
3841
option(CDDP_CPP_GUROBI "Whether to use Gurobi solver." OFF)
@@ -68,12 +71,21 @@ if (CDDP_CPP_CASADI)
6871
# make
6972
# sudo make install
7073
# echo "export LD_LIBRARY_PATH=/home/<path_to_casadi>/casadi/build/lib:$LD_LIBRARY_PATH" >> ~/.bashrc && source ~/.bashrc
74+
7175
find_package(casadi REQUIRED)
72-
set(CASADI_INCLUDE_DIR /usr/local/include/casadi)
76+
set(CASADI_INCLUDE_DIR /$ENV{HOME}/local/include/casadi)
7377
find_library(CASADI_LIBRARY NAMES casadi HINTS ${CASADI_INCLUDE_DIR}/../lib $ENV{CASADI_PREFIX}/lib)
7478
set(CASADI_LIBRARIES ${CASADI_LIBRARIES} ${CASADI_LIBRARY})
7579
endif()
7680

81+
if (CDDP_CPP_ACADO)
82+
# Assuming that Acado is installed by: https://acado.github.io/install_linux.html
83+
set(ACADO_DIR $ENV{HOME}/github/ACADOtoolkit)
84+
# Include ACADO header files
85+
include_directories(${ACADO_DIR})
86+
include_directories(${ACADO_DIR}/acado)
87+
endif()
88+
7789
# Enable FetchContent for downloading dependencies
7890
include(FetchContent)
7991

@@ -252,25 +264,20 @@ if(TORCH_FOUND AND CDDP_CPP_TORCH_GPU)
252264
set_property(TARGET ${PROJECT_NAME} PROPERTY CUDA_ARCHITECTURES native)
253265
endif()
254266

255-
if (CDDP_CPP_SQP)
256-
# OSQP-CPP
257-
FetchContent_Declare(
258-
osqp-cpp
259-
GIT_REPOSITORY https://github.com/astomodynamics/osqp-cpp.git
260-
GIT_TAG master
261-
)
262-
FetchContent_MakeAvailable(osqp-cpp)
263-
FetchContent_GetProperties(osqp-cpp)
264-
target_link_libraries(${PROJECT_NAME} osqp-cpp)
267+
if (CDDP_CPP_SQP AND CDDP_CPP_CASADI)
265268
# add sqp solver to cddp
266-
target_sources(${PROJECT_NAME} PRIVATE src/sqp_core/sqp.cpp)
269+
target_sources(${PROJECT_NAME} PRIVATE src/sqp_core/sqp_core.cpp)
267270
endif()
268271

269272
if (CDDP_CPP_CASADI)
270273
target_include_directories(${PROJECT_NAME} PUBLIC ${CASADI_INCLUDE_DIR})
271274
target_link_libraries(${PROJECT_NAME} ${CASADI_LIBRARIES})
272275
endif()
273276

277+
if (CDDP_CPP_ACADO)
278+
target_link_libraries(${PROJECT_NAME} ${ACADO_DIR}/build/lib/libacado_toolkit_s.so)
279+
endif()
280+
274281
# Gurobi
275282
if (CDDP_CPP_GUROBI)
276283
if (NOT GUROBI_ROOT)

examples/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@ target_link_libraries(mpc_hcw cddp)
5050
if (CDDP_CPP_CASADI)
5151
add_executable(ipopt_car ipopt_car.cpp)
5252
target_link_libraries(ipopt_car cddp)
53+
54+
add_executable(ipopt_unicycle ipopt_unicycle.cpp)
55+
target_link_libraries(ipopt_unicycle cddp)
56+
57+
add_executable(ipopt_cartpole ipopt_cartpole.cpp)
58+
target_link_libraries(ipopt_cartpole cddp)
59+
endif()
60+
61+
if (CDDP_CPP_CASADI AND CDDP_CPP_SQP)
62+
add_executable(sqp_unicycle sqp_unicycle.cpp)
63+
target_link_libraries(sqp_unicycle cddp)
64+
endif()
65+
66+
# Acado examples
67+
if (CDDP_CPP_ACADO)
68+
add_executable(acado_car acado_car.cpp)
69+
target_link_libraries(acado_car cddp)
70+
71+
# add_executable(acado_unicycle acado_unicycle.cpp)
72+
# target_link_libraries(acado_unicycle cddp)
73+
74+
# add_executable(acado_cartpole acado_cartpole.cpp)
75+
# target_link_libraries(acado_cartpole cddp)
5376
endif()
5477

5578
# Neural dynamics examples

examples/acado_car.cpp

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Copyright 2024 Tomo Sasaki
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <acado/acado_toolkit.hpp>
18+
#include <acado/acado_optimal_control.hpp>
19+
#include <Eigen/Dense>
20+
#include <iostream>
21+
#include <vector>
22+
#include <chrono>
23+
#include <cmath>
24+
#include "animation.hpp"
25+
#include "matplotlibcpp.hpp"
26+
27+
namespace plt = matplotlibcpp;
28+
using namespace ACADO;
29+
30+
//---------------------------------------------------------------------
31+
// Helper function: Plot a car box using matplotlibcpp and Eigen
32+
//---------------------------------------------------------------------
33+
void plotCarBox(const Eigen::VectorXd& state, const Eigen::VectorXd& control,
34+
double length, double width, const std::string& color) {
35+
double x = state(0);
36+
double y = state(1);
37+
double theta = state(2);
38+
39+
// Compute car corners (polygon)
40+
std::vector<double> car_x(5), car_y(5);
41+
42+
// Front right
43+
car_x[0] = x + length/2 * cos(theta) - width/2 * sin(theta);
44+
car_y[0] = y + length/2 * sin(theta) + width/2 * cos(theta);
45+
46+
// Front left
47+
car_x[1] = x + length/2 * cos(theta) + width/2 * sin(theta);
48+
car_y[1] = y + length/2 * sin(theta) - width/2 * cos(theta);
49+
50+
// Rear left
51+
car_x[2] = x - length/2 * cos(theta) + width/2 * sin(theta);
52+
car_y[2] = y - length/2 * sin(theta) - width/2 * cos(theta);
53+
54+
// Rear right
55+
car_x[3] = x - length/2 * cos(theta) - width/2 * sin(theta);
56+
car_y[3] = y - length/2 * sin(theta) + width/2 * cos(theta);
57+
58+
// Close the polygon
59+
car_x[4] = car_x[0];
60+
car_y[4] = car_y[0];
61+
62+
// Plot the car body
63+
std::map<std::string, std::string> keywords;
64+
keywords["color"] = color;
65+
plt::plot(car_x, car_y, keywords);
66+
67+
// Plot the base point
68+
std::vector<double> base_x = {x};
69+
std::vector<double> base_y = {y};
70+
keywords["color"] = "red";
71+
keywords["marker"] = "o";
72+
plt::plot(base_x, base_y, keywords);
73+
}
74+
75+
//---------------------------------------------------------------------
76+
// Main function using ACADO to solve the car parking problem
77+
//---------------------------------------------------------------------
78+
int main() {
79+
// Problem parameters
80+
const int state_dim = 4; // [x, y, theta, v]
81+
const int control_dim = 2; // [steering angle, acceleration]
82+
const int horizon = 500; // Discretization steps
83+
const double timestep = 0.03;
84+
const double T = horizon * timestep;
85+
const double wheelbase = 2.0;
86+
87+
// Define differential states and controls
88+
DifferentialState x, y, theta, v;
89+
Control delta, a;
90+
91+
// Define the dynamic system (differential equation)
92+
DifferentialEquation f(0.0, T);
93+
f << dot(x) == v * cos(theta);
94+
f << dot(y) == v * sin(theta);
95+
f << dot(theta) == v * tan(delta) / wheelbase;
96+
f << dot(v) == a;
97+
98+
// Set up OCP
99+
OCP ocp(0.0, T, horizon);
100+
101+
// Subject to the system dynamics
102+
ocp.subjectTo( f );
103+
104+
// Initial conditions (at t=0)
105+
ocp.subjectTo( AT_START, x == 1.0 );
106+
ocp.subjectTo( AT_START, y == 1.0 );
107+
ocp.subjectTo( AT_START, theta == 1.5 * M_PI );
108+
ocp.subjectTo( AT_START, v == 0.0 );
109+
110+
// (Loose) state bounds
111+
ocp.subjectTo( -1e20 <= x <= 1e20 );
112+
ocp.subjectTo( -1e20 <= y <= 1e20 );
113+
ocp.subjectTo( -1e20 <= theta <= 1e20 );
114+
ocp.subjectTo( -1e20 <= v <= 1e20 );
115+
116+
// Control bounds (steering angle and acceleration)
117+
ocp.subjectTo( -0.5 <= delta <= 0.5 );
118+
ocp.subjectTo( -2.0 <= a <= 2.0 );
119+
120+
// Define the cost (objective) terms.
121+
ocp.minimizeLagrangeTerm( 1e-2 * delta * delta + 1e-4 * a * a + 1e-3 * (x*x + y*y) );
122+
123+
// Terminal cost
124+
ocp.minimizeMayerTerm( 0.1 * (x*x + y*y) + 1.0 * (theta*theta) + 0.3 * (v*v) );
125+
126+
// Set up the optimization algorithm and options
127+
OptimizationAlgorithm algorithm(ocp);
128+
algorithm.set( MAX_NUM_ITERATIONS, 1000 );
129+
130+
// Solve the OCP while measuring solve time
131+
auto start_time = std::chrono::high_resolution_clock::now();
132+
algorithm.solve();
133+
auto end_time = std::chrono::high_resolution_clock::now();
134+
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
135+
std::cout << "Solve time: " << duration.count() << " microseconds" << std::endl;
136+
137+
// Retrieve the solution: state and control trajectories
138+
VariablesGrid stateGrid, controlGrid;
139+
algorithm.getDifferentialStates(stateGrid);
140+
algorithm.getControls(controlGrid);
141+
142+
std::cout << "Solution retrieved: "
143+
<< stateGrid.getNumPoints() << " states, "
144+
<< controlGrid.getNumPoints() << " controls." << std::endl;
145+
146+
// Extract state trajectory (for plotting/animation)
147+
std::vector<double> x_hist, y_hist;
148+
for (unsigned int i = 0; i < stateGrid.getNumPoints(); i++) {
149+
DVector state = stateGrid.getVector(i); // state order: [x, y, theta, v]
150+
x_hist.push_back(state(0));
151+
y_hist.push_back(state(1));
152+
}
153+
154+
// Animation setup (using your custom Animation class)
155+
Animation::AnimationConfig config;
156+
config.width = 800;
157+
config.height = 800;
158+
config.frame_skip = 5;
159+
config.frame_delay = 10;
160+
Animation animation(config);
161+
162+
double car_length = 2.1;
163+
double car_width = 0.9;
164+
Eigen::VectorXd empty_control = Eigen::VectorXd::Zero(control_dim);
165+
166+
// For each time step, plot the trajectory and the car configuration.
167+
for (unsigned int i = 0; i < stateGrid.getNumPoints(); i++) {
168+
DVector state = stateGrid.getVector(i);
169+
Eigen::VectorXd eigenState(state.getDim());
170+
for (int j = 0; j < state.getDim(); j++) {
171+
eigenState(j) = state(j);
172+
}
173+
animation.newFrame();
174+
plt::plot(x_hist, y_hist, "b-");
175+
176+
// Define goal state (same as in your CasADi example)
177+
Eigen::VectorXd goal_state(state_dim);
178+
goal_state << 0.0, 0.0, 0.0, 0.0;
179+
plotCarBox(goal_state, empty_control, car_length, car_width, "r");
180+
plotCarBox(eigenState, empty_control, car_length, car_width, "k");
181+
182+
plt::grid(true);
183+
plt::xlim(-4, 4);
184+
plt::ylim(-4, 4);
185+
animation.saveFrame(i);
186+
}
187+
animation.createGif("car_parking_acado.gif");
188+
189+
return 0;
190+
}

examples/cddp_car.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ class CarParkingObjective : public NonlinearObjective {
5252
return cf_.dot(sabs(final_state, pf_)) + running_cost(final_state, Eigen::VectorXd::Zero(2), 0);
5353
}
5454

55-
const Eigen::VectorXd& getReferenceState() const override {
56-
return reference_state_;
57-
}
58-
5955
private:
6056
// Helper function for smooth absolute value (pseudo-Huber)
6157
Eigen::VectorXd sabs(const Eigen::VectorXd& x, const Eigen::VectorXd& p) const {

examples/ipopt_car.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
Copyright 2024 Tomo Sasaki
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
116
#include <iostream>
217
#include <vector>
318
#include <chrono>

0 commit comments

Comments
 (0)