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