# DepthAI ROS 驱动

> **版本说明**
> 本页面涵盖
> **ROS v3 驱动**
> 。
> - **Humble / Jazzy：**
> ROS 包名使用
> `_v3`
> 后缀，例如
> `depthai_ros_driver_v3`
> 和 Debian 包
> `ros-$ROS_DISTRO-depthai-ros-v3`
> 。
> - **Kilted 及更新版本：**
> v3 是默认驱动，因此包名不带后缀，例如
> `depthai_ros_driver`
> 和 Debian 包
> `ros-$ROS_DISTRO-depthai-ros`
> 。
> 如果你希望在最新的 Humble/Jazzy ROS 二进制文件同步至稳定的 ROS apt 仓库之前获取它们，请启用
> **ROS 测试仓库**
> 。

## 快速入门

### 经典安装

安装 ROS 后，安装与你发行版匹配的 DepthAI ROS 包。

#### Humble/Jazzy

```bash
sudo apt install ros2-testing-apt-source
sudo apt update
sudo apt install ros-$ROS_DISTRO-depthai-ros-v3
```

运行驱动：

```bash
ros2 launch depthai_ros_driver_v3 driver.launch.py
```

如需在 RViz 中可视化数据：

```bash
ros2 launch depthai_ros_driver_v3 driver.launch.py use_rviz:=true
```

如需将可组合驱动包作为独立节点运行：

```bash
ros2 run depthai_ros_driver_v3 driver_node
```

#### Kilted 及更新版本

```bash
sudo apt install ros-$ROS_DISTRO-depthai-ros
```

运行驱动：

```bash
ros2 launch depthai_ros_driver driver.launch.py
```

如需在 RViz 中可视化数据：

```bash
ros2 launch depthai_ros_driver driver.launch.py use_rviz:=true
```

如需将可组合驱动包作为独立节点运行：

```bash
ros2 run depthai_ros_driver driver_node
```

use_rviz:=true 对所有使用 driver.launch.py 的启动文件均有效。

注意，此启动文件针对 OAK-D 系列设备，对于不同配置的设备（如 OAK-D-PoE-SR 或 OAK-FFC），你可能需要使用不同的启动文件或不同的参数组合。

每个启动文件都会将 ROS 驱动作为可组合节点启动在其自身的可组合节点容器中。

### 预构建 Docker 镜像

你也可以使用我们预构建的 Docker 镜像。虽然这些镜像主要供测试和调试使用，但它们可以作为不同项目版本之间的参考点。

它们还可以用于在不同 ROS 版本之间发布数据。例如，在 Kilted 容器中运行的驱动发布的数据应该能够被使用 Jazzy 发行版的主机访问（尽管此功能取决于发行版之间的差异，可能并非在所有情况下都有效）。

如需运行带有 Rviz 支持的驱动，你首先需要执行以下命令，以便弹出 GUI 窗口：

```bash
xhost +local:docker
```

