moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
command.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 * BSD 3-Clause License
3 *
4 * Copyright (c) 2019, Los Alamos National Security, LLC
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 are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * * Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *******************************************************************************/
33
34/* Title : command.cpp
35 * Project : moveit_servo
36 * Created : 06/04/2023
37 * Author : Brian O'Neil, Andy Zelenak, Blake Anderson, V Mohammed Ibrahim
38 */
39
42
43namespace
44{
45rclcpp::Logger getLogger()
46{
47 return moveit::getLogger("moveit.ros.servo");
48}
49
60const Eigen::VectorXd createMoveGroupDelta(const Eigen::VectorXd& sub_group_deltas,
61 const moveit::core::RobotStatePtr& robot_state,
62 const servo::Params& servo_params,
63 const moveit_servo::JointNameToMoveGroupIndexMap& joint_name_group_index_map)
64{
65 const auto& subgroup_joint_names =
66 robot_state->getJointModelGroup(servo_params.active_subgroup)->getActiveJointModelNames();
67
68 // Create
69 Eigen::VectorXd move_group_delta_theta = Eigen::VectorXd::Zero(
70 robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveJointModelNames().size());
71 for (size_t index = 0; index < subgroup_joint_names.size(); index++)
72 {
73 move_group_delta_theta[joint_name_group_index_map.at(subgroup_joint_names.at(index))] = sub_group_deltas[index];
74 }
75 return move_group_delta_theta;
76};
77} // namespace
78
79namespace moveit_servo
80{
81
82JointDeltaResult jointDeltaFromJointJog(const JointJogCommand& command, const moveit::core::RobotStatePtr& robot_state,
83 const servo::Params& servo_params,
84 const JointNameToMoveGroupIndexMap& joint_name_group_index_map)
85{
86 // Find the target joint position based on the commanded joint velocity
87 const auto& group_name =
88 servo_params.active_subgroup.empty() ? servo_params.move_group_name : servo_params.active_subgroup;
89 const moveit::core::JointModelGroup* joint_model_group = robot_state->getJointModelGroup(group_name);
90 const auto joint_names = joint_model_group->getActiveJointModelNames();
91 Eigen::VectorXd joint_position_delta(joint_names.size());
92 Eigen::VectorXd velocities(joint_names.size());
93
94 velocities.setZero();
95 if (command.velocities.size() != command.names.size())
96 {
97 RCLCPP_WARN_STREAM(getLogger(), "Invalid joint jog command. Each joint name must have one corresponding "
98 "velocity command. Received "
99 << command.names.size() << " joints with " << command.velocities.size()
100 << " commands.");
101 return std::make_pair(StatusCode::INVALID, joint_position_delta);
102 }
103
104 for (size_t i = 0; i < command.names.size(); ++i)
105 {
106 auto it = std::find(joint_names.begin(), joint_names.end(), command.names[i]);
107 if (it != std::end(joint_names))
108 {
109 velocities[std::distance(joint_names.begin(), it)] = command.velocities[i];
110 }
111 else
112 {
113 RCLCPP_WARN_STREAM(getLogger(), "Invalid joint name: " << command.names[i]
114 << "Either you're sending commands for a joint "
115 "that is not part of the move group or certain joints "
116 "cannot be moved because a "
117 "subgroup is active and they are not part of it.");
118 return std::make_pair(StatusCode::INVALID, joint_position_delta);
119 }
120 }
121
122 if (!isValidCommand(velocities))
123 {
124 RCLCPP_WARN_STREAM(getLogger(), "Invalid velocity values in joint jog command");
125 return std::make_pair(StatusCode::INVALID, joint_position_delta);
126 }
127
128 joint_position_delta = velocities * servo_params.publish_period;
129 if (servo_params.command_in_type == "unitless")
130 {
131 joint_position_delta *= servo_params.scale.joint;
132 }
133
134 if (!servo_params.active_subgroup.empty() && servo_params.active_subgroup != servo_params.move_group_name)
135 {
136 return std::make_pair(StatusCode::NO_WARNING, createMoveGroupDelta(joint_position_delta, robot_state, servo_params,
137 joint_name_group_index_map));
138 }
139
140 return std::make_pair(StatusCode::NO_WARNING, joint_position_delta);
141}
142
143JointDeltaResult jointDeltaFromTwist(const TwistCommand& command, const moveit::core::RobotStatePtr& robot_state,
144 const servo::Params& servo_params, const std::string& planning_frame,
145 const JointNameToMoveGroupIndexMap& joint_name_group_index_map)
146{
148 const int num_joints =
149 robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveJointModelNames().size();
150 Eigen::VectorXd joint_position_delta(num_joints);
151 Eigen::Vector<double, 6> cartesian_position_delta;
152
153 if (command.frame_id != planning_frame)
154 {
155 RCLCPP_WARN_STREAM(getLogger(), "Command frame is: " << command.frame_id << ", expected: " << planning_frame);
156 return std::make_pair(StatusCode::INVALID, joint_position_delta);
157 }
158
159 if (!isValidCommand(command))
160 {
161 RCLCPP_WARN_STREAM(getLogger(), "Invalid twist command.");
162 return std::make_pair(StatusCode::INVALID, joint_position_delta);
163 }
164
165 if (command.velocities.isZero())
166 {
167 joint_position_delta.setZero();
168 }
169 else
170 {
171 // Compute the Cartesian position delta based on incoming twist command.
172 cartesian_position_delta = command.velocities * servo_params.publish_period;
173 // This scaling is supposed to be applied to the command.
174 // But since it is only used here, we avoid creating a copy of the command,
175 // by applying the scaling to the computed Cartesian delta instead.
176 if (servo_params.command_in_type == "unitless")
177 {
178 cartesian_position_delta.head<3>() *= servo_params.scale.linear;
179 cartesian_position_delta.tail<3>() *= servo_params.scale.rotational;
180 }
181 else if (servo_params.command_in_type == "speed_units")
182 {
183 if (servo_params.scale.linear > 0.0)
184 {
185 const auto linear_speed_scale = command.velocities.head<3>().norm() / servo_params.scale.linear;
186 if (linear_speed_scale > 1.0)
187 {
188 cartesian_position_delta.head<3>() /= linear_speed_scale;
189 }
190 }
191 if (servo_params.scale.rotational > 0.0)
192 {
193 const auto angular_speed_scale = command.velocities.tail<3>().norm() / servo_params.scale.rotational;
194 if (angular_speed_scale > 1.0)
195 {
196 cartesian_position_delta.tail<3>() /= angular_speed_scale;
197 }
198 }
199 }
200
201 // Compute the required change in joint angles.
202 const auto delta_result =
203 jointDeltaFromIK(cartesian_position_delta, robot_state, servo_params, joint_name_group_index_map);
204 status = delta_result.first;
205 if (status != StatusCode::INVALID)
206 {
207 joint_position_delta = delta_result.second;
208 // Get velocity scaling information for singularity.
209 const auto singularity_scaling_info =
210 velocityScalingFactorForSingularity(robot_state, cartesian_position_delta, servo_params);
211 // Apply velocity scaling for singularity, if there was any scaling.
212 if (singularity_scaling_info.second != StatusCode::NO_WARNING)
213 {
214 status = singularity_scaling_info.second;
215 RCLCPP_WARN_STREAM(getLogger(), SERVO_STATUS_CODE_MAP.at(status));
216 joint_position_delta *= singularity_scaling_info.first;
217 }
218 }
219 }
220
221 return std::make_pair(status, joint_position_delta);
222}
223
224JointDeltaResult jointDeltaFromPose(const PoseCommand& command, const moveit::core::RobotStatePtr& robot_state,
225 const servo::Params& servo_params, const std::string& planning_frame,
226 const std::string& ee_frame,
227 const JointNameToMoveGroupIndexMap& joint_name_group_index_map)
228{
230 const int num_joints =
231 robot_state->getJointModelGroup(servo_params.move_group_name)->getActiveJointModelNames().size();
232 Eigen::VectorXd joint_position_delta(num_joints);
233
234 if (!isValidCommand(command))
235 {
236 RCLCPP_WARN_STREAM(getLogger(), "Invalid pose command.");
237 return std::make_pair(StatusCode::INVALID, joint_position_delta);
238 }
239
240 if (command.frame_id != planning_frame)
241 {
242 RCLCPP_WARN_STREAM(getLogger(), "Command frame is: " << command.frame_id << " expected: " << planning_frame);
243 return std::make_pair(StatusCode::INVALID, joint_position_delta);
244 }
245
246 Eigen::Vector<double, 6> cartesian_position_delta;
247
248 // Compute linear and angular change needed.
249 const Eigen::Isometry3d ee_pose{ robot_state->getGlobalLinkTransform(planning_frame).inverse() *
250 robot_state->getGlobalLinkTransform(ee_frame) };
251 const Eigen::Quaterniond q_current(ee_pose.rotation());
252 Eigen::Quaterniond q_target(command.pose.rotation());
253 Eigen::Vector3d translation_error = command.pose.translation() - ee_pose.translation();
254
255 // Limit the commands by the maximum linear and angular speeds provided.
256 if (servo_params.scale.linear > 0.0)
257 {
258 const auto linear_speed_scale =
259 (translation_error.norm() / servo_params.publish_period) / servo_params.scale.linear;
260 if (linear_speed_scale > 1.0)
261 {
262 translation_error /= linear_speed_scale;
263 }
264 }
265 if (servo_params.scale.rotational > 0.0)
266 {
267 const auto angular_speed_scale =
268 (std::abs(q_target.angularDistance(q_current)) / servo_params.publish_period) / servo_params.scale.rotational;
269 if (angular_speed_scale > 1.0)
270 {
271 q_target = q_current.slerp(1.0 / angular_speed_scale, q_target);
272 }
273 }
274
275 // Compute the Cartesian deltas from the velocity-scaled values.
276 const auto angle_axis_error = Eigen::AngleAxisd(q_target * q_current.inverse());
277 cartesian_position_delta.head<3>() = translation_error;
278 cartesian_position_delta.tail<3>() = angle_axis_error.axis() * angle_axis_error.angle();
279
280 // Compute the required change in joint angles.
281 const auto delta_result =
282 jointDeltaFromIK(cartesian_position_delta, robot_state, servo_params, joint_name_group_index_map);
283 status = delta_result.first;
284 if (status != StatusCode::INVALID)
285 {
286 joint_position_delta = delta_result.second;
287 // Get velocity scaling information for singularity.
288 const auto singularity_scaling_info =
289 velocityScalingFactorForSingularity(robot_state, cartesian_position_delta, servo_params);
290 // Apply velocity scaling for singularity, if there was any scaling.
291 if (singularity_scaling_info.second != StatusCode::NO_WARNING)
292 {
293 status = singularity_scaling_info.second;
294 RCLCPP_WARN_STREAM(getLogger(), SERVO_STATUS_CODE_MAP.at(status));
295 joint_position_delta *= singularity_scaling_info.first;
296 }
297 }
298 return std::make_pair(status, joint_position_delta);
299}
300
301JointDeltaResult jointDeltaFromIK(const Eigen::VectorXd& cartesian_position_delta,
302 const moveit::core::RobotStatePtr& robot_state, const servo::Params& servo_params,
303 const JointNameToMoveGroupIndexMap& joint_name_group_index_map)
304{
305 const auto& group_name =
306 servo_params.active_subgroup.empty() ? servo_params.move_group_name : servo_params.active_subgroup;
307 const moveit::core::JointModelGroup* joint_model_group = robot_state->getJointModelGroup(group_name);
308
309 std::vector<double> current_joint_positions;
310 robot_state->copyJointGroupPositions(joint_model_group, current_joint_positions);
311
312 Eigen::VectorXd delta_theta(current_joint_positions.size());
314
315 const kinematics::KinematicsBaseConstPtr ik_solver = joint_model_group->getSolverInstance();
316 bool ik_solver_supports_group = true;
317 if (ik_solver)
318 {
319 ik_solver_supports_group = ik_solver->supportsGroup(joint_model_group);
320 if (!ik_solver_supports_group)
321 {
322 status = StatusCode::INVALID;
323 RCLCPP_ERROR_STREAM(getLogger(), "Loaded IK plugin does not support group " << joint_model_group->getName());
324 }
325 }
326
327 if (ik_solver && ik_solver_supports_group)
328 {
329 const Eigen::Isometry3d base_to_tip_frame_transform =
330 robot_state->getGlobalLinkTransform(ik_solver->getBaseFrame()).inverse() *
331 robot_state->getGlobalLinkTransform(ik_solver->getTipFrame());
332
333 const geometry_msgs::msg::Pose next_pose =
334 poseFromCartesianDelta(cartesian_position_delta, base_to_tip_frame_transform);
335
336 // setup for IK call
337 std::vector<double> solution;
338 solution.reserve(current_joint_positions.size());
339 moveit_msgs::msg::MoveItErrorCodes err;
341 opts.return_approximate_solution = true;
342 if (ik_solver->searchPositionIK(next_pose, current_joint_positions, servo_params.publish_period / 2.0, solution,
343 err, opts))
344 {
345 // find the difference in joint positions that will get us to the desired pose
346 for (size_t i = 0; i < current_joint_positions.size(); ++i)
347 {
348 delta_theta[i] = solution.at(i) - current_joint_positions.at(i);
349 }
350 }
351 else
352 {
353 status = StatusCode::INVALID;
354 RCLCPP_WARN_STREAM(getLogger(), "Could not find IK solution for requested motion, got error code " << err.val);
355 }
356 }
357 else
358 {
359 // Robot does not have an IK solver, use inverse Jacobian to compute IK.
360 const Eigen::MatrixXd jacobian = robot_state->getJacobian(joint_model_group);
361 const Eigen::JacobiSVD<Eigen::MatrixXd> svd =
362 Eigen::JacobiSVD<Eigen::MatrixXd>(jacobian, Eigen::ComputeThinU | Eigen::ComputeThinV);
363 const Eigen::MatrixXd matrix_s = svd.singularValues().asDiagonal();
364 const Eigen::MatrixXd pseudo_inverse = svd.matrixV() * matrix_s.inverse() * svd.matrixU().transpose();
365
366 delta_theta = pseudo_inverse * cartesian_position_delta;
367 }
368
369 if (!servo_params.active_subgroup.empty() && servo_params.active_subgroup != servo_params.move_group_name)
370 {
371 return std::make_pair(status,
372 createMoveGroupDelta(delta_theta, robot_state, servo_params, joint_name_group_index_map));
373 }
374
375 return std::make_pair(status, delta_theta);
376}
377
378} // namespace moveit_servo
const std::vector< std::string > & getActiveJointModelNames() const
Get the names of the active joints in this group. These are the names of the joints returned by getJo...
const std::string & getName() const
Get the name of the joint group.
const kinematics::KinematicsBaseConstPtr getSolverInstance() const
JointDeltaResult jointDeltaFromTwist(const TwistCommand &command, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const std::string &planning_frame, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Compute the change in joint position for the given twist command.
Definition command.cpp:143
const std::unordered_map< StatusCode, std::string > SERVO_STATUS_CODE_MAP({ { StatusCode::INVALID, "Invalid" }, { StatusCode::NO_WARNING, "No warnings" }, { StatusCode::DECELERATE_FOR_APPROACHING_SINGULARITY, "Moving closer to a singularity, decelerating" }, { StatusCode::HALT_FOR_SINGULARITY, "Very close to a singularity, emergency stop" }, { StatusCode::DECELERATE_FOR_LEAVING_SINGULARITY, "Moving away from a singularity, decelerating" }, { StatusCode::DECELERATE_FOR_COLLISION, "Close to a collision, decelerating" }, { StatusCode::HALT_FOR_COLLISION, "Collision detected, emergency stop" }, { StatusCode::JOINT_BOUND, "Close to a joint bound (position or velocity), halting" } })
geometry_msgs::msg::Pose poseFromCartesianDelta(const Eigen::VectorXd &delta_x, const Eigen::Isometry3d &base_to_tip_frame_transform)
Create a pose message for the provided change in Cartesian position.
Definition common.cpp:115
std::pair< double, StatusCode > velocityScalingFactorForSingularity(const moveit::core::RobotStatePtr &robot_state, const Eigen::VectorXd &target_delta_x, const servo::Params &servo_params)
Computes scaling factor for velocity when the robot is near a singularity.
Definition common.cpp:282
JointDeltaResult jointDeltaFromJointJog(const JointJogCommand &command, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Compute the change in joint position for the given joint jog command.
Definition command.cpp:82
bool isValidCommand(const Eigen::VectorXd &command)
Checks if a given VectorXd is a valid command.
Definition common.cpp:87
JointDeltaResult jointDeltaFromIK(const Eigen::VectorXd &cartesian_position_delta, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Computes the required change in joint angles for given Cartesian change, using the robot's IK solver.
Definition command.cpp:301
std::pair< StatusCode, Eigen::VectorXd > JointDeltaResult
Definition datatypes.hpp:85
JointDeltaResult jointDeltaFromPose(const PoseCommand &command, const moveit::core::RobotStatePtr &robot_state, const servo::Params &servo_params, const std::string &planning_frame, const std::string &ee_frame, const JointNameToMoveGroupIndexMap &joint_name_group_index_map)
Compute the change in joint position for the given pose command.
Definition command.cpp:224
std::unordered_map< std::string, std::size_t > JointNameToMoveGroupIndexMap
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition logger.cpp:79
A set of options for the kinematics solver.
std::vector< std::string > names
Definition datatypes.hpp:90
std::vector< double > velocities
Definition datatypes.hpp:91
Eigen::Isometry3d pose
Eigen::Vector< double, 6 > velocities
Definition datatypes.hpp:99