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 
37 #include <ros/ros.h>
42 #include <moveit_msgs/MotionPlanRequest.h>
43 
44 namespace chomp
45 {
47 {
48 }
49 
50 bool ChompPlanner::solve(const planning_scene::PlanningSceneConstPtr& planning_scene,
51  const moveit_msgs::MotionPlanRequest& req, const chomp::ChompParameters& params,
52  moveit_msgs::MotionPlanDetailedResponse& res) const
53 {
54  if (!planning_scene)
55  {
56  ROS_ERROR_STREAM_NAMED("chomp_planner", "No planning scene initialized.");
57  res.error_code.val = moveit_msgs::MoveItErrorCodes::FAILURE;
58  return false;
59  }
60 
61  if (req.start_state.joint_state.position.empty())
62  {
63  ROS_ERROR_STREAM_NAMED("chomp_planner", "Start state is empty");
64  res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE;
65  return false;
66  }
67 
68  if (not planning_scene->getRobotModel()->satisfiesPositionBounds(req.start_state.joint_state.position.data()))
69  {
70  ROS_ERROR_STREAM_NAMED("chomp_planner", "Start state violates joint limits");
71  res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE;
72  return false;
73  }
74 
75  ros::WallTime start_time = ros::WallTime::now();
76  ChompTrajectory trajectory(planning_scene->getRobotModel(), 3.0, .03, req.group_name);
77 
78  jointStateToArray(planning_scene->getRobotModel(), req.start_state.joint_state, req.group_name,
79  trajectory.getTrajectoryPoint(0));
80 
81  if (req.goal_constraints.empty())
82  {
83  ROS_ERROR_STREAM_NAMED("chomp_planner", "No goal constraints specified!");
84  res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
85  return false;
86  }
87 
88  if (req.goal_constraints[0].joint_constraints.empty())
89  {
90  ROS_ERROR_STREAM("Only joint-space goals are supported");
91  res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_GOAL_CONSTRAINTS;
92  return false;
93  }
94 
95  int goal_index = trajectory.getNumPoints() - 1;
96  trajectory.getTrajectoryPoint(goal_index) = trajectory.getTrajectoryPoint(0);
97  sensor_msgs::JointState js;
98  for (unsigned int i = 0; i < req.goal_constraints[0].joint_constraints.size(); i++)
99  {
100  js.name.push_back(req.goal_constraints[0].joint_constraints[i].joint_name);
101  js.position.push_back(req.goal_constraints[0].joint_constraints[i].position);
102  ROS_INFO_STREAM_NAMED("chomp_planner", "Setting joint " << req.goal_constraints[0].joint_constraints[i].joint_name
103  << " to position "
104  << req.goal_constraints[0].joint_constraints[i].position);
105  }
106  jointStateToArray(planning_scene->getRobotModel(), js, req.group_name, trajectory.getTrajectoryPoint(goal_index));
107 
108  const moveit::core::JointModelGroup* model_group =
109  planning_scene->getRobotModel()->getJointModelGroup(req.group_name);
110  // fix the goal to move the shortest angular distance for wrap-around joints:
111  for (size_t i = 0; i < model_group->getActiveJointModels().size(); i++)
112  {
113  const moveit::core::JointModel* model = model_group->getActiveJointModels()[i];
114  const moveit::core::RevoluteJointModel* revolute_joint =
115  dynamic_cast<const moveit::core::RevoluteJointModel*>(model);
116 
117  if (revolute_joint != NULL)
118  {
119  if (revolute_joint->isContinuous())
120  {
121  double start = (trajectory)(0, i);
122  double end = (trajectory)(goal_index, i);
123  ROS_INFO_STREAM("Start is " << start << " end " << end << " short " << shortestAngularDistance(start, end));
124  (trajectory)(goal_index, i) = start + shortestAngularDistance(start, end);
125  }
126  }
127  }
128 
129  const std::vector<std::string>& active_joint_names = model_group->getActiveJointModelNames();
130  const Eigen::MatrixXd goal_state = trajectory.getTrajectoryPoint(goal_index);
131  moveit::core::RobotState goal_robot_state = planning_scene->getCurrentState();
132  goal_robot_state.setVariablePositions(
133  active_joint_names, std::vector<double>(goal_state.data(), goal_state.data() + active_joint_names.size()));
134 
135  if (not goal_robot_state.satisfiesBounds())
136  {
137  ROS_ERROR_STREAM_NAMED("chomp_planner", "Goal state violates joint limits");
138  res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_ROBOT_STATE;
139  return false;
140  }
141 
142  // fill in an initial trajectory based on user choice from the chomp_config.yaml file
143  if (params.trajectory_initialization_method_.compare("quintic-spline") == 0)
144  trajectory.fillInMinJerk();
145  else if (params.trajectory_initialization_method_.compare("linear") == 0)
146  trajectory.fillInLinearInterpolation();
147  else if (params.trajectory_initialization_method_.compare("cubic") == 0)
148  trajectory.fillInCubicInterpolation();
149  else if (params.trajectory_initialization_method_.compare("fillTrajectory") == 0)
150  {
151  if (!(trajectory.fillInFromTrajectory(res)))
152  {
153  ROS_ERROR_STREAM_NAMED("chomp_planner", "Input trajectory has less than 2 points, "
154  "trajectory must contain at least start and goal state");
155  return false;
156  }
157  }
158  else
159  ROS_ERROR_STREAM_NAMED("chomp_planner", "invalid interpolation method specified in the chomp_planner file");
160 
161  ROS_INFO_NAMED("chomp_planner", "CHOMP trajectory initialized using method: %s ",
162  (params.trajectory_initialization_method_).c_str());
163 
164  // optimize!
165  moveit::core::RobotState start_state(planning_scene->getCurrentState());
166  moveit::core::robotStateMsgToRobotState(req.start_state, start_state);
167  start_state.update();
168 
169  ros::WallTime create_time = ros::WallTime::now();
170 
171  int replan_count = 0;
172  bool replan_flag = false;
173  double org_learning_rate = 0.04, org_ridge_factor = 0.0, org_planning_time_limit = 10;
174  int org_max_iterations = 200;
175 
176  // storing the initial chomp parameters values
177  org_learning_rate = params.learning_rate_;
178  org_ridge_factor = params.ridge_factor_;
179  org_planning_time_limit = params.planning_time_limit_;
180  org_max_iterations = params.max_iterations_;
181 
182  std::unique_ptr<ChompOptimizer> optimizer;
183 
184  // create a non_const_params variable which stores the non constant version of the const params variable
185  ChompParameters params_nonconst = params_nonconst.getNonConstParams(params);
186 
187  // while loop for replanning (recovery behaviour) if collision free optimized solution not found
188  while (true)
189  {
190  if (replan_flag)
191  {
192  // increase learning rate in hope to find a successful path; increase ridge factor to avoid obstacles; add 5
193  // additional secs in hope to find a solution; increase maximum iterations
194  params_nonconst.setRecoveryParams(params_nonconst.learning_rate_ + 0.02, params_nonconst.ridge_factor_ + 0.002,
195  params_nonconst.planning_time_limit_ + 5, params_nonconst.max_iterations_ + 50);
196  }
197 
198  // initialize a ChompOptimizer object to load up the optimizer with default parameters or with updated parameters in
199  // case of a recovery behaviour
200  optimizer.reset(new ChompOptimizer(&trajectory, planning_scene, req.group_name, &params_nonconst, start_state));
201  if (!optimizer->isInitialized())
202  {
203  ROS_ERROR_STREAM_NAMED("chomp_planner", "Could not initialize optimizer");
204  res.error_code.val = moveit_msgs::MoveItErrorCodes::PLANNING_FAILED;
205  return false;
206  }
207 
208  ROS_DEBUG_NAMED("chomp_planner", "Optimization took %f sec to create",
209  (ros::WallTime::now() - create_time).toSec());
210 
211  bool optimization_result = optimizer->optimize();
212 
213  // replan with updated parameters if no solution is found
214  if (params_nonconst.enable_failure_recovery_)
215  {
216  ROS_INFO_NAMED("chomp_planner", "Planned with Chomp Parameters (learning_rate, ridge_factor, "
217  "planning_time_limit, max_iterations), attempt: # %d ",
218  (replan_count + 1));
219  ROS_INFO_NAMED("chomp_planner", "Learning rate: %f ridge factor: %f planning time limit: %f max_iterations %d ",
220  params_nonconst.learning_rate_, params_nonconst.ridge_factor_,
221  params_nonconst.planning_time_limit_, params_nonconst.max_iterations_);
222 
223  if (!optimization_result && replan_count < params_nonconst.max_recovery_attempts_)
224  {
225  replan_count++;
226  replan_flag = true;
227  }
228  else
229  {
230  break;
231  }
232  }
233  else
234  break;
235  } // end of while loop
236 
237  // resetting the CHOMP Parameters to the original values after a successful plan
238  params_nonconst.setRecoveryParams(org_learning_rate, org_ridge_factor, org_planning_time_limit, org_max_iterations);
239 
240  ROS_DEBUG_NAMED("chomp_planner", "Optimization actually took %f sec to run",
241  (ros::WallTime::now() - create_time).toSec());
242  create_time = ros::WallTime::now();
243  // assume that the trajectory is now optimized, fill in the output structure:
244 
245  ROS_DEBUG_NAMED("chomp_planner", "Output trajectory has %d joints", trajectory.getNumJoints());
246 
247  res.trajectory.resize(1);
248 
249  res.trajectory[0].joint_trajectory.joint_names = active_joint_names;
250 
251  res.trajectory[0].joint_trajectory.header = req.start_state.joint_state.header; // @TODO this is probably a hack
252 
253  // fill in the entire trajectory
254  res.trajectory[0].joint_trajectory.points.resize(trajectory.getNumPoints());
255  for (int i = 0; i < trajectory.getNumPoints(); i++)
256  {
257  res.trajectory[0].joint_trajectory.points[i].positions.resize(trajectory.getNumJoints());
258  for (size_t j = 0; j < res.trajectory[0].joint_trajectory.points[i].positions.size(); j++)
259  {
260  res.trajectory[0].joint_trajectory.points[i].positions[j] = trajectory.getTrajectoryPoint(i)(j);
261  }
262  // Setting invalid timestamps.
263  // Further filtering is required to set valid timestamps accounting for velocity and acceleration constraints.
264  res.trajectory[0].joint_trajectory.points[i].time_from_start = ros::Duration(0.0);
265  }
266 
267  ROS_DEBUG_NAMED("chomp_planner", "Bottom took %f sec to create", (ros::WallTime::now() - create_time).toSec());
268  ROS_DEBUG_NAMED("chomp_planner", "Serviced planning request in %f wall-seconds, trajectory duration is %f",
269  (ros::WallTime::now() - start_time).toSec(),
270  res.trajectory[0].joint_trajectory.points[goal_index].time_from_start.toSec());
271  res.error_code.val = moveit_msgs::MoveItErrorCodes::SUCCESS;
272  res.processing_time.push_back((ros::WallTime::now() - start_time).toSec());
273 
274  // report planning failure if path has collisions
275  if (not optimizer->isCollisionFree())
276  {
277  ROS_ERROR_STREAM_NAMED("chomp_planner", "Motion plan is invalid.");
278  res.error_code.val = moveit_msgs::MoveItErrorCodes::INVALID_MOTION_PLAN;
279  return false;
280  }
281 
282  // check that final state is within goal tolerances
283  kinematic_constraints::JointConstraint jc(planning_scene->getRobotModel());
284  robot_state::RobotState last_state(start_state);
285  last_state.setVariablePositions(res.trajectory[0].joint_trajectory.joint_names,
286  res.trajectory[0].joint_trajectory.points.back().positions);
287 
288  bool constraints_are_ok = true;
289  for (const moveit_msgs::JointConstraint& constraint : req.goal_constraints[0].joint_constraints)
290  {
291  constraints_are_ok = constraints_are_ok and jc.configure(constraint);
292  constraints_are_ok = constraints_are_ok and jc.decide(last_state).satisfied;
293  if (not constraints_are_ok)
294  {
295  ROS_ERROR_STREAM_NAMED("chomp_planner", "Goal constraints are violated: " << constraint.joint_name);
296  res.error_code.val = moveit_msgs::MoveItErrorCodes::GOAL_CONSTRAINTS_VIOLATED;
297  return false;
298  }
299  }
300 
301  return true;
302 }
303 }
std::string trajectory_initialization_method_
#define ROS_INFO_NAMED(name,...)
ROSCPP_DECL void start()
#define ROS_ERROR_STREAM_NAMED(name, args)
ChompParameters getNonConstParams(ChompParameters params)
#define ROS_INFO_STREAM_NAMED(name, args)
void setVariablePositions(const double *position)
#define ROS_DEBUG_NAMED(name,...)
void setRecoveryParams(double learning_rate, double ridge_factor, int planning_time_limit, int max_iterations)
robot_trajectory::RobotTrajectory trajectory(rmodel,"right_arm")
bool robotStateMsgToRobotState(const Transforms &tf, const moveit_msgs::RobotState &robot_state, RobotState &state, bool copy_attached_bodies=true)
bool solve(const planning_scene::PlanningSceneConstPtr &planning_scene, const moveit_msgs::MotionPlanRequest &req, const ChompParameters &params, moveit_msgs::MotionPlanDetailedResponse &res) const
static void jointStateToArray(const moveit::core::RobotModelConstPtr &kmodel, const sensor_msgs::JointState &joint_state, const std::string &planning_group_name, Eigen::MatrixXd::RowXpr joint_array)
Definition: chomp_utils.h:55
bool satisfiesBounds(double margin=0.0) const
int max_iterations_
maximum time the optimizer can take to find a solution before terminating
Represents a discretized joint-space trajectory for CHOMP.
static double shortestAngularDistance(double start, double end)
Definition: chomp_utils.h:88
#define ROS_INFO_STREAM(args)
static WallTime now()
bool enable_failure_recovery_
trajectory initialization method to be specified
#define ROS_ERROR_STREAM(args)


chomp_motion_planner
Author(s): Gil Jones , Mrinal Kalakrishnan
autogenerated on Sun Oct 18 2020 13:17:08