hokuyo3d.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014, ATR, Atsushi Watanabe
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the copyright holder nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <boost/bind.hpp>
00031 #include <boost/chrono.hpp>
00032 #include <boost/thread.hpp>
00033 #include <boost/thread/lock_guard.hpp>
00034 #include <boost/thread/mutex.hpp>
00035 #include <boost/asio.hpp>
00036 
00037 #include <algorithm>
00038 #include <deque>
00039 #include <string>
00040 
00041 #include <ros/ros.h>
00042 #include <sensor_msgs/PointCloud.h>
00043 #include <sensor_msgs/PointCloud2.h>
00044 #include <sensor_msgs/point_cloud2_iterator.h>
00045 #include <sensor_msgs/MagneticField.h>
00046 #include <sensor_msgs/Imu.h>
00047 #include <sensor_msgs/point_cloud_conversion.h>
00048 
00049 #include <vssp.h>
00050 
00051 class Hokuyo3dNode
00052 {
00053 public:
00054   void cbPoint(
00055       const vssp::Header &header,
00056       const vssp::RangeHeader &range_header,
00057       const vssp::RangeIndex &range_index,
00058       const boost::shared_array<uint16_t> &index,
00059       const boost::shared_array<vssp::XYZI> &points,
00060       const boost::posix_time::ptime &time_read)
00061   {
00062     if (timestamp_base_ == ros::Time(0))
00063       return;
00064     // Pack scan data
00065     if (enable_pc_)
00066     {
00067       if (cloud_.points.size() == 0)
00068       {
00069         // Start packing PointCloud message
00070         cloud_.header.frame_id = frame_id_;
00071         cloud_.header.stamp = timestamp_base_ + ros::Duration(range_header.line_head_timestamp_ms * 0.001);
00072       }
00073       // Pack PointCloud message
00074       for (int i = 0; i < index[range_index.nspots]; i++)
00075       {
00076         if (points[i].r < range_min_)
00077         {
00078           continue;
00079         }
00080         geometry_msgs::Point32 point;
00081         point.x = points[i].x;
00082         point.y = points[i].y;
00083         point.z = points[i].z;
00084         cloud_.points.push_back(point);
00085         cloud_.channels[0].values.push_back(points[i].i);
00086       }
00087     }
00088     if (enable_pc2_)
00089     {
00090       if (cloud2_.data.size() == 0)
00091       {
00092         // Start packing PointCloud2 message
00093         cloud2_.header.frame_id = frame_id_;
00094         cloud2_.header.stamp = timestamp_base_ + ros::Duration(range_header.line_head_timestamp_ms * 0.001);
00095         cloud2_.row_step = 0;
00096         cloud2_.width = 0;
00097       }
00098       // Pack PointCloud2 message
00099       cloud2_.data.resize((cloud2_.width + index[range_index.nspots]) * cloud2_.point_step);
00100 
00101       float *data = reinterpret_cast<float *>(&cloud2_.data[0]);
00102       data += cloud2_.width * cloud2_.point_step / sizeof(float);
00103       for (int i = 0; i < index[range_index.nspots]; i++)
00104       {
00105         if (points[i].r < range_min_)
00106         {
00107           continue;
00108         }
00109         *(data++) = points[i].x;
00110         *(data++) = points[i].y;
00111         *(data++) = points[i].z;
00112         *(data++) = points[i].i;
00113         cloud2_.width++;
00114       }
00115       cloud2_.row_step = cloud2_.width * cloud2_.point_step;
00116     }
00117     // Publish points
00118     if ((cycle_ == CYCLE_FIELD && (range_header.field != field_ || range_header.frame != frame_)) ||
00119         (cycle_ == CYCLE_FRAME && (range_header.frame != frame_)) || (cycle_ == CYCLE_LINE))
00120     {
00121       if (enable_pc_)
00122       {
00123         if (cloud_.header.stamp < cloud_stamp_last_ && !allow_jump_back_)
00124         {
00125           ROS_INFO("Dropping timestamp jump backed cloud");
00126         }
00127         else
00128         {
00129           pub_pc_.publish(cloud_);
00130         }
00131         cloud_stamp_last_ = cloud_.header.stamp;
00132         cloud_.points.clear();
00133         cloud_.channels[0].values.clear();
00134       }
00135       if (enable_pc2_)
00136       {
00137         cloud2_.data.resize(cloud2_.width * cloud2_.point_step);
00138         if (cloud2_.header.stamp < cloud_stamp_last_ && !allow_jump_back_)
00139         {
00140           ROS_INFO("Dropping timestamp jump backed cloud2");
00141         }
00142         else
00143         {
00144           pub_pc2_.publish(cloud2_);
00145         }
00146         cloud_stamp_last_ = cloud2_.header.stamp;
00147         cloud2_.data.clear();
00148       }
00149       if (range_header.frame != frame_)
00150         ping();
00151       frame_ = range_header.frame;
00152       field_ = range_header.field;
00153       line_ = range_header.line;
00154     }
00155   }
00156   void cbError(
00157       const vssp::Header &header,
00158       const std::string &message,
00159       const boost::posix_time::ptime &time_read)
00160   {
00161     ROS_ERROR("%s", message.c_str());
00162   }
00163   void cbPing(
00164       const vssp::Header &header,
00165       const boost::posix_time::ptime &time_read)
00166   {
00167     const ros::Time now = ros::Time::fromBoost(time_read);
00168     const ros::Duration delay =
00169         ((now - time_ping_) - ros::Duration(header.send_time_ms * 0.001 - header.received_time_ms * 0.001)) * 0.5;
00170     const ros::Time base = time_ping_ + delay - ros::Duration(header.received_time_ms * 0.001);
00171 
00172     timestamp_base_buffer_.push_back(base);
00173     if (timestamp_base_buffer_.size() > 5)
00174       timestamp_base_buffer_.pop_front();
00175 
00176     auto sorted_timstamp_base = timestamp_base_buffer_;
00177     std::sort(sorted_timstamp_base.begin(), sorted_timstamp_base.end());
00178 
00179     if (timestamp_base_ == ros::Time(0))
00180       timestamp_base_ = sorted_timstamp_base[sorted_timstamp_base.size() / 2];
00181     else
00182       timestamp_base_ += (sorted_timstamp_base[sorted_timstamp_base.size() / 2] - timestamp_base_) * 0.1;
00183 
00184     ROS_DEBUG("timestamp_base: %lf", timestamp_base_.toSec());
00185   }
00186   void cbAux(
00187       const vssp::Header &header,
00188       const vssp::AuxHeader &aux_header,
00189       const boost::shared_array<vssp::Aux> &auxs,
00190       const boost::posix_time::ptime &time_read)
00191   {
00192     if (timestamp_base_ == ros::Time(0))
00193       return;
00194     ros::Time stamp = timestamp_base_ + ros::Duration(aux_header.timestamp_ms * 0.001);
00195 
00196     if ((aux_header.data_bitfield & (vssp::AX_MASK_ANGVEL | vssp::AX_MASK_LINACC)) ==
00197         (vssp::AX_MASK_ANGVEL | vssp::AX_MASK_LINACC))
00198     {
00199       imu_.header.frame_id = imu_frame_id_;
00200       imu_.header.stamp = stamp;
00201       for (int i = 0; i < aux_header.data_count; i++)
00202       {
00203         imu_.orientation_covariance[0] = -1.0;
00204         imu_.angular_velocity.x = auxs[i].ang_vel.x;
00205         imu_.angular_velocity.y = auxs[i].ang_vel.y;
00206         imu_.angular_velocity.z = auxs[i].ang_vel.z;
00207         imu_.linear_acceleration.x = auxs[i].lin_acc.x;
00208         imu_.linear_acceleration.y = auxs[i].lin_acc.y;
00209         imu_.linear_acceleration.z = auxs[i].lin_acc.z;
00210         if (imu_stamp_last_ > imu_.header.stamp && !allow_jump_back_)
00211         {
00212           ROS_INFO("Dropping timestamp jump backed imu");
00213         }
00214         else
00215         {
00216           pub_imu_.publish(imu_);
00217         }
00218         imu_stamp_last_ = imu_.header.stamp;
00219         imu_.header.stamp += ros::Duration(aux_header.data_ms * 0.001);
00220       }
00221     }
00222     if ((aux_header.data_bitfield & vssp::AX_MASK_MAG) == vssp::AX_MASK_MAG)
00223     {
00224       mag_.header.frame_id = mag_frame_id_;
00225       mag_.header.stamp = stamp;
00226       for (int i = 0; i < aux_header.data_count; i++)
00227       {
00228         mag_.magnetic_field.x = auxs[i].mag.x;
00229         mag_.magnetic_field.y = auxs[i].mag.y;
00230         mag_.magnetic_field.z = auxs[i].mag.z;
00231         if (mag_stamp_last_ > imu_.header.stamp && !allow_jump_back_)
00232         {
00233           ROS_INFO("Dropping timestamp jump backed mag");
00234         }
00235         else
00236         {
00237           pub_mag_.publish(mag_);
00238         }
00239         mag_stamp_last_ = imu_.header.stamp;
00240         mag_.header.stamp += ros::Duration(aux_header.data_ms * 0.001);
00241       }
00242     }
00243   }
00244   void cbConnect(bool success)
00245   {
00246     if (success)
00247     {
00248       ROS_INFO("Connection established");
00249       ping();
00250       if (set_auto_reset_)
00251         driver_.setAutoReset(auto_reset_);
00252       driver_.setHorizontalInterlace(horizontal_interlace_);
00253       driver_.requestHorizontalTable();
00254       driver_.setVerticalInterlace(vertical_interlace_);
00255       driver_.requestVerticalTable(vertical_interlace_);
00256       driver_.requestData(true, true);
00257       driver_.requestAuxData();
00258       driver_.receivePackets();
00259       ROS_INFO("Communication started");
00260     }
00261     else
00262     {
00263       ROS_ERROR("Connection failed");
00264     }
00265   }
00266   Hokuyo3dNode()
00267     : pnh_("~")
00268     , timestamp_base_(0)
00269     , timer_(io_, boost::posix_time::milliseconds(500))
00270   {
00271     if (pnh_.hasParam("horizontal_interlace") || !pnh_.hasParam("interlace"))
00272     {
00273       pnh_.param("horizontal_interlace", horizontal_interlace_, 4);
00274     }
00275     else if (pnh_.hasParam("interlace"))
00276     {
00277       ROS_WARN("'interlace' parameter is deprecated. Use horizontal_interlace instead.");
00278       pnh_.param("interlace", horizontal_interlace_, 4);
00279     }
00280     pnh_.param("vertical_interlace", vertical_interlace_, 1);
00281     pnh_.param("ip", ip_, std::string("192.168.0.10"));
00282     pnh_.param("port", port_, 10940);
00283     pnh_.param("frame_id", frame_id_, std::string("hokuyo3d"));
00284     pnh_.param("imu_frame_id", imu_frame_id_, frame_id_ + "_imu");
00285     pnh_.param("mag_frame_id", mag_frame_id_, frame_id_ + "_mag");
00286     pnh_.param("range_min", range_min_, 0.0);
00287     set_auto_reset_ = pnh_.hasParam("auto_reset");
00288     pnh_.param("auto_reset", auto_reset_, false);
00289 
00290     pnh_.param("allow_jump_back", allow_jump_back_, false);
00291 
00292     std::string output_cycle;
00293     pnh_.param("output_cycle", output_cycle, std::string("field"));
00294 
00295     if (output_cycle.compare("frame") == 0)
00296       cycle_ = CYCLE_FRAME;
00297     else if (output_cycle.compare("field") == 0)
00298       cycle_ = CYCLE_FIELD;
00299     else if (output_cycle.compare("line") == 0)
00300       cycle_ = CYCLE_LINE;
00301     else
00302     {
00303       ROS_ERROR("Unknown output_cycle value %s", output_cycle.c_str());
00304       ros::shutdown();
00305     }
00306 
00307     driver_.setTimeout(2.0);
00308     ROS_INFO("Connecting to %s", ip_.c_str());
00309     driver_.registerCallback(boost::bind(&Hokuyo3dNode::cbPoint, this, _1, _2, _3, _4, _5, _6));
00310     driver_.registerAuxCallback(boost::bind(&Hokuyo3dNode::cbAux, this, _1, _2, _3, _4));
00311     driver_.registerPingCallback(boost::bind(&Hokuyo3dNode::cbPing, this, _1, _2));
00312     driver_.registerErrorCallback(boost::bind(&Hokuyo3dNode::cbError, this, _1, _2, _3));
00313     field_ = 0;
00314     frame_ = 0;
00315     line_ = 0;
00316 
00317     sensor_msgs::ChannelFloat32 channel;
00318     channel.name = std::string("intensity");
00319     cloud_.channels.push_back(channel);
00320 
00321     cloud2_.height = 1;
00322     cloud2_.is_bigendian = false;
00323     cloud2_.is_dense = false;
00324     sensor_msgs::PointCloud2Modifier pc2_modifier(cloud2_);
00325     pc2_modifier.setPointCloud2Fields(4, "x", 1, sensor_msgs::PointField::FLOAT32, "y", 1,
00326                                       sensor_msgs::PointField::FLOAT32, "z", 1, sensor_msgs::PointField::FLOAT32,
00327                                       "intensity", 1, sensor_msgs::PointField::FLOAT32);
00328 
00329     pub_imu_ = pnh_.advertise<sensor_msgs::Imu>("imu", 5);
00330     pub_mag_ = pnh_.advertise<sensor_msgs::MagneticField>("mag", 5);
00331 
00332     enable_pc_ = enable_pc2_ = false;
00333     ros::SubscriberStatusCallback cb_con = boost::bind(&Hokuyo3dNode::cbSubscriber, this);
00334 
00335     boost::lock_guard<boost::mutex> lock(connect_mutex_);
00336     pub_pc_ = pnh_.advertise<sensor_msgs::PointCloud>("hokuyo_cloud", 5, cb_con, cb_con);
00337     pub_pc2_ = pnh_.advertise<sensor_msgs::PointCloud2>("hokuyo_cloud2", 5, cb_con, cb_con);
00338 
00339     // Start communication with the sensor
00340     driver_.connect(ip_.c_str(), port_, boost::bind(&Hokuyo3dNode::cbConnect, this, _1));
00341   }
00342   ~Hokuyo3dNode()
00343   {
00344     driver_.requestAuxData(false);
00345     driver_.requestData(true, false);
00346     driver_.requestData(false, false);
00347     driver_.poll();
00348     ROS_INFO("Communication stoped");
00349   }
00350   void cbSubscriber()
00351   {
00352     boost::lock_guard<boost::mutex> lock(connect_mutex_);
00353     if (pub_pc_.getNumSubscribers() > 0)
00354     {
00355       enable_pc_ = true;
00356       ROS_DEBUG("PointCloud output enabled");
00357     }
00358     else
00359     {
00360       enable_pc_ = false;
00361       ROS_DEBUG("PointCloud output disabled");
00362     }
00363     if (pub_pc2_.getNumSubscribers() > 0)
00364     {
00365       enable_pc2_ = true;
00366       ROS_DEBUG("PointCloud2 output enabled");
00367     }
00368     else
00369     {
00370       enable_pc2_ = false;
00371       ROS_DEBUG("PointCloud2 output disabled");
00372     }
00373   }
00374   bool poll()
00375   {
00376     if (driver_.poll())
00377     {
00378       return true;
00379     }
00380     ROS_INFO("Connection closed");
00381     return false;
00382   }
00383   void cbTimer(const boost::system::error_code& error)
00384   {
00385     if (error)
00386       return;
00387 
00388     if (!ros::ok())
00389     {
00390       driver_.stop();
00391     }
00392     else
00393     {
00394       timer_.expires_at(
00395           timer_.expires_at() +
00396           boost::posix_time::milliseconds(500));
00397       timer_.async_wait(boost::bind(&Hokuyo3dNode::cbTimer, this, _1));
00398     }
00399   }
00400   void spin()
00401   {
00402     timer_.async_wait(boost::bind(&Hokuyo3dNode::cbTimer, this, _1));
00403     boost::thread thread(
00404         boost::bind(&boost::asio::io_service::run, &io_));
00405 
00406     ros::AsyncSpinner spinner(1);
00407     spinner.start();
00408     driver_.spin();
00409     spinner.stop();
00410     timer_.cancel();
00411     ROS_INFO("Connection closed");
00412   }
00413   void ping()
00414   {
00415     driver_.requestPing();
00416     time_ping_ = ros::Time::now();
00417   }
00418 
00419 protected:
00420   ros::NodeHandle pnh_;
00421   ros::Publisher pub_pc_;
00422   ros::Publisher pub_pc2_;
00423   ros::Publisher pub_imu_;
00424   ros::Publisher pub_mag_;
00425   vssp::VsspDriver driver_;
00426   sensor_msgs::PointCloud cloud_;
00427   sensor_msgs::PointCloud2 cloud2_;
00428   sensor_msgs::Imu imu_;
00429   sensor_msgs::MagneticField mag_;
00430 
00431   bool enable_pc_;
00432   bool enable_pc2_;
00433   bool allow_jump_back_;
00434   boost::mutex connect_mutex_;
00435 
00436   ros::Time time_ping_;
00437   ros::Time timestamp_base_;
00438   std::deque<ros::Time> timestamp_base_buffer_;
00439   ros::Time imu_stamp_last_;
00440   ros::Time mag_stamp_last_;
00441   ros::Time cloud_stamp_last_;
00442 
00443   boost::asio::io_service io_;
00444   boost::asio::deadline_timer timer_;
00445 
00446   int field_;
00447   int frame_;
00448   int line_;
00449 
00450   enum PublishCycle
00451   {
00452     CYCLE_FIELD,
00453     CYCLE_FRAME,
00454     CYCLE_LINE
00455   };
00456   PublishCycle cycle_;
00457   std::string ip_;
00458   int port_;
00459   int horizontal_interlace_;
00460   int vertical_interlace_;
00461   double range_min_;
00462   std::string frame_id_;
00463   std::string imu_frame_id_;
00464   std::string mag_frame_id_;
00465   bool auto_reset_;
00466   bool set_auto_reset_;
00467 };
00468 
00469 int main(int argc, char **argv)
00470 {
00471   ros::init(argc, argv, "hokuyo3d");
00472   Hokuyo3dNode node;
00473 
00474   node.spin();
00475 
00476   return 1;
00477 }


hokuyo3d
Author(s): Atsushi Watanabe
autogenerated on Thu Jun 6 2019 20:08:29