Skip to content

Commit dd29b5f

Browse files
committed
Implement jerk-limited smoothing for Servo
1 parent 46a543d commit dd29b5f

File tree

6 files changed

+119
-18
lines changed

6 files changed

+119
-18
lines changed

moveit_core/online_signal_smoothing/include/moveit/online_signal_smoothing/ruckig_filter.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ class RuckigFilterPlugin : public SmoothingBaseClass
8282
const Eigen::VectorXd& accelerations) override;
8383

8484
private:
85+
/**
86+
* A utility to print Ruckig's internal state
87+
*/
88+
void printRuckigState();
89+
8590
rclcpp::Node::SharedPtr node_;
8691
void getDefaultJointVelAccelBounds();
8792

@@ -90,7 +95,7 @@ class RuckigFilterPlugin : public SmoothingBaseClass
9095
size_t num_joints_;
9196
moveit::core::RobotModelConstPtr robot_model_;
9297

93-
bool have_initial_ruckig_output_ = false;
98+
bool have_initial_ruckig_output_;
9499
std::optional<ruckig::Ruckig<ruckig::DynamicDOFs>> ruckig_;
95100
std::optional<ruckig::InputParameter<ruckig::DynamicDOFs>> ruckig_input_;
96101
std::optional<ruckig::OutputParameter<ruckig::DynamicDOFs>> ruckig_output_;

moveit_core/online_signal_smoothing/src/ruckig_filter.cpp

