Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 55 additions & 18 deletions moveit_core/trajectory_processing/src/ruckig_traj_smoothing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,20 @@
#include <Eigen/Geometry>
#include <limits>
#include <moveit/trajectory_processing/ruckig_traj_smoothing.hpp>
#include <moveit/robot_model/joint_model.hpp>
#include <vector>
#include <moveit/utils/logger.hpp>

namespace trajectory_processing
{
namespace
{
constexpr double DEFAULT_MAX_VELOCITY = 5; // rad/s
constexpr double DEFAULT_MAX_ACCELERATION = 10; // rad/s^2
constexpr double DEFAULT_MAX_JERK = 1000; // rad/s^3
constexpr double DEFAULT_MAX_VELOCITY = 5; // rad/s
constexpr double DEFAULT_MAX_ACCELERATION = 10; // rad/s^2
constexpr double DEFAULT_MAX_JERK = 1000; // rad/s^3
constexpr double DEFAULT_MAX_VELOCITY_PRISMATIC = 0.5; // m/s
constexpr double DEFAULT_MAX_ACCELERATION_PRISMATIC = 1.0; // m/s^2
constexpr double DEFAULT_MAX_JERK_PRISMATIC = 100.0; // m/s^3
constexpr double MAX_DURATION_EXTENSION_FACTOR = 50.0;
constexpr double DURATION_EXTENSION_FRACTION = 1.1;
// If "mitigate_overshoot" is enabled, overshoot is checked with this timestep
Expand Down Expand Up @@ -201,6 +205,8 @@ bool RuckigSmoothing::getRobotModelBounds(const double max_velocity_scaling_fact
for (size_t i = 0; i < num_dof; ++i)
{
const moveit::core::VariableBounds& bounds = rmodel.getVariableBounds(vars.at(i));
const moveit::core::JointModel* joint_model = rmodel.getJointOfVariable(vars.at(i));
const bool is_prismatic = joint_model && (joint_model->getType() == moveit::core::JointModel::PRISMATIC);

// This assumes min/max bounds are symmetric
if (bounds.velocity_bounded_)
Expand All @@ -209,35 +215,66 @@ bool RuckigSmoothing::getRobotModelBounds(const double max_velocity_scaling_fact
}
else
{
RCLCPP_WARN_STREAM_ONCE(getLogger(),
"Joint velocity limits are not defined. Using the default "
<< DEFAULT_MAX_VELOCITY
<< " rad/s. You can define velocity limits in the URDF or joint_limits.yaml.");
ruckig_input.max_velocity.at(i) = max_velocity_scaling_factor * DEFAULT_MAX_VELOCITY;
if (is_prismatic)
{
RCLCPP_WARN_STREAM_ONCE(getLogger(),
"Joint velocity limits are not defined. Using the default "
<< DEFAULT_MAX_VELOCITY_PRISMATIC
<< " m/s. You can define velocity limits in the URDF or joint_limits.yaml.");
ruckig_input.max_velocity.at(i) = max_velocity_scaling_factor * DEFAULT_MAX_VELOCITY_PRISMATIC;
}
else
{
RCLCPP_WARN_STREAM_ONCE(getLogger(),
"Joint velocity limits are not defined. Using the default "
<< DEFAULT_MAX_VELOCITY
<< " rad/s. You can define velocity limits in the URDF or joint_limits.yaml.");
ruckig_input.max_velocity.at(i) = max_velocity_scaling_factor * DEFAULT_MAX_VELOCITY;
}
}
if (bounds.acceleration_bounded_)
{
ruckig_input.max_acceleration.at(i) = max_acceleration_scaling_factor * bounds.max_acceleration_;
}
else
{
RCLCPP_WARN_STREAM_ONCE(getLogger(),
"Joint acceleration limits are not defined. Using the default "
<< DEFAULT_MAX_ACCELERATION
<< " rad/s^2. You can define acceleration limits in the URDF or joint_limits.yaml.");
ruckig_input.max_acceleration.at(i) = max_acceleration_scaling_factor * DEFAULT_MAX_ACCELERATION;
if (is_prismatic)
{
RCLCPP_WARN_STREAM_ONCE(getLogger(),
"Joint acceleration limits are not defined. Using the default "
<< DEFAULT_MAX_ACCELERATION_PRISMATIC
<< " m/s^2. You can define acceleration limits in the URDF or joint_limits.yaml.");
ruckig_input.max_acceleration.at(i) = max_acceleration_scaling_factor * DEFAULT_MAX_ACCELERATION_PRISMATIC;
}
else
{
RCLCPP_WARN_STREAM_ONCE(
getLogger(), "Joint acceleration limits are not defined. Using the default "
<< DEFAULT_MAX_ACCELERATION
<< " rad/s^2. You can define acceleration limits in the URDF or joint_limits.yaml.");
ruckig_input.max_acceleration.at(i) = max_acceleration_scaling_factor * DEFAULT_MAX_ACCELERATION;
}
}
ruckig_input.max_jerk.at(i) = bounds.jerk_bounded_ ? bounds.max_jerk_ : DEFAULT_MAX_JERK;
if (bounds.jerk_bounded_)
{
ruckig_input.max_jerk.at(i) = bounds.max_jerk_;
}
else
{
RCLCPP_WARN_STREAM_ONCE(getLogger(), "Joint jerk limits are not defined. Using the default "
<< DEFAULT_MAX_JERK
<< " rad/s^3. You can define jerk limits in joint_limits.yaml.");
ruckig_input.max_jerk.at(i) = DEFAULT_MAX_JERK;
if (is_prismatic)
{
RCLCPP_WARN_STREAM_ONCE(getLogger(), "Joint jerk limits are not defined. Using the default "
<< DEFAULT_MAX_JERK_PRISMATIC
<< " m/s^3. You can define jerk limits in joint_limits.yaml.");
ruckig_input.max_jerk.at(i) = DEFAULT_MAX_JERK_PRISMATIC;
}
else
{
RCLCPP_WARN_STREAM_ONCE(getLogger(), "Joint jerk limits are not defined. Using the default "
<< DEFAULT_MAX_JERK
<< " rad/s^3. You can define jerk limits in joint_limits.yaml.");
ruckig_input.max_jerk.at(i) = DEFAULT_MAX_JERK;
}
}
}

Expand Down
132 changes: 132 additions & 0 deletions moveit_core/trajectory_processing/test/test_ruckig_traj_smoothing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,138 @@ TEST_F(RuckigTests, single_waypoint)
}
}

TEST(RuckigTestsPrismatic, prismatic_joint_defaults)
{
moveit::core::RobotModelBuilder builder("prismatic_bot", "base_link");
builder.addChain("base_link->link1", "prismatic", {}, { 1.0, 0.0, 0.0 });
builder.addGroup({ "base_link", "link1" }, { "base_link-link1-joint" }, "prismatic_group");
moveit::core::RobotModelPtr robot_model = builder.build();
ASSERT_TRUE(robot_model != nullptr);

// Set up trajectory with 2 waypoints
auto trajectory = std::make_shared<robot_trajectory::RobotTrajectory>(robot_model, "prismatic_group");

moveit::core::RobotState robot_state(robot_model);
robot_state.setToDefaultValues();
robot_state.setVariablePosition("base_link-link1-joint", 0.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.0);

robot_state.setVariablePosition("base_link-link1-joint", 1.5);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 2.0);

trajectory_processing::RuckigSmoothing smoother;
EXPECT_TRUE(
smoother.applySmoothing(*trajectory, 1.0 /* max vel scaling factor */, 1.0 /* max accel scaling factor */));

// The distance is 1.5 meters.
// The default maximum velocity for a prismatic joint is 0.5 m/s.
// The default maximum acceleration is 1.0 m/s^2.
// Using Ruckig, the total duration T must satisfy the velocity constraint:
// T >= distance / max_velocity = 1.5 / 0.5 = 3.0 seconds.
double duration = trajectory->getWayPointDurationFromStart(trajectory->getWayPointCount() - 1);
EXPECT_GE(duration, 3.0);
}

