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
0 commit comments