Skip to content

Commit 6587aa7

Browse files
Add manipulator example (#30)
1 parent 3a08336 commit 6587aa7

File tree

105 files changed

+280
-1
lines changed

Some content is hidden

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

105 files changed

+280
-1
lines changed

README.md

Lines changed: 10 additions & 0 deletions

examples/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ add_executable(cddp_cartpole cddp_cartpole.cpp)
1414
target_link_libraries(cddp_cartpole cddp Eigen3::Eigen)
1515

1616
add_executable(cddp_quadrotor cddp_quadrotor.cpp)
17-
target_link_libraries(cddp_quadrotor cddp Eigen3::Eigen)
17+
target_link_libraries(cddp_quadrotor cddp Eigen3::Eigen)
18+
19+
add_executable(cddp_manipulator cddp_manipulator.cpp)
20+
target_link_libraries(cddp_manipulator cddp Eigen3::Eigen)

examples/cddp_manipulator.cpp

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
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+
#include <iostream>
17+
#include <vector>
18+
#include <filesystem>
19+
20+
#include "cddp.hpp"
21+
22+
namespace plt = matplotlibcpp;
23+
namespace fs = std::filesystem;
24+
25+
int main() {
26+
// System dimensions
27+
int state_dim = 6; // [q1, q2, q3, dq1, dq2, dq3]
28+
int control_dim = 3; // [tau1, tau2, tau3]
29+
int horizon = 200; // Time horizon for optimization
30+
double timestep = 0.01;
31+
32+
// Create manipulator instance
33+
auto system = std::make_unique<cddp::Manipulator>(timestep, "rk4");
34+
35+
// Cost matrices
36+
Eigen::MatrixXd Q = Eigen::MatrixXd::Zero(state_dim, state_dim);
37+
// Position costs (joint angles)
38+
Q.diagonal().segment<3>(0) = 1.0 * Eigen::Vector3d::Ones();
39+
// Velocity costs
40+
Q.diagonal().segment<3>(3) = 0.1 * Eigen::Vector3d::Ones();
41+
42+
// Control cost matrix (penalize large torques)
43+
Eigen::MatrixXd R = 0.1 * Eigen::MatrixXd::Identity(control_dim, control_dim);
44+
45+
// Terminal cost matrix (important for reaching target)
46+
Eigen::MatrixXd Qf = 100.0 * Q;
47+
48+
// Initial state (current manipulator configuration)
49+
Eigen::VectorXd initial_state = Eigen::VectorXd::Zero(state_dim);
50+
initial_state << 0.0, -M_PI/2, M_PI, // Initial joint angles
51+
0.0, 0.0, 0.0; // Initial joint velocities
52+
53+
// Goal state (desired configuration)
54+
Eigen::VectorXd goal_state = Eigen::VectorXd::Zero(state_dim);
55+
goal_state << M_PI, -M_PI/6, -M_PI/3, // Target joint angles
56+
0.0, 0.0, 0.0; // Zero final velocities
57+
58+
// Create objective function with no reference trajectory
59+
std::vector<Eigen::VectorXd> empty_reference_states;
60+
auto objective = std::make_unique<cddp::QuadraticObjective>(
61+
Q, R, Qf, goal_state, empty_reference_states, timestep);
62+
63+
// Create CDDP solver
64+
cddp::CDDP cddp_solver(initial_state, goal_state, horizon, timestep);
65+
cddp_solver.setDynamicalSystem(std::move(system));
66+
cddp_solver.setObjective(std::move(objective));
67+
68+
// Control constraints (joint torque limits)
69+
double max_torque = 50.0;
70+
Eigen::VectorXd control_lower_bound = -max_torque * Eigen::VectorXd::Ones(control_dim);
71+
Eigen::VectorXd control_upper_bound = max_torque * Eigen::VectorXd::Ones(control_dim);
72+
73+
cddp_solver.addConstraint("ControlBoxConstraint",
74+
std::make_unique<cddp::ControlBoxConstraint>(control_lower_bound, control_upper_bound));
75+
76+
77+
// Solver options
78+
cddp::CDDPOptions options;
79+
options.max_iterations = 100;
80+
options.max_line_search_iterations = 20;
81+
// options.regularization_type = "control";
82+
// options.regularization_update_factor = 2.0;
83+
// options.min_regularization = 1e-6;
84+
// options.max_regularization = 1e10;
85+
options.verbose = true;
86+
cddp_solver.setOptions(options);
87+
88+
// Initial trajectory
89+
std::vector<Eigen::VectorXd> X(horizon + 1, Eigen::VectorXd::Zero(state_dim));
90+
std::vector<Eigen::VectorXd> U(horizon, Eigen::VectorXd::Zero(control_dim));
91+
92+
// Linear interpolation for initial trajectory
93+
for (int i = 0; i <= horizon; ++i) {
94+
double alpha = static_cast<double>(i) / horizon;
95+
X[i] = (1.0 - alpha) * initial_state + alpha * goal_state;
96+
}
97+
cddp_solver.setInitialTrajectory(X, U);
98+
99+
// Solve the optimal control problem
100+
cddp::CDDPSolution solution = cddp_solver.solve();
101+
102+
// Create plot directory
103+
const std::string plotDirectory = "../results/tests";
104+
if (!fs::exists(plotDirectory)) {
105+
fs::create_directory(plotDirectory);
106+
}
107+
108+
// Extract trajectories
109+
std::vector<double> time, time2;
110+
std::vector<std::vector<double>> joint_angles(3);
111+
std::vector<std::vector<double>> joint_velocities(3);
112+
std::vector<std::vector<double>> joint_torques(3);
113+
114+
for (size_t i = 0; i < solution.time_sequence.size(); ++i) {
115+
time.push_back(solution.time_sequence[i]);
116+
117+
// State trajectory
118+
if (i < solution.state_sequence.size()) {
119+
for (int j = 0; j < 3; ++j) {
120+
joint_angles[j].push_back(solution.state_sequence[i](j));
121+
joint_velocities[j].push_back(solution.state_sequence[i](j+3));
122+
}
123+
}
124+
125+
// Control trajectory
126+
if (i < solution.control_sequence.size()) {
127+
for (int j = 0; j < 3; ++j) {
128+
joint_torques[j].push_back(solution.control_sequence[i](j));
129+
}
130+
time2.push_back(solution.time_sequence[i]);
131+
}
132+
}
133+
134+
// Plot results
135+
plt::figure_size(1200, 800);
136+
137+
// Joint angles
138+
plt::subplot(3, 1, 1);
139+
std::map<std::string, std::string> keywords;
140+
keywords["linewidth"] = "2";
141+
142+
keywords["label"] = "Joint 1";
143+
plt::plot(time, joint_angles[0], keywords);
144+
keywords["label"] = "Joint 2";
145+
plt::plot(time, joint_angles[1], keywords);
146+
keywords["label"] = "Joint 3";
147+
plt::plot(time, joint_angles[2], keywords);
148+
149+
plt::title("Joint Angles");
150+
plt::xlabel("Time [s]");
151+
plt::ylabel("Angle [rad]");
152+
plt::legend();
153+
plt::grid(true);
154+
155+
// Joint velocities
156+
plt::subplot(3, 1, 2);
157+
keywords["label"] = "Joint 1";
158+
plt::plot(time, joint_velocities[0], keywords);
159+
keywords["label"] = "Joint 2";
160+
plt::plot(time, joint_velocities[1], keywords);
161+
keywords["label"] = "Joint 3";
162+
plt::plot(time, joint_velocities[2], keywords);
163+
164+
plt::title("Joint Velocities");
165+
plt::xlabel("Time [s]");
166+
plt::ylabel("Velocity [rad/s]");
167+
plt::legend();
168+
plt::grid(true);
169+
170+
// Control inputs
171+
plt::subplot(3, 1, 3);
172+
keywords["label"] = "Joint 1";
173+
plt::plot(time2, joint_torques[0], keywords);
174+
keywords["label"] = "Joint 2";
175+
plt::plot(time2, joint_torques[1], keywords);
176+
keywords["label"] = "Joint 3";
177+
plt::plot(time2, joint_torques[2], keywords);
178+
179+
plt::title("Joint Torques");
180+
plt::xlabel("Time [s]");
181+
plt::ylabel("Torque [Nm]");
182+
plt::legend();
183+
plt::grid(true);
184+
185+
plt::tight_layout();
186+
plt::save(plotDirectory + "/manipulator_cddp_results.png");
187+
// plt::clf();
188+
189+
cddp::Manipulator manipulator(timestep, "rk4");
190+
191+
// Generate animation of the solution
192+
const long fg = plt::figure();
193+
plt::figure_size(800, 600);
194+
195+
for (size_t i = 0; i < solution.state_sequence.size(); i += 5) { // Animate every 5th frame
196+
plt::clf();
197+
198+
const auto& state = solution.state_sequence[i];
199+
200+
// Get transformations
201+
auto transforms = manipulator.getTransformationMatrices(state(0), state(1), state(2));
202+
Eigen::Matrix4d T01 = transforms[0];
203+
Eigen::Matrix4d T02 = T01 * transforms[1];
204+
Eigen::Matrix4d T03 = T02 * transforms[2];
205+
Eigen::Matrix4d T04 = T03 * transforms[3];
206+
207+
// Get end-effector position
208+
Eigen::Vector4d r3; // End-point wrt Frame 3
209+
r3 << manipulator.getLinkLength('c'), 0, manipulator.getLinkLength('b'), 1;
210+
Eigen::Vector4d r0 = T03 * r3; // Position of end-effector
211+
// Get elbow position
212+
Eigen::Vector4d rm; // Intermediate point between O3 and O4
213+
rm = T03 * Eigen::Vector4d(0, 0, manipulator.getLinkLength('b'), 1);
214+
215+
// Plot links
216+
std::vector<double> x = {0, T03(0,3), rm(0), r0(0)};
217+
std::vector<double> y = {0, T03(1,3), rm(1), r0(1)};
218+
std::vector<double> z = {0, T03(2,3), rm(2), r0(2)};
219+
220+
std::map<std::string, std::string> link_keywords;
221+
link_keywords["color"] = "blue";
222+
link_keywords["linewidth"] = "2";
223+
plt::plot3(x, y, z, link_keywords, fg);
224+
225+
// Plot joints
226+
std::vector<double> joint_x = {0, T03(0,3), rm(0)};
227+
std::vector<double> joint_y = {0, T03(1,3), rm(1)};
228+
std::vector<double> joint_z = {0, T03(2,3), rm(2)};
229+
230+
std::map<std::string, std::string> joint_keywords;
231+
joint_keywords["color"] = "red";
232+
joint_keywords["marker"] = "o";
233+
plt::plot3(joint_x, joint_y, joint_z, joint_keywords, fg);
234+
235+
// Plot end-effector
236+
std::vector<double> ee_x = {r0(0)};
237+
std::vector<double> ee_y = {r0(1)};
238+
std::vector<double> ee_z = {r0(2)};
239+
240+
std::map<std::string, std::string> ee_keywords;
241+
ee_keywords["color"] = "red";
242+
ee_keywords["marker"] = "o";
243+
plt::plot3(ee_x, ee_y, ee_z, ee_keywords, fg);
244+
245+
plt::xlabel("X [m]");
246+
plt::ylabel("Y [m]");
247+
plt::set_zlabel("Z [m]");
248+
plt::title("Manipulator CDDP Solution");
249+
250+
plt::xlim(-2, 2);
251+
plt::ylim(-2, 2);
252+
plt::zlim(-1, 3);
253+
plt::grid(true);
254+
plt::view_init(30, -60);
255+
256+
plt::save(plotDirectory + "/manipulator_frame_" + std::to_string(i/5) + ".png");
257+
plt::pause(0.01);
258+
}
259+
// plt::show for 3 seconds
260+
plt::show(3000);
261+
262+
return 0;
263+
}
264+
265+
// Create gif from images using ImageMagick:
266+
// convert -delay 5 ../results/tests/manipulator_frame_*.png ../results/tests/manipulator.gif

results/tests/manipulator.gif

1.28 MB
108 KB

results/tests/manipulator_frame_0.png

-7.37 KB

results/tests/manipulator_frame_1.png

-6.64 KB
-7.8 KB
-8 KB
-8.28 KB

0 commit comments

Comments
 (0)