36#include <rclcpp/logger.hpp>
37#include <rclcpp/logging.hpp>
40#include <Eigen/Geometry>
50constexpr double DEFAULT_MAX_VELOCITY = 5;
51constexpr double DEFAULT_MAX_ACCELERATION = 10;
52constexpr double DEFAULT_MAX_JERK = 1000;
53constexpr double MAX_DURATION_EXTENSION_FACTOR = 50.0;
54constexpr double DURATION_EXTENSION_FRACTION = 1.1;
56constexpr double OVERSHOOT_CHECK_PERIOD = 0.01;
65 const double max_velocity_scaling_factor,
66 const double max_acceleration_scaling_factor,
const bool mitigate_overshoot,
67 const double overshoot_threshold)
69 if (!validateGroup(trajectory))
75 if (num_waypoints < 2)
77 RCLCPP_WARN(getLogger(),
78 "Trajectory does not have enough points to smooth with Ruckig. Returning an unmodified trajectory.");
85 ruckig::InputParameter<ruckig::DynamicDOFs> ruckig_input{ num_dof };
86 if (!getRobotModelBounds(max_velocity_scaling_factor, max_acceleration_scaling_factor, group, ruckig_input))
88 RCLCPP_ERROR(getLogger(),
"Error while retrieving kinematic limits (vel/accel/jerk) from RobotModel.");
92 return runRuckig(trajectory, ruckig_input, mitigate_overshoot, overshoot_threshold);
96 const std::unordered_map<std::string, double>& velocity_limits,
97 const std::unordered_map<std::string, double>& acceleration_limits,
98 const std::unordered_map<std::string, double>& jerk_limits,
99 const double max_velocity_scaling_factor,
100 const double max_acceleration_scaling_factor,
const bool mitigate_overshoot,
101 const double overshoot_threshold)
103 if (!validateGroup(trajectory))
109 if (num_waypoints < 2)
111 RCLCPP_WARN(getLogger(),
112 "Trajectory does not have enough points to smooth with Ruckig. Returning an unmodified trajectory.");
119 ruckig::InputParameter<ruckig::DynamicDOFs> ruckig_input{ num_dof };
120 if (!getRobotModelBounds(max_velocity_scaling_factor, max_acceleration_scaling_factor, group, ruckig_input))
122 RCLCPP_ERROR(getLogger(),
"Error while retrieving kinematic limits (vel/accel/jerk) from RobotModel.");
129 for (
size_t j = 0; j < num_joints; ++j)
132 auto it = velocity_limits.find(vars[j]);
133 if (it != velocity_limits.end())
135 ruckig_input.max_velocity.at(j) = it->second * max_velocity_scaling_factor;
138 it = acceleration_limits.find(vars[j]);
139 if (it != acceleration_limits.end())
141 ruckig_input.max_acceleration.at(j) = it->second * max_acceleration_scaling_factor;
144 it = jerk_limits.find(vars[j]);
145 if (it != jerk_limits.end())
147 ruckig_input.max_jerk.at(j) = it->second;
151 return runRuckig(trajectory, ruckig_input, mitigate_overshoot, overshoot_threshold);
155 const std::vector<moveit_msgs::msg::JointLimits>&
joint_limits,
156 const double max_velocity_scaling_factor,
157 const double max_acceleration_scaling_factor)
159 std::unordered_map<std::string, double> velocity_limits;
160 std::unordered_map<std::string, double> acceleration_limits;
161 std::unordered_map<std::string, double> jerk_limits;
165 if (limit.has_velocity_limits)
167 velocity_limits[limit.joint_name] = limit.max_velocity;
169 if (limit.has_acceleration_limits)
171 acceleration_limits[limit.joint_name] = limit.max_acceleration;
173 if (limit.has_jerk_limits)
175 jerk_limits[limit.joint_name] = limit.max_jerk;
178 return applySmoothing(trajectory, velocity_limits, acceleration_limits, jerk_limits, max_velocity_scaling_factor,
179 max_acceleration_scaling_factor);
187 RCLCPP_ERROR(getLogger(),
"The planner did not set the group the plan was computed for");
193bool RuckigSmoothing::getRobotModelBounds(
const double max_velocity_scaling_factor,
194 const double max_acceleration_scaling_factor,
196 ruckig::InputParameter<ruckig::DynamicDOFs>& ruckig_input)
201 for (
size_t i = 0; i < num_dof; ++i)
208 ruckig_input.max_velocity.at(i) = max_velocity_scaling_factor * bounds.
max_velocity_;
213 "Joint velocity limits are not defined. Using the default "
214 << DEFAULT_MAX_VELOCITY
215 <<
" rad/s. You can define velocity limits in the URDF or joint_limits.yaml.");
216 ruckig_input.max_velocity.at(i) = max_velocity_scaling_factor * DEFAULT_MAX_VELOCITY;
220 ruckig_input.max_acceleration.at(i) = max_acceleration_scaling_factor * bounds.
max_acceleration_;
225 "Joint acceleration limits are not defined. Using the default "
226 << DEFAULT_MAX_ACCELERATION
227 <<
" rad/s^2. You can define acceleration limits in the URDF or joint_limits.yaml.");
228 ruckig_input.max_acceleration.at(i) = max_acceleration_scaling_factor * DEFAULT_MAX_ACCELERATION;
233 ruckig_input.max_jerk.at(i) = bounds.
max_jerk_;
237 RCLCPP_WARN_STREAM_ONCE(
getLogger(),
"Joint jerk limits are not defined. Using the default "
239 <<
" rad/s^3. You can define jerk limits in joint_limits.yaml.");
240 ruckig_input.max_jerk.at(i) = DEFAULT_MAX_JERK;
248 ruckig::InputParameter<ruckig::DynamicDOFs>& ruckig_input,
249 const bool mitigate_overshoot,
const double overshoot_threshold)
254 ruckig::Trajectory<ruckig::DynamicDOFs, ruckig::StandardVector> ruckig_output(num_dof);
267 ruckig::Result ruckig_result;
268 double duration_extension_factor = 1;
269 bool smoothing_complete =
false;
270 size_t waypoint_idx = 0;
271 while ((duration_extension_factor <= MAX_DURATION_EXTENSION_FACTOR) && !smoothing_complete)
273 while (waypoint_idx < num_waypoints - 1)
275 moveit::core::RobotStatePtr curr_waypoint = trajectory.
getWayPointPtr(waypoint_idx);
276 moveit::core::RobotStatePtr next_waypoint = trajectory.
getWayPointPtr(waypoint_idx + 1);
278 getNextRuckigInput(curr_waypoint, next_waypoint, group, ruckig_input);
281 ruckig_result = ruckig.calculate(ruckig_input, ruckig_output);
285 bool overshoots =
false;
286 if (mitigate_overshoot)
288 overshoots = checkOvershoot(ruckig_output, num_dof, ruckig_input, overshoot_threshold);
296 if (!overshoots && (waypoint_idx == num_waypoints - 2) &&
297 (ruckig_result == ruckig::Result::Working || ruckig_result == ruckig::Result::Finished))
300 smoothing_complete =
true;
305 if (overshoots || (ruckig_result != ruckig::Result::Working && ruckig_result != ruckig::Result::Finished))
307 duration_extension_factor *= DURATION_EXTENSION_FRACTION;
312 extendTrajectoryDuration(duration_extension_factor, waypoint_idx, num_dof, move_group_idx, original_trajectory,
323 if (duration_extension_factor > MAX_DURATION_EXTENSION_FACTOR)
326 "Ruckig extended the trajectory duration to its maximum and still did not find a solution");
329 if (ruckig_result != ruckig::Result::Working && ruckig_result != ruckig::Result::Finished)
331 RCLCPP_ERROR_STREAM(
getLogger(),
"Ruckig trajectory smoothing failed. Ruckig error: " << ruckig_result);
338void RuckigSmoothing::extendTrajectoryDuration(
const double duration_extension_factor,
size_t waypoint_idx,
339 const size_t num_dof,
const std::vector<int>& move_group_idx,
344 duration_extension_factor *
352 for (
size_t joint = 0; joint < num_dof; ++joint)
354 target_state->setVariableVelocity(move_group_idx.at(joint),
355 (1 / duration_extension_factor) *
356 target_state->getVariableVelocity(move_group_idx.at(joint)));
357 double prev_velocity = prev_state->getVariableVelocity(move_group_idx.at(joint));
358 double curr_velocity = target_state->getVariableVelocity(move_group_idx.at(joint));
359 target_state->setVariableAcceleration(move_group_idx.at(joint), (curr_velocity - prev_velocity) / timestep);
365 ruckig::InputParameter<ruckig::DynamicDOFs>& ruckig_input)
370 std::vector<double> current_positions_vector(num_dof);
371 std::vector<double> current_velocities_vector(num_dof);
372 std::vector<double> current_accelerations_vector(num_dof);
374 for (
size_t i = 0; i < num_dof; ++i)
380 current_velocities_vector.at(i) =
381 std::clamp(current_velocities_vector.at(i), -ruckig_input.max_velocity.at(i), ruckig_input.max_velocity.at(i));
382 current_accelerations_vector.at(i) = std::clamp(
383 current_accelerations_vector.at(i), -ruckig_input.max_acceleration.at(i), ruckig_input.max_acceleration.at(i));
385 std::copy_n(current_positions_vector.begin(), num_dof, ruckig_input.current_position.begin());
386 std::copy_n(current_velocities_vector.begin(), num_dof, ruckig_input.current_velocity.begin());
387 std::copy_n(current_accelerations_vector.begin(), num_dof, ruckig_input.current_acceleration.begin());
390void RuckigSmoothing::getNextRuckigInput(
const moveit::core::RobotStateConstPtr& current_waypoint,
391 const moveit::core::RobotStateConstPtr& next_waypoint,
393 ruckig::InputParameter<ruckig::DynamicDOFs>& ruckig_input)
398 for (
size_t joint = 0; joint < num_dof; ++joint)
400 ruckig_input.current_position.at(joint) = current_waypoint->getVariablePosition(idx.at(joint));
401 ruckig_input.current_velocity.at(joint) = current_waypoint->getVariableVelocity(idx.at(joint));
402 ruckig_input.current_acceleration.at(joint) = current_waypoint->getVariableAcceleration(idx.at(joint));
405 ruckig_input.target_position.at(joint) = next_waypoint->getVariablePosition(idx.at(joint));
406 ruckig_input.target_velocity.at(joint) = next_waypoint->getVariableVelocity(idx.at(joint));
407 ruckig_input.target_acceleration.at(joint) = next_waypoint->getVariableAcceleration(idx.at(joint));
410 ruckig_input.current_velocity.at(joint) =
411 std::clamp(ruckig_input.current_velocity.at(joint), -ruckig_input.max_velocity.at(joint),
412 ruckig_input.max_velocity.at(joint));
413 ruckig_input.current_acceleration.at(joint) =
414 std::clamp(ruckig_input.current_acceleration.at(joint), -ruckig_input.max_acceleration.at(joint),
415 ruckig_input.max_acceleration.at(joint));
416 ruckig_input.target_velocity.at(joint) =
417 std::clamp(ruckig_input.target_velocity.at(joint), -ruckig_input.max_velocity.at(joint),
418 ruckig_input.max_velocity.at(joint));
419 ruckig_input.target_acceleration.at(joint) =
420 std::clamp(ruckig_input.target_acceleration.at(joint), -ruckig_input.max_acceleration.at(joint),
421 ruckig_input.max_acceleration.at(joint));
425bool RuckigSmoothing::checkOvershoot(ruckig::Trajectory<ruckig::DynamicDOFs, ruckig::StandardVector>& ruckig_trajectory,
426 const size_t num_dof, ruckig::InputParameter<ruckig::DynamicDOFs>& ruckig_input,
427 const double overshoot_threshold)
430 for (
double time_from_start = OVERSHOOT_CHECK_PERIOD; time_from_start < ruckig_trajectory.get_duration();
431 time_from_start += OVERSHOOT_CHECK_PERIOD)
433 std::vector<double> new_position(num_dof);
434 std::vector<double> new_velocity(num_dof);
435 std::vector<double> new_acceleration(num_dof);
436 ruckig_trajectory.at_time(time_from_start, new_position, new_velocity, new_acceleration);
438 for (
size_t joint = 0; joint < num_dof; ++joint)
441 double error = new_position[joint] - ruckig_input.target_position.at(joint);
442 if (((error / (ruckig_input.current_position.at(joint) - ruckig_input.target_position.at(joint))) < 0.0) &&
443 std::fabs(error) > overshoot_threshold)
const RobotModel & getParentModel() const
Get the kinematic model this group is part of.
const std::vector< std::string > & getVariableNames() const
Get the names of the variables that make up the joints included in this group. The number of returned...
const std::vector< int > & getVariableIndexList() const
Get the index locations in the complete robot state for all the variables in this group.
unsigned int getVariableCount() const
Get the number of variables that describe this joint group. This includes variables necessary for mim...
Definition of a kinematic model. This class is not thread safe, however multiple instances can be cre...
const VariableBounds & getVariableBounds(const std::string &variable) const
Get the bounds for a specific variable. Throw an exception of variable is not found.
Representation of a robot's state. This includes position, velocity, acceleration and effort.
double getVariableAcceleration(const std::string &variable) const
Get the acceleration of a particular variable. An exception is thrown if the variable is not known.
double getVariableVelocity(const std::string &variable) const
Get the velocity of a particular variable. An exception is thrown if the variable is not known.
double getVariablePosition(const std::string &variable) const
Get the position of a particular variable. An exception is thrown if the variable is not known.
Maintain a sequence of waypoints and the time durations between these waypoints.
double getAverageSegmentDuration() const
moveit::core::RobotStatePtr & getWayPointPtr(std::size_t index)
RobotTrajectory & setWayPointDurationFromPrevious(std::size_t index, double value)
RobotTrajectory & unwind()
const moveit::core::JointModelGroup * getGroup() const
moveit::core::RobotStatePtr & getFirstWayPointPtr()
std::size_t getWayPointCount() const
double getWayPointDurationFromPrevious(std::size_t index) const
static bool applySmoothing(robot_trajectory::RobotTrajectory &trajectory, const double max_velocity_scaling_factor=1.0, const double max_acceleration_scaling_factor=1.0, const bool mitigate_overshoot=false, const double overshoot_threshold=0.01)
Apply smoothing to a time-parameterized trajectory so that jerk limits are not violated.
rclcpp::Logger getLogger()
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
bool acceleration_bounded_