<add> 1. add odin1 SLAM mode && relocalization mode support
2. optimize rx data fps calculation 3. optimize exit logic on ctrl-c to reduce issue on restart the the driver
This commit is contained in:
+2
-1
@@ -1,3 +1,4 @@
|
||||
recorddata/
|
||||
/config/calib.yaml
|
||||
/log
|
||||
/log
|
||||
/map
|
||||
+14
-3
@@ -142,6 +142,7 @@ if(ROS_VERSION STREQUAL "ROS1")
|
||||
sensor_msgs
|
||||
nav_msgs
|
||||
cv_bridge
|
||||
tf
|
||||
image_transport
|
||||
)
|
||||
|
||||
@@ -224,6 +225,10 @@ elseif(ROS_VERSION STREQUAL "ROS2")
|
||||
find_package(image_transport REQUIRED)
|
||||
find_package(pcl_conversions REQUIRED)
|
||||
find_package(message_filters REQUIRED)
|
||||
find_package(tf2 REQUIRED)
|
||||
find_package(tf2_ros REQUIRED)
|
||||
find_package(geometry_msgs REQUIRED)
|
||||
find_package(tf2_geometry_msgs REQUIRED)
|
||||
|
||||
# Create executable
|
||||
add_executable(host_sdk_sample
|
||||
@@ -235,9 +240,8 @@ elseif(ROS_VERSION STREQUAL "ROS2")
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(host_sdk_sample
|
||||
${catkin_LIBRARIES}
|
||||
${COMMON_LIBS}
|
||||
yaml-cpp
|
||||
yaml-cpp
|
||||
usb-1.0
|
||||
)
|
||||
|
||||
@@ -250,9 +254,12 @@ elseif(ROS_VERSION STREQUAL "ROS2")
|
||||
visualization_msgs
|
||||
cv_bridge
|
||||
image_transport
|
||||
tf2
|
||||
tf2_ros
|
||||
geometry_msgs
|
||||
tf2_geometry_msgs
|
||||
)
|
||||
|
||||
|
||||
add_library(pointcloud_depth_converter_ros2 src/pointcloud_depth_converter.cpp)
|
||||
target_link_libraries(pointcloud_depth_converter_ros2
|
||||
${OpenCV_LIBS}
|
||||
@@ -348,6 +355,10 @@ elseif(ROS_VERSION STREQUAL "ROS2")
|
||||
image_transport
|
||||
pcl_conversions
|
||||
message_filters
|
||||
tf2
|
||||
tf2_ros
|
||||
geometry_msgs
|
||||
tf2_geometry_msgs
|
||||
)
|
||||
|
||||
ament_package()
|
||||
|
||||
@@ -16,7 +16,7 @@ This driver package provides core functionality for point cloud SLAM application
|
||||
|
||||
## 1. Version
|
||||
|
||||
Current Version: v0.5.2
|
||||
Current Version: v0.6.0
|
||||
|
||||
## 2. Preparation
|
||||
|
||||
@@ -149,6 +149,37 @@ ROS2 Demo Launch Instructions:
|
||||
```shell
|
||||
ros2 launch odin_ros_driver odin1_ros2.launch.py
|
||||
```
|
||||
|
||||
### 3.5 Operation Mode:
|
||||
|
||||
The operation mode can be configured via the `custom_map_mode` parameter in config/control_command.yaml.
|
||||
|
||||
#### Odometry mode
|
||||
|
||||
Set `custom_map_mode = 0` to enable odometry mode. In this mode, the map frame and odom frame share the same pose.
|
||||
|
||||
#### SLAM mode
|
||||
|
||||
Set `custom_map_mode = 1` to enable slam mode. This mode provides a complete SLAM system that builds upon the Odometry Mode by adding **loop closure detection** and **map saving** capabilities.
|
||||
|
||||
After launching the driver, odin1 will automatically perform mapping and cache map data. When the scene capture is complete, users need to execute `./set_param.sh save_map 1` in the driver's source directory to save all map data collected since the program started. The map will be saved to the location specified by the `mapping_result_dest_dir` and `mapping_result_file_name` parameters in config/control_command.yaml. If these parameters are not specified, default values will be used.
|
||||
|
||||
After the initial save, you can execute the command again to save a new map. Each save operation will generate a new map file. (Please allow at least 5 seconds between consecutive save operations)
|
||||
|
||||
The map origin corresponds to the odom coordinate system's origin at the program's startup.
|
||||
|
||||
##### Relocalization mode
|
||||
|
||||
To enable relocalization, set `custom_map_mode = 2` and specify the absolute path to the pre-built map using the `relocalization_map_abs_path` parameter in config/control_command.yaml.
|
||||
|
||||
Once launched, odin1 will initiate the relocalization process based on the current viewpoint and the specified map. To ensure a high success rate, it is recommended to starting within 1 meter ±10 degrees of the original position and orientation from the SLAM trajectory.
|
||||
|
||||
Note that relocalization performance is highly environment-dependent. In highly distinctive scenes, successful matching may occur even beyond the 1m/10° range, while other environments may require more stringent conditions. We advise testing in your target environment to determine practical tolerances.
|
||||
|
||||
If relocalization fails initially, the system will temporarily operate in a fallback SLAM mode (map saving is disabled in this state). During this time, you can freely move odin1. It will continue relocalization attempts in the background. Once successful, the TF between map and odom frames will be published. (Tip: Gently shaking or moving the device after initialization can help improve relocalization accuracy.)
|
||||
|
||||
The following topics are published in the odom frame: `/odin1/cloud_slam, /odin1/odom, /odin1/highodom and /odin1/path`. To obtain these in the map frame, apply the TF from odom frame to map frame.
|
||||
|
||||
## 4. File structure and data format
|
||||
### 4.1 File structure
|
||||
```shell
|
||||
@@ -216,6 +247,8 @@ Internal parameters of the Odin ROS driver are defined in config/control_command
|
||||
| odin1/cloud_slam | sendcloudslam | Slam_PointCloud Topic |
|
||||
| odin1/odometry | sendodom | Odom Topic |
|
||||
| odin1/odometry_high | sendodom | high frequency Odom Topic |
|
||||
| odin1/path | showpath | Odom Path Topic |
|
||||
| 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 |
|
||||
|
||||
@@ -275,6 +308,11 @@ float32 rgb // RGB value
|
||||
|-----------------------|----------------------|
|
||||
| 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. |
|
||||
| showcamerapose | Display Camera Pose and Field of View. |
|
||||
| custom_map_mode | Operation Modes: Mode 0 - Odometry mode: The map frame and odom frame share the same pose. Mode 1 - Mapping (with loop closure) mode: This mode supports map saving. Mode 2 - Relocalization mode: Requires specifying the absolute path to the map file. After successful relocalization, it will output the TF relationship between the map and odom frames.|
|
||||
| custom_init_pos | Initialization Position (currently unused). |
|
||||
| relocalization_map_abs_path | Absolute Path to Map File: Used for relocalization mode. |
|
||||
| mapping_result_dest_dir and mapping_result_file_name| Path and Name for Saving Maps in Mapping Mode: If not specified, default values will be used. |
|
||||
|
||||
## 5. FAQ
|
||||
### 5.1 Segmentation fault upon re-launching host SDK
|
||||
|
||||
@@ -14,3 +14,9 @@ register_keys:
|
||||
pubintensitygray: 0 # 0: off; 1: on
|
||||
showpath: 0 # 0: off; 1: on
|
||||
showcamerapose: 0 # 0: off; 1: on
|
||||
custom_save_map: 0
|
||||
custom_map_mode: 0 # 0: Odometry mode 1: SLAM mode 2: Relocalization mode
|
||||
custom_init_pos: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
|
||||
relocalization_map_abs_path: "" # must be set or will fail
|
||||
mapping_result_dest_dir: "" # "": use default value; other: use custom value
|
||||
mapping_result_file_name: "" # "": use default value; other: use custom value
|
||||
+47
-19
@@ -4,11 +4,12 @@ Panels:
|
||||
Name: Displays
|
||||
Property Tree Widget:
|
||||
Expanded:
|
||||
- /Global Options1
|
||||
- /Odometry1
|
||||
- /slam1
|
||||
- /dense_depth_demo1
|
||||
- /TF1/Frames1
|
||||
Splitter Ratio: 0.4993045926094055
|
||||
Tree Height: 549
|
||||
Tree Height: 837
|
||||
- Class: rviz/Selection
|
||||
Name: Selection
|
||||
- Class: rviz/Tool Properties
|
||||
@@ -26,7 +27,7 @@ Panels:
|
||||
- Class: rviz/Time
|
||||
Name: Time
|
||||
SyncMode: 0
|
||||
SyncSource: ""
|
||||
SyncSource: Image
|
||||
Preferences:
|
||||
PromptSaveOnExit: true
|
||||
Toolbars:
|
||||
@@ -116,7 +117,7 @@ Visualization Manager:
|
||||
Color: 255; 255; 255
|
||||
Color Transformer: RGB8
|
||||
Decay Time: 0
|
||||
Enabled: true
|
||||
Enabled: false
|
||||
Invert Rainbow: false
|
||||
Max Color: 255; 255; 255
|
||||
Min Color: 0; 0; 0
|
||||
@@ -131,7 +132,7 @@ Visualization Manager:
|
||||
Unreliable: false
|
||||
Use Fixed Frame: true
|
||||
Use rainbow: true
|
||||
Value: true
|
||||
Value: false
|
||||
- Class: rviz/Group
|
||||
Displays:
|
||||
- Angle Tolerance: 0.10000000149011612
|
||||
@@ -280,7 +281,7 @@ Visualization Manager:
|
||||
{}
|
||||
Queue Size: 100
|
||||
Value: false
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
Name: slam
|
||||
- Class: rviz/Group
|
||||
Displays:
|
||||
@@ -326,11 +327,38 @@ Visualization Manager:
|
||||
Value: false
|
||||
Enabled: false
|
||||
Name: dense_depth_demo
|
||||
- Class: rviz/TF
|
||||
Enabled: true
|
||||
Filter (blacklist): ""
|
||||
Filter (whitelist): ""
|
||||
Frame Timeout: 15
|
||||
Frames:
|
||||
All Enabled: true
|
||||
map:
|
||||
Value: true
|
||||
odin1_base_link:
|
||||
Value: true
|
||||
odom:
|
||||
Value: true
|
||||
Marker Alpha: 1
|
||||
Marker Scale: 1
|
||||
Name: TF
|
||||
Show Arrows: true
|
||||
Show Axes: true
|
||||
Show Names: true
|
||||
Tree:
|
||||
odom:
|
||||
map:
|
||||
{}
|
||||
odin1_base_link:
|
||||
{}
|
||||
Update Interval: 0
|
||||
Value: true
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Default Light: true
|
||||
Fixed Frame: map
|
||||
Fixed Frame: odom
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
@@ -354,7 +382,7 @@ Visualization Manager:
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz/Orbit
|
||||
Distance: 8.56683349609375
|
||||
Distance: 15.013533592224121
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
@@ -362,29 +390,29 @@ Visualization Manager:
|
||||
Value: false
|
||||
Field of View: 0.7853981852531433
|
||||
Focal Point:
|
||||
X: 1.4208989143371582
|
||||
Y: -0.7709289789199829
|
||||
Z: 0.5377280712127686
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Focal Shape Fixed Size: false
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 0.8353985548019409
|
||||
Target Frame: <Fixed Frame>
|
||||
Yaw: 2.530402660369873
|
||||
Pitch: 0.7853981852531433
|
||||
Target Frame: odom
|
||||
Yaw: 0.7853981852531433
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
collapsed: false
|
||||
Height: 1016
|
||||
Height: 1672
|
||||
Hide Left Dock: false
|
||||
Hide Right Dock: false
|
||||
Image:
|
||||
collapsed: false
|
||||
Image_undistort:
|
||||
collapsed: false
|
||||
QMainWindow State: 000000ff00000000fd0000000400000000000001bf0000033afc020000000bfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000b0fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d00000262000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000002a5000000d20000001600fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d00610067006500000001d5000000d70000001600fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f0072007400000002690000010e0000001600ffffff000000010000015f0000033afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000033a000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000007380000005efc0100000002fb0000000800540069006d00650100000000000007380000033700fffffffb0000000800540069006d006501000000000000045000000000000000000000040e0000033a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
QMainWindow State: 000000ff00000000fd00000004000000000000025f00000582fc020000000bfb0000001200530065006c0065006300740069006f006e00000001e10000009b000000b000fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000b0fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000006e000003b30000018200fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000042d000001c30000002600fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000047d000001730000002600fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f007200740000000568000000df0000002600ffffff000000010000015f00000582fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000006e000005820000013200fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e1000001970000000300000ab00000005efc0100000002fb0000000800540069006d0065010000000000000ab0000006dc00fffffffb0000000800540069006d00650100000000000004500000000000000000000006da0000058200000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Selection:
|
||||
collapsed: false
|
||||
Time:
|
||||
@@ -393,8 +421,8 @@ Window Geometry:
|
||||
collapsed: false
|
||||
Views:
|
||||
collapsed: false
|
||||
Width: 1848
|
||||
X: 57
|
||||
Y: 27
|
||||
Width: 2736
|
||||
X: 144
|
||||
Y: 54
|
||||
dense_depth_image:
|
||||
collapsed: false
|
||||
+40
-16
@@ -120,7 +120,7 @@ Visualization Manager:
|
||||
Color: 255; 255; 255
|
||||
Color Transformer: RGB8
|
||||
Decay Time: 0
|
||||
Enabled: true
|
||||
Enabled: false
|
||||
Invert Rainbow: false
|
||||
Max Color: 255; 255; 255
|
||||
Max Intensity: 2.3509885615147286e-38
|
||||
@@ -141,7 +141,7 @@ Visualization Manager:
|
||||
Value: /odin1/cloud_render
|
||||
Use Fixed Frame: true
|
||||
Use rainbow: true
|
||||
Value: true
|
||||
Value: false
|
||||
- Class: rviz_common/Group
|
||||
Displays:
|
||||
- Angle Tolerance: 0.10000000149011612
|
||||
@@ -318,7 +318,7 @@ Visualization Manager:
|
||||
Reliability Policy: Reliable
|
||||
Value: /odin1/path
|
||||
Value: false
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
Name: slam
|
||||
- Class: rviz_common/Group
|
||||
Displays:
|
||||
@@ -372,10 +372,34 @@ Visualization Manager:
|
||||
Value: false
|
||||
Enabled: false
|
||||
Name: dense_depth_demo
|
||||
- Class: rviz_default_plugins/TF
|
||||
Enabled: true
|
||||
Frame Timeout: 15
|
||||
Frames:
|
||||
All Enabled: true
|
||||
map:
|
||||
Value: true
|
||||
odin1_base_link:
|
||||
Value: true
|
||||
odom:
|
||||
Value: true
|
||||
Marker Scale: 1
|
||||
Name: TF
|
||||
Show Arrows: true
|
||||
Show Axes: true
|
||||
Show Names: false
|
||||
Tree:
|
||||
odom:
|
||||
map:
|
||||
{}
|
||||
odin1_base_link:
|
||||
{}
|
||||
Update Interval: 0
|
||||
Value: true
|
||||
Enabled: true
|
||||
Global Options:
|
||||
Background Color: 48; 48; 48
|
||||
Fixed Frame: map
|
||||
Fixed Frame: odom
|
||||
Frame Rate: 30
|
||||
Name: root
|
||||
Tools:
|
||||
@@ -418,25 +442,25 @@ Visualization Manager:
|
||||
Views:
|
||||
Current:
|
||||
Class: rviz_default_plugins/Orbit
|
||||
Distance: 9.397579193115234
|
||||
Distance: 10.9336576461792
|
||||
Enable Stereo Rendering:
|
||||
Stereo Eye Separation: 0.05999999865889549
|
||||
Stereo Focal Distance: 1
|
||||
Swap Stereo Eyes: false
|
||||
Value: false
|
||||
Focal Point:
|
||||
X: 1.1073570251464844
|
||||
Y: 0.6017969250679016
|
||||
Z: 0.4314231276512146
|
||||
X: 0
|
||||
Y: 0
|
||||
Z: 0
|
||||
Focal Shape Fixed Size: false
|
||||
Focal Shape Size: 0.05000000074505806
|
||||
Invert Z Axis: false
|
||||
Name: Current View
|
||||
Near Clip Distance: 0.009999999776482582
|
||||
Pitch: 1.035398006439209
|
||||
Target Frame: <Fixed Frame>
|
||||
Pitch: 0.3203984200954437
|
||||
Target Frame: odom
|
||||
Value: Orbit (rviz)
|
||||
Yaw: 2.960400104522705
|
||||
Yaw: 3.130404233932495
|
||||
Saved: ~
|
||||
Window Geometry:
|
||||
Displays:
|
||||
@@ -448,15 +472,15 @@ Window Geometry:
|
||||
collapsed: false
|
||||
Image_undistort:
|
||||
collapsed: false
|
||||
QMainWindow State: 000000ff00000000fd0000000400000000000001f50000039efc020000000bfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002d8000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000031b000000c00000002800fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000023e0000007a0000002800fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f00720074000000028f0000014c0000002800ffffff000000010000010f0000039efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000039e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000004280000039e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
QMainWindow State: 000000ff00000000fd0000000400000000000001f50000039efc020000000bfb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002d8000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000031b000000c00000002800fffffffb0000002200640065006e00730065005f00640065007000740068005f0069006d006100670065000000023e0000007a0000002800fffffffb0000001e0049006d006100670065005f0075006e0064006900730074006f00720074000000028f0000014c0000002800ffffff000000010000010f0000039efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d0000039e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000004700000039e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
|
||||
Selection:
|
||||
collapsed: false
|
||||
Tool Properties:
|
||||
collapsed: false
|
||||
Views:
|
||||
collapsed: false
|
||||
Width: 1848
|
||||
X: 392
|
||||
Y: 165
|
||||
Width: 1920
|
||||
X: 540
|
||||
Y: 124
|
||||
dense_depth_image:
|
||||
collapsed: false
|
||||
collapsed: false
|
||||
|
||||
+217
-133
@@ -55,6 +55,12 @@ struct CameraParams {
|
||||
double p1, p2;
|
||||
};
|
||||
|
||||
enum class OdometryType {
|
||||
STANDARD = LIDAR_DT_SLAM_ODOMETRY,
|
||||
HIGHFREQ = LIDAR_DT_SLAM_ODOMETRY_HIGHFREQ,
|
||||
TRANSFORM = LIDAR_DT_SLAM_ODOMETRY_TF
|
||||
};
|
||||
|
||||
#define LOG_LEVEL_NONE 0
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_WARN 2
|
||||
@@ -79,6 +85,8 @@ extern int g_sendcloudrender;
|
||||
#include <nav_msgs/msg/odometry.hpp>
|
||||
#include <nav_msgs/msg/path.hpp>
|
||||
#include <sensor_msgs/msg/point_field.hpp>
|
||||
#include "tf2/LinearMath/Quaternion.h"
|
||||
#include "tf2_ros/transform_broadcaster.h"
|
||||
namespace ros {
|
||||
using namespace rclcpp;
|
||||
using namespace std_msgs::msg;
|
||||
@@ -106,7 +114,9 @@ extern int g_sendcloudrender;
|
||||
#include <nav_msgs/Odometry.h>
|
||||
#include <nav_msgs/Path.h>
|
||||
#include <sensor_msgs/Image.h>
|
||||
|
||||
#include <tf2_ros/transform_broadcaster.h>
|
||||
#include <tf2/LinearMath/Quaternion.h>
|
||||
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
|
||||
namespace ros {
|
||||
using namespace ::ros;
|
||||
using namespace sensor_msgs;
|
||||
@@ -454,7 +464,7 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
|
||||
// Create and publish RGB point cloud
|
||||
PointCloud2Msg output_msg;
|
||||
output_msg.header.frame_id = "map";
|
||||
output_msg.header.frame_id = "odin1_base_link";
|
||||
output_msg.header.stamp = rgb_msg->header.stamp; // Use original image timestamp
|
||||
output_msg.height = 1;
|
||||
output_msg.width = valid_point_num;
|
||||
@@ -523,7 +533,7 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
#endif
|
||||
|
||||
// Set message header
|
||||
msg->header.frame_id = "map";
|
||||
msg->header.frame_id = "odin1_base_link";
|
||||
msg->header.stamp = ns_to_ros_time(cloud.timestamp);
|
||||
|
||||
msg->height = cloud.height;
|
||||
@@ -901,9 +911,9 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
{
|
||||
#ifdef ROS2
|
||||
sensor_msgs::msg::PointCloud2 msg;
|
||||
msg.header.frame_id = "map";
|
||||
msg.header.frame_id = "odom";
|
||||
msg.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp);
|
||||
|
||||
|
||||
//RCLCPP_INFO(rclcpp::get_logger("device_cb"), "Point cloudrgba %ld",stream->imageList[0].timestamp);
|
||||
|
||||
size_t pt_size = sizeof(int32_t) * 3 + sizeof(int32_t) * 4;
|
||||
@@ -929,7 +939,7 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
sensor_msgs::PointCloud2Iterator<float> iter_rgb(msg, "rgb");
|
||||
#else
|
||||
sensor_msgs::PointCloud2 msg;
|
||||
msg.header.frame_id = "map";
|
||||
msg.header.frame_id = "odom";
|
||||
msg.header.stamp = ns_to_ros_time(stream->imageList[0].timestamp);
|
||||
|
||||
size_t pt_size = sizeof(int32_t) * 3 + sizeof(int32_t) * 4;
|
||||
@@ -1027,7 +1037,7 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
#endif
|
||||
}
|
||||
|
||||
void publishOdometry(capture_Image_List_t* stream, bool is_highfreq, bool show_path, bool show_camerapose) {
|
||||
void publishOdometry(capture_Image_List_t* stream, OdometryType odom_type, bool show_path, bool show_camerapose) {
|
||||
|
||||
#ifdef ROS2
|
||||
auto msg = nav_msgs::msg::Odometry();
|
||||
@@ -1035,8 +1045,8 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
ros::Odometry msg;
|
||||
#endif
|
||||
|
||||
msg.header.frame_id = "map";
|
||||
msg.child_frame_id = "base_link";
|
||||
msg.header.frame_id = "odom";
|
||||
msg.child_frame_id = "odin1_base_link";
|
||||
|
||||
//RCLCPP_INFO(rclcpp::get_logger("device_cb"), "odom %ld",odom_data->timestamp_ns);
|
||||
|
||||
@@ -1056,7 +1066,7 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
msg.pose.pose.orientation.w = static_cast<double>(odom_data->orient[3]) / 1e6;
|
||||
|
||||
// Enqueue binary logging for pose
|
||||
if (!is_highfreq && data_logger_) {
|
||||
if ((odom_type == OdometryType::STANDARD) && data_logger_) {
|
||||
const uint32_t idx_now = pose_index_.fetch_add(1, std::memory_order_relaxed);
|
||||
const double ts_sec = static_cast<double>(odom_data->timestamp_ns) / 1e9;
|
||||
float pose_arr[7];
|
||||
@@ -1112,127 +1122,193 @@ void process_pair(const ImageConstPtr &rgb_msg, const PointCloud2ConstPtr &pcd_m
|
||||
}
|
||||
|
||||
#ifdef ROS2
|
||||
if (is_highfreq) {
|
||||
odom_highfreq_publisher_->publish(std::move(msg));
|
||||
} else {
|
||||
odom_publisher_->publish(std::move(msg));
|
||||
switch(odom_type) {
|
||||
case OdometryType::STANDARD:
|
||||
{
|
||||
geometry_msgs::msg::TransformStamped transformStamped;
|
||||
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;
|
||||
transformStamped.transform.translation.y = msg.pose.pose.position.y;
|
||||
transformStamped.transform.translation.z = msg.pose.pose.position.z;
|
||||
transformStamped.transform.rotation.x = msg.pose.pose.orientation.x;
|
||||
transformStamped.transform.rotation.y = msg.pose.pose.orientation.y;
|
||||
transformStamped.transform.rotation.z = msg.pose.pose.orientation.z;
|
||||
transformStamped.transform.rotation.w = msg.pose.pose.orientation.w;
|
||||
tf_broadcaster->sendTransform(transformStamped);
|
||||
odom_publisher_->publish(msg);
|
||||
|
||||
// Publish odom trajectory as visualization markers (green lines connecting adjacent points)
|
||||
static visualization_msgs::msg::Marker marker;
|
||||
static std::vector<geometry_msgs::msg::Point> path_points;
|
||||
|
||||
if (show_path) {
|
||||
marker.header = msg.header;
|
||||
marker.ns = "odom_trajectory";
|
||||
marker.id = 0;
|
||||
marker.type = visualization_msgs::msg::Marker::LINE_STRIP;
|
||||
marker.action = visualization_msgs::msg::Marker::ADD;
|
||||
marker.pose.orientation.w = 1.0;
|
||||
marker.scale.x = 0.02; // Line width
|
||||
marker.color.r = 0.0;
|
||||
marker.color.g = 1.0;
|
||||
marker.color.b = 0.0;
|
||||
marker.color.a = 1.0;
|
||||
|
||||
geometry_msgs::msg::Point pt;
|
||||
pt.x = msg.pose.pose.position.x;
|
||||
pt.y = msg.pose.pose.position.y;
|
||||
pt.z = msg.pose.pose.position.z;
|
||||
path_points.push_back(pt);
|
||||
// Publish odom trajectory as visualization markers (green lines connecting adjacent points)
|
||||
static visualization_msgs::msg::Marker marker;
|
||||
static std::vector<geometry_msgs::msg::Point> path_points;
|
||||
|
||||
// Keep only recent points to avoid memory issues (e.g., last 1000 points)
|
||||
if (path_points.size() > 30000) {
|
||||
path_points.erase(path_points.begin());
|
||||
if (show_path) {
|
||||
marker.header = msg.header;
|
||||
marker.ns = "odom_trajectory";
|
||||
marker.id = 0;
|
||||
marker.type = visualization_msgs::msg::Marker::LINE_STRIP;
|
||||
marker.action = visualization_msgs::msg::Marker::ADD;
|
||||
marker.pose.orientation.w = 1.0;
|
||||
marker.scale.x = 0.02; // Line width
|
||||
marker.color.r = 0.0;
|
||||
marker.color.g = 1.0;
|
||||
marker.color.b = 0.0;
|
||||
marker.color.a = 1.0;
|
||||
|
||||
geometry_msgs::msg::Point pt;
|
||||
pt.x = msg.pose.pose.position.x;
|
||||
pt.y = msg.pose.pose.position.y;
|
||||
pt.z = msg.pose.pose.position.z;
|
||||
path_points.push_back(pt);
|
||||
|
||||
// Keep only recent points to avoid memory issues (e.g., last 1000 points)
|
||||
if (path_points.size() > 30000) {
|
||||
path_points.erase(path_points.begin());
|
||||
}
|
||||
|
||||
marker.points = path_points;
|
||||
|
||||
// Publish marker array
|
||||
static visualization_msgs::msg::MarkerArray marker_array;
|
||||
marker_array.markers.clear(); // Clear previous markers
|
||||
marker_array.markers.push_back(marker);
|
||||
path_publisher_->publish(marker_array);
|
||||
}
|
||||
|
||||
marker.points = path_points;
|
||||
|
||||
// Publish marker array
|
||||
static visualization_msgs::msg::MarkerArray marker_array;
|
||||
marker_array.markers.clear(); // Clear previous markers
|
||||
marker_array.markers.push_back(marker);
|
||||
path_publisher_->publish(marker_array);
|
||||
}
|
||||
|
||||
if (show_camerapose) {
|
||||
// camera pose visualization (ROS2)
|
||||
Eigen::Vector3d P(msg.pose.pose.position.x,
|
||||
msg.pose.pose.position.y,
|
||||
msg.pose.pose.position.z);
|
||||
Eigen::Quaterniond R(msg.pose.pose.orientation.w,
|
||||
msg.pose.pose.orientation.x,
|
||||
msg.pose.pose.orientation.y,
|
||||
msg.pose.pose.orientation.z);
|
||||
if (extrinsic_ok_) {
|
||||
P = P + R * t_ic_;
|
||||
R = R * R_ic_;
|
||||
if (show_camerapose) {
|
||||
// camera pose visualization (ROS2)
|
||||
Eigen::Vector3d P(msg.pose.pose.position.x,
|
||||
msg.pose.pose.position.y,
|
||||
msg.pose.pose.position.z);
|
||||
Eigen::Quaterniond R(msg.pose.pose.orientation.w,
|
||||
msg.pose.pose.orientation.x,
|
||||
msg.pose.pose.orientation.y,
|
||||
msg.pose.pose.orientation.z);
|
||||
if (extrinsic_ok_) {
|
||||
P = P + R * t_ic_;
|
||||
R = R * R_ic_;
|
||||
}
|
||||
cameraposevisual_.reset();
|
||||
cameraposevisual_.add_pose(P, R);
|
||||
cameraposevisual_.publish_by(*pub_camera_pose_visual_, msg.header);
|
||||
}
|
||||
cameraposevisual_.reset();
|
||||
cameraposevisual_.add_pose(P, R);
|
||||
cameraposevisual_.publish_by(*pub_camera_pose_visual_, msg.header);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OdometryType::HIGHFREQ:
|
||||
odom_highfreq_publisher_->publish(std::move(msg));
|
||||
break;
|
||||
case OdometryType::TRANSFORM:
|
||||
{
|
||||
geometry_msgs::msg::TransformStamped transformStamped;
|
||||
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;
|
||||
transformStamped.transform.translation.y = msg.pose.pose.position.y;
|
||||
transformStamped.transform.translation.z = msg.pose.pose.position.z;
|
||||
transformStamped.transform.rotation.x = msg.pose.pose.orientation.x;
|
||||
transformStamped.transform.rotation.y = msg.pose.pose.orientation.y;
|
||||
transformStamped.transform.rotation.z = msg.pose.pose.orientation.z;
|
||||
transformStamped.transform.rotation.w = msg.pose.pose.orientation.w;
|
||||
tf_broadcaster->sendTransform(transformStamped);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (is_highfreq) {
|
||||
odom_highfreq_publisher_.publish(msg);
|
||||
} else {
|
||||
odom_publisher_.publish(msg);
|
||||
switch(odom_type) {
|
||||
case OdometryType::STANDARD:
|
||||
{
|
||||
geometry_msgs::TransformStamped transformStamped;
|
||||
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;
|
||||
transformStamped.transform.translation.y = msg.pose.pose.position.y;
|
||||
transformStamped.transform.translation.z = msg.pose.pose.position.z;
|
||||
transformStamped.transform.rotation.x = msg.pose.pose.orientation.x;
|
||||
transformStamped.transform.rotation.y = msg.pose.pose.orientation.y;
|
||||
transformStamped.transform.rotation.z = msg.pose.pose.orientation.z;
|
||||
transformStamped.transform.rotation.w = msg.pose.pose.orientation.w;
|
||||
tf_broadcaster->sendTransform(transformStamped);
|
||||
odom_publisher_.publish(msg);
|
||||
|
||||
if (show_path) {
|
||||
// Publish odom trajectory as visualization markers (green lines connecting adjacent points)
|
||||
static visualization_msgs::Marker marker;
|
||||
static std::vector<geometry_msgs::Point> path_points;
|
||||
|
||||
marker.header = msg.header;
|
||||
marker.ns = "odom_trajectory";
|
||||
marker.id = 0;
|
||||
marker.type = visualization_msgs::Marker::LINE_STRIP;
|
||||
marker.action = visualization_msgs::Marker::ADD;
|
||||
marker.pose.orientation.w = 1.0;
|
||||
marker.scale.x = 0.02; // Line width
|
||||
marker.color.r = 0.0;
|
||||
marker.color.g = 1.0;
|
||||
marker.color.b = 0.0;
|
||||
marker.color.a = 1.0;
|
||||
|
||||
geometry_msgs::Point pt;
|
||||
pt.x = msg.pose.pose.position.x;
|
||||
pt.y = msg.pose.pose.position.y;
|
||||
pt.z = msg.pose.pose.position.z;
|
||||
path_points.push_back(pt);
|
||||
|
||||
// Keep only recent points to avoid memory issues (e.g., last 1000 points)
|
||||
if (path_points.size() > 30000) {
|
||||
path_points.erase(path_points.begin());
|
||||
}
|
||||
|
||||
marker.points = path_points;
|
||||
|
||||
// Publish marker array
|
||||
static visualization_msgs::MarkerArray marker_array;
|
||||
marker_array.markers.clear(); // Clear previous markers
|
||||
marker_array.markers.push_back(marker);
|
||||
path_publisher_.publish(marker_array);
|
||||
}
|
||||
|
||||
if (show_camerapose) {
|
||||
// camera pose visualization (ROS1)
|
||||
Eigen::Vector3d P(msg.pose.pose.position.x,
|
||||
msg.pose.pose.position.y,
|
||||
msg.pose.pose.position.z);
|
||||
Eigen::Quaterniond R(msg.pose.pose.orientation.w,
|
||||
msg.pose.pose.orientation.x,
|
||||
msg.pose.pose.orientation.y,
|
||||
msg.pose.pose.orientation.z);
|
||||
if (show_path) {
|
||||
// Publish odom trajectory as visualization markers (green lines connecting adjacent points)
|
||||
static visualization_msgs::Marker marker;
|
||||
static std::vector<geometry_msgs::Point> path_points;
|
||||
|
||||
if (extrinsic_ok_) {
|
||||
P = P + R * t_ic_;
|
||||
R = R * R_ic_;
|
||||
marker.header = msg.header;
|
||||
marker.ns = "odom_trajectory";
|
||||
marker.id = 0;
|
||||
marker.type = visualization_msgs::Marker::LINE_STRIP;
|
||||
marker.action = visualization_msgs::Marker::ADD;
|
||||
marker.pose.orientation.w = 1.0;
|
||||
marker.scale.x = 0.02; // Line width
|
||||
marker.color.r = 0.0;
|
||||
marker.color.g = 1.0;
|
||||
marker.color.b = 0.0;
|
||||
marker.color.a = 1.0;
|
||||
|
||||
geometry_msgs::Point pt;
|
||||
pt.x = msg.pose.pose.position.x;
|
||||
pt.y = msg.pose.pose.position.y;
|
||||
pt.z = msg.pose.pose.position.z;
|
||||
path_points.push_back(pt);
|
||||
|
||||
// Keep only recent points to avoid memory issues (e.g., last 1000 points)
|
||||
if (path_points.size() > 30000) {
|
||||
path_points.erase(path_points.begin());
|
||||
}
|
||||
|
||||
marker.points = path_points;
|
||||
|
||||
// Publish marker array
|
||||
static visualization_msgs::MarkerArray marker_array;
|
||||
marker_array.markers.clear(); // Clear previous markers
|
||||
marker_array.markers.push_back(marker);
|
||||
path_publisher_.publish(marker_array);
|
||||
}
|
||||
cameraposevisual_.reset();
|
||||
cameraposevisual_.add_pose(P, R);
|
||||
cameraposevisual_.publish_by(pub_camera_pose_visual_, msg.header);
|
||||
}
|
||||
|
||||
if (show_camerapose) {
|
||||
// camera pose visualization (ROS1)
|
||||
Eigen::Vector3d P(msg.pose.pose.position.x,
|
||||
msg.pose.pose.position.y,
|
||||
msg.pose.pose.position.z);
|
||||
Eigen::Quaterniond R(msg.pose.pose.orientation.w,
|
||||
msg.pose.pose.orientation.x,
|
||||
msg.pose.pose.orientation.y,
|
||||
msg.pose.pose.orientation.z);
|
||||
|
||||
if (extrinsic_ok_) {
|
||||
P = P + R * t_ic_;
|
||||
R = R * R_ic_;
|
||||
}
|
||||
cameraposevisual_.reset();
|
||||
cameraposevisual_.add_pose(P, R);
|
||||
cameraposevisual_.publish_by(pub_camera_pose_visual_, msg.header);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OdometryType::HIGHFREQ:
|
||||
odom_highfreq_publisher_.publish(msg);
|
||||
break;
|
||||
case OdometryType::TRANSFORM:
|
||||
{
|
||||
geometry_msgs::TransformStamped transformStamped;
|
||||
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;
|
||||
transformStamped.transform.translation.y = msg.pose.pose.position.y;
|
||||
transformStamped.transform.translation.z = msg.pose.pose.position.z;
|
||||
transformStamped.transform.rotation.x = msg.pose.pose.orientation.x;
|
||||
transformStamped.transform.rotation.y = msg.pose.pose.orientation.y;
|
||||
transformStamped.transform.rotation.z = msg.pose.pose.orientation.z;
|
||||
transformStamped.transform.rotation.w = msg.pose.pose.orientation.w;
|
||||
tf_broadcaster->sendTransform(transformStamped);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1436,18 +1512,23 @@ private:
|
||||
|
||||
void initialize_publishers() {
|
||||
#ifdef ROS2
|
||||
imu_pub_ = node_->create_publisher<ros::Imu>("odin1/imu", 10);
|
||||
rgb_pub_ = node_->create_publisher<ros::Image>("odin1/image", 10);
|
||||
cloud_pub_ = node_->create_publisher<ros::PointCloud2>("odin1/cloud_raw", 10);
|
||||
xyzrgbacloud_pub_ = node_->create_publisher<ros::PointCloud2>("odin1/cloud_slam", 10);
|
||||
odom_publisher_ = node_->create_publisher<ros::Odometry>("odin1/odometry", 10);
|
||||
odom_highfreq_publisher_ = node_->create_publisher<ros::Odometry>("odin1/odometry_highfreq", 10);
|
||||
path_publisher_ = node_->create_publisher<visualization_msgs::msg::MarkerArray>("odin1/path", 10);
|
||||
pub_camera_pose_visual_ = node_->create_publisher<visualization_msgs::msg::MarkerArray>("odin1/camera_pose_visual", 10);
|
||||
rgbcloud_pub_ = node_->create_publisher<sensor_msgs::msg::PointCloud2>("odin1/cloud_render", 10);
|
||||
compressed_rgb_pub_ = node_->create_publisher<sensor_msgs::msg::CompressedImage>("odin1/image/compressed", 10);
|
||||
undistort_rgb_pub_ = node_->create_publisher<sensor_msgs::msg::Image>("odin1/image/undistorted", 10);
|
||||
intensity_gray_pub_ = node_->create_publisher<sensor_msgs::msg::Image>("odin1/image/intensity_gray", 10);
|
||||
auto qos_profile = rclcpp::QoS(1)
|
||||
.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE)
|
||||
.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);
|
||||
|
||||
imu_pub_ = node_->create_publisher<ros::Imu>("odin1/imu", qos_profile);
|
||||
rgb_pub_ = node_->create_publisher<ros::Image>("odin1/image", qos_profile);
|
||||
cloud_pub_ = node_->create_publisher<ros::PointCloud2>("odin1/cloud_raw", qos_profile);
|
||||
xyzrgbacloud_pub_ = node_->create_publisher<ros::PointCloud2>("odin1/cloud_slam", qos_profile);
|
||||
odom_publisher_ = node_->create_publisher<ros::Odometry>("odin1/odometry", qos_profile);
|
||||
odom_highfreq_publisher_ = node_->create_publisher<ros::Odometry>("odin1/odometry_highfreq", qos_profile);
|
||||
path_publisher_ = node_->create_publisher<visualization_msgs::msg::MarkerArray>("odin1/path", qos_profile);
|
||||
pub_camera_pose_visual_ = node_->create_publisher<visualization_msgs::msg::MarkerArray>("odin1/camera_pose_visual", qos_profile);
|
||||
rgbcloud_pub_ = node_->create_publisher<sensor_msgs::msg::PointCloud2>("odin1/cloud_render", qos_profile);
|
||||
compressed_rgb_pub_ = node_->create_publisher<sensor_msgs::msg::CompressedImage>("odin1/image/compressed", qos_profile);
|
||||
undistort_rgb_pub_ = node_->create_publisher<sensor_msgs::msg::Image>("odin1/image/undistorted", qos_profile);
|
||||
intensity_gray_pub_ = node_->create_publisher<sensor_msgs::msg::Image>("odin1/image/intensity_gray", qos_profile);
|
||||
tf_broadcaster = std::make_unique<tf2_ros::TransformBroadcaster>(node_);
|
||||
#endif
|
||||
}
|
||||
#ifdef ROS1
|
||||
@@ -1464,6 +1545,7 @@ private:
|
||||
compressed_rgb_pub_ = nh.advertise<sensor_msgs::CompressedImage>("odin1/image/compressed", 10);
|
||||
undistort_rgb_pub_ = nh.advertise<sensor_msgs::Image>("odin1/image/undistorted", 10);
|
||||
intensity_gray_pub_ = nh.advertise<sensor_msgs::Image>("odin1/image/intensity_gray", 10);
|
||||
tf_broadcaster = std::make_unique<tf2_ros::TransformBroadcaster>();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1484,6 +1566,7 @@ private:
|
||||
rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr intensity_gray_pub_;
|
||||
rclcpp::Publisher<visualization_msgs::msg::MarkerArray>::SharedPtr pub_camera_pose_visual_;
|
||||
camera_pose_visualization cameraposevisual_;
|
||||
std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster;
|
||||
#else
|
||||
ros::Publisher imu_pub_;
|
||||
ros::Publisher rgb_pub_;
|
||||
@@ -1500,6 +1583,7 @@ private:
|
||||
ros::Publisher compressed_rgb_pub_; // New compressed image publisher
|
||||
ros::Publisher undistort_rgb_pub_;
|
||||
ros::Publisher intensity_gray_pub_;
|
||||
std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -197,6 +197,54 @@ void lidar_log_set_level(lidar_log_level_e level);
|
||||
*/
|
||||
int lidar_get_version(device_handle device);
|
||||
|
||||
/**
|
||||
* @brief Set custom algorithm parameters for the device
|
||||
*
|
||||
* Sends custom parameter settings to the device.
|
||||
*
|
||||
* @param device Handle to the target device
|
||||
* @param param_name String name of the parameter to set
|
||||
* @param value_data Pointer to the value data to set for the parameter
|
||||
* @param value_length Length of the value data in bytes
|
||||
* @return int 0 on success, negative error code on failure
|
||||
*/
|
||||
int lidar_set_custom_parameter(device_handle device, const char* param_name, const void* value_data, size_t value_length);
|
||||
|
||||
/**
|
||||
* @brief Get custom algorithm parameters for the device
|
||||
*
|
||||
* Get custom parameter settings from the device.
|
||||
*
|
||||
* @param device Handle to the target device
|
||||
* @param param_name String name of the parameter to get
|
||||
* @param value Integer value to get for the parameter
|
||||
* @return int 0 on success, negative error code on failure
|
||||
*/
|
||||
int lidar_get_custom_parameter(device_handle device, const char* param_name, int* value);
|
||||
|
||||
/**
|
||||
* @brief Set the map file used for relocalization
|
||||
*
|
||||
* Read & send specified map file to device for relocalization
|
||||
*
|
||||
* @param device Handle to the target device
|
||||
* @param abs_path Absolute path to the map file
|
||||
* @return int 0 on success, otherwise on failure
|
||||
*/
|
||||
int lidar_set_relocalization_map(device_handle device, const char* abs_path);
|
||||
|
||||
/**
|
||||
* @brief Get the mapping result file from device
|
||||
*
|
||||
* Read & send specified map file from device to host
|
||||
*
|
||||
* @param device Handle to the target device
|
||||
* @param dest_dir Destination directory to save the map file
|
||||
* @param file_name File name to save the map file
|
||||
* @return int 0 on success, -1 on failure without error code, error code (> 0) otherwise
|
||||
*/
|
||||
int lidar_get_mapping_result(device_handle device, const char* dest_dir, const char* file_name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -47,14 +47,15 @@ typedef enum {
|
||||
} lidar_mode_e;
|
||||
|
||||
typedef enum {
|
||||
LIDAR_DT_NONE = 0,
|
||||
LIDAR_DT_RAW_RGB = 1 << 1,
|
||||
LIDAR_DT_RAW_IMU = 1 << 2,
|
||||
LIDAR_DT_RAW_DTOF = 1 << 3,
|
||||
LIDAR_DT_SLAM_CLOUD = 1 << 4,
|
||||
LIDAR_DT_SLAM_ODOMETRY = 1 << 5,
|
||||
LIDAR_DT_DEV_STATUS = 1 << 6,
|
||||
LIDAR_DT_SLAM_ODOMETRY_HIGHFREQ = 1 << 7,
|
||||
LIDAR_DT_NONE = 0,
|
||||
LIDAR_DT_RAW_RGB,
|
||||
LIDAR_DT_RAW_IMU,
|
||||
LIDAR_DT_RAW_DTOF,
|
||||
LIDAR_DT_SLAM_CLOUD,
|
||||
LIDAR_DT_SLAM_ODOMETRY,
|
||||
LIDAR_DT_DEV_STATUS,
|
||||
LIDAR_DT_SLAM_ODOMETRY_HIGHFREQ,
|
||||
LIDAR_DT_SLAM_ODOMETRY_TF,
|
||||
} lidar_data_type_e;
|
||||
|
||||
typedef struct {
|
||||
|
||||
+62
-4
@@ -13,25 +13,83 @@ limitations under the License.
|
||||
#ifndef YAML_PARSER_H
|
||||
#define YAML_PARSER_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include "lidar_api.h"
|
||||
|
||||
namespace odin_ros_driver {
|
||||
|
||||
// Data type enum for supporting different value types
|
||||
enum class DataType {
|
||||
INT_TYPE,
|
||||
FLOAT_ARRAY_TYPE,
|
||||
INT_ARRAY_TYPE,
|
||||
};
|
||||
|
||||
// Generic parameter value holder
|
||||
struct ParameterValue {
|
||||
DataType type;
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
ParameterValue() : type(DataType::INT_TYPE) {}
|
||||
|
||||
template<typename T>
|
||||
void setData(const T& value) {
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&value);
|
||||
data.assign(ptr, ptr + sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void setArray(const std::vector<T>& arr) {
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(arr.data());
|
||||
data.assign(ptr, ptr + arr.size() * sizeof(T));
|
||||
}
|
||||
|
||||
size_t getSize() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
const void* getData() const {
|
||||
return data.empty() ? nullptr : data.data();
|
||||
}
|
||||
};
|
||||
|
||||
class YamlParser {
|
||||
public:
|
||||
YamlParser(const std::string& config_file);
|
||||
|
||||
|
||||
bool loadConfig();
|
||||
const std::map<std::string, int>& getRegisterKeys() const;
|
||||
const std::map<std::string, std::string>& getRegisterKeysStrVal() const;
|
||||
const std::map<std::string, ParameterValue>& getCustomParameters() const;
|
||||
void printConfig() const;
|
||||
bool applyCustomParameters(device_handle device);
|
||||
int getCustomParameterInt(const std::string& param_name, int default_value) const;
|
||||
|
||||
int getCustomMapMode(int default_value) const {
|
||||
auto it = custom_parameters_.find("map_mode");
|
||||
if (it != custom_parameters_.end() && it->second.type == DataType::INT_TYPE) {
|
||||
printf("custom_map_mode = %d\n", *(int*)it->second.getData());
|
||||
return *(int*)it->second.getData();
|
||||
} else {
|
||||
return default_value;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::string config_file_;
|
||||
std::string config_file_;
|
||||
std::map<std::string, int> register_keys_;
|
||||
std::map<std::string, std::string> register_keys_str_val_;
|
||||
std::map<std::string, ParameterValue> custom_parameters_;
|
||||
|
||||
std::unordered_set<std::string> allowed_key_w_str_val = {"relocalization_map_abs_path", "mapping_result_dest_dir", "mapping_result_file_name"};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Binary file not shown.
Binary file not shown.
@@ -13,8 +13,14 @@
|
||||
<depend>std_msgs</depend>
|
||||
<depend>sensor_msgs</depend>
|
||||
<depend>nav_msgs</depend>
|
||||
<depend>geometry_msgs</depend>
|
||||
<depend>cv_bridge</depend>
|
||||
<depend>image_transport</depend>
|
||||
<depend>pcl_conversions</depend>
|
||||
<depend>message_filters</depend>
|
||||
<depend>tf2</depend>
|
||||
<depend>tf2_ros</depend>
|
||||
<depend>tf2_geometry_msgs</depend>
|
||||
<!-- Specify build type as ament -->
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: ./set_param.sh <parameter_name> <value>
|
||||
# Example: ./set_param.sh save_map 1
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 <parameter_name> <value>"
|
||||
echo "Example: $0 save_map 1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PARAM_NAME=$1
|
||||
VALUE=$2
|
||||
|
||||
COMMAND_FILE="/tmp/odin_command.txt"
|
||||
|
||||
# Create the command file with the parameter
|
||||
echo "set $PARAM_NAME $VALUE" > "$COMMAND_FILE"
|
||||
|
||||
echo "Command sent: set $PARAM_NAME $VALUE"
|
||||
echo "Command file: $COMMAND_FILE"
|
||||
+401
-31
@@ -37,6 +37,7 @@ limitations under the License.
|
||||
#include <array>
|
||||
// #include <yaml-cpp/yaml.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#ifdef ROS2
|
||||
#include <ament_index_cpp/get_package_share_directory.hpp>
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
@@ -44,7 +45,7 @@ limitations under the License.
|
||||
#include <ros/package.h>
|
||||
#include <ros/ros.h>
|
||||
#endif
|
||||
#define ros_driver_version "0.5.2"
|
||||
#define ros_driver_version "0.6.0"
|
||||
// Global variable declarations
|
||||
static device_handle odinDevice = nullptr;
|
||||
static std::atomic<bool> deviceConnected(false);
|
||||
@@ -52,15 +53,23 @@ static std::atomic<bool> deviceDisconnected(false); // Device disconnection fla
|
||||
static std::mutex device_mutex; // Device operation mutex lock
|
||||
static std::atomic<bool> g_connection_timeout(false);
|
||||
static std::atomic<bool> g_usb_version_error(false);
|
||||
static std::atomic<bool> g_shutdown_requested(false); // Signal handler flag
|
||||
#ifdef ROS2
|
||||
std::shared_ptr<MultiSensorPublisher> g_ros_object = nullptr;
|
||||
#else
|
||||
MultiSensorPublisher* g_ros_object = nullptr;
|
||||
#endif
|
||||
|
||||
int g_log_level = LOG_LEVEL_INFO;
|
||||
int g_log_level = LOG_LEVEL_INFO;
|
||||
int g_show_fps = 0; // FPS display toggle control
|
||||
|
||||
// Custom parameter monitoring
|
||||
static std::atomic<bool> g_param_monitor_running(false);
|
||||
static std::thread g_param_monitor_thread;
|
||||
|
||||
// Command file monitoring
|
||||
static std::string g_command_file_path = "";
|
||||
|
||||
static std::mutex g_rgb_mutex;
|
||||
static std::shared_ptr<cv::Mat> g_latest_bgr;
|
||||
static uint64_t g_latest_rgb_timestamp = 0;
|
||||
@@ -69,6 +78,7 @@ static capture_Image_List_t g_latest_rgb;
|
||||
static bool g_renderer_initialized = false;
|
||||
static std::shared_ptr<rawCloudRender> g_renderer = nullptr;
|
||||
std::string calib_file_ = "";
|
||||
static std::shared_ptr<odin_ros_driver::YamlParser> g_parser = nullptr;
|
||||
|
||||
// usb device
|
||||
static std::string TARGET_VENDOR = "2207";
|
||||
@@ -89,9 +99,19 @@ int g_show_path = 0;
|
||||
int g_show_camerapose = 0;
|
||||
|
||||
std::filesystem::path log_root_dir_;
|
||||
int g_custom_map_mode = 0;
|
||||
|
||||
std::string g_relocalization_map_abs_path = "";
|
||||
std::string g_mapping_result_dest_dir = "";
|
||||
std::string g_mapping_result_file_name = "";
|
||||
|
||||
const char* DEV_STATUS_CSV_FILE = "dev_status.csv";
|
||||
FILE* dev_status_csv_file = nullptr;
|
||||
|
||||
std::filesystem::path map_root_dir_;
|
||||
|
||||
char driver_start_time[32];
|
||||
|
||||
typedef struct {
|
||||
struct timespec start = {0, 0};
|
||||
struct timespec last = {0, 0};
|
||||
@@ -206,6 +226,240 @@ void collect_children(pid_t pid, std::vector<pid_t>& all) {
|
||||
|
||||
void clear_all_queues();
|
||||
|
||||
static bool convert_calib_to_cam_in_ex(const std::string& calib_path, const std::filesystem::path& out_path);
|
||||
|
||||
// Signal handler for Ctrl+C
|
||||
static void signal_handler(int signum) {
|
||||
if (signum == SIGINT || signum == SIGTERM) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("signal_handler"), "Received signal %d, shutting down...", signum);
|
||||
#else
|
||||
ROS_INFO("Received signal %d, shutting down...", signum);
|
||||
#endif
|
||||
|
||||
g_shutdown_requested = true;
|
||||
|
||||
// Stop custom parameter monitoring thread
|
||||
g_param_monitor_running = false;
|
||||
if (g_param_monitor_thread.joinable()) {
|
||||
g_param_monitor_thread.join();
|
||||
}
|
||||
|
||||
// Close device
|
||||
if (odinDevice) {
|
||||
// Convert calib.yaml to cam_in_ex.txt at program end
|
||||
if (g_ros_object) {
|
||||
const std::filesystem::path out_path = g_ros_object->get_root_dir() / "image" / "cam_in_ex.txt";
|
||||
(void)convert_calib_to_cam_in_ex(calib_file_, out_path);
|
||||
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"), "pose_index: %d", g_ros_object->get_pose_index());
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"), "cloud_index: %d", g_ros_object->get_cloud_index());
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"), "image_index: %d", g_ros_object->get_image_index());
|
||||
#else
|
||||
ROS_INFO("pose_index: %d", g_ros_object->get_pose_index());
|
||||
ROS_INFO("cloud_index: %d", g_ros_object->get_cloud_index());
|
||||
ROS_INFO("image_index: %d", g_ros_object->get_image_index());
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("signal_handler"), "Closing device...");
|
||||
#else
|
||||
ROS_INFO("Closing device...");
|
||||
#endif
|
||||
|
||||
if (lidar_stop_stream(odinDevice, LIDAR_MODE_SLAM))
|
||||
{
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"), "lidar_stop_stream failed");
|
||||
#else
|
||||
ROS_INFO("lidar_stop_stream failed");
|
||||
#endif
|
||||
}
|
||||
odinDevice = nullptr;
|
||||
}
|
||||
|
||||
// Deinitialize lidar system
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("signal_handler"), "Deinitializing lidar system...");
|
||||
#else
|
||||
ROS_INFO("Deinitializing lidar system...");
|
||||
#endif
|
||||
lidar_system_deinit();
|
||||
|
||||
// Close CSV file
|
||||
if (dev_status_csv_file) {
|
||||
std::fflush(dev_status_csv_file);
|
||||
fclose(dev_status_csv_file);
|
||||
dev_status_csv_file = nullptr;
|
||||
}
|
||||
|
||||
// Shutdown ROS
|
||||
#ifdef ROS2
|
||||
rclcpp::shutdown();
|
||||
#else
|
||||
ros::shutdown();
|
||||
#endif
|
||||
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Custom parameter monitoring function
|
||||
static void custom_parameter_monitor() {
|
||||
int last_save_map_val = -1;
|
||||
while (g_param_monitor_running && deviceConnected) {
|
||||
if (odinDevice) {
|
||||
if (g_custom_map_mode == 1) {
|
||||
int value = 0;
|
||||
int result = lidar_get_custom_parameter(odinDevice, "save_map", &value);
|
||||
|
||||
if (result == 0) {
|
||||
// #ifdef ROS2
|
||||
// RCLCPP_INFO(rclcpp::get_logger("param_monitor"), "save_map = %d", value);
|
||||
// #else
|
||||
// ROS_INFO("save_map = %d", value);
|
||||
// #endif
|
||||
|
||||
if (last_save_map_val == 1 && value == 0) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm{};
|
||||
#ifdef _WIN32
|
||||
localtime_s(&tm, &t);
|
||||
#else
|
||||
localtime_r(&t, &tm);
|
||||
#endif
|
||||
char map_save_time[32];
|
||||
std::strftime(map_save_time, sizeof(map_save_time), "%Y%m%d_%H%M%S", &tm);
|
||||
|
||||
std::string map_dir = g_mapping_result_dest_dir != "" ? g_mapping_result_dest_dir : map_root_dir_.string();
|
||||
std::string map_name = g_mapping_result_file_name != "" ? g_mapping_result_file_name : "map_" + std::string(map_save_time) + ".bin";
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("param_monitor"), "Map is saved on device, now transfering to [%s/%s]", map_dir.c_str(), map_name.c_str());
|
||||
#else
|
||||
ROS_INFO("Map is saved on device, now transfering to [%s/%s]", map_dir.c_str(), map_name.c_str());
|
||||
#endif
|
||||
int ret = lidar_get_mapping_result(odinDevice, map_dir.c_str(), map_name.c_str());
|
||||
if (ret < 0 ) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("param_monitor"), "Failed to get mapping result");
|
||||
#else
|
||||
ROS_WARN("Failed to get mapping result");
|
||||
#endif
|
||||
} else if (ret == 0) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("param_monitor"), "map get success");
|
||||
#else
|
||||
ROS_INFO("map get success");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("param_monitor"), "Failed to get mapping result, error code: %d", ret);
|
||||
#else
|
||||
ROS_WARN("Failed to get mapping result, error code: %d", ret);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
last_save_map_val = value;
|
||||
|
||||
} else {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("param_monitor"),
|
||||
"Failed to get save_map parameter, error: %d", result);
|
||||
#else
|
||||
ROS_WARN("Failed to get save_map parameter, error: %d", result);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep for 1 second (1Hz)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
// Process command from file
|
||||
static void process_command_file() {
|
||||
if (!std::filesystem::exists(g_command_file_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ifstream file(g_command_file_path);
|
||||
if (!file.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
if (std::getline(file, line)) {
|
||||
file.close();
|
||||
|
||||
// Delete the command file after reading
|
||||
std::filesystem::remove(g_command_file_path);
|
||||
|
||||
if (line.empty()) return;
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string command, param_name, value_str;
|
||||
|
||||
if (!(iss >> command >> param_name >> value_str)) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("command_processor"), "Invalid command format. Usage: set <parameter_name> <value>");
|
||||
#else
|
||||
ROS_WARN("Invalid command format. Usage: set <parameter_name> <value>");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == "set") {
|
||||
if (!deviceConnected || !odinDevice) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("command_processor"), "Device not connected!");
|
||||
#else
|
||||
ROS_WARN("Device not connected!");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int value = std::stoi(value_str);
|
||||
int result = lidar_set_custom_parameter(odinDevice, param_name.c_str(), &value, sizeof(int));
|
||||
|
||||
if (result == 0) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("command_processor"),
|
||||
"Successfully set %s = %d", param_name.c_str(), value);
|
||||
#else
|
||||
ROS_INFO("Successfully set %s = %d", param_name.c_str(), value);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef ROS2
|
||||
RCLCPP_ERROR(rclcpp::get_logger("command_processor"),
|
||||
"Failed to set %s = %d, error: %d", param_name.c_str(), value, result);
|
||||
#else
|
||||
ROS_ERROR("Failed to set %s = %d, error: %d", param_name.c_str(), value, result);
|
||||
#endif
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_ERROR(rclcpp::get_logger("command_processor"), "Invalid value: %s", value_str.c_str());
|
||||
#else
|
||||
ROS_ERROR("Invalid value: %s", value_str.c_str());
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("command_processor"), "Unknown command: %s", command.c_str());
|
||||
#else
|
||||
ROS_WARN("Unknown command: %s", command.c_str());
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
// detect USB3.0
|
||||
bool isUsb3OrHigher(const std::string& vendorId, const std::string& productId) {
|
||||
std::string command = "lsusb -d " + vendorId + ":" + productId + " -v | grep 'bcdUSB'";
|
||||
@@ -510,7 +764,7 @@ static void lidar_data_callback(const lidar_data_t *data, void *user_data)
|
||||
break;
|
||||
case LIDAR_DT_SLAM_ODOMETRY:
|
||||
if (g_sendodom) {
|
||||
g_ros_object->publishOdometry((capture_Image_List_t *)&data->stream, false, g_show_path, g_show_camerapose);
|
||||
g_ros_object->publishOdometry((capture_Image_List_t *)&data->stream, OdometryType::STANDARD, g_show_path, g_show_camerapose);
|
||||
}
|
||||
update_count(&slam_odom_rx_fps);
|
||||
break;
|
||||
@@ -667,11 +921,16 @@ static void lidar_data_callback(const lidar_data_t *data, void *user_data)
|
||||
case LIDAR_DT_SLAM_ODOMETRY_HIGHFREQ:
|
||||
{
|
||||
if (g_sendodom) {
|
||||
g_ros_object->publishOdometry((capture_Image_List_t *)&data->stream, true, false, false);
|
||||
g_ros_object->publishOdometry((capture_Image_List_t *)&data->stream, OdometryType::HIGHFREQ, false, false);
|
||||
}
|
||||
update_count(&slam_odom_highfreq_rx_fps);
|
||||
}
|
||||
break;
|
||||
case LIDAR_DT_SLAM_ODOMETRY_TF:
|
||||
{
|
||||
g_ros_object->publishOdometry((capture_Image_List_t *)&data->stream, OdometryType::TRANSFORM, false, false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Unknown lidar data type: %x", data->type);
|
||||
return;
|
||||
@@ -845,6 +1104,42 @@ static void lidar_device_callback(const lidar_device_info_t* device, bool attach
|
||||
odinDevice = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply custom parameters after setting mode
|
||||
if (g_parser && !g_parser->applyCustomParameters(odinDevice)) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_WARN(rclcpp::get_logger("device_cb"), "Some custom parameters failed to apply");
|
||||
#else
|
||||
ROS_WARN("Some custom parameters failed to apply");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"), "Custom map mode: %d", g_custom_map_mode);
|
||||
#else
|
||||
ROS_INFO("Custom map mode: %d", g_custom_map_mode);
|
||||
#endif
|
||||
|
||||
if (g_custom_map_mode == 2) {
|
||||
if (g_relocalization_map_abs_path != "" && std::filesystem::exists(g_relocalization_map_abs_path) &&
|
||||
lidar_set_relocalization_map(odinDevice, g_relocalization_map_abs_path.c_str()) == 0) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"), "Relocalization map set successfully");
|
||||
#else
|
||||
ROS_INFO("Relocalization map set successfully");
|
||||
#endif
|
||||
} else {
|
||||
#ifdef ROS2
|
||||
RCLCPP_ERROR(rclcpp::get_logger("device_cb"), "Relocalization map path set fail");
|
||||
#else
|
||||
ROS_ERROR("Relocalization map path set fail");
|
||||
#endif
|
||||
lidar_close_device(odinDevice);
|
||||
lidar_destory_device(odinDevice);
|
||||
odinDevice = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lidar_data_callback_info_t data_callback_info;
|
||||
data_callback_info.data_callback = lidar_data_callback;
|
||||
@@ -937,7 +1232,18 @@ static void lidar_device_callback(const lidar_device_info_t* device, bool attach
|
||||
|
||||
software_connect_timing = false;
|
||||
deviceConnected = true;
|
||||
deviceDisconnected = false;
|
||||
deviceDisconnected = false;
|
||||
|
||||
// Start custom parameter monitoring thread
|
||||
g_param_monitor_running = true;
|
||||
g_param_monitor_thread = std::thread(custom_parameter_monitor);
|
||||
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("device_cb"),
|
||||
"Command interface ready. Use: echo 'set save_map 1' > %s", g_command_file_path.c_str());
|
||||
#else
|
||||
ROS_INFO("Command interface ready. Use: echo 'set save_map 1' > %s", g_command_file_path.c_str());
|
||||
#endif
|
||||
|
||||
bool load_status = g_ros_object->loadCameraParams(calib_config);
|
||||
if (g_sendrgb_undistort && load_status == 0) {
|
||||
@@ -962,6 +1268,13 @@ static void lidar_device_callback(const lidar_device_info_t* device, bool attach
|
||||
|
||||
deviceConnected = false;
|
||||
deviceDisconnected = true;
|
||||
|
||||
// Stop custom parameter monitoring thread
|
||||
g_param_monitor_running = false;
|
||||
if (g_param_monitor_thread.joinable()) {
|
||||
g_param_monitor_thread.join();
|
||||
}
|
||||
|
||||
|
||||
clear_all_queues();
|
||||
|
||||
@@ -991,6 +1304,10 @@ int main(int argc, char *argv[])
|
||||
g_ros_object = new MultiSensorPublisher(nh);
|
||||
#endif
|
||||
|
||||
// Register signal handlers for Ctrl+C
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
try {
|
||||
#ifdef ROS2
|
||||
std::string package_path = get_package_source_directory();
|
||||
@@ -998,10 +1315,20 @@ int main(int argc, char *argv[])
|
||||
#else
|
||||
std::string package_path = get_package_share_path("odin_ros_driver");
|
||||
#endif
|
||||
std::string config_file = package_path + "/config/control_command.yaml";
|
||||
|
||||
odin_ros_driver::YamlParser parser(config_file);
|
||||
if (!parser.loadConfig()) {
|
||||
std::string config_dir = package_path + "/config";
|
||||
std::string config_file = config_dir + "/control_command.yaml";
|
||||
|
||||
// Initialize command file path to /tmp/odin_command.txt
|
||||
g_command_file_path = "/tmp/odin_command.txt";
|
||||
|
||||
#ifdef ROS2
|
||||
RCLCPP_INFO(rclcpp::get_logger("init"), "Command file path set to: %s", g_command_file_path.c_str());
|
||||
#else
|
||||
ROS_INFO("Command file path set to: %s", g_command_file_path.c_str());
|
||||
#endif
|
||||
|
||||
g_parser = std::make_shared<odin_ros_driver::YamlParser>(config_file);
|
||||
if (!g_parser->loadConfig()) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_ERROR(node->get_logger(), "Failed to load config file: %s", config_file.c_str());
|
||||
#else
|
||||
@@ -1010,8 +1337,9 @@ int main(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto keys = parser.getRegisterKeys();
|
||||
parser.printConfig();
|
||||
auto keys = g_parser->getRegisterKeys();
|
||||
auto keys_w_str_val = g_parser->getRegisterKeysStrVal();
|
||||
g_parser->printConfig();
|
||||
|
||||
auto get_key_value = [&](const std::string& key, int default_value) -> int {
|
||||
auto it = keys.find(key);
|
||||
@@ -1033,52 +1361,74 @@ int main(int argc, char *argv[])
|
||||
g_show_path = get_key_value("showpath", 0);
|
||||
g_show_camerapose = get_key_value("showcamerapose", 0);
|
||||
g_log_level = get_key_value("log_devel", LOG_LEVEL_INFO);
|
||||
|
||||
auto get_key_str_value = [&](const std::string& key, const std::string& default_value) -> std::string {
|
||||
auto it = keys_w_str_val.find(key);
|
||||
return it != keys_w_str_val.end() ? it->second : default_value;
|
||||
};
|
||||
|
||||
g_relocalization_map_abs_path = get_key_str_value("relocalization_map_abs_path", "");
|
||||
g_mapping_result_dest_dir = get_key_str_value("mapping_result_dest_dir", "");
|
||||
g_mapping_result_file_name = get_key_str_value("mapping_result_file_name", "");
|
||||
|
||||
g_custom_map_mode = g_parser->getCustomMapMode(2);
|
||||
|
||||
lidar_log_set_level(LIDAR_LOG_INFO);
|
||||
|
||||
const std::string package_name = "odin_ros_driver";
|
||||
std::string data_dir = "";
|
||||
std::string log_dir = "";
|
||||
std::string map_dir = "";
|
||||
#ifdef ROS2
|
||||
char* ros_workspace = std::getenv("COLCON_PREFIX_PATH");
|
||||
if (ros_workspace) {
|
||||
std::string workspace_path(ros_workspace);
|
||||
size_t pos = workspace_path.find("/install");
|
||||
if (pos != std::string::npos) {
|
||||
data_dir = workspace_path.substr(0, pos) + "/src/odin_ros_driver/recorddata";
|
||||
log_dir = workspace_path.substr(0, pos) + "/src/odin_ros_driver/log";
|
||||
std::string workspace_path(ros_workspace);
|
||||
size_t pos = workspace_path.find("/install");
|
||||
if (pos != std::string::npos) {
|
||||
data_dir = workspace_path.substr(0, pos) + "/src/odin_ros_driver/recorddata";
|
||||
log_dir = workspace_path.substr(0, pos) + "/src/odin_ros_driver/log";
|
||||
map_dir = workspace_path.substr(0, pos) + "/src/odin_ros_driver/map";
|
||||
} else {
|
||||
data_dir = ament_index_cpp::get_package_share_directory(package_name) + "/recorddata";
|
||||
log_dir = ament_index_cpp::get_package_share_directory(package_name) + "/log";
|
||||
map_dir = ament_index_cpp::get_package_share_directory(package_name) + "/map";
|
||||
}
|
||||
} else {
|
||||
data_dir = ament_index_cpp::get_package_share_directory(package_name) + "/recorddata";
|
||||
log_dir = ament_index_cpp::get_package_share_directory(package_name) + "/log";
|
||||
}
|
||||
} else {
|
||||
data_dir = ament_index_cpp::get_package_share_directory(package_name) + "/recorddata";
|
||||
log_dir = ament_index_cpp::get_package_share_directory(package_name) + "/log";
|
||||
map_dir = ament_index_cpp::get_package_share_directory(package_name) + "/map";
|
||||
}
|
||||
#else
|
||||
data_dir = ros::package::getPath(package_name) + "/recorddata";
|
||||
log_dir = ros::package::getPath(package_name) + "/log";
|
||||
map_dir = ros::package::getPath(package_name) + "/map";
|
||||
#endif
|
||||
|
||||
if (g_record_data) {
|
||||
g_ros_object->initialize_data_logger(data_dir);
|
||||
}
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm{};
|
||||
#ifdef _WIN32
|
||||
localtime_s(&tm, &t);
|
||||
#else
|
||||
localtime_r(&t, &tm);
|
||||
#endif
|
||||
std::strftime(driver_start_time, sizeof(driver_start_time), "%Y%m%d_%H%M%S", &tm);
|
||||
|
||||
if (g_devstatus_log) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(now);
|
||||
std::tm tm{};
|
||||
#ifdef _WIN32
|
||||
localtime_s(&tm, &t);
|
||||
#else
|
||||
localtime_r(&t, &tm);
|
||||
#endif
|
||||
char buf[32];
|
||||
std::strftime(buf, sizeof(buf), "%Y%m%d_%H%M%S", &tm);
|
||||
std::string folder_name = std::string("Driver_") + std::string(buf);
|
||||
std::string folder_name = std::string("Driver_") + std::string(driver_start_time);
|
||||
log_root_dir_ = std::filesystem::path(log_dir) / folder_name;
|
||||
std::filesystem::create_directories(log_root_dir_);
|
||||
}
|
||||
|
||||
if (g_custom_map_mode == 1 && g_mapping_result_dest_dir == "") {
|
||||
map_root_dir_ = std::filesystem::path(map_dir) / driver_start_time;
|
||||
std::filesystem::create_directories(map_root_dir_);
|
||||
}
|
||||
|
||||
if (lidar_system_init(lidar_device_callback)) {
|
||||
#ifdef ROS2
|
||||
RCLCPP_ERROR(node->get_logger(), "Lidar system init failed");
|
||||
@@ -1178,6 +1528,12 @@ int main(int argc, char *argv[])
|
||||
if (g_sendcloudrender) {
|
||||
g_ros_object->try_process_pair();
|
||||
}
|
||||
|
||||
// Check for command file
|
||||
if (deviceConnected) {
|
||||
process_command_file();
|
||||
}
|
||||
|
||||
disconnect_msg_printed = false;
|
||||
|
||||
// Wait 0.1 seconds
|
||||
@@ -1206,6 +1562,12 @@ int main(int argc, char *argv[])
|
||||
if (g_sendcloudrender) {
|
||||
g_ros_object->try_process_pair();
|
||||
}
|
||||
|
||||
// Check for command file
|
||||
if (deviceConnected) {
|
||||
process_command_file();
|
||||
}
|
||||
|
||||
disconnect_msg_printed = false;
|
||||
|
||||
// Wait 0.1 seconds
|
||||
@@ -1257,6 +1619,14 @@ int main(int argc, char *argv[])
|
||||
dev_status_csv_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop custom parameter monitoring thread on exit
|
||||
g_param_monitor_running = false;
|
||||
if (g_param_monitor_thread.joinable()) {
|
||||
g_param_monitor_thread.join();
|
||||
}
|
||||
|
||||
|
||||
// lidar_system_deinit();
|
||||
|
||||
|
||||
|
||||
+167
-24
@@ -15,57 +15,133 @@ limitations under the License.
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
#include <cstring>
|
||||
namespace odin_ros_driver {
|
||||
|
||||
YamlParser::YamlParser(const std::string& config_file)
|
||||
: config_file_(config_file) {} // // Using config_file_ member variable
|
||||
YamlParser::YamlParser(const std::string& config_file)
|
||||
: config_file_(config_file) {}
|
||||
|
||||
bool YamlParser::loadConfig() {
|
||||
try {
|
||||
// Using member variable config_file_
|
||||
std::cerr << "Loading config file: " << config_file_ << std::endl;
|
||||
|
||||
|
||||
// Check if file exists
|
||||
if (!std::filesystem::exists(config_file_)) {
|
||||
std::cerr << "Config file not found: " << config_file_ << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Print file contents
|
||||
std::ifstream file(config_file_);
|
||||
std::string content((std::istreambuf_iterator<char>(file)),
|
||||
std::string content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
std::cerr << "Config file content:\n" << content << "\n--- End of file ---" << std::endl;
|
||||
|
||||
|
||||
// Load YAML
|
||||
YAML::Node config = YAML::LoadFile(config_file_);
|
||||
|
||||
|
||||
// Check if 'register_keys' node exists
|
||||
if (!config["register_keys"]) {
|
||||
std::cerr << "Missing 'register_keys' section in config file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
YAML::Node register_keys = config["register_keys"];
|
||||
register_keys_.clear();
|
||||
|
||||
register_keys_str_val_.clear();
|
||||
custom_parameters_.clear();
|
||||
|
||||
// Print number of key-value pairs found
|
||||
std::cerr << "Found " << register_keys.size() << " keys in config" << std::endl;
|
||||
|
||||
|
||||
for (YAML::const_iterator it = register_keys.begin(); it != register_keys.end(); ++it) {
|
||||
std::string key = it->first.as<std::string>();
|
||||
int value = it->second.as<int>();
|
||||
|
||||
const YAML::Node& value_node = it->second;
|
||||
|
||||
// Convert key to lowercase
|
||||
std::transform(key.begin(), key.end(), key.begin(),
|
||||
std::transform(key.begin(), key.end(), key.begin(),
|
||||
[](unsigned char c){ return std::tolower(c); });
|
||||
|
||||
register_keys_[key] = value;
|
||||
std::cerr << "Loaded key: " << key << " = " << value << std::endl;
|
||||
|
||||
// Check if this is a custom parameter
|
||||
if (key.substr(0, 7) == "custom_") {
|
||||
std::string param_name = key.substr(7);
|
||||
|
||||
// Handle different value types
|
||||
if (value_node.IsScalar()) {
|
||||
// Single scalar value (int or float)
|
||||
try {
|
||||
int int_value = value_node.as<int>();
|
||||
ParameterValue param_value;
|
||||
param_value.type = DataType::INT_TYPE;
|
||||
param_value.setData(int_value);
|
||||
custom_parameters_[param_name] = param_value;
|
||||
std::cerr << "Loaded custom parameter (int): " << param_name << " = " << int_value << std::endl;
|
||||
} catch (...) {
|
||||
try {
|
||||
double float_value = value_node.as<double>();
|
||||
ParameterValue param_value;
|
||||
param_value.type = DataType::FLOAT_ARRAY_TYPE;
|
||||
std::vector<float> float_array = {static_cast<float>(float_value)};
|
||||
param_value.setArray(float_array);
|
||||
custom_parameters_[param_name] = param_value;
|
||||
std::cerr << "Loaded custom parameter (float): " << param_name << " = " << float_value << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Failed to parse custom parameter " << param_name << ": " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
} else if (value_node.IsSequence()) {
|
||||
// Array of values
|
||||
size_t array_size = value_node.size();
|
||||
if (array_size == 0) {
|
||||
std::cerr << "Empty array for custom parameter: " << param_name << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to detect if it's a float or int array based on first element
|
||||
try {
|
||||
// Try to parse as float array first
|
||||
std::vector<float> float_array;
|
||||
for (size_t i = 0; i < array_size; ++i) {
|
||||
float_array.push_back(value_node[i].as<float>());
|
||||
}
|
||||
ParameterValue param_value;
|
||||
param_value.type = DataType::FLOAT_ARRAY_TYPE;
|
||||
param_value.setArray(float_array);
|
||||
custom_parameters_[param_name] = param_value;
|
||||
|
||||
std::cerr << "Loaded custom parameter (float array): " << param_name << " = [";
|
||||
for (size_t i = 0; i < float_array.size(); ++i) {
|
||||
if (i > 0) std::cerr << ", ";
|
||||
std::cerr << std::fixed << std::setprecision(4) << float_array[i];
|
||||
}
|
||||
std::cerr << "]" << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Failed to parse custom parameter array " << param_name << ": " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
} else if (allowed_key_w_str_val.find(key) != allowed_key_w_str_val.end()) {
|
||||
try {
|
||||
std::string value = value_node.as<std::string>();
|
||||
register_keys_str_val_[key] = value;
|
||||
std::cerr << "Loaded key: " << key << " = " << value << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Failed to parse key " << key << ": " << e.what() << std::endl;
|
||||
}
|
||||
} else {
|
||||
// Regular (non-custom) integer parameter
|
||||
try {
|
||||
int value = value_node.as<int>();
|
||||
register_keys_[key] = value;
|
||||
std::cerr << "Loaded key: " << key << " = " << value << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Failed to parse key " << key << ": " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
} catch (const YAML::Exception& e) {
|
||||
std::cerr << "YAML exception: " << e.what() << std::endl;
|
||||
@@ -80,16 +156,83 @@ const std::map<std::string, int>& YamlParser::getRegisterKeys() const {
|
||||
return register_keys_;
|
||||
}
|
||||
|
||||
const std::map<std::string, ParameterValue>& YamlParser::getCustomParameters() const {
|
||||
return custom_parameters_;
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string>& YamlParser::getRegisterKeysStrVal() const {
|
||||
return register_keys_str_val_;
|
||||
}
|
||||
|
||||
void YamlParser::printConfig() const {
|
||||
std::cerr << "Configuration Keys:" << std::endl;
|
||||
if (register_keys_.empty()) {
|
||||
std::cerr << " (empty)" << std::endl;
|
||||
return;
|
||||
std::cerr << " (int val empty)" << std::endl;
|
||||
} else {
|
||||
for (const auto& [key, value] : register_keys_) {
|
||||
std::cerr << " " << key << ": " << value << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [key, value] : register_keys_) {
|
||||
std::cerr << " " << key << ": " << value << std::endl;
|
||||
|
||||
if (register_keys_str_val_.empty()) {
|
||||
std::cerr << " (str_val empty)" << std::endl;
|
||||
} else {
|
||||
for (const auto& [key, value] : register_keys_str_val_) {
|
||||
std::cerr << " " << key << ": " << value << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "Custom Parameters:" << std::endl;
|
||||
if (custom_parameters_.empty()) {
|
||||
std::cerr << " (custom param empty)" << std::endl;
|
||||
} else {
|
||||
for (const auto& [key, param_val] : custom_parameters_) {
|
||||
std::cerr << " " << key << ": (size=" << param_val.getSize() << " bytes)";
|
||||
if (param_val.type == DataType::INT_TYPE && param_val.getSize() == sizeof(int)) {
|
||||
int int_val = *reinterpret_cast<const int*>(param_val.getData());
|
||||
std::cerr << " = " << int_val;
|
||||
} else if (param_val.type == DataType::FLOAT_ARRAY_TYPE && param_val.getSize() % sizeof(float) == 0) {
|
||||
size_t count = param_val.getSize() / sizeof(float);
|
||||
const float* float_arr = reinterpret_cast<const float*>(param_val.getData());
|
||||
std::cerr << " = [";
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (i > 0) std::cerr << ", ";
|
||||
std::cerr << std::fixed << std::setprecision(4) << float_arr[i];
|
||||
}
|
||||
std::cerr << "]";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool YamlParser::applyCustomParameters(device_handle device) {
|
||||
bool success = true;
|
||||
|
||||
for (const auto& [param_name, param_value] : custom_parameters_) {
|
||||
std::cerr << "Setting custom parameter: " << param_name << " (size=" << param_value.getSize() << " bytes)" << std::endl;
|
||||
|
||||
int result = lidar_set_custom_parameter(device, param_name.c_str(), param_value.getData(), param_value.getSize());
|
||||
if (result != 0) {
|
||||
std::cerr << "Failed to set custom parameter " << param_name << ": error code " << result << std::endl;
|
||||
success = false;
|
||||
} else {
|
||||
std::cerr << "Successfully set custom parameter " << param_name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int YamlParser::getCustomParameterInt(const std::string& param_name, int default_value) const {
|
||||
auto it = custom_parameters_.find(param_name);
|
||||
if (it != custom_parameters_.end()) {
|
||||
const ParameterValue& param_value = it->second;
|
||||
if (param_value.type == DataType::INT_TYPE && param_value.getSize() == sizeof(int)) {
|
||||
return *reinterpret_cast<const int*>(param_value.getData());
|
||||
}
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user