官方文档 : https://www.orbbec.com.cn/index/Gemini330/info.html?cate=119&id=80
B站官方教学: https://www.bilibili.com/video/BV16p421S7p9
ROS2相关源码 : https://gitee.com/orbbecdeveloper/OrbbecSDK_ROS2

一、安装Orbbec SDK
SDK可视化界面下载娱安装:https://github.com/orbbec/OrbbecSDK_v2/releases

下载对应平台的sdk(windows、linux 、macos)
手动下载解压后运行
bash
cd OrbbecViewer_v2.6.3_202512231449_f605fe9_linux_x86_64/
sudo ./OrbbecViewer
可视化结果

二、安装ROS相关环境和包
ROS2相关源码 : https://gitee.com/orbbecdeveloper/OrbbecSDK_ROS2
2.1 安装ros2和依赖 (已安装可跳过)
安装ros
bash
wget http://fishros.com/install -O fishros && bash fishros
source ROS2
bash
source /opt/ros/$ROS_DISTRO/setup.bash
2.2 建ROS工作空间 + 拉源码
bash
sudo apt install ros-humble-orbbec-camera ros-humble-orbbec-description
bash
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
git clone https://gitee.com/orbbecdeveloper/OrbbecSDK_ROS2.git
cd OrbbecSDK_ROS2
git checkout v2-main
等待下,月500M, log
Cloning into 'OrbbecSDK_ROS2'...
remote: Enumerating objects: 15383, done.
remote: Counting objects: 100% (1341/1341), done.
remote: Compressing objects: 100% (499/499), done.
remote: Total 15383 (delta 948), reused 1170 (delta 822), pack-reused 14042 (from 1)
Receiving objects: 100% (15383/15383), 508.10 MiB | 4.84 MiB/s, done.
Resolving deltas: 100% (10801/10801), done.
2.3 装依赖(官方给的依赖清单):
bash
sudo apt update
sudo apt install -y \
libgflags-dev nlohmann-json3-dev libdw-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
sudo apt install -y libssl-dev
2.4 编译
bash
cd ~/ros2_ws
source /opt/ros/$ROS_DISTRO/setup.bash
cd ~/ros2_ws
colcon build --event-handlers console_direct+ --cmake-args -DCMAKE_BUILD_TYPE=Release
2.5 装 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.6 读取设备信息
OrbbecSDK_ROS2/orbbec_camera/scripts
bash
source ~/ros2_ws/install/setup.bash
ros2 run orbbec_camera list_devices_node
查看信息
USB port_id: 2-6-12
Modified USB port_id: 2-6
INFO\] \[1767083724.975152353\] \[list_device_node\]: - Name: Orbbec Gemini 335L, PID: 0x0804, SN/ID: CP2AB53000xx, Connection: USB3.2 \[INFO\] \[1767083724.975169428\] \[list_device_node\]: serial: CP2AB5300014 \[INFO\] \[1767083724.975172003\] \[list_device_node\]: usb port: 2-6 \[INFO\] \[1767083724.975174137\] \[list_device_node\]: usb connect type: USB3.2
三、运行相机
3.1 启动相机节点
bash
ros2 launch orbbec_camera gemini_330_series.launch.py \
enable_accel:=true \
enable_gyro:=true \
enable_left_ir:=true \
enable_right_ir:=true \
enable_sync_output_accel_gyro:=true
其中的参数说明:
enable_accel:是否启用加速度传感器
enable_gyro:是否启用陀螺仪传感器
enable_left_ir:是否启用左侧红外传感器
enable_right_ir:是否启用右侧红外传感器
enable_sync_output_accel_gyro:是否同步输出加速度和陀螺仪数据
发布的相机节点
bash
### Orbbec ros2 相机节点
```bash
/camera/accel/imu_info
/camera/color/camera_info
/camera/color/image_raw
/camera/color/image_raw/compressed
/camera/color/image_raw/theora
/camera/color/metadata
/camera/depth/camera_info
/camera/depth/image_raw
/camera/depth/image_raw/compressedDepth
/camera/depth/metadata
/camera/depth/points
/camera/depth_filter_status
/camera/device_status
/camera/gyro/imu_info
/camera/gyro_accel/sample
/camera/left_ir/camera_info
/camera/left_ir/image_raw
/camera/left_ir/image_raw/compressed
/camera/left_ir/image_raw/theora
/camera/left_ir/metadata
/camera/right_ir/camera_info
/camera/right_ir/image_raw
/camera/right_ir/image_raw/compressed
/camera/right_ir/image_raw/theora
/camera/right_ir/metadata
3.2 启动订阅代码
订阅并可视化
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import time
from collections import deque
import cv2
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
class RgbViewer(Node):
def __init__(self, image_topic: str, window_name: str = "Orbbec RGB"):
super().__init__("orbbec_rgb_viewer_min")
self.bridge = CvBridge()
self.image_topic = image_topic
self.window_name = window_name
# 用最近一段时间的到帧间隔计算 FPS(更稳定)
self.ts = deque(maxlen=60)
self.sub = self.create_subscription(
Image,
self.image_topic,
self.cb,
10,
)
self.get_logger().info(f"Subscribing: {self.image_topic}")
cv2.namedWindow(self.window_name, cv2.WINDOW_NORMAL)
def cb(self, msg: Image):
# 记录时间戳(ROS stamp 优先;缺失就用 wall time)
if msg.header.stamp.sec == 0 and msg.header.stamp.nanosec == 0:
t = time.time()
else:
t = msg.header.stamp.sec + msg.header.stamp.nanosec * 1e-9
self.ts.append(t)
# FPS
fps = 0.0
if len(self.ts) >= 2:
dt = (self.ts[-1] - self.ts[0])
if dt > 1e-6:
fps = (len(self.ts) - 1) / dt
# 转 OpenCV
try:
img = self.bridge.imgmsg_to_cv2(msg, desired_encoding="bgr8")
except Exception as e:
self.get_logger().error(f"cv_bridge convert failed: {e}")
return
h, w = img.shape[:2]
enc = msg.encoding if msg.encoding else "unknown"
overlay = f"{w}x{h} enc:{enc} fps:{fps:.1f}"
cv2.putText(
img, overlay, (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2, cv2.LINE_AA
)
cv2.imshow(self.window_name, img)
# 让窗口可刷新/可关闭
key = cv2.waitKey(1) & 0xFF
if key == 27: # ESC 退出
rclpy.shutdown()
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--topic", default="/camera/color/image_raw", help="RGB Image topic")
args = parser.parse_args()
rclpy.init()
node = RgbViewer(args.topic)
try:
rclpy.spin(node)
finally:
node.destroy_node()
cv2.destroyAllWindows()
if rclpy.ok():
rclpy.shutdown()
if __name__ == "__main__":
main()