YARP
Yet Another Robot Platform
realsense2withIMUDriver.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * All rights reserved.
4  *
5  * This software may be modified and distributed under the terms of the
6  * BSD-3-Clause license. See the accompanying LICENSE file for details.
7  */
8 
10 
11 #include <yarp/os/Value.h>
12 #include <yarp/os/LogComponent.h>
13 
14 #include <yarp/sig/ImageUtils.h>
15 
16 #include <yarp/math/Quaternion.h>
17 #include <yarp/math/Math.h>
18 
19 #include <algorithm>
20 #include <cmath>
21 #include <cstdint>
22 #include <iomanip>
23 #include <librealsense2/rsutil.h>
24 #include <librealsense2/rs.hpp>
25 #include <mutex>
26 
27  /**********************************************************************************************************/
28  // This software module is experimental.
29  // It is provided with uncomplete documentation and it may be modified/renamed/removed without any notice.
30  /**********************************************************************************************************/
31 
32 using namespace yarp::dev;
33 using namespace yarp::sig;
34 using namespace yarp::os;
35 
36 using namespace std;
37 
38 namespace {
39 YARP_LOG_COMPONENT(REALSENSE2WITHIMU, "yarp.device.realsense2withIMU")
40 }
41 
42 
43 struct float3 {
44  float x;
45  float y;
46  float z;
47 
49  {
50  return { x * t, y * t, z * t };
51  }
52 
54  {
55  return { x - t, y - t, z - t };
56  }
57 
58  void operator*=(float t)
59  {
60  x = x * t;
61  y = y * t;
62  z = z * t;
63  }
64 
65  void add(float t1, float t2, float t3)
66  {
67  x += t1;
68  y += t2;
69  z += t3;
70  }
71 };
72 
74 {
75  // theta is the angle of camera rotation in x, y and z components
76  float3 theta;
77  std::mutex theta_mtx;
78  /* alpha indicates the part that gyro and accelerometer take in computation of theta; higher alpha gives more weight to gyro, but too high
79  values cause drift; lower alpha gives more weight to accelerometer, which is more sensitive to disturbances */
80  float alpha = 0.98;
81  bool first = true;
82  // Keeps the arrival time of previous gyro frame
83  double last_ts_gyro = 0;
84 public:
85  // Function to calculate the change in angle of motion based on data from gyro
86  void process_gyro(rs2_vector gyro_data, double ts)
87  {
88  if (first) // On the first iteration, use only data from accelerometer to set the camera's initial position
89  {
90  last_ts_gyro = ts;
91  return;
92  }
93  // Holds the change in angle, as calculated from gyro
94  float3 gyro_angle;
95 
96  // Initialize gyro_angle with data from gyro
97  gyro_angle.x = gyro_data.x; // Pitch
98  gyro_angle.y = gyro_data.y; // Yaw
99  gyro_angle.z = gyro_data.z; // Roll
100 
101  // Compute the difference between arrival times of previous and current gyro frames
102  double dt_gyro = (ts - last_ts_gyro) / 1000.0;
103  last_ts_gyro = ts;
104 
105  // Change in angle equals gyro measures * time passed since last measurement
106  gyro_angle = gyro_angle * dt_gyro;
107 
108  // Apply the calculated change of angle to the current angle (theta)
109  std::lock_guard<std::mutex> lock(theta_mtx);
110  theta.add(-gyro_angle.z, -gyro_angle.y, gyro_angle.x);
111  }
112 
113  void process_accel(rs2_vector accel_data)
114  {
115  // Holds the angle as calculated from accelerometer data
116  float3 accel_angle;
117 
118  // Calculate rotation angle from accelerometer data
119  accel_angle.z = atan2(accel_data.y, accel_data.z);
120  accel_angle.x = atan2(accel_data.x, sqrt(accel_data.y * accel_data.y + accel_data.z * accel_data.z));
121  accel_angle.y = 0; //ADDED by randaz81
122 
123  // If it is the first iteration, set initial pose of camera according to accelerometer data (note the different handling for Y axis)
124  std::lock_guard<std::mutex> lock(theta_mtx);
125  if (first)
126  {
127  first = false;
128  theta = accel_angle;
129  // Since we can't infer the angle around Y axis using accelerometer data, we'll use PI as a convetion for the initial pose
130  theta.y = M_PI;
131  }
132  else
133  {
134  /*
135  Apply Complementary Filter:
136  - high-pass filter = theta * alpha: allows short-duration signals to pass through while filtering out signals
137  that are steady over time, is used to cancel out drift.
138  - low-pass filter = accel * (1- alpha): lets through long term changes, filtering out short term fluctuations
139  */
140  theta.x = theta.x * alpha + accel_angle.x * (1 - alpha);
141  theta.z = theta.z * alpha + accel_angle.z * (1 - alpha);
142  }
143  }
144 
145  // Returns the current rotation angle
147  {
148  std::lock_guard<std::mutex> lock(theta_mtx);
149  return theta;
150  }
151 };
152 
153 //---------------------------------------------------------------
154 
155 #if 0
156 static std::string get_device_information(const rs2::device& dev)
157 {
158 
159  std::stringstream ss;
160  ss << "Device information: " << std::endl;
161 
162  for (int i = 0; i < static_cast<int>(RS2_CAMERA_INFO_COUNT); i++) {
163  auto info_type = static_cast<rs2_camera_info>(i);
164  ss << " " << std::left << std::setw(20) << info_type << " : ";
165 
166  if (dev.supports(info_type))
167  ss << dev.get_info(info_type) << std::endl;
168  else
169  ss << "N/A" << std::endl;
170  }
171  return ss.str();
172 }
173 
174 static bool setOption(rs2_option option, const rs2::sensor* sensor, float value)
175 {
176 
177  if (!sensor) {
178  return false;
179  }
180 
181  // First, verify that the sensor actually supports this option
182  if (!sensor->supports(option)) {
183  yCError(REALSENSE2WITHIMU) << "The option" << rs2_option_to_string(option) << "is not supported by this sensor";
184  return false;
185  }
186 
187  // To set an option to a different value, we can call set_option with a new value
188  try {
189  sensor->set_option(option, value);
190  } catch (const rs2::error& e) {
191  // Some options can only be set while the camera is streaming,
192  // and generally the hardware might fail so it is good practice to catch exceptions from set_option
193  yCError(REALSENSE2WITHIMU) << "Failed to set option " << rs2_option_to_string(option) << ". (" << e.what() << ")";
194  return false;
195  }
196  return true;
197 }
198 
199 static bool getOption(rs2_option option, const rs2::sensor* sensor, float& value)
200 {
201  if (!sensor) {
202  return false;
203  }
204 
205  // First, verify that the sensor actually supports this option
206  if (!sensor->supports(option)) {
207  yCError(REALSENSE2WITHIMU) << "The option" << rs2_option_to_string(option) << "is not supported by this sensor";
208  return false;
209  }
210 
211  // To set an option to a different value, we can call set_option with a new value
212  try {
213  value = sensor->get_option(option);
214  } catch (const rs2::error& e) {
215  // Some options can only be set while the camera is streaming,
216  // and generally the hardware might fail so it is good practice to catch exceptions from set_option
217  yCError(REALSENSE2WITHIMU) << "Failed to get option " << rs2_option_to_string(option) << ". (" << e.what() << ")";
218  return false;
219  }
220  return true;
221 }
222 
223 static void settingErrorMsg(const string& error, bool& ret)
224 {
225  yCError(REALSENSE2WITHIMU) << error.c_str();
226  ret = false;
227 }
228 #endif
229 
232 {
233  m_rotation_estimator=nullptr;
234 }
235 
236 #if 0
238 {
239  try {
240  m_profile = m_pipeline.start(m_cfg);
241  } catch (const rs2::error& e) {
242  yCError(REALSENSE2WITHIMU) << "Failed to start the pipeline:"
243  << "(" << e.what() << ")";
244  m_lastError = e.what();
245  return false;
246  }
247  return true;
248 }
249 
251 {
252  try {
253  m_pipeline.stop();
254  } catch (const rs2::error& e) {
255  yCError(REALSENSE2WITHIMU) << "Failed to stop the pipeline:"
256  << "(" << e.what() << ")";
257  m_lastError = e.what();
258  return false;
259  }
260  return true;
261 }
262 
264 {
265  std::lock_guard<std::mutex> guard(m_mutex);
266  if (!pipelineShutdown())
267  return false;
268 
269  return pipelineStartup();
270 }
271 
272 
274 {
275  m_cfg.enable_stream(RS2_STREAM_COLOR, m_color_intrin.width, m_color_intrin.height, RS2_FORMAT_RGB8, m_fps);
276  m_cfg.enable_stream(RS2_STREAM_DEPTH, m_depth_intrin.width, m_depth_intrin.height, RS2_FORMAT_Z16, m_fps);
277  yCWarning(REALSENSE2WITHIMU) << "Format not supported, use --verbose for more details. Setting the fallback format";
278  std::cout << "COLOR: " << m_color_intrin.width << "x" << m_color_intrin.height << " fps: " << m_fps << std::endl;
279  std::cout << "DEPTH: " << m_depth_intrin.width << "x" << m_depth_intrin.height << " fps: " << m_fps << std::endl;
280 }
281 #endif
282 
284 {
285  yCWarning(REALSENSE2WITHIMU) << "This software module is experimental.";
286  yCWarning(REALSENSE2WITHIMU) << "It is provided with uncomplete documentation and it may be modified/renamed/removed without any notice.";
287 
288  string sensor_is = "d435i";
289  bool b = true;
290 
291  if (sensor_is == "d435")
292  {
293  b &= realsense2Driver::open(config);
294  //m_sensor_has_pose_capabilities = false;
296  }
297  else if (sensor_is == "d435i")
298  {
299  b &= realsense2Driver::open(config);
300  //m_sensor_has_pose_capabilities = false;
302  m_cfg.enable_stream(RS2_STREAM_ACCEL, RS2_FORMAT_MOTION_XYZ32F);
303  m_cfg.enable_stream(RS2_STREAM_GYRO, RS2_FORMAT_MOTION_XYZ32F);
304  b &= pipelineRestart();
305  }
306  /*
307  //T265 is very diffcukt to implement without major refactoring.
308  //here some infos, just for reference
309  else if(sensor_is == "t265")
310  {
311  b &= realsense2Driver::open(config);
312  m_sensor_has_pose_capabilities = true;
313  m_sensor_has_orientation_estimator = false;
314  m_cfg.enable_stream(RS2_STREAM_ACCEL, RS2_FORMAT_MOTION_XYZ32F);
315  m_cfg.enable_stream(RS2_STREAM_GYRO, RS2_FORMAT_MOTION_XYZ32F);
316  m_cfg.enable_stream(RS2_STREAM_POSE, RS2_FORMAT_6DOF);
317  b &= pipelineRestart();
318  }*/
319  else
320  {
321  yCError(REALSENSE2WITHIMU) << "Unkwnon device";
322  return false;
323  }
324 
325  return b;
326 }
327 
329 {
331  return true;
332 }
333 
334 //---------------------------------------------------------------------------------------------------------------
335 /* IThreeAxisGyroscopes methods */
337 {
338  return 1;
339 }
340 
342 {
343  if (sens_index != 0) { return yarp::dev::MAS_status::MAS_UNKNOWN; }
345 }
346 
347 bool realsense2withIMUDriver::getThreeAxisGyroscopeName(size_t sens_index, std::string& name) const
348 {
349  if (sens_index != 0) { return false; }
351  return true;
352 }
353 
354 bool realsense2withIMUDriver::getThreeAxisGyroscopeFrameName(size_t sens_index, std::string& frameName) const
355 {
356  if (sens_index != 0) { return false; }
357  frameName = m_gyroFrameName;
358  return true;
359 }
360 
361 bool realsense2withIMUDriver::getThreeAxisGyroscopeMeasure(size_t sens_index, yarp::sig::Vector& out, double& timestamp) const
362 {
363  if (sens_index != 0) {
364  return false;
365  }
366 
367  std::lock_guard<std::mutex> guard(realsense2Driver::m_mutex);
368  rs2::frameset dataframe = m_pipeline.wait_for_frames();
369  auto fg = dataframe.first(RS2_STREAM_GYRO, RS2_FORMAT_MOTION_XYZ32F);
370  rs2::motion_frame gyro = fg.as<rs2::motion_frame>();
371  m_last_gyro = gyro.get_motion_data();
372  out.resize(3);
373  out[0] = m_last_gyro.x;
374  out[1] = m_last_gyro.y;
375  out[2] = m_last_gyro.z;
376  return true;
377 }
378 
379 //-------------------------------------------------------------------------------------------------------
380 
381 /* IThreeAxisLinearAccelerometers methods */
383 {
384  return 1;
385 }
386 
388 {
389  if (sens_index != 0) { return yarp::dev::MAS_status::MAS_UNKNOWN; }
391 }
392 
393 bool realsense2withIMUDriver::getThreeAxisLinearAccelerometerName(size_t sens_index, std::string& name) const
394 {
395  if (sens_index != 0) { return false; }
397  return true;
398 }
399 
400 bool realsense2withIMUDriver::getThreeAxisLinearAccelerometerFrameName(size_t sens_index, std::string& frameName) const
401 {
402  if (sens_index != 0) { return false; }
403  frameName = m_accelFrameName;
404  return true;
405 }
406 
407 bool realsense2withIMUDriver::getThreeAxisLinearAccelerometerMeasure(size_t sens_index, yarp::sig::Vector& out, double& timestamp) const
408 {
409  std::lock_guard<std::mutex> guard(m_mutex);
410  rs2::frameset dataframe = m_pipeline.wait_for_frames();
411  auto fa = dataframe.first(RS2_STREAM_ACCEL, RS2_FORMAT_MOTION_XYZ32F);
412  rs2::motion_frame accel = fa.as<rs2::motion_frame>();
413  m_last_accel = accel.get_motion_data();
414  out.resize(3);
415  out[0] = m_last_accel.x;
416  out[1] = m_last_accel.y;
417  out[2] = m_last_accel.z;
418  return true;
419 }
420 
421 //-------------------------------------------------------------------------------------------------------
422 
423 /* IOrientationSensors methods */
425 {
426  if (m_sensor_has_orientation_estimator) { return 1; }
427  return 0;
428 }
429 
431 {
433  if (sens_index != 0) { return yarp::dev::MAS_status::MAS_UNKNOWN; }
435 }
436 
437 bool realsense2withIMUDriver::getOrientationSensorName(size_t sens_index, std::string& name) const
438 {
440  {
441  return false;
442  }
443  if (sens_index != 0) { return false; }
445  return true;
446 }
447 
448 bool realsense2withIMUDriver::getOrientationSensorFrameName(size_t sens_index, std::string& frameName) const
449 {
451  {
452  return false;
453  }
454  if (sens_index != 0) { return false; }
455  frameName = m_orientationFrameName;
456  return true;
457 }
458 
459 
460 bool realsense2withIMUDriver::getOrientationSensorMeasureAsRollPitchYaw(size_t sens_index, yarp::sig::Vector& rpy, double& timestamp) const
461 {
462  if (sens_index != 0) { return false; }
464  {
465  std::lock_guard<std::mutex> guard(m_mutex);
466  rs2::frameset dataframe = m_pipeline.wait_for_frames();
467  auto motion = dataframe.as<rs2::motion_frame>();
468  if (motion && motion.get_profile().stream_type() == RS2_STREAM_GYRO &&
469  motion.get_profile().format() == RS2_FORMAT_MOTION_XYZ32F)
470  {
471  // Get the timestamp of the current frame
472  double ts = motion.get_timestamp();
473  // Get gyro measures
474  rs2_vector gyro_data = motion.get_motion_data();
475  // Call function that computes the angle of motion based on the retrieved measures
476  m_rotation_estimator->process_gyro(gyro_data, ts);
477  }
478  // If casting succeeded and the arrived frame is from accelerometer stream
479  if (motion && motion.get_profile().stream_type() == RS2_STREAM_ACCEL &&
480  motion.get_profile().format() == RS2_FORMAT_MOTION_XYZ32F)
481  {
482  // Get accelerometer measures
483  rs2_vector accel_data = motion.get_motion_data();
484  // Call function that computes the angle of motion based on the retrieved measures
485  m_rotation_estimator->process_accel(accel_data);
486  }
488  rpy.resize(3);
489  rpy[0] = 0 + theta.x * 180.0 / M_PI; //here we can eventually adjust the sign and/or sum an offset
490  rpy[1] = 0 + theta.y * 180.0 / M_PI;
491  rpy[2] = 0 + theta.z * 180.0 / M_PI;
492  return true;
493  }
494  return false;
495 }
float t
bool ret
realsense2 : driver for realsense2 compatible devices.
rs2::pipeline m_pipeline
rs2_intrinsics m_color_intrin
rs2::pipeline_profile m_profile
rs2_intrinsics m_depth_intrin
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
std::string m_lastError
bool getThreeAxisLinearAccelerometerMeasure(size_t sens_index, yarp::sig::Vector &out, double &timestamp) const override
Get the last reading of the specified sensor.
size_t getNrOfOrientationSensors() const override
Get the number of orientation sensors exposed by this device.
size_t getNrOfThreeAxisLinearAccelerometers() const override
Get the number of three axis linear accelerometers exposed by this device.
bool getThreeAxisLinearAccelerometerName(size_t sens_index, std::string &name) const override
Get the name of the specified sensor.
bool getThreeAxisGyroscopeName(size_t sens_index, std::string &name) const override
Get the name of the specified sensor.
yarp::dev::MAS_status getThreeAxisLinearAccelerometerStatus(size_t sens_index) const override
Get the status of the specified sensor.
bool getOrientationSensorName(size_t sens_index, std::string &name) const override
Get the name of the specified sensor.
bool getThreeAxisGyroscopeFrameName(size_t sens_index, std::string &frameName) const override
Get the name of the frame of the specified sensor.
bool close() override
Close the DeviceDriver.
bool getOrientationSensorFrameName(size_t sens_index, std::string &frameName) const override
Get the name of the frame of the specified sensor.
const std::string m_accel_sensor_tag
bool getThreeAxisGyroscopeMeasure(size_t sens_index, yarp::sig::Vector &out, double &timestamp) const override
Get the last reading of the gyroscope.
bool getThreeAxisLinearAccelerometerFrameName(size_t sens_index, std::string &frameName) const override
Get the name of the frame of the specified sensor.
const std::string m_gyro_sensor_tag
rotation_estimator * m_rotation_estimator
size_t getNrOfThreeAxisGyroscopes() const override
Get the number of three axis gyroscopes exposed by this sensor.
bool getOrientationSensorMeasureAsRollPitchYaw(size_t sens_index, yarp::sig::Vector &rpy, double &timestamp) const override
Get the last reading of the orientation sensor as roll pitch yaw.
yarp::dev::MAS_status getOrientationSensorStatus(size_t sens_index) const override
Get the status of the specified sensor.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
const std::string m_orientation_sensor_tag
yarp::dev::MAS_status getThreeAxisGyroscopeStatus(size_t sens_index) const override
Get the status of the specified sensor.
void process_gyro(rs2_vector gyro_data, double ts)
void process_accel(rs2_vector accel_data)
A base class for nested structures that can be searched.
Definition: Searchable.h:69
void resize(size_t size) override
Resize the vector.
Definition: Vector.h:254
#define M_PI
#define yCError(component,...)
Definition: LogComponent.h:157
#define yCWarning(component,...)
Definition: LogComponent.h:146
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:80
An interface for the device drivers.
MAS_status
Status of a given analog sensor exposed by a multiple analog sensors interface.
@ MAS_UNKNOWN
The sensor is in an unknown state.
@ MAS_OK
The sensor is working correctly.
An interface to the operating system, including Port based communication.
Signal processing.
Definition: Image.h:25
static bool setOption(rs2_option option, const rs2::sensor *sensor, float value)
static bool getOption(rs2_option option, const rs2::sensor *sensor, float &value)
static std::string get_device_information(const rs2::device &dev)
static void settingErrorMsg(const string &error, bool &ret)
void operator*=(float t)
float3 operator-(float t)
void add(float t1, float t2, float t3)
float3 operator*(float t)