YARP
Yet Another Robot Platform
laserFromExternalPort.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #define _USE_MATH_DEFINES
20 
21 #include "laserFromExternalPort.h"
22 
23 #include <yarp/os/Time.h>
24 #include <yarp/os/Log.h>
25 #include <yarp/os/LogStream.h>
26 #include <yarp/os/ResourceFinder.h>
27 #include <yarp/math/Math.h>
28 
29 #include <cmath>
30 #include <cstring>
31 #include <cstdlib>
32 #include <iostream>
33 #include <limits>
34 #include <mutex>
35 
36 using namespace std;
37 using namespace yarp::os;
38 using namespace yarp::dev;
39 
40 #ifndef DEG2RAD
41 #define DEG2RAD M_PI/180.0
42 #endif
43 
44 #ifndef RAD2DEG
45 #define RAD2DEG 180/M_PI
46 #endif
47 
48 YARP_LOG_COMPONENT(LASER_FROM_EXTERNAL_PORT, "yarp.devices.laserFromExternalPort")
49 
50 /*
51 yarpdev --device Rangefinder2DWrapper --subdevice laserFromExternalPort \
52 --SENSOR::input_ports_name "(/port1 /port2)" \
53 --TRANSFORM_CLIENT::local /LaserFromExternalPort/tfClient \
54 --TRANSFORM_CLIENT::remote /transformServer \
55 --TRANSFORMS::src_frames "(/frame1 /frame2)" \
56 --TRANSFORMS::dst_frame /output_frame
57 --period 10 \
58 --name /outlaser:o
59 
60 yarpdev --device Rangefinder2DWrapper --subdevice laserFromExternalPort \
61 --SENSOR::min_angle 0
62 --SENSOR::max_angle 360
63 --SENSOR::resolution 0.5
64 --SENSOR::input_ports_name "(/port1 /port2)" \
65 --TRANSFORM_CLIENT::local /LaserFromExternalPort/tfClient \
66 --TRANSFORM_CLIENT::remote /transformServer \
67 --TRANSFORMS::src_frames "(/frame1 /frame2)" \
68 --TRANSFORMS::dst_frame /output_frame
69 --period 10 \
70 --name /outlaser:o
71 */
72 
73 double constrainAngle(double x)
74 {
75  x = fmod(x, 360);
76  if (x < 0)
77  x += 360;
78  return x;
79 }
80 
82 {
83  m_contains_data=false;
84 }
85 
87 {
88  m_port_mutex.lock();
89  m_lastScan = b;
90  getEnvelope(m_lastStamp);
91  m_contains_data=true;
92  m_port_mutex.unlock();
93 }
94 
96 {
97  //this blocks untils the first data is received;
98  size_t counter =0;
99  while (m_contains_data==false)
100  {
102  if (counter++ > 100) {yCDebug(LASER_FROM_EXTERNAL_PORT) << "Waiting for incoming data..."; counter=0;}
103  }
104 
105  m_port_mutex.lock();
106  data = m_lastScan;
107  stmp = m_lastStamp;
108  m_port_mutex.unlock();
109 }
110 
111 //-------------------------------------------------------------------------------------
112 
114 {
115  Property subConfig;
116  m_info = "LaserFromExternalPort device";
117 
118 #ifdef LASER_DEBUG
119  yCDebug(LASER_FROM_EXTERNAL_PORT) << "%s\n", config.toString().c_str());
120 #endif
121 
122  if (this->parseConfiguration(config) == false)
123  {
124  yCError(LASER_FROM_EXTERNAL_PORT) << "Error parsing parameters";
125  return false;
126  }
127 
128  yarp::os::Searchable& general_config = config.findGroup("SENSOR");
129 
130  if (general_config.check("input_ports_name")) //this parameter is optional
131  {
132  yarp::os::Bottle* portlist = general_config.find("input_ports_name").asList();
133  if (portlist)
134  {
135  for (size_t i = 0; i < portlist->size(); i++)
136  m_port_names.push_back(portlist->get(i).asString());
137  }
138  else
139  {
140  m_port_names.push_back("/laserFromExternalPort:i");
141  }
142 
143  for (size_t i = 0; i < m_port_names.size(); i++)
144  {
145  InputPortProcessor proc;
146  m_input_ports.push_back(proc);
147  }
148  m_last_stamp.resize(m_port_names.size());
149  m_last_scan_data.resize(m_port_names.size());
150  }
151 
152  if (general_config.check("base_type")) //this parameter is optional
153  {
154  string bt = general_config.find("base_type").asString();
155  if (bt=="infinity") { m_base_type = base_enum::BASE_IS_INF; }
156  else if (bt=="nan") { m_base_type = base_enum::BASE_IS_NAN; }
157  else if (bt=="zero") {m_base_type = base_enum::BASE_IS_ZERO;}
158  else { yCError(LASER_FROM_EXTERNAL_PORT) << "Invalid value of param base_type"; return false;
159  }
160  }
161 
162  //set the base value
163  m_empty_laser_data = m_laser_data;
164  if (m_base_type == base_enum::BASE_IS_INF)
165  {
166  for (size_t i = 0; i < m_empty_laser_data.size(); i++) m_empty_laser_data[i] = std::numeric_limits<double>::infinity();
167  }
168  else if (m_base_type == base_enum::BASE_IS_NAN)
169  {
170  for (size_t i = 0; i < m_empty_laser_data.size(); i++) m_empty_laser_data[i] = std::nanf("");
171  }
172  else if (m_base_type == base_enum::BASE_IS_ZERO)
173  {
174  for (size_t i = 0; i < m_empty_laser_data.size(); i++) m_empty_laser_data[i] = 0;
175  }
176  else
177  {
178  yCError(LASER_FROM_EXTERNAL_PORT) << "Invalid m_base_type";
179  return false;
180  }
181 
182  if (general_config.check("override")) //this parameter is optional
183  {
184  if (m_input_ports.size() != 1)
185  {
186  yCError(LASER_FROM_EXTERNAL_PORT) << "option override cannot be used when multiple ports are involved";
187  return false;
188  }
189  else
190  {
191  m_option_override_limits = true;
192  }
193  }
194 
195  //open the tc client
196  if (config.check("TRANSFORMS") && config.check("TRANSFORM_CLIENT"))
197  {
198  yarp::os::Searchable& transforms_config = config.findGroup("TRANSFORMS");
199  yarp::os::Bottle* src_frames_list = transforms_config.find("src_frames").asList();
200  if (src_frames_list)
201  {
202  if (src_frames_list->size() != m_input_ports.size())
203  {
204  yCError(LASER_FROM_EXTERNAL_PORT) << "src_frames invalid number";
205  return false;
206  }
207  for (size_t i = 0; i < src_frames_list->size(); i++)
208  {
209  m_src_frame_id.push_back(src_frames_list->get(i).asString());
210  }
211  }
212  else
213  {
214  yCError(LASER_FROM_EXTERNAL_PORT) << "src_frames invalid value or param not found";
215  return false;
216  }
217  m_dst_frame_id = transforms_config.find("dst_frame").asString();
218  if (m_dst_frame_id=="")
219  {
220  yCError(LASER_FROM_EXTERNAL_PORT) << "dst_frame param not found";
221  return false;
222  }
223 
224 
225  std::string client_cfg_string = config.findGroup("TRANSFORM_CLIENT").toString();
226  if (client_cfg_string=="")
227  {
228  yCError(LASER_FROM_EXTERNAL_PORT) << "TRANSFORM_CLIENT param not found";
229  return false;
230  }
231 
232  Property tcprop;
233  tcprop.fromString(client_cfg_string);
234  tcprop.put("device", "transformClient");
235 
236  m_tc_driver.open(tcprop);
237  if (!m_tc_driver.isValid())
238  {
239  yCError(LASER_FROM_EXTERNAL_PORT) << "Error opening PolyDriver check parameters";
240  return false;
241  }
242  m_tc_driver.view(m_iTc);
243  if (!m_iTc)
244  {
245  yCError(LASER_FROM_EXTERNAL_PORT) << "Error opening iFrameTransform interface. Device not available";
246  return false;
247  }
249  }
250 
251  for (size_t i=0; i<m_input_ports.size(); i++)
252  {
253  m_input_ports[i].useCallback();
254  if (m_input_ports[i].open(m_port_names[i]) == false)
255  {
256  yCError(LASER_FROM_EXTERNAL_PORT) << "Error opening port:" << m_port_names[i];
257  return false;
258  }
259  }
260  PeriodicThread::start();
261 
262  yCInfo(LASER_FROM_EXTERNAL_PORT, "LaserFromExternalPort: Sensor ready");
263  return true;
264 }
265 
267 {
268  PeriodicThread::stop();
269 
270  for (auto it=m_input_ports.begin(); it!= m_input_ports.end(); it++)
271  {
272  it->close();
273  }
274 
275  yCInfo(LASER_FROM_EXTERNAL_PORT) << "LaserFromExternalPort closed";
276  return true;
277 }
278 
279 
280 
281 bool LaserFromExternalPort::setDistanceRange(double min, double max)
282 {
283  std::lock_guard<std::mutex> guard(m_mutex);
284  m_min_distance = min;
285  m_max_distance = max;
286  return true;
287 }
288 
289 bool LaserFromExternalPort::setScanLimits(double min, double max)
290 {
291  std::lock_guard<std::mutex> guard(m_mutex);
292  yCWarning(LASER_FROM_EXTERNAL_PORT) << "setScanLimits not yet implemented";
293  return true;
294 }
295 
296 
297 
299 {
300  std::lock_guard<std::mutex> guard(m_mutex);
301  yCWarning(LASER_FROM_EXTERNAL_PORT, "setHorizontalResolution not yet implemented");
302  return true;
303 }
304 
306 {
307  std::lock_guard<std::mutex> guard(m_mutex);
308  yCWarning(LASER_FROM_EXTERNAL_PORT, "setScanRate not yet implemented");
309  return false;
310 }
311 
312 
313 
315 {
316 #ifdef LASER_DEBUG
317  yCDebug(LASER_FROM_EXTERNAL_PORT) <<"LaserFromExternalPort:: thread initialising...\n");
318  yCDebug(LASER_FROM_EXTERNAL_PORT) <<"... done!\n");
319 #endif
320 
321  return true;
322 }
323 
325 {
326  yarp::sig::Vector temp(3);
327  temp = yarp::math::dcm2rpy(m);
328  double t_off_rad = temp[2];
329  double x_off = m[0][3];
330  double y_off = m[1][3];
331 
332 #ifdef DO_NOTHING_DEBUG
333  double x_off = 0;
334  double y_off = 0;
335  double t_off_deg = 0;
336  double t_off_rad = 0;
337 #endif
338 
340  double resolution = (scan_data.angle_max - scan_data.angle_min)/ scan_data.scans.size(); // deg/elem
341  for (size_t i = 0; i < scan_data.scans.size(); i++)
342  {
343  double distance = scan_data.scans[i];
344  if (distance == std::numeric_limits<double>::infinity())
345  {
346  distance = 100;
347  }
348  if (std::isnan(distance))
349  {
350  //skip nan
351  }
352  else
353  {
354  //if we received a valid value, process it and put it in the right slot
355  double angle_input_deg = (i * resolution) + scan_data.angle_min;
356  double angle_input_rad = (angle_input_deg) * DEG2RAD;
357 
358  //calculate vertical and horizontal components of input angle
359  double Ay = (sin(angle_input_rad + t_off_rad) * distance);
360  double Ax = (cos(angle_input_rad + t_off_rad) * distance);
361 
362  //calculate vertical and horizontal components of new angle with offset.
363  double By = Ay + y_off;
364  double Bx = Ax + x_off;
365 
366  double angle_output_rad = atan2(By, Bx); //the output is (-pi +pi)
367  double angle_output_deg = angle_output_rad * RAD2DEG; //the output is (-180 +180)
368  angle_output_deg = constrainAngle(angle_output_deg); //the output is (0 360(
369 
370  //check if angle is inside the min max limits of the target vector, otherwise skip it
371  if (angle_output_deg > m_max_angle) { continue; }
372  if (angle_output_deg < m_min_angle) { continue; }
373 
374  //compute the new slot
375  int new_i = lrint ((angle_output_deg - m_min_angle) / m_resolution);
376  if (static_cast<size_t>(new_i) == m_laser_data.size()) {new_i=0;}
377 
378  yCAssert(LASER_FROM_EXTERNAL_PORT, new_i >= 0);
379  yCAssert(LASER_FROM_EXTERNAL_PORT, static_cast<size_t>(new_i) < m_laser_data.size());
380 
381  //compute the distance
382  double newdistance = std::sqrt((Bx * Bx) + (By * By));
383 
384  //assignment on empty (nan) slots or in valid slots if distance is shorter
385  if (std::isnan(m_laser_data[new_i]))
386  {
387  m_laser_data[new_i] = newdistance;
388  }
389  else if (newdistance < m_laser_data[new_i])
390  {
391  m_laser_data[new_i] = newdistance;
392  }
393  }
394  }
395 }
396 
398 {
399 #ifdef DEBUG_TIMING
400  double t1 = yarp::os::Time::now();
401 #endif
402  std::lock_guard<std::mutex> guard(m_mutex);
403  m_laser_data = m_empty_laser_data;
404 
405  size_t nports= m_input_ports.size();
406  if (nports == 1) //one single port, optimes version
407  {
408  m_input_ports[0].getLast(m_last_scan_data[0], m_last_stamp[0]);
409  size_t received_scans = m_last_scan_data[0].scans.size();
410 
411  if (m_option_override_limits)
412  {
413  //this overrides user setting with parameters received from the port
414  m_sensorsNum = received_scans;
415  m_max_angle = m_last_scan_data[0].angle_max;
416  m_min_angle = m_last_scan_data[0].angle_min;
417  m_max_distance = m_last_scan_data[0].range_max;
418  m_min_distance = m_last_scan_data[0].range_min;
419  m_resolution = received_scans / (m_max_angle - m_min_angle);
420  if (m_laser_data.size() != m_sensorsNum) m_laser_data.resize(m_sensorsNum);
421  }
422 
423  if (m_iTc==nullptr)
424  {
425  for (size_t elem = 0; elem < m_sensorsNum; elem++)
426  {
427  m_laser_data[elem] = m_last_scan_data[0].scans[elem]; //m
428  }
429  }
430  else
431  {
432  yarp::sig::Matrix m(4, 4); m.eye();
433  bool frame_exists = m_iTc->getTransform(m_src_frame_id[0], m_dst_frame_id, m);
434  if (frame_exists == false)
435  {
436  yCWarning(LASER_FROM_EXTERNAL_PORT) << "Unable to found m matrix" << "and" << m_dst_frame_id;
437  }
438  calculate(m_last_scan_data[0], m);
439  }
440  }
441  else //multiple ports
442  {
443  for (size_t i=0; i<nports;i++)
444  {
445  yarp::sig::Matrix m(4, 4); m.eye();
446  bool frame_exists = m_iTc->getTransform(m_src_frame_id[i], m_dst_frame_id, m);
447  if (frame_exists == false)
448  {
449  yCWarning(LASER_FROM_EXTERNAL_PORT) << "Unable to found m matrix between" << "and" << m_dst_frame_id;
450  }
451  m_input_ports[i].getLast(m_last_scan_data[i], m_last_stamp[i]);
452  calculate(m_last_scan_data[i], m);
453  }
454  }
455 
456  applyLimitsOnLaserData();
457 
458  return;
459 }
460 
462 {
463 #ifdef LASER_DEBUG
464  yCDebug(LASER_FROM_EXTERNAL_PORT) <<"Thread releasing...");
465  yCDebug(LASER_FROM_EXTERNAL_PORT) <<"... done.");
466 #endif
467 
468  return;
469 }
void onRead(yarp::sig::Vector &v) override
int getLast(yarp::sig::Vector &data, yarp::os::Stamp &stmp)
bool close() override
Close the DeviceDriver.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
bool threadInit() override
Initialization method.
bool setScanRate(double rate) override
set the scan rate (scans per seconds)
void calculate(yarp::dev::LaserScan2D scan, yarp::sig::Matrix m)
bool setHorizontalResolution(double step) override
get the angular step between two measurments (if available)
bool setDistanceRange(double min, double max) override
set the device detection range.
void run() override
Loop function.
void threadRelease() override
Release method.
bool setScanLimits(double min, double max) override
set the scan angular range.
yarp::sig::Vector scans
the scan data, measured in [m].
Definition: LaserScan2D.h:50
double angle_min
first angle of the scan [deg]
Definition: LaserScan2D.h:34
double angle_max
last angle of the scan [deg]
Definition: LaserScan2D.h:38
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:73
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:254
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:249
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:214
A class for storing options and configuration information.
Definition: Property.h:37
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1046
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:998
A base class for nested structures that can be searched.
Definition: Searchable.h:69
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.
An abstraction for a time stamp and/or sequence number.
Definition: Stamp.h:25
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:243
virtual std::string asString() const
Get string value.
Definition: Value.cpp:237
A class for a Matrix.
Definition: Matrix.h:46
const Matrix & eye()
Build an identity matrix, don't resize.
Definition: Matrix.cpp:429
size_t size() const
Definition: Vector.h:355
double constrainAngle(double x)
const yarp::os::LogComponent & LASER_FROM_EXTERNAL_PORT()
#define RAD2DEG
#define DEG2RAD
@ BASE_IS_NAN
@ BASE_IS_ZERO
@ BASE_IS_INF
#define yCInfo(component,...)
Definition: LogComponent.h:135
#define yCError(component,...)
Definition: LogComponent.h:157
#define yCAssert(component, x)
Definition: LogComponent.h:172
#define yCWarning(component,...)
Definition: LogComponent.h:146
#define yCDebug(component,...)
Definition: LogComponent.h:112
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
An interface for the device drivers.
yarp::sig::Vector dcm2rpy(const yarp::sig::Matrix &R)
Converts a dcm (direction cosine matrix) rotation matrix to roll-pitch-yaw angles (defined in Math....
Definition: math.cpp:780
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:114
An interface to the operating system, including Port based communication.