Skip to content

Commit e0539be

Browse files
Add bicycle model test
1 parent b8fee60 commit e0539be

File tree

3 files changed

+252
-0
lines changed

3 files changed

+252
-0
lines changed

include/cddp/model/Bicycle.hpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#ifndef CDDP_BICYCLE_HPP
2+
#define CDDP_BICYCLE_HPP
3+
4+
#include "Eigen/Dense"
5+
#include <vector>
6+
#include "cddp_core/DynamicalSystem.hpp"
7+
8+
namespace cddp {
9+
10+
class Bicycle : public cddp::DynamicalSystem {
11+
public:
12+
int state_size_; // State dimension (x, y, theta, v)
13+
int control_size_; // Control dimension (a, delta)
14+
double timestep_; // Time step
15+
double wheelbase_; // Distance between front and rear axles
16+
int integration_type_;
17+
18+
Bicycle(int state_dim, int control_dim, double timestep, double wheelbase, int integration_type) :
19+
DynamicalSystem(state_dim, control_dim, timestep, integration_type) {
20+
state_size_ = state_dim; // Should be 4: [x, y, theta, v]
21+
control_size_ = control_dim; // Should be 2: [acceleration, steering_angle]
22+
timestep_ = timestep;
23+
wheelbase_ = wheelbase;
24+
integration_type_ = integration_type;
25+
}
26+
27+
Eigen::VectorXd dynamics(const Eigen::VectorXd &state, const Eigen::VectorXd &control) override {
28+
// State: [x, y, theta, v]
29+
// Control: [acceleration, steering_angle]
30+
Eigen::VectorXd state_dot = Eigen::VectorXd::Zero(state_size_);
31+
32+
double v = state(3); // velocity
33+
double theta = state(2); // heading angle
34+
double delta = control(1); // steering angle
35+
double a = control(0); // acceleration
36+
37+
// Kinematic bicycle model equations
38+
state_dot(0) = v * cos(theta); // dx/dt
39+
state_dot(1) = v * sin(theta); // dy/dt
40+
state_dot(2) = (v / wheelbase_) * tan(delta); // dtheta/dt
41+
state_dot(3) = a; // dv/dt
42+
43+
return state_dot;
44+
}
45+
46+
std::tuple<Eigen::MatrixXd, Eigen::MatrixXd> getDynamicsJacobian(
47+
const Eigen::VectorXd &state, const Eigen::VectorXd &control) override {
48+
// Initialize Jacobian matrices
49+
Eigen::MatrixXd A = Eigen::MatrixXd::Zero(state_size_, state_size_); // df/dx
50+
Eigen::MatrixXd B = Eigen::MatrixXd::Zero(state_size_, control_size_); // df/du
51+
52+
double v = state(3); // velocity
53+
double theta = state(2); // heading angle
54+
double delta = control(1); // steering angle
55+
56+
// State Jacobian (A matrix)
57+
// df1/dx = d(dx/dt)/dx
58+
A(0, 2) = -v * sin(theta); // df1/dtheta
59+
A(0, 3) = cos(theta); // df1/dv
60+
61+
// df2/dx = d(dy/dt)/dx
62+
A(1, 2) = v * cos(theta); // df2/dtheta
63+
A(1, 3) = sin(theta); // df2/dv
64+
65+
// df3/dx = d(dtheta/dt)/dx
66+
A(2, 3) = tan(delta) / wheelbase_; // df3/dv
67+
68+
// Control Jacobian (B matrix)
69+
// df/du
70+
B(3, 0) = 1.0; // df4/da (acceleration effect on velocity)
71+
B(2, 1) = v / (wheelbase_ * pow(cos(delta), 2)); // df3/ddelta
72+
73+
return std::make_tuple(A, B);
74+
}
75+
76+
std::tuple<Eigen::MatrixXd, Eigen::MatrixXd, Eigen::MatrixXd> getDynamicsHessian(
77+
const Eigen::VectorXd &state, const Eigen::VectorXd &control) override {
78+
// Initialize Hessian matrices
79+
Eigen::MatrixXd hxx = Eigen::MatrixXd::Zero(state_size_ * state_size_, state_size_);
80+
Eigen::MatrixXd hxu = Eigen::MatrixXd::Zero(state_size_ * control_size_, state_size_);
81+
Eigen::MatrixXd huu = Eigen::MatrixXd::Zero(state_size_ * control_size_, control_size_);
82+
83+
double v = state(3); // velocity
84+
double theta = state(2); // heading angle
85+
double delta = control(1); // steering angle
86+
87+
// Fill in non-zero Hessian terms
88+
// Second derivatives with respect to states
89+
int idx;
90+
91+
// d²(dx/dt)/dtheta²
92+
idx = 2 * state_size_ + 0; // (theta, x) component
93+
hxx(idx, 2) = -v * cos(theta);
94+
95+
// d²(dy/dt)/dtheta²
96+
idx = 2 * state_size_ + 1; // (theta, y) component
97+
hxx(idx, 2) = -v * sin(theta);
98+
99+
// Mixed derivatives (state-control)
100+
// d²(dtheta/dt)/dv/ddelta
101+
idx = 3 * control_size_ + 1; // (v, delta) component
102+
hxu(idx, 2) = 1.0 / (wheelbase_ * pow(cos(delta), 2));
103+
104+
// Second derivatives with respect to controls
105+
// d²(dtheta/dt)/ddelta²
106+
idx = 1 * control_size_ + 2; // (delta, theta) component
107+
huu(idx, 1) = 2.0 * v * sin(delta) / (wheelbase_ * pow(cos(delta), 3));
108+
109+
return std::make_tuple(hxx, hxu, huu);
110+
}
111+
};
112+
113+
} // namespace cddp
114+
115+
#endif // CDDP_BICYCLE_HPP

