moveit2
The MoveIt Motion Planning Framework for ROS 2.
utilities.cpp
Go to the documentation of this file.
1 // Copyright 2022 PickNik Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 //
9 // * Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 //
13 // * Neither the name of the PickNik Inc. nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 
29 /* Author : Andy Zelenak
30  Desc : Free functions. We keep them in a separate translation unit to reduce .o filesize
31  Title : utilities.cpp
32  Project : moveit_servo
33 */
34 
35 #include <moveit_servo/utilities.h>
36 
37 namespace moveit_servo
38 {
39 namespace
40 {
41 static const rclcpp::Logger LOGGER = rclcpp::get_logger("moveit_servo.utilities");
42 constexpr auto ROS_LOG_THROTTLE_PERIOD = std::chrono::milliseconds(3000).count();
43 } // namespace
44 
46 bool isNonZero(const geometry_msgs::msg::TwistStamped& msg)
47 {
48  return msg.twist.linear.x != 0.0 || msg.twist.linear.y != 0.0 || msg.twist.linear.z != 0.0 ||
49  msg.twist.angular.x != 0.0 || msg.twist.angular.y != 0.0 || msg.twist.angular.z != 0.0;
50 }
51 
53 bool isNonZero(const control_msgs::msg::JointJog& msg)
54 {
55  bool all_zeros = true;
56  for (double delta : msg.velocities)
57  {
58  all_zeros &= (delta == 0.0);
59  }
60  return !all_zeros;
61 }
62 
64 geometry_msgs::msg::TransformStamped convertIsometryToTransform(const Eigen::Isometry3d& eigen_tf,
65  const std::string& parent_frame,
66  const std::string& child_frame)
67 {
68  geometry_msgs::msg::TransformStamped output = tf2::eigenToTransform(eigen_tf);
69  output.header.frame_id = parent_frame;
70  output.child_frame_id = child_frame;
71 
72  return output;
73 }
74 
89  const Eigen::VectorXd& commanded_twist,
90  const Eigen::JacobiSVD<Eigen::MatrixXd>& svd,
91  const Eigen::MatrixXd& pseudo_inverse,
92  const double hard_stop_singularity_threshold,
93  const double lower_singularity_threshold,
94  const double leaving_singularity_threshold_multiplier, rclcpp::Clock& clock,
95  moveit::core::RobotStatePtr& current_state, StatusCode& status)
96 {
97  double velocity_scale = 1;
98  std::size_t num_dimensions = commanded_twist.size();
99 
100  // Find the direction away from nearest singularity.
101  // The last column of U from the SVD of the Jacobian points directly toward or away from the singularity.
102  // The sign can flip at any time, so we have to do some extra checking.
103  // Look ahead to see if the Jacobian's condition will decrease.
104  Eigen::VectorXd vector_toward_singularity = svd.matrixU().col(num_dimensions - 1);
105 
106  double ini_condition = svd.singularValues()(0) / svd.singularValues()(svd.singularValues().size() - 1);
107 
108  // This singular vector tends to flip direction unpredictably. See R. Bro,
109  // "Resolving the Sign Ambiguity in the Singular Value Decomposition".
110  // Look ahead to see if the Jacobian's condition will decrease in this
111  // direction. Start with a scaled version of the singular vector
112  Eigen::VectorXd delta_x(num_dimensions);
113  double scale = 100;
114  delta_x = vector_toward_singularity / scale;
115 
116  // Calculate a small change in joints
117  Eigen::VectorXd new_theta;
118  current_state->copyJointGroupPositions(joint_model_group, new_theta);
119  new_theta += pseudo_inverse * delta_x;
120  current_state->setJointGroupPositions(joint_model_group, new_theta);
121  Eigen::MatrixXd new_jacobian = current_state->getJacobian(joint_model_group);
122 
123  Eigen::JacobiSVD<Eigen::MatrixXd> new_svd(new_jacobian);
124  double new_condition = new_svd.singularValues()(0) / new_svd.singularValues()(new_svd.singularValues().size() - 1);
125  // If new_condition < ini_condition, the singular vector does point towards a
126  // singularity. Otherwise, flip its direction.
127  if (ini_condition >= new_condition)
128  {
129  vector_toward_singularity *= -1;
130  }
131 
132  // If this dot product is positive, we're moving toward singularity
133  double dot = vector_toward_singularity.dot(commanded_twist);
134  // see https://github.com/ros-planning/moveit2/pull/620#issuecomment-1201418258 for visual explanation of algorithm
135  double upper_threshold = dot > 0 ? hard_stop_singularity_threshold :
136  (hard_stop_singularity_threshold - lower_singularity_threshold) *
137  leaving_singularity_threshold_multiplier +
138  lower_singularity_threshold;
139  if ((ini_condition > lower_singularity_threshold) && (ini_condition < hard_stop_singularity_threshold))
140  {
141  velocity_scale =
142  1. - (ini_condition - lower_singularity_threshold) / (upper_threshold - lower_singularity_threshold);
143  status =
145  RCLCPP_WARN_STREAM_THROTTLE(LOGGER, clock, ROS_LOG_THROTTLE_PERIOD, SERVO_STATUS_CODE_MAP.at(status));
146  }
147 
148  // Very close to singularity, so halt.
149  else if (ini_condition >= upper_threshold)
150  {
151  velocity_scale = 0;
153  RCLCPP_WARN_STREAM_THROTTLE(LOGGER, clock, ROS_LOG_THROTTLE_PERIOD, SERVO_STATUS_CODE_MAP.at(status));
154  }
155 
156  return velocity_scale;
157 }
158 
159 } // namespace moveit_servo
constexpr size_t ROS_LOG_THROTTLE_PERIOD
double velocityScalingFactorForSingularity(const moveit::core::JointModelGroup *joint_model_group, const Eigen::VectorXd &commanded_twist, const Eigen::JacobiSVD< Eigen::MatrixXd > &svd, const Eigen::MatrixXd &pseudo_inverse, const double hard_stop_singularity_threshold, const double lower_singularity_threshold, const double leaving_singularity_threshold_multiplier, rclcpp::Clock &clock, moveit::core::RobotStatePtr &current_state, StatusCode &status)
Possibly calculate a velocity scaling factor, due to proximity of singularity and direction of motion...
Definition: utilities.cpp:88
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_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" }, { StatusCode::DECELERATE_FOR_LEAVING_SINGULARITY, "Moving away from a singularity, decelerating" } })
geometry_msgs::msg::TransformStamped convertIsometryToTransform(const Eigen::Isometry3d &eigen_tf, const std::string &parent_frame, const std::string &child_frame)
Helper function for converting Eigen::Isometry3d to geometry_msgs/TransformStamped.
Definition: utilities.cpp:64
bool isNonZero(const geometry_msgs::msg::TwistStamped &msg)
Helper function for detecting zeroed message.
Definition: utilities.cpp:46
const rclcpp::Logger LOGGER
Definition: async_test.h:31