From 4dcf0e0a972785004ac7d722f0c8c828daaad8fe Mon Sep 17 00:00:00 2001 From: mt-lifan Date: Wed, 11 Feb 2026 15:47:50 +0800 Subject: [PATCH] feat 1.add cloud_raw confidence filter threshold to config yaml} 2.update sdk for ntp functions 3.add set camera FPS function in yaml(10FPS,14.5FPS) 4.release ros driver 0.9.0 5.Add time alignment mode for odin1 device synchronization with host 6.Add new time alignment option (use_host_ros_time=2) to align odin1 sensor timestamps to host time axis using PTP sync data 7.Implement PTP smoothing with 30-sample moving average window for delay and offset calculations 8.Refactor timestamp handling with make_aligned_stamp() helper function to centralize time conversion logic across all data types (IMU, point clouds, images, odometry) 9.Add LIDAR_DT_NTP data type for receiving 10.add imu data record 11.add cloud reprojection demo --- CMakeLists.txt | 40 +++ README.md | 10 +- config/control_command.yaml | 19 +- config/odin_ros.rviz | 38 ++- config/odin_ros2.rviz | 35 +- include/cloud_reprojection_ros_node.hpp | 104 ++++++ include/cloud_reprojector.hpp | 79 +++++ include/data_logger.h | 6 + include/host_sdk_sample.h | 246 +++++++------- include/lidar_api.h | 13 +- include/lidar_api_type.h | 17 +- launch_ROS1/odin1_ros1.launch | 5 + launch_ROS2/odin1_ros2.launch.py | 16 + lib/liblydHostApi_amd.a | Bin 415618 -> 422616 bytes lib/liblydHostApi_arm.a | Bin 448146 -> 455912 bytes package.xml | 35 +- src/cloud_reprojection_ros.cpp | 435 ++++++++++++++++++++++++ src/cloud_reprojector.cpp | 113 ++++++ src/host_sdk_sample.cpp | 122 ++++++- 19 files changed, 1143 insertions(+), 190 deletions(-) create mode 100644 include/cloud_reprojection_ros_node.hpp create mode 100644 include/cloud_reprojector.hpp create mode 100644 src/cloud_reprojection_ros.cpp create mode 100644 src/cloud_reprojector.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 23d83b5..1135cf7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,20 @@ if(ROS_VERSION STREQUAL "ROS1") ${PCL_LIBRARIES} ) + add_library(cloud_reprojector src/cloud_reprojector.cpp) + target_link_libraries(cloud_reprojector + ${OpenCV_LIBS} + ${PCL_LIBRARIES} + ) + + add_executable(cloud_reprojection_node src/cloud_reprojection_ros.cpp) + target_link_libraries(cloud_reprojection_node + cloud_reprojector + ${catkin_LIBRARIES} + ${OpenCV_LIBS} + ${PCL_LIBRARIES} + ) + # Installation rules install(TARGETS host_sdk_sample RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} @@ -301,11 +315,37 @@ elseif(ROS_VERSION STREQUAL "ROS2") message_filters ) + add_library(cloud_reprojector_ros2 src/cloud_reprojector.cpp) + target_compile_definitions(cloud_reprojector_ros2 PRIVATE ROS2) + target_link_libraries(cloud_reprojector_ros2 + ${OpenCV_LIBS} + ${PCL_LIBRARIES} + ) + + add_executable(cloud_reprojection_ros2_node src/cloud_reprojection_ros.cpp) + target_compile_definitions(cloud_reprojection_ros2_node PRIVATE ROS2) + target_link_libraries(cloud_reprojection_ros2_node + cloud_reprojector_ros2 + ${OpenCV_LIBS} + ${PCL_LIBRARIES} + yaml-cpp + ) + ament_target_dependencies(cloud_reprojection_ros2_node + rclcpp + sensor_msgs + nav_msgs + cv_bridge + image_transport + pcl_conversions + message_filters + ) + # Installation rules - ensure all install targets are defined before ament_package() # Install executable install(TARGETS host_sdk_sample pcd2depth_ros2_node + cloud_reprojection_ros2_node EXPORT export_${PROJECT_NAME} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib diff --git a/README.md b/README.md index 48bdf5e..0a0d5cf 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ This driver package provides core functionality for point cloud SLAM application ## 1. Version -Current version: v0.8.0 +Current version: v0.9.0 -Required device firmware version: v0.9.0 +Required device firmware version: v0.10.0 ## 2. Preparation @@ -198,6 +198,8 @@ Odin_ROS_Driver/ // ROS1/ROS2 driver package pcd2depth_ros.cpp //Source code for pcd2depth_ros pcd2depth_ros2.cpp //Source code for pcd2depth_ros2 pointcloud_depth_converter.cpp //Source code for pointcloud_depth_converter + cloud_reprojection_ros.cpp //Source code for cloud reprojection node (ROS1/ROS2) + cloud_reprojector.cpp //Core logic for cloud reprojection lib/ liblydHostApi_amd.a // Static library for AMD platform liblydHostApi_arm.a // Static library for ARM platform @@ -211,6 +213,8 @@ Odin_ROS_Driver/ // ROS1/ROS2 driver package depth_image_ros_node.hpp // depth_image_ros_node depth_image_ros2_node.hpp // depth_image_ros2_node pointcloud_depth_converter.hpp // pointcloud_depth_convert + cloud_reprojection_ros_node.hpp // cloud_reprojection_ros_node (ROS1/ROS2) + cloud_reprojector.hpp // Core class for cloud reprojection config/ control_command.yaml // Control parameter file for driver calib.yaml // Machine calibration yaml,differ for each individual device. Retrieved from the device everytime it connects to ROS driver @@ -255,6 +259,7 @@ Internal parameters of the Odin ROS driver are defined in config/control_command | tf | sendodom | tf tree Topic | | odin1/depth_img_competetion | senddepth | Dense depth image Topic. Demo, high computing power required. One-to-one with odin1/image_undistort. To utilize the data please directly subscribe to this topic instead of echoing it. Original value is already depth data, no need for further convert. | | odin1/depth_img_competetion_cloud | senddepth | Dense Depth_Cloud Topic. Demo, high computing power required | +| odin1/reprojected_image | sendreprojection | Reprojected cloud to image Topic. Projects cloud_slam to camera image using odometry. Processed on host device. | ### 4.4 Data format @@ -310,6 +315,7 @@ float32 rgb // RGB value |control_command.yaml | Detailed Description | |-----------------------|----------------------| +| use_host_ros_time | Time synchronization mode: 0 - use odin internal system time as data timestamp (typical and recommended); 1 - use host ROS time upon receive (not recommended for most users); 2 - align odin1 time to host time via NTP-like synchronization, timestamp is the sensor data reception time on host time axis. | | strict_usb3.0_check | Strict USB3.0 check, if off, allow connection even if usb connection is below usb 3.0 | | recorddata | Record data in specific format that can be imported into MindCloud(TM) for post-processing. Please be aware that this will consume a lot of storage space. Testing shows 9.5G for 10mins of data. | | devstatuslog | Device status logging, currently save device status (soc temperature, cpu usage, ram usage, dtof sensor temp .etc) and data tx & rx rate to devstatus.csv under log folder. A new file will be created every time the driver is started. | diff --git a/config/control_command.yaml b/config/control_command.yaml index 63b1325..7bdd2cc 100644 --- a/config/control_command.yaml +++ b/config/control_command.yaml @@ -3,9 +3,10 @@ register_keys: # ATTENTION: usb 3.0 is always recommended, as advance functionality like SLAM mode requires usb 3.0 for reliable map file transfer strict_usb3.0_check: 0 # 0: off: 1: on; - # 0: use odin internal system time as data time stamp as before, typical and recommanded; - # 1: use host ros time (upon recieve) as data time stamp, only use if you specifically require this setup, not recommanded for most user - use_host_ros_time: 0 + # 0: use odin internal system time as data time stamp, typical and recommended; + # 1: use host ros time (upon receive) as data time stamp, only use if you specifically require this setup, not recommended for most users + # 2: align odin1 time to host time, timestamp is the sensor data reception time on host time axis + use_host_ros_time: 2 streamctrl: 1 # 0: off; 1: on @@ -33,6 +34,12 @@ register_keys: # raw dtof data senddtof: 1 # 0: off; 1: on + cloud_raw_confidence_threshold: 35 # please refer to readme for more details + + # dtof sensor frame rate. Supported values: 100 (10fps) or 145 (14.5fps) + # Higher frame rate provides smoother point cloud data but may increase data bandwidth + # Note: value is multiplied by 10 (e.g., 145 means 14.5fps) + dtof_fps: 100 # 100: 10fps; 145: 14.5fps # slam cloud data sendcloudslam: 1 # 0: off; 1: on @@ -46,10 +53,14 @@ register_keys: # Processed on host device senddepth: 0 # 0: off; 1: on + # cloud reprojection demo, projects cloud_slam to camera image using odometry + # Processed on host device + sendreprojection: 1 # 0: off; 1: on + # record rgb, odometry, and slam cloud data as proprietary olx format for further processing in MindCloud(TM) software. # save path: ws/src/odin_ros_driver/recorddata/{record_start_time}/ # ATTENTION: please copy the full folder for post-processing. - recorddata: 0 # 0: off; 1: on + recorddata: 1 # 0: off; 1: on # Save device runtime status info to ws/src/odin_ros_driver/log/Driver_{drvier_start_time}/Conn_{device_connection_time}/dev_status.csv devstatuslog: 1 # 0: off; 1: on. diff --git a/config/odin_ros.rviz b/config/odin_ros.rviz index baca4e4..20634c7 100644 --- a/config/odin_ros.rviz +++ b/config/odin_ros.rviz @@ -9,7 +9,7 @@ Panels: - /slam1 - /TF1/Frames1 Splitter Ratio: 0.4993045926094055 - Tree Height: 837 + Tree Height: 495 - Class: rviz/Selection Name: Selection - Class: rviz/Tool Properties @@ -77,6 +77,18 @@ Visualization Manager: Transport Hint: raw Unreliable: false Value: false + - Class: rviz/Image + Enabled: false + Image Topic: /odin1/reprojected_image + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: cloudslam_reprojected + Normalize Range: true + Queue Size: 2 + Transport Hint: raw + Unreliable: false + Value: false - Alpha: 1 Autocompute Intensity Bounds: true Autocompute Value Bounds: @@ -334,8 +346,6 @@ Visualization Manager: Frame Timeout: 15 Frames: All Enabled: true - map: - Value: true odin1_base_link: Value: true odom: @@ -348,8 +358,6 @@ Visualization Manager: Show Names: true Tree: odom: - map: - {} odin1_base_link: {} Update Interval: 0 @@ -382,7 +390,7 @@ Visualization Manager: Views: Current: Class: rviz/Orbit - Distance: 15.013533592224121 + Distance: 16.25591278076172 Enable Stereo Rendering: Stereo Eye Separation: 0.05999999865889549 Stereo Focal Distance: 1 @@ -398,21 +406,21 @@ Visualization Manager: Invert Z Axis: false Name: Current View Near Clip Distance: 0.009999999776482582 - Pitch: 0.7853981852531433 + Pitch: 1.010398030281067 Target Frame: odom - Yaw: 0.7853981852531433 + Yaw: 0.8753980994224548 Saved: ~ Window Geometry: Displays: collapsed: false - Height: 1672 + Height: 1016 Hide Left Dock: false Hide Right Dock: false Image: collapsed: false Image_undistort: collapsed: false - QMainWindow State: 000000ff00000000fd00000004000000000000025f00000582fc020000000bfb0000001200530065006c0065006300740069006f006e00000001e10000009b000000b000fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000b0fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000006e000003b30000018200fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000042d000001c30000002600fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000047d000001730000002600fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f007200740000000568000000df0000002600ffffff000000010000015f00000582fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000006e000005820000013200fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e1000001970000000300000ab00000005efc0100000002fb0000000800540069006d0065010000000000000ab0000006dc00fffffffb0000000800540069006d00650100000000000004500000000000000000000006da0000058200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + QMainWindow State: 000000ff00000000fd0000000400000000000001e70000033afc020000000cfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000b0fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000022c000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000026f000001080000001600fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000047d000001730000001600fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f007200740000000568000000df0000001600fffffffb0000002a0063006c006f007500640073006c0061006d005f0072006500700072006f006a0065006300740065006400000002b2000000c50000001600ffffff000000010000015f0000033afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000033a000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000005efc0100000002fb0000000800540069006d0065010000000000000738000003bc00fffffffb0000000800540069006d00650100000000000004500000000000000000000003e60000033a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 Selection: collapsed: false Time: @@ -421,8 +429,10 @@ Window Geometry: collapsed: false Views: collapsed: false - Width: 2736 - X: 144 - Y: 54 + Width: 1848 + X: 72 + Y: 27 + cloudslam_reprojected: + collapsed: false dense_depth_image: - collapsed: false \ No newline at end of file + collapsed: false diff --git a/config/odin_ros2.rviz b/config/odin_ros2.rviz index 124933e..48d42c3 100644 --- a/config/odin_ros2.rviz +++ b/config/odin_ros2.rviz @@ -7,11 +7,12 @@ Panels: - /Global Options1 - /Status1 - /Image1/Topic1 + - /cloudslam_reprojected1 - /Odometry1 - /slam1 - /dense_depth_demo1 Splitter Ratio: 0.5 - Tree Height: 667 + Tree Height: 593 - Class: rviz_common/Selection Name: Selection - Class: rviz_common/Tool Properties @@ -74,6 +75,20 @@ Visualization Manager: Reliability Policy: Reliable Value: /odin1/image/undistorted Value: false + - Class: rviz_default_plugins/Image + Enabled: false + Max Value: 1 + Median window: 5 + Min Value: 0 + Name: cloudslam_reprojected + Normalize Range: true + Topic: + Depth: 5 + Durability Policy: Volatile + History Policy: Keep Last + Reliability Policy: Reliable + Value: /odin1/reprojected_image + Value: false - Alpha: 1 Autocompute Intensity Bounds: true Autocompute Value Bounds: @@ -377,8 +392,6 @@ Visualization Manager: Frame Timeout: 15 Frames: All Enabled: true - map: - Value: true odin1_base_link: Value: true odom: @@ -390,8 +403,6 @@ Visualization Manager: Show Names: false Tree: odom: - map: - {} odin1_base_link: {} Update Interval: 0 @@ -449,18 +460,18 @@ Visualization Manager: Swap Stereo Eyes: false Value: false Focal Point: - X: 0 - Y: 0 - Z: 0 + X: 0.2568470537662506 + Y: 2.1451337337493896 + Z: 0.3774382472038269 Focal Shape Fixed Size: false Focal Shape Size: 0.05000000074505806 Invert Z Axis: false Name: Current View Near Clip Distance: 0.009999999776482582 - Pitch: 0.3203984200954437 + Pitch: 0.3953983187675476 Target Frame: odom Value: Orbit (rviz) - Yaw: 3.130404233932495 + Yaw: 3.230407953262329 Saved: ~ Window Geometry: Displays: @@ -472,7 +483,7 @@ Window Geometry: collapsed: false Image_undistort: collapsed: false - QMainWindow State: 000000ff00000000fd0000000400000000000001f50000039efc020000000bfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002d8000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000031b000000c00000002800fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000023e0000007a0000002800fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f00720074000000028f0000014c0000002800ffffff000000010000010f0000039efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000039e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000004700000039e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 + QMainWindow State: 000000ff00000000fd0000000400000000000001d40000039efc020000000cfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000028e000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002d10000010a0000002800fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000023e0000007a0000002800fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f00720074000000028f0000014c0000002800fffffffb0000002a0063006c006f007500640073006c0061006d005f0072006500700072006f006a006500630074006500640000000310000000cb0000002800ffffff000000010000010f0000039efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000039e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000004910000039e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 Selection: collapsed: false Tool Properties: @@ -482,5 +493,7 @@ Window Geometry: Width: 1920 X: 540 Y: 124 + cloudslam_reprojected: + collapsed: false dense_depth_image: collapsed: false diff --git a/include/cloud_reprojection_ros_node.hpp b/include/cloud_reprojection_ros_node.hpp new file mode 100644 index 0000000..4408255 --- /dev/null +++ b/include/cloud_reprojection_ros_node.hpp @@ -0,0 +1,104 @@ +/* +Copyright 2025 Manifold Tech Ltd.(www.manifoldtech.com.co) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#ifdef ROS2 + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#include +#include +#include + +#include "cloud_reprojector.hpp" + +#include +#include + +#ifdef ROS2 +class CloudReprojectionRosNode : public rclcpp::Node +{ +public: + CloudReprojectionRosNode(const rclcpp::NodeOptions& options = rclcpp::NodeOptions()); + +private: + using PointCloud2 = sensor_msgs::msg::PointCloud2; + using Odometry = nav_msgs::msg::Odometry; + using Image = sensor_msgs::msg::Image; + + std::string cloud_slam_topic_; + std::string odometry_topic_; + std::string reprojected_image_topic_; + + message_filters::Subscriber cloud_sub_; + message_filters::Subscriber odom_sub_; + + typedef message_filters::sync_policies::ExactTime MySyncPolicy; + typedef message_filters::Synchronizer Sync; + std::shared_ptr sync_; + + image_transport::Publisher reprojected_image_pub_; + + std::unique_ptr reprojector_; + + void loadParameters(); + void syncCallback(const PointCloud2::ConstSharedPtr& cloud_msg, + const Odometry::ConstSharedPtr& odom_msg); +}; +#else +class CloudReprojectionRosNode +{ +public: + CloudReprojectionRosNode(ros::NodeHandle& nh, ros::NodeHandle& pnh); + +private: + ros::NodeHandle nh_, pnh_; + + std::string cloud_slam_topic_; + std::string odometry_topic_; + std::string reprojected_image_topic_; + + message_filters::Subscriber cloud_sub_; + message_filters::Subscriber odom_sub_; + + typedef message_filters::sync_policies::ExactTime MySyncPolicy; + typedef message_filters::Synchronizer Sync; + std::shared_ptr sync_; + + ros::Publisher reprojected_image_pub_; + + std::unique_ptr reprojector_; + + void loadParameters(); + void syncCallback(const sensor_msgs::PointCloud2ConstPtr& cloud_msg, + const nav_msgs::OdometryConstPtr& odom_msg); +}; +#endif diff --git a/include/cloud_reprojector.hpp b/include/cloud_reprojector.hpp new file mode 100644 index 0000000..e7ff884 --- /dev/null +++ b/include/cloud_reprojector.hpp @@ -0,0 +1,79 @@ +/* +Copyright 2025 Manifold Tech Ltd.(www.manifoldtech.com.co) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "polynomial_camera.hpp" + +#include + +class CloudReprojector +{ +public: + struct CameraParams + { + int image_width = 1600; + int image_height = 1296; + double A11 = 0.0, A12 = 0.0, A22 = 0.0; + double u0 = 0.0, v0 = 0.0; + double k2 = 0.0, k3 = 0.0, k4 = 0.0, k5 = 0.0, k6 = 0.0, k7 = 0.0; + }; + + struct ExtrinsicParams + { + Eigen::Matrix4d Tcl = Eigen::Matrix4d::Identity(); // camera to lidar + Eigen::Matrix4d Til = Eigen::Matrix4d::Identity(); // lidar to imu (fixed) + Eigen::Matrix4d Tic = Eigen::Matrix4d::Identity(); // camera to imu (calculated) + }; + + struct OdomPose + { + Eigen::Quaterniond orientation = Eigen::Quaterniond::Identity(); + Eigen::Vector3d position = Eigen::Vector3d::Zero(); + }; + + CloudReprojector(); + ~CloudReprojector() = default; + + bool initialize(const CameraParams& cam_params, const ExtrinsicParams& ext_params); + + cv::Mat reprojectCloud(const pcl::PointCloud& cloud_odom, + const OdomPose& odom_pose); + + void setPointRadius(int radius) { point_radius_ = radius; } + int getPointRadius() const { return point_radius_; } + + const CameraParams& getCameraParams() const { return camera_params_; } + const ExtrinsicParams& getExtrinsicParams() const { return extrinsic_params_; } + + static Eigen::Matrix4d calculateTic(const Eigen::Matrix4d& Tcl, const Eigen::Matrix4d& Til); + +private: + Eigen::Matrix4d odomPoseToMatrix(const OdomPose& odom) const; + + cv::Mat projectCloudToImage(const pcl::PointCloud& cloud_in_cam) const; + + CameraParams camera_params_; + ExtrinsicParams extrinsic_params_; + std::unique_ptr camera_model_; + + int point_radius_ = 4; + bool initialized_ = false; +}; diff --git a/include/data_logger.h b/include/data_logger.h index 1366440..7311790 100644 --- a/include/data_logger.h +++ b/include/data_logger.h @@ -79,6 +79,7 @@ public: //cloud_writer_ = std::make_unique(root_dir_ / "OdinPointCloud.olx", opts.batch_size); image_writer_ = std::make_unique(root_dir_ / "OdinImage.bin", opts.batch_size); roatation_writer_ = std::make_unique(root_dir_ / "OdinRotate.bin", opts.batch_size); + imu_writer_ = std::make_unique(root_dir_ / "OdinIMU.bin", opts.batch_size); } ~BinaryDataLogger() { @@ -87,6 +88,7 @@ public: if (cloud_writer_) cloud_writer_->shutdown(); if (image_writer_) image_writer_->shutdown(); if (roatation_writer_) roatation_writer_->shutdown(); + if (imu_writer_) imu_writer_->shutdown(); } const std::filesystem::path& root_dir() const { return root_dir_; } @@ -104,6 +106,9 @@ public: void enqueueRotateFrame(std::vector&& blob) { if (roatation_writer_) roatation_writer_->enqueue(std::move(blob)); } + void enqueueIMUFrame(std::vector&& blob) { + if (imu_writer_) imu_writer_->enqueue(std::move(blob)); + } private: struct Writer { @@ -194,4 +199,5 @@ private: std::unique_ptr cloud_writer_; std::unique_ptr image_writer_; std::unique_ptr roatation_writer_; + std::unique_ptr imu_writer_; }; diff --git a/include/host_sdk_sample.h b/include/host_sdk_sample.h index c6c92bb..5909823 100644 --- a/include/host_sdk_sample.h +++ b/include/host_sdk_sample.h @@ -70,6 +70,9 @@ enum class OdometryType { extern int g_log_level; extern int g_sendcloudrender; +extern int g_use_host_ros_time; +double get_ptp_smoothed_delay(); +double get_ptp_smoothed_offset(); #ifdef ROS2 #include "rclcpp/rclcpp.hpp" @@ -177,15 +180,40 @@ inline uint64_t ros_time_to_ns(const ros::Time &t) { #endif } +inline ros::Time make_aligned_stamp(uint64_t sensor_timestamp_ns +#ifdef ROS2 + , const rclcpp::Node::SharedPtr& node +#endif + ) { + if (g_use_host_ros_time == 1) { + #ifdef ROS2 + return node->now(); + #else + return ros::Time::now(); + #endif + } + + uint64_t ts_ns = sensor_timestamp_ns; + if (g_use_host_ros_time == 2) { + const double offset_s = get_ptp_smoothed_offset(); + const int64_t offset_ns = static_cast(offset_s * 1e9); + const int64_t base_ns = static_cast(sensor_timestamp_ns); + const int64_t aligned_ns = base_ns - offset_ns; + ts_ns = (aligned_ns < 0) ? 0ULL : static_cast(aligned_ns); + } + + return ns_to_ros_time(ts_ns); +} + class RosNodeControlInterface { public: virtual ~RosNodeControlInterface() = default; virtual void setDtofSubframeODR(int odr) = 0; virtual int getDtofSubframeODR() const = 0; - virtual void setUseHostRosTime(bool use_host_ros_time) = 0; - virtual bool useHostRosTime() const = 0; virtual void setSendOdomBaseLinkTF(bool send_odom_baselink_tf) = 0; virtual bool sendOdomBaseLinkTF() const = 0; + virtual void setCloudRawConfidenceThreshold(int threshold) = 0; + virtual int cloudRawConfidenceThreshold() const = 0; }; RosNodeControlInterface* getRosNodeControl(); @@ -241,15 +269,11 @@ public: ros::Imu imu_msg; #endif - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - imu_msg.header.stamp = node_->now(); - #else - imu_msg.header.stamp = ros::Time::now(); - #endif - } else { - imu_msg.header.stamp = ns_to_ros_time(stream->stamp); - } + #ifdef ROS2 + imu_msg.header.stamp = make_aligned_stamp(stream->stamp, node_); + #else + imu_msg.header.stamp = make_aligned_stamp(stream->stamp); + #endif imu_msg.header.frame_id = "imu_link"; imu_msg.linear_acceleration.y = -1 * stream->accel_x; @@ -270,6 +294,31 @@ public: #else imu_pub_.publish(imu_msg); #endif + + if(data_logger_) { + const double ts_sec = static_cast(stream->stamp) / 1e9; + float ax = imu_msg.linear_acceleration.x; + float ay = imu_msg.linear_acceleration.y; + float az = imu_msg.linear_acceleration.z; + float wx = imu_msg.angular_velocity.x; + float wy = imu_msg.angular_velocity.y; + float wz = imu_msg.angular_velocity.z; + + std::vector blob; + blob.reserve(sizeof(double) + sizeof(float) * 6); + auto append_pod = [&](const auto& v) { + const uint8_t* p = reinterpret_cast(&v); + blob.insert(blob.end(), p, p + sizeof(v)); + }; + append_pod(ts_sec); + append_pod(ax); + append_pod(ay); + append_pod(az); + append_pod(wx); + append_pod(wy); + append_pod(wz); + data_logger_->enqueueIMUFrame(std::move(blob)); + } } #ifdef ROS2 using ImageMsg = sensor_msgs::msg::Image; @@ -550,15 +599,11 @@ void publishIntensityCloud(capture_Image_List_t* stream, int idx) // Set message header msg->header.frame_id = "odin1_base_link"; - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - msg->header.stamp = node_->now(); - #else - msg->header.stamp = ros::Time::now(); - #endif - } else { - msg->header.stamp = ns_to_ros_time(cloud.timestamp); - } + #ifdef ROS2 + msg->header.stamp = make_aligned_stamp(cloud.timestamp, node_); + #else + msg->header.stamp = make_aligned_stamp(cloud.timestamp); + #endif msg->height = cloud.height; msg->width = cloud.width; @@ -596,9 +641,9 @@ void publishIntensityCloud(capture_Image_List_t* stream, int idx) uint8_t* intensity_data = static_cast(stream->imageList[2].pAddr); uint16_t* confidence_data = static_cast(stream->imageList[3].pAddr); - + int confidence_threshold = getRosNodeControl()->cloudRawConfidenceThreshold(); for (int i = 0; i < total_points; ++i) { - if (confidence_data[i] < 35) { + if (confidence_data[i] < confidence_threshold) { *iter_x = 0.0f; ++iter_x; *iter_y = 0.0f; ++iter_y; *iter_z = 0.0f; ++iter_z; @@ -647,11 +692,10 @@ void publishIntensityCloud(capture_Image_List_t* stream, int idx) } } - { + // Only cache point cloud if cloud_render is enabled + if (g_sendcloudrender) { std::lock_guard lock(pcd_queue_mutex_); - // Get actual point count - const int real_point_count = cloud.width * cloud.height; // Create deep copy of point cloud #ifdef ROS2 auto msg_copy = std::make_shared(*msg); @@ -679,15 +723,11 @@ void publishIntensityCloud(capture_Image_List_t* stream, int idx) void publishGrayUInt8(capture_Image_List_t *stream, int idx) { ImageMsg msg; - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - msg.header.stamp = node_->now(); - #else - msg.header.stamp = ros::Time::now(); - #endif - } else { - msg.header.stamp = ns_to_ros_time(stream->imageList[idx].timestamp); - } + #ifdef ROS2 + msg.header.stamp = make_aligned_stamp(stream->imageList[idx].timestamp, node_); + #else + msg.header.stamp = make_aligned_stamp(stream->imageList[idx].timestamp); + #endif msg.header.frame_id = "map"; int width = stream->imageList[idx].width; @@ -731,15 +771,11 @@ void publishRgb(capture_Image_List_t *stream) { cv::Mat decoded_image = cv::imdecode(jpeg_data, cv::IMREAD_COLOR); cv_bridge::CvImage cv_image; - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - cv_image.header.stamp = node_->now(); - #else - cv_image.header.stamp = ros::Time::now(); - #endif - } else { - cv_image.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp); - } + #ifdef ROS2 + cv_image.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp, node_); + #else + cv_image.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp); + #endif cv_image.encoding = "bgr8"; cv_image.image = decoded_image; @@ -775,15 +811,11 @@ void publishRgb(capture_Image_List_t *stream) { if (m_undistort_map_init_success) { cv::remap(decoded_image, undistorted_image, m_undistort_map_x, m_undistort_map_y, cv::INTER_LINEAR); - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - cv_undistorted_image.header.stamp = node_->now(); - #else - cv_undistorted_image.header.stamp = ros::Time::now(); - #endif - } else { - cv_undistorted_image.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp); - } + #ifdef ROS2 + cv_undistorted_image.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp, node_); + #else + cv_undistorted_image.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp); + #endif cv_undistorted_image.encoding = "bgr8"; cv_undistorted_image.image = undistorted_image; } @@ -795,13 +827,9 @@ void publishRgb(capture_Image_List_t *stream) { undistort_rgb_pub_->publish(*cv_undistorted_image.toImageMsg()); } - // original jpeg + // original jpeg - always publish as it's small sensor_msgs::msg::CompressedImage jpeg_msg; - if (getRosNodeControl()->useHostRosTime()) { - jpeg_msg.header.stamp = node_->now(); - } else { - jpeg_msg.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp); - } + jpeg_msg.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp, node_); jpeg_msg.format = "jpeg"; jpeg_msg.data = jpeg_data; @@ -816,11 +844,7 @@ void publishRgb(capture_Image_List_t *stream) { // original jpeg sensor_msgs::CompressedImagePtr jpeg_msg(new sensor_msgs::CompressedImage()); - if (getRosNodeControl()->useHostRosTime()) { - jpeg_msg->header.stamp = ros::Time::now(); - } else { - jpeg_msg->header.stamp = ns_to_ros_time(stream->imageList[0].timestamp); - } + jpeg_msg->header.stamp = make_aligned_stamp(stream->imageList[0].timestamp); jpeg_msg->format = "jpeg"; jpeg_msg->data = jpeg_data; @@ -837,11 +861,7 @@ void publishRgb(capture_Image_List_t *stream) { #ifdef ROS2 sensor_msgs::msg::PointCloud2 msg; msg.header.frame_id = "odom"; - if (getRosNodeControl()->useHostRosTime()) { - msg.header.stamp = node_->now(); - } else { - msg.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp); - } + msg.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp, node_); //RCLCPP_INFO(rclcpp::get_logger("device_cb"), "Point cloudrgba %ld",stream->imageList[0].timestamp); @@ -869,11 +889,7 @@ void publishRgb(capture_Image_List_t *stream) { #else sensor_msgs::PointCloud2 msg; msg.header.frame_id = "odom"; - if (getRosNodeControl()->useHostRosTime()) { - msg.header.stamp = ros::Time::now(); - } else { - msg.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp); - } + msg.header.stamp = make_aligned_stamp(stream->imageList[0].timestamp); size_t pt_size = sizeof(int32_t) * 3 + sizeof(int32_t) * 4; uint32_t points = stream->imageList[idx].length / pt_size; @@ -1098,15 +1114,11 @@ void publishRgb(capture_Image_List_t *stream) { if (data_len == sizeof(ros_odom_convert_complete_t)) { ros_odom_convert_complete_t* odom_data = (ros_odom_convert_complete_t*)stream->imageList[0].pAddr; - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - msg.header.stamp = node_->now(); - #else - msg.header.stamp = ros::Time::now(); - #endif - } else { - msg.header.stamp = ns_to_ros_time(odom_data->timestamp_ns); - } + #ifdef ROS2 + msg.header.stamp = make_aligned_stamp(odom_data->timestamp_ns, node_); + #else + msg.header.stamp = make_aligned_stamp(odom_data->timestamp_ns); + #endif msg.pose.pose.position.x = static_cast(odom_data->pos[0]) / 1e6; msg.pose.pose.position.y = static_cast(odom_data->pos[1]) / 1e6; @@ -1160,15 +1172,11 @@ void publishRgb(capture_Image_List_t *stream) { } else if (data_len == sizeof(ros2_odom_convert_t)) { ros2_odom_convert_t* odom_data = (ros2_odom_convert_t*)stream->imageList[0].pAddr; - if (getRosNodeControl()->useHostRosTime()) { - #ifdef ROS2 - msg.header.stamp = node_->now(); - #else - msg.header.stamp = ros::Time::now(); - #endif - } else { - msg.header.stamp = ns_to_ros_time(odom_data->timestamp_ns); - } + #ifdef ROS2 + msg.header.stamp = make_aligned_stamp(odom_data->timestamp_ns, node_); + #else + msg.header.stamp = make_aligned_stamp(odom_data->timestamp_ns); + #endif msg.pose.pose.position.x = static_cast(odom_data->pos[0]) / 1e6; msg.pose.pose.position.y = static_cast(odom_data->pos[1]) / 1e6; @@ -1186,11 +1194,7 @@ void publishRgb(capture_Image_List_t *stream) { { if (getRosNodeControl()->sendOdomBaseLinkTF()) { geometry_msgs::msg::TransformStamped transformStamped; - if (getRosNodeControl()->useHostRosTime()) { - transformStamped.header.stamp = node_->now(); - } else { - transformStamped.header.stamp = msg.header.stamp; - } + transformStamped.header.stamp = msg.header.stamp; transformStamped.header.frame_id = "odom"; transformStamped.child_frame_id = "odin1_base_link"; transformStamped.transform.translation.x = msg.pose.pose.position.x; @@ -1266,11 +1270,7 @@ void publishRgb(capture_Image_List_t *stream) { case OdometryType::TRANSFORM: { geometry_msgs::msg::TransformStamped transformStamped; - if (getRosNodeControl()->useHostRosTime()) { - transformStamped.header.stamp = node_->now(); - } else { - transformStamped.header.stamp = msg.header.stamp; - } + transformStamped.header.stamp = msg.header.stamp; transformStamped.header.frame_id = "odom"; transformStamped.child_frame_id = "map"; transformStamped.transform.translation.x = msg.pose.pose.position.x; @@ -1290,11 +1290,7 @@ void publishRgb(capture_Image_List_t *stream) { { if (getRosNodeControl()->sendOdomBaseLinkTF()) { geometry_msgs::TransformStamped transformStamped; - if (getRosNodeControl()->useHostRosTime()) { - transformStamped.header.stamp = ros::Time::now(); - } else { - transformStamped.header.stamp = msg.header.stamp; - } + transformStamped.header.stamp = msg.header.stamp; transformStamped.header.frame_id = "odom"; transformStamped.child_frame_id = "odin1_base_link"; transformStamped.transform.translation.x = msg.pose.pose.position.x; @@ -1371,11 +1367,7 @@ void publishRgb(capture_Image_List_t *stream) { case OdometryType::TRANSFORM: { geometry_msgs::TransformStamped transformStamped; - if (getRosNodeControl()->useHostRosTime()) { - transformStamped.header.stamp = ros::Time::now(); - } else { - transformStamped.header.stamp = msg.header.stamp; - } + transformStamped.header.stamp = msg.header.stamp; transformStamped.header.frame_id = "odom"; transformStamped.child_frame_id = "map"; transformStamped.transform.translation.x = msg.pose.pose.position.x; @@ -1592,22 +1584,28 @@ private: void initialize_publishers() { #ifdef ROS2 - auto qos_profile = rclcpp::QoS(1) + // Small data with queue depth 1 + auto qos_small = rclcpp::QoS(1) .reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE) .durability(RMW_QOS_POLICY_DURABILITY_VOLATILE); - imu_pub_ = node_->create_publisher("odin1/imu", qos_profile); - rgb_pub_ = node_->create_publisher("odin1/image", qos_profile); - cloud_pub_ = node_->create_publisher("odin1/cloud_raw", qos_profile); - xyzrgbacloud_pub_ = node_->create_publisher("odin1/cloud_slam", qos_profile); - odom_publisher_ = node_->create_publisher("odin1/odometry", qos_profile); - odom_highfreq_publisher_ = node_->create_publisher("odin1/odometry_highfreq", qos_profile); - path_publisher_ = node_->create_publisher("odin1/path", qos_profile); - pub_camera_pose_visual_ = node_->create_publisher("odin1/camera_pose_visual", qos_profile); - rgbcloud_pub_ = node_->create_publisher("odin1/cloud_render", qos_profile); - compressed_rgb_pub_ = node_->create_publisher("odin1/image/compressed", qos_profile); - undistort_rgb_pub_ = node_->create_publisher("odin1/image/undistorted", qos_profile); - intensity_gray_pub_ = node_->create_publisher("odin1/image/intensity_gray", qos_profile); + // Large sensor data with larger queue to avoid blocking + auto qos_sensor = rclcpp::QoS(10) + .reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE) + .durability(RMW_QOS_POLICY_DURABILITY_VOLATILE); + + imu_pub_ = node_->create_publisher("odin1/imu", qos_small); + rgb_pub_ = node_->create_publisher("odin1/image", qos_sensor); + cloud_pub_ = node_->create_publisher("odin1/cloud_raw", qos_sensor); + xyzrgbacloud_pub_ = node_->create_publisher("odin1/cloud_slam", qos_sensor); + odom_publisher_ = node_->create_publisher("odin1/odometry", qos_small); + odom_highfreq_publisher_ = node_->create_publisher("odin1/odometry_highfreq", qos_small); + path_publisher_ = node_->create_publisher("odin1/path", qos_sensor); + pub_camera_pose_visual_ = node_->create_publisher("odin1/camera_pose_visual", qos_sensor); + rgbcloud_pub_ = node_->create_publisher("odin1/cloud_render", qos_sensor); + compressed_rgb_pub_ = node_->create_publisher("odin1/image/compressed", qos_small); + undistort_rgb_pub_ = node_->create_publisher("odin1/image/undistorted", qos_sensor); + intensity_gray_pub_ = node_->create_publisher("odin1/image/intensity_gray", qos_sensor); tf_broadcaster = std::make_unique(node_); #endif } diff --git a/include/lidar_api.h b/include/lidar_api.h index c7bc9c1..21268a0 100644 --- a/include/lidar_api.h +++ b/include/lidar_api.h @@ -128,7 +128,6 @@ int lidar_set_mode(device_handle device, int mode); * * @param device Handle to the target device * @param type Type of data stream to start (see stream type definitions in lidar_api_type.h) - * @param dtof_subframe_odr DTOF subframe ODR from device, used for raw point cloud per-point time offset estimation * @return int 0 on success, negative error code on failure */ int lidar_start_stream(device_handle device, int type, uint32_t &dtof_subframe_odr); @@ -255,6 +254,18 @@ int lidar_get_custom_parameter(device_handle device, const char* param_name, int */ int lidar_enable_encrypted_device_log(device_handle device, const char* dest_dir); + +/** + * @brief Set the depth parameters for the device + * + * This function must be called before starting data stream. + * + * @param device Handle to the target device + * @param params Pointer to the depth parameters to set + * @return int 0 on success, negative error code on failure + */ +int lidar_set_depth_parameter(device_handle device, const lidar_depth_para_t *params); + #ifdef __cplusplus } #endif diff --git a/include/lidar_api_type.h b/include/lidar_api_type.h index 7b4cc61..4d127ae 100644 --- a/include/lidar_api_type.h +++ b/include/lidar_api_type.h @@ -56,7 +56,8 @@ typedef enum { LIDAR_DT_DEV_STATUS, LIDAR_DT_SLAM_ODOMETRY_HIGHFREQ, LIDAR_DT_SLAM_ODOMETRY_TF, - LIDAR_DT_SLAM_WIWC + LIDAR_DT_SLAM_WIWC, + LIDAR_DT_NTP } lidar_data_type_e; typedef struct { @@ -117,6 +118,11 @@ typedef struct { uint32_t height; } buffer_List_t; +typedef struct { + double delay; + double offset; +} ptp_sync_data_t; + typedef struct capture_Image_List_t { uint32_t imageCount; buffer_List_t imageList[DEVICE_MAX_CH_NUMBER]; @@ -220,6 +226,15 @@ typedef enum { LIDAR_DEVICE_STREAM_STOPPED, } lidar_device_initial_state_e; +typedef enum { + LIDAR_DEPTH_ODR_10HZ = 0, + LIDAR_DEPTH_ODR_14_5HZ, +} lidar_depth_odr_e; + +typedef struct { + lidar_depth_odr_e odr; +} lidar_depth_para_t; + #ifdef __cplusplus } #endif diff --git a/launch_ROS1/odin1_ros1.launch b/launch_ROS1/odin1_ros1.launch index da14c97..e9790f2 100644 --- a/launch_ROS1/odin1_ros1.launch +++ b/launch_ROS1/odin1_ros1.launch @@ -21,6 +21,11 @@ + + + + + diff --git a/launch_ROS2/odin1_ros2.launch.py b/launch_ROS2/odin1_ros2.launch.py index 34224f0..af0f4cd 100644 --- a/launch_ROS2/odin1_ros2.launch.py +++ b/launch_ROS2/odin1_ros2.launch.py @@ -50,6 +50,21 @@ def generate_launch_description(): output='screen', parameters=[pcd2depth_params] ) + + # Cloud reprojection node + reprojection_config_path = os.path.join(package_dir, 'config', 'control_command.yaml') + with open(reprojection_config_path, 'r') as f: + reprojection_params = yaml.safe_load(f) + reprojection_calib_path = os.path.join(package_dir, 'config', 'calib.yaml') + reprojection_params['calib_file_path'] = reprojection_calib_path + cloud_reprojection_node = Node( + package='odin_ros_driver', + executable='cloud_reprojection_ros2_node', + name='cloud_reprojection_ros2_node', + output='screen', + parameters=[reprojection_params] + ) + # Create RViz2 node - loads specified configuration file rviz_node = Node( package='rviz2', @@ -65,6 +80,7 @@ def generate_launch_description(): ld.add_action(rviz_config_arg) # Add RViz configuration argument ld.add_action(host_sdk_node) ld.add_action(pcd2depth_node) + ld.add_action(cloud_reprojection_node) ld.add_action(rviz_node) # Add RViz node return ld diff --git a/lib/liblydHostApi_amd.a b/lib/liblydHostApi_amd.a index 8660301fbfd218e8828b1be9db171c9feecb8164..9a258881915ecf032afbec854957708da2204568 100644 GIT binary patch delta 80889 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 delta 72776 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