+77-4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ bool RuckigFilterPlugin::initialize(rclcpp::Node::SharedPtr node, moveit::core::
5656
node_ = node;
5757
num_joints_ = num_joints;
5858
robot_model_ = robot_model;
59+
have_initial_ruckig_output_ = false;
5960

6061
// get node parameters and store in member variables
6162
auto param_listener = online_signal_smoothing::ParamListener(node_);
@@ -67,9 +68,9 @@ bool RuckigFilterPlugin::initialize(rclcpp::Node::SharedPtr node, moveit::core::
6768
ruckig_input.max_velocity = joint_velocity_bounds_;
6869
ruckig_input.max_acceleration = joint_acceleration_bounds_;
6970
ruckig_input.max_jerk = joint_jerk_bounds_;
70-
// initialize positions. All other quantities are initialized to zero in the constructor.
71-
// This will be overwritten in the first control loop
7271
ruckig_input.current_position = std::vector<double>(num_joints_, 0.0);
72+
ruckig_input.current_velocity = std::vector<double>(num_joints_, 0.0);
73+
ruckig_input.current_acceleration = std::vector<double>(num_joints_, 0.0);
7374
ruckig_input_ = ruckig_input;
7475

7576
ruckig_output_.emplace(ruckig::OutputParameter<ruckig::DynamicDOFs>(num_joints_));
@@ -82,12 +83,68 @@ bool RuckigFilterPlugin::initialize(rclcpp::Node::SharedPtr node, moveit::core::
8283
bool RuckigFilterPlugin::doSmoothing(Eigen::VectorXd& positions, Eigen::VectorXd& velocities,
8384
Eigen::VectorXd& accelerations)
8485
{
86+
if (have_initial_ruckig_output_ && ruckig_input_ && ruckig_output_)
87+
{
88+
ruckig_output_->pass_to_input(*ruckig_input_);
89+
}
90+
// else
91+
// {
92+
// // Force a new calculation
93+
// ruckig_.reset();
94+
// }
95+
96+
// Update Ruckig target state
97+
// This assumes stationary at the target (zero vel, zero accel)
98+
std::cerr << "Updating target position" << std::endl;
99+
ruckig_input_->target_position = std::vector<double>(positions.data(), positions.data() + positions.size());
100+
101+
// Call the Ruckig algorithm
102+
std::cerr << "State prior to ruckig_.update()" << std::endl;
103+
printRuckigState();
104+
ruckig::Result ruckig_result = ruckig_->update(*ruckig_input_, *ruckig_output_);
105+
106+
// Finished means the target state can be reached in this timestep.
107+
// Working means the target state can be reached but not in this timestep.
108+
// ErrorSynchronizationCalculation means smoothing was successful but the robot will deviate a bit from the desired
109+
// path.
110+
// See https://github.com/pantor/ruckig/blob/master/include/ruckig/input_parameter.hpp
111+
if (ruckig_result != ruckig::Result::Finished && ruckig_result != ruckig::Result::Working &&
112+
ruckig_result != ruckig::Result::ErrorSynchronizationCalculation)
113+
114+
{
115+
RCLCPP_ERROR_STREAM(getLogger(), "Ruckig jerk-limited smoothing failed with code: " << ruckig_result);
116+
printRuckigState();
117+
// Return without modifying the position/vel/accel
118+
have_initial_ruckig_output_ = false;
119+
return true;
120+
}
121+
122+
// Update the target state with Ruckig output
123+
positions = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(ruckig_output_->new_position.data(),
124+
ruckig_output_->new_position.size());
125+
velocities = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(ruckig_output_->new_velocity.data(),
126+
ruckig_output_->new_velocity.size());
127+
accelerations = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(ruckig_output_->new_acceleration.data(),
128+
ruckig_output_->new_acceleration.size());
129+
have_initial_ruckig_output_ = true;
130+
85131
return true;
86132
}
87133

88134
bool RuckigFilterPlugin::reset(const Eigen::VectorXd& positions, const Eigen::VectorXd& velocities,
89135
const Eigen::VectorXd& accelerations)
90136
{
137+
// Initialize Ruckig
138+
std::cerr << "Setting the current position in reset()!" << std::endl;
139+
ruckig_input_->current_position = std::vector<double>(positions.data(), positions.data() + positions.size());
140+
ruckig_input_->current_velocity = std::vector<double>(velocities.data(), velocities.data() + velocities.size());
141+
ruckig_input_->current_acceleration =
142+
std::vector<double>(accelerations.data(), accelerations.data() + accelerations.size());
143+
144+
std::cerr << ruckig_input_->current_acceleration[0] << " " << ruckig_input_->current_acceleration[1] << " "
145+
<< ruckig_input_->current_acceleration[2] << std::endl;
146+
147+
have_initial_ruckig_output_ = false;
91148
return true;
92149
}
93150

@@ -121,12 +178,28 @@ void RuckigFilterPlugin::getDefaultJointVelAccelBounds()
121178
joint_acceleration_bounds_.push_back(DBL_MAX);
122179
}
123180

124-
// MoveIt and the URDF don't support jerk limits, so use a safe default always
125-
joint_jerk_bounds_.push_back(DEFAULT_JERK_BOUND);
181+
if (bound.jerk_bounded_)
182+
{
183+
// Assume symmetric limits
184+
joint_jerk_bounds_.push_back(bound.max_jerk_);
185+
}
186+
else
187+
{
188+
RCLCPP_ERROR_STREAM(getLogger(), "WARNING: No joint jerk limit defined. A default jerk limit of "
189+
<< DEFAULT_JERK_BOUND << " rad/s^3 has been applied.");
190+
joint_jerk_bounds_.push_back(DEFAULT_JERK_BOUND);
191+
}
126192
}
127193

128194
assert(joint_jerk_bounds_.size() == num_joints_);
129195
}
196+
197+
void RuckigFilterPlugin::printRuckigState()
198+
{
199+
RCLCPP_ERROR_STREAM(getLogger(), ruckig_->delta_time << "\nRuckig input:\n"
200+
<< ruckig_input_->to_string() << "\nRuckig output:\n"
201+
<< ruckig_output_->to_string());
202+
}
130203
} // namespace online_signal_smoothing
131204

132205
#include <pluginlib/class_list_macros.hpp>

moveit_ros/moveit_servo/include/moveit_servo/servo.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class Servo
117117
servo::Params& getParams();
118118

119119
/**
120-
* \brief Get the current state of the robot as given by planning scene monitor.
120+
* \brief Get the current state of the robot as given by planning scene monitor. This is blocking.
121121
* @return The current state of the robot.
122122
*/
123123
KinematicState getCurrentRobotState() const;

moveit_ros/moveit_servo/src/servo.cpp

+22-10
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,6 @@ Servo::Servo(const rclcpp::Node::SharedPtr& node, std::shared_ptr<const servo::P
7979

8080
moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
8181

82-
// Load the smoothing plugin
83-
if (servo_params_.use_smoothing)
84-
{
85-
setSmoothingPlugin();
86-
}
87-
else
88-
{
89-
RCLCPP_WARN(logger_, "No smoothing plugin loaded");
90-
}
91-
9282
// Create the collision checker and start collision checking.
9383
collision_monitor_ =
9484
std::make_unique<CollisionMonitor>(planning_scene_monitor_, servo_params_, std::ref(collision_velocity_scale_));
@@ -125,6 +115,17 @@ Servo::Servo(const rclcpp::Node::SharedPtr& node, std::shared_ptr<const servo::P
125115
joint_name_to_index_maps_.insert(
126116
std::make_pair<std::string, JointNameToMoveGroupIndexMap>(std::string(sub_group_name), std::move(new_map)));
127117
}
118+
119+
// Load the smoothing plugin
120+
if (servo_params_.use_smoothing)
121+
{
122+
setSmoothingPlugin();
123+
}
124+
else
125+
{
126+
RCLCPP_WARN(logger_, "No smoothing plugin loaded");
127+
}
128+
128129
RCLCPP_INFO_STREAM(logger_, "Servo initialized successfully");
129130
}
130131

