|
8 | 8 | #include <pluginlib/class_list_macros.hpp> |
9 | 9 | #include <rclcpp/rclcpp.hpp> |
10 | 10 |
|
| 11 | +#include <chrono> |
11 | 12 | #include <moveit/kinematics_base/kinematics_base.h> |
12 | 13 | #include <moveit/robot_model/joint_model_group.h> |
13 | 14 | #include <moveit/robot_state/robot_state.h> |
@@ -164,119 +165,160 @@ class PickIKPlugin : public kinematics::KinematicsBase { |
164 | 165 | // single function used by gradient descent to calculate cost of solution |
165 | 166 | auto const cost_fn = make_cost_fn(pose_cost_functions, goals, fk_fn); |
166 | 167 |
|
167 | | - // Search for a solution using either the local or global solver. |
168 | | - std::optional<std::vector<double>> maybe_solution; |
169 | | - if (params.mode == "global") { |
170 | | - MemeticIkParams ik_params; |
171 | | - ik_params.population_size = static_cast<size_t>(params.memetic_population_size); |
172 | | - ik_params.elite_size = static_cast<size_t>(params.memetic_elite_size); |
173 | | - ik_params.wipeout_fitness_tol = params.memetic_wipeout_fitness_tol; |
174 | | - ik_params.stop_optimization_on_valid_solution = |
175 | | - params.stop_optimization_on_valid_solution; |
176 | | - ik_params.num_threads = static_cast<size_t>(params.memetic_num_threads); |
177 | | - ik_params.stop_on_first_soln = params.memetic_stop_on_first_solution; |
178 | | - ik_params.max_generations = static_cast<int>(params.memetic_max_generations); |
179 | | - ik_params.max_time = timeout; |
180 | | - |
181 | | - ik_params.gd_params.step_size = params.gd_step_size; |
182 | | - ik_params.gd_params.min_cost_delta = params.gd_min_cost_delta; |
183 | | - ik_params.gd_params.max_iterations = static_cast<int>(params.memetic_gd_max_iters); |
184 | | - ik_params.gd_params.max_time = params.memetic_gd_max_time; |
185 | | - |
186 | | - maybe_solution = ik_memetic(ik_seed_state, |
187 | | - robot_, |
188 | | - cost_fn, |
189 | | - solution_fn, |
190 | | - ik_params, |
191 | | - options.return_approximate_solution, |
192 | | - false /* No debug print */); |
193 | | - } else if (params.mode == "local") { |
194 | | - GradientIkParams gd_params; |
195 | | - gd_params.step_size = params.gd_step_size; |
196 | | - gd_params.min_cost_delta = params.gd_min_cost_delta; |
197 | | - gd_params.max_time = timeout; |
198 | | - gd_params.max_iterations = static_cast<int>(params.gd_max_iters); |
199 | | - gd_params.stop_optimization_on_valid_solution = |
200 | | - params.stop_optimization_on_valid_solution; |
201 | | - |
202 | | - maybe_solution = ik_gradient(ik_seed_state, |
203 | | - robot_, |
204 | | - cost_fn, |
205 | | - solution_fn, |
206 | | - gd_params, |
207 | | - options.return_approximate_solution); |
208 | | - } else { |
209 | | - RCLCPP_ERROR(LOGGER, "Invalid solver mode: %s", params.mode.c_str()); |
210 | | - return false; |
211 | | - } |
212 | | - |
213 | | - if (maybe_solution.has_value()) { |
214 | | - // Set the output parameter solution. |
215 | | - // Assumes that the angles were already wrapped by the solver. |
216 | | - error_code.val = error_code.SUCCESS; |
217 | | - solution = maybe_solution.value(); |
218 | | - } else { |
219 | | - error_code.val = error_code.NO_IK_SOLUTION; |
220 | | - solution = ik_seed_state; |
| 168 | + // Set up initial optimization variables |
| 169 | + bool done_optimizing = false; |
| 170 | + bool found_valid_solution = false; |
| 171 | + double remaining_timeout = timeout; |
| 172 | + std::chrono::duration<double> total_optim_time{0.0}; |
| 173 | + std::chrono::duration<double> const total_timeout{timeout}; |
| 174 | + auto last_optim_time = std::chrono::system_clock::now(); |
| 175 | + |
| 176 | + // If the initial state is not valid, restart from a random valid state. |
| 177 | + auto init_state = ik_seed_state; |
| 178 | + if (!robot_.is_valid_configuration(init_state)) { |
| 179 | + RCLCPP_WARN( |
| 180 | + LOGGER, |
| 181 | + "Initial guess exceeds joint limits. Regenerating a random valid configuration."); |
| 182 | + init_state = robot_.get_random_valid_configuration(); |
221 | 183 | } |
222 | 184 |
|
223 | | - // If using an approximate solution, check against the maximum allowable pose and joint |
224 | | - // thresholds. If the approximate solution is too far from the goal frame, |
225 | | - // fall back to the initial state. |
226 | | - if (options.return_approximate_solution) { |
227 | | - // Check pose thresholds |
228 | | - std::optional<double> approximate_solution_position_threshold = std::nullopt; |
229 | | - if (test_position) { |
230 | | - approximate_solution_position_threshold = |
231 | | - params.approximate_solution_position_threshold; |
232 | | - } |
233 | | - std::optional<double> approximate_solution_orientation_threshold = std::nullopt; |
234 | | - if (test_rotation) { |
235 | | - approximate_solution_orientation_threshold = |
236 | | - params.approximate_solution_orientation_threshold; |
| 185 | + // Optimize until a valid solution is found or we have timed out. |
| 186 | + while (!done_optimizing) { |
| 187 | + // Search for a solution using either the local or global solver. |
| 188 | + std::optional<std::vector<double>> maybe_solution; |
| 189 | + if (params.mode == "global") { |
| 190 | + MemeticIkParams ik_params; |
| 191 | + ik_params.population_size = static_cast<size_t>(params.memetic_population_size); |
| 192 | + ik_params.elite_size = static_cast<size_t>(params.memetic_elite_size); |
| 193 | + ik_params.wipeout_fitness_tol = params.memetic_wipeout_fitness_tol; |
| 194 | + ik_params.stop_optimization_on_valid_solution = |
| 195 | + params.stop_optimization_on_valid_solution; |
| 196 | + ik_params.num_threads = static_cast<size_t>(params.memetic_num_threads); |
| 197 | + ik_params.stop_on_first_soln = params.memetic_stop_on_first_solution; |
| 198 | + ik_params.max_generations = static_cast<int>(params.memetic_max_generations); |
| 199 | + ik_params.max_time = remaining_timeout; |
| 200 | + |
| 201 | + ik_params.gd_params.step_size = params.gd_step_size; |
| 202 | + ik_params.gd_params.min_cost_delta = params.gd_min_cost_delta; |
| 203 | + ik_params.gd_params.max_iterations = static_cast<int>(params.memetic_gd_max_iters); |
| 204 | + ik_params.gd_params.max_time = params.memetic_gd_max_time; |
| 205 | + |
| 206 | + maybe_solution = ik_memetic(ik_seed_state, |
| 207 | + robot_, |
| 208 | + cost_fn, |
| 209 | + solution_fn, |
| 210 | + ik_params, |
| 211 | + options.return_approximate_solution, |
| 212 | + false /* No debug print */); |
| 213 | + } else if (params.mode == "local") { |
| 214 | + GradientIkParams gd_params; |
| 215 | + gd_params.step_size = params.gd_step_size; |
| 216 | + gd_params.min_cost_delta = params.gd_min_cost_delta; |
| 217 | + gd_params.max_time = remaining_timeout; |
| 218 | + gd_params.max_iterations = static_cast<int>(params.gd_max_iters); |
| 219 | + gd_params.stop_optimization_on_valid_solution = |
| 220 | + params.stop_optimization_on_valid_solution; |
| 221 | + |
| 222 | + maybe_solution = ik_gradient(ik_seed_state, |
| 223 | + robot_, |
| 224 | + cost_fn, |
| 225 | + solution_fn, |
| 226 | + gd_params, |
| 227 | + options.return_approximate_solution); |
| 228 | + } else { |
| 229 | + RCLCPP_ERROR(LOGGER, "Invalid solver mode: %s", params.mode.c_str()); |
| 230 | + return false; |
237 | 231 | } |
238 | | - auto const approx_frame_tests = |
239 | | - make_frame_tests(goal_frames, |
240 | | - approximate_solution_position_threshold, |
241 | | - approximate_solution_orientation_threshold); |
242 | | - |
243 | | - // If we have no cost threshold, we don't need to check the goals |
244 | | - if (params.approximate_solution_cost_threshold <= 0.0) { |
245 | | - goals.clear(); |
| 232 | + |
| 233 | + if (maybe_solution.has_value()) { |
| 234 | + // Set the output parameter solution. |
| 235 | + // Assumes that the angles were already wrapped by the solver. |
| 236 | + error_code.val = error_code.SUCCESS; |
| 237 | + solution = maybe_solution.value(); |
| 238 | + } else { |
| 239 | + error_code.val = error_code.NO_IK_SOLUTION; |
| 240 | + solution = ik_seed_state; |
246 | 241 | } |
247 | 242 |
|
248 | | - auto const approx_solution_fn = |
249 | | - make_is_solution_test_fn(frame_tests, |
250 | | - goals, |
251 | | - params.approximate_solution_cost_threshold, |
252 | | - fk_fn); |
253 | | - |
254 | | - bool approx_solution_valid = approx_solution_fn(solution); |
255 | | - |
256 | | - // Check joint thresholds |
257 | | - if (approx_solution_valid && params.approximate_solution_joint_threshold > 0.0) { |
258 | | - for (size_t i = 0; i < solution.size(); ++i) { |
259 | | - if (std::abs(solution[i] - ik_seed_state[i]) > |
260 | | - params.approximate_solution_joint_threshold) { |
261 | | - approx_solution_valid = false; |
262 | | - break; |
| 243 | + // If using an approximate solution, check against the maximum allowable pose and joint |
| 244 | + // thresholds. If the approximate solution is too far from the goal frame, |
| 245 | + // fall back to the initial state. |
| 246 | + if (options.return_approximate_solution) { |
| 247 | + // Check pose thresholds |
| 248 | + std::optional<double> approximate_solution_position_threshold = std::nullopt; |
| 249 | + if (test_position) { |
| 250 | + approximate_solution_position_threshold = |
| 251 | + params.approximate_solution_position_threshold; |
| 252 | + } |
| 253 | + std::optional<double> approximate_solution_orientation_threshold = std::nullopt; |
| 254 | + if (test_rotation) { |
| 255 | + approximate_solution_orientation_threshold = |
| 256 | + params.approximate_solution_orientation_threshold; |
| 257 | + } |
| 258 | + auto const approx_frame_tests = |
| 259 | + make_frame_tests(goal_frames, |
| 260 | + approximate_solution_position_threshold, |
| 261 | + approximate_solution_orientation_threshold); |
| 262 | + |
| 263 | + // If we have no cost threshold, we don't need to check the goals |
| 264 | + if (params.approximate_solution_cost_threshold <= 0.0) { |
| 265 | + goals.clear(); |
| 266 | + } |
| 267 | + |
| 268 | + auto const approx_solution_fn = |
| 269 | + make_is_solution_test_fn(frame_tests, |
| 270 | + goals, |
| 271 | + params.approximate_solution_cost_threshold, |
| 272 | + fk_fn); |
| 273 | + |
| 274 | + bool approx_solution_valid = approx_solution_fn(solution); |
| 275 | + |
| 276 | + // Check joint thresholds |
| 277 | + if (approx_solution_valid && params.approximate_solution_joint_threshold > 0.0) { |
| 278 | + for (size_t i = 0; i < solution.size(); ++i) { |
| 279 | + if (std::abs(solution[i] - ik_seed_state[i]) > |
| 280 | + params.approximate_solution_joint_threshold) { |
| 281 | + approx_solution_valid = false; |
| 282 | + break; |
| 283 | + } |
263 | 284 | } |
264 | 285 | } |
| 286 | + |
| 287 | + if (!approx_solution_valid) { |
| 288 | + error_code.val = error_code.NO_IK_SOLUTION; |
| 289 | + solution = ik_seed_state; |
| 290 | + } |
265 | 291 | } |
266 | 292 |
|
267 | | - if (!approx_solution_valid) { |
268 | | - error_code.val = error_code.NO_IK_SOLUTION; |
269 | | - solution = ik_seed_state; |
| 293 | + // Execute solution callback only on successful solution. |
| 294 | + auto const found_solution = error_code.val == error_code.SUCCESS; |
| 295 | + if (solution_callback && found_solution) { |
| 296 | + solution_callback(ik_poses.front(), solution, error_code); |
| 297 | + } |
| 298 | + found_valid_solution = error_code.val == error_code.SUCCESS; |
| 299 | + |
| 300 | + // Check for timeout. |
| 301 | + auto const current_time = std::chrono::system_clock::now(); |
| 302 | + total_optim_time += (current_time - last_optim_time); |
| 303 | + last_optim_time = current_time; |
| 304 | + bool const timeout_elapsed = (total_optim_time >= total_timeout); |
| 305 | + |
| 306 | + // If we found a valid solution or hit the timeout, we are done optimizing. |
| 307 | + // Otherwise, pick a random new initial seed and keep optimizing with the remaining |
| 308 | + // time. |
| 309 | + if (found_valid_solution || timeout_elapsed) { |
| 310 | + done_optimizing = true; |
| 311 | + } else { |
| 312 | + init_state = robot_.get_random_valid_configuration(); |
| 313 | + remaining_timeout -= total_optim_time.count(); |
270 | 314 | } |
271 | 315 | } |
272 | 316 |
|
273 | | - // Execute solution callback only on successful solution. |
274 | | - auto const found_solution = error_code.val == error_code.SUCCESS; |
275 | | - if (solution_callback && found_solution) { |
276 | | - solution_callback(ik_poses.front(), solution, error_code); |
| 317 | + if (!robot_.is_valid_configuration(solution)) { |
| 318 | + std::cout << "INVALID SOLUTION!" << std::endl; |
277 | 319 | } |
278 | 320 |
|
279 | | - return error_code.val == error_code.SUCCESS; |
| 321 | + return found_valid_solution; |
280 | 322 | } |
281 | 323 |
|
282 | 324 | virtual std::vector<std::string> const& getJointNames() const { return joint_names_; } |
|
0 commit comments