Skip to content

Commit a48fa9a

Browse files
Unicycle model and Dubins Car model (#74)
* Modify Dubins Car to Unicycle * Add dubins car model and example
1 parent 03ad6c7 commit a48fa9a

File tree

17 files changed

+763
-237
lines changed

17 files changed

+763
-237
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,11 @@ endif()
208208

209209
set(dynamics_model_srcs
210210
src/dynamics_model/pendulum.cpp
211-
src/dynamics_model/dubins_car.cpp
211+
src/dynamics_model/unicycle.cpp
212212
src/dynamics_model/bicycle.cpp
213213
src/dynamics_model/cartpole.cpp
214214
src/dynamics_model/car.cpp
215+
src/dynamics_model/dubins_car.cpp
215216
src/dynamics_model/quadrotor.cpp
216217
src/dynamics_model/manipulator.cpp
217218
src/dynamics_model/spacecraft_linear.cpp

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ $$
3737
$$
3838

3939
## Examples
40-
### Dubins Car
40+
### Unicycle
4141

4242
Simple car-like robot with velocity and steering control:
4343

4444
```bash
45-
./examples/cddp_dubins_car // after building
45+
./examples/cddp_unicycle // after building
4646
```
4747

48-
<img src="results/tests/dubins_car.gif" width="300" alt="Dubins Car CDDP">
48+
<img src="results/tests/unicycle.gif" width="300" alt="Unicycle Model CDDP">
4949

5050
### Bicycle Model
5151

examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ target_link_libraries(cddp_cartpole cddp)
2525
add_executable(cddp_dubins_car cddp_dubins_car.cpp)
2626
target_link_libraries(cddp_dubins_car cddp)
2727

28+
add_executable(cddp_unicycle cddp_unicycle.cpp)
29+
target_link_libraries(cddp_unicycle cddp)
30+
2831
add_executable(cddp_manipulator cddp_manipulator.cpp)
2932
target_link_libraries(cddp_manipulator cddp)
3033

examples/cddp_dubins_car.cpp

Lines changed: 107 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -13,192 +13,207 @@
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
16+
1617
#include <iostream>
1718
#include <vector>
1819
#include <filesystem>
1920

20-
#include "cddp.hpp"
21+
#include "cddp.hpp" // Adjust include as needed for your CDDP framework and DubinsCar definition
2122

2223
namespace plt = matplotlibcpp;
2324
namespace fs = std::filesystem;
2425

2526
int main() {
2627
// Problem parameters
27-
int state_dim = 3;
28-
int control_dim = 2;
29-
int horizon = 100;
30-
double timestep = 0.03;
31-
std::string integration_type = "euler";
28+
const int state_dim = 3; // [x, y, theta]
29+
const int control_dim = 1; // [omega]
30+
const int horizon = 100; // planning horizon
31+
const double timestep = 0.03; // integration step
32+
const std::string integration_type = "euler";
3233

33-
// Create a dubins car instance
34-
std::unique_ptr<cddp::DynamicalSystem> system = std::make_unique<cddp::DubinsCar>(timestep, integration_type); // Create unique_ptr
34+
// Create a DubinsCar instance (constant speed + single steering input)
35+
double forward_speed = 1.0; // For example, 1.0 m/s
36+
std::unique_ptr<cddp::DynamicalSystem> system =
37+
std::make_unique<cddp::DubinsCar>(forward_speed, timestep, integration_type);
3538

3639
// Create objective function
40+
// State cost matrix Q (typically zero or small if you only penalize final state heavily)
3741
Eigen::MatrixXd Q = Eigen::MatrixXd::Zero(state_dim, state_dim);
42+
43+
// Control cost matrix R
44+
// For single control dimension, this is a 1x1 matrix:
3845
Eigen::MatrixXd R = 0.5 * Eigen::MatrixXd::Identity(control_dim, control_dim);
46+
47+
// Final state cost matrix Qf
48+
// For example, a heavier penalty on final position/orientation
3949
Eigen::MatrixXd Qf = Eigen::MatrixXd::Identity(state_dim, state_dim);
40-
Qf << 50.0, 0.0, 0.0,
41-
0.0, 50.0, 0.0,
42-
0.0, 0.0, 10.0;
43-
Qf = 0.5 * Qf;
50+
Qf(0,0) = 50.0; // x
51+
Qf(1,1) = 50.0; // y
52+
Qf(2,2) = 10.0; // theta
53+
Qf = 0.5 * Qf; // scaling
54+
55+
// Goal state
4456
Eigen::VectorXd goal_state(state_dim);
45-
goal_state << 2.0, 2.0, M_PI/2.0;
57+
goal_state << 2.0, 2.0, M_PI / 2.0;
4658

47-
// Create an empty vector of Eigen::VectorXd
59+
// Create an empty reference-state sequence (if needed for time-varying references)
4860
std::vector<Eigen::VectorXd> empty_reference_states;
49-
auto objective = std::make_unique<cddp::QuadraticObjective>(Q, R, Qf, goal_state, empty_reference_states, timestep);
5061

51-
// Initial and target states
62+
// Build the quadratic objective
63+
auto objective = std::make_unique<cddp::QuadraticObjective>(
64+
Q, R, Qf, goal_state, empty_reference_states, timestep);
65+
66+
// Initial state
5267
Eigen::VectorXd initial_state(state_dim);
53-
initial_state << 0.0, 0.0, M_PI/4.0;
68+
initial_state << 0.0, 0.0, M_PI / 4.0;
5469

5570
// Create CDDP solver
5671
cddp::CDDP cddp_solver(initial_state, goal_state, horizon, timestep);
5772
cddp_solver.setDynamicalSystem(std::move(system));
5873
cddp_solver.setObjective(std::move(objective));
5974

60-
// Define constraints
75+
// Define box constraints on control (here, single dimension: -pi to pi)
6176
Eigen::VectorXd control_lower_bound(control_dim);
62-
control_lower_bound << -1.0, -M_PI;
6377
Eigen::VectorXd control_upper_bound(control_dim);
64-
control_upper_bound << 1.0, M_PI;
65-
78+
control_lower_bound << -M_PI; // min turn rate
79+
control_upper_bound << M_PI; // max turn rate
80+
6681
// Add the constraint to the solver
67-
cddp_solver.addConstraint(std::string("ControlBoxConstraint"), std::make_unique<cddp::ControlBoxConstraint>(control_lower_bound, control_upper_bound));
82+
cddp_solver.addConstraint(
83+
"ControlBoxConstraint",
84+
std::make_unique<cddp::ControlBoxConstraint>(control_lower_bound, control_upper_bound));
85+
86+
// (Optional) retrieve the constraint object
6887
auto constraint = cddp_solver.getConstraint<cddp::ControlBoxConstraint>("ControlBoxConstraint");
6988

70-
// Set options
89+
// Set CDDP options
7190
cddp::CDDPOptions options;
72-
options.max_iterations = 10;
91+
options.max_iterations = 10; // for demonstration
7392
options.barrier_coeff = 1e-2;
7493
options.barrier_factor = 0.1;
7594
cddp_solver.setOptions(options);
7695

77-
// Set initial trajectory
96+
// Set an initial guess for the trajectory
97+
// States (X) is horizon+1 in length, each dimension: 3
98+
// Controls (U) is horizon in length, each dimension: 1
7899
std::vector<Eigen::VectorXd> X(horizon + 1, Eigen::VectorXd::Zero(state_dim));
79-
std::vector<Eigen::VectorXd> U(horizon, Eigen::VectorXd::Zero(control_dim));
100+
std::vector<Eigen::VectorXd> U(horizon, Eigen::VectorXd::Zero(control_dim));
80101
cddp_solver.setInitialTrajectory(X, U);
81102

82103
// Solve the problem
83-
// cddp::CDDPSolution solution = cddp_solver.solve("CLCDDP");
104+
// Possible algorithms: "CLCDDP", "LogCDDP", etc.
84105
cddp::CDDPSolution solution = cddp_solver.solve("LogCDDP");
85106

86-
// Extract solution
87-
auto X_sol = solution.state_sequence; // size: horizon + 1
107+
// Extract the solution sequences
108+
auto X_sol = solution.state_sequence; // size: horizon + 1
88109
auto U_sol = solution.control_sequence; // size: horizon
89-
auto t_sol = solution.time_sequence; // size: horizon + 1
110+
auto t_sol = solution.time_sequence; // size: horizon + 1
90111

91-
// Create directory for saving plot (if it doesn't exist)
112+
// Create directory for saving plots (if it doesn't exist)
92113
const std::string plotDirectory = "../results/tests";
93114
if (!fs::exists(plotDirectory)) {
94-
fs::create_directory(plotDirectory);
115+
fs::create_directories(plotDirectory);
95116
}
96117

97-
// Plot the solution (x-y plane)
118+
// Gather data for plotting
98119
std::vector<double> x_arr, y_arr, theta_arr;
99120
for (const auto& x : X_sol) {
100121
x_arr.push_back(x(0));
101122
y_arr.push_back(x(1));
102123
theta_arr.push_back(x(2));
103124
}
104125

105-
// Plot the solution (control inputs)
106-
std::vector<double> v_arr, omega_arr;
126+
// For single-dim control: just store the steering rate
127+
std::vector<double> omega_arr;
107128
for (const auto& u : U_sol) {
108-
v_arr.push_back(u(0));
109-
omega_arr.push_back(u(1));
129+
omega_arr.push_back(u(0));
110130
}
111131

112-
// Plot the solution by subplots
132+
// Plot state trajectory & control
113133
plt::subplot(2, 1, 1);
114134
plt::plot(x_arr, y_arr);
115-
plt::title("State Trajectory");
135+
plt::title("DubinsCar State Trajectory");
116136
plt::xlabel("x");
117137
plt::ylabel("y");
118138

119139
plt::subplot(2, 1, 2);
120-
plt::plot(v_arr);
121140
plt::plot(omega_arr);
122-
plt::title("Control Inputs");
123-
plt::save(plotDirectory + "/dubincs_car_cddp_test.png");
141+
plt::title("Steering Rate Control (omega)");
142+
plt::save(plotDirectory + "/dubins_car_cddp_test.png");
124143

125-
// Create figure and axes
144+
// Optional: Generate an animation
145+
// (requires multiple frames, so you may want to store and
146+
// convert them to a GIF afterward).
126147
plt::figure_size(800, 600);
127-
plt::title("Dubins Car Trajectory");
148+
plt::title("DubinsCar Trajectory");
128149
plt::xlabel("x");
129150
plt::ylabel("y");
130-
plt::xlim(-1, 3); // Adjust limits as needed
131-
plt::ylim(-1, 3); // Adjust limits as needed
151+
plt::xlim(-1, 3);
152+
plt::ylim(-1, 3);
132153

133154
// Car dimensions
134155
double car_length = 0.2;
135-
double car_width = 0.1;
136-
137-
// Animation function
138-
for (int i = 0; i < X_sol.size(); ++i) {
156+
double car_width = 0.1;
139157

158+
// Animation loop
159+
for (int i = 0; i < static_cast<int>(X_sol.size()); ++i) {
140160
if (i % 5 == 0) {
141-
// Clear previous plot
142-
plt::clf();
161+
plt::clf();
143162

144-
// Plot car as a box with heading
145-
double x = x_arr[i];
146-
double y = y_arr[i];
163+
// Current pose
164+
double x = x_arr[i];
165+
double y = y_arr[i];
147166
double theta = theta_arr[i];
148-
149-
// Calculate car corner points
150-
std::vector<double> car_x(5);
151-
std::vector<double> car_y(5);
152167

153-
car_x[0] = x + car_length/2 * cos(theta) - car_width/2 * sin(theta);
154-
car_y[0] = y + car_length/2 * sin(theta) + car_width/2 * cos(theta);
168+
// Compute corners of the car rectangle
169+
std::vector<double> car_x(5), car_y(5);
155170

156-
car_x[1] = x + car_length/2 * cos(theta) + car_width/2 * sin(theta);
157-
car_y[1] = y + car_length/2 * sin(theta) - car_width/2 * cos(theta);
171+
// Front-left corner
172+
car_x[0] = x + car_length/2.0 * cos(theta) - car_width/2.0 * sin(theta);
173+
car_y[0] = y + car_length/2.0 * sin(theta) + car_width/2.0 * cos(theta);
158174

159-
car_x[2] = x - car_length/2 * cos(theta) + car_width/2 * sin(theta);
160-
car_y[2] = y - car_length/2 * sin(theta) - car_width/2 * cos(theta);
175+
// Front-right corner
176+
car_x[1] = x + car_length/2.0 * cos(theta) + car_width/2.0 * sin(theta);
177+
car_y[1] = y + car_length/2.0 * sin(theta) - car_width/2.0 * cos(theta);
161178

162-
car_x[3] = x - car_length/2 * cos(theta) - car_width/2 * sin(theta);
163-
car_y[3] = y - car_length/2 * sin(theta) + car_width/2 * cos(theta);
179+
// Rear-right corner
180+
car_x[2] = x - car_length/2.0 * cos(theta) + car_width/2.0 * sin(theta);
181+
car_y[2] = y - car_length/2.0 * sin(theta) - car_width/2.0 * cos(theta);
164182

165-
car_x[4] = car_x[0]; // Close the shape
183+
// Rear-left corner
184+
car_x[3] = x - car_length/2.0 * cos(theta) - car_width/2.0 * sin(theta);
185+
car_y[3] = y - car_length/2.0 * sin(theta) + car_width/2.0 * cos(theta);
186+
187+
// Close the shape
188+
car_x[4] = car_x[0];
166189
car_y[4] = car_y[0];
167190

168191
// Plot the car
169192
plt::plot(car_x, car_y, "k-");
170193

171-
// Plot trajectory up to current point
172-
plt::plot(std::vector<double>(x_arr.begin(), x_arr.begin() + i + 1),
173-
std::vector<double>(y_arr.begin(), y_arr.begin() + i + 1), "b-");
174-
175-
// Add plot title
176-
plt::title("Dubins Car Trajectory");
194+
// Plot the trajectory so far
195+
plt::plot(std::vector<double>(x_arr.begin(), x_arr.begin() + i + 1),
196+
std::vector<double>(y_arr.begin(), y_arr.begin() + i + 1),
197+
"b-");
177198

178-
// Set labels
199+
// Title and axes
200+
plt::title("DubinsCar Trajectory");
179201
plt::xlabel("x");
180202
plt::ylabel("y");
203+
plt::xlim(-1, 3);
204+
plt::ylim(-1, 3);
181205

182-
// Adjust limits as needed
183-
plt::xlim(-1, 3);
184-
plt::ylim(-1, 3);
185-
186-
// Enable legend
187-
plt::legend();
188-
189-
// Save current frame as an image
206+
// Save frame
190207
std::string filename = plotDirectory + "/dubins_car_frame_" + std::to_string(i) + ".png";
191208
plt::save(filename);
192-
193-
// Display plot continuously
194-
plt::pause(0.01); // Pause for a short time
195209

210+
// Brief pause for "animation" effect
211+
plt::pause(0.01);
196212
}
197-
};
198-
}
213+
}
199214

200-
// Create gif from images using ImageMagick
201-
// Installation:
202-
// $ sudo apt-get install imagemagick
215+
// Optionally, you can convert frames to a GIF via ImageMagick:
216+
// convert -delay 100 ../results/tests/dubins_car_frame_*.png ../results/tests/dubins_car.gif
203217

204-
// convert -delay 100 ../results/tests/dubins_car_frame_*.png ../results/tests/dubins_car.gif
218+
return 0;
219+
}

0 commit comments

Comments
 (0)