YARP
Yet Another Robot Platform
rpLidar3.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 "rpLidar3.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(RP_LIDAR3, "yarp.devices.RpLidar3")
34
35//-------------------------------------------------------------------------------------
36bool RpLidar3::startMotor()
37{
38 u_result result = m_drv->startMotor();
39 if (result != RESULT_OK)
40 {
41 handleError(result);
42 yCError(RP_LIDAR3) << "Unable to start motor";
43 return false;
44 }
45 yCInfo(RP_LIDAR3) << "Motor started successfully";
46
47 if (m_pwm_val > 0 && m_pwm_val < 1023)
48 {
49 result = m_drv->setMotorPWM(m_pwm_val);
50 if (result != RESULT_OK)
51 {
52 handleError(result);
53 yCError(RP_LIDAR3) << "Unable to setMotorPWM";
54 return false;
55 }
56 yCInfo(RP_LIDAR3) << "Motor pwm set to " << m_pwm_val;
57 }
58 else
59 {
60 yCError(RP_LIDAR3) << "Invalid motor pwm request " << m_pwm_val << ". It should be a value between 0 and 1023.";
61 return false;
62 }
63
64 return true;
65}
66
67bool RpLidar3::startScan()
68{
69 RplidarScanMode current_scan_mode;
70 std::vector<RplidarScanMode> scanModes;
71 u_result op_result = m_drv->getAllSupportedScanModes(scanModes);
72 yCInfo(RP_LIDAR3) << "List of Scan Modes";
73 for (size_t i = 0; i < scanModes.size(); i++)
74 {
75 yCInfo(RP_LIDAR3) << "id:" << scanModes[i].id
76 << ", mode:" << scanModes[i].scan_mode
77 << ", max distance:" << scanModes[i].max_distance
78 << ", us per sample:" << scanModes[i].us_per_sample
79 << ", samples/s:" << 1.0 / scanModes[i].us_per_sample * 1000000;
80 }
81
82 yInfo() << "Using scan mode: " << m_scan_mode;
83
84 if (IS_OK(op_result))
85 {
86 _u16 selectedScanMode = _u16(-1);
87 for (std::vector<RplidarScanMode>::iterator iter = scanModes.begin(); iter != scanModes.end(); iter++)
88 {
89 if (iter->scan_mode == m_scan_mode)
90 {
91 selectedScanMode = iter->id;
92 break;
93 }
94 }
95
96 if (selectedScanMode == _u16(-1))
97 {
98 yCInfo(RP_LIDAR3, "scan mode `%s' is not supported by lidar, supported modes:", m_scan_mode.c_str());
99 for (std::vector<RplidarScanMode>::iterator iter = scanModes.begin(); iter != scanModes.end(); iter++)
100 {
101 yCInfo(RP_LIDAR3, "\t%s: max_distance: %.1f m, Point number: %.1fK", iter->scan_mode, iter->max_distance, (1000 / iter->us_per_sample));
102 }
103 op_result = RESULT_OPERATION_FAIL;
104 }
105 else
106 {
107 op_result = m_drv->startScanExpress(false /* not force scan */, selectedScanMode, 0, &current_scan_mode);
108 }
109 }
110
111 if (op_result != RESULT_OK)
112 {
113 handleError(op_result);
114 return false;
115 }
116
117 yCInfo(RP_LIDAR3) << "Scan started successfully";
118 return true;
119}
120
122{
123 std::string serial;
124 int baudrate;
125
127
128 //parse all the parameters related to the linear/angular range of the sensor
129 if (this->parseConfiguration(config) == false)
130 {
131 yCError(RP_LIDAR3) << ": error parsing parameters";
132 return false;
133 }
134
135 bool br = config.check("GENERAL");
136 if (br != false)
137 {
138 //general options
139 {
140 yarp::os::Searchable& general_config = config.findGroup("GENERAL");
141 if (general_config.check("serial_port") == false) { yCError(RP_LIDAR3) << "Missing serial_port param in GENERAL group"; return false; }
142 if (general_config.check("serial_baudrate") == false) { yCError(RP_LIDAR3) << "Missing serial_baudrate param in GENERAL group"; return false; }
143 if (general_config.check("sample_buffer_life") == false) { yCError(RP_LIDAR3) << "Missing sample_buffer_life param in GENERAL group"; return false; }
144 if (general_config.check("thread_period"))
145 {
146 double thread_period = general_config.find("thread_period").asInt32() / 1000.0;
147 this->setPeriod(thread_period);
148 }
149
150 baudrate = general_config.find("serial_baudrate").asInt32();
151 m_serialPort = general_config.find("serial_port").asString();
152 m_buffer_life = general_config.find("sample_buffer_life").asInt32();
153 }
154
155 //options specific to the rplidar sdk
156 {
157 yarp::os::Searchable& rplidar_config = config.findGroup("RPLIDAR");
158 if (rplidar_config.check("motor_pwm"))
159 {
160 m_pwm_val = rplidar_config.find("motor_pwm").asInt32();
161 }
162 if (rplidar_config.check("express_mode"))
163 {
164 m_inExpressMode = rplidar_config.find("express_mode").asInt32();
165 }
166 if (rplidar_config.check("scan_mode"))
167 {
168 m_scan_mode = rplidar_config.find("scan_mode").asString();
169 }
170 if (rplidar_config.check("force_scan"))
171 {
172 m_force_scan = rplidar_config.find("force_scan").asInt32();
173 }
174 }
175 }
176 else
177 {
178 yCError(RP_LIDAR3) << "Missing GENERAL section";
179 return false;
180 }
181
182 m_drv = RPlidarDriver::CreateDriver(rp::standalone::rplidar::DRIVER_TYPE_SERIALPORT);
183 if (!m_drv)
184 {
185 yCError(RP_LIDAR3) << "Create Driver fail, exit\n";
186 return false;
187 }
188
189 if (IS_FAIL(m_drv->connect(m_serialPort.c_str(), (_u32)baudrate)))
190 {
191 yCError(RP_LIDAR3) << "Error, cannot bind to the specified serial port:", m_serialPort.c_str();
192 RPlidarDriver::DisposeDriver(m_drv);
193 return false;
194 }
195
196 if (!deviceinfo()) return false;
197 if (!startMotor()) return false;
198 if (!startScan()) return false;
199
200 yCInfo(RP_LIDAR3) << "Device info:" << m_info;
201 yCInfo(RP_LIDAR3,"max_dist %f, min_dist %f", m_max_distance, m_min_distance);
202 yCInfo(RP_LIDAR3,"max_angle %f, min_angle %f", m_max_angle, m_min_angle);
203 yCInfo(RP_LIDAR3,"resolution %f", m_resolution);
204 yCInfo(RP_LIDAR3,"sensors %zu", m_sensorsNum);
205 PeriodicThread::start();
206 return true;
207}
208
210{
211 yCDebug(RP_LIDAR3) << "closing..";
212 PeriodicThread::stop();
213
214 m_drv->stopMotor();
215 m_drv->stop();
216 RPlidarDriver::DisposeDriver(m_drv);
217 yCInfo(RP_LIDAR3) << "rpLidar closed";
218 return true;
219}
220
221bool RpLidar3::setDistanceRange(double min, double max)
222{
223 std::lock_guard<std::mutex> guard(m_mutex);
224 m_min_distance = min;
225 m_max_distance = max;
226 return true;
227}
228
229bool RpLidar3::setScanLimits(double min, double max)
230{
231 std::lock_guard<std::mutex> guard(m_mutex);
232 m_min_angle = min;
233 m_max_angle = max;
234 return true;
235}
236
238{
239 std::lock_guard<std::mutex> guard(m_mutex);
241 return true;
242}
243
244bool RpLidar3::setScanRate(double rate)
245{
246 std::lock_guard<std::mutex> guard(m_mutex);
247 yCWarning(RP_LIDAR3, "setScanRate not yet implemented");
248 return false;
249}
250
252{
253 return true;
254}
255
256//#define DEBUG_LOCKING 1
257#ifndef _countof
258#define _countof(_Array) (int)(sizeof(_Array) / sizeof (_Array[0]))
259#endif
260
262{
263 bool b = updateLidarData();
264 if (b)
265 {
266 m_mutex.unlock();
267 }
268 else
269 {
270 yCWarning(RP_LIDAR3, "updateLidarData() failed.");
271 }
272 return;
273}
274
276{
277 u_result op_result;
278 size_t count = m_nodes_num;
279 static int life = 0;
280 op_result = m_drv->grabScanDataHq(m_nodes, count);
281 if (op_result != RESULT_OK)
282 {
283 yCError(RP_LIDAR3) << m_serialPort << ": grabbing scan data failed";
284 handleError(op_result);
285 return false;
286 }
287
288 m_drv->ascendScanData(m_nodes, count);
289
290 if (op_result != RESULT_OK)
291 {
292 yCError(RP_LIDAR3) << "ascending scan data failed\n";
293 handleError(op_result);
294 return false;
295 }
296
297 if (m_buffer_life && life % m_buffer_life == 0)
298 {
299 for (size_t i = 0; i < m_laser_data.size(); i++)
300 {
301 //m_laser_data[i]=0; //0 is a terribly unsafe value and should be avoided.
302 m_laser_data[i] = std::numeric_limits<double>::infinity();
303 }
304 }
305
306 //this lock protects m_laser_data. It is released at the end of the run(),
307 //after that the following methods are called: applyLimitsOnLaserData(), updateTimestamp()
308 m_mutex.lock();
309
310 for (size_t i = 0; i < count; ++i)
311 {
312
313 double distance = (m_nodes[i].dist_mm_q2 / 1000.f / (1 << 2)); //m
314 double angle = (m_nodes[i].angle_z_q14 * 90.f / (1 << 14)); //deg
315 double quality = (m_nodes[i].quality >> RPLIDAR_RESP_MEASUREMENT_QUALITY_SHIFT); //to be verified
316 angle = (360 - angle);
317
318 if (angle >= 360)
319 {
320 angle -= 360;
321 }
322
323 //checkme, is it useful?
324 if (angle > 360)
325 {
326 yCWarning(RP_LIDAR3) << "Invalid angle";
327 }
328
329 if (quality == 0 || distance == 0)
330 {
331 distance = std::numeric_limits<double>::infinity();
332 }
333
334 int elem = (int)(angle / m_resolution);
335 if (elem >= 0 && elem < (int)m_laser_data.size())
336 {
337 m_laser_data[elem] = distance;
338 }
339 else
340 {
341 yCDebug(RP_LIDAR3) << "RpLidar::run() invalid angle: elem" << elem << ">" << "laser_data.size()" << m_laser_data.size();
342 }
343 }
344
345 life++;
346 return true;
347}
348
350{
351#ifdef LASER_DEBUG
352 yCDebug(RP_LIDAR3,"RpLidar Thread releasing...");
353 yCDebug(RP_LIDAR3,"... done.");
354#endif
355
356 return;
357}
358
359void RpLidar3::handleError(u_result error)
360{
361 switch (error)
362 {
363 case RESULT_FAIL_BIT:
364 yCError(RP_LIDAR3) << "error: 'FAIL BIT'";
365 break;
366 case RESULT_ALREADY_DONE:
367 yCError(RP_LIDAR3) << "error: 'ALREADY DONE'";
368 break;
369 case RESULT_INVALID_DATA:
370 yCError(RP_LIDAR3) << "error: 'INVALID DATA'";
371 break;
372 case RESULT_OPERATION_FAIL:
373 yCError(RP_LIDAR3) << "error: 'OPERATION FAIL'";
374 break;
375 case RESULT_OPERATION_TIMEOUT:
376 yCError(RP_LIDAR3) << "error: 'OPERATION TIMEOUT'";
377 break;
378 case RESULT_OPERATION_STOP:
379 yCError(RP_LIDAR3) << "error: 'OPERATION STOP'";
380 break;
381 case RESULT_OPERATION_NOT_SUPPORT:
382 yCError(RP_LIDAR3) << "error: 'OPERATION NOT SUPPORT'";
383 break;
384 case RESULT_FORMAT_NOT_SUPPORT:
385 yCError(RP_LIDAR3) << "error: 'FORMAT NOT SUPPORT'";
386 break;
387 case RESULT_INSUFFICIENT_MEMORY:
388 yCError(RP_LIDAR3) << "error: 'INSUFFICENT MEMORY'";
389 break;
390 }
391}
392
393bool RpLidar3::deviceinfo()
394{
395 if (m_drv)
396 {
397 u_result result;
398 rplidar_response_device_info_t info;
399 std::string serialNumber;
400
401 result = m_drv->getDeviceInfo(info);
402 if (result != RESULT_OK)
403 {
404 handleError(result);
405 yCError(RP_LIDAR3) << "Unable to getDeviceInfo";
406 return false;
407 }
408
409 for (unsigned char i : info.serialnum)
410 {
411 serialNumber += std::to_string(i);
412 }
413
414 m_info = std::string("Firmware Version: ") + std::to_string(info.firmware_version) +
415 "\nHardware Version: " + std::to_string(info.hardware_version) +
416 "\nModel: " + std::to_string(info.model) +
417 "\nSerial Number:" + serialNumber;
418 return true;
419 }
420 yCError(RP_LIDAR3) << "sdk driver not intialised?!";
421 return false;
422}
#define yInfo(...)
Definition: Log.h:314
rpLidar2: The device driver for the RP2 lidar
Definition: rpLidar3.h:50
const size_t m_nodes_num
Definition: rpLidar3.h:63
rplidar_response_measurement_node_hq_t * m_nodes
Definition: rpLidar3.h:64
std::string m_serialPort
Definition: rpLidar3.h:61
bool acquireDataFromHW() override final
This method should be implemented by the user, and contain the logic to grab data from the hardware.
Definition: rpLidar3.cpp:275
void run() override
Loop function.
Definition: rpLidar3.cpp:261
bool m_force_scan
Definition: rpLidar3.h:58
int m_pwm_val
Definition: rpLidar3.h:60
std::string m_scan_mode
Definition: rpLidar3.h:59
void threadRelease() override
Release method.
Definition: rpLidar3.cpp:349
bool setDistanceRange(double min, double max) override
set the device detection range.
Definition: rpLidar3.cpp:221
bool m_inExpressMode
Definition: rpLidar3.h:57
bool threadInit() override
Initialization method.
Definition: rpLidar3.cpp:251
bool setScanRate(double rate) override
set the scan rate (scans per seconds)
Definition: rpLidar3.cpp:244
bool close() override
Close the DeviceDriver.
Definition: rpLidar3.cpp:209
int m_buffer_life
Definition: rpLidar3.h:56
bool setScanLimits(double min, double max) override
set the scan angular range.
Definition: rpLidar3.cpp:229
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
Definition: rpLidar3.cpp:121
bool setHorizontalResolution(double step) override
get the angular step between two measurements (if available)
Definition: rpLidar3.cpp:237
rplidardrv * m_drv
Definition: rpLidar3.h:62
virtual bool updateLidarData()
This utility method calls in sequence: grabDataFromHW(), updateTimestamp and applyLimitsOnLaserData()...
yarp::dev::IRangefinder2D::Device_status m_device_status
bool parseConfiguration(yarp::os::Searchable &config)
bool setPeriod(double period)
Set the (new) period of the thread.
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
const yarp::os::LogComponent & RP_LIDAR3()
Definition: rpLidar3.cpp:33