TEST(RuckigTestsPrismatic, multi_joint_indexing)
{
moveit::core::RobotModelBuilder builder("multi_joint_bot", "base_link");
builder.addChain("base_link->link1", "revolute", {}, { 0.0, 0.0, 1.0 });
builder.addChain("link1->link2", "prismatic", {}, { 1.0, 0.0, 0.0 });
builder.addChain("link2->link3", "revolute", {}, { 0.0, 0.0, 1.0 });
builder.addGroup({ "base_link", "link1", "link2", "link3" },
{ "base_link-link1-joint", "link1-link2-joint", "link2-link3-joint" }, "multi_group");
moveit::core::RobotModelPtr robot_model = builder.build();
ASSERT_TRUE(robot_model != nullptr);

trajectory_processing::RuckigSmoothing smoother;

// Test 1: Only Joint 1 (revolute) moves. It should use the revolute default limit (5.0 rad/s), so duration should be short (< 1.0s).
{
auto trajectory = std::make_shared<robot_trajectory::RobotTrajectory>(robot_model, "multi_group");
moveit::core::RobotState robot_state(robot_model);
robot_state.setToDefaultValues();
robot_state.setVariablePosition("base_link-link1-joint", 0.0);
robot_state.setVariablePosition("link1-link2-joint", 0.0);
robot_state.setVariablePosition("link2-link3-joint", 0.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.0);

robot_state.setVariablePosition("base_link-link1-joint", 1.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.2);

EXPECT_TRUE(smoother.applySmoothing(*trajectory, 1.0, 1.0));
double duration = trajectory->getWayPointDurationFromStart(trajectory->getWayPointCount() - 1);
EXPECT_LT(duration, 1.0);
}

// Test 2: Only Joint 2 (prismatic) moves. It should use the prismatic default limit (0.5 m/s), so duration should be
// long (>= 3.0s for 1.5m).
{
auto trajectory = std::make_shared<robot_trajectory::RobotTrajectory>(robot_model, "multi_group");
moveit::core::RobotState robot_state(robot_model);
robot_state.setToDefaultValues();
robot_state.setVariablePosition("base_link-link1-joint", 0.0);
robot_state.setVariablePosition("link1-link2-joint", 0.0);
robot_state.setVariablePosition("link2-link3-joint", 0.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.0);

robot_state.setVariablePosition("link1-link2-joint", 1.5);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 2.0);

EXPECT_TRUE(smoother.applySmoothing(*trajectory, 1.0, 1.0));
double duration = trajectory->getWayPointDurationFromStart(trajectory->getWayPointCount() - 1);
EXPECT_GE(duration, 3.0);
}

