moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
move_action_capability.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: Ioan Sucan */
36
38
47
48namespace move_group
49{
50
51namespace
52{
53rclcpp::Logger getLogger()
54{
55 return moveit::getLogger("moveit.ros.move_group.move_action");
56}
57} // namespace
58
60 : MoveGroupCapability("move_action"), move_state_(IDLE), preempt_requested_{ false }
61{
62}
63
65{
66 // start the move action server
67 auto node = context_->moveit_cpp_->getNode();
68 execute_action_server_ = rclcpp_action::create_server<MGAction>(
69 node, MOVE_ACTION,
70 [](const rclcpp_action::GoalUUID& /*unused*/, const std::shared_ptr<const MGAction::Goal>& /*unused*/) {
71 RCLCPP_INFO(getLogger(), "MoveGroupMoveAction: Received request");
72 return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
73 },
74 [this](const std::shared_ptr<MGActionGoal>& /*unused*/) {
75 RCLCPP_INFO(getLogger(), "MoveGroupMoveAction: Received request to cancel goal");
76 preemptMoveCallback();
77 return rclcpp_action::CancelResponse::ACCEPT;
78 },
79 [this](const std::shared_ptr<MGActionGoal>& goal) {
80 std::thread{ [this](const std::shared_ptr<move_group::MGActionGoal>& goal) { executeMoveCallback(goal); }, goal }
81 .detach();
82 });
83}
84
85void MoveGroupMoveAction::executeMoveCallback(const std::shared_ptr<MGActionGoal>& goal)
86{
87 goal_ = goal;
88 RCLCPP_INFO(getLogger(), "executing..");
89 setMoveState(PLANNING, goal_);
90 // before we start planning, ensure that we have the latest robot state received...
91 auto node = context_->moveit_cpp_->getNode();
92 context_->planning_scene_monitor_->waitForCurrentRobotState(node->get_clock()->now());
93 context_->planning_scene_monitor_->updateFrameTransforms();
94
95 auto action_res = std::make_shared<MGAction::Result>();
96 if (goal->get_goal()->planning_options.plan_only || !context_->allow_trajectory_execution_)
97 {
98 if (!goal->get_goal()->planning_options.plan_only)
99 {
100 RCLCPP_WARN(getLogger(), "This instance of MoveGroup is not allowed to execute trajectories "
101 "but the goal request has plan_only set to false. "
102 "Only a motion plan will be computed anyway.");
103 }
104 executeMoveCallbackPlanOnly(goal, action_res);
105 }
106 else
107 {
108 executeMoveCallbackPlanAndExecute(goal, action_res);
109 }
110
111 bool planned_trajectory_empty = trajectory_processing::isTrajectoryEmpty(action_res->planned_trajectory);
112 // @todo: Response messages
113 RCLCPP_INFO_STREAM(getLogger(), getActionResultString(action_res->error_code, planned_trajectory_empty,
114 goal->get_goal()->planning_options.plan_only));
115 if (action_res->error_code.val == moveit_msgs::msg::MoveItErrorCodes::SUCCESS)
116 {
117 goal->succeed(action_res);
118 }
119 else if (action_res->error_code.val == moveit_msgs::msg::MoveItErrorCodes::PREEMPTED)
120 {
121 goal->canceled(action_res);
122 }
123 else
124 {
125 goal->abort(action_res);
126 }
127
128 setMoveState(IDLE, goal_);
129 preempt_requested_ = false;
130 goal_.reset();
131}
132
133void MoveGroupMoveAction::executeMoveCallbackPlanAndExecute(const std::shared_ptr<MGActionGoal>& goal,
134 std::shared_ptr<MGAction::Result>& action_res)
135{
136 RCLCPP_INFO(getLogger(), "Combined planning and execution request received for MoveGroup action. "
137 "Forwarding to planning and execution pipeline.");
138
139 if (moveit::core::isEmpty(goal->get_goal()->planning_options.planning_scene_diff))
140 {
141 planning_scene_monitor::LockedPlanningSceneRO lscene(context_->planning_scene_monitor_);
142 const moveit::core::RobotState& current_state = lscene->getCurrentState();
143
144 // check to see if the desired constraints are already met
145 for (std::size_t i = 0; i < goal->get_goal()->request.goal_constraints.size(); ++i)
146 {
147 if (lscene->isStateConstrained(
148 current_state, kinematic_constraints::mergeConstraints(goal->get_goal()->request.goal_constraints[i],
149 goal->get_goal()->request.path_constraints)))
150 {
151 RCLCPP_INFO(getLogger(), "Goal constraints are already satisfied. No need to plan or execute any motions");
152 action_res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::SUCCESS;
153 return;
154 }
155 }
156 }
157
158 plan_execution::PlanExecution::Options opt;
159
160 const moveit_msgs::msg::MotionPlanRequest& motion_plan_request =
161 moveit::core::isEmpty(goal->get_goal()->request.start_state) ? goal->get_goal()->request :
162 clearRequestStartState(goal->get_goal()->request);
163 const moveit_msgs::msg::PlanningScene& planning_scene_diff =
164 moveit::core::isEmpty(goal->get_goal()->planning_options.planning_scene_diff.robot_state) ?
165 goal->get_goal()->planning_options.planning_scene_diff :
166 clearSceneRobotState(goal->get_goal()->planning_options.planning_scene_diff);
167
168 opt.replan = goal->get_goal()->planning_options.replan;
169 opt.replan_attemps = goal->get_goal()->planning_options.replan_attempts;
170 opt.replan_delay = goal->get_goal()->planning_options.replan_delay;
171 opt.before_execution_callback_ = [this] { startMoveExecutionCallback(); };
172
173 opt.plan_callback = [this, &motion_plan_request](plan_execution::ExecutableMotionPlan& plan) {
174 return planUsingPlanningPipeline(motion_plan_request, plan);
175 };
176
177 plan_execution::ExecutableMotionPlan plan;
178 if (preempt_requested_)
179 {
180 RCLCPP_INFO(getLogger(), "Preempt requested before the goal is planned and executed.");
181 action_res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::PREEMPTED;
182 return;
183 }
184
185 context_->plan_execution_->planAndExecute(plan, planning_scene_diff, opt);
186
187 convertToMsg(plan.plan_components, action_res->trajectory_start, action_res->planned_trajectory);
188 if (plan.executed_trajectory)
189 plan.executed_trajectory->getRobotTrajectoryMsg(action_res->executed_trajectory);
190 action_res->error_code = plan.error_code;
191}
192
193void MoveGroupMoveAction::executeMoveCallbackPlanOnly(const std::shared_ptr<MGActionGoal>& goal,
194 std::shared_ptr<MGAction::Result>& action_res)
195{
196 RCLCPP_INFO(getLogger(), "Planning request received for MoveGroup action. Forwarding to planning pipeline.");
197
198 planning_interface::MotionPlanResponse res;
199
200 if (preempt_requested_)
201 {
202 RCLCPP_INFO(getLogger(), "Preempt requested before the goal is planned.");
203 action_res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::PREEMPTED;
204 return;
205 }
206
207 // Select planning_pipeline to handle request
208 const planning_pipeline::PlanningPipelinePtr planning_pipeline =
209 resolvePlanningPipeline(goal->get_goal()->request.pipeline_id);
210 if (!planning_pipeline)
211 {
212 action_res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
213 return;
214 }
215
216 try
217 {
218 auto scene =
219 context_->planning_scene_monitor_->copyPlanningScene(goal->get_goal()->planning_options.planning_scene_diff);
220 if (!planning_pipeline->generatePlan(scene, goal->get_goal()->request, res, context_->debug_))
221 {
222 RCLCPP_ERROR(getLogger(), "Generating a plan with planning pipeline failed.");
223 res.error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
224 }
225 }
226 catch (std::exception& ex)
227 {
228 RCLCPP_ERROR(getLogger(), "Planning pipeline threw an exception: %s", ex.what());
229 res.error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
230 }
231
232 convertToMsg(res.trajectory, action_res->trajectory_start, action_res->planned_trajectory);
233 action_res->error_code = res.error_code;
234 action_res->planning_time = res.planning_time;
235}
236
237bool MoveGroupMoveAction::planUsingPlanningPipeline(const planning_interface::MotionPlanRequest& req,
238 plan_execution::ExecutableMotionPlan& plan)
239{
240 setMoveState(PLANNING, goal_);
241
242 bool solved = false;
243 planning_interface::MotionPlanResponse res;
244
245 // Select planning_pipeline to handle request
246 const planning_pipeline::PlanningPipelinePtr planning_pipeline = resolvePlanningPipeline(req.pipeline_id);
247 if (!planning_pipeline)
248 {
249 res.error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
250 return solved;
251 }
252
253 try
254 {
255 solved = planning_pipeline->generatePlan(plan.copyPlanningScene(), req, res, context_->debug_);
256 }
257 catch (std::exception& ex)
258 {
259 RCLCPP_ERROR(getLogger(), "Planning pipeline threw an exception: %s", ex.what());
260 res.error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
261 }
262 if (res.trajectory)
263 {
264 plan.plan_components.resize(1);
265 plan.plan_components[0].trajectory = res.trajectory;
266 plan.plan_components[0].description = "plan";
267 }
269
270 return solved;
271}
272
273void MoveGroupMoveAction::startMoveExecutionCallback()
274{
275 setMoveState(MONITOR, goal_);
276}
277
278void MoveGroupMoveAction::startMoveLookCallback()
279{
280 setMoveState(LOOK, goal_);
281}
282
283void MoveGroupMoveAction::preemptMoveCallback()
284{
285 preempt_requested_ = true;
286 context_->plan_execution_->stop();
287}
288
289void MoveGroupMoveAction::setMoveState(MoveGroupState state, const std::shared_ptr<MGActionGoal>& goal)
290{
291 move_state_ = state;
292
293 if (goal)
294 {
295 auto move_feedback = std::make_shared<MGAction::Feedback>();
296 move_feedback->state = stateToStr(state);
297 goal->publish_feedback(move_feedback);
298 }
299}
300} // namespace move_group
301
302#include <pluginlib/class_list_macros.hpp>
303
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
moveit_msgs::msg::PlanningScene clearSceneRobotState(const moveit_msgs::msg::PlanningScene &scene) const
planning_interface::MotionPlanRequest clearRequestStartState(const planning_interface::MotionPlanRequest &request) const
void convertToMsg(const std::vector< plan_execution::ExecutableTrajectory > &trajectory, moveit_msgs::msg::RobotState &first_state_msg, std::vector< moveit_msgs::msg::RobotTrajectory > &trajectory_msg) const
std::string getActionResultString(const moveit_msgs::msg::MoveItErrorCodes &error_code, bool planned_trajectory_empty, bool plan_only)
std::string stateToStr(MoveGroupState state) const
planning_pipeline::PlanningPipelinePtr resolvePlanningPipeline(const std::string &pipeline_id) const
MoveGroupCapability(const std::string &capability_name)
moveit_msgs::msg::Constraints mergeConstraints(const moveit_msgs::msg::Constraints &first, const moveit_msgs::msg::Constraints &second)
Merge two sets of constraints into one.
Definition utils.cpp:64
bool isEmpty(const moveit_msgs::msg::PlanningScene &msg)
Check if a message includes any information about a planning scene, or whether it is empty.
planning_interface::MotionPlanResponse plan(std::shared_ptr< moveit_cpp::PlanningComponent > &planning_component, std::shared_ptr< moveit_cpp::PlanningComponent::PlanRequestParameters > &single_plan_parameters, std::shared_ptr< moveit_cpp::PlanningComponent::MultiPipelinePlanRequestParameters > &multi_plan_parameters, std::shared_ptr< planning_scene::PlanningScene > &planning_scene, std::optional< const moveit::planning_pipeline_interfaces::SolutionSelectionFunction > solution_selection_function, std::optional< moveit::planning_pipeline_interfaces::StoppingCriterionFunction > stopping_criterion_callback)
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition logger.cpp:106
moveit_msgs::msg::MotionPlanRequest MotionPlanRequest
bool isTrajectoryEmpty(const moveit_msgs::msg::RobotTrajectory &trajectory)
Checks if a robot trajectory is empty.
double replan_delay
The amount of time to wait in between replanning attempts (in seconds).
ExecutableMotionPlanComputationFn plan_callback
Callback for computing motion plans. This callback must always be specified.
std::function< void()> before_execution_callback_
bool replan
Flag indicating whether replanning is allowed.
moveit::core::MoveItErrorCode error_code
robot_trajectory::RobotTrajectoryPtr trajectory