/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2012-, Open Perception, Inc. * Copyright (c) 2012,2015 The MITRE Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the copyright holder(s) nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #pragma once #include "pcl/pcl_config.h" #include #include #include #include #include #include #include #include #define HDL_Grabber_toRadians(x) ((x) * M_PI / 180.0) namespace pcl { /** \brief Grabber for the Velodyne High-Definition-Laser (HDL) * \author Keven Ring * \ingroup io */ class PCL_EXPORTS HDLGrabber : public Grabber { public: /** \brief Signal used for a single sector * Represents 1 corrected packet from the HDL Velodyne */ using sig_cb_velodyne_hdl_scan_point_cloud_xyz = void (const pcl::PointCloud::ConstPtr &, float, float); /** \brief Signal used for a single sector * Represents 1 corrected packet from the HDL Velodyne. Each laser has a different RGB */ using sig_cb_velodyne_hdl_scan_point_cloud_xyzrgba = void (const pcl::PointCloud::ConstPtr &, float, float); /** \brief Signal used for a single sector * Represents 1 corrected packet from the HDL Velodyne with the returned intensity. */ using sig_cb_velodyne_hdl_scan_point_cloud_xyzi = void (const pcl::PointCloud::ConstPtr &, float, float); /** \brief Signal used for a 360 degree sweep * Represents multiple corrected packets from the HDL Velodyne * This signal is sent when the Velodyne passes angle "0" */ using sig_cb_velodyne_hdl_sweep_point_cloud_xyz = void (const pcl::PointCloud::ConstPtr &); /** \brief Signal used for a 360 degree sweep * Represents multiple corrected packets from the HDL Velodyne with the returned intensity * This signal is sent when the Velodyne passes angle "0" */ using sig_cb_velodyne_hdl_sweep_point_cloud_xyzi = void (const pcl::PointCloud::ConstPtr &); /** \brief Signal used for a 360 degree sweep * Represents multiple corrected packets from the HDL Velodyne * This signal is sent when the Velodyne passes angle "0". Each laser has a different RGB */ using sig_cb_velodyne_hdl_sweep_point_cloud_xyzrgba = void (const pcl::PointCloud::ConstPtr &); /** \brief Constructor taking an optional path to an HDL corrections file. The Grabber will listen on the default IP/port for data packets [192.168.3.255/2368] * \param[in] correctionsFile Path to a file which contains the correction parameters for the HDL. This parameter is mandatory for the HDL-64, optional for the HDL-32 * \param[in] pcapFile Path to a file which contains previously captured data packets. This parameter is optional */ HDLGrabber (const std::string& correctionsFile = "", const std::string& pcapFile = ""); /** \brief Constructor taking a specified IP/port and an optional path to an HDL corrections file. * \param[in] ipAddress IP Address that should be used to listen for HDL packets * \param[in] port UDP Port that should be used to listen for HDL packets * \param[in] correctionsFile Path to a file which contains the correction parameters for the HDL. This field is mandatory for the HDL-64, optional for the HDL-32 */ HDLGrabber (const boost::asio::ip::address& ipAddress, const std::uint16_t port, const std::string& correctionsFile = ""); /** \brief virtual Destructor inherited from the Grabber interface. It never throws. */ ~HDLGrabber () noexcept; /** \brief Starts processing the Velodyne packets, either from the network or PCAP file. */ void start () override; /** \brief Stops processing the Velodyne packets, either from the network or PCAP file */ void stop () override; /** \brief Obtains the name of this I/O Grabber * \return The name of the grabber */ std::string getName () const override; /** \brief Check if the grabber is still running. * \return TRUE if the grabber is running, FALSE otherwise */ bool isRunning () const override; /** \brief Returns the number of frames per second. */ float getFramesPerSecond () const override; /** \brief Allows one to filter packets based on the SOURCE IP address and PORT * This can be used, for instance, if multiple HDL LIDARs are on the same network */ void filterPackets (const boost::asio::ip::address& ipAddress, const std::uint16_t port = 443); /** \brief Allows one to customize the colors used by each laser. * \param[in] color RGB color to set * \param[in] laserNumber Number of laser to set color */ void setLaserColorRGB (const pcl::RGB& color, const std::uint8_t laserNumber); /** \brief Allows one to customize the colors used for each of the lasers. * \param[in] begin begin iterator of RGB color array * \param[in] end end iterator of RGB color array */ template void setLaserColorRGB (const IterT& begin, const IterT& end) { std::copy (begin, end, laser_rgb_mapping_); } /** \brief Any returns from the HDL with a distance less than this are discarded. * This value is in meters * Default: 0.0 */ void setMinimumDistanceThreshold (float & minThreshold); /** \brief Any returns from the HDL with a distance greater than this are discarded. * This value is in meters * Default: 10000.0 */ void setMaximumDistanceThreshold (float & maxThreshold); /** \brief Returns the current minimum distance threshold, in meters */ float getMinimumDistanceThreshold (); /** \brief Returns the current maximum distance threshold, in meters */ float getMaximumDistanceThreshold (); /** \brief Returns the maximum number of lasers */ virtual std::uint8_t getMaximumNumberOfLasers () const; protected: static const std::uint16_t HDL_DATA_PORT = 2368; static const std::uint16_t HDL_NUM_ROT_ANGLES = 36001; static const std::uint8_t HDL_LASER_PER_FIRING = 32; static const std::uint8_t HDL_MAX_NUM_LASERS = 64; static const std::uint8_t HDL_FIRING_PER_PKT = 12; enum HDLBlock { BLOCK_0_TO_31 = 0xeeff, BLOCK_32_TO_63 = 0xddff }; #pragma pack(push, 1) struct HDLLaserReturn { std::uint16_t distance; std::uint8_t intensity; }; #pragma pack(pop) struct HDLFiringData { std::uint16_t blockIdentifier; std::uint16_t rotationalPosition; HDLLaserReturn laserReturns[HDL_LASER_PER_FIRING]; }; struct HDLDataPacket { HDLFiringData firingData[HDL_FIRING_PER_PKT]; std::uint32_t gpsTimestamp; std::uint8_t mode; std::uint8_t sensorType; }; struct HDLLaserCorrection { double azimuthCorrection; double verticalCorrection; double distanceCorrection; double verticalOffsetCorrection; double horizontalOffsetCorrection; double sinVertCorrection; double cosVertCorrection; double sinVertOffsetCorrection; double cosVertOffsetCorrection; }; HDLLaserCorrection laser_corrections_[HDL_MAX_NUM_LASERS]; std::uint16_t last_azimuth_; pcl::PointCloud::Ptr current_scan_xyz_, current_sweep_xyz_; pcl::PointCloud::Ptr current_scan_xyzi_, current_sweep_xyzi_; pcl::PointCloud::Ptr current_scan_xyzrgba_, current_sweep_xyzrgba_; boost::signals2::signal* sweep_xyz_signal_; boost::signals2::signal* sweep_xyzrgba_signal_; boost::signals2::signal* sweep_xyzi_signal_; boost::signals2::signal* scan_xyz_signal_; boost::signals2::signal* scan_xyzrgba_signal_; boost::signals2::signal* scan_xyzi_signal_; void fireCurrentSweep (); void fireCurrentScan (const std::uint16_t startAngle, const std::uint16_t endAngle); void computeXYZI (pcl::PointXYZI& pointXYZI, std::uint16_t azimuth, HDLLaserReturn laserReturn, HDLLaserCorrection correction) const; private: static double *cos_lookup_table_; static double *sin_lookup_table_; pcl::SynchronizedQueue hdl_data_; boost::asio::ip::udp::endpoint udp_listener_endpoint_; boost::asio::ip::address source_address_filter_; std::uint16_t source_port_filter_; boost::asio::io_service hdl_read_socket_service_; boost::asio::ip::udp::socket *hdl_read_socket_; std::string pcap_file_name_; std::thread *queue_consumer_thread_; std::thread *hdl_read_packet_thread_; bool terminate_read_packet_thread_; pcl::RGB laser_rgb_mapping_[HDL_MAX_NUM_LASERS]; float min_distance_threshold_; float max_distance_threshold_; virtual void toPointClouds (HDLDataPacket *dataPacket); virtual boost::asio::ip::address getDefaultNetworkAddress (); void initialize (const std::string& correctionsFile = ""); void processVelodynePackets (); void enqueueHDLPacket (const std::uint8_t *data, std::size_t bytesReceived); void loadCorrectionsFile (const std::string& correctionsFile); void loadHDL32Corrections (); void readPacketsFromSocket (); #ifdef HAVE_PCAP void readPacketsFromPcap(); #endif //#ifdef HAVE_PCAP bool isAddressUnspecified (const boost::asio::ip::address& ip_address); }; }