test/CDDPProblemTests_Bicycle.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include <iostream>
2+
#include "Eigen/Dense"
3+
// #include "cddp/cddp_core/CDDPProblem.hpp"
4+
#include "CDDP.hpp"
5+
#include "model/DubinsCar.hpp"
6+
#include "matplotlibcpp.hpp"
7+
8+
9+
#include <iostream>
10+
#include "Eigen/Dense"
11+
// #include "cddp/cddp_core/CDDPProblem.hpp"
12+
#include "CDDP.hpp"
13+
#include "model/Bicycle.hpp"
14+
#include "matplotlibcpp.hpp"
15+
16+
using namespace cddp;
17+
namespace plt = matplotlibcpp;
18+
// Simple Test Function
19+
bool testBasicCDDP() {
20+
const int STATE_DIM = 4;
21+
const int CONTROL_DIM = 2;
22+
const double TIMESTEP = 0.1;
23+
const int HORIZON = 100;
24+
const double WHEEL_BASE = 1.5;
25+
const int INTEGRATION_TYPE = 0; // 0 for Euler, 1 for Heun, 2 for RK3, 3 for RK4
26+
27+
// Problem Setup
28+
Eigen::VectorXd initial_state(STATE_DIM);
29+
initial_state << 0.0, 0.0, M_PI/4.0, 0.0; // Initial state
30+
31+
// Set goal state
32+
Eigen::VectorXd goal_state(STATE_DIM);
33+
goal_state << 5.0, 5.0, M_PI/2.0, 0.0;
34+
35+
Bicycle system(STATE_DIM, CONTROL_DIM, TIMESTEP, WHEEL_BASE, INTEGRATION_TYPE);
36+
CDDPProblem cddp_solver(initial_state, goal_state, HORIZON, TIMESTEP);
37+
38+
// Set dynamical system
39+
cddp_solver.setDynamicalSystem(std::make_unique<Bicycle>(system));
40+
41+
42+
// Simple Cost Matrices
43+
Eigen::MatrixXd Q(STATE_DIM, STATE_DIM);
44+
Q << 0e-2, 0, 0, 0.0,
45+
0, 0e-2, 0, 0.0,
46+
0, 0, 0e-3, 0.0,
47+
0, 0, 0, 0.0;
48+
49+
Eigen::MatrixXd R(CONTROL_DIM, CONTROL_DIM);
50+
R << 1e+0, 0,
51+
0, 1e+0;
52+
53+
Eigen::MatrixXd Qf(STATE_DIM, STATE_DIM);
54+
Qf << 50, 0, 0, 0.0,
55+
0, 50, 0, 0.0,
56+
0, 0, 10, 0.0,
57+
0, 0, 0, 10.0;
58+
59+
QuadraticCost objective(Q, R, Qf, goal_state, TIMESTEP);
60+
cddp_solver.setObjective(std::make_unique<QuadraticCost>(objective));
61+
62+
// Add constraints
63+
Eigen::VectorXd lower_bound(CONTROL_DIM);
64+
lower_bound << -10.0, -M_PI/3;
65+
66+
Eigen::VectorXd upper_bound(CONTROL_DIM);
67+
upper_bound << 10.0, M_PI/3;
68+
69+
ControlBoxConstraint control_constraint(lower_bound, upper_bound);
70+
cddp_solver.addConstraint(std::make_unique<ControlBoxConstraint>(control_constraint));
71+
72+
CDDPOptions opts;
73+
// // Set options if needed
74+
opts.max_iterations = 20;
75+
// opts.cost_tolerance = 1e-6;
76+
// opts.grad_tolerance = 1e-8;
77+
// opts.print_iterations = false;
78+
cddp_solver.setOptions(opts);
79+
80+
81+
// Set initial trajectory if needed
82+
std::vector<Eigen::VectorXd> X = std::vector<Eigen::VectorXd>(HORIZON + 1, Eigen::VectorXd::Zero(STATE_DIM));
83+
std::vector<Eigen::VectorXd> U = std::vector<Eigen::VectorXd>(HORIZON, Eigen::VectorXd::Zero(CONTROL_DIM));
84+
cddp_solver.setInitialTrajectory(X, U);
85+
86+
// Solve!
87+
std::vector<Eigen::VectorXd> U_sol = cddp_solver.solve();
88+
89+
std::vector<Eigen::VectorXd> X_sol = cddp_solver.getTrajectory();
90+
91+
// Print solution
92+
std::cout << "Solution: "<< std::endl;
93+
// print last state
94+
std::cout << "Final State: " << X_sol.back().transpose() << std::endl;
95+
// print initial control
96+
std::cout << "Initial Control: " << U_sol[0].transpose() << std::endl;
97+
98+
// Plotting
99+
std::vector<double> x, y, theta, v;
100+
for (int i = 0; i < X_sol.size(); i++) {
101+
x.push_back(X_sol[i](0));
102+
y.push_back(X_sol[i](1));
103+
theta.push_back(X_sol[i](2));
104+
v.push_back(X_sol[i](3));
105+
}
106+
107+
plt::figure();
108+
plt::plot(x, y);
109+
plt::xlabel("X");
110+
plt::ylabel("Y");
111+
plt::title("Bicycle model car Trajectory");
112+
plt::show();
113+
114+
// for (int i = 0; i < U_sol.size(); i++) {
115+
// std::cout << "Control " << i << ": " << U_sol[i].transpose() << std::endl;
116+
// }
117+
118+
return true;
119+
}
120+
121+
int main() {
122+
if (testBasicCDDP()) {
123+
std::cout << "Basic CDDP Test Passed!" << std::endl;
124+
} else {
125+
std::cout << "Basic CDDP Test Failed!" << std::endl;
126+
return 1; // Indicate failure
127+
}
128+
return 0;
129+
}

test/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,13 @@ target_link_libraries(cddp_problem_tests PUBLIC Eigen3::Eigen Python3::Python
2121
Python3::Module
2222
Python3::NumPy PRIVATE osqp-cpp)
2323

24+
# ---- CDDPTests_Bicycle ----
25+
add_executable(cddp_bicycle_tests
26+
CDDPProblemTests_Bicycle.cpp
27+
../src/cddp_core/CDDPProblem.cpp)
28+
target_link_libraries(cddp_bicycle_tests PUBLIC Eigen3::Eigen Python3::Python
29+
Python3::Module
30+
Python3::NumPy PRIVATE osqp-cpp)
31+
2432

2533

0 commit comments

Comments
 (0)