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
25namespace {
26YARP_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
282bool 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
290bool laserHokuyo::setDistanceRange(double min, double max)
291{
292 yCError(LASERHOKUYO, "setDistanceRange NOT YET IMPLEMENTED");
293 return false;
294}
295
296bool laserHokuyo::getScanLimits(double& min, double& max)
297{
298 //degrees
299 min = min_angle;
300 max = max_angle;
301 return true;
302}
303
304bool 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
322bool laserHokuyo::getScanRate(double& rate)
323{
324 rate = 20; //20 Hz = 50 ms
325 return true;
326}
327
329{
330 yCError(LASERHOKUYO, "setScanRate NOT YET IMPLEMENTED");
331 return false;
332}
333
334bool laserHokuyo::getRawData(yarp::sig::Vector &out, double* timestamp)
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
349bool laserHokuyo::getLaserMeasurement(std::vector<LaserMeasurementData> &data, double* timestamp)
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
393inline 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
407inline 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
419int 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 {
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;
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
582bool 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
bool getRawData(yarp::sig::Vector &data, double *timestamp) override
Get the device measurements.
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
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 measurements (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 getLaserMeasurement(std::vector< LaserMeasurementData > &data, double *timestamp) override
Get the device measurements.
bool setDistanceRange(double min, double max) override
set the device detection range.
bool getDeviceInfo(std::string &device_info) override
get the device hardware characteristics
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 measurements.
bool view(T *&x)
Get an interface to the device driver.
Definition: DeviceDriver.h:88
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:64
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:33
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:63
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 Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
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:43
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:357
void resize(size_t size) override
Resize the vector.
Definition: Vector.h:220
size_t size() const
Definition: Vector.h:321
void push_back(const T &elem)
Push a new element in the vector: size is changed.
Definition: Vector.h:248
#define DEG2RAD
Definition: laserHokuyo.cpp:21
#define yCInfo(component,...)
Definition: LogComponent.h:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCTrace(component,...)
Definition: LogComponent.h:84
#define yCDebug(component,...)
Definition: LogComponent.h:128
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:76