YARP
Yet Another Robot Platform
rpLidar2.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 #include "rpLidar2.h"
7 
8 #include <yarp/os/Time.h>
9 #include <yarp/os/Log.h>
10 #include <yarp/os/LogStream.h>
11 #include <yarp/os/ResourceFinder.h>
12 
13 #include <cstdlib>
14 #include <cstring>
15 #include <iostream>
16 #include <limits>
17 #include <mutex>
18 
19 #ifndef _USE_MATH_DEFINES
20 #define _USE_MATH_DEFINES
21 #endif
22 #include <cmath>
23 
24 #ifndef DEG2RAD
25 #define DEG2RAD M_PI/180.0
26 #endif
27 
28 //#define LASER_DEBUG
29 //#define FORCE_SCAN
30 
31 using namespace rp::standalone::rplidar;
32 
33 YARP_LOG_COMPONENT(RP2_LIDAR, "yarp.devices.rpLidar2")
34 
35 //-------------------------------------------------------------------------------------
36 
37 bool RpLidar2::open(yarp::os::Searchable& config)
38 {
39  std::string serial;
40  int baudrate;
41  u_result result;
42 
43  m_device_status = DEVICE_OK_STANBY;
44  m_min_distance = 0.1; //m
45  m_max_distance = 5; //m
46  m_pwm_val = 0;
47 
48  //parse all the parameters related to the linear/angular range of the sensor
49  if (this->parseConfiguration(config) == false)
50  {
51  yCError(RP2_LIDAR) << ": error parsing parameters";
52  return false;
53  }
54 
55  bool br = config.check("GENERAL");
56  if (br != false)
57  {
58  yarp::os::Searchable& general_config = config.findGroup("GENERAL");
59  if (general_config.check("serial_port") == false) { yCError(RP2_LIDAR) << "Missing serial_port param in GENERAL group"; return false; }
60  if (general_config.check("serial_baudrate") == false) { yCError(RP2_LIDAR) << "Missing serial_baudrate param in GENERAL group"; return false; }
61  if (general_config.check("sample_buffer_life") == false) { yCError(RP2_LIDAR) << "Missing sample_buffer_life param in GENERAL group"; return false; }
62 
63  baudrate = general_config.find("serial_baudrate").asInt32();
64  m_serialPort = general_config.find("serial_port").asString();
65  m_buffer_life = general_config.find("sample_buffer_life").asInt32();
66  if (general_config.check("motor_pwm"))
67  {
68  m_pwm_val = general_config.find("motor_pwm").asInt32();
69  }
70  if (general_config.check("thread_period"))
71  {
72  double thread_period = general_config.find("thread_period").asInt32() / 1000.0;
73  this->setPeriod(thread_period);
74  }
75  }
76  else
77  {
78  yCError(RP2_LIDAR) << "Missing GENERAL section";
79  return false;
80  }
81 
82  m_drv = RPlidarDriver::CreateDriver(rp::standalone::rplidar::DRIVER_TYPE_SERIALPORT);
83  if (!m_drv)
84  {
85  yCError(RP2_LIDAR) << "Create Driver fail, exit\n";
86  return false;
87  }
88 
89  if (IS_FAIL(m_drv->connect(m_serialPort.c_str(), (_u32)baudrate)))
90  {
91  yCError(RP2_LIDAR) << "Error, cannot bind to the specified serial port:", m_serialPort.c_str();
92  RPlidarDriver::DisposeDriver(m_drv);
93  return false;
94  }
95 
96  m_info = deviceinfo();
97  yCInfo(RP2_LIDAR, "max_dist %f, min_dist %f", m_max_distance, m_min_distance);
98 
99  bool m_inExpressMode=false;
100  result = m_drv->checkExpressScanSupported(m_inExpressMode);
101  if (result == RESULT_OK && m_inExpressMode==true)
102  {
103  yCInfo(RP2_LIDAR) << "Express scan mode is supported";
104  }
105  else
106  {
107  yCWarning(RP2_LIDAR) << "Device does not supports express scan mode";
108  }
109 
110  result = m_drv->startMotor();
111  if (result != RESULT_OK)
112  {
113  handleError(result);
114  return false;
115  }
116  yCInfo(RP2_LIDAR) << "Motor started successfully";
117 
118  if (m_pwm_val!=0)
119  {
120  if (m_pwm_val>0 && m_pwm_val<1023)
121  {
122  result = m_drv->setMotorPWM(m_pwm_val);
123  if (result != RESULT_OK)
124  {
125  handleError(result);
126  return false;
127  }
128  yCInfo(RP2_LIDAR) << "Motor pwm set to "<< m_pwm_val;
129  }
130  else
131  {
132  yCError(RP2_LIDAR) << "Invalid motor pwm request " << m_pwm_val <<". It should be a value between 0 and 1023.";
133  return false;
134  }
135  }
136  else
137  {
138  yCInfo(RP2_LIDAR) << "Motor pwm set to default value (600?)";
139  }
140 
141  bool forceScan =false;
142  result = m_drv->startScan(forceScan,m_inExpressMode);
143 
144  if (result != RESULT_OK)
145  {
146  handleError(result);
147  return false;
148  }
149  yCInfo(RP2_LIDAR) << "Scan started successfully";
150 
151  yCInfo(RP2_LIDAR) << "Device info:" << m_info;
152  yCInfo(RP2_LIDAR,"max_angle %f, min_angle %f", m_max_angle, m_min_angle);
153  yCInfo(RP2_LIDAR,"resolution %f", m_resolution);
154  yCInfo(RP2_LIDAR,"sensors %zu", m_sensorsNum);
155  PeriodicThread::start();
156  return true;
157 }
158 
160 {
161  m_drv->stopMotor();
162  RPlidarDriver::DisposeDriver(m_drv);
163  PeriodicThread::stop();
164  yCInfo(RP2_LIDAR) << "rpLidar closed";
165  return true;
166 }
167 
168 bool RpLidar2::setDistanceRange(double min, double max)
169 {
170  std::lock_guard<std::mutex> guard(m_mutex);
171  m_min_distance = min;
172  m_max_distance = max;
173  return true;
174 }
175 
176 bool RpLidar2::setScanLimits(double min, double max)
177 {
178  std::lock_guard<std::mutex> guard(m_mutex);
179  m_min_angle = min;
180  m_max_angle = max;
181  return true;
182 }
183 
185 {
186  std::lock_guard<std::mutex> guard(m_mutex);
187  m_resolution = step;
188  return true;
189 }
190 
191 bool RpLidar2::setScanRate(double rate)
192 {
193  std::lock_guard<std::mutex> guard(m_mutex);
194  yCWarning(RP2_LIDAR, "setScanRate not yet implemented");
195  return false;
196 }
197 
199 {
200  return true;
201 }
202 
203 //#define DEBUG_LOCKING 1
204 #ifndef _countof
205 #define _countof(_Array) (int)(sizeof(_Array) / sizeof (_Array[0]))
206 #endif
207 
209 {
210  updateLidarData();
211  m_mutex.unlock();
212  return;
213 }
214 
216 {
217  u_result op_result;
218  rplidar_response_measurement_node_t nodes[2048];
219  size_t count = _countof(nodes);
220  static int life = 0;
221  op_result = m_drv->grabScanData(nodes, count);
222  if (op_result != RESULT_OK)
223  {
224  yCError(RP2_LIDAR) << m_serialPort << ": grabbing scan data failed";
225  handleError(op_result);
226  return false;
227  }
228 
229  float frequency = 0;
230  bool is4kmode = false;
231  op_result = m_drv->getFrequency(m_inExpressMode, count, frequency, is4kmode);
232  if (op_result != RESULT_OK)
233  {
234  yCError(RP2_LIDAR) << "getFrequency failed";
235  return false;
236  }
237 
238  m_drv->ascendScanData(nodes, count);
239 
240  if (op_result != RESULT_OK)
241  {
242  yCError(RP2_LIDAR) << "ascending scan data failed\n";
243  handleError(op_result);
244  return false;
245  }
246 
247  if (m_buffer_life && life % m_buffer_life == 0)
248  {
249  for (size_t i = 0; i < m_laser_data.size(); i++)
250  {
251  //m_laser_data[i]=0; //0 is a terribly unsafe value and should be avoided.
252  m_laser_data[i] = std::numeric_limits<double>::infinity();
253  }
254  }
255 
256  //this lock protects m_laser_data. It is released at the end of the run(),
257  //after that the following methods are called: applyLimitsOnLaserData(), updateTimestamp()
258  m_mutex.lock();
259 
260  for (size_t i = 0; i < count; ++i)
261  {
262 
263  double distance = nodes[i].distance_q2 / 4.0f / 1000.0; //m
264  double angle = (float)((nodes[i].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT) / 64.0f); //deg
265  double quality = nodes[i].sync_quality >> RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT;
266  angle = (360 - angle);
267 
268  if (angle >= 360)
269  {
270  angle -= 360;
271  }
272 
273  //checkme, is it useful?
274  if (angle > 360)
275  {
276  yCWarning(RP2_LIDAR) << "Invalid angle";
277  }
278 
279  if (quality == 0 || distance == 0)
280  {
281  distance = std::numeric_limits<double>::infinity();
282  }
283 
284  int elem = (int)(angle / m_resolution);
285  if (elem >= 0 && elem < (int)m_laser_data.size())
286  {
287  m_laser_data[elem] = distance;
288  }
289  else
290  {
291  yCDebug(RP2_LIDAR) << "RpLidar::run() invalid angle: elem" << elem << ">" << "laser_data.size()" << m_laser_data.size();
292  }
293  }
294 
295  life++;
296  return true;
297 }
298 
300 {
301 #ifdef LASER_DEBUG
302  yCDebug(RP2_LIDAR,"RpLidar Thread releasing...");
303  yCDebug(RP2_LIDAR,"... done.");
304 #endif
305 
306  return;
307 }
308 
309 void RpLidar2::handleError(u_result error)
310 {
311  switch (error)
312  {
313  case RESULT_FAIL_BIT:
314  yCError(RP2_LIDAR) << "error: 'FAIL BIT'";
315  break;
316  case RESULT_ALREADY_DONE:
317  yCError(RP2_LIDAR) << "error: 'ALREADY DONE'";
318  break;
319  case RESULT_INVALID_DATA:
320  yCError(RP2_LIDAR) << "error: 'INVALID DATA'";
321  break;
322  case RESULT_OPERATION_FAIL:
323  yCError(RP2_LIDAR) << "error: 'OPERATION FAIL'";
324  break;
325  case RESULT_OPERATION_TIMEOUT:
326  yCError(RP2_LIDAR) << "error: 'OPERATION TIMEOUT'";
327  break;
328  case RESULT_OPERATION_STOP:
329  yCError(RP2_LIDAR) << "error: 'OPERATION STOP'";
330  break;
331  case RESULT_OPERATION_NOT_SUPPORT:
332  yCError(RP2_LIDAR) << "error: 'OPERATION NOT SUPPORT'";
333  break;
334  case RESULT_FORMAT_NOT_SUPPORT:
335  yCError(RP2_LIDAR) << "error: 'FORMAT NOT SUPPORT'";
336  break;
337  case RESULT_INSUFFICIENT_MEMORY:
338  yCError(RP2_LIDAR) << "error: 'INSUFFICENT MEMORY'";
339  break;
340  }
341 }
342 
343 std::string RpLidar2::deviceinfo()
344 {
345  if (m_drv)
346  {
347  u_result result;
348  rplidar_response_device_info_t info;
349  std::string serialNumber;
350 
351  result = m_drv->getDeviceInfo(info);
352  if (result != RESULT_OK)
353  {
354  handleError(result);
355  return {};
356  }
357 
358  for (unsigned char i : info.serialnum)
359  {
360  serialNumber += std::to_string(i);
361  }
362 
363  return "Firmware Version: " + std::to_string(info.firmware_version) +
364  "\nHardware Version: " + std::to_string(info.hardware_version) +
365  "\nModel: " + std::to_string(info.model) +
366  "\nSerial Number:" + serialNumber;
367  }
368  return {};
369 }
rpLidar2: The device driver for the RP2 lidar
Definition: rpLidar2.h:47
bool setScanRate(double rate) override
set the scan rate (scans per seconds)
Definition: rpLidar2.cpp:191
bool setScanLimits(double min, double max) override
set the scan angular range.
Definition: rpLidar2.cpp:176
bool close() override
Close the DeviceDriver.
Definition: rpLidar2.cpp:159
bool threadInit() override
Initialization method.
Definition: rpLidar2.cpp:198
bool acquireDataFromHW() override final
This method should be implemented by the user, and contain the logic to grab data from the hardware.
Definition: rpLidar2.cpp:215
bool setDistanceRange(double min, double max) override
set the device detection range.
Definition: rpLidar2.cpp:168
bool setHorizontalResolution(double step) override
get the angular step between two measurments (if available)
Definition: rpLidar2.cpp:184
void threadRelease() override
Release method.
Definition: rpLidar2.cpp:299
void run() override
Loop function.
Definition: rpLidar2.cpp:208
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 Bottle & findGroup(const std::string &key) const =0
Gets a list corresponding to a given keyword.
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
#define yCInfo(component,...)
Definition: LogComponent.h:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCWarning(component,...)
Definition: LogComponent.h:143
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:77
std::string to_string(IntegerType x)
Definition: numeric.h:115
The main, catch-all namespace for YARP.
Definition: dirs.h:16
#define _countof(_Array)
Definition: rpLidar2.cpp:205
const yarp::os::LogComponent & RP2_LIDAR()
Definition: rpLidar2.cpp:33