5I9SQE=tG zYRtXydd#DW_yD-2m^&=y1b$P(4(R%R{HHM;~U=T@S0hE$C1DEg5Ac%%0A6 zK61p4GGR!$Bia-Awsp*jy9J$k-*TIW$Ff}*YW`{!m@!f8b7G5y`}#!F&z?gqI9r&9 zW0;`-2r6wI&jjU1t$eSdGxxlF-VO+K=Yt<`_m0!}@Z(&305pC%T0wV0_~@uvK{xF< zj@#qV?L{#1N_;YCVh=tvo9<|{&Scki<+yW4AO5|X8ZE8BI6-Kp(hw4#|S<7&|cg=ZlQKKjAau>GPxS8iLi{meNa2L;7<*xa3^U2NN72 z!?G6`{-F-9Vb7SMzixD%U!7s$<_Ne62HzfMa3hSz>q#Kw&$R=gJQjcFRFF+ZdI#6hjEk5=c z=cw|=<0d|Ao#zdhUEZ7 z_)Pj)`p!6sY<~eWW;wig%nF^051hw9He!Wu-iHs`M@Jow@X3{CI9)8G zpl<&oHs*<;TTTTh(MMyyxdG{Y+Ms(#OztrQnWy%i^FzzQZBc&76W#nO*!u>y;)KJ5VJECnxNp~_6P62iy14m- zHET$yk=2M>)Ij$M3)|-_{C}IxC#*0s623ZNtsXK=lTN}-q#pjtN+ZGW!B^IV;UkTU zORy{AYM{Iwmr+ChZjA}yd(0HG9*SL}(X1f-&p01|>;7)tNnU{?T^8;f)@4PJt@LvU ztnae&$TIr5Xxu2|BNRK2iDkRkI40*3qpuNn6F82?_+)sv{cCH|$kX`LbhQ__K8F{- zwyrl%1W^|7k!Lagu&x+dnqwp$%yZ2qQ1TD!QuE*%c>N#NAI$bOaN9qv*GY*d;iPqg zX~sMR+rPDL9=i4s9BH^M!(*`HTkC4b`IlvxF=cS$zpQO$Srjb$#+qsyqm8U6nAB}8 zCMmG7+q%r%}Yws^!F^+d|zT*fEkN^z- zrv;9R-p#n@gd=K+ukiW(9hYJwxdQPK+lL=u`-gRZ zXIT^fosZLRS?r1WiXT|L!x7{zKjQ`AT^Qlo2PsJqpNacAZTjBI30hlm;dAT*;Op;k zMdb%Wd#oFRww7P0(GDdjGGi*>s~+n&X3S%7{SVd#bJv6L{ttM$2bd$>b*Bb=Povri zx~W*W_ZNlnnlUA45#fV-ILJLCCc$ez z;>mc|7SEzyYbr5!xiOJ&PkEnJi2JPl3Won|ZNnZ~n~9rKu}$L|e#V)4?OLh@xWSI) z$>_ImceSmLGrQt5Ru#5>_2c38&F0qX#~-kt9J=-?vGWVevFtIBM(kF)cUL4NnRc~# zVh3#VvqyV+OgkBOv8sjXmi@GO_!+i|mgl5pAH$QxPWZEz{ieC~dDu3@-eYdv1(!JO zXU(<0gU_AzPdMPs@8Pfn?1=)W#?N{Ls!DtclKQK4Mehi16eq^9AeKMI&Np|x3CG9KXb!@fDC8^V0N=JOGTO%T=%4sOeYCyBJaGVKj-?bF zgwYvj{_a?Nv$^YaST@eyZti-6ZAbRXcstKD4WFUZnm@HT+xW#=kRXx zxnbsA=OV1x=OXa)rTFfiMPO9@$3Cd zxb_)LG9XQ##-#!8m=)jS#$7*;v*2GR@EkaKsT~~Lj=Q_*7IBHNVyQjh5*;s_$vqv% z?Ng2~$IX!(xQ)tUEEm`nCgmEptm}9Su}k5_rFQt-=n;4(7a7+?8Cm^423US?0kjSDAVW{)HDU>O!g zxD8Clb65#B{#YxecQqPEMH^Yi5i^~*!u>s#xXsb==dsZXjoqfv``8$lF}wYFSD(u; z{xW8raq8UhKKx8~)7cPcdUw)3akl(7&~Xzb&^HiH&8$ac{qne^*711D0i=+OE~~ z|#M#w$Er8wWOsB;#q$+YL1TXU$Z@Hi#;@A30s z((ryJ4v__5uCOl}3_WNdZH#ID3XI9lV<+UA750=lI62VHctHpKcb!q}rPi_B)rXhU zdyc;h-dETWL$QyG!>L!`ei+Yr7F}Vt+3LN>fo_nsbud_T!*V~Up`0tH0 z+J?8|h0Wf8Vh0}V^n7}kP00LpJ(gkg*j8Dv(c)!w2O5<_thylFC8!FA>&~H8WiZ_h zXbVcct>z%3+Gj7`|7{JVNP8gF-{~2BkNvi@h<<;OgZ}9Y15~!gp}XD8r`z2ur2E!f z>;3s>gIlYlJmok4N&hEC-ijNre7c(Ng|pF{9;6I2GjOMp6e>k6_E4xs;lB-I!yRkt z$&TQsgHqjrl>zhN)-86liT(SrE%rA080oF{w4nzuH~Lxy*KDfDhQ0ka#$Zhh!a`c>qre=-@&7BGLet)1njZdeB~t56b@dL3;r{ z_Gwfp7R``bYR|)`tJId-ACPvq<01QB_=xJYcvGHwc^Ioel}FI01y4MJK4amvN3i-W zp0G0eIYJK~wWpD6_~udjq9Xb&L;6q_tr3Y=-z@T!;WdsOiBHd%4^ykAEPgzG`?M$G zp1df#GQmAPrFzm+lO1=F)(_x4>zq)I!gUaRMbd77ukp}9wOo+-IBrz1h!tU1dW)@AikS{0m43R7Yc=r# zA0qyQ9RXR7lfbaQ3k#RXbPG2$hJinBM?=Q&B9#@;@}wO~Rf$<3)yamkx@T zi`%vhf+qT81&XK9#Sgv`h5jyq7hlZrG+r3T1l}ZY+Uw}c+n4z%GSX&pK;}N53k>f&LE1BaK_8$5=gSa36i0^O~h&ORS$`i=ND!*YH4iS@W5)>9X^51Jsgv@6!(D(RO zLL=BFK}+W0S25IO#yalukeE-G{Q`cSz(dz_{m4LGKEQV-dXyK>^5T!Y=-|Z=x~WhE zB<{d&U%)pQ`W-L+!HWreXQ519Z05z&y!Z<*j`8AOyfFD*Lijx+?j-~nPvhX($UY~O z;M_9S;Q#S~26&7|X>>6>S{xZhoM7-ezM0Vdym0ek2P+`zSsd5h9J-k8HWYRR&P|lH z>_@n#PSJPq=gf37qAFh0ga30#_-pJa;m>FjZHTXO&}@+iA~1~Nu>&EAY0tPGHd5;c zS>diOaeC^Wvpb#O`yFoPadfYJvD=!m)>H9<{Q&X0W$i6D7!_0tFZ{SHQ zA37TC#bNm(s#Y<^=`XXegVTrb9>l4-TZlUqF?X^N#bgFNbddSF>rGbm!8g&SUf7h0 zjz+&_qAu~#g;YF4h^rJacNzT_^W}d?i7Qa>F5}9Fjfc=lzG&6)ikLg8MKQ+p>LKQ9 z_hA;-sl#X!FKk+cjf^WEqTg2HYFEVEW#-$=SIRp|T=I-4=c)orI*9O?vqTj{Ce3<>NJuR$T1lAyCGZ`2@mjkti76`)d$LV+o4e#2110=R6 zCafM$$Gi5AB+Mz=D^*NRGR%B9^%q=!I3c>3vM4-fXNyYf12lmQS#5|c26eGz` zG}|AcuZ{1qTpoH4bCDIxt^NjcIf7bYjl!keMn#GnfxM4m**E${ja1BQOnBe^Xu$b; zF>eoM75!J1RpwuntO_-)3@D-E3=u+^BIb4MLosIc?q69}wJj{GS6h^tOMyi#Frycszweb1qD2BQKhYzITe?`DSbA;+|68bWHZjIXu06AudfcYy-|33}2>KKs zDaZ(PpM5+SH;}6R9Q#qBP@^41?VWg{N3r{4ofC??h~=9u8lD#T0Bocb>bi&z8374P z@JE>0pW!*S4FRZ2z)p($6re^*vlg4|9xWO!$!gW`I!O4M1gus0Zx8cty1`3X-c3sduG6IM=tp~t^i$6DR}M_RIfADu z-I2g+1g>|}or=CnW+Nxlw@JtunUJ{@`%Y;)&aqtr*IDf4EIP%dw9cXrR2)YZ{en4* zfp8EH6CyQj$zqg-%k0O)#ttMuR)}@d;xt^E#cS?D1vg2WzQkRMzRFE1$N7JqQ3ct- z?)xf(WAcO@JoX6JL5y z_7k{%@`)EXYZSP=69;`{r|lD#A#lAlvLFR@%dT`zFG zPv;9?an9-cAtBH^ zc`NuMQDL1ztkc@XX+0@u?Pjz>f4b!pL&vb>pZ(|-s*&0pG+DVt?u-2)0 zr?gw(I;A}vzmU>it%71POyr$H=}6*EaR*WFtv&*$`YNT70;iN^819EFz0J4?KAR*( z@Yy6g!u{v{;jdY0>A5(}EH?4KtHHQAzRXBF7()IrJubTF8$P>@8864M-F~xhpb2vbT=w;b%Hvi_*3zAfzxJCx#{NI zgc(ts6q%2t$by4Ccn)<&v2&=ek5J>xl&tzU%!F#5WJZw!*O`eDxa_l%neiGf{luQ{ zf2q+Vnj~H`ka&_}pi+{o=rbJleQyWEsN*9?sOYTWcLc~vSh3<+?N?hkX{DUBqB5bz zNHYdsm72NCyh_7m=G6k%8LSbw&frdt51u8ZqdKjEG%;`0YmOw|py;a%9#HgE{FuNw zZOX#qB;zfiU-75%hF|iqmrR5o;oygq(|Ds3_cce>6a0jh?5dKsKn<7KhC(e`7e#8$ zrCF4QOSAEsyEZZM;xv7UCo1|X8A%);&F*=t0+%qL&AA>9UXL3PQo`<)S!0xrSxuMdlum)m zP9bqWMPH>gkmDC#Ek=!{SBv3s!j96Wj`jGlY-JLjB;>4FL`t+s9SfiD!ZXCE9BasA zo%1}!Nag&{SoSayvv({kyBdEy+n5EU~z6@$_ zz{gW(?e%jvkLOejyutWCkiQbs-DhW6WHK3vnKFs-fYE-x*S<0a7lQ)_`Wbl!{ua{r{PKBi@J3J zr!1-d*K?fTHc&@rzA%t2Dl6Wk;j-ca4VM)cD!A&uM&O(npTW+sS1{AanSsKaEN2lV zR}H&KSnISjYq*TAMZ?z#T3Q9J)6%BsW7IbrGem&Lgg~dIUBhL?ofPE0#?a)19${Y^!r)pulzW*jN~TJDxL2O?;|j=%8Z|RH zEDvdTzVO$i;c_!He269nQ`D-~h9=Ton}$nw$245J>r$#wTlJ)(ui`$FSbSdW-fcRh z+=^=|or$PP{A#f9R^0WE(=p1%$Mn%FzoKOBVBkwE8MsT}`h<}x@C&-H=4YV$3S6HU z#D6%yaH*cBIm&SI7Fe%UEb#(GUuC9H(O2<0fnP9R?&ma_O_SJ}1p4zWXEfzfwQkeB z!l^JbfPJUXAOh`HROmR9cp&6@;fxeDna@c0pmRLTO`puulZ_@G*JETUxEjDJfm8lf zJV(K4t4s%f_?1}fgq(#G%BPYuO~=0#qlkPNxEC>}SjrHE3cP?*Ro@>pvuLy(=^cT;?qqZq{(=zlG!cyrNccOc=Vb48#>XR z6$`m|G$~H#%XpG}xP839WjwfR@^G&JUo{oHGm0J(k~K|9L8^vJ&v+l(59=@FL6t!X zji#K(R>@Ju=(IW!YkD2%|otZ@my1{)hnB7zlP^?E2BZfai(Ig zLrP^VZ8$PQ8?>!c_@Hg)B|fI()JVoKp+zE;V4`73Q@KCMq^Qki^+*km7ptQv*cXgv;#eWpnT*qL=`LQwrMqN-Gr}PHUfe^FBJ#Gys(5}unio&M zGd2t2;r*%j)p9)hCLI5dM@KAf3Ak7IpxhKf$Gvzjar{eu;fGY1<<8%lxb>VKAs=8} z=*Z__>1UkK1g^lE01}GuR&87uZrG6|aKN`_>8lcFzhFIGh<91O)X zVWp5$=GKe4X;Ce3%AAVtgxYYt6x)x4;FrNr8jklH3h~YCPGGk=%-#fkCn7cuAHkcw z*T)Q;&TeU#anoTq{r)&@I*i6IbIlAqrnfBC-;dopDZaoa;(`t#y;TREFV)9HbId&*8{=y5Dzt*fs`6CbF&*Ewh1o!lI^+kepC3H(vq6CiB z;QpY*e^<4yheMon`l9MUiMyz}Z+IK?m^TNx3_9nOypKS>h zF30*dIWlkdSIcv5rmN-U&~Y6KTzfO!0)~f@F1YjN&EC?ZqKAS9IT(Ks;)3Tn7+)!a z!NI^S@Nf7)%g8O1!*9Uw>~-O5_vJIPgQMz}-fz5ud#YRD>c>KRi`mN#l_BPKj11r6 zr_uu>Y?t1YF*M5DPMuS?(9}9@@R0P)Xee=B{C%5zBv zGk2Oj$CdH7?TF!g$rC3__MOCva;oy7F-~Uu>8TQA}zZL!cVR+^hRJ}F!mu)!7e4V-Niok6Y zCrU}4kl~Uiley$?y&`bTXNZ~rH26PxXMvSGPx))fV{rBzc#>TU=iiRItvdFygSiDV zZorwa>oz*bo9M=ktL%nwBzHii)jAHs2@FULvDr>lBg~~)BEzWRGQh@|TRF#>TRA6) zZ#~MJGzAo~Y||!@@U-&Rvdt=g%~zPy2?b~G6h+Mm~!@Z>XgWHcs^8Bi04B~93ZMtxO@If zGX_8zI%e4(2|&k<*kPGvyEQ+c#*1;n>rLyoBcH^An& zLWQOP;g!rUokH-dcjkUh5J7NR%bdjJdwHC_MJ1|M0i9(|?!EBp^PxAu+B?J1-Tmz8 zHGm7xk%J>VPQU1DX z(!|4bZy~-r2HRKlu5dKCT1LlMYtZ~IH>^&DBA4~LcEZ^^@vN^;H92w6zln0+$U%ax zciNPF{GdOGoAL`-{z@p*UA_q}9HeUlD+m4Na$V6Oim{zo!rXTve7y?yrMk-+4*4x4 zH_2`yyi}kCvJT*e)Ov^x%G;mP$XqrZ$v;ed z>!vVb5|z4a_%~`2NdaSqi+`HA-9%QHle&D@{t>8UgUOWRki>td! z-{WHVU;r}uaJVd$grkt?!+Rn8v;*%%zm(Q4!7&vP%gT@Mah{D<9JWZ)sTVpg6gl~4#rB5gkrP>+skRN^uP(%!akFU^5P*J2{6EB zE5L{sAT`S74FOogF~g;PCQV0G|Mq$-Wl`k3L;^l|2{8;?UVsGBMzbdp`9 zQ4%_3Qf};EjKA<%<*!wqQ~t=W4e$l7z$zQ;1eST&sfqUyT(}KKTK-|LCVuHyaXSiC zdNj&{x;?FFJO;2fQQWc z58~ZKL+>RaMcAb%tq(I7b%c*Fca?vUT>a;y;(F(mhJ$#xflo0!jjhOCG+8CV5}bF9 z9C$I5`60WptZ|}>PyW(C&IR8OB7CA0D;j*XF&p-$I*;Ql_mjUL|KH+1P zeuLUQ&UV@|P7kP?azu(rlUjC9Qzo_ePa7`&^EOYEDzkX0|80RJ8BK z4LqA!cuzR5H)kkPys9FeQxR*-?FO9#%@x?L*d5ZYnsVHD%I=_0+p~Dh5Y4+Q*-XbM z72o_T9b3&VGG?Ts;)c6t|GB?f=6}bfs`;r5ZVzj6)Ht$#5A83FdY~OER}AwHPjWT8VF7*)QVr zt#%V2yom z?fy1im>I$c3gwm_p|R&CbloTAaH0yg(>R-5p8%EoA@W+8)5fN}%d(@xPG##a@LoGfG z29M(1@a|`Q!O^4e$S1I${_=OB%X9S)QRk0R7T;a4|0TSKCwnLg^)I2Ib4T$CLhOD# z1n-0Q-;Y=2bMPsH*Hv+^x*qITRj6j}4-4R5oxl5`BlK6}iPH=4YBlzgJBHtv){ZK{ z%3W}^dyYk*8V)^$4-g3PIPeLTUrsgM6q#S`&oy?pu%D|?HJth`HWN?xcNa6ZL#Cg( z^qFKHz;t--cHJP`?GgPbbH81~I~(!s;%-Jy%g9p2=^-*{iWC1S#kGH0<#G9C(wsFs zwui5#%&9~=VBT;H@PBK}yc(91= zR>u+(Ck-rTPWqmGSse+AW>xGWRY`J_s!dAqj~g!j^(v3nzCrmTf9uAvP#+ttW`j<% zy}@y6@`=f9@=jlxcO&hL@^_NUGcf0rzvioO!W9=e=DxVVOMew{T&q6jkGFH-HE;_%m#G^|rW@9Yq((QN>)wtng|m#ssOjDwmt!J!(xl(W}nz zBp)RZ>2p_9saksc+VdX#Wk_ZX z`Ab$2C}KE1N*di$V$No>6q@8x^Hf-o(y2iCYaUbnSE9dsnOUg(6?YHnX}%XX>sq$k zeL*9fMl(J<_aY8Ee8~TKx7G&xvF;f-e|`|}1@AOPN|U+_mnL;9uB+bT1xyXHS(K1; zLxxMb5yf@7QB{m2mczB*&*Qr%&jPRMaYRV!DcJUB#AP`qD+%0Y2z)8pEX<>GsoPDIM3XQ zvS7I6StNe<#sP+0Q(iU!!dIEQW@!=Els}1h;gdo8{Kt-upfIH$@Um`{NNesJv9o9n zmVbao3%;BOSMluq)Q54<)R`dBr`~XBR|9jaL!;s1-=y;BW+jQkHHqM5hqR$p1-MZm z&jW8W1qko?fIFAV?PIf@v-_dOX*0%VJEo?#qx;4WkY~7@jJ6uDxR6WTfJi^?LpeXUJ#~dZKbXlUT)?KAIP|5L5!h|S* z%WCFslhC}jmTz^d^=Fn)_ed~OJg5Q(RYHAnV^}4;3!eQb4nuuvJjG_&z|(J{oYRIc zFrQ&AdWe78aPePa?#9AtN=--1E2aQ>l=iAgBz#RJ`j%>U2+n^Dx3C=8pA6!S)c3%@ zeHu^H%kQDve=EH;*m#eBDn;CG?jWF5MP&e}(KId+fNc;D(hRE{0T zp?m$Y$ARp7$u8PSt#X))M#A&%^^-y>pY5*ikBbqPF_SOGnim=_$H_&^?etl~T*{X` zrNlRn`y^4BDWHoBt~7~+S1W%#oy3*D=B><0$jD}syI9G7m5_H>-f2K3)O?VY9Adjw za@6D#1;&_L#l}rJ;y+>Xh+vb%;m-TRS-DdtSe&Mz`F=c>oK-orD03>zbA03#{>GLs zf%xV}K1knJ?wkU@Co#z8I z6@CI2d2rsX@4VQFP zhD*9yRg4y(PWfxzL>%^<#KZbzou96+Yh!2Exq6S+$=q$^cflV|;?>^u2l3hmev9b@ z!7rMeAk!qz49Syr^6V|1<4}=B+Ba{wv~PiAS!A=7Wyx>}TQ=$BH)N~EU--K6*RljY zN9iw?4tAPz7l1YT>jSMU7DNDO|L`dAHUI9c~RDVS#PIs|ROst+*<4 z{hShgs$e}0_Zu!@118-X&rUo=v&1;$ac~d3$1+=?Pv5US*V^wa(ZbVsi#x5{CfC*CqsLS*v(BXgB0OE8=2ePR}-9W#=DPNRaigl zeheiZf`gBR-<*G{-KHuL*@oxc=kNHB-RFP!QhJ|1ecM;Y-gZdV-{;Tg8txYaEzAFlnO?6_p4grMdeSU=9wSetaLRz(T+zB?)v`j7;~>2yn_6a z-{uv4l+No)*v;ueLyFRs>2O5yN}zJX<*>1mxt%?#m`jICo@(M-&+Fo*fG+OwI+I9v zgYwr~K%?^4yqo!!^SXhL(i!rIO6ay_`5RRUH6LRo$JuU`oHF@DfobMeu^Cg2_@_-C zQGS;A=8Y|-yoZ@Hi6mg&aPePK1!%>VmA~fEkC8N_ef#CF+s#i0Z; zTJadI^ka04RtCkmnyNJ3((Ee3r4`l8t$cCjcEHsdE_v#RZ~lIKVJ}s*!2}3zV(yxy zFOP1{t3V?}11w-WI>e$}M^2 zPAnT&QEpXL$fXdz#@s5mZu3Z`;MCvXP2!;jTHg?s%@M<^a>$U)-0GC0{IyQ_9D&c_ z4xyfDEijs!3(m4NDjf zCkE%Azy-c6(XACh#*?_fA2i`wZuyCT|C4P|#zG~WK(Dt~v3a9!c^jyPoJv7Sg zuC-xQNTAMe@o!Y=bi13Bzvf+V+O-HT(YqE6Q~U7P4cB@jQ<;$YBt94lkKS;-ctYjY zojGYa*Kx|XytjL5^N}DT%8aUxPsJf=lS)SVobeaFp!{{S7L`BpJ2t+9UR43E;1$++ zT?J?!xSwj~2E^079sjV$|0GslSHMfmDL)E2a1pG$pRUc-!dYC+t>bX%b#7*$9xk9c z)xc)ELu+Dg$5O&_&X;7qW%FY$#f$u{#5WHOlANf`6d;!xI!z+s-O69<-=q9BA7Jhp zd{;p2(1u7w`~uObm}V6hNX12!&2^6aEs=`LD(P{t;c}I^RdLO7j#+1JRSbNb9IT2V z;#*bB-H_3N<^fZj)C8Yjissz0>u#D;{+cgB2a1I+j`s`v zjVG=JqmBM_x&Tge;#x@~O(Rus3RiQh8>tFXRS_-@=g@qlmd$n=sb?tM>J8Oilr|~wo*kOEVea-{^{4oCkIzKFVfOh_6ysUhHZqH3TKn?Sg6#V6j z;cIhy7`c-4z#fE}F6_!tIQ=v(#-PKkjl+4jHUVdGEj7iV(h2#wlzrN88D%rf?Ql&q zmoXrDW(}7-bHq0frAoQr1rs2AiMbud%gSGO*E-72*EZ?ci@T`9!ZK!$q$IbE|ujxs)$?T8M9* z{aZ}|8^hit65gf!wZ7fTU-LocPDp<7W9BD(7aYj`1gYapFN;vP%v1b(iWg8EG(8iJ z9MTu7$~eG@p?Q@`)UOgXe8Rtq5$d{eTl4R3>>?8Er)BPtiX2vvI_#fc!i++lrYt0MFUic|5W~zwouDUwDO2z$9#Z@zR z&Hs_&am9bEc)iN6{Tqp=0xiAEBMk*dzWG#<|L^TDe6Y zr71G+QGzn1iAz3nnQ?>{JW5k!jP0(v$Hj=t5|ghzmyD&%U59G_GUif__?MeJaviRc z_~sSwcqSE4WfDn1wc+AlrwY(hVZHL#Ji(ksB-{w$UHkf&xxu1$>?4o5Ai>1BuySMp1obRFDUmSwpVRqkF&jL8+&V#PD;9!U7wT;CH+asMACni zHIwvTWfdMYFh4VXr}hLrs`4I z+DxOei|uw)b~BgJA}aMX)2QrYyB(E-CZCM5A?9{e4x4hsf5haGK{iT!b5zRju*Xay z379lo{HIg_McBG`a|@@HzvlDIosbiHeqB|DYIw`v;`QRhKsa(B`!ULBmCa#pmCbvM zl+9Qltr_Cy)+&661^pqvAw!cst`;Zi`HxofyquvYnN zo?z~jJsIfzzc1{LNTz%4S&-EwitLX;zrul2L+HEKO2HSbkZ?LJZZQRCGVR|~4*@n+6{`1Y! z0!!QE543ovJzH-2%53pdLNKR=24WsL`UesmSBdkP%fOa#Qee1jfic77rJ04y?LaId zzV+C=gac$CO2MUuOTlG^OTp!e>w(zFoaV~NW|I@749tBKkGcJu`P$jlZls;etu|eT zOBK5fmp5njFt^(D5#Op!KL=QC1`L;i2Mw2khZNV^Of%o2O`1o|0*{(ScC^YYF}KPr z8!i=GFmzg=ct4Xm-&pCR!@kX1KhEuN}@~SgPBYWh?11g-W_U!zJB- z;gV-i<<~k46W@C4+$5ZEt(qfEmQ=WI`?4ucw8CHQxM;i_gpE70W}>B1>xN6?0-vEg z(gQub6AVGc5bmh+za8HEVp4KNImQl*Y{Mm2fht1_QmFhjuVPLECIJ6F6y6Eb-^QtB z0zWdEAmclDy+5I%wNChSL^R_)mlwW`dbFytotLzs&G014*>1SJ)~nO&k?J;uO1d7y zC0(E4l5S8HqnkdY{52mX{$9UN`i^uxE+;v_X{ApRXPDc4?EGiw@OP1ytDo_g1B#=c zrKB#8KKspKZu6AD>6cLDN~rleT;MaW-C7-VxV3u77{&#D--T;O8aacEFp;duaFHxw zc$!BfzO@a@NVT$A2A`yBGhEWO8!qX(RWVxW9_6q3C~GTc(zsB>JOE0WK`hlk$U4To!4M^l1F%>;qszkywMZWrIKv6 zqO=$;=~@k!be*ag-S{r$uX!KwH`1f1xUOz72xpxrV;tvp@%q}@B$?;8F)w*D{uZV9 zdpMkO;Oq!4@P&(Rtq?A_wIVp>*6`V~uiy@_^mA0U-L%RKm-d&#ITu|C%`Q65URJ$Y z!zCJDDTh@kZdA#&C`~F%^FH4k=pS+OH-6wZfWAfJ)S?M$(WGw?Opk=4x5}uPRwcQy zB^8-5JfCNXv~O2xUR6YIc?*V1x<$h!-HIwkHz@QtsqUsqeX@mIg~$wc!#LH|gY#mpbDwyg~VEAsUHqJ-)bS;gFL71hjbd*g;n zo?7CYw*kvz7wS!b@J8lVh$iK)h3JQi*gjbL0nWxF_<=WKcoil{1w62ED&S51PK6nj zT+gIw!=?7K%&iJ@sxmsyyy22(f%xV%Ou3bA$pi>rVQy7eRsLFqJpRs_&oE@e4-_hn zuRnV7M|i4Q2K#@Al9a>gAK?NIc->kRoX53PHT&CP5;t5LR%>{FE)`wJ+zQvo+zy{6 z;`G#Xr>GQ=Fac6PD|0JQ_Y-tF&__!c{}H}|Q(p$3fM(b184@8)BFvHqb0osN%B_2M z!EiY=T{K)?`n|;5im<}mim*z2^By!QV9f+b0ih>JZ7V`1b60>KB?WNq$Jlm!BHK@J zQ3@B`S~)bkwQATuj+1FzrPOW5-~XW(Opk}7`=wg-%2yZD2pyOz)ud{nQzZS>dwv@v%D>kYDpxeUU&H&aX>a~Vy@sV9cFNi=N|)#Xv58Iwr(B6EAjv%XFK z=;wJPY}Aws$o@PPv{6&$G3KrStxySbt3-miaKsIwpl*xJWOdjEdD$lPs^T3@y z4`^4^VBrh2<*o8Evy<)_%I+j5yQSnacip77lyc^-k$QuuR$OmE z3B~m=XeWNPzuux{@8bYh@UP(6Dg24A2t3LrEBvG?LkmB}T!e3B?Pg3K;qxkw68^40 zA%7<+iH12*33I8C@G|C3B;5&vPf;g~@p77$2h!|sw}};%r&@Jp;Ay&%A^J2ysh7B9 zGnaY^&v}}^`|eWWnXMcf*hS^`FNUL`a zTtIU$?~8Pvoex>l=v`>MMV%t3Ky#{u&C*F?E;V7IMVaA)JVBP5JmOzzxcJA39}c)y zXpw4FfadMY?LMs=I3A2lZvB~ux;DF63z13uhAj_&FW>+#fixMHC2-<(90-*v z-<8UYzj8ryu)50+8_0PbRhz+&;yM!`y3`vk4Qzmmm?+iAW~)n+;S!cG>AHAxZ!!MD z+myf7rJeZJt5gGU6dZYriyS*6^Gi@Q2sKH|C0cf$h?s94P)}cyCP?chj;l z3tv@fbv4(Nzvg*x7SrO%VDStts^EfKi^B=GRtH)Ch>LnSg=@u)U!vZ#Ni<^a3DnNW#bM~z=Y?VV&U4G=!Z zoTgRsb)QSj>7e4;O~VQsT)~M=9LR%?U!lx}kntN_;8R20S~(Qs8ub4PH;ZNbz^*xg zpg8_zinP5@Yq-dXUsU}Xd!e4q(g^8=2E!$+(WEP4F%rgKc#HDaVz(0C+6$d3z^Pr% zJHswhfbeeSPVKZ3uVGxjef0hD?oGF8<<`VpI=3jFj7jFwd%{~) zp5LfEvs})Kvg>j(pCxB0r<}`)GM93M=P-B8(&e-~OYP}Vc3sY}DJRb5jGA(UkE=Yo zob2aF^}OfcSPjbfoV)#Q-_Uc^3RkNE`mg_-@aEtwE!JsK@VtvjEj>?;8!hKyx6FK_ z<;+)@+hMo5O`f%F@~ji5d+_x54fOP0@$%jyJ%7DgnapjqBFt^IVm%~OQI8w7y%V=* zLeX!-BN;D2#t44j=3Z>lJOAAX%>Ndrm9i0h?}qg4rQV|63^jJ~q1S z_u;CH&-FHce=jVIhH@Y?wj=W6$=`>kGwz?-LAALIy@UAQhO4-Cp-Xvb4y_+oobNdG zh6cr5o_s!*cqr6+X!NR7k1?0?K77*fY36H&$KFcCXIw^0SKTc9FV`wCskke?sfhg6 zh^GS5zMf(-l!R$1ysCuO6Nb0nM7+oFY337#SKUnhOP6qLP5Ndy^n=h<55;dKhweAf zQYsL*oz~NaFO?CGW?t&QQcmk}!=s0Ze+WAM9rt>R#+-SC%w;=iDaF?vrS3%ra7)!;IptdhsR-`)9>nbJ_?8vLo~#DJCaD>u*uq`S-DZ;covQc*BKoR;r#> zeA9TCduzAit`eo~9tXavxJz1sO?Q8hD`+W|sC$Oi-=w(nf0F&Hi2oQagb%(Upsf=Bb*K6KMiF=(|?2uGp4`0=$rpmpe*!EA{A0j7?mzbqhz|xcgJnH%W-@rouKz{#XxN(pfBsqMreNe$Z}F>F!Qh*B#CAqckuj(D z#2wc|`RQOZ7(Lbd>(|}}D_`6Zg2Lr+_RbvkiJ{LmFi^N76Ozl}=#KJJ0jPL3<4wKE zJEAaOxML^$pS&Gc28Sc?+H&~N&N>b&^1 zZ-@MII6pXz?tezp_z%f6?`tspXN-zn0k5Gc*#D#6W3RsrVk^i3SHj_y@S)45-KBE4 zP`D!-UY!hH3B%6VdL_Ju#$e`n@3CKEz(uTSY1EAeoUY)H=R(&&lVj0wj{$-!tAHy{jWrb0LG lEd2#lJ_$!w!?)}j{ynXJBNr odin_ros_driver 0.0.1 - ROS driver for Odin sensor + ROS2 driver for Odin sensor rlk Apache 2.0 - - - catkin - - - roscpp + + ament_cmake + + rclcpp + std_msgs sensor_msgs nav_msgs + geometry_msgs cv_bridge image_transport - - - eigen - opencv - yaml-cpp - - - - catkin - - + pcl_conversions + message_filters + tf2 + tf2_ros + tf2_geometry_msgs + + + ament_cmake + + \ No newline at end of file diff --git a/src/cloud_reprojection_ros.cpp b/src/cloud_reprojection_ros.cpp new file mode 100644 index 0000000..9207c53 --- /dev/null +++ b/src/cloud_reprojection_ros.cpp @@ -0,0 +1,435 @@ +/* +Copyright 2025 Manifold Tech Ltd.(www.manifoldtech.com.co) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cloud_reprojection_ros_node.hpp" + +#include +#include +#include +#include + +#ifdef ROS2 + #include + #include + #include +#else + #include +#endif + +// Fixed Til (T_imu_lidar): lidar position in imu frame, transforms from lidar to imu +// TODO: Fill in the actual Til values for your sensor setup +static Eigen::Matrix4d getFixedTil() +{ + Eigen::Matrix4d Til = Eigen::Matrix4d::Identity(); + Til(0, 3) = -0.02663; + Til(1, 3) = 0.03447; + Til(2, 3) = 0.02174; + return Til; +} + +static bool fileExists(const std::string& filename) { + struct stat buffer; + return (stat(filename.c_str(), &buffer) == 0); +} + +#ifdef ROS2 +// Helper function to get package source directory for ROS2 +static std::string get_package_source_directory() { + std::string current_file = __FILE__; + size_t pos = current_file.find("/src/cloud_reprojection_ros.cpp"); + if (pos != std::string::npos) { + return current_file.substr(0, pos); + } + return ""; +} + +// ==================== ROS2 Implementation ==================== + +CloudReprojectionRosNode::CloudReprojectionRosNode(const rclcpp::NodeOptions& options) + : Node("cloud_reprojection_node", options) +{ + loadParameters(); + + RCLCPP_INFO_STREAM(this->get_logger(), + "\n cloud_slam_topic: " << cloud_slam_topic_ + << "\n odometry_topic: " << odometry_topic_ + << "\n reprojected_image_topic: " << reprojected_image_topic_); + + cloud_sub_.subscribe(this, cloud_slam_topic_); + odom_sub_.subscribe(this, odometry_topic_); + + sync_ = std::make_shared(MySyncPolicy(10), cloud_sub_, odom_sub_); + sync_->registerCallback(std::bind(&CloudReprojectionRosNode::syncCallback, this, + std::placeholders::_1, std::placeholders::_2)); + + reprojected_image_pub_ = image_transport::create_publisher(this, reprojected_image_topic_); + + RCLCPP_INFO(this->get_logger(), "CloudReprojectionRosNode initialized successfully"); +} + +void CloudReprojectionRosNode::loadParameters() +{ + // Declare and get parameters + this->declare_parameter("cloud_slam_topic", "/odin1/cloud_slam"); + this->declare_parameter("odometry_topic", "/odin1/odometry"); + this->declare_parameter("reprojected_image_topic", "/odin1/reprojected_image"); + + cloud_slam_topic_ = this->get_parameter("cloud_slam_topic").as_string(); + odometry_topic_ = this->get_parameter("odometry_topic").as_string(); + reprojected_image_topic_ = this->get_parameter("reprojected_image_topic").as_string(); + + // Load camera parameters from calib.yaml file directly + std::string package_path = get_package_source_directory(); + std::string calib_file = package_path + "/config/calib.yaml"; + + YAML::Node calib_config; + try { + calib_config = YAML::LoadFile(calib_file); + } catch (const std::exception& e) { + RCLCPP_ERROR(this->get_logger(), "Failed to load calib.yaml: %s", e.what()); + rclcpp::shutdown(); + return; + } + + CloudReprojector::CameraParams cam_params; + try { + cam_params.image_width = calib_config["cam_0"]["image_width"].as(); + cam_params.image_height = calib_config["cam_0"]["image_height"].as(); + cam_params.A11 = calib_config["cam_0"]["A11"].as(); + cam_params.A12 = calib_config["cam_0"]["A12"].as(); + cam_params.A22 = calib_config["cam_0"]["A22"].as(); + cam_params.u0 = calib_config["cam_0"]["u0"].as(); + cam_params.v0 = calib_config["cam_0"]["v0"].as(); + cam_params.k2 = calib_config["cam_0"]["k2"].as(); + cam_params.k3 = calib_config["cam_0"]["k3"].as(); + cam_params.k4 = calib_config["cam_0"]["k4"].as(); + cam_params.k5 = calib_config["cam_0"]["k5"].as(); + cam_params.k6 = calib_config["cam_0"]["k6"].as(); + cam_params.k7 = calib_config["cam_0"]["k7"].as(); + } catch (const std::exception& e) { + RCLCPP_ERROR(this->get_logger(), "Failed to parse camera parameters: %s", e.what()); + rclcpp::shutdown(); + return; + } + + // Load extrinsic parameters + CloudReprojector::ExtrinsicParams ext_params; + try { + auto Tcl_vec = calib_config["Tcl_0"].as>(); + + if (Tcl_vec.size() == 16) + { + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + ext_params.Tcl(i, j) = Tcl_vec[i * 4 + j]; + } + else + { + RCLCPP_ERROR(this->get_logger(), "Tcl_0 has invalid size: %zu (expected 16)", Tcl_vec.size()); + rclcpp::shutdown(); + return; + } + } catch (const std::exception& e) { + RCLCPP_ERROR(this->get_logger(), "Failed to parse Tcl_0: %s", e.what()); + rclcpp::shutdown(); + return; + } + + ext_params.Til = getFixedTil(); + ext_params.Tic = CloudReprojector::calculateTic(ext_params.Tcl, ext_params.Til); + + RCLCPP_INFO_STREAM(this->get_logger(), "Loaded Tcl (camera to lidar):\n" << ext_params.Tcl); + RCLCPP_INFO_STREAM(this->get_logger(), "Fixed Til (lidar to imu):\n" << ext_params.Til); + RCLCPP_INFO_STREAM(this->get_logger(), "Calculated Tic:\n" << ext_params.Tic); + + RCLCPP_INFO(this->get_logger(), "Camera intrinsics:"); + RCLCPP_INFO(this->get_logger(), "Image size: %dx%d", cam_params.image_width, cam_params.image_height); + RCLCPP_INFO(this->get_logger(), "Intrinsics: A11=%f A12=%f A22=%f u0=%f v0=%f", + cam_params.A11, cam_params.A12, cam_params.A22, cam_params.u0, cam_params.v0); + + reprojector_ = std::make_unique(); + if (!reprojector_->initialize(cam_params, ext_params)) + { + RCLCPP_ERROR(this->get_logger(), "Failed to initialize CloudReprojector"); + rclcpp::shutdown(); + } +} + +void CloudReprojectionRosNode::syncCallback( + const PointCloud2::ConstSharedPtr& cloud_msg, + const Odometry::ConstSharedPtr& odom_msg) +{ + pcl::PointCloud cloud_odom; + pcl::fromROSMsg(*cloud_msg, cloud_odom); + + if (cloud_odom.empty()) + { + RCLCPP_WARN(this->get_logger(), "Empty cloud_slam received"); + return; + } + + CloudReprojector::OdomPose odom_pose; + odom_pose.orientation = Eigen::Quaterniond( + odom_msg->pose.pose.orientation.w, + odom_msg->pose.pose.orientation.x, + odom_msg->pose.pose.orientation.y, + odom_msg->pose.pose.orientation.z + ); + odom_pose.position = Eigen::Vector3d( + odom_msg->pose.pose.position.x, + odom_msg->pose.pose.position.y, + odom_msg->pose.pose.position.z + ); + + cv::Mat reprojected_img = reprojector_->reprojectCloud(cloud_odom, odom_pose); + + auto img_msg = cv_bridge::CvImage(cloud_msg->header, "bgr8", reprojected_img).toImageMsg(); + reprojected_image_pub_.publish(*img_msg); +} + +// ==================== ROS2 Main ==================== +int main(int argc, char** argv) +{ + rclcpp::init(argc, argv); + + auto temp_node = std::make_shared("cloud_reprojection_check"); + + // Check if reprojection is enabled from control_command.yaml + std::string package_path = get_package_source_directory(); + std::string config_file = package_path + "/config/control_command.yaml"; + + try { + YAML::Node config = YAML::LoadFile(config_file); + std::cout << "config: " << config_file << std::endl; + if (!config["register_keys"] || !config["register_keys"]["sendreprojection"]) { + RCLCPP_INFO(temp_node->get_logger(), "sendreprojection parameter not found, cloud reprojection disabled."); + rclcpp::shutdown(); + return 0; + } + + int sendreprojection = config["register_keys"]["sendreprojection"].as(); + if (sendreprojection == 0) { + RCLCPP_INFO(temp_node->get_logger(), "Cloud reprojection will not be published."); + rclcpp::shutdown(); + return 0; + } + } catch (const std::exception& e) { + RCLCPP_ERROR(temp_node->get_logger(), "Failed to read config: %s", e.what()); + rclcpp::shutdown(); + return 1; + } + + // Wait for calib.yaml file to be generated by host_sdk_sample + std::string calib_file = package_path + "/config/calib.yaml"; + RCLCPP_INFO(temp_node->get_logger(), "Waiting for calib.yaml file at: %s", calib_file.c_str()); + + int wait_count = 0; + while (rclcpp::ok() && !fileExists(calib_file)) { + if (wait_count % 10 == 0) { + RCLCPP_INFO(temp_node->get_logger(), "Still waiting for calib.yaml file..."); + } + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + wait_count++; + + // Timeout after 5 seconds + if (wait_count > 10) { + RCLCPP_ERROR(temp_node->get_logger(), "Timeout waiting for calib.yaml file"); + rclcpp::shutdown(); + return 1; + } + } + + if (!rclcpp::ok()) { + RCLCPP_INFO(temp_node->get_logger(), "Node shutdown before calib.yaml file was found."); + return 0; + } + + RCLCPP_INFO(temp_node->get_logger(), "Found calib.yaml file! Starting cloud reprojection node..."); + + auto node = std::make_shared(); + + RCLCPP_INFO(node->get_logger(), "CloudReprojectionRosNode started"); + + rclcpp::spin(node); + rclcpp::shutdown(); + + return 0; +} + +#else +// ==================== ROS1 Implementation ==================== + +CloudReprojectionRosNode::CloudReprojectionRosNode(ros::NodeHandle& nh, ros::NodeHandle& pnh) + : nh_(nh), pnh_(pnh) +{ + loadParameters(); + + ROS_INFO_STREAM("\n cloud_slam_topic: " << cloud_slam_topic_ + << "\n odometry_topic: " << odometry_topic_ + << "\n reprojected_image_topic: " << reprojected_image_topic_); + + cloud_sub_.subscribe(nh_, cloud_slam_topic_, 1); + odom_sub_.subscribe(nh_, odometry_topic_, 1); + + sync_ = std::make_shared(MySyncPolicy(10), cloud_sub_, odom_sub_); + sync_->registerCallback(boost::bind(&CloudReprojectionRosNode::syncCallback, this, _1, _2)); + + reprojected_image_pub_ = nh_.advertise(reprojected_image_topic_, 1); + + ROS_INFO("CloudReprojectionRosNode initialized successfully"); +} + +void CloudReprojectionRosNode::loadParameters() +{ + pnh_.param("cloud_slam_topic", cloud_slam_topic_, std::string("/odin1/cloud_slam")); + pnh_.param("odometry_topic", odometry_topic_, std::string("/odin1/odometry")); + pnh_.param("reprojected_image_topic", reprojected_image_topic_, std::string("/odin1/reprojected_image")); + + // Load camera parameters + CloudReprojector::CameraParams cam_params; + pnh_.param("cam_0/image_width", cam_params.image_width, 1600); + pnh_.param("cam_0/image_height", cam_params.image_height, 1296); + pnh_.param("cam_0/A11", cam_params.A11, 0.0); + pnh_.param("cam_0/A12", cam_params.A12, 0.0); + pnh_.param("cam_0/A22", cam_params.A22, 0.0); + pnh_.param("cam_0/u0", cam_params.u0, 0.0); + pnh_.param("cam_0/v0", cam_params.v0, 0.0); + pnh_.param("cam_0/k2", cam_params.k2, 0.0); + pnh_.param("cam_0/k3", cam_params.k3, 0.0); + pnh_.param("cam_0/k4", cam_params.k4, 0.0); + pnh_.param("cam_0/k5", cam_params.k5, 0.0); + pnh_.param("cam_0/k6", cam_params.k6, 0.0); + pnh_.param("cam_0/k7", cam_params.k7, 0.0); + + // Load extrinsic parameters + CloudReprojector::ExtrinsicParams ext_params; + std::vector Tcl_vec_param; + if (pnh_.getParam("Tcl_0", Tcl_vec_param) && Tcl_vec_param.size() == 16) + { + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + ext_params.Tcl(i, j) = Tcl_vec_param[i * 4 + j]; + } + else + { + ROS_ERROR("Tcl_0 param missing or invalid."); + ros::shutdown(); + return; + } + + ext_params.Til = getFixedTil(); + ext_params.Tic = CloudReprojector::calculateTic(ext_params.Tcl, ext_params.Til); + + ROS_INFO_STREAM("Loaded Tcl (camera to lidar):\n" << ext_params.Tcl); + ROS_INFO_STREAM("Fixed Til (lidar to imu):\n" << ext_params.Til); + ROS_INFO_STREAM("Calculated Tic:\n" << ext_params.Tic); + + ROS_INFO("Camera intrinsics:"); + ROS_INFO("Image size: %dx%d", cam_params.image_width, cam_params.image_height); + ROS_INFO("Intrinsics: A11=%f A12=%f A22=%f u0=%f v0=%f", + cam_params.A11, cam_params.A12, cam_params.A22, cam_params.u0, cam_params.v0); + + reprojector_ = std::make_unique(); + if (!reprojector_->initialize(cam_params, ext_params)) + { + ROS_ERROR("Failed to initialize CloudReprojector"); + ros::shutdown(); + } +} + +void CloudReprojectionRosNode::syncCallback( + const sensor_msgs::PointCloud2ConstPtr& cloud_msg, + const nav_msgs::OdometryConstPtr& odom_msg) +{ + pcl::PointCloud cloud_odom; + pcl::fromROSMsg(*cloud_msg, cloud_odom); + + if (cloud_odom.empty()) + { + ROS_WARN("Empty cloud_slam received"); + return; + } + + CloudReprojector::OdomPose odom_pose; + odom_pose.orientation = Eigen::Quaterniond( + odom_msg->pose.pose.orientation.w, + odom_msg->pose.pose.orientation.x, + odom_msg->pose.pose.orientation.y, + odom_msg->pose.pose.orientation.z + ); + odom_pose.position = Eigen::Vector3d( + odom_msg->pose.pose.position.x, + odom_msg->pose.pose.position.y, + odom_msg->pose.pose.position.z + ); + + cv::Mat reprojected_img = reprojector_->reprojectCloud(cloud_odom, odom_pose); + + sensor_msgs::ImagePtr img_msg = cv_bridge::CvImage(cloud_msg->header, "bgr8", reprojected_img).toImageMsg(); + reprojected_image_pub_.publish(img_msg); +} + +// ==================== ROS1 Main ==================== +int main(int argc, char **argv) +{ + ros::init(argc, argv, "cloud_reprojection"); + ros::NodeHandle nh; + ros::NodeHandle pnh("~"); + + // Check if reprojection is enabled + int sendreprojection = 0; + pnh.param("register_keys/sendreprojection", sendreprojection, 0); + if(sendreprojection == 0) + { + ROS_INFO("Cloud reprojection will not be published."); + return 0; + } + + std::string calib_file_path; + pnh.param("calib_file_path", calib_file_path, ""); + + ROS_INFO("Waiting for calib.yaml file at: %s", calib_file_path.c_str()); + while(ros::ok() && !fileExists(calib_file_path)) + { + ROS_INFO_THROTTLE(5, "Still waiting for calib.yaml file..."); + ros::Duration(0.5).sleep(); + ros::spinOnce(); + } + + if(!ros::ok()) + { + ROS_INFO("Node shutdown before calib.yaml file was found."); + return 0; + } + + ROS_INFO("Found calib.yaml file! Loading parameters..."); + + std::string node_name = ros::this_node::getName(); + std::string rosparam_command = "rosparam load " + calib_file_path + " " + node_name; + int result = system(rosparam_command.c_str()); + + if(result == 0) + { + ROS_INFO("Successfully loaded parameters from calib.yaml to namespace: %s", node_name.c_str()); + } + else + { + ROS_ERROR("Failed to load parameters from calib.yaml"); + return 1; + } + + CloudReprojectionRosNode reprojection_node(nh, pnh); + ros::spin(); + return 0; +} +#endif diff --git a/src/cloud_reprojector.cpp b/src/cloud_reprojector.cpp new file mode 100644 index 0000000..7ede028 --- /dev/null +++ b/src/cloud_reprojector.cpp @@ -0,0 +1,113 @@ +/* +Copyright 2025 Manifold Tech Ltd.(www.manifoldtech.com.co) +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cloud_reprojector.hpp" +#include + +CloudReprojector::CloudReprojector() +{ +} + +bool CloudReprojector::initialize(const CameraParams& cam_params, const ExtrinsicParams& ext_params) +{ + camera_params_ = cam_params; + extrinsic_params_ = ext_params; + + if (camera_params_.A11 < 1e-6 || camera_params_.A22 < 1e-6 || + camera_params_.u0 < 1e-6 || camera_params_.v0 < 1e-6) + { + return false; + } + + camera_model_ = std::make_unique( + camera_params_.image_width, camera_params_.image_height, + camera_params_.A11, camera_params_.A22, + camera_params_.u0, camera_params_.v0, + camera_params_.A12, + camera_params_.k2, camera_params_.k3, camera_params_.k4, + camera_params_.k5, camera_params_.k6, camera_params_.k7 + ); + + initialized_ = true; + return true; +} + +Eigen::Matrix4d CloudReprojector::calculateTic(const Eigen::Matrix4d& Tcl, const Eigen::Matrix4d& Til) +{ + // Tic = Til * Tlc = Til * Tcl.inverse() + Eigen::Matrix4d Tlc = Tcl.inverse(); + return Til * Tlc; +} + +Eigen::Matrix4d CloudReprojector::odomPoseToMatrix(const OdomPose& odom) const +{ + Eigen::Matrix4d T = Eigen::Matrix4d::Identity(); + T.block<3, 3>(0, 0) = odom.orientation.toRotationMatrix(); + T(0, 3) = odom.position.x(); + T(1, 3) = odom.position.y(); + T(2, 3) = odom.position.z(); + return T; +} + +cv::Mat CloudReprojector::reprojectCloud(const pcl::PointCloud& cloud_odom, + const OdomPose& odom_pose) +{ + if (!initialized_) + { + return cv::Mat(); + } + + // T_odom_imu: imu pose in odom frame + Eigen::Matrix4d T_odom_imu = odomPoseToMatrix(odom_pose); + + // T_imu_odom: transforms points from odom frame to imu frame + Eigen::Matrix4d T_imu_odom = T_odom_imu.inverse(); + + // T_cam_imu = Tic.inverse(): transforms from imu to camera + Eigen::Matrix4d T_cam_imu = extrinsic_params_.Tic.inverse(); + + // T_cam_odom: transforms points from odom frame to camera frame + Eigen::Matrix4d T_cam_odom = T_cam_imu * T_imu_odom; + + pcl::PointCloud cloud_in_cam; + pcl::transformPointCloud(cloud_odom, cloud_in_cam, T_cam_odom); + + return projectCloudToImage(cloud_in_cam); +} + +cv::Mat CloudReprojector::projectCloudToImage(const pcl::PointCloud& cloud_in_cam) const +{ + cv::Mat img = cv::Mat::zeros(camera_params_.image_height, camera_params_.image_width, CV_8UC3); + img.setTo(cv::Scalar(255, 255, 255)); + + for (const auto& pt : cloud_in_cam) + { + if (pt.z <= 0.01) + continue; + + Eigen::Vector3d pt_cam(pt.x, pt.y, pt.z); + Eigen::Vector2d uv = camera_model_->world2cam(pt_cam); + + int u_int = static_cast(std::round(uv[0])); + int v_int = static_cast(std::round(uv[1])); + + if (u_int >= 0 && u_int < camera_params_.image_width && + v_int >= 0 && v_int < camera_params_.image_height) + { + cv::circle(img, cv::Point(u_int, v_int), point_radius_, + cv::Scalar(pt.b, pt.g, pt.r), -1); + } + } + + return img; +} diff --git a/src/host_sdk_sample.cpp b/src/host_sdk_sample.cpp index 136e0a0..3640d9e 100644 --- a/src/host_sdk_sample.cpp +++ b/src/host_sdk_sample.cpp @@ -46,9 +46,9 @@ limitations under the License. #include #include #endif -#define ros_driver_version "0.8.0" +#define ros_driver_version "0.9.0" #define required_firmware_version_major 0 -#define required_firmware_version_minor 9 +#define required_firmware_version_minor 10 #define required_firmware_version_patch 0 // Global variable declarations @@ -85,6 +85,21 @@ static std::shared_ptr g_renderer = nullptr; std::string calib_file_ = ""; static std::shared_ptr g_parser = nullptr; +static constexpr size_t PTP_SMOOTH_WINDOW_SIZE = 30; +static std::mutex g_ptp_mutex; +static std::deque g_ptp_delay_buf; +static std::deque g_ptp_offset_buf; +static std::atomic g_ptp_delay_smooth{0.0}; +static std::atomic g_ptp_offset_smooth{0.0}; + +double get_ptp_smoothed_delay() { + return g_ptp_delay_smooth.load(std::memory_order_relaxed); +} + +double get_ptp_smoothed_offset() { + return g_ptp_offset_smooth.load(std::memory_order_relaxed); +} + // usb device static std::string TARGET_VENDOR = "2207"; static std::string TARGET_PRODUCT = "0019"; @@ -106,6 +121,8 @@ int g_show_camerapose = 0; int g_strict_usb3_0_check = 0; int g_use_host_ros_time = 0; int g_save_log = 0; +int g_cloud_raw_confidence_threshold = 35; +int g_dtof_fps = 145; // DTOF sensor frame rate: 100 (10fps) or 145 (14.5fps) std::filesystem::path log_root_dir_; int g_custom_map_mode = 0; @@ -178,15 +195,7 @@ class RosNodeControlImpl : public RosNodeControlInterface { int getDtofSubframeODR() const override { return dtof_subframe_interval_time; } - - void setUseHostRosTime(bool use_host_ros_time) override { - pub_use_host_ros_time = use_host_ros_time; - } - - bool useHostRosTime() const override { - return pub_use_host_ros_time; - } - + void setSendOdomBaseLinkTF(bool send_odom_baselink_tf) override { pub_odom_baselink_tf = send_odom_baselink_tf; } @@ -195,10 +204,17 @@ class RosNodeControlImpl : public RosNodeControlInterface { return pub_odom_baselink_tf; } + void setCloudRawConfidenceThreshold(int threshold) { + cloud_raw_confidence_threshold = threshold; + } + int cloudRawConfidenceThreshold() const { + return cloud_raw_confidence_threshold; + } private: int dtof_subframe_interval_time = 0; bool pub_use_host_ros_time = false; bool pub_odom_baselink_tf = false; + int cloud_raw_confidence_threshold = 35; }; static RosNodeControlImpl g_rosNodeControlImpl; @@ -988,6 +1004,45 @@ static void lidar_data_callback(const lidar_data_t *data, void *user_data) } } break; + case LIDAR_DT_NTP: + { + uint32_t data_len = data->stream.imageList[0].length; + if (data_len == sizeof(ptp_sync_data_t)) { + ptp_sync_data_t* ptp_data = (ptp_sync_data_t*)data->stream.imageList[0].pAddr; + { + std::lock_guard lock(g_ptp_mutex); + g_ptp_delay_buf.push_back(ptp_data->delay); + g_ptp_offset_buf.push_back(ptp_data->offset); + + if (g_ptp_delay_buf.size() > PTP_SMOOTH_WINDOW_SIZE) { + g_ptp_delay_buf.pop_front(); + } + if (g_ptp_offset_buf.size() > PTP_SMOOTH_WINDOW_SIZE) { + g_ptp_offset_buf.pop_front(); + } + + double delay_sum = 0.0; + for (double v : g_ptp_delay_buf) delay_sum += v; + double offset_sum = 0.0; + for (double v : g_ptp_offset_buf) offset_sum += v; + + if (!g_ptp_delay_buf.empty()) { + g_ptp_delay_smooth.store(delay_sum / static_cast(g_ptp_delay_buf.size()), std::memory_order_relaxed); + } + if (!g_ptp_offset_buf.empty()) { + g_ptp_offset_smooth.store(offset_sum / static_cast(g_ptp_offset_buf.size()), std::memory_order_relaxed); + } + } + + // std::cout << std::setprecision(16) + // << "PTP delay: " << ptp_data->delay + // << " offset:" << ptp_data->offset + // << " smooth_delay:" << get_ptp_smoothed_delay() + // << " smooth_offset:" << get_ptp_smoothed_offset() + // << std::endl; + } + } + break; default: printf("Unknown lidar data type: %x", data->type); return; @@ -1292,6 +1347,44 @@ static void lidar_device_callback(const lidar_device_info_t* device, bool attach #endif } + + // Set DTOF sensor frame rate based on configuration + // Supported values: 100 (10fps) or 145 (14.5fps) + lidar_depth_para_t dtofpara; + if (g_dtof_fps == 100) { + dtofpara.odr = LIDAR_DEPTH_ODR_10HZ; + } else if (g_dtof_fps == 145) { + dtofpara.odr = LIDAR_DEPTH_ODR_14_5HZ; + } else { + // Default to 14.5Hz if invalid value + dtofpara.odr = LIDAR_DEPTH_ODR_14_5HZ; + #ifdef ROS2 + RCLCPP_WARN(rclcpp::get_logger("ros[host_sdk_sample]"), + "Invalid dtof_fps value: %d, using default 14.5fps", g_dtof_fps); + #else + ROS_WARN("Invalid dtof_fps value: %d, using default 14.5fps", g_dtof_fps); + #endif + } + + if(lidar_set_depth_parameter(odinDevice, &dtofpara)) { + printf("set depth parameter failed.\n"); + #ifdef ROS2 + RCLCPP_WARN(rclcpp::get_logger("ros[host_sdk_sample]"), "set depth parameter failed"); + #else + ROS_WARN("set depth parameter failed"); + #endif + return; + } + + // Log the configured frame rate + #ifdef ROS2 + RCLCPP_INFO(rclcpp::get_logger("ros[host_sdk_sample]"), + "DTOF sensor frame rate set to %.1f fps", g_dtof_fps / 10.0); + #else + ROS_INFO("DTOF sensor frame rate set to %.1f fps", g_dtof_fps / 10.0); + #endif + + if (lidar_set_mode(odinDevice, type)) { #ifdef ROS2 RCLCPP_ERROR(rclcpp::get_logger("device_cb"), "Set mode failed"); @@ -1554,6 +1647,9 @@ int main(int argc, char *argv[]) g_sendrgb = get_key_value("sendrgb", 1); g_sendimu = get_key_value("sendimu", 1); g_senddtof = get_key_value("senddtof", 1); + g_cloud_raw_confidence_threshold = get_key_value("cloud_raw_confidence_threshold", 35); + g_rosNodeControlImpl.setCloudRawConfidenceThreshold(g_cloud_raw_confidence_threshold); + g_dtof_fps = get_key_value("dtof_fps", 145); // Read DTOF frame rate from config (100=10fps, 145=14.5fps) g_sendodom = get_key_value("sendodom", 1); g_send_odom_baselink_tf = get_key_value("send_odom_baselink_tf", 0); g_sendcloudslam = get_key_value("sendcloudslam", 0); @@ -1571,10 +1667,6 @@ int main(int argc, char *argv[]) g_use_host_ros_time = get_key_value("use_host_ros_time", 0); g_save_log = get_key_value("save_log", 0); - if (g_use_host_ros_time) { - g_rosNodeControlImpl.setUseHostRosTime(true); - } - if (g_send_odom_baselink_tf) { g_rosNodeControlImpl.setSendOdomBaseLinkTF(true); }