├── CONTRIBUTING.md ├── .github └── workflows │ ├── ros-lint.yml │ └── ros-ci.yml ├── CHANGELOG.rst ├── package.xml ├── launch └── ld08.launch.py ├── include ├── transform.hpp ├── slbf.hpp ├── pointdata.hpp ├── cmd_interface_linux.hpp └── lipkg.hpp ├── CMakeLists.txt ├── README.md ├── src ├── main.cpp ├── transform.cpp ├── slbf.cpp ├── cmd_interface_linux.cpp └── lipkg.cpp └── LICENSE /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Any contribution that you make to this repository will 2 | be under the Apache 2 License, as dictated by that 3 | [license](http://www.apache.org/licenses/LICENSE-2.0.html): 4 | 5 | ~~~ 6 | 5. Submission of Contributions. Unless You explicitly state otherwise, 7 | any Contribution intentionally submitted for inclusion in the Work 8 | by You to the Licensor shall be under the terms and conditions of 9 | this License, without any additional terms or conditions. 10 | Notwithstanding the above, nothing herein shall supersede or modify 11 | the terms of any separate license agreement you may have executed 12 | with Licensor regarding such Contributions. 13 | ~~~ 14 | 15 | Contributors must sign-off each commit by adding a `Signed-off-by: ...` 16 | line to commit messages to certify that they have the right to submit 17 | the code they are contributing to the project according to the 18 | [Developer Certificate of Origin (DCO)](https://developercertificate.org/). 19 | -------------------------------------------------------------------------------- /.github/workflows/ros-lint.yml: -------------------------------------------------------------------------------- 1 | # The name of the workflow 2 | name: Lint 3 | 4 | # Specifies the events that trigger the workflow 5 | on: 6 | pull_request: 7 | 8 | # Defines a set of jobs to be run as part of the workflow 9 | jobs: 10 | ament_lint: 11 | runs-on: ubuntu-latest 12 | container: 13 | image: rostooling/setup-ros-docker:ubuntu-noble-ros-rolling-ros-base-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | linter: [cppcheck, cpplint, uncrustify, flake8, pep257, lint_cmake, xmllint, copyright] 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | 22 | - name: Setup ROS environment 23 | uses: ros-tooling/setup-ros@v0.7 24 | 25 | - name: Run Linter 26 | env: 27 | AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS: 1 28 | uses: ros-tooling/action-ros-lint@master 29 | with: 30 | linter: ${{ matrix.linter }} 31 | distribution: rolling 32 | package-name: "*" 33 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Changelog for package ld08_driver 3 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 | 5 | 1.1.4 (2025-05-28) 6 | ------------------ 7 | * Deprecate ament_include_dependency usage in CMakeLists.txt 8 | * Contributor: Hyungyu Kim 9 | 10 | 1.1.3 (2025-04-11) 11 | ------------------ 12 | * Support flexible configuration of the frame_id used when publishing the scan topic 13 | * Reduce CPU usage of the ld08 LiDAR node by adding sleep to its main loop, related PR(https://github.com/ROBOTIS-GIT/ld08_driver/pull/23) 14 | * Contributors: Hyungyu Kim, Xander Soldaat 15 | 16 | 1.1.2 (2025-04-02) 17 | ------------------ 18 | * Removed the unused parameter from the constructor of the SlTransform class 19 | * Contributors: Hyungyu Kim 20 | 21 | 1.1.1 (2025-03-27) 22 | ------------------ 23 | * Added the boost and libudev-dev dependency for release 24 | * Modified the CI and lint 25 | * Contributors: Pyo, Hyungyu Kim 26 | 27 | 1.1.0 (2025-02-25) 28 | ------------------ 29 | * ROS 2 Humble Hawksbill supported 30 | * Added Lint and CI 31 | * Contributors: Hye-Jong Kim, Hyungyu Kim 32 | 33 | 1.0.0 (2022-02-04) 34 | ------------------ 35 | * First release 36 | * Contributors: Will Son 37 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ld08_driver 5 | 1.1.4 6 | 7 | ROS package for LDS-02(LD08) Lidar. 8 | The Lidar sensor sends data to the Host controller for the Simultaneous Localization And Mapping(SLAM). 9 | 10 | Pyo 11 | Apache 2.0 12 | http://wiki.ros.org/ld08_driver 13 | https://github.com/ROBOTIS-GIT/ld08_driver 14 | https://github.com/ROBOTIS-GIT/ld08_driver/issues 15 | LD Robot 16 | Weyne 17 | Will Son 18 | Hye-Jong KIM 19 | ament_cmake 20 | boost 21 | sensor_msgs 22 | libudev-dev 23 | rclcpp 24 | 25 | ament_cmake 26 | 27 | 28 | -------------------------------------------------------------------------------- /launch/ld08.launch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright 2021 ROBOTIS CO., LTD. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Authors: Will Son 18 | 19 | 20 | from launch import LaunchDescription 21 | from launch.substitutions import LaunchConfiguration 22 | from launch_ros.actions import Node 23 | 24 | 25 | def generate_launch_description(): 26 | 27 | frame_id = LaunchConfiguration('frame_id', default='base_scan') 28 | namespace = LaunchConfiguration('namespace', default='') 29 | 30 | return LaunchDescription([ 31 | Node( 32 | package='ld08_driver', 33 | executable='ld08_driver', 34 | name='ld08_driver', 35 | output='screen', 36 | parameters=[ 37 | {'frame_id': frame_id}, 38 | {'namespace': namespace}, 39 | ]), 40 | ]) 41 | -------------------------------------------------------------------------------- /include/transform.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #ifndef TRANSFORM_HPP_ 18 | #define TRANSFORM_HPP_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "../include/lipkg.hpp" 24 | 25 | enum class LDVersion 26 | { 27 | LD_ZERO, /*Zero generation lidar*/ 28 | LD_THREE, /*Third generation lidar*/ 29 | LD_EIGHT, /*Eight generation radar*/ 30 | LD_NINE, /*Nine generation radar*/ 31 | }; 32 | 33 | class SlTransform 34 | { 35 | private: 36 | bool to_right_hand = true; 37 | double offset_x; 38 | double offset_y; 39 | 40 | public: 41 | explicit SlTransform(LDVersion version); 42 | Points2D Transform(const Points2D & data); 43 | ~SlTransform(); 44 | }; 45 | 46 | 47 | #endif // TRANSFORM_HPP_ 48 | -------------------------------------------------------------------------------- /include/slbf.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #ifndef SLBF_HPP_ 18 | #define SLBF_HPP_ 19 | 20 | #include 21 | #include 22 | #include "../include/pointdata.hpp" 23 | 24 | class Slbf 25 | { 26 | private: 27 | const int CONFIDENCE_LOW = 92; 28 | 29 | // Default scanning frequency, which can be changed according to radar protocol 30 | const int SCAN_FRE = 2300; 31 | double curr_speed; 32 | 33 | // whether strict filtering is enabled within 300 mm, 34 | // the effective value may be lost, 35 | // and the time sequence of recharging needs to be disabled 36 | bool enable_strict_policy; 37 | Slbf() = delete; 38 | Slbf(const Slbf &) = delete; 39 | Slbf & operator=(const Slbf &) = delete; 40 | 41 | public: 42 | explicit Slbf(int speed, bool strict_policy = true); 43 | Points2D NearFilter(const Points2D & tmp) const; 44 | void EnableStrictPolicy(bool enable); 45 | ~Slbf(); 46 | }; 47 | 48 | #endif // SLBF_HPP_ 49 | -------------------------------------------------------------------------------- /include/pointdata.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #ifndef POINTDATA_HPP_ 18 | #define POINTDATA_HPP_ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | struct PointData 25 | { 26 | // Polar coordinate representation 27 | float angle; 28 | uint16_t distance; 29 | uint8_t confidence; 30 | 31 | // Rectangular coordinate representation 32 | double x; 33 | double y; 34 | 35 | PointData(float angle, uint16_t distance, uint8_t confidence, double x = 0, double y = 0) 36 | { 37 | this->angle = angle; 38 | this->distance = distance; 39 | this->confidence = confidence; 40 | this->x = x; 41 | this->y = y; 42 | } 43 | 44 | PointData() 45 | { 46 | } 47 | 48 | friend std::ostream & operator<<(std::ostream & os, const PointData & data) 49 | { 50 | os << data.angle << " " \ 51 | << data.distance << " " \ 52 | << static_cast(data.confidence) << " " \ 53 | << data.x << " " \ 54 | << data.y; 55 | return os; 56 | } 57 | }; 58 | 59 | 60 | typedef std::vector Points2D; 61 | 62 | #endif // POINTDATA_HPP_ 63 | -------------------------------------------------------------------------------- /include/cmd_interface_linux.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #ifndef CMD_INTERFACE_LINUX_HPP_ 18 | #define CMD_INTERFACE_LINUX_HPP_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class CmdInterfaceLinux 31 | { 32 | public: 33 | explicit CmdInterfaceLinux(int32_t ver); 34 | ~CmdInterfaceLinux(); 35 | 36 | bool Open(std::string & port_name); 37 | bool Close(); 38 | bool ReadFromIO(uint8_t * rx_buf, uint32_t rx_buf_len, uint32_t * rx_len); 39 | bool WriteToIo(const uint8_t * tx_buf, uint32_t tx_buf_len, uint32_t * tx_len); 40 | bool GetCmdDevices(std::vector> & device_list); 41 | void SetReadCallback(std::function callback) 42 | { 43 | mReadCallback = callback; 44 | } 45 | bool IsOpened() 46 | { 47 | return mIsCmdOpened.load(); 48 | } 49 | 50 | private: 51 | std::thread * mRxThread; 52 | int64_t mRxCount; 53 | std::function mReadCallback; 54 | int32_t version; 55 | static void mRxThreadProc(void * param); 56 | int32_t mComHandle; 57 | std::atomic mIsCmdOpened, mRxThreadExitFlag; 58 | }; 59 | 60 | #endif // CMD_INTERFACE_LINUX_HPP_ 61 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Set minimum required version of cmake, project name and compile options 3 | ################################################################################ 4 | cmake_minimum_required(VERSION 3.5) 5 | project(ld08_driver) 6 | 7 | if(NOT CMAKE_CXX_STANDARD) 8 | set(CMAKE_CXX_STANDARD 17) 9 | endif() 10 | 11 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 12 | add_compile_options(-Wall -Wextra -Wpedantic) 13 | endif() 14 | 15 | if(MSVC) 16 | add_compile_definitions( 17 | _USE_MATH_DEFINES 18 | ) 19 | endif() 20 | 21 | ################################################################################ 22 | # Find ament packages and libraries for ament and system dependencies 23 | ################################################################################ 24 | find_package(ament_cmake REQUIRED) 25 | find_package(rclcpp REQUIRED) 26 | find_package(sensor_msgs REQUIRED) 27 | find_package(Boost REQUIRED system) 28 | 29 | ################################################################################ 30 | # Build 31 | ################################################################################ 32 | include_directories( 33 | include 34 | ) 35 | 36 | set(CMAKE_CXX_FLAGS "-std=c++17 ${CMAKE_CXX_FLAGS}") 37 | 38 | file(GLOB MAIN_SRC src/*.cpp) 39 | 40 | add_executable(ld08_driver ${MAIN_SRC}) 41 | target_link_libraries(ld08_driver 42 | ${sensor_msgs_TARGETS} 43 | ${std_msgs_TARGETS} 44 | Boost::system 45 | pthread 46 | rclcpp::rclcpp 47 | udev 48 | ) 49 | 50 | 51 | ################################################################################ 52 | # Install 53 | ################################################################################ 54 | install(DIRECTORY launch 55 | DESTINATION share/${PROJECT_NAME} 56 | ) 57 | 58 | install( 59 | TARGETS ld08_driver 60 | RUNTIME DESTINATION lib/${PROJECT_NAME} 61 | ) 62 | 63 | ################################################################################ 64 | # Macro for ament package 65 | ################################################################################ 66 | ament_export_dependencies( 67 | rclcpp 68 | std_msgs 69 | sensor_msgs 70 | Boost::system 71 | pthread 72 | udev 73 | ) 74 | ament_export_include_directories(include) 75 | ament_package() 76 | -------------------------------------------------------------------------------- /.github/workflows/ros-ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | on: 5 | push: 6 | branches: [ main, humble, jazzy] 7 | pull_request: 8 | branches: [ main, humble, jazzy] 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | ROS_CI: 13 | runs-on: ubuntu-22.04 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | ros_distribution: 18 | - humble 19 | - jazzy 20 | - rolling 21 | include: 22 | # ROS 2 Humble Hawksbill 23 | - docker_image: ubuntu:jammy 24 | ros_distribution: humble 25 | ros_version: 2 26 | # ROS 2 Jazzy Jalisco 27 | - docker_image: ubuntu:noble 28 | ros_distribution: jazzy 29 | ros_version: 2 30 | # ROS 2 Rolling Ridley 31 | - docker_image: ubuntu:noble 32 | ros_distribution: rolling 33 | ros_version: 2 34 | container: 35 | image: ${{ matrix.docker_image }} 36 | steps: 37 | - name: Setup directories 38 | run: mkdir -p ros_ws/src 39 | 40 | - name: checkout 41 | uses: actions/checkout@v4 42 | with: 43 | path: ros_ws/src 44 | - name: Setup ROS environment 45 | uses: ros-tooling/setup-ros@v0.7 46 | with: 47 | required-ros-distributions: ${{ matrix.ros_distribution }} 48 | 49 | - name: Check and Install ROS dependencies 50 | shell: bash 51 | run: | 52 | set -e 53 | source /opt/ros/${{ matrix.ros_distribution }}/setup.bash 54 | echo "--- Updating rosdep definitions ---" 55 | rosdep update 56 | echo "--- Installing system dependencies for ROS 2 ${{ matrix.ros_distribution }} ---" 57 | rosdep install --from-paths ros_ws/src --ignore-src -y -r --rosdistro ${{ matrix.ros_distribution }} 58 | echo "--- Performing rosdep check for ROS 2 ${{ matrix.ros_distribution }} ---" 59 | if rosdep check --from-paths ros_ws/src --ignore-src --rosdistro ${{ matrix.ros_distribution }}; then 60 | echo "--- rosdep check passed ---" 61 | else 62 | echo "--- rosdep check failed: Missing system dependencies or unresolvable keys. ---" 63 | exit 1 64 | fi 65 | 66 | - name: Build and Test 67 | uses: ros-tooling/action-ros-ci@v0.3 68 | with: 69 | target-ros2-distro: ${{ matrix.ros_distribution }} 70 | vcs-repo-file-url: "" 71 | package-name: ld08_driver 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LDS-02 (LD08) 2 | 3 | 4 | Since 2022, the LDS-02 (LD08) replaces the previous LDS-01 sensor. 5 | 6 | - Active Branches: noetic, humble, jazzy, main 7 | - Legacy Branches: *-devel 8 | 9 | ## Open Source Projects Related to TurtleBot3 10 | - [turtlebot3](https://github.com/ROBOTIS-GIT/turtlebot3) 11 | - [turtlebot3_msgs](https://github.com/ROBOTIS-GIT/turtlebot3_msgs) 12 | - [turtlebot3_simulations](https://github.com/ROBOTIS-GIT/turtlebot3_simulations) 13 | - [turtlebot3_manipulation](https://github.com/ROBOTIS-GIT/turtlebot3_manipulation) 14 | - [turtlebot3_autorace](https://github.com/ROBOTIS-GIT/turtlebot3_autorace) 15 | - [turtlebot3_applications](https://github.com/ROBOTIS-GIT/turtlebot3_applications) 16 | - [turtlebot3_applications_msgs](https://github.com/ROBOTIS-GIT/turtlebot3_applications_msgs) 17 | - [turtlebot3_machine_learning](https://github.com/ROBOTIS-GIT/turtlebot3_machine_learning) 18 | - [turtlebot3_home_service_challenge](https://github.com/ROBOTIS-GIT/turtlebot3_home_service_challenge) 19 | - [hls_lfcd_lds_driver](https://github.com/ROBOTIS-GIT/hls_lfcd_lds_driver) 20 | - [ld08_driver](https://github.com/ROBOTIS-GIT/ld08_driver) 21 | - [open_manipulator](https://github.com/ROBOTIS-GIT/open_manipulator) 22 | - [dynamixel_sdk](https://github.com/ROBOTIS-GIT/DynamixelSDK) 23 | - [OpenCR-Hardware](https://github.com/ROBOTIS-GIT/OpenCR-Hardware) 24 | - [OpenCR](https://github.com/ROBOTIS-GIT/OpenCR) 25 | 26 | ## Documentation, Videos, and Community 27 | 28 | ### Official Documentation 29 | - ⚙️ **[ROBOTIS DYNAMIXEL](https://dynamixel.com/)** 30 | - 📚 **[ROBOTIS e-Manual for Dynamixel SDK](http://emanual.robotis.com/docs/en/software/dynamixel/dynamixel_sdk/overview/)** 31 | - 📚 **[ROBOTIS e-Manual for TurtleBot3](http://turtlebot3.robotis.com/)** 32 | - 📚 **[ROBOTIS e-Manual for OpenMANIPULATOR-X](https://emanual.robotis.com/docs/en/platform/openmanipulator_x/overview/)** 33 | 34 | ### Learning Resources 35 | - 🎥 **[ROBOTIS YouTube Channel](https://www.youtube.com/@ROBOTISCHANNEL)** 36 | - 🎥 **[ROBOTIS Open Source YouTube Channel](https://www.youtube.com/@ROBOTISOpenSourceTeam)** 37 | - 🎥 **[ROBOTIS TurtleBot3 YouTube Playlist](https://www.youtube.com/playlist?list=PLRG6WP3c31_XI3wlvHlx2Mp8BYqgqDURU)** 38 | - 🎥 **[ROBOTIS OpenMANIPULATOR YouTube Playlist](https://www.youtube.com/playlist?list=PLRG6WP3c31_WpEsB6_Rdt3KhiopXQlUkb)** 39 | 40 | ### Community & Support 41 | - 💬 **[ROBOTIS Community Forum](https://forum.robotis.com/)** 42 | - 💬 **[TurtleBot category from ROS Community](https://discourse.ros.org/c/turtlebot/)** 43 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include "../include/cmd_interface_linux.hpp" 24 | #include "../include/lipkg.hpp" 25 | #include "../include/transform.hpp" 26 | 27 | 28 | int main(int argc, char ** argv) 29 | { 30 | rclcpp::init(argc, argv); 31 | auto node = rclcpp::Node::make_shared("laser_scan_publisher"); 32 | rclcpp::Publisher::SharedPtr lidar_pub; 33 | rclcpp::Rate loop_rate(100.0); 34 | 35 | node->declare_parameter("frame_id", "base_scan"); 36 | node->declare_parameter("namespace", ""); 37 | std::string frame_id = node->get_parameter("frame_id").as_string(); 38 | std::string name_space = node->get_parameter("namespace").as_string(); 39 | if (name_space != "") { 40 | frame_id = name_space + "/" + frame_id; 41 | } 42 | 43 | LiPkg * pkg; 44 | std::string product; 45 | int32_t ver = 8; 46 | pkg = new LD08_LiPkg; 47 | 48 | CmdInterfaceLinux cmd_port(ver); 49 | std::vector> device_list; 50 | std::string port_name; 51 | cmd_port.GetCmdDevices(device_list); 52 | for (auto n : device_list) { 53 | std::cout << n.first << " " << n.second << std::endl; 54 | if (strstr(n.second.c_str(), "CP2102")) { 55 | port_name = n.first; 56 | } 57 | } 58 | 59 | if (port_name.empty() == false) { 60 | std::cout << "FOUND LDS-02" << product << std::endl; 61 | cmd_port.SetReadCallback( 62 | [&pkg](const char * byte, size_t len) { 63 | if (pkg->Parse((const uint8_t *)(byte), len)) { 64 | pkg->AssemblePacket(); 65 | } 66 | }); 67 | 68 | if (cmd_port.Open(port_name)) { 69 | std::cout << "LDS-02" << product << " started successfully " << std::endl; 70 | } 71 | 72 | // char topic_name[20]={0}; 73 | // strcat(topic_name,product.c_str()); 74 | // strcat(topic_name,"/LDLiDAR"); 75 | lidar_pub = node->create_publisher( 76 | "scan", rclcpp::QoS(rclcpp::SensorDataQoS()) 77 | ); 78 | 79 | while (rclcpp::ok()) { 80 | if (pkg->IsFrameReady()) { 81 | pkg->setStamp(node->now()); 82 | pkg->setFrameId(frame_id); 83 | lidar_pub->publish(pkg->GetLaserScan()); 84 | pkg->ResetFrameReady(); 85 | } 86 | loop_rate.sleep(); 87 | } 88 | } else { 89 | std::cout << "Can't find LDS-02" << product << std::endl; 90 | } 91 | 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /src/transform.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #include "../include/transform.hpp" 18 | #include 19 | 20 | /*! 21 | \brief transfer the origin to the center of lidar circle 22 | \param[in] 23 | \arg version lidar version, different versions have different parameter settings 24 | \arg data lidar raw data 25 | \arg to_right_hand a flag whether convert to right-hand coordinate system 26 | 27 | \param[out] data 28 | \retval Data after coordinate conversion 29 | */ 30 | SlTransform::SlTransform(LDVersion version) 31 | { 32 | switch (version) { 33 | case LDVersion::LD_ZERO: 34 | case LDVersion::LD_NINE: 35 | offset_x = 8.1; 36 | offset_y = -22.5156; 37 | break; 38 | 39 | case LDVersion::LD_THREE: 40 | case LDVersion::LD_EIGHT: 41 | offset_x = 5.9; 42 | offset_y = -20.14; 43 | break; 44 | 45 | default: 46 | break; 47 | } 48 | } 49 | 50 | Points2D SlTransform::Transform(const Points2D & data) 51 | { 52 | Points2D tmp2; 53 | for (auto n : data) { 54 | /*Filter out invalid data*/ 55 | if (n.distance == 0) { 56 | continue; 57 | } 58 | /*transfer the origin to the center of lidar circle*/ 59 | /*The default direction of radar rotation is clockwise*/ 60 | /*transfer to the right-hand coordinate system*/ 61 | float right_hand = (360.f - n.angle); 62 | double x = n.distance + offset_x; 63 | double y = n.distance * 0.11923 + offset_y; 64 | double d = sqrt(x * x + y * y); 65 | double shift = atan(y / x) * 180.f / 3.14159; 66 | /*Choose whether to use the right-hand system according to the flag*/ 67 | double angle; 68 | if (to_right_hand) { 69 | angle = right_hand + shift; 70 | } else { 71 | angle = n.angle - shift; 72 | } 73 | 74 | if (angle > 360) { 75 | angle -= 360; 76 | } 77 | if (angle < 0) { 78 | angle += 360; 79 | } 80 | 81 | tmp2.push_back(PointData(angle, d, n.confidence, x, y)); 82 | } 83 | 84 | return tmp2; 85 | } 86 | 87 | SlTransform::~SlTransform() 88 | { 89 | } 90 | 91 | 92 | /* 93 | 1.Internal conversion of lidar data, transfer the origin to the center of lidar circle, and transfer to the right-hand coordinate system 94 | 2.TODO : Transfer to machine center (this part needs to be completed by customers according to their own machine structure) 95 | 3.TODO : Real time calibration of rotation and translation deviation through IMU data (this part needs to be completed by customers according to their own equipment) 96 | */ 97 | void LD00_LiPkg::Transform(std::vector & tmp) 98 | { 99 | SlTransform trans(LDVersion::LD_ZERO); 100 | tmp = trans.Transform(tmp); 101 | } 102 | 103 | void LD03_LiPkg::Transform(std::vector & tmp) 104 | { 105 | SlTransform trans(LDVersion::LD_THREE); 106 | tmp = trans.Transform(tmp); 107 | } 108 | 109 | void LD08_LiPkg::Transform(std::vector & tmp) 110 | { 111 | SlTransform trans(LDVersion::LD_EIGHT); 112 | tmp = trans.Transform(tmp); 113 | // std::cout << "Transform LD_EIGHT!!" << std::endl; 114 | } 115 | 116 | void LD09_LiPkg::Transform(std::vector & tmp) 117 | { 118 | SlTransform trans(LDVersion::LD_NINE); 119 | tmp = trans.Transform(tmp); 120 | } 121 | -------------------------------------------------------------------------------- /include/lipkg.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #ifndef LIPKG_HPP_ 18 | #define LIPKG_HPP_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "../include/pointdata.hpp" 30 | 31 | #define ANGLE_TO_RADIAN(angle) ((angle) * 3141.59 / 180000) 32 | #define RADIAN_TO_ANGLE(angle) ((angle) * 180000 / 3141.59) 33 | 34 | enum 35 | { 36 | PKG_HEADER = 0x54, 37 | PKG_VER_LEN = 0x2C, 38 | POINT_PER_PACK = 12, 39 | }; 40 | 41 | 42 | typedef struct __attribute__((packed)) 43 | { 44 | uint16_t distance; 45 | uint8_t confidence; 46 | } LidarPointStructDef; 47 | 48 | typedef struct __attribute__((packed)) 49 | { 50 | uint8_t header; 51 | uint8_t ver_len; 52 | uint16_t speed; 53 | uint16_t start_angle; 54 | LidarPointStructDef point[POINT_PER_PACK]; 55 | uint16_t end_angle; 56 | uint16_t timestamp; 57 | uint8_t crc8; 58 | } LiDARFrameTypeDef; 59 | 60 | 61 | typedef std::vector Points2D; 62 | 63 | struct FrameData 64 | { 65 | float angle_min; 66 | float angle_max; 67 | uint32_t len; 68 | std::vector distance; 69 | std::vector intensities; 70 | }; 71 | 72 | 73 | class LiPkg 74 | { 75 | public: 76 | LiPkg(); 77 | // Lidar spin speed (Hz) 78 | double GetSpeed(void); 79 | // time stamp of the packet 80 | uint16_t GetTimestamp(void) {return mTimestamp;} 81 | // a packet is ready 82 | bool IsPkgReady(void) {return mIsPkgReady;} 83 | // Lidar data frame is ready 84 | bool IsFrameReady(void) {return mIsFrameReady;} 85 | void ResetFrameReady(void) {mIsFrameReady = false;} 86 | // the number of errors in parser process of lidar data frame 87 | int32_t GetErrorTimes(void) {return mErrorTimes;} 88 | // original data package 89 | const std::array & GetPkgData(void); 90 | // parse single packet 91 | bool Parse(const uint8_t * data, int32_t len); 92 | // transform raw data to stantard data 93 | virtual void Transform(std::vector & tmp) = 0; 94 | // combine stantard data into data frames and calibrate 95 | bool AssemblePacket(); 96 | const FrameData & GetFrameData(void) {mIsFrameReady = false; return mFrameData;} 97 | sensor_msgs::msg::LaserScan GetLaserScan() {return output;} 98 | void setStamp(rclcpp::Time timeStamp) {output.header.stamp = timeStamp;} 99 | void setFrameId(std::string frame_id) {output.header.frame_id = frame_id;} 100 | 101 | private: 102 | uint16_t mTimestamp; 103 | double mSpeed; 104 | int32_t mErrorTimes; 105 | bool mIsFrameReady; 106 | bool mIsPkgReady; 107 | std::vector mDataTmp; 108 | std::array mOnePkg; 109 | std::vector mFrameTemp; 110 | FrameData mFrameData; 111 | sensor_msgs::msg::LaserScan output; 112 | void ToLaserscan(std::vector src); 113 | }; 114 | 115 | class LD00_LiPkg : public LiPkg 116 | { 117 | public: 118 | virtual void Transform(std::vector & tmp); 119 | }; 120 | 121 | class LD03_LiPkg : public LiPkg 122 | { 123 | public: 124 | virtual void Transform(std::vector & tmp); 125 | }; 126 | 127 | class LD08_LiPkg : public LiPkg 128 | { 129 | public: 130 | virtual void Transform(std::vector & tmp); 131 | }; 132 | 133 | class LD09_LiPkg : public LiPkg 134 | { 135 | public: 136 | virtual void Transform(std::vector & tmp); 137 | }; 138 | 139 | #endif // LIPKG_HPP_ 140 | -------------------------------------------------------------------------------- /src/slbf.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #include 18 | #include 19 | #include 20 | #include "../include/lipkg.hpp" 21 | #include "../include/slbf.hpp" 22 | 23 | /* 24 | \brief Set current speed 25 | \param[in] 26 | \arg speed Current lidar speed 27 | \arg strict_policy The flag to enable very strict filtering 28 | \param[out] none 29 | \retval none 30 | */ 31 | Slbf::Slbf(int speed, bool strict_policy) 32 | { 33 | curr_speed = speed; 34 | enable_strict_policy = strict_policy; 35 | } 36 | 37 | Slbf::~Slbf() 38 | { 39 | } 40 | 41 | /* 42 | \brief Filter within 1m to filter out unreasonable data points 43 | \param[in] 44 | \arg data A circle of lidar data packed 45 | \param[out] none 46 | \retval Standard data 47 | */ 48 | Points2D Slbf::NearFilter(const Points2D & data) const 49 | { 50 | Points2D normal, pending, item; 51 | std::vector group; 52 | int sunshine_amount = 0; 53 | 54 | for (auto n : data) { 55 | if (n.distance < 1000) { 56 | pending.push_back(n); 57 | } else { 58 | normal.push_back(n); 59 | } 60 | } 61 | 62 | if (data.empty()) { 63 | return normal; 64 | } 65 | 66 | double angle_delta_up_limit = curr_speed / SCAN_FRE * 1.5; 67 | double angle_delta_down_limit = curr_speed / SCAN_FRE - 0.2; 68 | 69 | std::sort( 70 | pending.begin(), pending.end(), [](PointData a, PointData b) {return a.angle < b.angle;} 71 | ); 72 | 73 | PointData last(-10, 0, 0); 74 | 75 | for (auto n : pending) { 76 | if (abs(n.angle - last.angle) > angle_delta_up_limit || 77 | abs(n.distance - last.distance) > 50) 78 | { 79 | if (item.empty() == false) { 80 | group.push_back(item); 81 | item.clear(); 82 | } 83 | } 84 | item.push_back(n); 85 | last = n; 86 | } 87 | 88 | if (item.empty() == false) { 89 | group.push_back(item); 90 | } 91 | 92 | if (group.empty()) { 93 | return normal; 94 | } 95 | 96 | 97 | auto first_item = group.front().front(); 98 | auto last_item = group.back().back(); 99 | if (abs(first_item.angle + 360.f - last_item.angle) < angle_delta_up_limit && 100 | abs(first_item.distance - last_item.distance) < 50) 101 | { 102 | if (group.size() > 1) { 103 | group.front().insert(group.front().begin(), group.back().begin(), group.back().end()); 104 | group.erase(group.end() - 1); 105 | } 106 | } 107 | 108 | for (auto n : group) { 109 | if (n.size() == 0) { 110 | continue; 111 | } 112 | 113 | if (n.size() > 15) { 114 | normal.insert(normal.end(), n.begin(), n.end()); 115 | continue; 116 | } 117 | 118 | 119 | for (auto m : n) { 120 | int flag = m.confidence & 0x01; 121 | sunshine_amount += (flag == 1); 122 | } 123 | 124 | double sunshine_rate = static_cast(sunshine_amount) / static_cast(n.size()); 125 | 126 | double confidence_avg = 0; 127 | double dis_avg = 0; 128 | for (auto m : n) { 129 | confidence_avg += m.confidence; 130 | dis_avg += m.distance; 131 | } 132 | confidence_avg /= n.size(); 133 | dis_avg /= n.size(); 134 | 135 | if (sunshine_rate < 0.2 && confidence_avg > CONFIDENCE_LOW) { 136 | normal.insert(normal.end(), n.begin(), n.end()); 137 | continue; 138 | } 139 | 140 | if (sunshine_rate > 0.5 && confidence_avg < CONFIDENCE_LOW) { 141 | continue; 142 | } 143 | 144 | if (enable_strict_policy) { 145 | if (dis_avg < 300 && confidence_avg < CONFIDENCE_LOW && n.size() < 5) { 146 | continue; 147 | } 148 | 149 | if (dis_avg < 300 && sunshine_rate > 0.9 && n.size() < 3) { 150 | continue; 151 | } 152 | } 153 | 154 | double diff_avg = 0; 155 | 156 | for (uint32_t i = 1; i < (uint32_t)n.size(); i++) { 157 | diff_avg += abs(n[i].angle - n[i - 1].angle); 158 | } 159 | 160 | diff_avg /= static_cast(n.size() - 1); 161 | 162 | if (diff_avg > angle_delta_down_limit) { 163 | normal.insert(normal.end(), n.begin(), n.end()); 164 | } 165 | } 166 | 167 | return normal; 168 | } 169 | 170 | /* 171 | \brief Enable strong filtering 172 | \param[in] 173 | \arg enable : true ,false 174 | \param[out] none 175 | \retval 176 | */ 177 | void Slbf::EnableStrictPolicy(bool enable) 178 | { 179 | enable_strict_policy = enable; 180 | } 181 | -------------------------------------------------------------------------------- /src/cmd_interface_linux.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #include "../include/cmd_interface_linux.hpp" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define MAX_ACK_BUF_LEN 2304000 30 | 31 | CmdInterfaceLinux::CmdInterfaceLinux(int32_t ver) 32 | : mRxThread(nullptr), mRxCount(0), mReadCallback(nullptr), version(ver) 33 | { 34 | mComHandle = -1; 35 | } 36 | 37 | 38 | CmdInterfaceLinux::~CmdInterfaceLinux() 39 | { 40 | Close(); 41 | } 42 | 43 | 44 | bool CmdInterfaceLinux::Open(std::string & port_name) 45 | { 46 | int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK); 47 | 48 | mComHandle = open(port_name.c_str(), flags); 49 | if (-1 == mComHandle) { 50 | std::cout << "CmdInterfaceLinux::Open open error!" << std::endl; 51 | return false; 52 | } 53 | 54 | // get port options 55 | struct termios options; 56 | if (-1 == tcgetattr(mComHandle, &options)) { 57 | Close(); 58 | std::cout << "CmdInterfaceLinux::Open tcgetattr error!" << std::endl; 59 | return false; 60 | } 61 | 62 | options.c_cflag |= (tcflag_t)(CLOCAL | CREAD | CS8 | CRTSCTS); 63 | options.c_cflag &= (tcflag_t) ~(CSTOPB | PARENB | PARODD); 64 | // |ECHOPRT 65 | options.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN); 66 | options.c_oflag &= (tcflag_t) ~(OPOST); 67 | options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | INLCR | IGNCR | ICRNL | IGNBRK); 68 | 69 | options.c_cc[VMIN] = 0; 70 | options.c_cc[VTIME] = 0; 71 | 72 | switch (version) { 73 | case 0: cfsetispeed(&options, B115200);break; 74 | case 3: cfsetispeed(&options, B115200);break; 75 | case 6: cfsetispeed(&options, B230400);break; 76 | case 8: cfsetispeed(&options, B115200);break; 77 | case 9: cfsetispeed(&options, B115200);break; 78 | default: cfsetispeed(&options, B115200); 79 | } 80 | 81 | if (tcsetattr(mComHandle, TCSANOW, &options) < 0) { 82 | std::cout << "CmdInterfaceLinux::Open tcsetattr error!" << std::endl; 83 | Close(); 84 | return false; 85 | } 86 | 87 | tcflush(mComHandle, TCIFLUSH); 88 | 89 | mRxThreadExitFlag = false; 90 | mRxThread = new std::thread(mRxThreadProc, this); 91 | mIsCmdOpened = true; 92 | 93 | return true; 94 | } 95 | 96 | 97 | bool CmdInterfaceLinux::Close() 98 | { 99 | if (mIsCmdOpened == false) { 100 | return true; 101 | } 102 | 103 | mRxThreadExitFlag = true; 104 | 105 | if (mComHandle != -1) { 106 | close(mComHandle); 107 | mComHandle = -1; 108 | } 109 | 110 | if ((mRxThread != nullptr) && mRxThread->joinable()) { 111 | mRxThread->join(); 112 | delete mRxThread; 113 | mRxThread = NULL; 114 | } 115 | 116 | mIsCmdOpened = false; 117 | 118 | return true; 119 | } 120 | 121 | 122 | bool CmdInterfaceLinux::GetCmdDevices( 123 | std::vector> & device_list) 125 | { 126 | struct udev * udev; 127 | struct udev_enumerate * enumerate; 128 | struct udev_list_entry * devices, * dev_list_entry; 129 | struct udev_device * dev; 130 | 131 | udev = udev_new(); 132 | if (!udev) { 133 | return false; 134 | } 135 | enumerate = udev_enumerate_new(udev); 136 | udev_enumerate_add_match_subsystem(enumerate, "tty"); 137 | udev_enumerate_scan_devices(enumerate); 138 | devices = udev_enumerate_get_list_entry(enumerate); 139 | udev_list_entry_foreach(dev_list_entry, devices) 140 | { 141 | const char * path; 142 | 143 | path = udev_list_entry_get_name(dev_list_entry); 144 | dev = udev_device_new_from_syspath(udev, path); 145 | std::string dev_path = std::string(udev_device_get_devnode(dev)); 146 | dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); 147 | if (dev) { 148 | std::pair p; 149 | p.first = dev_path; 150 | p.second = udev_device_get_sysattr_value(dev, "product"); 151 | device_list.push_back(p); 152 | udev_device_unref(dev); 153 | } else { 154 | continue; 155 | } 156 | } 157 | udev_enumerate_unref(enumerate); 158 | udev_unref(udev); 159 | return true; 160 | } 161 | 162 | bool CmdInterfaceLinux::ReadFromIO(uint8_t * rx_buf, uint32_t rx_buf_len, uint32_t * rx_len) 163 | { 164 | static timespec timeout = {0, (int32_t)(100 * 1e6)}; 165 | int32_t len = -1; 166 | 167 | if (IsOpened()) { 168 | fd_set read_fds; 169 | FD_ZERO(&read_fds); 170 | FD_SET(mComHandle, &read_fds); 171 | int r = pselect(mComHandle + 1, &read_fds, NULL, NULL, &timeout, NULL); 172 | if (r < 0) { 173 | // Select was interrupted 174 | if (errno == EINTR) { 175 | return false; 176 | } 177 | } else if (r == 0) { // timeout 178 | return false; 179 | } 180 | 181 | if (FD_ISSET(mComHandle, &read_fds)) { 182 | len = (int32_t)read(mComHandle, rx_buf, rx_buf_len); 183 | if ((len != -1) && rx_len) { 184 | *rx_len = len; 185 | } 186 | } 187 | } 188 | return len == -1 ? false : true; 189 | } 190 | 191 | 192 | bool CmdInterfaceLinux::WriteToIo(const uint8_t * tx_buf, uint32_t tx_buf_len, uint32_t * tx_len) 193 | { 194 | int32_t len = -1; 195 | 196 | if (IsOpened()) { 197 | len = (int32_t)write(mComHandle, tx_buf, tx_buf_len); 198 | if ((len != -1) && tx_len) { 199 | *tx_len = len; 200 | } 201 | } 202 | return len == -1 ? false : true; 203 | } 204 | 205 | 206 | void CmdInterfaceLinux::mRxThreadProc(void * param) 207 | { 208 | CmdInterfaceLinux * cmd_if = reinterpret_cast(param); 209 | char * rx_buf = new char[MAX_ACK_BUF_LEN + 1]; 210 | while (!cmd_if->mRxThreadExitFlag.load()) { 211 | uint32_t readed = 0; 212 | bool res = cmd_if->ReadFromIO( 213 | reinterpret_cast(rx_buf), MAX_ACK_BUF_LEN, &readed); 214 | if (res && readed) { 215 | cmd_if->mRxCount += readed; 216 | if (cmd_if->mReadCallback != nullptr) { 217 | cmd_if->mReadCallback(rx_buf, readed); 218 | } 219 | } 220 | } 221 | 222 | delete[] rx_buf; 223 | } 224 | -------------------------------------------------------------------------------- /src/lipkg.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ROBOTIS CO., LTD. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Author: LD Robot, Will Son 16 | 17 | #include "../include/lipkg.hpp" 18 | #include 19 | #include 20 | 21 | 22 | #define USE_SLBF 23 | 24 | #ifdef USE_SLBI 25 | #include "slbi.hpp" 26 | #endif 27 | 28 | #ifdef USE_SLBF 29 | #include "slbf.hpp" 30 | #endif 31 | 32 | 33 | static const uint8_t CrcTable[256] = 34 | { 35 | 0x00, 0x4d, 0x9a, 0xd7, 0x79, 0x34, 0xe3, 36 | 0xae, 0xf2, 0xbf, 0x68, 0x25, 0x8b, 0xc6, 0x11, 0x5c, 0xa9, 0xe4, 0x33, 37 | 0x7e, 0xd0, 0x9d, 0x4a, 0x07, 0x5b, 0x16, 0xc1, 0x8c, 0x22, 0x6f, 0xb8, 38 | 0xf5, 0x1f, 0x52, 0x85, 0xc8, 0x66, 0x2b, 0xfc, 0xb1, 0xed, 0xa0, 0x77, 39 | 0x3a, 0x94, 0xd9, 0x0e, 0x43, 0xb6, 0xfb, 0x2c, 0x61, 0xcf, 0x82, 0x55, 40 | 0x18, 0x44, 0x09, 0xde, 0x93, 0x3d, 0x70, 0xa7, 0xea, 0x3e, 0x73, 0xa4, 41 | 0xe9, 0x47, 0x0a, 0xdd, 0x90, 0xcc, 0x81, 0x56, 0x1b, 0xb5, 0xf8, 0x2f, 42 | 0x62, 0x97, 0xda, 0x0d, 0x40, 0xee, 0xa3, 0x74, 0x39, 0x65, 0x28, 0xff, 43 | 0xb2, 0x1c, 0x51, 0x86, 0xcb, 0x21, 0x6c, 0xbb, 0xf6, 0x58, 0x15, 0xc2, 44 | 0x8f, 0xd3, 0x9e, 0x49, 0x04, 0xaa, 0xe7, 0x30, 0x7d, 0x88, 0xc5, 0x12, 45 | 0x5f, 0xf1, 0xbc, 0x6b, 0x26, 0x7a, 0x37, 0xe0, 0xad, 0x03, 0x4e, 0x99, 46 | 0xd4, 0x7c, 0x31, 0xe6, 0xab, 0x05, 0x48, 0x9f, 0xd2, 0x8e, 0xc3, 0x14, 47 | 0x59, 0xf7, 0xba, 0x6d, 0x20, 0xd5, 0x98, 0x4f, 0x02, 0xac, 0xe1, 0x36, 48 | 0x7b, 0x27, 0x6a, 0xbd, 0xf0, 0x5e, 0x13, 0xc4, 0x89, 0x63, 0x2e, 0xf9, 49 | 0xb4, 0x1a, 0x57, 0x80, 0xcd, 0x91, 0xdc, 0x0b, 0x46, 0xe8, 0xa5, 0x72, 50 | 0x3f, 0xca, 0x87, 0x50, 0x1d, 0xb3, 0xfe, 0x29, 0x64, 0x38, 0x75, 0xa2, 51 | 0xef, 0x41, 0x0c, 0xdb, 0x96, 0x42, 0x0f, 0xd8, 0x95, 0x3b, 0x76, 0xa1, 52 | 0xec, 0xb0, 0xfd, 0x2a, 0x67, 0xc9, 0x84, 0x53, 0x1e, 0xeb, 0xa6, 0x71, 53 | 0x3c, 0x92, 0xdf, 0x08, 0x45, 0x19, 0x54, 0x83, 0xce, 0x60, 0x2d, 0xfa, 54 | 0xb7, 0x5d, 0x10, 0xc7, 0x8a, 0x24, 0x69, 0xbe, 0xf3, 0xaf, 0xe2, 0x35, 55 | 0x78, 0xd6, 0x9b, 0x4c, 0x01, 0xf4, 0xb9, 0x6e, 0x23, 0x8d, 0xc0, 0x17, 56 | 0x5a, 0x06, 0x4b, 0x9c, 0xd1, 0x7f, 0x32, 0xe5, 0xa8 57 | }; 58 | 59 | 60 | LiPkg::LiPkg() 61 | : mTimestamp(0), mSpeed(0), mErrorTimes(0), mIsFrameReady(false), mIsPkgReady(false) 62 | { 63 | } 64 | 65 | double LiPkg::GetSpeed(void) 66 | { 67 | return mSpeed / 360.0; 68 | } 69 | 70 | bool LiPkg::Parse(const uint8_t * data, int32_t len) 71 | { 72 | for (int i = 0; i < len; i++) { 73 | mDataTmp.push_back(*(data + i)); 74 | } 75 | 76 | if (mDataTmp.size() < sizeof(LiDARFrameTypeDef)) { 77 | return false; 78 | } 79 | 80 | if (mDataTmp.size() > sizeof(LiDARFrameTypeDef) * 100) { 81 | mErrorTimes++; 82 | mDataTmp.clear(); 83 | return false; 84 | } 85 | 86 | uint16_t start = 0; 87 | 88 | while (start < mDataTmp.size() - 2) { 89 | start = 0; 90 | while (start < mDataTmp.size() - 2) { 91 | if ((mDataTmp[start] == PKG_HEADER) && (mDataTmp[start + 1] == PKG_VER_LEN)) { 92 | break; 93 | } 94 | 95 | if ((mDataTmp[start] == PKG_HEADER) && 96 | (mDataTmp[start + 1] == (PKG_VER_LEN | (0x07 << 5)) 97 | )) 98 | { 99 | break; 100 | } 101 | start++; 102 | } 103 | 104 | if (start != 0) { 105 | mErrorTimes++; 106 | for (int i = 0; i < start; i++) { 107 | mDataTmp.erase(mDataTmp.begin()); 108 | } 109 | } 110 | 111 | if (mDataTmp.size() < sizeof(LiDARFrameTypeDef)) { 112 | return false; 113 | } 114 | 115 | LiDARFrameTypeDef * pkg = reinterpret_cast(mDataTmp.data()); 116 | uint8_t crc = 0; 117 | 118 | for (uint32_t i = 0; i < sizeof(LiDARFrameTypeDef) - 1; i++) { 119 | crc = CrcTable[(crc ^ mDataTmp[i]) & 0xff]; 120 | } 121 | 122 | if (crc == pkg->crc8) { 123 | double diff = (pkg->end_angle / 100 - pkg->start_angle / 100 + 360) % 360; 124 | if (diff > static_cast(pkg->speed) * POINT_PER_PACK / 2300 * 3 / 2) { 125 | mErrorTimes++; 126 | } else { 127 | mSpeed = pkg->speed; 128 | mTimestamp = pkg->timestamp; 129 | uint32_t diff = 130 | ((uint32_t)pkg->end_angle + 36000 - (uint32_t)pkg->start_angle) % 36000; 131 | float step = diff / (POINT_PER_PACK - 1) / 100.0; 132 | float start = static_cast(pkg->start_angle) / 100.0; 133 | float end = static_cast(pkg->end_angle % 36000) / 100.0; 134 | PointData data; 135 | for (int i = 0; i < POINT_PER_PACK; i++) { 136 | data.distance = pkg->point[i].distance; 137 | data.angle = start + i * step; 138 | if (data.angle >= 360.0) { 139 | data.angle -= 360.0; 140 | } 141 | data.confidence = pkg->point[i].confidence; 142 | mOnePkg[i] = data; 143 | mFrameTemp.push_back(PointData(data.angle, data.distance, data.confidence)); 144 | } 145 | // prevent angle invert 146 | mOnePkg.back().angle = end; 147 | 148 | mIsPkgReady = true; 149 | } 150 | 151 | for (uint32_t i = 0; i < sizeof(LiDARFrameTypeDef); i++) { 152 | mDataTmp.erase(mDataTmp.begin()); 153 | } 154 | 155 | if (mDataTmp.size() < sizeof(LiDARFrameTypeDef)) { 156 | break; 157 | } 158 | } else { 159 | mErrorTimes++; 160 | // only remove header,not all frame,because lidar data may contain head*/ 161 | for (int i = 0; i < 2; i++) { 162 | mDataTmp.erase(mDataTmp.begin()); 163 | } 164 | } 165 | } 166 | 167 | return true; 168 | } 169 | 170 | bool LiPkg::AssemblePacket() 171 | { 172 | float last_angle = 0; 173 | std::vector tmp; 174 | int count = 0; 175 | for (auto n = mFrameTemp.begin(); n != mFrameTemp.end(); n++) { 176 | // wait for enough data, need enough data to show a circle*/ 177 | if ((*n).angle - last_angle < (-350.f)) { // enough data has been obtained 178 | mFrameData.len = tmp.size(); 179 | Transform(tmp); // transform raw data to stantard data 180 | 181 | #ifdef USE_SLBI 182 | Slbi sb(2300); 183 | sb.FindBarcode(tmp); 184 | #endif 185 | 186 | #ifdef USE_SLBF 187 | Slbf sb(mSpeed); 188 | tmp = sb.NearFilter(tmp); 189 | #endif 190 | 191 | std::sort( 192 | tmp.begin(), tmp.end(), [](PointData a, PointData b) {return a.angle < b.angle;} 193 | ); 194 | if (tmp.size() > 0) { 195 | ToLaserscan(tmp); 196 | mIsFrameReady = true; 197 | for (auto i = 0; i < count; i++) { 198 | mFrameTemp.erase(mFrameTemp.begin()); 199 | } 200 | return true; 201 | } 202 | } else { 203 | tmp.push_back(*n); // getting data */ 204 | } 205 | 206 | count++; 207 | last_angle = (*n).angle; 208 | } 209 | 210 | return false; 211 | } 212 | 213 | const std::array & LiPkg::GetPkgData(void) 214 | { 215 | mIsPkgReady = false; 216 | return mOnePkg; 217 | } 218 | 219 | void LiPkg::ToLaserscan(std::vector src) 220 | { 221 | float angle_min, angle_max, range_min, range_max, angle_increment; 222 | 223 | // Adjust the parameters according to the demand 224 | angle_min = ANGLE_TO_RADIAN(src.front().angle); 225 | angle_max = ANGLE_TO_RADIAN(src.back().angle); 226 | range_min = 0.0; 227 | range_max = 100.0; 228 | 229 | angle_increment = ANGLE_TO_RADIAN(mSpeed / 2300); 230 | 231 | // Calculate the number of scanning points 232 | unsigned int beam_size = ceil((angle_max - angle_min) / angle_increment); 233 | output.angle_min = angle_min; 234 | output.angle_max = angle_max; 235 | output.range_min = range_min; 236 | output.range_max = range_max; 237 | output.angle_increment = angle_increment; 238 | // mSpeed = rotation_degree / sec 239 | output.time_increment = (360 / mSpeed) / ((angle_max - angle_min) / angle_increment); 240 | output.scan_time = 360 / mSpeed; 241 | 242 | // First fill all the data with Nan 243 | output.ranges.assign(beam_size, std::numeric_limits::quiet_NaN()); 244 | output.intensities.assign(beam_size, std::numeric_limits::quiet_NaN()); 245 | 246 | for (auto point : src) { 247 | float range = point.distance / 1000.f; 248 | float angle = ANGLE_TO_RADIAN(point.angle); 249 | 250 | float index = (angle - output.angle_min) / output.angle_increment; 251 | if (index >= 0.0 && index < beam_size) { 252 | // If the current content is Nan, it is assigned directly 253 | if (std::isnan(output.ranges[index])) { 254 | output.ranges[index] = range; 255 | } else { 256 | // Otherwise, only when the distance is less than the current value, it can be re assigned 257 | if (range < output.ranges[index]) { 258 | output.ranges[index] = range; 259 | } 260 | } 261 | output.intensities[index] = point.confidence; 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------