然后，按照 [构建文章](https://docs.luxonis.com/software-v3/depthai/ros/build-from-source.md) 中描述的方式运行容器：

```bash
docker run -it -v /dev/:/dev/ --privileged -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix luxonis/depthai-ros:kilted-latest
```

将运行一个交互式 Docker 会话。你也可以尝试：

```bash
docker run -it -v /dev/:/dev/  --privileged -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix luxonis/depthai-ros:kilted-latest [CMD]
```

要为特定版本运行容器，可以使用 v{版本号}-kilted，其中版本号为 x.x.x 格式，例如：

```bash
docker run -it -v /dev/:/dev/ --privileged -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix luxonis/depthai-ros:v3.0.9-kilted
```

我们还提供适用于 ARM64 的容器，它们的名称略有不同，但用法相同：

```bash
docker run -it -v /dev/:/dev/ --privileged -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix luxonis/depthai-ros:kilted-arm64-latest
```

```bash
docker run -it -v /dev/:/dev/ --privileged -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix luxonis/depthai-ros:v3.0.9-kilted-arm64
```

你也可以将外部 YAML 文件作为配置传递给驱动。你既可以在容器内部编辑这些文件，也可以在你的主机上创建一个 .yaml 文件（例如 /home/你的用户名/params/example_config.yaml）并将其作为参数传递给可执行文件，如下所示：

```bash
docker run -it -v /dev/:/dev/ -v /home/你的用户名/params:/params --network host --privileged -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix depthai-ros ros2 launch depthai_ros_driver driver.launch.py params_file:=/params/example_config.yaml
```

## 参数

完整参数列表请见 [此处。](https://docs.luxonis.com/software-v3/depthai/ros/parameters.md)

## 启动文件

 * driver.launch.py 以 RGBD 模式启动相机，并在空间模式下运行神经网络（使用 Yolo V6）。
 * rgbd_pcl.launch.py 以基本 RGBD 配置启动相机，不加载任何神经网络。同时加载 ROS 深度处理节点用于 RGBD 点云。
 * example_multicam.launch.py 同时启动多台相机，每台相机位于不同的容器中。编辑 config 目录下的 multicam_example.yaml 配置文件以更改参数（目前仅适用于 RVC2）。
 * example_segmentation.launch.py 以 RGBD + 语义分割模式启动相机（管道类型=RGBD，nn_type=rgb）。
 * pointcloud.launch.py - 类似于 rgbd_pcl.launch.py，但不使用 RGB 组件生成点云。注意，这里的点云发布在不同的主题上。
 * rtabmap.launch.py 启动相机和 RTAB-MAP RGBD SLAM（你需要先安装它 - sudo apt install ros-$ROS_DISTRO-rtabmap-ros）。你可能需要通过参数设置手动对焦。
 * sr_rgbd_pcl.launch.py - 应作为 SR 相机的基线，因其仅有两个传感器。
 * sr_poe_rgbd_pcl.launch.py - 应作为 SR PoE 相机的基线。
 * oak_t.launch.py - 专用于 OAK-T 系列的启动文件。
 * calibration.launch.py - 用于启动 ROS 校准工具，主要用于测试。如果你发现相机校准无效，请使用我们的内部校准工具或直接联系我们。
 * vio.launch.py - 启动相机并默认启用 VIO 管道。

## 从外参发布 TF

帧默认名称：

 * parent_frame: "oak_parent_frame"
 * base_frame: "oak" (或节点名称)

在驱动中，所有帧都添加了节点名称前缀，以便在使用多台相机时更容易区分，例如 oak_imu_frame

默认情况下，相机变换根据设备的相机校准数据发布。

使用 driver.launch.py 时，你可以设置 pass_tf_args_as_params:=true，以便用 TF 参数填充这些参数。例如： ros2 launch depthai_ros_driver driver.launch.py
pass_tf_args_as_params:=true parent_frame:=map cam_pos_x:=1.0 imu_from_descr:=true

也可以使用 driver.i_tf_custom_urdf_path 和 driver.i_tf_custom_xacro_args 设置自定义 URDF 路径（目前仅支持绝对路径）和自定义 xacro 参数。请注意，robot_state_publisher
必须运行。

> **关于外参的说明**
> 如果你的相机 IMU 未校准，将会显示警告，并且 IMU 将以零旋转和平移发布。 你可以通过设置
> `driver.i_tf_imu_from_descr: true`
> 来覆盖此行为。这将根据相机型号从 URDF 发布默认的 IMU 外参。

## 自定义管道

由于所有类型都定义为插件，因此也可以创建自定义管道。

为此，你可以创建一个自定义包（例如 dai_ros_plugins），并在该包中创建一个可执行文件（dai_ros_plugins.cpp）。在该文件中，定义一个继承自
depthai_ros_driver::pipeline_gen::BasePipeline 的自定义插件，并重写 createPipeline 方法。

例如：

```cpp
std::vector<std::unique_ptr<depthai_ros_driver::dai_nodes::BaseNode>> DaiRosPlugins::createPipeline(
    std::shared_ptr<rclcpp::Node> node,
    std::shared_ptr<dai::Device> device,
    std::shared_ptr<dai::Pipeline> pipeline,
    std::shared_ptr<depthai_ros_driver::param_handlers::PipelineGenParamHandler> ph,
    const std::string& deviceName,
    bool rsCompat,
    const std::string& /*nnType*/) {
    namespace dai_nodes = depthai_ros_driver::dai_nodes;
    std::vector<std::unique_ptr<dai_nodes::BaseNode>> daiNodes;
    auto left = std::make_unique<dai_nodes::SensorWrapper>("left", node, pipeline, deviceName, rsCompat, dai::CameraBoardSocket::CAM_B);
    auto right = std::make_unique<dai_nodes::SensorWrapper>("right", node, pipeline, deviceName, rsCompat, dai::CameraBoardSocket::CAM_C);
    daiNodes.push_back(std::move(left));
    daiNodes.push_back(std::move(right));
    return daiNodes;
}
```

之后导出插件，例如：

```cpp
#include <pluginlib/class_list_macros.hpp>

PLUGINLIB_EXPORT_CLASS(dai_ros_plugins::DaiRosPlugins, depthai_ros_driver::pipeline_gen::BasePipeline)
```

在你的包内的 plugins.xml 文件中添加插件定义：

```xml
<library path="dai_ros_plugins">
  <class type="dai_ros_plugins::DaiRosPlugins" base_class_type="depthai_ros_driver::pipeline_gen::BasePipeline">
    <description>这是一个方形插件。</description>
  </class>
</library>
```

现在你可以将创建的插件用作管道，只需将 pipeline_gen.i_pipeline_type 设置为 test_plugins::Test。

你可以在 [我们的应用示例](https://github.com/luxonis/oak-examples/tree/main/apps/ros/ros-driver-custom-workspace/src/dai_ros_plugins)
中查看包含插件的最终包的样子。

## 特定相机配置

### PoE 相机

由于 PoE 相机使用的协议吞吐量低于 USB，运行默认驱动启动可能会导致延迟，具体取决于所选分辨率/帧率。为了解决这个问题，你可以使用编码帧，这允许你保持所需的分辨率/帧率，但会因压缩而降低图像质量。另一个区别是，在此模式下禁用了 subpixel
深度过滤。例如，对 RGB 相机启用低带宽，更改参数：

 * rgb.i_low_bandwidth - 设置为 true 以启用
 * rgb.i_low_bandwidth_quality - 所需的质量百分比（默认-50） 有关所有流的示例参数，请参见 low_bandwidth.yaml 文件。

### ToF 相机

OAK-D-SR-PoE 由于其特定配置需要单独的启动文件 - sr_poe_rgbd_pcl.launch.py。 此文件也使用了针对 OAK-D-SR 的流水线配置，您可以在上方找到关于流水线类型的更多信息。 请注意，由于传感器插槽命名约定，ToF
相机当前在 rgb 帧下发布图像。

## 停止/启动相机以节省电源/重新配置

停止相机也可用于省电，因为流水线会从设备中移除。当相机停止时，话题也会被移除。

此外，还可以选择在相机遇到错误时启用自动重启。为此，请设置： pipeline_gen.i_enable_diagnostics: true driver.i_restart_on_diagnostics_error: true

## RealSense 兼容性

为了快速集成测试，您可以使用 driver.launch.py 并添加 rs_compat:=true 参数来模拟 Realsense 相机的行为。 这将设置相机使用 RGBD 流水线，并发布与 Realsense 相机兼容的话题名称。 TF 坐标系也将以
Realsense 格式提供。与 RS 节点的工作方式类似，您可以使用 pointcloud.enable:=true 参数启用点云发布。 请注意，驱动程序将以不同的名称和命名空间启动，并且使用不同的 DAI 节点名称，因此您可能需要相应地调整配置文件。
命名约定如下：

 * 命名空间：'' -> camera
 * 节点名称：oak -> camera
 * rgb -> color
 * stereo -> depth
 * 'left' -> infra_2
 * 'right' -> infra_1
 * 坐标系 oak -> camera_link

### 示例

让我们看看启动文件需要做哪些更改：

```python
# 示例：包含 RealSense 驱动程序
rs_camera_launch_include = IncludeLaunchDescription(
  PythonLaunchDescriptionSource([
      PathJoinSubstitution([
          FindPackageShare('realsense2_camera'),
          'launch',
          'rs_launch.py',
      ])
  ]),
  launch_arguments={
      'pointcloud.enable': true,
  }.items()
)
```

以下启动描述将在 RGBD 模式下运行相机驱动程序，并启用点云。 现在要调整为使用 OAK 相机，您需要更改以下内容：

```python
# 示例：包含 OAK 驱动程序
oak_driver_launch_include = IncludeLaunchDescription(
  PythonLaunchDescriptionSource([
      PathJoinSubstitution([
          FindPackageShare('depthai_ros_driver'),
          'launch',
          'driver.launch.py',
      ])
  ]),
  launch_arguments={
      'rs_compat': true,
      'pointcloud.enable': true,
  }.items()
)
```

这将启动相机并发布相同的话题，唯一的区别是深度图的默认配置文件是 1280x720x30，而不是 848x480x30。

### 使用 RealSense 兼容性的 RTABMap 示例

让我们看看 rtabmap_ros 包中的一个示例：realsense_d435i_color.launch.py。

注意 截至本示例编写时（2024-08-27），需要按如下方式调整 RS 话题名称：

#### 修改前

```python
remappings=[
('imu', '/imu/data'),
('rgb/image', '/camera/color/image_raw'),
('rgb/camera_info', '/camera/color/camera_info'),
('depth/image', '/camera/realigned_depth_to_color/image_raw')]
```

#### 修改后

```python
remappings=[
('imu', '/imu/data'),
('rgb/image', '/camera/camera/color/image_raw'),
('rgb/camera_info', '/camera/camera/color/camera_info'),
('depth/image', '/camera/camera/depth/image_rect_raw')]
```

现在要使用 RS 相机启动该示例，根据说明需要运行以下命令：

```bash
ros2 launch realsense2_camera rs_launch.py enable_gyro:=true enable_accel:=true unite_imu_method:=1 enable_sync:=true
```

要使用 OAK 相机启动相同的示例，需要运行以下命令：

```bash
ros2 launch depthai_ros_driver driver.launch.py rs_compat:=true
```

请注意，对于 ROS2 节点，推荐使用组件（components）以提高性能并减少延迟。 RS 相机驱动程序未提供在组件容器中运行相机节点的专用启动文件（除 intra_process_demo 外），因此您需要创建自定义容器。 DepthAI ROS
驱动程序的默认启动文件默认以组合模式运行，因此您可以在运行时向同一容器添加更多节点。 您也可以参考 depthai_ros_driver 包中的 rtabmap.launch.py 示例，了解如何使用 OAK 相机运行
RTABMap。您还可以对此示例稍作修改，以启用 RS 模式。

## 重新标定

如果您想使用设备提供的标定值以外的其他标定值，可以通过以下方式实现：

 * 使用每个图像流可用的 set_camera_info 服务。
 * 使用 i_calibration_file 参数指向标定文件。注意 相机名称必须以 / 开头，例如 /rgb。有关示例标定文件，请参见 depthai_ros_driver/config/calibration。 提供了
   calibration.launch 文件，用于在单目和立体配置下启动 ROS 相机标定节点。 标定文件语法（来自 camera_info_manager）：

```yaml
- file:///full/path/to/local/file.yaml
- file:///full/path/to/videre/file.ini
- package://camera_info_manager/tests/test_calibration.yaml
- package://ros_package_name/calibrations/camera3.yaml
```

## 开发者指南

为了在隔离工作区内更方便地进行开发，可以使用 Visual Studio Code 配合 DevContainers 插件，具体步骤如下：

 * 创建独立的工作区
 * 将仓库克隆到 src 目录中
 * 将 .devcontainer 目录复制到主工作区目录中
 * 在 VSCode 中打开工作区目录
