moveit2
The MoveIt Motion Planning Framework for ROS 2.
chomp_planner.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: E. Gil Jones */
36 
41 // TODO: Remove conditional include when released to all active distros.
42 #if __has_include(<tf2/LinearMath/Quaternion.hpp>)
43 #include <tf2/LinearMath/Quaternion.hpp>
44 #else
45 #include <tf2/LinearMath/Quaternion.h>
46 #endif
47 #include <tf2_eigen/tf2_eigen.hpp>
48 #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
49 
50 #include <chrono>
51 
52 namespace chomp
53 {
54 static const rclcpp::Logger LOGGER = rclcpp::get_logger("chomp_planner");
55 
56 bool ChompPlanner::solve(const planning_scene::PlanningSceneConstPtr& planning_scene,
59 {
60  auto start_time = std::chrono::system_clock::now();
61  if (!planning_scene)
62  {
63  RCLCPP_ERROR(LOGGER, "No planning scene initialized.");
64  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
65  return false;
66  }
67 
68  // get the specified start state
69  moveit::core::RobotState start_state = planning_scene->getCurrentState();
70  moveit::core::robotStateMsgToRobotState(planning_scene->getTransforms(), req.start_state, start_state);
71 
72  if (!start_state.satisfiesBounds())
73  {
74  RCLCPP_ERROR(LOGGER, "Start state violates joint limits");
75  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::INVALID_ROBOT_STATE;
76  return false;
77  }
78 
79  ChompTrajectory trajectory(planning_scene->getRobotModel(), 3.0, .03, req.group_name);
80  robotStateToArray(start_state, req.group_name, trajectory.getTrajectoryPoint(0));
81 
82  if (req.goal_constraints.size() != 1)
83  {
84  RCLCPP_ERROR(LOGGER, "Expecting exactly one goal constraint, got: %zd", req.goal_constraints.size());
85  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
86  return false;
87  }
88 
89  if (req.goal_constraints[0].joint_constraints.empty() || !req.goal_constraints[0].position_constraints.empty() ||
90  !req.goal_constraints[0].orientation_constraints.empty())
91  {
92  RCLCPP_ERROR(LOGGER, "Only joint-space goals are supported");
93  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
94  return false;
95  }
96 
97  const size_t goal_index = trajectory.getNumPoints() - 1;
98  moveit::core::RobotState goal_state(start_state);
99  for (const moveit_msgs::msg::JointConstraint& joint_constraint : req.goal_constraints[0].joint_constraints)
100  goal_state.setVariablePosition(joint_constraint.joint_name, joint_constraint.position);
101  if (!goal_state.satisfiesBounds())
102  {
103  RCLCPP_ERROR(LOGGER, "Goal state violates joint limits");
104  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::INVALID_ROBOT_STATE;
105  return false;
106  }
107  robotStateToArray(goal_state, req.group_name, trajectory.getTrajectoryPoint(goal_index));
108 
109  const moveit::core::JointModelGroup* model_group =
110  planning_scene->getRobotModel()->getJointModelGroup(req.group_name);
111  // fix the goal to move the shortest angular distance for wrap-around joints:
112  for (size_t i = 0; i < model_group->getActiveJointModels().size(); ++i)
113  {
114  const moveit::core::JointModel* model = model_group->getActiveJointModels()[i];
115  const moveit::core::RevoluteJointModel* revolute_joint =
116  dynamic_cast<const moveit::core::RevoluteJointModel*>(model);
117 
118  if (revolute_joint != nullptr)
119  {
120  if (revolute_joint->isContinuous())
121  {
122  double start = (trajectory)(0, i);
123  double end = (trajectory)(goal_index, i);
124  RCLCPP_INFO(LOGGER, "Start is %f end %f short %f", start, end, shortestAngularDistance(start, end));
125  (trajectory)(goal_index, i) = start + shortestAngularDistance(start, end);
126  }
127  }
128  }
129 
130  // fill in an initial trajectory based on user choice from the chomp_config.yaml file
131  if (params.trajectory_initialization_method_.compare("quintic-spline") == 0)
132  trajectory.fillInMinJerk();
133  else if (params.trajectory_initialization_method_.compare("linear") == 0)
134  trajectory.fillInLinearInterpolation();
135  else if (params.trajectory_initialization_method_.compare("cubic") == 0)
136  trajectory.fillInCubicInterpolation();
137  else if (params.trajectory_initialization_method_.compare("fillTrajectory") == 0)
138  {
139  if (!(trajectory.fillInFromTrajectory(*res.trajectory_[0])))
140  {
141  RCLCPP_ERROR(LOGGER, "Input trajectory has less than 2 points, "
142  "trajectory must contain at least start and goal state");
143  return false;
144  }
145  }
146  else
147  {
148  RCLCPP_ERROR(LOGGER, "invalid interpolation method specified in the chomp_planner file");
149  return false;
150  }
151 
152  RCLCPP_INFO(LOGGER, "CHOMP trajectory initialized using method: %s ",
153  (params.trajectory_initialization_method_).c_str());
154 
155  // optimize!
156  auto create_time = std::chrono::system_clock::now();
157 
158  int replan_count = 0;
159  bool replan_flag = false;
160  double org_learning_rate = 0.04, org_ridge_factor = 0.0, org_planning_time_limit = 10;
161  int org_max_iterations = 200;
162 
163  // storing the initial chomp parameters values
164  org_learning_rate = params.learning_rate_;
165  org_ridge_factor = params.ridge_factor_;
166  org_planning_time_limit = params.planning_time_limit_;
167  org_max_iterations = params.max_iterations_;
168 
169  std::unique_ptr<ChompOptimizer> optimizer;
170 
171  // create a non_const_params variable which stores the non constant version of the const params variable
172  ChompParameters params_nonconst = params;
173 
174  // while loop for replanning (recovery behaviour) if collision free optimized solution not found
175  while (true)
176  {
177  if (replan_flag)
178  {
179  // increase learning rate in hope to find a successful path; increase ridge factor to avoid obstacles; add 5
180  // additional secs in hope to find a solution; increase maximum iterations
181  params_nonconst.setRecoveryParams(params_nonconst.learning_rate_ + 0.02, params_nonconst.ridge_factor_ + 0.002,
182  params_nonconst.planning_time_limit_ + 5, params_nonconst.max_iterations_ + 50);
183  }
184 
185  // initialize a ChompOptimizer object to load up the optimizer with default parameters or with updated parameters in
186  // case of a recovery behaviour
187  optimizer =
188  std::make_unique<ChompOptimizer>(&trajectory, planning_scene, req.group_name, &params_nonconst, start_state);
189  if (!optimizer->isInitialized())
190  {
191  RCLCPP_ERROR(LOGGER, "Could not initialize optimizer");
192  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::PLANNING_FAILED;
193  return false;
194  }
195 
196  RCLCPP_DEBUG(LOGGER, "Optimization took %ld sec to create",
197  (std::chrono::system_clock::now() - create_time).count());
198 
199  bool optimization_result = optimizer->optimize();
200 
201  // replan with updated parameters if no solution is found
202  if (params_nonconst.enable_failure_recovery_)
203  {
204  RCLCPP_INFO(LOGGER,
205  "Planned with Chomp Parameters (learning_rate, ridge_factor, "
206  "planning_time_limit, max_iterations), attempt: # %d ",
207  (replan_count + 1));
208  RCLCPP_INFO(LOGGER, "Learning rate: %f ridge factor: %f planning time limit: %f max_iterations %d ",
209  params_nonconst.learning_rate_, params_nonconst.ridge_factor_, params_nonconst.planning_time_limit_,
210  params_nonconst.max_iterations_);
211 
212  if (!optimization_result && replan_count < params_nonconst.max_recovery_attempts_)
213  {
214  replan_count++;
215  replan_flag = true;
216  }
217  else
218  {
219  break;
220  }
221  }
222  else
223  break;
224  } // end of while loop
225 
226  // resetting the CHOMP Parameters to the original values after a successful plan
227  params_nonconst.setRecoveryParams(org_learning_rate, org_ridge_factor, org_planning_time_limit, org_max_iterations);
228 
229  RCLCPP_DEBUG(LOGGER, "Optimization actually took %ld sec to run",
230  (std::chrono::system_clock::now() - create_time).count());
231  create_time = std::chrono::system_clock::now();
232  // assume that the trajectory is now optimized, fill in the output structure:
233 
234  RCLCPP_DEBUG(LOGGER, "Output trajectory has %zd joints", trajectory.getNumJoints());
235 
236  auto result = std::make_shared<robot_trajectory::RobotTrajectory>(planning_scene->getRobotModel(), req.group_name);
237  // fill in the entire trajectory
238  for (size_t i = 0; i < trajectory.getNumPoints(); ++i)
239  {
240  const Eigen::MatrixXd::RowXpr source = trajectory.getTrajectoryPoint(i);
241  auto state = std::make_shared<moveit::core::RobotState>(start_state);
242  size_t joint_index = 0;
243  for (const moveit::core::JointModel* jm : result->getGroup()->getActiveJointModels())
244  {
245  assert(jm->getVariableCount() == 1);
246  state->setVariablePosition(jm->getFirstVariableIndex(), source[joint_index++]);
247  }
248  result->addSuffixWayPoint(state, 0.0);
249  }
250 
251  res.trajectory_.resize(1);
252  res.trajectory_[0] = result;
253 
254  RCLCPP_DEBUG(LOGGER, "Bottom took %ld sec to create", (std::chrono::system_clock::now() - create_time).count());
255  RCLCPP_DEBUG(LOGGER, "Serviced planning request in %ld wall-seconds",
256  (std::chrono::system_clock::now() - start_time).count());
257 
258  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::SUCCESS;
259  res.processing_time_.resize(1);
260  res.processing_time_[0] = std::chrono::duration<double>(std::chrono::system_clock::now() - start_time).count();
261 
262  // report planning failure if path has collisions
263  if (!optimizer->isCollisionFree())
264  {
265  RCLCPP_ERROR(LOGGER, "Motion plan is invalid.");
266  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::INVALID_MOTION_PLAN;
267  return false;
268  }
269 
270  // check that final state is within goal tolerances
272  const moveit::core::RobotState& last_state = result->getLastWayPoint();
273  for (const moveit_msgs::msg::JointConstraint& constraint : req.goal_constraints[0].joint_constraints)
274  {
275  if (!jc.configure(constraint) || !jc.decide(last_state).satisfied)
276  {
277  RCLCPP_ERROR(LOGGER, "Goal constraints are violated: %s", constraint.joint_name.c_str());
278  res.error_code_.val = moveit_msgs::msg::MoveItErrorCodes::GOAL_CONSTRAINTS_VIOLATED;
279  return false;
280  }
281  }
282 
283  res.processing_time_.resize(1);
284  res.processing_time_[0] = std::chrono::duration<double>(std::chrono::system_clock::now() - start_time).count();
285 
286  return true;
287 }
288 
289 } // namespace chomp
std::string trajectory_initialization_method_
void setRecoveryParams(double learning_rate, double ridge_factor, int planning_time_limit, int max_iterations)
bool solve(const planning_scene::PlanningSceneConstPtr &planning_scene, const planning_interface::MotionPlanRequest &req, const ChompParameters &params, planning_interface::MotionPlanDetailedResponse &res) const
Represents a discretized joint-space trajectory for CHOMP.
size_t getNumPoints() const
Gets the number of points in the trajectory.
void fillInCubicInterpolation()
Generates a cubic interpolation of the trajectory from the start index to end index.
bool fillInFromTrajectory(const robot_trajectory::RobotTrajectory &trajectory)
Receives the path obtained from a given MotionPlanDetailedResponse res object's trajectory (e....
size_t getNumJoints() const
Gets the number of joints in each trajectory point.
Eigen::MatrixXd::RowXpr getTrajectoryPoint(int traj_point)
void fillInLinearInterpolation()
Generates a linearly interpolated trajectory from the start index to end index.
void fillInMinJerk()
Generates a minimum jerk trajectory from the start index to end index.
Class for handling single DOF joint constraints.
ConstraintEvaluationResult decide(const moveit::core::RobotState &state, bool verbose=false) const override
Decide whether the constraint is satisfied in the indicated state.
bool configure(const moveit_msgs::msg::JointConstraint &jc)
Configure the constraint based on a moveit_msgs::msg::JointConstraint.
const std::vector< const JointModel * > & getActiveJointModels() const
Get the active joints in this group (that have controllable DOF). This does not include mimic joints.
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
Definition: joint_model.h:117
bool isContinuous() const
Check if this joint wraps around.
Representation of a robot's state. This includes position, velocity, acceleration and effort.
Definition: robot_state.h:90
void setVariablePosition(const std::string &variable, double value)
Set the position of a single variable. An exception is thrown if the variable name is not known.
Definition: robot_state.h:188
bool satisfiesBounds(double margin=0.0) const
bool robotStateMsgToRobotState(const Transforms &tf, const moveit_msgs::msg::RobotState &robot_state, RobotState &state, bool copy_attached_bodies=true)
Convert a robot state msg (with accompanying extra transforms) to a MoveIt robot state.
moveit_msgs::msg::MotionPlanRequest MotionPlanRequest
This namespace includes the central class for representing planning contexts.
bool satisfied
Whether or not the constraint or constraints were satisfied.
moveit_msgs::msg::MoveItErrorCodes error_code_
std::vector< robot_trajectory::RobotTrajectoryPtr > trajectory_