Skip to content

Commit 1d9c72f

Browse files
Add HCW example and update CMakeLists.txt to include new executables (#71)
1 parent 549a401 commit 1d9c72f

File tree

7 files changed

+624
-0
lines changed

7 files changed

+624
-0
lines changed

examples/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ target_link_libraries(cddp_pendulum cddp)
3737
add_executable(cddp_quadrotor cddp_quadrotor.cpp)
3838
target_link_libraries(cddp_quadrotor cddp)
3939

40+
add_executable(cddp_hcw cddp_hcw.cpp)
41+
target_link_libraries(cddp_hcw cddp)
42+
43+
add_executable(mpc_hcw mpc_hcw.cpp)
44+
target_link_libraries(mpc_hcw cddp)
45+
4046
# Ipopt examples
4147
if (CDDP_CPP_CASADI)
4248
add_executable(ipopt_car ipopt_car.cpp)

examples/cddp_hcw.cpp

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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+
/*
18+
* Example code demonstrating an HCW (Hill-Clohessy-Wiltshire) rendezvous problem with CDDP
19+
*/
20+
21+
#include <iostream>
22+
#include <vector>
23+
#include <random>
24+
#include <cmath>
25+
#include <filesystem>
26+
#include <Eigen/Dense>
27+
28+
#include "cddp.hpp"
29+
30+
namespace plt = matplotlibcpp;
31+
namespace fs = std::filesystem;
32+
using namespace cddp;
33+
34+
// ----------------------------------------------------------------------------------------
35+
// Main function demonstrating usage
36+
// ----------------------------------------------------------------------------------------
37+
int main() {
38+
// Random number generator for optional initial control sequence
39+
std::random_device rd;
40+
std::mt19937 gen(rd());
41+
std::normal_distribution<double> d(0.0, 0.01);
42+
43+
// HCW problem dimensions
44+
const int STATE_DIM = 6; // [x, y, z, vx, vy, vz]
45+
const int CONTROL_DIM = 3; // [Fx, Fy, Fz]
46+
47+
// Set up the time horizon
48+
int horizon = 50; // Number of steps
49+
double timestep = 10.0; // Timestep in seconds (example)
50+
std::string integration_type = "euler"; // or "rk4", etc.
51+
52+
// HCW parameters
53+
double mean_motion = 0.001107;
54+
double mass = 100.0;
55+
56+
// Create the HCW dynamical system
57+
// (this class is defined in your "spacecraft_linear.hpp")
58+
std::unique_ptr<cddp::DynamicalSystem> system =
59+
std::make_unique<cddp::HCW>(timestep, mean_motion, mass, integration_type);
60+
61+
// Initial state (for example, some offset and small velocity)
62+
Eigen::VectorXd initial_state(STATE_DIM);
63+
initial_state << 50.0, 14.0, 0.0, 0.0, 0.0, 0.0;
64+
65+
// Goal state (origin in relative coordinates, zero velocity)
66+
Eigen::VectorXd goal_state(STATE_DIM);
67+
goal_state.setZero();
68+
69+
// Cost matrices
70+
Eigen::MatrixXd Q = Eigen::MatrixXd::Zero(STATE_DIM, STATE_DIM);
71+
{
72+
Q(0,0) = 1e3; Q(1,1) = 1e3; Q(2,2) = 1e3;
73+
Q(3,3) = 1e1; Q(4,4) = 1e1; Q(5,5) = 1e1;
74+
}
75+
76+
Eigen::MatrixXd R = Eigen::MatrixXd::Zero(CONTROL_DIM, CONTROL_DIM);
77+
{
78+
R(0,0) = 1e2; R(1,1) = 1e2; R(2,2) = 1e2;
79+
}
80+
81+
Eigen::MatrixXd Qf = Eigen::MatrixXd::Zero(STATE_DIM, STATE_DIM);
82+
// {
83+
// Qf(0,0) = 1e3;
84+
// Qf(1,1) = 1e3;
85+
// Qf(2,2) = 1e3;
86+
// Qf(3,3) = 1e1;
87+
// Qf(4,4) = 1e1;
88+
// Qf(5,5) = 1e1;
89+
// }
90+
91+
std::vector<Eigen::VectorXd> empty_reference_states;
92+
auto objective = std::make_unique<cddp::QuadraticObjective>(Q, R, Qf, goal_state, empty_reference_states, timestep);
93+
94+
// Create the CDDP solver
95+
cddp::CDDP cddp_solver(initial_state, goal_state, horizon, timestep);
96+
cddp_solver.setDynamicalSystem(std::move(system));
97+
cddp_solver.setObjective(std::move(objective));
98+
99+
// Optionally add control constraints (e.g., max thruster force)
100+
Eigen::VectorXd umin(CONTROL_DIM);
101+
Eigen::VectorXd umax(CONTROL_DIM);
102+
// Suppose each axis force is limited to +/- 2 N
103+
umin << -1.0, -1.0, -1.0;
104+
umax << 1.0, 1.0, 1.0;
105+
106+
cddp_solver.addConstraint("ControlBoxConstraint",
107+
std::make_unique<cddp::ControlBoxConstraint>(umin, umax));
108+
109+
// Set solver options
110+
cddp::CDDPOptions options;
111+
options.max_iterations = 300; // Max number of CDDP iterations
112+
options.verbose = true; // Print progress
113+
options.cost_tolerance = 1e-3; // Stop if improvement below this
114+
options.grad_tolerance = 1e-3; // Stop if gradient below this
115+
options.regularization_type = "control"; // Common regularization approach
116+
options.debug = false;
117+
options.use_parallel = true;
118+
options.num_threads = 8; // Parallelization
119+
cddp_solver.setOptions(options);
120+
121+
// Initialize the trajectory (X,U) with something nontrivial
122+
std::vector<Eigen::VectorXd> X(horizon + 1, Eigen::VectorXd::Zero(STATE_DIM));
123+
std::vector<Eigen::VectorXd> U(horizon, Eigen::VectorXd::Zero(CONTROL_DIM));
124+
125+
// Set the initial state
126+
X[0] = initial_state;
127+
128+
// Random or small constant initialization for control
129+
for (auto& u : U) {
130+
// u << d(gen), d(gen), d(gen); // small random thruster
131+
u << 0.0, 0.0, 0.0; // zero thruster
132+
}
133+
134+
// Compute the initial cost by rolling out the initial controls
135+
double J_init = 0.0;
136+
for (int t = 0; t < horizon; t++) {
137+
J_init += cddp_solver.getObjective().running_cost(X[t], U[t], t);
138+
X[t+1] = cddp_solver.getSystem().getDiscreteDynamics(X[t], U[t]);
139+
}
140+
J_init += cddp_solver.getObjective().terminal_cost(X[horizon]);
141+
std::cout << "[Info] Initial cost: " << J_init << std::endl;
142+
std::cout << "[Info] Initial final state: " << X[horizon].transpose() << std::endl;
143+
144+
// Pass this initial guess to the solver
145+
cddp_solver.setInitialTrajectory(X, U);
146+
147+
// Solve
148+
cddp::CDDPSolution solution = cddp_solver.solve();
149+
150+
// Extract solution and print result
151+
double J_final = solution.cost_sequence.back();
152+
std::cout << "\n[Result] CDDP solved." << std::endl;
153+
std::cout << "[Result] Final cost: " << J_final << std::endl;
154+
std::cout << "[Result] Final state: "
155+
<< solution.state_sequence.back().transpose() << std::endl;
156+
157+
// Plot results
158+
auto X_sol = solution.state_sequence;
159+
auto U_sol = solution.control_sequence;
160+
auto t_sol = solution.time_sequence;
161+
162+
// Create plot directory
163+
const std::string plotDirectory = "../results/tests";
164+
if (!fs::exists(plotDirectory)) {
165+
fs::create_directory(plotDirectory);
166+
}
167+
168+
// Extract solution data
169+
std::vector<double> x_arr, y_arr, z_arr, vx_arr, vy_arr, vz_arr, time_arr;
170+
for (size_t i = 0; i < X_sol.size(); ++i) {
171+
time_arr.push_back(t_sol[i]);
172+
x_arr.push_back(X_sol[i](0));
173+
y_arr.push_back(X_sol[i](1));
174+
z_arr.push_back(X_sol[i](2));
175+
vx_arr.push_back(X_sol[i](3));
176+
vy_arr.push_back(X_sol[i](4));
177+
vz_arr.push_back(X_sol[i](5));
178+
}
179+
180+
// Plot results
181+
plt::figure_size(1200, 800);
182+
plt::subplot(2, 3, 1);
183+
plt::title("X Position");
184+
plt::plot(time_arr, x_arr, "b-");
185+
plt::xlabel("Time [s]");
186+
plt::ylabel("Position [m]");
187+
plt::grid(true);
188+
189+
plt::subplot(2, 3, 2);
190+
plt::title("Y Position");
191+
plt::plot(time_arr, y_arr, "b-");
192+
plt::xlabel("Time [s]");
193+
plt::ylabel("Position [m]");
194+
plt::grid(true);
195+
196+
plt::subplot(2, 3, 3);
197+
plt::title("Z Position");
198+
plt::plot(time_arr, z_arr, "b-");
199+
plt::xlabel("Time [s]");
200+
plt::ylabel("Position [m]");
201+
plt::grid(true);
202+
203+
plt::subplot(2, 3, 4);
204+
plt::title("X Velocity");
205+
plt::plot(time_arr, vx_arr, "b-");
206+
plt::xlabel("Time [s]");
207+
plt::ylabel("Velocity [m/s]");
208+
plt::grid(true);
209+
210+
plt::subplot(2, 3, 5);
211+
plt::title("Y Velocity");
212+
plt::plot(time_arr, vy_arr, "b-");
213+
plt::xlabel("Time [s]");
214+
plt::ylabel("Velocity [m/s]");
215+
plt::grid(true);
216+
217+
plt::subplot(2, 3, 6);
218+
plt::title("Z Velocity");
219+
plt::plot(time_arr, vz_arr, "b-");
220+
plt::xlabel("Time [s]");
221+
plt::ylabel("Velocity [m/s]");
222+
plt::grid(true);
223+
224+
// plt::tight_layout();
225+
plt::save(plotDirectory + "/hcw_results.png");
226+
plt::clf();
227+
228+
// Plot control inputs
229+
std::vector<double> fx_arr, fy_arr, fz_arr, time_arr2;
230+
for (size_t i = 0; i < U_sol.size(); ++i) {
231+
time_arr2.push_back(t_sol[i]);
232+
fx_arr.push_back(U_sol[i](0));
233+
fy_arr.push_back(U_sol[i](1));
234+
fz_arr.push_back(U_sol[i](2));
235+
}
236+
237+
plt::figure_size(800, 600);
238+
plt::title("Control Inputs");
239+
plt::plot(time_arr2, fx_arr, "b-");
240+
plt::plot(time_arr2, fy_arr, "r-");
241+
plt::plot(time_arr2, fz_arr, "g-");
242+
// Plot the control limits
243+
plt::plot(time_arr2, std::vector<double>(time_arr2.size(), umin(0)), "k--");
244+
plt::plot(time_arr2, std::vector<double>(time_arr2.size(), umax(0)), "k--");
245+
plt::plot(time_arr2, std::vector<double>(time_arr2.size(), umin(1)), "k--");
246+
plt::plot(time_arr2, std::vector<double>(time_arr2.size(), umax(1)), "k--");
247+
plt::plot(time_arr2, std::vector<double>(time_arr2.size(), umin(2)), "k--");
248+
plt::plot(time_arr2, std::vector<double>(time_arr2.size(), umax(2)), "k--");
249+
plt::xlabel("Time [s]");
250+
plt::ylabel("Force [N]");
251+
plt::grid(true);
252+
253+
plt::save(plotDirectory + "/hcw_control_inputs.png");
254+
plt::clf();
255+
256+
257+
return 0;
258+
}

0 commit comments

Comments
 (0)