diff --git a/sr_mechanism_controllers/CMakeLists.txt b/sr_mechanism_controllers/CMakeLists.txt
index cbaefb03d..53557852b 100644
--- a/sr_mechanism_controllers/CMakeLists.txt
+++ b/sr_mechanism_controllers/CMakeLists.txt
@@ -49,6 +49,7 @@ add_library(sr_mechanism_controllers
example/srh_syntouch_controllers.cpp
src/sr_controller.cpp
src/sr_plain_pid.cpp
+ src/joint_state_controller.cpp
)
add_dependencies(sr_mechanism_controllers ${catkin_EXPORTED_TARGETS})
target_link_libraries(sr_mechanism_controllers ${catkin_LIBRARIES})
diff --git a/sr_mechanism_controllers/controller_plugins.xml b/sr_mechanism_controllers/controller_plugins.xml
index c88a99492..6751e123e 100644
--- a/sr_mechanism_controllers/controller_plugins.xml
+++ b/sr_mechanism_controllers/controller_plugins.xml
@@ -26,4 +26,7 @@
+
diff --git a/sr_mechanism_controllers/include/sr_mechanism_controllers/joint_state_controller.h b/sr_mechanism_controllers/include/sr_mechanism_controllers/joint_state_controller.h
new file mode 100644
index 000000000..73f4c9b81
--- /dev/null
+++ b/sr_mechanism_controllers/include/sr_mechanism_controllers/joint_state_controller.h
@@ -0,0 +1,104 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2012, hiDOF INC.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of hiDOF, Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Author: Wim Meeussen
+ */
+
+#pragma once
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace controller
+{
+
+/**
+ * \brief Controller that publishes the state of all joints in a robot.
+ *
+ * This controller publishes the state of all resources registered to a \c hardware_interface::JointStateInterface to a
+ * topic of type \c sensor_msgs/JointState. The following is a basic configuration of the controller.
+ *
+ * \code
+ * joint_state_controller:
+ * type: joint_state_controller/JointStateController
+ * publish_rate: 50
+ * \endcode
+ *
+ * It's possible to optionally specify a set of extra joints not contained in a
+ * \c hardware_interface::JointStateInterface with custom (and static) default values. The following is an example
+ * configuration specifying extra joints.
+ *
+ * \code
+ * joint_state_controller:
+ * type: joint_state_controller/JointStateController
+ * publish_rate: 50
+ * extra_joints:
+ * - name: 'extra1'
+ * position: 10.0
+ * velocity: 20.0
+ * effort: 30.0
+ *
+ * - name: 'extra2'
+ * position: -10.0
+ *
+ * - name: 'extra3'
+ * \endcode
+ *
+ * An unspecified position, velocity or acceleration defaults to zero.
+ */
+class JointStateController: public controller_interface::Controller
+{
+public:
+ JointStateController() : publish_rate_(0.0) {}
+
+ virtual bool init(hardware_interface::JointStateInterface* hw,
+ ros::NodeHandle& root_nh,
+ ros::NodeHandle& controller_nh);
+ virtual void starting(const ros::Time& time);
+ virtual void update(const ros::Time& time, const ros::Duration& /*period*/);
+ virtual void stopping(const ros::Time& /*time*/);
+
+private:
+ std::vector joint_state_;
+ std::shared_ptr > realtime_pub_;
+ boost::scoped_ptr > msg_buffer_;
+ sensor_msgs::JointState msg_;
+ ros::Time last_publish_time_;
+ double publish_rate_;
+ unsigned int num_hw_joints_; ///< Number of joints present in the JointStateInterface, excluding extra joints
+
+ void addExtraJoints(const ros::NodeHandle& nh, sensor_msgs::JointState& msg);
+};
+
+}
\ No newline at end of file
diff --git a/sr_mechanism_controllers/include/sr_mechanism_controllers/sr_controller.hpp b/sr_mechanism_controllers/include/sr_mechanism_controllers/sr_controller.hpp
index 1877c4b09..d7f9cbbf1 100644
--- a/sr_mechanism_controllers/include/sr_mechanism_controllers/sr_controller.hpp
+++ b/sr_mechanism_controllers/include/sr_mechanism_controllers/sr_controller.hpp
@@ -28,13 +28,14 @@
#define _SRH_CONTROLLER_HPP_
#include
+#include
#include
#include
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -145,8 +146,11 @@ class SrController :
ros::NodeHandle node_, n_tilde_;
std::string joint_name_;
- boost::scoped_ptr > controller_state_publisher_;
+
+ boost::scoped_ptr > msg_buffer_;
+ control_msgs::JointControllerState msg_;
boost::scoped_ptr friction_compensator;
diff --git a/sr_mechanism_controllers/src/joint_state_controller.cpp b/sr_mechanism_controllers/src/joint_state_controller.cpp
new file mode 100644
index 000000000..7853a3f81
--- /dev/null
+++ b/sr_mechanism_controllers/src/joint_state_controller.cpp
@@ -0,0 +1,205 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2012, hiDOF INC.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of hiDOF Inc nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Author: Wim Meeussen
+ */
+
+#include
+#include
+#include
+
+#include "sr_mechanism_controllers/joint_state_controller.h"
+
+namespace controller
+{
+
+ bool JointStateController::init(hardware_interface::JointStateInterface* hw,
+ ros::NodeHandle& root_nh,
+ ros::NodeHandle& controller_nh)
+ {
+ // List of joints to be published
+ std::vector joint_names;
+
+ // Get list of joints: This allows specifying a desired order, or
+ // alternatively, only publish states for a subset of joints. If the
+ // parameter is not set, all joint states will be published in the order
+ // specified by the hardware interface.
+ if (controller_nh.getParam("joints", joint_names)) {
+ ROS_INFO_STREAM("Joints parameter specified, publishing specified joints in desired order.");
+ } else {
+ // get all joint names from the hardware interface
+ joint_names = hw->getNames();
+ }
+
+ num_hw_joints_ = joint_names.size();
+ for (unsigned i=0; i(root_nh, "joint_states", queue_size));
+ msg_buffer_.reset(new boost::circular_buffer(queue_size));
+
+ // get joints and allocate message
+ for (unsigned i=0; igetHandle(joint_names[i]));
+ msg_.name.push_back(joint_names[i]);
+ msg_.position.push_back(0.0);
+ msg_.velocity.push_back(0.0);
+ msg_.effort.push_back(0.0);
+ }
+ addExtraJoints(controller_nh, msg_);
+
+ return true;
+ }
+
+ void JointStateController::starting(const ros::Time& time)
+ {
+ // initialize time
+ last_publish_time_ = time;
+ }
+
+ void JointStateController::update(const ros::Time& time, const ros::Duration& /*period*/)
+ {
+ // limit rate of publishing
+ if (publish_rate_ > 0.0 && last_publish_time_ + ros::Duration(1.0/publish_rate_) < time){
+
+ // populate joint state message:
+ // - fill only joints that are present in the JointStateInterface, i.e. indices [0, num_hw_joints_)
+ // - leave unchanged extra joints, which have static values, i.e. indices from num_hw_joints_ onwards
+ msg_.header.stamp = time;
+ for (unsigned i=0; ipush_back(msg_);
+
+ // try to publish
+ if (realtime_pub_->trylock()){
+ // we're actually publishing, so increment time
+ last_publish_time_ = last_publish_time_ + ros::Duration(1.0/publish_rate_);
+
+ while(!msg_buffer_->empty())
+ {
+ realtime_pub_->msg_buffer_->push_back(msg_buffer_->front());
+ msg_buffer_->pop_front();
+ }
+
+ realtime_pub_->unlockAndPublish();
+ }
+ }
+ }
+
+ void JointStateController::stopping(const ros::Time& /*time*/)
+ {}
+
+ void JointStateController::addExtraJoints(const ros::NodeHandle& nh, sensor_msgs::JointState& msg)
+ {
+
+ // Preconditions
+ XmlRpc::XmlRpcValue list;
+ if (!nh.getParam("extra_joints", list))
+ {
+ ROS_DEBUG("No extra joints specification found.");
+ return;
+ }
+
+ if (list.getType() != XmlRpc::XmlRpcValue::TypeArray)
+ {
+ ROS_ERROR("Extra joints specification is not an array. Ignoring.");
+ return;
+ }
+
+ for(std::size_t i = 0; i < list.size(); ++i)
+ {
+ XmlRpc::XmlRpcValue& elem = list[i];
+
+ if (elem.getType() != XmlRpc::XmlRpcValue::TypeStruct)
+ {
+ ROS_ERROR_STREAM("Extra joint specification is not a struct, but rather '" << elem.getType() <<
+ "'. Ignoring.");
+ continue;
+ }
+
+ if (!elem.hasMember("name"))
+ {
+ ROS_ERROR_STREAM("Extra joint does not specify name. Ignoring.");
+ continue;
+ }
+
+ const std::string name = elem["name"];
+ if (std::find(msg.name.begin(), msg.name.end(), name) != msg.name.end())
+ {
+ ROS_WARN_STREAM("Joint state interface already contains specified extra joint '" << name << "'.");
+ continue;
+ }
+
+ const bool has_pos = elem.hasMember("position");
+ const bool has_vel = elem.hasMember("velocity");
+ const bool has_eff = elem.hasMember("effort");
+
+ const XmlRpc::XmlRpcValue::Type typeDouble = XmlRpc::XmlRpcValue::TypeDouble;
+ if (has_pos && elem["position"].getType() != typeDouble)
+ {
+ ROS_ERROR_STREAM("Extra joint '" << name << "' does not specify a valid default position. Ignoring.");
+ continue;
+ }
+ if (has_vel && elem["velocity"].getType() != typeDouble)
+ {
+ ROS_ERROR_STREAM("Extra joint '" << name << "' does not specify a valid default velocity. Ignoring.");
+ continue;
+ }
+ if (has_eff && elem["effort"].getType() != typeDouble)
+ {
+ ROS_ERROR_STREAM("Extra joint '" << name << "' does not specify a valid default effort. Ignoring.");
+ continue;
+ }
+
+ // State of extra joint
+ const double pos = has_pos ? static_cast(elem["position"]) : 0.0;
+ const double vel = has_vel ? static_cast(elem["velocity"]) : 0.0;
+ const double eff = has_eff ? static_cast(elem["effort"]) : 0.0;
+
+ // Add extra joints to message
+ msg.name.push_back(name);
+ msg.position.push_back(pos);
+ msg.velocity.push_back(vel);
+ msg.effort.push_back(eff);
+ }
+ }
+
+}
+
+PLUGINLIB_EXPORT_CLASS( controller::JointStateController, controller_interface::ControllerBase)
\ No newline at end of file
diff --git a/sr_mechanism_controllers/src/srh_joint_effort_controller.cpp b/sr_mechanism_controllers/src/srh_joint_effort_controller.cpp
index 29d84bbee..1580df7c4 100644
--- a/sr_mechanism_controllers/src/srh_joint_effort_controller.cpp
+++ b/sr_mechanism_controllers/src/srh_joint_effort_controller.cpp
@@ -70,8 +70,10 @@ namespace controller
return false;
}
- controller_state_publisher_.reset(new realtime_tools::RealtimePublisher
- (node_, "state", 1));
+ int queue_size = 50;
+ controller_state_publisher_.reset(new sr_utilities::RealtimePublisher
+ (node_, "state", queue_size));
+ msg_buffer_.reset(new boost::circular_buffer(queue_size));
ROS_DEBUG(" --------- ");
ROS_DEBUG_STREAM("Init: " << joint_name_);
@@ -193,25 +195,32 @@ namespace controller
joint_state_->commanded_effort_ = commanded_effort;
- if (loop_count_ % 10 == 0)
+ if (true) //(loop_count_ % 10 == 0)
{
+ msg_.header.stamp = time;
+ msg_.set_point = command_;
+ msg_.process_value = joint_state_->effort_;
+ // @todo compute the derivative of the effort.
+ msg_.process_value_dot = -1.0;
+ msg_.error = commanded_effort - joint_state_->effort_;
+ msg_.time_step = period.toSec();
+ msg_.command = commanded_effort;
+
+ double dummy;
+ getGains(msg_.p,
+ msg_.i,
+ msg_.d,
+ msg_.i_clamp,
+ dummy);
+ msg_buffer_->push_back(msg_);
+
if (controller_state_publisher_ && controller_state_publisher_->trylock())
{
- controller_state_publisher_->msg_.header.stamp = time;
- controller_state_publisher_->msg_.set_point = command_;
- controller_state_publisher_->msg_.process_value = joint_state_->effort_;
- // @todo compute the derivative of the effort.
- controller_state_publisher_->msg_.process_value_dot = -1.0;
- controller_state_publisher_->msg_.error = commanded_effort - joint_state_->effort_;
- controller_state_publisher_->msg_.time_step = period.toSec();
- controller_state_publisher_->msg_.command = commanded_effort;
-
- double dummy;
- getGains(controller_state_publisher_->msg_.p,
- controller_state_publisher_->msg_.i,
- controller_state_publisher_->msg_.d,
- controller_state_publisher_->msg_.i_clamp,
- dummy);
+ while(!msg_buffer_->empty())
+ {
+ controller_state_publisher_->msg_buffer_->push_back(msg_buffer_->front());
+ msg_buffer_->pop_front();
+ }
controller_state_publisher_->unlockAndPublish();
}
}
diff --git a/sr_mechanism_controllers/src/srh_joint_position_controller.cpp b/sr_mechanism_controllers/src/srh_joint_position_controller.cpp
index c33d651e6..0070a69d5 100644
--- a/sr_mechanism_controllers/src/srh_joint_position_controller.cpp
+++ b/sr_mechanism_controllers/src/srh_joint_position_controller.cpp
@@ -80,8 +80,10 @@ namespace controller
return false;
}
- controller_state_publisher_.reset(new realtime_tools::RealtimePublisher
- (node_, "state", 1));
+ int queue_size = 50;
+ controller_state_publisher_.reset(new sr_utilities::RealtimePublisher
+ (node_, "state", queue_size));
+ msg_buffer_.reset(new boost::circular_buffer(queue_size));
ROS_DEBUG(" --------- ");
ROS_DEBUG_STREAM("Init: " << joint_name_);
@@ -223,7 +225,8 @@ namespace controller
error_position = joint_state_->position_ - command_;
}
- bool in_deadband = hysteresis_deadband.is_in_deadband(command_, error_position, position_deadband);
+ // setting nb_errors_for_avg to 1:
+ bool in_deadband = hysteresis_deadband.is_in_deadband(command_, error_position, position_deadband, 5.0, 1);
// don't compute the error if we're in the deadband.
if (in_deadband)
@@ -258,33 +261,38 @@ namespace controller
joint_state_->commanded_effort_ = commanded_effort;
- if (loop_count_ % 10 == 0)
+ if (true) // if (loop_count_ % 10 == 0)
{
+ msg_.header.stamp = time;
+ msg_.set_point = command_;
+ if (has_j2)
+ {
+ msg_.process_value = joint_state_->position_ + joint_state_2->position_;
+ msg_.process_value_dot = joint_state_->velocity_ + joint_state_2->velocity_;
+ }
+ else
+ {
+ msg_.process_value = joint_state_->position_;
+ msg_.process_value_dot = joint_state_->velocity_;
+ }
+ msg_.error = error_position;
+ msg_.time_step = period.toSec();
+ msg_.command = commanded_effort;
+
+ double dummy;
+ getGains(msg_.p,
+ msg_.i,
+ msg_.d,
+ msg_.i_clamp,
+ dummy);
+ msg_buffer_->push_back(msg_);
if (controller_state_publisher_ && controller_state_publisher_->trylock())
{
- controller_state_publisher_->msg_.header.stamp = time;
- controller_state_publisher_->msg_.set_point = command_;
- if (has_j2)
- {
- controller_state_publisher_->msg_.process_value = joint_state_->position_ + joint_state_2->position_;
- controller_state_publisher_->msg_.process_value_dot = joint_state_->velocity_ + joint_state_2->velocity_;
- }
- else
+ while(!msg_buffer_->empty())
{
- controller_state_publisher_->msg_.process_value = joint_state_->position_;
- controller_state_publisher_->msg_.process_value_dot = joint_state_->velocity_;
+ controller_state_publisher_->msg_buffer_->push_back(msg_buffer_->front());
+ msg_buffer_->pop_front();
}
-
- controller_state_publisher_->msg_.error = error_position;
- controller_state_publisher_->msg_.time_step = period.toSec();
- controller_state_publisher_->msg_.command = commanded_effort;
-
- double dummy;
- getGains(controller_state_publisher_->msg_.p,
- controller_state_publisher_->msg_.i,
- controller_state_publisher_->msg_.d,
- controller_state_publisher_->msg_.i_clamp,
- dummy);
controller_state_publisher_->unlockAndPublish();
}
}
diff --git a/sr_mechanism_controllers/src/srh_joint_variable_pid_position_controller.cpp b/sr_mechanism_controllers/src/srh_joint_variable_pid_position_controller.cpp
index 84610467b..4caa4ca9a 100644
--- a/sr_mechanism_controllers/src/srh_joint_variable_pid_position_controller.cpp
+++ b/sr_mechanism_controllers/src/srh_joint_variable_pid_position_controller.cpp
@@ -93,7 +93,7 @@ namespace controller
double dummy;
pid_controller_position_->getGains(p_init_, i_init_, d_init_, i_clamp_, dummy);
- controller_state_publisher_.reset(new realtime_tools::RealtimePublisher
+ controller_state_publisher_.reset(new sr_utilities::RealtimePublisher
(node_, "state", 1));
ROS_DEBUG(" --------- ");
diff --git a/sr_mechanism_controllers/src/srh_joint_velocity_controller.cpp b/sr_mechanism_controllers/src/srh_joint_velocity_controller.cpp
index 3d3e95a9f..cc357606b 100644
--- a/sr_mechanism_controllers/src/srh_joint_velocity_controller.cpp
+++ b/sr_mechanism_controllers/src/srh_joint_velocity_controller.cpp
@@ -80,7 +80,7 @@ namespace controller
return false;
}
- controller_state_publisher_.reset(new realtime_tools::RealtimePublisher
+ controller_state_publisher_.reset(new sr_utilities::RealtimePublisher
(node_, "state", 1));
ROS_DEBUG(" --------- ");
diff --git a/sr_utilities/include/sr_utilities/realtime_publisher.h b/sr_utilities/include/sr_utilities/realtime_publisher.h
new file mode 100644
index 000000000..34a776ebe
--- /dev/null
+++ b/sr_utilities/include/sr_utilities/realtime_publisher.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2008, Willow Garage, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the Willow Garage, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+ /*
+* Copyright 2021 Shadow Robot Company Ltd.
+*
+* This program is free software: you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the Free
+* Software Foundation version 2 of the License.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program. If not, see .
+*/
+
+/*
+ * Publishing ROS messages is difficult, as the publish function is
+ * not realtime safe. This class provides the proper locking so that
+ * you can call publish in realtime and a separate (non-realtime)
+ * thread will ensure that the message gets published over ROS.
+ *
+ * Author: Stuart Glaser
+ */
+#ifndef SR_UTILITIES__REALTIME_PUBLISHER_H_
+#define SR_UTILITIES__REALTIME_PUBLISHER_H_
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace sr_utilities {
+
+template
+class RealtimePublisher
+{
+
+public:
+ // TODO delete this field when all controllers have been adapted
+ Msg msg_;
+ /// The msg_buffer_ contains the msgs that will be inserted in the outgoing_msg_buffer_
+ boost::scoped_ptr > msg_buffer_;
+
+ /** \brief Constructor for the realtime publisher
+ *
+ * \param node the nodehandle that specifies the namespace (or prefix) that is used to advertise the ROS topic
+ * \param topic the topic name to advertise
+ * \param queue_size the size of the outgoing ROS buffer
+ * \param latched . optional argument (defaults to false) to specify is publisher is latched or not
+ */
+ RealtimePublisher(const ros::NodeHandle &node, const std::string &topic, int queue_size, bool latched=false)
+ : topic_(topic), node_(node), is_running_(false), keep_running_(false), turn_(LOOP_NOT_STARTED)
+ {
+ construct(queue_size, latched);
+ }
+
+ RealtimePublisher()
+ : is_running_(false), keep_running_(false), turn_(LOOP_NOT_STARTED)
+ {
+ }
+
+ /// Destructor
+ ~RealtimePublisher()
+ {
+ stop();
+ while (is_running())
+ {
+ std::this_thread::sleep_for(std::chrono::microseconds(100));
+ }
+
+ if (thread_.joinable())
+ {
+ thread_.join();
+ }
+ publisher_.shutdown();
+ }
+
+ void init(const ros::NodeHandle &node, const std::string &topic, int queue_size, bool latched=false)
+ {
+ topic_ = topic;
+ node_ = node;
+ construct(queue_size, latched);
+ }
+
+ /// Stop the realtime publisher from sending out more ROS messages
+ void stop()
+ {
+ keep_running_ = false;
+#ifdef NON_POLLING
+ updated_cond_.notify_one(); // So the publishing loop can exit
+#endif
+ }
+
+ /** \brief Try to get the data lock from realtime
+ *
+ * To publish data from the realtime loop, you need to run trylock to
+ * attempt to get unique access to the msg_ variable. Trylock returns
+ * true if the lock was aquired, and false if it failed to get the lock.
+ */
+ bool trylock()
+ {
+ if (msg_mutex_.try_lock())
+ {
+ if (turn_ == REALTIME)
+ {
+ return true;
+ }
+ else
+ {
+ msg_mutex_.unlock();
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /** \brief Unlock the msg_ variable
+ *
+ * After a successful trylock and after the data is written to the mgs_
+ * variable, the lock has to be released for the message to get
+ * published on the specified topic.
+ */
+ void unlockAndPublish()
+ {
+ turn_ = NON_REALTIME;
+ msg_mutex_.unlock();
+#ifdef NON_POLLING
+ updated_cond_.notify_one();
+#endif
+ }
+
+ /** \brief Get the data lock form non-realtime
+ *
+ * To publish data from the realtime loop, you need to run trylock to
+ * attempt to get unique access to the msg_ variable. Trylock returns
+ * true if the lock was aquired, and false if it failed to get the lock.
+ */
+ void lock()
+ {
+#ifdef NON_POLLING
+ msg_mutex_.lock();
+#else
+ // never actually block on the lock
+ while (!msg_mutex_.try_lock())
+ {
+ std::this_thread::sleep_for(std::chrono::microseconds(200));
+ }
+#endif
+ }
+
+ /** \brief Unlocks the data without publishing anything
+ *
+ */
+ void unlock()
+ {
+ msg_mutex_.unlock();
+ }
+
+private:
+ // non-copyable
+ RealtimePublisher(const RealtimePublisher &) = delete;
+ RealtimePublisher & operator=(const RealtimePublisher &) = delete;
+
+ void construct(int queue_size, bool latched=false)
+ {
+ publisher_ = node_.advertise(topic_, queue_size, latched);
+ msg_buffer_.reset(new boost::circular_buffer(queue_size));
+ outgoing_msg_buffer_.reset(new boost::circular_buffer(queue_size));
+ keep_running_ = true;
+ thread_ = std::thread(&RealtimePublisher::publishingLoop, this);
+ }
+
+
+ bool is_running() const { return is_running_; }
+
+ void publishingLoop()
+ {
+ // Set to realtime scheduler for this thread
+ struct sched_param thread_param;
+ int policy = SCHED_FIFO;
+ thread_param.sched_priority = sched_get_priority_max(policy) - 10;
+ pthread_setschedparam(pthread_self(), policy, &thread_param);
+
+ is_running_ = true;
+ turn_ = REALTIME;
+
+ while (keep_running_)
+ {
+ Msg outgoing;
+
+ // Locks msg_ and copies it
+ lock();
+ while (turn_ != NON_REALTIME && keep_running_)
+ {
+#ifdef NON_POLLING
+ updated_cond_.wait(lock);
+#else
+ unlock();
+ std::this_thread::sleep_for(std::chrono::microseconds(500));
+ lock();
+#endif
+ }
+
+ while(!msg_buffer_->empty())
+ {
+ outgoing_msg_buffer_->push_back(msg_buffer_->front());
+ msg_buffer_->pop_front();
+ }
+ // outgoing = msg_;
+ turn_ = REALTIME;
+
+ unlock();
+
+ // Sends the outgoing message
+ if (keep_running_)
+ {
+ while(!outgoing_msg_buffer_->empty())
+ {
+ publisher_.publish(outgoing_msg_buffer_->front());
+ outgoing_msg_buffer_->pop_front();
+ }
+ }
+ // publisher_.publish(outgoing);
+ }
+ is_running_ = false;
+ }
+
+ std::string topic_;
+ ros::NodeHandle node_;
+ ros::Publisher publisher_;
+ std::atomic is_running_;
+ std::atomic keep_running_;
+
+ std::thread thread_;
+
+ std::mutex msg_mutex_; // Protects msg_
+
+#ifdef NON_POLLING
+ std::condition_variable updated_cond_;
+#endif
+
+ enum {REALTIME, NON_REALTIME, LOOP_NOT_STARTED};
+ std::atomic turn_; // Who's turn is it to use msg_?
+ /// The msg_buffer_ contains the data that will get published on the ROS topic.
+ boost::scoped_ptr > outgoing_msg_buffer_;
+};
+
+template
+using RealtimePublisherSharedPtr = std::shared_ptr >;
+
+}
+
+#endif
\ No newline at end of file