qb_device_hardware_interface.cpp
Go to the documentation of this file.
1 /***
2  * Software License Agreement: BSD 3-Clause License
3  *
4  * Copyright (c) 2016-2018, qbrobotics®
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
8  * following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, this list of conditions and the
11  * following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
14  * following disclaimer in the documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
17  * products derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
25  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
29 
30 using namespace qb_device_hardware_interface;
31 
32 qbDeviceHW::qbDeviceHW(qb_device_transmission_interface::TransmissionPtr transmission, const std::vector<std::string> &actuators, const std::vector<std::string> &joints)
33  : spinner_(1),
34  transmission_(transmission),
35  actuators_(actuators),
36  joints_(joints) {
37  spinner_.start();
39 }
40 
43  spinner_.stop();
44 }
45 
47  if (services_.at("activate_motors")) {
48  qb_device_srvs::Trigger srv;
49  srv.request.id = device_.id;
50  srv.request.max_repeats = device_.max_repeats;
51  services_.at("activate_motors").call(srv);
52  if (!srv.response.success) {
53  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot activate device [" << device_.id << "].");
54  return -1;
55  }
56  ROS_INFO_STREAM_NAMED("device_hw", "[DeviceHW] device [" << device_.id << "] motors are active!");
57  return 0;
58  }
59  ROS_WARN_STREAM_NAMED("device_hw", "[DeviceHW] service [activate_motors] seems no longer advertised. Trying to reconnect...");
61  return -1;
62 }
63 
64 std::vector<std::string> qbDeviceHW::addNamespacePrefix(const std::vector<std::string> &vector) {
65  std::vector<std::string> namespaced_vector(vector);
66  std::string prefix = device_.name + "_";
67  for (auto &elem : namespaced_vector) {
68  if (!std::regex_match(elem, std::regex("^" + prefix + ".*"))) {
69  elem = prefix + elem;
70  }
71  }
72  return namespaced_vector;
73 }
74 
76  if (services_.at("deactivate_motors")) {
77  qb_device_srvs::Trigger srv;
78  srv.request.id = device_.id;
79  srv.request.max_repeats = device_.max_repeats;
80  services_.at("deactivate_motors").call(srv);
81  if (!srv.response.success) {
82  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot deactivate device [" << device_.id << "].");
83  return -1;
84  }
85  ROS_INFO_STREAM_NAMED("device_hw", "[DeviceHW] device [" << device_.id << "] motors are inactive.");
86  return 0;
87  }
88  ROS_WARN_STREAM_NAMED("device_hw", "[DeviceHW] service [deactivate_motors] seems no longer advertised. Trying to reconnect...");
90  return -1;
91 }
92 
93 std::string qbDeviceHW::getInfo() {
94  if (services_.at("get_info")) {
95  qb_device_srvs::Trigger srv;
96  srv.request.id = device_.id;
97  srv.request.max_repeats = device_.max_repeats;
98  services_.at("get_info").call(srv);
99  if (!srv.response.success) {
100  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot get info from device [" << device_.id << "].");
101  return "";
102  }
103  return srv.response.message;
104  }
105  ROS_WARN_STREAM_NAMED("device_hw", "[DeviceHW] service [get_info] seems no longer advertised. Trying to reconnect...");
107  return "";
108 }
109 
110 int qbDeviceHW::getMeasurements(std::vector<double> &positions, std::vector<double> &currents, ros::Time &stamp) {
111  if (services_.at("get_measurements")) {
112  qb_device_srvs::GetMeasurements srv;
113  srv.request.id = device_.id;
114  srv.request.max_repeats = device_.max_repeats;
115  srv.request.get_currents = device_.get_currents;
116  srv.request.get_positions = device_.get_positions;
117  srv.request.get_distinct_packages = device_.get_distinct_packages;
118  services_.at("get_measurements").call(srv);
119  if (!srv.response.success) {
120  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot get measurements from device [" << device_.id << "].");
121  return srv.response.failures; // positions and currents are not updated
122  }
123 
124  // positions and currents cannot be resized because actuators state handle in the caller stores their addresses
125  for (int i=0; i<positions.size() && i<srv.response.positions.size(); i++) {
126  positions.at(i) = srv.response.positions.at(i) * device_.motor_axis_direction;
127  }
128  for (int i=0; i<currents.size() && i<srv.response.currents.size(); i++) {
129  currents.at(i) = srv.response.currents.at(i);
130  }
131  stamp = srv.response.stamp;
132  return srv.response.failures;
133  }
134  ROS_WARN_STREAM_NAMED("device_hw", "[DeviceHW] service [get_measurements] seems no longer advertised. Trying to reconnect...");
136  return -1;
137 }
138 
139 bool qbDeviceHW::init(ros::NodeHandle &root_nh, ros::NodeHandle &robot_hw_nh) {
140  node_handle_ = robot_hw_nh;
141  if (!robot_hw_nh.getParam("device_name", device_.name)) {
142  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot retrieve 'device_name' from the Parameter Service [" << robot_hw_nh.getNamespace() << "].");
143  return false;
144  }
145  if (!robot_hw_nh.getParam("device_id", device_.id)) {
146  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot retrieve 'device_id' from the Parameter Service [" << robot_hw_nh.getNamespace() << "].");
147  return false;
148  }
149  if (!urdf_model_.initParamWithNodeHandle("robot_description", root_nh)) {
150  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot retrieve 'robot_description' from the Parameter Service [" << robot_hw_nh.getNamespace() << "].");
151  return false;
152  }
153 
154  state_publisher_ = robot_hw_nh.advertise<qb_device_msgs::StateStamped>("state", 1);
155 
156  actuators_.setJoints(robot_hw_nh.param<std::vector<std::string>>("actuators", addNamespacePrefix(actuators_.names)));
157  joints_.setJoints(robot_hw_nh.param<std::vector<std::string>>("joints", addNamespacePrefix(joints_.names)));
158 
161  transmission_.initialize(robot_hw_nh.param<std::string>("transmission", "transmission"), actuators_, joints_);
162 
163  use_simulator_mode_ = robot_hw_nh.param<bool>("use_simulator_mode", false);
164 
167 
168  return true;
169 }
170 
172  if (services_.at("initialize_device")) {
173  qb_device_srvs::InitializeDevice srv;
174  srv.request.id = device_.id;
175  srv.request.activate = node_handle_.param<bool>("activate_on_initialization", false) && !use_simulator_mode_;
176  srv.request.rescan = node_handle_.param<bool>("rescan_on_initialization", false);
177  int max_repeats = node_handle_.param<int>("max_repeats", 3);
178  srv.request.max_repeats = max_repeats;
179  services_.at("initialize_device").call(srv);
180  if (!srv.response.success) {
181  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot initialize device [" << device_.id << "].");
182  return -1;
183  }
184  device_.max_repeats = max_repeats;
185  device_.get_currents = node_handle_.param<bool>("get_currents", true);
186  device_.get_positions = node_handle_.param<bool>("get_positions", true);
187  device_.get_distinct_packages = node_handle_.param<bool>("get_distinct_packages", false);
188  device_.set_commands = node_handle_.param<bool>("set_commands", true);
189  device_.set_commands_async = node_handle_.param<bool>("set_commands_async", false);
190  device_.serial_port = srv.response.info.serial_port;
191  device_.position_limits = srv.response.info.position_limits;
192  device_.encoder_resolutions = srv.response.info.encoder_resolutions;
193 
194  device_info_.id = device_.id;
195  device_info_.serial_port = device_.serial_port;
196  device_info_.max_repeats = device_.max_repeats;
197  device_info_.get_currents = device_.get_currents;
198  device_info_.get_positions = device_.get_positions;
199  device_info_.get_distinct_packages = device_.get_distinct_packages;
200  device_info_.set_commands = device_.set_commands;
201  device_info_.set_commands_async = device_.set_commands_async;
202  device_info_.position_limits = device_.position_limits;
203  device_info_.encoder_resolutions = device_.encoder_resolutions;
204 
205  ROS_INFO_STREAM_NAMED("device_hw", "[DeviceHW] device [" << device_.id << "] is initialized.");
206  return 0;
207  }
208  ROS_WARN_STREAM_NAMED("device_hw", "[DeviceHW] service [initialize_device] is no longer advertised.");
209  resetServicesAndWait(false);
210  return -1;
211 }
212 
214  services_["activate_motors"] = node_handle_.serviceClient<qb_device_srvs::Trigger>("/communication_handler/activate_motors", true);
215  services_["deactivate_motors"] = node_handle_.serviceClient<qb_device_srvs::Trigger>("/communication_handler/deactivate_motors", true);
216  services_["get_info"] = node_handle_.serviceClient<qb_device_srvs::Trigger>("/communication_handler/get_info", true);
217  services_["get_measurements"] = node_handle_.serviceClient<qb_device_srvs::GetMeasurements>("/communication_handler/get_measurements", true);
218  services_["initialize_device"] = node_handle_.serviceClient<qb_device_srvs::InitializeDevice>("/communication_handler/initialize_device", true);
219  services_["set_commands"] = node_handle_.serviceClient<qb_device_srvs::SetCommands>("/communication_handler/set_commands", true);
220  waitForServices();
221 }
222 
224  qb_device_msgs::StateStamped msg;
225 
226  msg.device_info = device_info_;
227  msg.device_data.is_reliable = actuators_.is_reliable;
228  msg.device_data.consecutive_failures = actuators_.consecutive_failures;
229  msg.header.stamp = actuators_.stamp;
230  msg.header.frame_id = device_.name;
231 
232  for (int i=0; i<actuators_.names.size(); i++) {
233  qb_device_msgs::ResourceData msg_actuator_data;
234  msg_actuator_data.name = actuators_.names.at(i);
235  msg_actuator_data.position = actuators_.positions.at(i);
236  msg_actuator_data.velocity = actuators_.velocities.at(i);
237  msg_actuator_data.effort = actuators_.efforts.at(i);
238  msg_actuator_data.command = actuators_.commands.at(i);
239  msg.device_data.actuators.push_back(msg_actuator_data);
240  }
241 
242  for (int i=0; i<joints_.names.size(); i++) {
243  qb_device_msgs::ResourceData msg_joint_data;
244  msg_joint_data.name = joints_.names.at(i);
245  msg_joint_data.position = joints_.positions.at(i);
246  msg_joint_data.velocity = joints_.velocities.at(i);
247  msg_joint_data.effort = joints_.efforts.at(i);
248  msg_joint_data.command = joints_.commands.at(i);
249  msg.device_data.joints.push_back(msg_joint_data);
250  }
251 
253 }
254 
255 void qbDeviceHW::read(const ros::Time &time, const ros::Duration &period) {
256  // store old actuator positions
257  std::vector<double> actuator_position_old(actuators_.positions);
258  // read actuator state from the hardware
260  if (actuators_.is_reliable) {
261  // update velocities which are computed by differentiation, e.g. (pos_new - pos_old)/delta_t
262  for (int i = 0; i < actuators_.names.size(); i++) {
263  actuators_.velocities.at(i) = (actuators_.positions.at(i) - actuator_position_old.at(i)) / period.toSec();
264  }
265  }
266 
267  if (use_simulator_mode_) {
269  }
270 
271  // no needs to enforce joint limits: they come from the real device
272 
273  // propagate current actuator state to joints
275 
276  // make data available for other ROS nodes
277  publish();
278 }
279 
280 void qbDeviceHW::resetServicesAndWait(const bool &reinitialize_device) {
281  waitForServices();
282  // reset all the service clients in case they were not yet advertised during initialization
284  if (reinitialize_device) {
286  }
287 }
288 
289 int qbDeviceHW::setCommands(const std::vector<double> &commands) {
290  if (services_.at("set_commands")) {
291  qb_device_srvs::SetCommands srv;
292  srv.request.id = device_.id;
293  srv.request.max_repeats = device_.max_repeats;
294  srv.request.set_commands = device_.set_commands;
295  srv.request.set_commands_async = device_.set_commands_async;
296 
297  for (auto const &command : commands) {
298  srv.request.commands.push_back(static_cast<short int>(command) * device_.motor_axis_direction);
299  }
300 
301  services_.at("set_commands").call(srv);
302  if (!srv.response.success) {
303  ROS_ERROR_STREAM_NAMED("device_hw", "[DeviceHW] cannot send commands to device [" << device_.id << "].");
304  return -1;
305  }
306  return 0;
307  }
308  ROS_WARN_STREAM_NAMED("device_hw", "[DeviceHW] service [set_commands] seems no longer advertised. Trying to reconnect...");
310  return -1;
311 }
312 
314  while(initializeDevice()) {
315  ros::Duration(1.0).sleep();
316  }
317 }
318 
320  for (auto &service : services_) {
321  service.second.waitForExistence();
322  }
323  ROS_INFO_STREAM_NAMED("device_hw", "[DeviceHW] is connected to all the services advertise by [CommunicationHandler].");
324 }
325 
326 void qbDeviceHW::write(const ros::Time &time, const ros::Duration &period) {
327  // enforce joint limits for all registered interfaces
329 
330  // propagate joint commands to actuators
332 
333  // send actuator commands to the hardware
335 }
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
void resetServicesAndWait(const bool &reinitialize_device=true)
Re-subscribe to all the services advertised by the Communication Handler and wait until all the servi...
qb_device_hardware_interface::qbDeviceHWResources joints_
std::shared_ptr< transmission_interface::Transmission > TransmissionPtr
void initialize(ros::NodeHandle &robot_hw_nh, qb_device_hardware_interface::qbDeviceHWResources &joints, const urdf::Model &urdf_model, hardware_interface::PositionJointInterface &joint_position)
Retrieve the joint limits for each given joint.
void setReliability(const int &max_repeats, const int &consecutive_failures)
Compare the given consecutive failures w.r.t.
void publish(const boost::shared_ptr< M > &message) const
#define ROS_ERROR_STREAM_NAMED(name, args)
virtual int getMeasurements(std::vector< double > &positions, std::vector< double > &currents, ros::Time &stamp)
Call the service to retrieve device measurements (both positions and currents) and wait for the respo...
void publish()
Construct a qb_device_msgs::StateStamped message of the whole device state with the data retrieved du...
bool sleep() const
void initialize(const std::string &transmission_name, qb_device_hardware_interface::qbDeviceHWResources &actuators, qb_device_hardware_interface::qbDeviceHWResources &joints)
Initialize the Device Transmission Resources interfaces.
qb_device_joint_limits_interface::qbDeviceJointLimitsResources joint_limits_
URDF_EXPORT bool initParamWithNodeHandle(const std::string &param, const ros::NodeHandle &nh=ros::NodeHandle())
#define ROS_INFO_STREAM_NAMED(name, args)
virtual std::string getInfo()
Call the service to retrieve the printable configuration setup of the device and wait for the respons...
bool init(ros::NodeHandle &root_nh, ros::NodeHandle &robot_hw_nh) override
The init function is called to initialize the RobotHW from a non-realtime thread. ...
~qbDeviceHW() override
Deactivate motors and stop the async spinner.
ROSLIB_DECL std::string command(const std::string &cmd)
hardware_interface::PositionJointInterface joint_position
qb_device_hardware_interface::qbDeviceResources device_
transmission_interface::JointToActuatorPositionInterface joint_to_actuator_position
bool param(const std::string &param_name, T &param_val, const T &default_val) const
void enforceLimits(const ros::Duration &period)
Enforce limits for all managed interfaces.
qb_device_hardware_interface::qbDeviceHWResources actuators_
void write(const ros::Time &time, const ros::Duration &period) override
Enforce joint limits for all registered joint limit interfaces, propagate joint commands to actuators...
const std::string & getNamespace() const
virtual int deactivateMotors()
Call the service to deactivate the device motors and wait for the response.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
qb_device_transmission_interface::qbDeviceTransmissionResources transmission_
virtual int activateMotors()
Call the service to activate the device motors and wait for the response.
void waitForServices()
Wait until all the services advertised by the Communication Handler are active, then reinitialize the...
void initializeServicesAndWait()
Subscribe to all the services advertised by the Communication Handler and wait until all the services...
std::vector< std::string > addNamespacePrefix(const std::vector< std::string > &vector)
Add the namespace prefix stored in the private namespace_ to all the elements of the given vector...
#define ROS_INFO_STREAM(args)
qbDeviceHW(qb_device_transmission_interface::TransmissionPtr transmission, const std::vector< std::string > &actuators, const std::vector< std::string > &joints)
Initialize HW resources and interfaces, and wait until it is initialized from the Communication Handl...
transmission_interface::ActuatorToJointStateInterface actuator_to_joint_state
bool getParam(const std::string &key, std::string &s) const
virtual int initializeDevice()
Call the service to initialize the device with parameters from the Communication Handler and wait for...
void waitForInitialization()
Wait until the device is initialized.
virtual int setCommands(const std::vector< double > &commands)
Call the service to send reference commands to the device and wait for the response.
std::map< std::string, ros::ServiceClient > services_
void setJoints(const std::vector< std::string > &joints)
Initialize the joint names of the Resource with the given vector.
qb_device_hardware_interface::qbDeviceHWInterfaces interfaces_
void read(const ros::Time &time, const ros::Duration &period) override
Read actuator state from the hardware, propagate it to joint states and publish the whole device stat...
void initialize(hardware_interface::RobotHW *robot, qbDeviceHWResources &joints)
Initialize the joint_state and joint_position HW interfaces with the given device Resource...
#define ROS_WARN_STREAM_NAMED(name, args)


qb_device_hardware_interface
Author(s): qbrobotics®
autogenerated on Wed Oct 9 2019 03:45:36