laser.cpp
Go to the documentation of this file.
1 
34 #include <arpa/inet.h>
35 
36 #include <angles/angles.h>
38 
39 #include <multisense_ros/laser.h>
40 #include <multisense_ros/RawLidarData.h>
41 #include <multisense_ros/RawLidarCal.h>
42 
43 
44 using namespace crl::multisense;
45 
46 namespace { // anonymous
47 
48 const uint32_t laser_cloud_step = 16;
49 
50 tf2::Transform makeTransform(float T[4][4])
51 {
52  //
53  // Manually create the rotation matrix
54  tf2::Matrix3x3 rot{T[0][0],
55  T[0][1],
56  T[0][2],
57  T[1][0],
58  T[1][1],
59  T[1][2],
60  T[2][0],
61  T[2][1],
62  T[2][2]};
63 
64  //
65  // Maually create the translation vector
66  tf2::Vector3 trans{T[0][3], T[1][3], T[2][3]};
67 
68  return tf2::Transform{rot, trans};
69 }
70 
71 
72 
73 } // anonymous
74 
75 namespace multisense_ros {
76 
77 const float Laser::EXPECTED_RATE = 40.0;
78 
79 namespace { // anonymous
80 
81 //
82 // Shims for c-style driver callbacks
83 
84 void lCB(const lidar::Header& header,
85  void* userDataP)
86 {
87  reinterpret_cast<Laser*>(userDataP)->scanCallback(header);
88 }
89 
90 void pCB(const lidar::Header& header,
91  void* userDataP)
92 {
93  reinterpret_cast<Laser*>(userDataP)->pointCloudCallback(header);
94 }
95 
96 } // anonymous
97 
98 Laser::Laser(Channel* driver,
99  const std::string& tf_prefix):
100  driver_(driver),
101  subscribers_(0),
102  spindle_angle_(0.0),
103  previous_scan_time_(0.0)
104 
105 {
106  //
107  // Get device info
108 
109  system::DeviceInfo deviceInfo;
110 
111  Status status = driver_->getDeviceInfo(deviceInfo);
112  if (Status_Ok != status) {
113  ROS_ERROR("Laser: failed to query device info: %s",
114  Channel::statusString(status));
115  return;
116  }
117 
118  if (deviceInfo.hardwareRevision != system::DeviceInfo::HARDWARE_REV_MULTISENSE_SL) {
119  ROS_INFO("hardware does not support a laser");
120  return;
121  }
122 
123  ros::NodeHandle nh("");
124 
125  //
126  // Set frame ID
127 
128  frame_id_ = tf_prefix + "/head_hokuyo_frame";
129 
130  left_camera_optical_ = tf_prefix + "/" + "left_camera_optical_frame";
131  motor_ = tf_prefix + "/" + "motor";
132  spindle_ = tf_prefix + "/" + "spindle";
133  hokuyo_ = tf_prefix + "/" + "hokuyo_link";
134 
135  ROS_INFO("laser frame id: %s", frame_id_.c_str());
136 
137  //
138  // Stop lidar stream
139 
140  stop();
141 
142  //
143  // Query calibration from sensor
144 
146  if (Status_Ok != status)
147  ROS_WARN("could not query lidar calibration (%s), using URDF defaults",
148  Channel::statusString(status));
149  else {
150 
151  //
152  // Create two static transforms representing sensor calibration
155 
156  }
157 
158  //
159  // Create scan publisher
160 
161  scan_pub_ = nh.advertise<sensor_msgs::LaserScan>("lidar_scan", 20,
162  std::bind(&Laser::subscribe, this),
163  std::bind(&Laser::unsubscribe, this));
164 
165  //
166  // Initialize point cloud structure
167 
168  point_cloud_.is_bigendian = (htonl(1) == 1);
169  point_cloud_.is_dense = true;
170  point_cloud_.point_step = laser_cloud_step;
171  point_cloud_.height = 1;
172  point_cloud_.header.frame_id = tf_prefix + "/left_camera_optical_frame";
173 
174  point_cloud_.fields.resize(4);
175  point_cloud_.fields[0].name = "x";
176  point_cloud_.fields[0].offset = 0;
177  point_cloud_.fields[0].count = 1;
178  point_cloud_.fields[0].datatype = sensor_msgs::PointField::FLOAT32;
179  point_cloud_.fields[1].name = "y";
180  point_cloud_.fields[1].offset = 4;
181  point_cloud_.fields[1].count = 1;
182  point_cloud_.fields[1].datatype = sensor_msgs::PointField::FLOAT32;
183  point_cloud_.fields[2].name = "z";
184  point_cloud_.fields[2].offset = 8;
185  point_cloud_.fields[2].count = 1;
186  point_cloud_.fields[2].datatype = sensor_msgs::PointField::FLOAT32;
187  point_cloud_.fields[3].name = "intensity";
188  point_cloud_.fields[3].offset = 12;
189  point_cloud_.fields[3].count = 1;
190  point_cloud_.fields[3].datatype = sensor_msgs::PointField::FLOAT32;
191 
192  //
193  // Create point cloud publisher
194 
195  point_cloud_pub_ = nh.advertise<sensor_msgs::PointCloud2>("lidar_points2", 5,
196  std::bind(&Laser::subscribe, this),
197  std::bind(&Laser::unsubscribe, this));
198 
199  //
200  // Create calibration publishers
201 
202  ros::NodeHandle calibration_nh(nh, "calibration");
203  raw_lidar_cal_pub_ = calibration_nh.advertise<multisense_ros::RawLidarCal>("raw_lidar_cal", 1, true);
204  raw_lidar_data_pub_ = calibration_nh.advertise<multisense_ros::RawLidarData>("raw_lidar_data", 20,
205  std::bind(&Laser::subscribe, this),
206  std::bind(&Laser::unsubscribe, this));
207 
208  //
209  // Publish calibration
210 
211  multisense_ros::RawLidarCal ros_msg;
212 
213  const float *calP = reinterpret_cast<const float*>(&(lidar_cal_.laserToSpindle[0][0]));
214  for(uint32_t i=0; i<16; ++i)
215  ros_msg.laserToSpindle[i] = calP[i];
216 
217  calP = reinterpret_cast<const float*>(&(lidar_cal_.cameraToSpindleFixed[0][0]));
218  for(uint32_t i=0; i<16; ++i)
219  ros_msg.cameraToSpindleFixed[i] = calP[i];
220 
221  raw_lidar_cal_pub_.publish(ros_msg);
222 
223 
224  //
225  // Populate the jointstates message for publishing the laser spindle
226  // angle
227 
228  joint_states_.name.resize(1);
229  joint_states_.position.resize(1);
230  joint_states_.velocity.resize(1);
231  joint_states_.effort.resize(1);
232  joint_states_.name[0] = tf_prefix + "/motor_joint";
233  joint_states_.position[0] = 0.0;
234  joint_states_.velocity[0] = 0.0;
235  joint_states_.effort[0] = 0.0;
236 
237  //
238  // Create a joint state publisher
239 
240  joint_states_pub_ = nh.advertise<sensor_msgs::JointState>("joint_states", 1, true);
241 
242  //
243  // Publish the static transforms from our calibration.
244 
245  std::vector<geometry_msgs::TransformStamped> stamped_transforms(2);
246  stamped_transforms[0].header.stamp = ros::Time::now();
247  stamped_transforms[0].header.frame_id = left_camera_optical_;
248  stamped_transforms[0].child_frame_id = motor_;
249  stamped_transforms[0].transform = tf2::toMsg(motor_to_camera_);
250 
251  stamped_transforms[1].header.stamp = ros::Time::now();
252  stamped_transforms[1].header.frame_id = spindle_;
253  stamped_transforms[1].child_frame_id = hokuyo_;
254  stamped_transforms[1].transform = tf2::toMsg(laser_to_spindle_);
255 
256  static_tf_broadcaster_.sendTransform(stamped_transforms);
257 
258  //
259  // Create a timer routine to publish the laser transform even when nothing
260  // is subscribed to the laser topics. Publishing occurs at 1Hz
261 
263 
264  //
265  // Register callbacks, driver creates dedicated background thread for each
266 
267  driver_->addIsolatedCallback(lCB, this);
268  driver_->addIsolatedCallback(pCB, this);
269 
270 
271 }
272 
274 {
275  std::lock_guard<std::mutex> lock(sub_lock_);
276  stop();
279 }
280 
281 
283 {
284  //
285  // Get out if we have no work to do
286 
288  return;
289 
290  point_cloud_.data.resize(laser_cloud_step * header.pointCount);
291  point_cloud_.row_step = header.pointCount * laser_cloud_step;
292  point_cloud_.width = header.pointCount;
293  point_cloud_.header.stamp = ros::Time(header.timeStartSeconds,
294  1000 * header.timeStartMicroSeconds);
295  //
296  // For convenience below
297 
298  uint8_t *cloudP = reinterpret_cast<uint8_t*>(&point_cloud_.data[0]);
299  const uint32_t pointSize = 3 * sizeof(float); // x, y, z
300  const double arcRadians = 1e-6 * static_cast<double>(header.scanArc);
301  const double mirrorThetaStart = -arcRadians / 2.0;
302  const double spindleAngleStart = angles::normalize_angle(1e-6 * static_cast<double>(header.spindleAngleStart));
303  const double spindleAngleEnd = angles::normalize_angle(1e-6 * static_cast<double>(header.spindleAngleEnd));
304  const double spindleAngleRange = angles::normalize_angle(spindleAngleEnd - spindleAngleStart);
305 
306  for(uint32_t i=0; i<header.pointCount; ++i, cloudP += laser_cloud_step) {
307 
308  //
309  // Percent through the scan arc
310 
311  const double percent = static_cast<double>(i) / static_cast<double>(header.pointCount - 1);
312 
313  //
314  // The mirror angle for this point
315 
316  const double mirrorTheta = mirrorThetaStart + percent * arcRadians;
317 
318  //
319  // The rotation about the spindle
320 
321  const double spindleTheta = spindleAngleStart + percent * spindleAngleRange;
322 
323  const tf2::Transform spindle_to_motor = getSpindleTransform(spindleTheta);
324 
325  //
326  // The coordinate in left optical frame
327 
328  const double rangeMeters = 1e-3 * static_cast<double>(header.rangesP[i]); // from millimeters
329  const tf2::Vector3 pointMotor = (laser_to_spindle_ *
330  tf2::Vector3(rangeMeters * std::sin(mirrorTheta), 0.0,
331  rangeMeters * std::cos(mirrorTheta)));
332  const tf2::Vector3 pointCamera = motor_to_camera_ * (spindle_to_motor * pointMotor);
333 
334  //
335  // Copy data to point cloud structure
336 
337  const float xyz[3] = {static_cast<float>(pointCamera.getX()),
338  static_cast<float>(pointCamera.getY()),
339  static_cast<float>(pointCamera.getZ())};
340 
341  memcpy(cloudP, &(xyz[0]), pointSize);
342  float* intensityChannel = reinterpret_cast<float*>(cloudP + pointSize);
343  *intensityChannel = static_cast<float>(header.intensitiesP[i]); // in device units
344  }
345 
347 }
348 
350 {
351 
352  const ros::Time start_absolute_time = ros::Time(header.timeStartSeconds,
353  1000 * header.timeStartMicroSeconds);
354  const ros::Time end_absolute_time = ros::Time(header.timeEndSeconds,
355  1000 * header.timeEndMicroSeconds);
356  const ros::Time scan_time((end_absolute_time - start_absolute_time).toSec());
357 
358  const float angle_start = 1e-6 * static_cast<float>(header.spindleAngleStart);
359  const float angle_end = 1e-6 * static_cast<float>(header.spindleAngleEnd);
360 
361  //
362  // Initialize the previous scan time to the start time if it has not
363  // been previously set
364 
366  {
367  previous_scan_time_ = start_absolute_time;
368  }
369 
370 
371  //
372  // Compute the velocity between our last scan and the start of our current
373  // scan
374 
375  float velocity = angles::normalize_angle((angle_start - spindle_angle_)) /
376  (start_absolute_time - previous_scan_time_).toSec();
377 
378  publishSpindleTransform(angle_start, velocity, start_absolute_time);
379  spindle_angle_ = angle_start;
380 
381  //
382  // Compute the velocity for the spindle during the duration of our
383  // laser scan
384 
385  velocity = angles::normalize_angle((angle_end - angle_start)) / scan_time.toSec();
386 
387  publishSpindleTransform(angle_end, velocity, end_absolute_time);
388  spindle_angle_ = angle_end;
389  previous_scan_time_ = end_absolute_time;
390 
391  if (scan_pub_.getNumSubscribers() > 0) {
392 
393  const double arcRadians = 1e-6 * static_cast<double>(header.scanArc);
394 
395  laser_msg_.header.frame_id = frame_id_;
396  laser_msg_.header.stamp = start_absolute_time;
397  laser_msg_.scan_time = scan_time.toSec();
398  laser_msg_.time_increment = laser_msg_.scan_time / header.pointCount;
399  laser_msg_.angle_min = -arcRadians / 2.0;
400  laser_msg_.angle_max = arcRadians / 2.0;
401  laser_msg_.angle_increment = arcRadians / (header.pointCount - 1);
402  laser_msg_.range_min = 0.0;
403  laser_msg_.range_max = static_cast<double>(header.maxRange) / 1000.0;
404 
405  laser_msg_.ranges.resize(header.pointCount);
406  laser_msg_.intensities.resize(header.pointCount);
407 
408  for (size_t i=0; i<header.pointCount; i++) {
409  laser_msg_.ranges[i] = 1e-3 * static_cast<float>(header.rangesP[i]); // from millimeters
410  laser_msg_.intensities[i] = static_cast<float>(header.intensitiesP[i]); // in device units
411  }
412 
414  }
415 
417 
418  RawLidarData::Ptr ros_msg(new RawLidarData);
419 
420  ros_msg->scan_count = header.scanId;
421  ros_msg->time_start = start_absolute_time;
422  ros_msg->time_end = end_absolute_time;
423  ros_msg->angle_start = header.spindleAngleStart;
424  ros_msg->angle_end = header.spindleAngleEnd;
425 
426  ros_msg->distance.resize(header.pointCount);
427  memcpy(&(ros_msg->distance[0]),
428  header.rangesP,
429  header.pointCount * sizeof(uint32_t));
430 
431  ros_msg->intensity.resize(header.pointCount);
432  memcpy(&(ros_msg->intensity[0]),
433  header.intensitiesP,
434  header.pointCount * sizeof(uint32_t));
435 
436  raw_lidar_data_pub_.publish(ros_msg);
437  }
438 }
439 
440 void Laser::publishSpindleTransform(const float spindle_angle, const float velocity, const ros::Time& time) {
441  joint_states_.header.stamp = time;
442  joint_states_.position[0] = spindle_angle;
443  joint_states_.velocity[0] = velocity;
445 }
446 
448 
449  //
450  // Spindle angle turns about the z-axis to create a transform where it adjusts
451  // yaw
452  tf2::Matrix3x3 spindle_rot = tf2::Matrix3x3();
453  spindle_rot.setRPY(0.0, 0.0, spindle_angle);
454  tf2::Transform spindle_to_motor = tf2::Transform(spindle_rot);
455 
456  return spindle_to_motor;
457 }
458 
460  (void) event;
461 
462  //
463  // If our message time is 0 or our message time is over 1 second old
464  // we are not subscribed to a laser topic anymore. Publish the default
465  // transform
466  if ( (laser_msg_.header.stamp.is_zero() ||
467  (ros::Time::now() - laser_msg_.header.stamp >= ros::Duration(1))) &&
468  (point_cloud_.header.stamp.is_zero() ||
469  (ros::Time::now() - point_cloud_.header.stamp >= ros::Duration(1))) )
470 
471  {
473  }
474 }
475 
477 {
478  subscribers_ = 0;
479 
480  Status status = driver_->stopStreams(Source_Lidar_Scan);
481  if (Status_Ok != status)
482  ROS_ERROR("Laser: failed to stop laser stream: %s",
483  Channel::statusString(status));
484 }
485 
487 {
488  std::lock_guard<std::mutex> lock(sub_lock_);
489 
490  if (--subscribers_ > 0)
491  return;
492 
493  stop();
494 }
495 
497 {
498  std::lock_guard<std::mutex> lock(sub_lock_);
499 
500  if (0 == subscribers_++) {
501 
502  Status status = driver_->startStreams(Source_Lidar_Scan);
503  if (Status_Ok != status)
504  ROS_ERROR("Laser: failed to start laser stream: %s",
505  Channel::statusString(status));
506  }
507 }
508 }
virtual Status startStreams(DataSource mask)=0
sensor_msgs::JointState joint_states_
Definition: laser.h:120
void publish(const boost::shared_ptr< M > &message) const
std::mutex sub_lock_
Definition: laser.h:125
void defaultTfPublisher(const ros::TimerEvent &event)
Definition: laser.cpp:459
crl::multisense::lidar::Calibration lidar_cal_
Definition: laser.h:88
sensor_msgs::LaserScan laser_msg_
Definition: laser.h:118
virtual Status stopStreams(DataSource mask)=0
std::string hokuyo_
Definition: laser.h:98
virtual Status getLidarCalibration(lidar::Calibration &c)=0
std::string frame_id_
Definition: laser.h:105
ros::Publisher raw_lidar_cal_pub_
Definition: laser.h:112
#define ROS_WARN(...)
float spindle_angle_
Definition: laser.h:136
ros::Publisher raw_lidar_data_pub_
Definition: laser.h:110
void setRPY(tf2Scalar roll, tf2Scalar pitch, tf2Scalar yaw)
virtual Status removeIsolatedCallback(image::Callback callback)=0
virtual Status addIsolatedCallback(image::Callback callback, DataSource imageSourceMask, void *userDataP=NULL)=0
const IntensityType * intensitiesP
void publishSpindleTransform(const float spindle_angle, const float velocity, const ros::Time &time)
Definition: laser.cpp:440
crl::multisense::Channel * driver_
Definition: laser.h:103
#define ROS_INFO(...)
tf2_ros::StaticTransformBroadcaster static_tf_broadcaster_
Definition: laser.h:76
std::string motor_
Definition: laser.h:96
ros::Publisher joint_states_pub_
Definition: laser.h:113
sensor_msgs::PointCloud2 point_cloud_
Definition: laser.h:119
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
std::string spindle_
Definition: laser.h:97
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
def normalize_angle(angle)
ros::Publisher scan_pub_
Definition: laser.h:104
B toMsg(const A &a)
uint32_t getNumSubscribers() const
ros::Time previous_scan_time_
Definition: laser.h:142
int32_t subscribers_
Definition: laser.h:126
static Time now()
virtual Status getDeviceInfo(system::DeviceInfo &info)=0
ros::Timer timer_
Definition: laser.h:131
void scanCallback(const crl::multisense::lidar::Header &header)
Definition: laser.cpp:349
tf2::Transform getSpindleTransform(float spindle_angle)
Definition: laser.cpp:447
ros::Publisher point_cloud_pub_
Definition: laser.h:111
void sendTransform(const geometry_msgs::TransformStamped &transform)
void pointCloudCallback(const crl::multisense::lidar::Header &header)
Definition: laser.cpp:282
#define ROS_ERROR(...)
std::string left_camera_optical_
Definition: laser.h:95
tf2::Transform motor_to_camera_
Definition: laser.h:90
tf2::Transform laser_to_spindle_
Definition: laser.h:91


multisense_ros
Author(s):
autogenerated on Sun Mar 14 2021 02:34:55