// Test 3: Only Joint 3 (revolute) moves. It should use the revolute default limit (5.0 rad/s), so duration should be short (< 1.0s).
{
auto trajectory = std::make_shared<robot_trajectory::RobotTrajectory>(robot_model, "multi_group");
moveit::core::RobotState robot_state(robot_model);
robot_state.setToDefaultValues();
robot_state.setVariablePosition("base_link-link1-joint", 0.0);
robot_state.setVariablePosition("link1-link2-joint", 0.0);
robot_state.setVariablePosition("link2-link3-joint", 0.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.0);

robot_state.setVariablePosition("link2-link3-joint", 1.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.2);

EXPECT_TRUE(smoother.applySmoothing(*trajectory, 1.0, 1.0));
double duration = trajectory->getWayPointDurationFromStart(trajectory->getWayPointCount() - 1);
EXPECT_LT(duration, 1.0);
}

// Test 4: All three joints move simultaneously. The duration should be constrained by the slowest joint (Joint 2,
// prismatic), so duration >= 3.0s.
{
auto trajectory = std::make_shared<robot_trajectory::RobotTrajectory>(robot_model, "multi_group");
moveit::core::RobotState robot_state(robot_model);
robot_state.setToDefaultValues();
robot_state.setVariablePosition("base_link-link1-joint", 0.0);
robot_state.setVariablePosition("link1-link2-joint", 0.0);
robot_state.setVariablePosition("link2-link3-joint", 0.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 0.0);

robot_state.setVariablePosition("base_link-link1-joint", 1.0);
robot_state.setVariablePosition("link1-link2-joint", 1.5);
robot_state.setVariablePosition("link2-link3-joint", 1.0);
robot_state.update();
trajectory->addSuffixWayPoint(robot_state, 2.0);

EXPECT_TRUE(smoother.applySmoothing(*trajectory, 1.0, 1.0));
double duration = trajectory->getWayPointDurationFromStart(trajectory->getWayPointCount() - 1);
EXPECT_GE(duration, 3.0);
}
}

int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
Expand Down