@@ -645,6 +646,17 @@ std::optional<PoseCommand> Servo::toPlanningFrame(const PoseCommand& command, co
645646

646647
KinematicState Servo::getCurrentRobotState() const
647648
{
649+
// waitForCurrentState(const rclcpp::Time& t = rclcpp::Clock(RCL_ROS_TIME).now(), double wait_time_s = 1.0) const;
650+
bool have_current_state = false;
651+
while (!have_current_state)
652+
{
653+
have_current_state =
654+
planning_scene_monitor_->getStateMonitor()->waitForCurrentState(rclcpp::Clock(RCL_ROS_TIME).now(), 1.0 /* s */);
655+
if (!have_current_state)
656+
{
657+
RCLCPP_WARN(logger_, "Waiting for the current state");
658+
}
659+
}
648660
moveit::core::RobotStatePtr robot_state = planning_scene_monitor_->getStateMonitor()->getCurrentState();
649661
return extractRobotState(robot_state, servo_params_.move_group_name);
650662
}

moveit_ros/moveit_servo/src/servo_node.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,13 @@ void ServoNode::servoLoop()
298298
while (planning_scene_monitor_->getLastUpdateTime().get_clock_type() != node_->get_clock()->get_clock_type() ||
299299
servo_node_start > planning_scene_monitor_->getLastUpdateTime())
300300
{
301-
RCLCPP_INFO(node_->get_logger(), "Waiting to receive robot state update.");
301+
RCLCPP_INFO(node_->get_logger(), "Waiting to receive a valid robot state update.");
302302
rclcpp::sleep_for(std::chrono::seconds(1));
303303
}
304304
KinematicState current_state = servo_->getCurrentRobotState();
305305
last_commanded_state_ = current_state;
306306
// Ensure the filter is up to date
307+
std::cerr << "Initial update of the filter with resetSmoothing()" << std::endl;
307308
servo_->resetSmoothing(current_state);
308309

309310
// Get the robot state and joint model group info.
@@ -383,7 +384,7 @@ void ServoNode::servoLoop()
383384
{
384385
// if no new command was created, use current robot state
385386
updateSlidingWindow(current_state, joint_cmd_rolling_window_, servo_params_.max_expected_latency, cur_time);
386-
servo_->resetSmoothing(current_state);
387+
// servo_->resetSmoothing(current_state);
387388
}
388389

389390
status_msg.code = static_cast<int8_t>(servo_->getStatus());

moveit_ros/moveit_servo/src/utils/common.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -493,13 +493,23 @@ planning_scene_monitor::PlanningSceneMonitorPtr createPlanningSceneMonitor(const
493493

494494
KinematicState extractRobotState(const moveit::core::RobotStatePtr& robot_state, const std::string& move_group_name)
495495
{
496+
std::cerr << "Doing extractRobotState" << std::endl;
496497
const moveit::core::JointModelGroup* joint_model_group = robot_state->getJointModelGroup(move_group_name);
497498
const auto joint_names = joint_model_group->getActiveJointModelNames();
498499
KinematicState current_state(joint_names.size());
499500
current_state.joint_names = joint_names;
500501
robot_state->copyJointGroupPositions(joint_model_group, current_state.positions);
501502
robot_state->copyJointGroupVelocities(joint_model_group, current_state.velocities);
503+
502504
robot_state->copyJointGroupAccelerations(joint_model_group, current_state.accelerations);
505+
// If any acceleration is nan, set all accelerations to zero
506+
if (std::any_of(current_state.accelerations.begin(), current_state.accelerations.end(),
507+
[](double v) { return isnan(v); }))
508+
{
509+
std::cerr << "Zeroing accelerations" << std::endl;
510+
robot_state->zeroAccelerations();
511+
robot_state->copyJointGroupAccelerations(joint_model_group, current_state.accelerations);
512+
}
503513

504514
return current_state;
505515
}

0 commit comments

Comments
 (0)