/* * SLLIDAR ROS2 NODE * * Copyright (c) 2009 - 2014 RoboPeak Team * http://www.robopeak.com * Copyright (c) 2014 - 2022 Shanghai Slamtec Co., Ltd. * http://www.slamtec.com * */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. 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. * * 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 HOLDER 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. * */ #include #include #include #include "sl_lidar.h" #include "math.h" #include #ifndef _countof #define _countof(_Array) (int)(sizeof(_Array) / sizeof(_Array[0])) #endif #define DEG2RAD(x) ((x)*M_PI/180.) #define ROS2VERSION "1.0.1" using namespace sl; bool need_exit = false; class SLlidarNode : public rclcpp::Node { public: SLlidarNode() : Node("sllidar_node") { scan_pub = this->create_publisher("scan", rclcpp::QoS(rclcpp::KeepLast(10))); } private: void init_param() { this->declare_parameter("channel_type","serial"); this->declare_parameter("tcp_ip", "192.168.0.7"); this->declare_parameter("tcp_port", 20108); this->declare_parameter("udp_ip","192.168.11.2"); this->declare_parameter("udp_port",8089); this->declare_parameter("serial_port", "/dev/ttyUSB0"); this->declare_parameter("serial_baudrate",1000000); this->declare_parameter("frame_id","laser_frame"); this->declare_parameter("inverted", false); this->declare_parameter("angle_compensate", false); this->declare_parameter("scan_mode",std::string()); this->declare_parameter("scan_frequency",10); this->get_parameter_or("channel_type", channel_type, "serial"); this->get_parameter_or("tcp_ip", tcp_ip, "192.168.0.7"); this->get_parameter_or("tcp_port", tcp_port, 20108); this->get_parameter_or("udp_ip", udp_ip, "192.168.11.2"); this->get_parameter_or("udp_port", udp_port, 8089); this->get_parameter_or("serial_port", serial_port, "/dev/ttyUSB0"); this->get_parameter_or("serial_baudrate", serial_baudrate, 1000000/*256000*/);//ros run for A1 A2, change to 256000 if A3 this->get_parameter_or("frame_id", frame_id, "laser_frame"); this->get_parameter_or("inverted", inverted, false); this->get_parameter_or("angle_compensate", angle_compensate, false); this->get_parameter_or("scan_mode", scan_mode, std::string()); if(channel_type == "udp") this->get_parameter_or("scan_frequency", scan_frequency, 20.0); else this->get_parameter_or("scan_frequency", scan_frequency, 10.0); } bool getSLLIDARDeviceInfo(ILidarDriver * drv) { sl_result op_result; sl_lidar_response_device_info_t devinfo; op_result = drv->getDeviceInfo(devinfo); if (SL_IS_FAIL(op_result)) { if (op_result == SL_RESULT_OPERATION_TIMEOUT) { RCLCPP_ERROR(this->get_logger(),"Error, operation time out. SL_RESULT_OPERATION_TIMEOUT! "); } else { RCLCPP_ERROR(this->get_logger(),"Error, unexpected error, code: %x",op_result); } return false; } // print out the device serial number, firmware and hardware version number.. char sn_str[37] = {'\0'}; for (int pos = 0; pos < 16 ;++pos) { sprintf(sn_str + (pos * 2),"%02X", devinfo.serialnum[pos]); } RCLCPP_INFO(this->get_logger(),"SLLidar S/N: %s",sn_str); RCLCPP_INFO(this->get_logger(),"Firmware Ver: %d.%02d",devinfo.firmware_version>>8, devinfo.firmware_version & 0xFF); RCLCPP_INFO(this->get_logger(),"Hardware Rev: %d",(int)devinfo.hardware_version); return true; } bool checkSLLIDARHealth(ILidarDriver * drv) { sl_result op_result; sl_lidar_response_device_health_t healthinfo; op_result = drv->getHealth(healthinfo); if (SL_IS_OK(op_result)) { RCLCPP_INFO(this->get_logger(),"SLLidar health status : %d", healthinfo.status); switch (healthinfo.status) { case SL_LIDAR_STATUS_OK: RCLCPP_INFO(this->get_logger(),"SLLidar health status : OK."); return true; case SL_LIDAR_STATUS_WARNING: RCLCPP_INFO(this->get_logger(),"SLLidar health status : Warning."); return true; case SL_LIDAR_STATUS_ERROR: RCLCPP_ERROR(this->get_logger(),"Error, SLLidar internal error detected. Please reboot the device to retry."); return false; default: RCLCPP_ERROR(this->get_logger(),"Error, Unknown internal error detected. Please reboot the device to retry."); return false; } } else { RCLCPP_ERROR(this->get_logger(),"Error, cannot retrieve SLLidar health code: %x", op_result); return false; } } bool stop_motor(const std::shared_ptr req, std::shared_ptr res) { (void)req; (void)res; if(!drv) return false; RCLCPP_DEBUG(this->get_logger(),"Stop motor"); drv->setMotorSpeed(0); return true; } bool start_motor(const std::shared_ptr req, std::shared_ptr res) { (void)req; (void)res; if(!drv) return false; if(drv->isConnected()) { RCLCPP_DEBUG(this->get_logger(),"Start motor"); sl_result ans=drv->setMotorSpeed(); if (SL_IS_FAIL(ans)) { RCLCPP_WARN(this->get_logger(), "Failed to start motor: %08x", ans); return false; } ans=drv->startScan(0,1); if (SL_IS_FAIL(ans)) { RCLCPP_WARN(this->get_logger(), "Failed to start scan: %08x", ans); } } else { RCLCPP_INFO(this->get_logger(),"lost connection"); return false; } return true; } static float getAngle(const sl_lidar_response_measurement_node_hq_t& node) { return node.angle_z_q14 * 90.f / 16384.f; } void publish_scan(rclcpp::Publisher::SharedPtr& pub, sl_lidar_response_measurement_node_hq_t *nodes, size_t node_count, rclcpp::Time start, double scan_time, bool inverted, float angle_min, float angle_max, float max_distance, std::string frame_id) { static int scan_count = 0; auto scan_msg = std::make_shared(); scan_msg->header.stamp = start; scan_msg->header.frame_id = frame_id; scan_count++; bool reversed = (angle_max > angle_min); if ( reversed ) { scan_msg->angle_min = M_PI - angle_max; scan_msg->angle_max = M_PI - angle_min; } else { scan_msg->angle_min = M_PI - angle_min; scan_msg->angle_max = M_PI - angle_max; } scan_msg->angle_increment = (scan_msg->angle_max - scan_msg->angle_min) / (double)(node_count-1); scan_msg->scan_time = scan_time; scan_msg->time_increment = scan_time / (double)(node_count-1); scan_msg->range_min = 0.05; scan_msg->range_max = max_distance;//8.0; scan_msg->intensities.resize(node_count); scan_msg->ranges.resize(node_count); bool reverse_data = (!inverted && reversed) || (inverted && !reversed); if (!reverse_data) { for (size_t i = 0; i < node_count; i++) { float read_value = (float) nodes[i].dist_mm_q2/4.0f/1000; if (read_value == 0.0) scan_msg->ranges[i] = std::numeric_limits::infinity(); else scan_msg->ranges[i] = read_value; scan_msg->intensities[i] = (float) (nodes[i].quality >> 2); } } else { for (size_t i = 0; i < node_count; i++) { float read_value = (float)nodes[i].dist_mm_q2/4.0f/1000; if (read_value == 0.0) scan_msg->ranges[node_count-1-i] = std::numeric_limits::infinity(); else scan_msg->ranges[node_count-1-i] = read_value; scan_msg->intensities[node_count-1-i] = (float) (nodes[i].quality >> 2); } } pub->publish(*scan_msg); } public: int work_loop() { init_param(); int ver_major = SL_LIDAR_SDK_VERSION_MAJOR; int ver_minor = SL_LIDAR_SDK_VERSION_MINOR; int ver_patch = SL_LIDAR_SDK_VERSION_PATCH; RCLCPP_INFO(this->get_logger(),"SLLidar running on ROS2 package SLLidar.ROS2 SDK Version:" ROS2VERSION ", SLLIDAR SDK Version:%d.%d.%d",ver_major,ver_minor,ver_patch); sl_result op_result; // create the driver instance drv = *createLidarDriver(); IChannel* _channel; if(channel_type == "tcp"){ _channel = *createTcpChannel(tcp_ip, tcp_port); } else if(channel_type == "udp"){ _channel = *createUdpChannel(udp_ip, udp_port); } else{ _channel = *createSerialPortChannel(serial_port, serial_baudrate); } if (SL_IS_FAIL((drv)->connect(_channel))) { if(channel_type == "tcp"){ RCLCPP_ERROR(this->get_logger(),"Error, cannot connect to the ip addr %s with the tcp port %s.",tcp_ip.c_str(),std::to_string(tcp_port).c_str()); } else if(channel_type == "udp"){ RCLCPP_ERROR(this->get_logger(),"Error, cannot connect to the ip addr %s with the udp port %s.",udp_ip.c_str(),std::to_string(udp_port).c_str()); } else{ RCLCPP_ERROR(this->get_logger(),"Error, cannot bind to the specified serial port %s.",serial_port.c_str()); } delete drv; return -1; } // get sllidar device info if (!getSLLIDARDeviceInfo(drv)) { return -1; } // check health... if (!checkSLLIDARHealth(drv)) { return -1; } stop_motor_service = this->create_service("stop_motor", std::bind(&SLlidarNode::stop_motor,this,std::placeholders::_1,std::placeholders::_2)); start_motor_service = this->create_service("start_motor", std::bind(&SLlidarNode::start_motor,this,std::placeholders::_1,std::placeholders::_2)); drv->setMotorSpeed(); LidarScanMode current_scan_mode; if (scan_mode.empty()) { op_result = drv->startScan(false /* not force scan */, true /* use typical scan mode */, 0, ¤t_scan_mode); } else { std::vector allSupportedScanModes; op_result = drv->getAllSupportedScanModes(allSupportedScanModes); if (SL_IS_OK(op_result)) { sl_u16 selectedScanMode = sl_u16(-1); for (std::vector::iterator iter = allSupportedScanModes.begin(); iter != allSupportedScanModes.end(); iter++) { if (iter->scan_mode == scan_mode) { selectedScanMode = iter->id; break; } } if (selectedScanMode == sl_u16(-1)) { RCLCPP_ERROR(this->get_logger(),"scan mode `%s' is not supported by lidar, supported modes:", scan_mode.c_str()); for (std::vector::iterator iter = allSupportedScanModes.begin(); iter != allSupportedScanModes.end(); iter++) { RCLCPP_ERROR(this->get_logger(),"\t%s: max_distance: %.1f m, Point number: %.1fK", iter->scan_mode, iter->max_distance, (1000/iter->us_per_sample)); } op_result = SL_RESULT_OPERATION_FAIL; } else { op_result = drv->startScanExpress(false /* not force scan */, selectedScanMode, 0, ¤t_scan_mode); } } } if(SL_IS_OK(op_result)) { //default frequent is 10 hz (by motor pwm value), current_scan_mode.us_per_sample is the number of scan point per us int points_per_circle = (int)(1000*1000/current_scan_mode.us_per_sample/scan_frequency); angle_compensate_multiple = points_per_circle/360.0 + 1; if(angle_compensate_multiple < 1) angle_compensate_multiple = 1.0; max_distance = (float)current_scan_mode.max_distance; RCLCPP_INFO(this->get_logger(),"current scan mode: %s, sample rate: %d Khz, max_distance: %.1f m, scan frequency:%.1f Hz, ", current_scan_mode.scan_mode,(int)(1000/current_scan_mode.us_per_sample+0.5),max_distance, scan_frequency); } else { RCLCPP_ERROR(this->get_logger(),"Can not start scan: %08x!", op_result); } rclcpp::Time start_scan_time; rclcpp::Time end_scan_time; double scan_duration; while (rclcpp::ok() && !need_exit) { sl_lidar_response_measurement_node_hq_t nodes[8192]; size_t count = _countof(nodes); start_scan_time = this->now(); op_result = drv->grabScanDataHq(nodes, count); end_scan_time = this->now(); scan_duration = (end_scan_time - start_scan_time).seconds(); if (op_result == SL_RESULT_OK) { op_result = drv->ascendScanData(nodes, count); float angle_min = DEG2RAD(0.0f); float angle_max = DEG2RAD(360.0f); if (op_result == SL_RESULT_OK) { if (angle_compensate) { //const int angle_compensate_multiple = 1; const int angle_compensate_nodes_count = 360*angle_compensate_multiple; int angle_compensate_offset = 0; auto angle_compensate_nodes = new sl_lidar_response_measurement_node_hq_t[angle_compensate_nodes_count]; memset(angle_compensate_nodes, 0, angle_compensate_nodes_count*sizeof(sl_lidar_response_measurement_node_hq_t)); size_t i = 0, j = 0; for( ; i < count; i++ ) { if (nodes[i].dist_mm_q2 != 0) { float angle = getAngle(nodes[i]); int angle_value = (int)(angle * angle_compensate_multiple); if ((angle_value - angle_compensate_offset) < 0) angle_compensate_offset = angle_value; for (j = 0; j < angle_compensate_multiple; j++) { int angle_compensate_nodes_index = angle_value-angle_compensate_offset + j; if(angle_compensate_nodes_index >= angle_compensate_nodes_count) angle_compensate_nodes_index = angle_compensate_nodes_count - 1; angle_compensate_nodes[angle_compensate_nodes_index] = nodes[i]; } } } publish_scan(scan_pub, angle_compensate_nodes, angle_compensate_nodes_count, start_scan_time, scan_duration, inverted, angle_min, angle_max, max_distance, frame_id); if (angle_compensate_nodes) { delete[] angle_compensate_nodes; angle_compensate_nodes = nullptr; } } else { int start_node = 0, end_node = 0; int i = 0; // find the first valid node and last valid node while (nodes[i++].dist_mm_q2 == 0); start_node = i-1; i = count -1; while (nodes[i--].dist_mm_q2 == 0); end_node = i+1; angle_min = DEG2RAD(getAngle(nodes[start_node])); angle_max = DEG2RAD(getAngle(nodes[end_node])); publish_scan(scan_pub, &nodes[start_node], end_node-start_node +1, start_scan_time, scan_duration, inverted, angle_min, angle_max, max_distance, frame_id); } } else if (op_result == SL_RESULT_OPERATION_FAIL) { // All the data is invalid, just publish them float angle_min = DEG2RAD(0.0f); float angle_max = DEG2RAD(359.0f); publish_scan(scan_pub, nodes, count, start_scan_time, scan_duration, inverted, angle_min, angle_max, max_distance, frame_id); } } rclcpp::spin_some(shared_from_this()); } // done! drv->setMotorSpeed(0); drv->stop(); RCLCPP_INFO(this->get_logger(),"Stop motor"); return 0; } private: rclcpp::Publisher::SharedPtr scan_pub; rclcpp::Service::SharedPtr start_motor_service; rclcpp::Service::SharedPtr stop_motor_service; std::string channel_type; std::string tcp_ip; std::string udp_ip; std::string serial_port; int tcp_port = 20108; int udp_port = 8089; int serial_baudrate = 115200; std::string frame_id; bool inverted = false; bool angle_compensate = true; float max_distance = 8.0; size_t angle_compensate_multiple = 1;//it stand of angle compensate at per 1 degree std::string scan_mode; float scan_frequency; ILidarDriver * drv; }; void ExitHandler(int sig) { (void)sig; need_exit = true; } int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto sllidar_node = std::make_shared(); signal(SIGINT,ExitHandler); int ret = sllidar_node->work_loop(); rclcpp::shutdown(); return ret; }