41#include <tf2_eigen/tf2_eigen.hpp>
42#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
50using namespace std::chrono_literals;
53 const moveit::core::RobotModelConstPtr& robot_model,
54 const std::shared_ptr<tf2_ros::Buffer>& tf_buffer,
bool use_sim_time)
55 : middleware_handle_(std::move(middleware_handle))
56 , tf_buffer_(tf_buffer)
57 , robot_model_(robot_model)
58 , robot_state_(robot_model)
59 , state_monitor_started_(false)
60 , copy_dynamics_(false)
61 , error_(std::numeric_limits<double>::epsilon())
62 , use_sim_time_(use_sim_time)
63 , logger_(moveit::
getLogger(
"moveit.ros.current_state_monitor"))
65 robot_state_.setToDefaultValues();
69 const moveit::core::RobotModelConstPtr& robot_model,
70 const std::shared_ptr<tf2_ros::Buffer>& tf_buffer,
bool use_sim_time)
83 std::unique_lock<std::mutex> slock(state_update_lock_);
85 return moveit::core::RobotStatePtr(result);
90 std::unique_lock<std::mutex> slock(state_update_lock_);
91 return current_state_time_;
96 std::unique_lock<std::mutex> slock(state_update_lock_);
98 return std::make_pair(moveit::core::RobotStatePtr(result), current_state_time_);
103 std::map<std::string, double> m;
104 std::unique_lock<std::mutex> slock(state_update_lock_);
105 const double* pos = robot_state_.getVariablePositions();
106 const std::vector<std::string>& names = robot_state_.getVariableNames();
107 for (std::size_t i = 0; i < names.size(); ++i)
108 m[names[i]] = pos[i];
114 std::unique_lock<std::mutex> slock(state_update_lock_);
115 const double* pos = robot_state_.getVariablePositions();
119 if (robot_state_.hasVelocities())
121 const double* vel = robot_state_.getVariableVelocities();
124 if (robot_state_.hasAccelerations())
126 const double* acc = robot_state_.getVariableAccelerations();
129 if (robot_state_.hasEffort())
131 const double* eff = robot_state_.getVariableEffort();
140 update_callbacks_.push_back(fn);
145 update_callbacks_.clear();
150 if (!state_monitor_started_ && robot_model_)
153 if (joint_states_topic.empty())
155 RCLCPP_ERROR(logger_,
"The joint states topic cannot be an empty string");
159 middleware_handle_->createJointStateSubscription(
160 joint_states_topic, [
this](
const sensor_msgs::msg::JointState::ConstSharedPtr& joint_state) {
161 return jointStateCallback(joint_state);
164 if (tf_buffer_ && !robot_model_->getMultiDOFJointModels().empty())
166 tf_buffer_->setUsingDedicatedThread(
true);
167 middleware_handle_->createDynamicTfSubscription([
this](
const auto& msg) {
return transformCallback(msg,
false); });
168 middleware_handle_->createStaticTfSubscription([
this](
const auto& msg) {
return transformCallback(msg,
true); });
170 state_monitor_started_ =
true;
171 monitor_start_time_ = middleware_handle_->now();
172 RCLCPP_INFO(logger_,
"Listening to joint states on topic '%s'", joint_states_topic.c_str());
178 return state_monitor_started_;
183 if (state_monitor_started_)
185 middleware_handle_->resetJointStateSubscription();
188 middleware_handle_->resetTfSubscriptions();
190 RCLCPP_DEBUG(logger_,
"No longer listening for joint states");
191 state_monitor_started_ =
false;
197 return middleware_handle_->getJointStateTopicName();
200bool CurrentStateMonitor::haveCompleteStateHelper(
const rclcpp::Time& oldest_allowed_update_time,
201 std::vector<std::string>* missing_joints)
const
203 const std::vector<const moveit::core::JointModel*>& active_joints = robot_model_->getActiveJointModels();
204 std::unique_lock<std::mutex> slock(state_update_lock_);
207 std::map<const moveit::core::JointModel*, rclcpp::Time>::const_iterator it = joint_time_.find(joint);
208 if (it == joint_time_.end())
210 RCLCPP_DEBUG(logger_,
"Joint '%s' has never been updated", joint->getName().c_str());
212 else if (it->second < oldest_allowed_update_time)
214 RCLCPP_DEBUG(logger_,
"Joint '%s' was last updated %0.3lf seconds before requested time",
215 joint->getName().c_str(), (oldest_allowed_update_time - it->second).seconds());
222 missing_joints->push_back(joint->getName());
229 return (missing_joints ==
nullptr) || missing_joints->empty();
234 rclcpp::Time start = middleware_handle_->now();
235 rclcpp::Duration elapsed(0, 0);
236 rclcpp::Duration timeout = rclcpp::Duration::from_seconds(wait_time_s);
238 rclcpp::Clock steady_clock(RCL_STEADY_TIME);
239 std::unique_lock<std::mutex> lock(state_update_lock_);
240 while (current_state_time_ < t)
244 if (state_update_condition_.wait_for(lock, 100ms) == std::cv_status::timeout)
248#pragma GCC diagnostic push
249#pragma GCC diagnostic ignored "-Wold-style-cast"
250 RCLCPP_WARN_SKIPFIRST_THROTTLE(logger_, steady_clock, 1000,
251 "No state update received within 100ms of system clock. "
252 "Have been waiting for %fs, timeout is %fs",
253 elapsed.seconds(), wait_time_s);
254#pragma GCC diagnostic pop
259 state_update_condition_.wait_for(lock, (timeout - elapsed).to_chrono<std::chrono::duration<double>>());
261 elapsed = middleware_handle_->now() - start;
262 if (elapsed > timeout)
265 "Didn't receive robot state (joint angles) with recent timestamp within "
266 "%f seconds. Requested time %f, but latest received state has time %f.\n"
267 "Check clock synchronization if your are running ROS across multiple machines!",
268 wait_time_s, t.seconds(), current_state_time_.seconds());
271 if (!middleware_handle_->ok())
273 RCLCPP_DEBUG(logger_,
"ROS context shut down while waiting for current robot state.");
282 const double sleep_step_s = std::min(0.05, wait_time_s / 10.0);
283 const auto sleep_step_duration = rclcpp::Duration::from_seconds(sleep_step_s);
284 double slept_time_s = 0.0;
287 middleware_handle_->sleepFor(sleep_step_duration.to_chrono<std::chrono::nanoseconds>());
288 slept_time_s += sleep_step_s;
300 std::vector<std::string> missing_joints;
306 std::set<std::string> mj;
307 mj.insert(missing_joints.begin(), missing_joints.end());
310 for (std::size_t i = 0; ok && i < names.size(); ++i)
312 if (mj.find(names[i]) != mj.end())
322void CurrentStateMonitor::jointStateCallback(
const sensor_msgs::msg::JointState::ConstSharedPtr& joint_state)
324 if (joint_state->name.size() != joint_state->position.size())
326 rclcpp::Clock steady_clock(RCL_STEADY_TIME);
327#pragma GCC diagnostic push
328#pragma GCC diagnostic ignored "-Wold-style-cast"
329 RCLCPP_ERROR_THROTTLE(logger_, steady_clock, 1000,
330 "State monitor received invalid joint state (number of joint names does not match number of "
332#pragma GCC diagnostic pop
338 std::unique_lock<std::mutex> _(state_update_lock_);
340 std::size_t n = joint_state->name.size();
341 current_state_time_ = joint_state->header.stamp;
342 for (std::size_t i = 0; i < n; ++i)
345 if (!robot_model_->hasJointModel(joint_state->name[i]))
350 const moveit::core::JointModel* jm = robot_model_->getJointModel(joint_state->name[i]);
355 joint_time_.insert_or_assign(jm, joint_state->header.stamp);
357 if (robot_state_.getJointPositions(jm)[0] != joint_state->position[i])
360 robot_state_.setJointPositions(jm, &(joint_state->position[i]));
365 if (
static_cast<const moveit::core::RevoluteJointModel*
>(jm)->isContinuous())
369 const moveit::core::VariableBounds& b =
388 if (joint_state->name.size() == joint_state->velocity.size() &&
389 (!robot_state_.hasVelocities() || robot_state_.getJointVelocities(jm)[0] != joint_state->velocity[i]))
392 robot_state_.setJointVelocities(jm, &(joint_state->velocity[i]));
396 if (joint_state->name.size() == joint_state->effort.size() &&
397 (!robot_state_.hasEffort() || robot_state_.getJointEffort(jm)[0] != joint_state->effort[i]))
400 robot_state_.setJointEfforts(jm, &(joint_state->effort[i]));
410 update_callback(joint_state);
414 state_update_condition_.notify_all();
417void CurrentStateMonitor::updateMultiDofJoints()
420 const std::vector<const moveit::core::JointModel*>& multi_dof_joints = robot_model_->getMultiDOFJointModels();
423 bool changes =
false;
425 std::unique_lock<std::mutex> _(state_update_lock_);
426 for (
const moveit::core::JointModel* joint : multi_dof_joints)
428 const std::string& child_frame = joint->getChildLinkModel()->getName();
429 const std::string& parent_frame =
430 joint->getParentLinkModel() ? joint->getParentLinkModel()->getName() : robot_model_->getModelFrame();
432 rclcpp::Time latest_common_time(0, 0, RCL_ROS_TIME);
433 geometry_msgs::msg::TransformStamped transf;
436 transf = tf_buffer_->lookupTransform(parent_frame, child_frame, tf2::TimePointZero);
437 latest_common_time = transf.header.stamp;
439 catch (tf2::TransformException& ex)
441 RCLCPP_WARN_ONCE(logger_,
442 "Unable to update multi-DOF joint '%s':"
443 "Failure to lookup transform between '%s'"
444 "and '%s' with TF exception: %s",
445 joint->getName().c_str(), parent_frame.c_str(), child_frame.c_str(), ex.what());
449 if (
auto joint_time_it = joint_time_.find(joint); joint_time_it == joint_time_.end())
450 joint_time_.emplace(joint, rclcpp::Time(0, 0, RCL_ROS_TIME));
453 if (latest_common_time <= joint_time_[joint] && latest_common_time > rclcpp::Time(0, 0, RCL_ROS_TIME))
455 joint_time_[joint] = latest_common_time;
457 std::vector<double> new_values(joint->getStateSpaceDimension());
458 const moveit::core::LinkModel* link = joint->getChildLinkModel();
461 joint->computeVariablePositions(tf2::transformToEigen(transf), new_values.data());
469 if (joint->distance(new_values.data(), robot_state_.getJointPositions(joint)) > 1e-5)
474 robot_state_.setJointPositions(joint, new_values.data());
484 auto joint_state = std::make_shared<sensor_msgs::msg::JointState>();
486 update_callback(joint_state);
492 state_update_condition_.notify_all();
497void CurrentStateMonitor::transformCallback(
const tf2_msgs::msg::TFMessage::ConstSharedPtr& msg,
const bool is_static)
499 for (
const auto& transform : msg->transforms)
503 tf_buffer_->setTransform(transform,
504 is_static ? middleware_handle_->getStaticTfTopicName() :
505 middleware_handle_->getDynamicTfTopicName(),
508 catch (tf2::TransformException& ex)
510 std::string temp = ex.what();
511 RCLCPP_ERROR(logger_,
"Failure to set received transform from %s to %s with error: %s\n",
512 transform.child_frame_id.c_str(), transform.header.frame_id.c_str(), temp.c_str());
515 updateMultiDofJoints();
const std::vector< std::string > & getJointModelNames() const
Get the names of the joints in this group. These are the names of the joints returned by getJointMode...
A joint from the robot. Models the transform that this joint applies in the kinematic chain....
std::size_t getVariableCount() const
Get the number of variables that describe this joint.
const VariableBounds & getVariableBounds(const std::string &variable) const
Get the bounds for a variable. Throw an exception if the variable was not found.
JointType getType() const
Get the type of joint.
bool jointOriginTransformIsIdentity() const
const Eigen::Isometry3d & getJointOriginTransform() const
When transforms are computed for this link, they are usually applied to the link's origin....
Representation of a robot's state. This includes position, velocity, acceleration and effort.
void setVariablePositions(const double *position)
It is assumed positions is an array containing the new positions for all variables in this state....
void setVariableVelocities(const double *velocity)
Given an array with velocity values for all variables, set those values as the velocities in this sta...
void setVariableEffort(const double *effort)
Given an array with effort values for all variables, set those values as the effort in this state.
void setVariableAccelerations(const double *acceleration)
Given an array with acceleration values for all variables, set those values as the accelerations in t...
This class contains the ros interfaces for CurrentStateMontior.
void startStateMonitor(const std::string &joint_states_topic="joint_states")
Start monitoring joint states on a particular topic.
bool isActive() const
Check if the state monitor is started.
void clearUpdateCallbacks()
Clear the functions to be called when an update to the joint state is received.
std::map< std::string, double > getCurrentStateValues() const
Get the current state values as a map from joint names to joint state values.
void addUpdateCallback(const JointStateUpdateCallback &fn)
Add a function that will be called whenever the joint state is updated.
std::pair< moveit::core::RobotStatePtr, rclcpp::Time > getCurrentStateAndTime() const
Get the current state and its time stamp.
void setToCurrentState(moveit::core::RobotState &upd) const
Set the state upd to the current state maintained by this class.
bool waitForCurrentState(const rclcpp::Time &t=rclcpp::Clock(RCL_ROS_TIME).now(), double wait_time_s=1.0) const
Wait for at most wait_time_s seconds (default 1s) for a robot state more recent than t.
rclcpp::Time getCurrentStateTime() const
Get the time stamp for the current state.
bool haveCompleteState() const
Query whether we have joint state information for all DOFs in the kinematic model.
void stopStateMonitor()
Stop monitoring the "joint_states" topic.
moveit::core::RobotStatePtr getCurrentState() const
Get the current state.
CurrentStateMonitor(std::unique_ptr< MiddlewareHandle > middleware_handle, const moveit::core::RobotModelConstPtr &robot_model, const std::shared_ptr< tf2_ros::Buffer > &tf_buffer, bool use_sim_time)
Constructor.
std::string getMonitoredTopic() const
Get the name of the topic being monitored. Returns an empty string if the monitor is inactive.
bool waitForCompleteState(double wait_time_s) const
Wait for at most wait_time_s seconds until the complete robot state is known.
void update(moveit::core::RobotState *self, bool force, std::string &category)
std::function< void(sensor_msgs::msg::JointState::ConstSharedPtr)> JointStateUpdateCallback
rclcpp::Logger getLogger()