cob_sick_s300.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
18 //##################
19 //#### includes ####
20 
21 // standard includes
22 //--
23 
24 // ROS includes
25 #include <ros/ros.h>
26 #include <XmlRpcException.h>
27 
28 // ROS message includes
29 #include <std_msgs/Bool.h>
30 #include <sensor_msgs/LaserScan.h>
31 #include <diagnostic_msgs/DiagnosticArray.h>
32 
33 // ROS service includes
34 //--
35 
36 // external includes
38 
39 #include <boost/date_time/posix_time/posix_time.hpp>
40 #include <boost/thread/thread.hpp>
41 #include <boost/lexical_cast.hpp>
42 
43 #define ROS_LOG_FOUND
44 
45 //####################
46 //#### node class ####
47 class NodeClass
48 {
49  //
50  public:
51 
53  // topics to publish
57 
58  // topics to subscribe, callback is called for new messages arriving
59  //--
60 
61  // service servers
62  //--
63 
64  // service clients
65  //--
66 
67  // global variables
68  std::string port;
69  std::string node_name;
70  int baud, scan_id;
71  bool inverted;
73  std::string frame_id;
75  unsigned int syncedSICKStamp;
77  bool debug_;
80  std_msgs::Bool inStandby_;
81 
82  // Constructor
84  {
85  // create a handle for this node, initialize node
86  //nh = ros::NodeHandle("~");
87 
88  if(!nh.hasParam("port")) ROS_WARN("Used default parameter for port");
89  nh.param("port", port, std::string("/dev/ttyUSB0"));
90 
91  if(!nh.hasParam("baud")) ROS_WARN("Used default parameter for baud");
92  nh.param("baud", baud, 500000);
93 
94  if(!nh.hasParam("scan_id")) ROS_WARN("Used default parameter for scan_id");
95  nh.param("scan_id", scan_id, 7);
96 
97  if(!nh.hasParam("inverted")) ROS_WARN("Used default parameter for inverted");
98  nh.param("inverted", inverted, false);
99 
100  if(!nh.hasParam("frame_id")) ROS_WARN("Used default parameter for frame_id");
101  nh.param("frame_id", frame_id, std::string("base_laser_link"));
102 
103  if(!nh.hasParam("scan_duration")) ROS_WARN("Used default parameter for scan_duration");
104  nh.param("scan_duration", scan_duration, 0.025); //no info about that in SICK-docu, but 0.025 is believable and looks good in rviz
105 
106  if(!nh.hasParam("scan_cycle_time")) ROS_WARN("Used default parameter for scan_cycle_time");
107  nh.param("scan_cycle_time", scan_cycle_time, 0.040); //SICK-docu says S300 scans every 40ms
108 
109  if(nh.hasParam("debug")) nh.param("debug", debug_, false);
110 
111  if(!nh.hasParam("communication_timeout")) ROS_WARN("Used default parameter for communication timeout");
112  nh.param("communication_timeout", communication_timeout, 0.2);
113 
114  try
115  {
116  //get params for each measurement
117  XmlRpc::XmlRpcValue field_params;
118  if(nh.getParam("fields",field_params) && field_params.getType() == XmlRpc::XmlRpcValue::TypeStruct)
119  {
120  for(XmlRpc::XmlRpcValue::iterator field=field_params.begin(); field!=field_params.
121  end(); field++)
122  {
123  int field_number = boost::lexical_cast<int>(field->first);
124  ROS_DEBUG("Found field %d in params", field_number);
125 
126  if(!field->second.hasMember("scale"))
127  {
128  ROS_ERROR("Missing parameter scale");
129  continue;
130  }
131 
132  if(!field->second.hasMember("start_angle"))
133  {
134  ROS_ERROR("Missing parameter start_angle");
135  continue;
136  }
137 
138  if(!field->second.hasMember("stop_angle"))
139  {
140  ROS_ERROR("Missing parameter stop_angle");
141  continue;
142  }
143 
145  param.dScale = field->second["scale"];
146  param.dStartAngle = field->second["start_angle"];
147  param.dStopAngle = field->second["stop_angle"];
148  scanner_.setRangeField(field_number, param);
149 
150  ROS_DEBUG("params %f %f %f", param.dScale, param.dStartAngle, param.dStopAngle);
151  }
152  }
153  else
154  {
155  //ROS_WARN("No params for the Sick S300 fieldset were specified --> will using default, but it's deprecated now, please adjust parameters!!!");
156 
157  //setting defaults to be backwards compatible
159  param.dScale = 0.01;
160  param.dStartAngle = -135.0/180.0*M_PI;
161  param.dStopAngle = 135.0/180.0*M_PI;
162  scanner_.setRangeField(1, param);
163  }
164  } catch(XmlRpc::XmlRpcException e)
165  {
166  ROS_ERROR_STREAM("Not all params for the Sick S300 fieldset could be read: " << e.getMessage() << "! Error code: " << e.getCode());
167  ROS_ERROR("Node is going to shut down.");
168  exit(-1);
169  }
170 
171  syncedSICKStamp = 0;
172  syncedROSTime = ros::Time::now();
173  syncedTimeReady = false;
174 
175  node_name = ros::this_node::getName();
176 
177  // implementation of topics to publish
178  topicPub_LaserScan = nh.advertise<sensor_msgs::LaserScan>("scan", 1);
179  topicPub_InStandby = nh.advertise<std_msgs::Bool>("scan_standby", 1);
180  topicPub_Diagnostic_ = nh.advertise<diagnostic_msgs::DiagnosticArray>("/diagnostics", 1);
181  }
182 
183  bool open() {
184  return scanner_.open(port.c_str(), baud, scan_id);
185  }
186 
187  bool receiveScan() {
188  std::vector< double > ranges, rangeAngles, intensities;
189  unsigned int iSickTimeStamp, iSickNow;
190 
191  int result = scanner_.getScan(ranges, rangeAngles, intensities, iSickTimeStamp, iSickNow, debug_);
192  static boost::posix_time::ptime point_time_communiaction_ok = boost::posix_time::microsec_clock::local_time();
193 
194  if(result)
195  {
196  if(scanner_.isInStandby())
197  {
198  publishWarn("scanner in standby");
199  ROS_WARN_THROTTLE(30, "scanner %s on port %s in standby", node_name.c_str(), port.c_str());
200  publishStandby(true);
201  }
202  else
203  {
204  publishStandby(false);
205  publishLaserScan(ranges, rangeAngles, intensities, iSickTimeStamp, iSickNow);
206  }
207 
208  point_time_communiaction_ok = boost::posix_time::microsec_clock::local_time();
209  }
210  else
211  {
212  boost::posix_time::time_duration diff = boost::posix_time::microsec_clock::local_time() - point_time_communiaction_ok;
213 
214  if (diff.total_milliseconds() > static_cast<int>(1000*communication_timeout))
215  {
216  ROS_WARN("Communiaction timeout");
217  return false;
218  }
219  }
220 
221  return true;
222  }
223 
224  // Destructor
226  {
227  }
228 
229  void publishStandby(bool inStandby)
230  {
231  this->inStandby_.data = inStandby;
232  topicPub_InStandby.publish(this->inStandby_);
233  }
234 
235  // other function declarations
236  void publishLaserScan(std::vector<double> vdDistM, std::vector<double> vdAngRAD, std::vector<double> vdIntensAU, unsigned int iSickTimeStamp, unsigned int iSickNow)
237  {
238  // fill message
239  int start_scan, stop_scan;
240  int num_readings = vdDistM.size(); // initialize with max scan size
241  start_scan = 0;
242  stop_scan = vdDistM.size();
243 
244  // Sync handling: find out exact scan time by using the syncTime-syncStamp pair:
245  // Timestamp: "This counter is internally incremented at each scan, i.e. every 40 ms (S300)"
246  if(iSickNow != 0) {
247  syncedROSTime = ros::Time::now() - ros::Duration(scan_cycle_time);
248  syncedSICKStamp = iSickNow;
249  syncedTimeReady = true;
250 
251  ROS_DEBUG("Got iSickNow, store sync-stamp: %d", syncedSICKStamp);
252  } else syncedTimeReady = false;
253 
254  // create LaserScan message
255  sensor_msgs::LaserScan laserScan;
256  if(syncedTimeReady) {
257  double timeDiff = (int)(iSickTimeStamp - syncedSICKStamp) * scan_cycle_time;
258  laserScan.header.stamp = syncedROSTime + ros::Duration(timeDiff);
259 
260  ROS_DEBUG("Time::now() - calculated sick time stamp = %f",(ros::Time::now() - laserScan.header.stamp).toSec());
261  } else {
262  laserScan.header.stamp = ros::Time::now();
263  }
264 
265  // fill message
266  laserScan.header.frame_id = frame_id;
267  laserScan.angle_increment = vdAngRAD[start_scan + 1] - vdAngRAD[start_scan];
268  laserScan.range_min = 0.001;
269  laserScan.range_max = 29.5; // though the specs state otherwise, the max range reported by the scanner is 29.96m
270  laserScan.time_increment = (scan_duration) / (vdDistM.size());
271 
272  // rescale scan
273  num_readings = vdDistM.size();
274  laserScan.angle_min = vdAngRAD[start_scan]; // first ScanAngle
275  laserScan.angle_max = vdAngRAD[stop_scan - 1]; // last ScanAngle
276  laserScan.ranges.resize(num_readings);
277  laserScan.intensities.resize(num_readings);
278 
279 
280  // check for inverted laser
281  if(inverted) {
282  // to be really accurate, we now invert time_increment
283  // laserScan.header.stamp = laserScan.header.stamp + ros::Duration(scan_duration); //adding of the sum over all negative increments would be mathematically correct, but looks worse.
284  laserScan.time_increment = - laserScan.time_increment;
285  } else {
286  laserScan.header.stamp = laserScan.header.stamp - ros::Duration(scan_duration); //to be consistent with the omission of the addition above
287  }
288 
289  for(int i = 0; i < (stop_scan - start_scan); i++)
290  {
291  if(inverted)
292  {
293  laserScan.ranges[i] = vdDistM[stop_scan-1-i];
294  laserScan.intensities[i] = vdIntensAU[stop_scan-1-i];
295  }
296  else
297  {
298  laserScan.ranges[i] = vdDistM[start_scan + i];
299  laserScan.intensities[i] = vdIntensAU[start_scan + i];
300  }
301  }
302 
303  // publish Laserscan-message
304  topicPub_LaserScan.publish(laserScan);
305 
306  //Diagnostics
307  diagnostic_msgs::DiagnosticArray diagnostics;
308  diagnostics.header.stamp = ros::Time::now();
309  diagnostics.status.resize(1);
310  diagnostics.status[0].level = 0;
311  diagnostics.status[0].name = nh.getNamespace();
312  diagnostics.status[0].message = "sick scanner running";
313  topicPub_Diagnostic_.publish(diagnostics);
314  }
315 
316  void publishError(std::string error_str) {
317  diagnostic_msgs::DiagnosticArray diagnostics;
318  diagnostics.header.stamp = ros::Time::now();
319  diagnostics.status.resize(1);
320  diagnostics.status[0].level = 2;
321  diagnostics.status[0].name = nh.getNamespace();
322  diagnostics.status[0].message = error_str;
323  topicPub_Diagnostic_.publish(diagnostics);
324  }
325 
326  void publishWarn(std::string warn_str) {
327  diagnostic_msgs::DiagnosticArray diagnostics;
328  diagnostics.header.stamp = ros::Time::now();
329  diagnostics.status.resize(1);
330  diagnostics.status[0].level = 1;
331  diagnostics.status[0].name = nh.getNamespace();
332  diagnostics.status[0].message = warn_str;
333  topicPub_Diagnostic_.publish(diagnostics);
334  }
335 };
336 
337 //#######################
338 //#### main programm ####
339 int main(int argc, char** argv)
340 {
341  // initialize ROS, spezify name of node
342  ros::init(argc, argv, "sick_s300");
343 
344  NodeClass nodeClass;
345 
346  while (ros::ok())
347  {
348  bool bOpenScan = false;
349  while (!bOpenScan && ros::ok()) {
350  ROS_INFO("Opening scanner... (port:%s)", nodeClass.port.c_str());
351 
352  bOpenScan = nodeClass.open();
353 
354  // check, if it is the first try to open scanner
355  if (!bOpenScan) {
356  ROS_ERROR("...scanner not available on port %s. Will retry every second.", nodeClass.port.c_str());
357  nodeClass.publishError("...scanner not available on port");
358  }
359  sleep(1); // wait for scan to get ready if successfull, or wait befor retrying
360  }
361  ROS_INFO("...scanner opened successfully on port %s", nodeClass.port.c_str());
362 
363  // main loop
364  while (ros::ok()) {
365  // read scan
366  if (!nodeClass.receiveScan())
367  {
368  break;
369  }
370  ros::spinOnce();
371  }
372  }
373  return 0;
374 }
ros::Publisher topicPub_Diagnostic_
double communication_timeout
const std::string & getMessage() const
ros::NodeHandle nh
bool param(const std::string &param_name, T &param_val, const T &default_val)
void setRangeField(const int field, const ParamType &param)
#define ROS_WARN_THROTTLE(rate,...)
void publish(const boost::shared_ptr< M > &message) const
ValueStruct::iterator iterator
int main(int argc, char **argv)
bool receiveScan()
void publishWarn(std::string warn_str)
ros::Publisher topicPub_LaserScan
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
ROSCPP_DECL const std::string & getName()
Type const & getType() const
#define ROS_WARN(...)
void publishError(std::string error_str)
bool open(const char *pcPort, int iBaudRate, int iScanId)
void publishStandby(bool inStandby)
void publishLaserScan(std::vector< double > vdDistM, std::vector< double > vdAngRAD, std::vector< double > vdIntensAU, unsigned int iSickTimeStamp, unsigned int iSickNow)
#define ROS_INFO(...)
std::string node_name
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ROSCPP_DECL bool ok()
const std::string & getNamespace() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool getScan(std::vector< double > &vdDistanceM, std::vector< double > &vdAngleRAD, std::vector< double > &vdIntensityAU, unsigned int &iTimestamp, unsigned int &iTimeNow, const bool debug)
bool syncedTimeReady
std::string frame_id
double scan_cycle_time
ros::Time syncedROSTime
bool getParam(const std::string &key, std::string &s) const
std_msgs::Bool inStandby_
static Time now()
std::string port
unsigned int syncedSICKStamp
ScannerSickS300 scanner_
bool hasParam(const std::string &key) const
#define ROS_ERROR_STREAM(args)
double scan_duration
ROSCPP_DECL void spinOnce()
#define ROS_ERROR(...)
ros::Publisher topicPub_InStandby
#define ROS_DEBUG(...)


cob_sick_s300
Author(s): Florian Weisshardt
autogenerated on Wed Apr 7 2021 02:11:50