Update Lidar README

This commit is contained in:
jj
2025-06-11 15:25:01 +08:00
parent 721d07d1fb
commit 80d9ba170c
7 changed files with 206 additions and 59 deletions
+1
View File
@@ -0,0 +1 @@
+142
View File
@@ -0,0 +1,142 @@
# OrbbecSDK ROS2 雷达驱动
此 ROS2 驱动程序支持您使用Orbbec 单线/多线 LiDAR,本文档提供安装说明、使用指南以及其他重要信息,帮助您快速上手使用该驱动程序。
## 1. 安装
### 1.1 先决条件
在使用 OrbbecSDK ROS2 LiDAR驱动程序之前,请确保您的系统上安装了以下依赖项:
- **ROS2**: 有效安装 ROS2(Humble、Foxy 或其他受支持的发行版)。
- 如果您需要帮助,请参阅 [ROS2 安装指南](https://docs.ros.org/en/foxy/Installation.html).
### 1.2 安装 deb 依赖项
```bash
# assume you have sourced ROS environment, same blow
sudo apt install libgflags-dev nlohmann-json3-dev \
ros-$ROS_DISTRO-image-transport ros-${ROS_DISTRO}-image-transport-plugins ros-${ROS_DISTRO}-compressed-image-transport \
ros-$ROS_DISTRO-image-publisher ros-$ROS_DISTRO-camera-info-manager \
ros-$ROS_DISTRO-diagnostic-updater ros-$ROS_DISTRO-diagnostic-msgs ros-$ROS_DISTRO-statistics-msgs \
ros-$ROS_DISTRO-backward-ros libdw-dev
```
### 1.3 安装 udev 规则
```bash
cd ~/ros2_ws/src/OrbbecSDK_ROS2/orbbec_camera/scripts
sudo bash install_udev_rules.sh
sudo udevadm control --reload-rules && sudo udevadm trigger
```
### 2. 入门
```bash
cd ~/ros2_ws/
# build release, Default is Debug
colcon build --event-handlers console_direct+ --cmake-args -DCMAKE_BUILD_TYPE=Release
```
启动雷达节点
* 第一个终端
```bash
. ./install/setup.bash
ros2 launch orbbec_camera lidar.launch.py
```
* 第二个终端
```bash
. ./install/setup.bash
rviz2
```
1.打开Rviz2。
2.添加一个 `PointCloud2`或者 `LaserScan`显示。
3.`PointCloud2`选择 `/lidar/cloud/points`话题,`LaserScan`选择 `/lidar/scan/points`话题。
4.将 `Fixed Frame`设置为 `lidar_lidar_frame`,以正确对齐数据。
* `PointCloud2`示例可视化:
![module in rviz2](./docs/images/lidar0.jpg)
* `LaserScan`示例可视化:
![module in rviz2](./docs/images/lidar1.png)
## 2. 用法
### 2.1 运行驱动程序
要启动驱动程序,请启动提供的 ROS2 启动文件:
```bash
source install/setup.bash
# Launch the driver with point cloud data
ros2 launch orbbec_camera lidar.launch.py lidar_format:=LIDAR_POINT
# Launch the driver with sphere point cloud data
ros2 launch orbbec_camera lidar.launch.py lidar_format:=LIDAR_SPHERE_POINT
# Launch the driver with laser scan data
ros2 launch orbbec_camera lidar.launch.py lidar_format:=LIDAR_SCAN
```
此命令将启动与 Orbbec LiDAR 设备接口的节点。运行此命令前,请确保 LiDAR 硬件已正确连接。
### 2.2 获取已连接雷达的设备信息
```bash
ros2 run orbbec_camera list_devices_node
```
此命令将列出已连接的 LiDAR 设备,并显示它们各自的 IP 地址和端口。您可以使用这些信息来配置驱动程序以连接到特定设备。
### 2.3 检查雷达支持哪些配置
```bash
ros2 run orbbec_camera list_camera_profile_mode_node
```
### 2.4 参数和配置
`lidar.launch.py`文件包含驱动程序的默认参数。您可以通过修改启动文件或创建自定义配置文件来自定义这些设置。关键参数包括:
- **device_type**:启动的设备类型。可选的值:`lidar`、`camera`。该参数设置为 `lidar`则启动雷达设备,设置为 `camera`则启动相机设备。
- **camera_name**:启动节点命名空间。
- **device_num**:设备数量。如果需要启动多个设备,则必须填写此项。
- **upgrade_firmware**:固件升级功能。输入参数是固件路径。
- **connection_delay**:重新打开设备的延迟时间(以毫秒为单位)。并且在热插拔时立即重新打开设备可能会导致固件崩溃。
- **publish_tf**:启用 TF 发布。
- **tf_publish_rate**:TF 发布频率。
- **lidar_format**:雷达的数据格式。可选值:`LIDAR_POINT`、`LIDAR_SPHERE_POINT` 、`LIDAR_SCAN`
- **lidar_rate**:雷达的扫描速率。
- **min_angle**:雷达扫描范围的最小角度,以度为单位(例如 `-135.0`)。默认值:`-135.0`。
- **max_angle**:雷达扫描范围的最大角度,以度为单位(例如 `135.0`)。默认值:`135.0`。
- **min_range**:雷达可测量的最小距离,以米为单位。默认值:`0.05`。
- **max_range**:雷达可测量的最大距离,以米为单位。默认值:`30.0`。
- **echo_mode**:雷达的回波模式。可选值:`Last Echo`,`First Echo`
- **point_cloud_qos**:ROS 2 消息服务质量 (QoS) 设置。可能的值包括 `SYSTEM_DEFAULT`、`DEFAULT`、`PARAMETER_EVENTS`、`SERVICES_DEFAULT`、`PARAMETERS` 和 `SENSOR_DATA`,并且不区分大小写。这些值分别对应于 `rmw_qos_profile_system_default`、`rmw_qos_profile_default`、`rmw_qos_profile_parameter_events`、`rmw_qos_profile_services_default`、`rmw_qos_profile_parameters` 和 `SENSOR_DATA`。
- **enumerate_net_device**:启用自动枚举网络设备。
- **net_device_ip**:网络设备的IP地址。
- **net_device_port**:网络这边的端口号。
- **log_level**:SDK日志级别,默认值为 `none`,可选值为 `debug`、`info`、`warn`、`error`、`fatal`
- **time_domain**:设备的时间戳类型。可选值为 `device`、`global`、`system`
- **config_file_path**:YAML 配置文件的路径。默认值为“”。如果未指定配置文件,则将使用启动文件中的默认参数。
- **enable_heartbeat**:启用心跳功能,默认为 `false`。如果设置为 `true`,摄像头节点将向固件发送心跳信号;如果需要硬件日志记录,也应设置为 `true`。
## pointcloud data 详细说明
Livox pointcloud2 (PointXYZRT) 点云格式,如下:
```
float32 x # X axis, unit:m
float32 y # Y axis, unit:m
float32 z # Z axis, unit:m
uint8 reflectivity # lidar reflectivity
uint8 tag # lidar tag
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

@@ -236,7 +236,7 @@ class OBLidarNode {
// lidar // lidar
std::string lidar_format_ = "ANY"; std::string lidar_format_ = "ANY";
int lidar_rate_ = 0; int lidar_rate_ = 0;
std::string echo_mode_ = "single channel"; std::string echo_mode_ = "";
std::map<stream_index_pair, int> rate_int_; std::map<stream_index_pair, int> rate_int_;
std::map<stream_index_pair, OBLiDARScanRate> rate_; std::map<stream_index_pair, OBLiDARScanRate> rate_;
std::map<stream_index_pair, std::string> frame_id_; std::map<stream_index_pair, std::string> frame_id_;
+57 -54
View File
@@ -8,7 +8,7 @@ from launch_ros.descriptions import ComposableNode
def load_yaml(file_path): def load_yaml(file_path):
with open(file_path, 'r') as f: with open(file_path, "r") as f:
return yaml.safe_load(f) return yaml.safe_load(f)
@@ -29,20 +29,22 @@ def convert_value(value):
return float(value) return float(value)
except ValueError: except ValueError:
pass pass
if value.lower() == 'true': if value.lower() == "true":
return True return True
elif value.lower() == 'false': elif value.lower() == "false":
return False return False
return value return value
def load_parameters(context, args): def load_parameters(context, args):
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args} default_params = {
config_file_path = LaunchConfiguration('config_file_path').perform(context) arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args
}
config_file_path = LaunchConfiguration("config_file_path").perform(context)
if config_file_path: if config_file_path:
yaml_params = load_yaml(config_file_path) yaml_params = load_yaml(config_file_path)
default_params = merge_params(default_params, yaml_params) default_params = merge_params(default_params, yaml_params)
skip_convert = {'config_file_path', 'usb_port', 'serial_number'} skip_convert = {"config_file_path", "usb_port", "serial_number"}
return { return {
key: (value if key in skip_convert else convert_value(value)) key: (value if key in skip_convert else convert_value(value))
for key, value in default_params.items() for key, value in default_params.items()
@@ -51,32 +53,32 @@ def load_parameters(context, args):
def generate_launch_description(): def generate_launch_description():
args = [ args = [
DeclareLaunchArgument('device_type', default_value='lidar'), DeclareLaunchArgument("device_type", default_value="lidar"),
DeclareLaunchArgument('camera_name', default_value='lidar'), DeclareLaunchArgument("camera_name", default_value="lidar"),
DeclareLaunchArgument('device_num', default_value='1'), DeclareLaunchArgument("device_num", default_value="1"),
DeclareLaunchArgument('upgrade_firmware', default_value=''), DeclareLaunchArgument("upgrade_firmware", default_value=""),
DeclareLaunchArgument('connection_delay', default_value='10'), DeclareLaunchArgument("connection_delay", default_value="10"),
DeclareLaunchArgument('publish_tf', default_value='true'), DeclareLaunchArgument("publish_tf", default_value="true"),
DeclareLaunchArgument('frame_id', default_value='scan'), DeclareLaunchArgument("tf_publish_rate", default_value="0.0"),
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'), DeclareLaunchArgument(
DeclareLaunchArgument('lidar_format', default_value='ANY'),#LIDAR_POINT, LIDAR_SPHERE_POINT, LIDAR_SCAN "lidar_format", default_value="ANY"
DeclareLaunchArgument('lidar_rate', default_value='20'), ), # LIDAR_POINT, LIDAR_SPHERE_POINT, LIDAR_SCAN
DeclareLaunchArgument('min_angle', default_value='-135.0'), DeclareLaunchArgument("lidar_rate", default_value="20"),
DeclareLaunchArgument('max_angle', default_value='135.0'), DeclareLaunchArgument("min_angle", default_value="-135.0"),
DeclareLaunchArgument('min_range', default_value='0.05'), DeclareLaunchArgument("max_angle", default_value="135.0"),
DeclareLaunchArgument('max_range', default_value='30.0'), DeclareLaunchArgument("min_range", default_value="0.05"),
DeclareLaunchArgument('echo_mode', default_value='single channel'), DeclareLaunchArgument("max_range", default_value="30.0"),
DeclareLaunchArgument('point_cloud_qos', default_value='default'), DeclareLaunchArgument("echo_mode", default_value="single channel"),
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices DeclareLaunchArgument("point_cloud_qos", default_value="default"),
# If you do not want to automatically enumerate network devices, DeclareLaunchArgument("enumerate_net_device", default_value="true"),
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090 DeclareLaunchArgument("net_device_ip", default_value=""),
DeclareLaunchArgument('enumerate_net_device', default_value='true'), DeclareLaunchArgument("net_device_port", default_value="0"),
DeclareLaunchArgument('net_device_ip', default_value=''), DeclareLaunchArgument("log_level", default_value="none"),
DeclareLaunchArgument('net_device_port', default_value='0'), DeclareLaunchArgument(
DeclareLaunchArgument('log_level', default_value='none'), "time_domain", default_value="device"
DeclareLaunchArgument('time_domain', default_value='device'),# global, device, system ), # global, device, system
DeclareLaunchArgument('config_file_path', default_value=''), DeclareLaunchArgument("config_file_path", default_value=""),
DeclareLaunchArgument('enable_heartbeat', default_value='false'), DeclareLaunchArgument("enable_heartbeat", default_value="false"),
] ]
def get_params(context, args): def get_params(context, args):
@@ -98,29 +100,30 @@ def generate_launch_description():
] ]
else: else:
return [ return [
GroupAction([ GroupAction(
PushRosNamespace(LaunchConfiguration("camera_name")), [
ComposableNodeContainer( PushRosNamespace(LaunchConfiguration("camera_name")),
name="camera_container", ComposableNodeContainer(
namespace="", name="camera_container",
package="rclcpp_components", namespace="",
executable="component_container", package="rclcpp_components",
composable_node_descriptions=[ executable="component_container",
ComposableNode( composable_node_descriptions=[
package="orbbec_camera", ComposableNode(
plugin="orbbec_camera::OBCameraNodeDriver", package="orbbec_camera",
name=LaunchConfiguration("camera_name"), plugin="orbbec_camera::OBCameraNodeDriver",
parameters=params, name=LaunchConfiguration("camera_name"),
), parameters=params,
], ),
# prefix=["xterm -e gdb -ex run --args"], ],
output="screen", # prefix=["xterm -e gdb -ex run --args"],
) output="screen",
]) ),
]
)
] ]
return LaunchDescription( return LaunchDescription(
args + [ args
OpaqueFunction(function=lambda context: create_node_action(context, args)) + [OpaqueFunction(function=lambda context: create_node_action(context, args))]
]
) )
+5 -4
View File
@@ -138,7 +138,7 @@ void OBLidarNode::getParameters() {
setAndGetNodeParameter<double>(tf_publish_rate_, "tf_publish_rate", 0.0); setAndGetNodeParameter<double>(tf_publish_rate_, "tf_publish_rate", 0.0);
setAndGetNodeParameter<std::string>(time_domain_, "time_domain", "global"); setAndGetNodeParameter<std::string>(time_domain_, "time_domain", "global");
setAndGetNodeParameter<bool>(enable_heartbeat_, "enable_heartbeat", false); setAndGetNodeParameter<bool>(enable_heartbeat_, "enable_heartbeat", false);
setAndGetNodeParameter<std::string>(echo_mode_, "echo_mode", "single channel"); setAndGetNodeParameter<std::string>(echo_mode_, "echo_mode", "");
setAndGetNodeParameter<std::string>(point_cloud_qos_, "point_cloud_qos", "default"); setAndGetNodeParameter<std::string>(point_cloud_qos_, "point_cloud_qos", "default");
setAndGetNodeParameter<float>(min_angle_, "min_angle", -135.0); setAndGetNodeParameter<float>(min_angle_, "min_angle", -135.0);
setAndGetNodeParameter<float>(max_angle_, "max_angle", 135.0); setAndGetNodeParameter<float>(max_angle_, "max_angle", 135.0);
@@ -164,10 +164,11 @@ void OBLidarNode::setupDevices() {
RCLCPP_INFO_STREAM(logger_, "Setting heartbeat to " << (enable_heartbeat_ ? "ON" : "OFF")); RCLCPP_INFO_STREAM(logger_, "Setting heartbeat to " << (enable_heartbeat_ ? "ON" : "OFF"));
TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_HEARTBEAT_BOOL, enable_heartbeat_); TRY_TO_SET_PROPERTY(setBoolProperty, OB_PROP_HEARTBEAT_BOOL, enable_heartbeat_);
} }
if (device_->isPropertySupported(OB_PROP_LIDAR_ECHO_MODE_INT, OB_PERMISSION_READ_WRITE)) { if (!echo_mode_.empty() &&
if (echo_mode_ == "single channel") { device_->isPropertySupported(OB_PROP_LIDAR_ECHO_MODE_INT, OB_PERMISSION_READ_WRITE)) {
if (echo_mode_ == "Last Echo") {
TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_ECHO_MODE_INT, 0); TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_ECHO_MODE_INT, 0);
} else if (echo_mode_ == "dual channel") { } else if (echo_mode_ == "First Echo") {
TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_ECHO_MODE_INT, 1); TRY_TO_SET_PROPERTY(setIntProperty, OB_PROP_LIDAR_ECHO_MODE_INT, 1);
} }
RCLCPP_INFO_STREAM( RCLCPP_INFO_STREAM(