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>
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
31using namespace rp::standalone::rplidar;
32
33YARP_LOG_COMPONENT(RP2_LIDAR, "yarp.devices.rpLidar2")
34
35//-------------------------------------------------------------------------------------
36
37bool 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 PeriodicThread::stop();
162
163 m_drv->stopMotor();
164 RPlidarDriver::DisposeDriver(m_drv);
165 yCInfo(RP2_LIDAR) << "rpLidar closed";
166 return true;
167}
168
169bool RpLidar2::setDistanceRange(double min, double max)
170{
171 std::lock_guard<std::mutex> guard(m_mutex);
172 m_min_distance = min;
173 m_max_distance = max;
174 return true;
175}
176
177bool RpLidar2::setScanLimits(double min, double max)
178{
179 std::lock_guard<std::mutex> guard(m_mutex);
180 m_min_angle = min;
181 m_max_angle = max;
182 return true;
183}
184
186{
187 std::lock_guard<std::mutex> guard(m_mutex);
189 return true;
190}
191
192bool RpLidar2::setScanRate(double rate)
193{
194 std::lock_guard<std::mutex> guard(m_mutex);
195 yCWarning(RP2_LIDAR, "setScanRate not yet implemented");
196 return false;
197}
198
200{
201 return true;
202}
203
204//#define DEBUG_LOCKING 1
205#ifndef _countof
206#define _countof(_Array) (int)(sizeof(_Array) / sizeof (_Array[0]))
207#endif
208
210{
212 m_mutex.unlock();
213 return;
214}
215
217{
218 u_result op_result;
219 rplidar_response_measurement_node_t nodes[2048];
220 size_t count = _countof(nodes);
221 static int life = 0;
222 op_result = m_drv->grabScanData(nodes, count);
223 if (op_result != RESULT_OK)
224 {
225 yCError(RP2_LIDAR) << m_serialPort << ": grabbing scan data failed";
226 handleError(op_result);
227 return false;
228 }
229
230 float frequency = 0;
231 bool is4kmode = false;
232 op_result = m_drv->getFrequency(m_inExpressMode, count, frequency, is4kmode);
233 if (op_result != RESULT_OK)
234 {
235 yCError(RP2_LIDAR) << "getFrequency failed";
236 return false;
237 }
238
239 m_drv->ascendScanData(nodes, count);
240
241 if (op_result != RESULT_OK)
242 {
243 yCError(RP2_LIDAR) << "ascending scan data failed\n";
244 handleError(op_result);
245 return false;
246 }
247
248 if (m_buffer_life && life % m_buffer_life == 0)
249 {
250 for (size_t i = 0; i < m_laser_data.size(); i++)
251 {
252 //m_laser_data[i]=0; //0 is a terribly unsafe value and should be avoided.
253 m_laser_data[i] = std::numeric_limits<double>::infinity();
254 }
255 }
256
257 //this lock protects m_laser_data. It is released at the end of the run(),
258 //after that the following methods are called: applyLimitsOnLaserData(), updateTimestamp()
259 m_mutex.lock();
260
261 for (size_t i = 0; i < count; ++i)
262 {
263
264 double distance = nodes[i].distance_q2 / 4.0f / 1000.0; //m
265 double angle = (float)((nodes[i].angle_q6_checkbit >> RPLIDAR_RESP_MEASUREMENT_ANGLE_SHIFT) / 64.0f); //deg
266 double quality = nodes[i].sync_quality >> RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT;
267 angle = (360 - angle);
268
269 if (angle >= 360)
270 {
271 angle -= 360;
272 }
273
274 //checkme, is it useful?
275 if (angle > 360)
276 {
277 yCWarning(RP2_LIDAR) << "Invalid angle";
278 }
279
280 if (quality == 0 || distance == 0)
281 {
282 distance = std::numeric_limits<double>::infinity();
283 }
284
285 int elem = (int)(angle / m_resolution);
286 if (elem >= 0 && elem < (int)m_laser_data.size())
287 {
288 m_laser_data[elem] = distance;
289 }
290 else
291 {
292 yCDebug(RP2_LIDAR) << "RpLidar::run() invalid angle: elem" << elem << ">" << "laser_data.size()" << m_laser_data.size();
293 }
294 }
295
296 life++;
297 return true;
298}
299
301{
302#ifdef LASER_DEBUG
303 yCDebug(RP2_LIDAR,"RpLidar Thread releasing...");
304 yCDebug(RP2_LIDAR,"... done.");
305#endif
306
307 return;
308}
309
310void RpLidar2::handleError(u_result error)
311{
312 switch (error)
313 {
314 case RESULT_FAIL_BIT:
315 yCError(RP2_LIDAR) << "error: 'FAIL BIT'";
316 break;
317 case RESULT_ALREADY_DONE:
318 yCError(RP2_LIDAR) << "error: 'ALREADY DONE'";
319 break;
320 case RESULT_INVALID_DATA:
321 yCError(RP2_LIDAR) << "error: 'INVALID DATA'";
322 break;
323 case RESULT_OPERATION_FAIL:
324 yCError(RP2_LIDAR) << "error: 'OPERATION FAIL'";
325 break;
326 case RESULT_OPERATION_TIMEOUT:
327 yCError(RP2_LIDAR) << "error: 'OPERATION TIMEOUT'";
328 break;
329 case RESULT_OPERATION_STOP:
330 yCError(RP2_LIDAR) << "error: 'OPERATION STOP'";
331 break;
332 case RESULT_OPERATION_NOT_SUPPORT:
333 yCError(RP2_LIDAR) << "error: 'OPERATION NOT SUPPORT'";
334 break;
335 case RESULT_FORMAT_NOT_SUPPORT:
336 yCError(RP2_LIDAR) << "error: 'FORMAT NOT SUPPORT'";
337 break;
338 case RESULT_INSUFFICIENT_MEMORY:
339 yCError(RP2_LIDAR) << "error: 'INSUFFICENT MEMORY'";
340 break;
341 }
342}
343
344std::string RpLidar2::deviceinfo()
345{
346 if (m_drv)
347 {
348 u_result result;
349 rplidar_response_device_info_t info;
350 std::string serialNumber;
351
352 result = m_drv->getDeviceInfo(info);
353 if (result != RESULT_OK)
354 {
355 handleError(result);
356 return {};
357 }
358
359 for (unsigned char i : info.serialnum)
360 {
361 serialNumber += std::to_string(i);
362 }
363
364 return "Firmware Version: " + std::to_string(info.firmware_version) +
365 "\nHardware Version: " + std::to_string(info.hardware_version) +
366 "\nModel: " + std::to_string(info.model) +
367 "\nSerial Number:" + serialNumber;
368 }
369 return {};
370}
rpLidar2: The device driver for the RP2 lidar
Definition: rpLidar2.h:47
rplidardrv * m_drv
Definition: rpLidar2.h:57
int m_buffer_life
Definition: rpLidar2.h:53
bool setScanRate(double rate) override
set the scan rate (scans per seconds)
Definition: rpLidar2.cpp:192
bool setScanLimits(double min, double max) override
set the scan angular range.
Definition: rpLidar2.cpp:177
bool close() override
Close the DeviceDriver.
Definition: rpLidar2.cpp:159
bool threadInit() override
Initialization method.
Definition: rpLidar2.cpp:199
bool m_inExpressMode
Definition: rpLidar2.h:54
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:216
bool setDistanceRange(double min, double max) override
set the device detection range.
Definition: rpLidar2.cpp:169
bool setHorizontalResolution(double step) override
get the angular step between two measurements (if available)
Definition: rpLidar2.cpp:185
std::string m_serialPort
Definition: rpLidar2.h:56
void threadRelease() override
Release method.
Definition: rpLidar2.cpp:300
void run() override
Loop function.
Definition: rpLidar2.cpp:209
virtual bool updateLidarData()
This utility method calls in sequence: grabDataFromHW(), updateTimestamp and applyLimitsOnLaserData()...
void step()
Call this to "step" the thread rather than starting it.
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 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.
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
size_t size() const
Definition: Vector.h:321
#define yCInfo(component,...)
Definition: LogComponent.h:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCWarning(component,...)
Definition: LogComponent.h:192
#define yCDebug(component,...)
Definition: LogComponent.h:128
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:76
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:206
const yarp::os::LogComponent & RP2_LIDAR()
Definition: rpLidar2.cpp:33