YARP
Yet Another Robot Platform
laserHokuyo.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #define _USE_MATH_DEFINES
20 
21 #include "laserHokuyo.h"
22 
23 #include <yarp/os/Time.h>
24 #include <yarp/os/Log.h>
25 #include <yarp/os/LogStream.h>
26 
27 #include <cmath>
28 #include <cstdlib>
29 #include <cstring>
30 #include <iostream>
31 #include <limits>
32 
33 #ifndef DEG2RAD
34 #define DEG2RAD M_PI/180.0
35 #endif
36 
37 using namespace std;
38 
39 namespace {
40 YARP_LOG_COMPONENT(LASERHOKUYO, "yarp.devices.laserHokuyo")
41 }
42 
44 {
45  internal_status = HOKUYO_STATUS_NOT_READY;
46  info = "Hokuyo Laser";
47  device_status = DEVICE_OK_STANBY;
48 
49  yCTrace(LASERHOKUYO, "%s", config.toString().c_str());
50 
51  bool br = config.check("GENERAL");
52  if (br == false)
53  {
54  yCError(LASERHOKUYO, "cannot read 'GENERAL' section");
55  return false;
56  }
57  yarp::os::Searchable& general_config = config.findGroup("GENERAL");
58 
59  //list of mandatory options
60  //TODO change comments
61  period = general_config.check("Period", Value(50), "Period of the sampling thread").asInt32() / 1000.0;
62 
63  if (general_config.check("max_angle") == false) { yCError(LASERHOKUYO) << "Missing max_angle param"; return false; }
64  if (general_config.check("min_angle") == false) { yCError(LASERHOKUYO) << "Missing min_angle param"; return false; }
65  max_angle = general_config.find("max_angle").asFloat64();
66  min_angle = general_config.find("min_angle").asFloat64();
67 
68  start_position = general_config.check("Start_Position", Value(0), "Start position").asInt32();
69  end_position = general_config.check("End_Position", Value(1080), "End Position").asInt32();
70 
71  error_codes = general_config.check("Convert_Error_Codes", Value(0), "Substitute error codes with legal measurments").asInt32();
72  std::string s = general_config.check("Laser_Mode", Value("GD"), "Laser Mode (GD/MD").asString();
73 
74  if (general_config.check("Measurement_Units"))
75  {
76  yCError(LASERHOKUYO) << "Deprecated parameter 'Measurement_Units'. Please Remove it from the configuration file.";
77  }
78 
79  if (error_codes==1)
80  {
81  yCInfo(LASERHOKUYO, "'error_codes' option enabled. Invalid samples will be substituted with out-of-range measurements.");
82  }
83  if (s=="GD")
84  {
85  laser_mode = GD_MODE;
86  yCInfo(LASERHOKUYO, "Using GD mode (single acquisition)");
87  }
88  else if (s=="MD")
89  {
90  laser_mode = MD_MODE;
91  yCInfo(LASERHOKUYO, "Using MD mode (continuous acquisition)");
92  }
93  else
94  {
95  laser_mode = GD_MODE;
96  yCError(LASERHOKUYO, "Laser_mode not found. Using GD mode (single acquisition)");
97  }
98  setPeriod(period);
99 
100  bool br2 = config.check("SERIAL_PORT_CONFIGURATION");
101  if (br2 == false)
102  {
103  yCError(LASERHOKUYO, "cannot read 'SERIAL_PORT_CONFIGURATION' section");
104  return false;
105  }
106  yarp::os::Searchable& serial_config = config.findGroup("SERIAL_PORT_CONFIGURATION");
107  string ss = serial_config.toString();
108  Property prop;
109  prop.fromString(ss);
110  prop.put("device", "serialport");
111 
112  driver.open(prop);
113  if (!driver.isValid())
114  {
115  yCError(LASERHOKUYO, "Error opening PolyDriver check parameters");
116  return false;
117  }
118 
119  pSerial=nullptr;
120  driver.view(pSerial);
121 
122  if (!pSerial)
123  {
124  yCError(LASERHOKUYO, "Error opening serial driver. Device not available");
125  return false;
126  }
127 
128  Bottle b;
129  Bottle b_ans;
130  string ans;
131 
132  // *** Check if the URG device is present ***
133  b.addString("SCIP2.0\n");
134  pSerial->send(b);
136  pSerial->receive(b_ans);
137  if (b_ans.size()>0)
138  {
139  yCInfo(LASERHOKUYO, "URG device successfully initialized.");
140  yCDebug(LASERHOKUYO, "%s", b_ans.get(0).asString().c_str());
141  }
142  else
143  {
144  yCError(LASERHOKUYO, "Error: URG device not found.");
145  //return false;
146  }
147  b.clear();
148  b_ans.clear();
149 
150  // *** Change the baud rate to 115200 ***
151  /*b.addString("SS01152001\n");
152  pSerial->send(b);
153  yarp::os::SystemClock::delaySystem(0.040);
154  pSerial->receive(b_ans);
155  ans = b_ans.get(0).asString();
156  yCDebug(LASERHOKUYO, "%s",ans.c_str());
157  b.clear();
158  b_ans.clear();*/
159 
160  // *** Read the firmware version parameters ***
161  b.addString("VV\n");
162  pSerial->send(b);
164  pSerial->receive(b_ans);
165  ans = b_ans.get(0).asString();
166  yCDebug(LASERHOKUYO, "%s",ans.c_str());
167  b.clear();
168  b_ans.clear();
169 
170  // *** Read the sensor specifications ***
171  b.addString("II\n");
172  pSerial->send(b);
174  pSerial->receive(b_ans);
175  ans = b_ans.get(0).asString();
176  yCDebug(LASERHOKUYO, "%s", ans.c_str());
177  b.clear();
178  b_ans.clear();
179 
180  // *** Read the URG configuration parameters ***
181  b.addString("PP\n");
182  pSerial->send(b);
184  pSerial->receive(b_ans);
185  /*
186  syntax of the answer:
187  MODL ... Model information of the sensor.
188  DMIN ... Minimum measurable distance [mm]
189  DMAX ... Maximum measurable distance [mm]
190  ARES ... Angular resolution(Number of splits in 360 degree)
191  AMIN ... First Step of the Measurement Range
192  AMAX ... Last Step of the Measurement Range
193  AFRT ... Step number on the sensor's front axis
194  SCAN ... Standard angular velocity
195  */
196  ans = b_ans.get(0).asString();
197  yCDebug(LASERHOKUYO, "%s", ans.c_str());
198  //parsing the answer
199  size_t found;
200  found = ans.find("MODL");
201  if (found!=string::npos) sensor_properties.MODL = string(ans.c_str()+found+5);
202  found = ans.find("DMIN");
203  if (found!=string::npos) sensor_properties.DMIN = atoi(ans.c_str()+found+5);
204  found = ans.find("DMAX");
205  if (found!=string::npos) sensor_properties.DMAX = atoi(ans.c_str()+found+5);
206  found = ans.find("ARES");
207  if (found!=string::npos) sensor_properties.ARES = atoi(ans.c_str()+found+5);
208  found = ans.find("AMIN");
209  if (found!=string::npos) sensor_properties.AMIN = atoi(ans.c_str()+found+5);
210  found = ans.find("AMAX");
211  if (found!=string::npos) sensor_properties.AMAX = atoi(ans.c_str()+found+5);
212  found = ans.find("AFRT");
213  if (found!=string::npos) sensor_properties.AFRT = atoi(ans.c_str()+found+5);
214  found = ans.find("SCAN");
215  if (found!=string::npos) sensor_properties.SCAN = atoi(ans.c_str()+found+5);
216  b.clear();
217  b_ans.clear();
218 
219  // *** Turns on the Laser ***
220  b.addString("BM\n");
221  pSerial->send(b);
223  pSerial->receive(b_ans);
224  // @@@TODO: Check the answer
225  b.clear();
226  b_ans.clear();
227 
228 
229 
230  //elements are:
231  sensorsNum=16*12;
232  laser_data.resize(sensorsNum);
233 
234  if (laser_mode==MD_MODE)
235  {
236  // *** Starts endless acquisition mode***
237  char message [255];
238  std::snprintf(message, 255, "MD%04d%04d%02d%01d%02d\n",start_position,end_position,1,1,0);
239  b.addString(message);
240  pSerial->send(b);
241  b.clear();
242  b_ans.clear();
243  }
244 
245  else if (laser_mode==GD_MODE)
246  {
247  // *** Starts one single acquisition ***
248  char message [255];
249  std::snprintf(message, 255, "GD%04d%04d%02d\n",start_position,end_position,1);
250  b.addString(message);
251  pSerial->send(b);
252  b.clear();
253  b_ans.clear();
254  }
255 
256  return PeriodicThread::start();
257 }
258 
260 {
261  PeriodicThread::stop();
262 
263  Bottle b;
264  Bottle b_ans;
265 
266  // *** Turns off the Laser ***
267  b.addString("QT\n");
268  pSerial->send(b);
269  pSerial->receive(b_ans);
270 
271  // @@@TODO: Check the answer
272 
273  b.clear();
274  b_ans.clear();
275 
276  driver.close();
277  return true;
278 }
279 
280 bool laserHokuyo::getDistanceRange(double& min, double& max)
281 {
282  //should return range 0.1-30m (or 100, 30000mm depending on the measurement units)
283  min = 0.1;
284  max = 30;
285  return true;
286 }
287 
288 bool laserHokuyo::setDistanceRange(double min, double max)
289 {
290  yCError(LASERHOKUYO, "setDistanceRange NOT YET IMPLEMENTED");
291  return false;
292 }
293 
294 bool laserHokuyo::getScanLimits(double& min, double& max)
295 {
296  //degrees
297  min = min_angle;
298  max = max_angle;
299  return true;
300 }
301 
302 bool laserHokuyo::setScanLimits(double min, double max)
303 {
304  yCError(LASERHOKUYO, "setScanLimits NOT YET IMPLEMENTED");
305  return false;
306 }
307 
309 {
310  step = 0.25; //deg //1080*0.25=270
311  return true;
312 }
313 
315 {
316  yCError(LASERHOKUYO, "setHorizontalResolution NOT YET IMPLEMENTED");
317  return false;
318 }
319 
320 bool laserHokuyo::getScanRate(double& rate)
321 {
322  rate = 20; //20 Hz = 50 ms
323  return true;
324 }
325 
326 bool laserHokuyo::setScanRate(double rate)
327 {
328  yCError(LASERHOKUYO, "setScanRate NOT YET IMPLEMENTED");
329  return false;
330 }
331 
333 {
334  if (internal_status != HOKUYO_STATUS_NOT_READY)
335  {
336  mutex.lock();
337  yCTrace(LASERHOKUYO, "data: %s", laser_data.toString().c_str());
338  out = laser_data;
339  mutex.unlock();
341  return true;
342  }
344  return false;
345 }
346 
347 bool laserHokuyo::getLaserMeasurement(std::vector<LaserMeasurementData> &data)
348 {
349  if (internal_status != HOKUYO_STATUS_NOT_READY)
350  {
351  mutex.lock();
352  yCTrace(LASERHOKUYO, "data: %s", laser_data.toString().c_str());
353  size_t size = laser_data.size();
354  data.resize(size);
355  if (max_angle < min_angle)
356  {
357  yCError(LASERHOKUYO) << "getLaserMeasurement failed";
358  mutex.unlock();
359  return false;
360  }
361 
362  double laser_angle_of_view = max_angle - min_angle;
363  for (size_t i = 0; i < size; i++)
364  {
365  double angle = (i / double(size)*laser_angle_of_view + min_angle)* DEG2RAD;
366  data[i].set_polar(laser_data[i], angle);
367  }
368  mutex.unlock();
370  return true;
371  }
372 
374  return false;
375 }
377 {
378  mutex.lock();
379  status = device_status;
380  mutex.unlock();
381  return true;
382 }
383 
385 {
386  yCTrace(LASERHOKUYO, "laserHokuyo:: thread initialising...");
387  yCTrace(LASERHOKUYO, "... done!");
388  return true;
389 }
390 
391 inline int laserHokuyo::calculateCheckSum(const char* buffer, int size, char actual_sum)
392 {
393  char expected_sum = 0x00;
394  int i;
395 
396  for (i = 0; i < size; ++i)
397  {
398  expected_sum += buffer[i];
399  }
400  expected_sum = (expected_sum & 0x3f) + 0x30;
401 
402  return (expected_sum == actual_sum) ? 0 : -1;
403 }
404 
405 inline long laserHokuyo::decodeDataValue(const char* data, int data_byte)
406 {
407  long value = 0;
408  for (int i = 0; i < data_byte; ++i)
409  {
410  value <<= 6;
411  value &= ~0x3f;
412  value |= data[i] - 0x30;
413  }
414  return value;
415 }
416 
417 int laserHokuyo::readData(const Laser_mode_type laser_mode, const char* text_data, const int text_data_len, int current_line, yarp::sig::Vector& data)
418 {
419  static char data_block [4000];
420 
421  if (text_data_len==0)
422  {
423  return HOKUYO_STATUS_ERROR_NOTHING_RECEIVED;
424  }
425 
426 // long int timestamp = 0 ;
427 
428  // termination complete check
429  if (text_data_len == 1 &&
430  text_data[0] == '\n')
431  {
432  //Decode the data
433  int len = strlen(data_block);
434  for (int value_counter =0; value_counter < len; value_counter+=3)
435  {
436  int value = decodeDataValue(data_block+value_counter, 3);
437  if (value<sensor_properties.DMIN && error_codes==1)
438  {
439  value=sensor_properties.DMAX;
440  }
441  //units are m
442  data.push_back(value/1000.0);
443  }
444  return HOKUYO_STATUS_ACQUISITION_COMPLETE;
445  }
446 
447  // check in the first line if it is a valid answer to GD command
448  if (current_line == 0)
449  {
450  data_block[0]='\0'; //resets the datablock;
451  if ((laser_mode == MD_MODE && (text_data[0] != 'M' || text_data[1] != 'D')) ||
452  (laser_mode == GD_MODE && (text_data[0] != 'G' || text_data[1] != 'D')))
453  {
454  yCTrace(LASERHOKUYO, "Invalid answer to a MD command: %s", text_data);
455  return HOKUYO_STATUS_ERROR_INVALID_COMMAND;
456  }
457  else
458  return HOKUYO_STATUS_OK;
459  }
460 
461  // check in the second line if the status of the sensor is ok
462  if (current_line == 1)
463  {
464  if ((laser_mode == MD_MODE && (text_data[0] != '9' || text_data[1] != '9' || text_data[2] != 'b' )) ||
465  (laser_mode == GD_MODE && (text_data[0] != '0' || text_data[1] != '0' || text_data[2] != 'P' )))
466  {
467  yCTrace(LASERHOKUYO, "Invalid sensor status: %s", text_data);
468  return HOKUYO_STATUS_ERROR_BUSY;
469  }
470  else
471  return HOKUYO_STATUS_OK;
472  }
473 
474  // verify the checksum for all the following lines
475  if (current_line >= 2)
476  {
477  char expected_checksum = text_data[text_data_len - 2];
478  if (calculateCheckSum(text_data, text_data_len - 2, expected_checksum) < 0)
479  {
480  yCTrace(LASERHOKUYO, "Checksum error, line: %d: %s", current_line, text_data);
481  return HOKUYO_STATUS_ERROR_INVALID_CHECKSUM;
482  }
483  }
484 
485  // read the sensor time stamp in the third line
486  if (current_line == 2)
487  {
488 // timestamp = decodeDataValue(text_data, 4);
489  }
490 
491  // read the data in the next lines
492  if (current_line >= 3)
493  {
494  strncat (data_block, text_data, text_data_len-2 );
495  }
496 
497  //increments the lines counter
498  //current_line++;
499 
500  return HOKUYO_STATUS_OK;
501 }
502 
504 {
505  //send the GD command: get one single measurement, D precision
506  Bottle b;
507  Bottle b_ans;
508  constexpr int buffer_size = 128;
509  char command [buffer_size];
510  char answer [buffer_size];
511  static double old;
512  yarp::sig::Vector data_vector;
513 
514  string data_text;
515  double t1 = yarp::os::SystemClock::nowSystem();
516  double t2 = 0;
517  bool timeout = false;
518  bool rx_completed = false;
519  bool error = false;
520  int current_line =0;
521  double maxtime=1;
522  do
523  {
524  int answer_len = pSerial->receiveLine(answer, buffer_size);
525  internal_status = readData(laser_mode, answer,answer_len,current_line,data_vector);
526  if (internal_status < 0 && internal_status != HOKUYO_STATUS_ERROR_NOTHING_RECEIVED)
527  {
528  error = true;
529  }
530  if (internal_status == HOKUYO_STATUS_OK)
531  {
532  current_line++;
533  }
534  if (internal_status == HOKUYO_STATUS_ACQUISITION_COMPLETE)
535  {
536  rx_completed = true;
537  }
539  if (t2-t1>maxtime) timeout = true;
540  }
541  while (rx_completed == false && timeout == false && error == false);
542 
543  if (timeout)
544  {
545  yCError(LASERHOKUYO, "laserHokuyo Timeout!");
546  }
547  if (error)
548  {
549  yCError(LASERHOKUYO, "laserHokuyo Communication Error, internal status=%d",internal_status);
550  }
551  yCTrace(LASERHOKUYO, "time: %.3f %.3f",t2-t1, t2-old);
552  old = t2;
553 
554  mutex.lock();
555 
556  if (rx_completed)
557  {
558  laser_data=data_vector;
559  }
560  mutex.unlock();
561 
562  if (laser_mode==GD_MODE)
563  {
564  std::snprintf(command, buffer_size, "GD%04d%04d%02d\n",start_position,end_position,1);
565  b.addString(command);
566  pSerial->send(b);
567  }
568 
569  //SystemClock::delaySystem (0.100);
570 }
571 
573 {
574  yCTrace(LASERHOKUYO, "laserHokuyo Thread releasing...");
575  yCTrace(LASERHOKUYO, "... done.");
576 }
577 
578 bool laserHokuyo::getDeviceInfo(std::string &device_info)
579 {
580  this->mutex.lock();
581  device_info = info;
582  this->mutex.unlock();
583  return true;
584 }
LogStream.h
DEG2RAD
#define DEG2RAD
Definition: laserHokuyo.cpp:34
yarp::os::Bottle
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
yarp::dev::IRangefinder2D::DEVICE_GENERAL_ERROR
@ DEVICE_GENERAL_ERROR
Definition: IRangefinder2D.h:44
yarp::os::Property::put
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:998
yarp::os::Bottle::clear
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:124
laserHokuyo::getLaserMeasurement
bool getLaserMeasurement(std::vector< LaserMeasurementData > &data) override
Get the device measurements.
Definition: laserHokuyo.cpp:347
yarp::os::Searchable
A base class for nested structures that can be searched.
Definition: Searchable.h:69
yarp::os::Bottle::size
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:254
laserHokuyo::threadRelease
void threadRelease() override
Release method.
Definition: laserHokuyo.cpp:572
laserHokuyo::setScanLimits
bool setScanLimits(double min, double max) override
set the scan angular range.
Definition: laserHokuyo.cpp:302
yarp::os::Searchable::findGroup
virtual Bottle & findGroup(const std::string &key) const =0
Gets a list corresponding to a given keyword.
yarp::os::Searchable::toString
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
yarp::os::Property::fromString
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1046
YARP_LOG_COMPONENT
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
yarp::dev::IRangefinder2D::Device_status
Device_status
Definition: IRangefinder2D.h:41
yarp::os::SystemClock::nowSystem
static double nowSystem()
Definition: SystemClock.cpp:37
yarp::sig::VectorOf< double >
Log.h
laserHokuyo::setDistanceRange
bool setDistanceRange(double min, double max) override
set the device detection range.
Definition: laserHokuyo.cpp:288
yarp::os::Bottle::get
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
yarp::os::SystemClock::delaySystem
static void delaySystem(double seconds)
Definition: SystemClock.cpp:32
laserHokuyo::threadInit
bool threadInit() override
Initialization method.
Definition: laserHokuyo.cpp:384
laserHokuyo.h
laserHokuyo::setScanRate
bool setScanRate(double rate) override
set the scan rate (scans per seconds)
Definition: laserHokuyo.cpp:326
laserHokuyo::getDistanceRange
bool getDistanceRange(double &min, double &max) override
get the device detection range
Definition: laserHokuyo.cpp:280
yarp::os::Value::asString
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
yarp::os::Searchable::check
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
buffer
Definition: V4L_camera.h:75
yarp::os::Searchable::find
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
laserHokuyo::run
void run() override
Loop function.
Definition: laserHokuyo.cpp:503
laserHokuyo::getRawData
bool getRawData(yarp::sig::Vector &data) override
Get the device measurements.
Definition: laserHokuyo.cpp:332
yarp::os::Bottle::addString
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:173
laserHokuyo::getScanRate
bool getScanRate(double &rate) override
get the scan rate (scans per seconds)
Definition: laserHokuyo.cpp:320
laserHokuyo::getScanLimits
bool getScanLimits(double &min, double &max) override
get the scan angular range.
Definition: laserHokuyo.cpp:294
yCError
#define yCError(component,...)
Definition: LogComponent.h:157
laserHokuyo::close
bool close() override
Close the DeviceDriver.
Definition: laserHokuyo.cpp:259
yCInfo
#define yCInfo(component,...)
Definition: LogComponent.h:135
yCDebug
#define yCDebug(component,...)
Definition: LogComponent.h:112
laserHokuyo::getDeviceStatus
bool getDeviceStatus(Device_status &status) override
get the device status
Definition: laserHokuyo.cpp:376
yarp::dev::IRangefinder2D::DEVICE_OK_IN_USE
@ DEVICE_OK_IN_USE
Definition: IRangefinder2D.h:43
laserHokuyo::setHorizontalResolution
bool setHorizontalResolution(double step) override
get the angular step between two measurments (if available)
Definition: laserHokuyo.cpp:314
laserHokuyo::getHorizontalResolution
bool getHorizontalResolution(double &step) override
get the angular step between two measurments.
Definition: laserHokuyo.cpp:308
Time.h
yCTrace
#define yCTrace(component,...)
Definition: LogComponent.h:88
yarp::os::Value
A single value (typically within a Bottle).
Definition: Value.h:47
laserHokuyo::open
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
Definition: laserHokuyo.cpp:43
yarp::os::Value::asFloat64
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:225
laserHokuyo::getDeviceInfo
bool getDeviceInfo(std::string &device_info) override
get the device hardware charactestics
Definition: laserHokuyo.cpp:578
yarp::sig::VectorOf::push_back
void push_back(const T &elem)
Push a new element in the vector: size is changed.
Definition: Vector.h:282
yarp::os::Property
A class for storing options and configuration information.
Definition: Property.h:37