00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00037 #include <chomp_motion_planner/chomp_planner_node.h>
00038 #include <chomp_motion_planner/chomp_trajectory.h>
00039 #include <chomp_motion_planner/chomp_utils.h>
00040 #include <chomp_motion_planner/chomp_parameters.h>
00041 #include <chomp_motion_planner/chomp_optimizer.h>
00042 #include <kdl/jntarray.hpp>
00043 #include <angles/angles.h>
00044 #include <visualization_msgs/MarkerArray.h>
00045 #include <spline_smoother/cubic_trajectory.h>
00046 #include <motion_planning_msgs/FilterJointTrajectory.h>
00047
00048 #include <map>
00049 #include <vector>
00050 #include <string>
00051
00052 using namespace std;
00053
00054 namespace chomp
00055 {
00056
00057 ChompPlannerNode::ChompPlannerNode(ros::NodeHandle node_handle) : node_handle_(node_handle)
00058
00059 {
00060
00061 }
00062
00063 bool ChompPlannerNode::init()
00064 {
00065
00066 node_handle_.param("trajectory_duration", trajectory_duration_, 3.0);
00067 node_handle_.param("trajectory_discretization", trajectory_discretization_, 0.03);
00068 node_handle_.param("use_additional_trajectory_filter", use_trajectory_filter_, true);
00069 node_handle_.param("minimum_spline_points", minimum_spline_points_, 40);
00070 node_handle_.param("maximum_spline_points", maximum_spline_points_, 100);
00071
00072 if(node_handle_.hasParam("joint_velocity_limits")) {
00073 XmlRpc::XmlRpcValue velocity_limits;
00074
00075 node_handle_.getParam("joint_velocity_limits", velocity_limits);
00076
00077 if(velocity_limits.getType() == XmlRpc::XmlRpcValue::TypeStruct) {
00078 if(velocity_limits.size() > 0) {
00079 for(XmlRpc::XmlRpcValue::iterator it = velocity_limits.begin();
00080 it != velocity_limits.end();
00081 it++) {
00082 joint_velocity_limits_[it->first] = it->second;
00083 ROS_DEBUG_STREAM("Vel limit for " << it->first << " is " << joint_velocity_limits_[it->first]);
00084 }
00085 }
00086 }
00087 }
00088
00089
00090
00091 collision_models_ = new planning_environment::CollisionModels("robot_description");
00092
00093 if(!collision_models_->loadedModels()) {
00094 ROS_ERROR("Collision models could not load models");
00095 return false;
00096 }
00097
00098 monitor_ = new planning_environment::CollisionSpaceMonitor(collision_models_, &tf_);
00099
00100 monitor_->waitForState();
00101 monitor_->setUseCollisionMap(true);
00102 monitor_->startEnvironmentMonitor();
00103
00104 reference_frame_ = monitor_->getRobotFrameId();
00105
00106
00107 if (!chomp_robot_model_.init(monitor_, reference_frame_))
00108 return false;
00109
00110
00111 chomp_parameters_.initFromNodeHandle();
00112
00113 double max_radius_clearance = chomp_robot_model_.getMaxRadiusClearance();
00114
00115
00116 if (!chomp_collision_space_.init(monitor_,max_radius_clearance, reference_frame_))
00117 return false;
00118
00119
00120 vis_marker_array_publisher_ = root_handle_.advertise<visualization_msgs::MarkerArray>( "visualization_marker_array", 0 );
00121 vis_marker_publisher_ = root_handle_.advertise<visualization_msgs::Marker>( "visualization_marker", 0 );
00122
00123
00124 plan_kinematic_path_service_ = root_handle_.advertiseService("chomp_planner_longrange/plan_path", &ChompPlannerNode::planKinematicPath, this);
00125
00126 filter_joint_trajectory_service_ = root_handle_.advertiseService("chomp_planner_longrange/filter_trajectory_with_constraints", &ChompPlannerNode::filterJointTrajectory, this);
00127
00128 if(use_trajectory_filter_) {
00129 filter_trajectory_client_ = root_handle_.serviceClient<motion_planning_msgs::FilterJointTrajectoryWithConstraints>("trajectory_filter/filter_trajectory_with_constraints");
00130
00131 ros::service::waitForService("trajectory_filter/filter_trajectory_with_constraints");
00132 }
00133
00134 ROS_INFO("Initalized CHOMP planning service...");
00135
00136 return true;
00137 }
00138
00139 ChompPlannerNode::~ChompPlannerNode()
00140 {
00141 delete collision_models_;
00142 delete monitor_;
00143 }
00144
00145 int ChompPlannerNode::run()
00146 {
00147 ros::spin();
00148 return 0;
00149 }
00150
00151 bool ChompPlannerNode::planKinematicPath(motion_planning_msgs::GetMotionPlan::Request &req, motion_planning_msgs::GetMotionPlan::Response &res)
00152 {
00153 if (!(req.motion_plan_request.goal_constraints.position_constraints.empty() && req.motion_plan_request.goal_constraints.orientation_constraints.empty()))
00154 {
00155 ROS_ERROR("CHOMP cannot handle pose contraints yet.");
00156 return false;
00157 }
00158
00159 sensor_msgs::JointState joint_goal_chomp = motion_planning_msgs::jointConstraintsToJointState(req.motion_plan_request.goal_constraints.joint_constraints);
00160 ROS_INFO("Chomp goal");
00161
00162 if(joint_goal_chomp.name.size() != joint_goal_chomp.position.size())
00163 {
00164 ROS_ERROR("Invalid chomp goal");
00165 return false;
00166 }
00167
00168 for(unsigned int i=0; i<joint_goal_chomp.name.size(); i++)
00169 {
00170 ROS_INFO("%s %f",joint_goal_chomp.name[i].c_str(),joint_goal_chomp.position[i]);
00171 }
00172
00173 ros::WallTime start_time = ros::WallTime::now();
00174 ROS_INFO("Received planning request...");
00175
00176
00177 const ChompRobotModel::ChompPlanningGroup* group = chomp_robot_model_.getPlanningGroup(req.motion_plan_request.group_name);
00178
00179 if (group==NULL)
00180 {
00181 ROS_ERROR("Could not load planning group %s", req.motion_plan_request.group_name.c_str());
00182 return false;
00183 }
00184
00185 ChompTrajectory trajectory(&chomp_robot_model_, trajectory_duration_, trajectory_discretization_);
00186
00187
00188 chomp_robot_model_.jointStateToArray(req.motion_plan_request.start_state.joint_state, trajectory.getTrajectoryPoint(0));
00189
00190 ROS_INFO_STREAM("Joint state has " << req.motion_plan_request.start_state.joint_state.name.size() << " joints");
00191
00192
00193 chomp_collision_space_.setStartState(*group, req.motion_plan_request.start_state);
00194
00195
00196
00197 chomp_robot_model_.generateAttachedObjectCollisionPoints(&(req.motion_plan_request.start_state));
00198 chomp_robot_model_.populatePlanningGroupCollisionPoints();
00199
00200
00201
00202 int goal_index = trajectory.getNumPoints()-1;
00203 trajectory.getTrajectoryPoint(goal_index) = trajectory.getTrajectoryPoint(0);
00204 chomp_robot_model_.jointStateToArray(motion_planning_msgs::jointConstraintsToJointState(req.motion_plan_request.goal_constraints.joint_constraints), trajectory.getTrajectoryPoint(goal_index));
00205
00206
00207 for (int i=0; i<group->num_joints_; i++)
00208 {
00209 if (group->chomp_joints_[i].wrap_around_)
00210 {
00211 int kdl_index = group->chomp_joints_[i].kdl_joint_index_;
00212 double start = trajectory(0, kdl_index);
00213 double end = trajectory(goal_index, kdl_index);
00214 trajectory(goal_index, kdl_index) = start + angles::shortest_angular_distance(start, end);
00215 }
00216 }
00217
00218
00219 trajectory.fillInMinJerk();
00220
00221
00222 chomp_parameters_.setPlanningTimeLimit(req.motion_plan_request.allowed_planning_time.toSec());
00223
00224
00225 ros::WallTime create_time = ros::WallTime::now();
00226 ChompOptimizer optimizer(&trajectory, &chomp_robot_model_, group, &chomp_parameters_,
00227 vis_marker_array_publisher_, vis_marker_publisher_, &chomp_collision_space_);
00228 ROS_INFO("Optimization took %f sec to create", (ros::WallTime::now() - create_time).toSec());
00229 optimizer.optimize();
00230 ROS_INFO("Optimization actually took %f sec to run", (ros::WallTime::now() - create_time).toSec());
00231
00232
00233
00234 std::vector<double> velocity_limits(group->num_joints_, std::numeric_limits<double>::max());
00235
00236
00237 res.trajectory.joint_trajectory.joint_names.resize(group->num_joints_);
00238 for (int i=0; i<group->num_joints_; i++)
00239 {
00240 res.trajectory.joint_trajectory.joint_names[i] = group->chomp_joints_[i].joint_name_;
00241
00242 if (joint_velocity_limits_.find(res.trajectory.joint_trajectory.joint_names[i])==joint_velocity_limits_.end())
00243 {
00244 joint_velocity_limits_[res.trajectory.joint_trajectory.joint_names[i]] = std::numeric_limits<double>::max();
00245 }
00246 velocity_limits[i] = joint_velocity_limits_[res.trajectory.joint_trajectory.joint_names[i]];
00247 }
00248
00249 res.trajectory.joint_trajectory.header = req.motion_plan_request.start_state.joint_state.header;
00250
00251
00252 res.trajectory.joint_trajectory.points.resize(trajectory.getNumPoints());
00253 for (int i=0; i<=goal_index; i++)
00254 {
00255 res.trajectory.joint_trajectory.points[i].positions.resize(group->num_joints_);
00256 for (int j=0; j<group->num_joints_; j++)
00257 {
00258 int kdl_joint_index = chomp_robot_model_.urdfNameToKdlNumber(res.trajectory.joint_trajectory.joint_names[j]);
00259 res.trajectory.joint_trajectory.points[i].positions[j] = trajectory(i, kdl_joint_index);
00260 }
00261 if (i==0)
00262 res.trajectory.joint_trajectory.points[i].time_from_start = ros::Duration(0.0);
00263 else
00264 {
00265 double duration = trajectory.getDiscretization();
00266
00267 for (int j=0; j<group->num_joints_; j++)
00268 {
00269 double d = fabs(res.trajectory.joint_trajectory.points[i].positions[j] - res.trajectory.joint_trajectory.points[i-1].positions[j]) / velocity_limits[j];
00270 if (d > duration)
00271 duration = d;
00272 }
00273 res.trajectory.joint_trajectory.points[i].time_from_start = res.trajectory.joint_trajectory.points[i-1].time_from_start + ros::Duration(duration);
00274 }
00275 }
00276
00277 ROS_INFO("Bottom took %f sec to create", (ros::WallTime::now() - create_time).toSec());
00278 ROS_INFO("Serviced planning request in %f wall-seconds, trajectory duration is %f", (ros::WallTime::now() - start_time).toSec(), res.trajectory.joint_trajectory.points[goal_index].time_from_start.toSec());
00279 return true;
00280 }
00281
00282 bool ChompPlannerNode::filterJointTrajectory(motion_planning_msgs::FilterJointTrajectoryWithConstraints::Request &req, motion_planning_msgs::FilterJointTrajectoryWithConstraints::Response &res)
00283 {
00284 ros::WallTime start_time = ros::WallTime::now();
00285 ROS_INFO_STREAM("Received filtering request with trajectory size " << req.trajectory.points.size());
00286
00287 if(req.path_constraints.joint_constraints.size() > 0 ||
00288 req.path_constraints.position_constraints.size() > 0 ||
00289 req.path_constraints.orientation_constraints.size() > 0 ||
00290 req.path_constraints.visibility_constraints.size() > 0) {
00291 if(use_trajectory_filter_) {
00292 ROS_INFO("Chomp can't handle path constraints, passing through to other trajectory filters");
00293 if(!filter_trajectory_client_.call(req,res)) {
00294 ROS_INFO("Pass through failed");
00295 } else {
00296 ROS_INFO("Pass through succeeded");
00297 }
00298 } else {
00299 ROS_INFO("Chomp can't handle path constraints, and not set up to use additional filter");
00300 }
00301 return true;
00302 }
00303 for (unsigned int i=0; i< req.trajectory.points.size(); i++)
00304 {
00305 req.trajectory.points[i].velocities.resize(req.trajectory.joint_names.size(),0.0);
00306 }
00307
00308 getLimits(req.trajectory, req.limits);
00309
00310 trajectory_msgs::JointTrajectory jtraj;
00311
00312 int num_points = req.trajectory.points.size();
00313 if(num_points > maximum_spline_points_) {
00314 num_points = maximum_spline_points_;
00315 } else if(num_points < minimum_spline_points_) {
00316 num_points = minimum_spline_points_;
00317 }
00318
00319
00320 spline_smoother::CubicTrajectory trajectory_solver;
00321 spline_smoother::SplineTrajectory spline;
00322
00323 trajectory_solver.parameterize(req.trajectory,req.limits,spline);
00324
00325 double smoother_time;
00326 spline_smoother::getTotalTime(spline, smoother_time);
00327
00328 ROS_INFO_STREAM("Total time given is " << smoother_time);
00329
00330 double t = 0.0;
00331 std::vector<double> times(num_points);
00332 for(int i = 0; i < num_points; i++,t += smoother_time/(1.0*(num_points-1))) {
00333 times[i] = t;
00334 }
00335
00336 spline_smoother::sampleSplineTrajectory(spline, times, jtraj);
00337
00338
00339
00340 t = 0.0;
00341 for(unsigned int i = 0; i < jtraj.points.size(); i++, t += smoother_time/(1.0*(num_points-1))) {
00342 jtraj.points[i].time_from_start = ros::Duration(t);
00343 }
00344
00345 ROS_INFO_STREAM("Sampled trajectory has " << jtraj.points.size() << " points with " << jtraj.points[0].positions.size() << " joints");
00346
00347
00348 std::string group_name;
00349 if(req.trajectory.joint_names[0].at(0) == 'r') {
00350 group_name = "right_arm";
00351 } else if(req.trajectory.joint_names[0].at(0) == 'l') {
00352 group_name="left_arm";
00353 } else {
00354 ROS_INFO_STREAM("Joint names don't seem to correspond to left or right arm");
00355 }
00356
00357
00358 const ChompRobotModel::ChompPlanningGroup* group = chomp_robot_model_.getPlanningGroup(group_name);
00359
00360 if (group==NULL)
00361 {
00362 ROS_ERROR("Could not load planning group %s", "right_arm");
00363 return false;
00364 }
00365
00366 ChompTrajectory trajectory(&chomp_robot_model_, group, jtraj);
00367
00368
00369 motion_planning_msgs::RobotState robot_state;
00370 monitor_->getCurrentRobotState(robot_state);
00371
00372 chomp_robot_model_.jointStateToArray(robot_state.joint_state, trajectory.getTrajectoryPoint(0));
00373 chomp_collision_space_.setStartState(*group, robot_state);
00374
00375
00376
00377
00378 chomp_robot_model_.generateAttachedObjectCollisionPoints(&(robot_state));
00379 chomp_robot_model_.populatePlanningGroupCollisionPoints();
00380
00381
00382
00383 int goal_index = trajectory.getNumPoints()-1;
00384 trajectory.getTrajectoryPoint(goal_index) = trajectory.getTrajectoryPoint(0);
00385
00386 sensor_msgs::JointState goal_state = motion_planning_msgs::createJointState(req.trajectory.joint_names, jtraj.points.back().positions);
00387
00388 chomp_robot_model_.jointStateToArray(goal_state, trajectory.getTrajectoryPoint(goal_index));
00389
00390
00391 for (int i=0; i<group->num_joints_; i++)
00392 {
00393 if (group->chomp_joints_[i].wrap_around_)
00394 {
00395 int kdl_index = group->chomp_joints_[i].kdl_joint_index_;
00396 double start = trajectory(0, kdl_index);
00397 double end = trajectory(goal_index, kdl_index);
00398 trajectory(goal_index, kdl_index) = start + angles::shortest_angular_distance(start, end);
00399 }
00400 }
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415 trajectory.fillInMinJerk();
00416 trajectory.overwriteTrajectory(jtraj);
00417
00418
00419 chomp_parameters_.setPlanningTimeLimit(req.allowed_time.toSec());
00420
00421
00422 ChompOptimizer optimizer(&trajectory, &chomp_robot_model_, group, &chomp_parameters_,
00423 vis_marker_array_publisher_, vis_marker_publisher_, &chomp_collision_space_);
00424 optimizer.optimize();
00425
00426
00427
00428 std::vector<double> velocity_limits(group->num_joints_, std::numeric_limits<double>::max());
00429
00430
00431 res.trajectory.joint_names.resize(group->num_joints_);
00432 for (int i=0; i<group->num_joints_; i++)
00433 {
00434 res.trajectory.joint_names[i] = group->chomp_joints_[i].joint_name_;
00435 velocity_limits[i] = joint_limits_[res.trajectory.joint_names[i]].max_velocity;
00436 }
00437
00438 res.trajectory.header.stamp = ros::Time::now();
00439 res.trajectory.header.frame_id = reference_frame_;
00440
00441
00442 res.trajectory.points.resize(trajectory.getNumPoints());
00443 for (int i=0; i< trajectory.getNumPoints(); i++)
00444 {
00445 res.trajectory.points[i].positions.resize(group->num_joints_);
00446 res.trajectory.points[i].velocities.resize(group->num_joints_);
00447 for (int j=0; j<group->num_joints_; j++)
00448 {
00449 int kdl_joint_index = chomp_robot_model_.urdfNameToKdlNumber(res.trajectory.joint_names[j]);
00450 res.trajectory.points[i].positions[j] = trajectory(i, kdl_joint_index);
00451 }
00452 if (i==0)
00453 res.trajectory.points[i].time_from_start = ros::Duration(0.0);
00454 else
00455 {
00456 double duration = trajectory.getDiscretization();
00457
00458 for (int j=0; j<group->num_joints_; j++)
00459 {
00460 double d = fabs(res.trajectory.points[i].positions[j] - res.trajectory.points[i-1].positions[j]) / velocity_limits[j];
00461 if (d > duration)
00462 duration = d;
00463 }
00464 try {
00465 res.trajectory.points[i].time_from_start = res.trajectory.points[i-1].time_from_start + ros::Duration(duration);
00466 } catch(...) {
00467 ROS_INFO_STREAM("Potentially weird duration of " << duration);
00468 }
00469 }
00470 }
00471 motion_planning_msgs::FilterJointTrajectoryWithConstraints::Request next_req;
00472 motion_planning_msgs::FilterJointTrajectoryWithConstraints::Response next_res;
00473
00474 if(use_trajectory_filter_) {
00475 next_req = req;
00476 next_req.trajectory = res.trajectory;
00477 next_req.allowed_time=ros::Duration(1.0);
00478
00479 if(filter_trajectory_client_.call(next_req, next_res)) {
00480 ROS_INFO_STREAM("Filter call ok. Sent trajectory had " << res.trajectory.points.size() << " points. Returned trajectory has " << next_res.trajectory.points.size() << " points ");
00481 } else {
00482 ROS_INFO("Filter call not ok");
00483 }
00484
00485 res.trajectory = next_res.trajectory;
00486 res.error_code = next_res.error_code;
00487 res.trajectory.header.stamp = ros::Time::now();
00488 res.trajectory.header.frame_id = reference_frame_;
00489 } else {
00490 res.error_code.val = res.error_code.val = res.error_code.SUCCESS;
00491 }
00492
00493
00494 for (unsigned int i=1; i<res.trajectory.points.size()-1; ++i)
00495 {
00496 double dt1 = (res.trajectory.points[i].time_from_start - res.trajectory.points[i-1].time_from_start).toSec();
00497 double dt2 = (res.trajectory.points[i+1].time_from_start - res.trajectory.points[i].time_from_start).toSec();
00498
00499
00500 for (int j=0; j<group->num_joints_; ++j)
00501 {
00502 double dx1 = res.trajectory.points[i].positions[j] - res.trajectory.points[i-1].positions[j];
00503 double dx2 = res.trajectory.points[i+1].positions[j] - res.trajectory.points[i].positions[j];
00504
00505 double v1 = dx1/dt1;
00506 double v2 = dx2/dt2;
00507
00508 res.trajectory.points[i].velocities[j] = 0.5*(v1 + v2);
00509 }
00510 }
00511
00512 ROS_INFO("Serviced filter request in %f wall-seconds, trajectory duration is %f", (ros::WallTime::now() - start_time).toSec(), res.trajectory.points.back().time_from_start.toSec());
00513 return true;
00514
00515 }
00516
00517 void ChompPlannerNode::getLimits(const trajectory_msgs::JointTrajectory& trajectory,
00518 std::vector<motion_planning_msgs::JointLimits>& limits_out)
00519 {
00520 int num_joints = trajectory.joint_names.size();
00521 limits_out.resize(num_joints);
00522 for (int i=0; i<num_joints; ++i)
00523 {
00524 std::map<std::string, motion_planning_msgs::JointLimits>::const_iterator limit_it = joint_limits_.find(trajectory.joint_names[i]);
00525 motion_planning_msgs::JointLimits limits;
00526 if (limit_it == joint_limits_.end())
00527 {
00528
00529 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/min_position", limits.min_position, -std::numeric_limits<double>::max());
00530 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/max_position", limits.max_position, std::numeric_limits<double>::max());
00531 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/max_velocity", limits.max_velocity, std::numeric_limits<double>::max());
00532 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/max_acceleration", limits.max_acceleration, std::numeric_limits<double>::max());
00533 bool boolean;
00534 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/has_position_limits", boolean, false);
00535 limits.has_position_limits = boolean?1:0;
00536 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/has_velocity_limits", boolean, false);
00537 limits.has_velocity_limits = boolean?1:0;
00538 node_handle_.param("joint_limits/"+trajectory.joint_names[i]+"/has_acceleration_limits", boolean, false);
00539 limits.has_acceleration_limits = boolean?1:0;
00540 joint_limits_.insert(make_pair(trajectory.joint_names[i], limits));
00541 }
00542 else
00543 {
00544 limits = limit_it->second;
00545 }
00546 limits_out[i] = limits;
00547 }
00548 }
00549
00550 }
00551
00552 int main(int argc, char** argv)
00553 {
00554 ros::init(argc, argv, "chomp_planner_node");
00555
00556
00557
00558
00559 ros::NodeHandle node_handle("~");
00560 chomp::ChompPlannerNode chomp_planner_node(node_handle);
00561 if (!chomp_planner_node.init())
00562 return 1;
00563 return chomp_planner_node.run();
00564
00565 }