简介:面向在ROS2环境中使用Intel RealSense D435/D405相机的机器人开发者与视觉工程人员,这份资源梳理了从Windows端安装RealSense Viewer、Linux端配置依赖与编译RealSense-ROS2,再到启动ROS2节点并通过话题读取RGB、深度、点云数据的完整链路,特别适合正在开展移动机器人导航、机械臂抓取或三维重建项目的工程师,也包含了安装前硬件检查、驱动冲突排查等实用经验。文件共3个,以HTML文档承载说明内容,辅以inscode代码配置与gitignore工程文件,zip压缩包仅6KB,十分轻量,可作为独立参考包或随仓库分发。已有151人学习,尤其适合需要同时兼顾Windows与Linux双系统开发、希望理清相机坐标系与深度信息单位换算等关键概念的中高级开发者。资源不仅给出从安装依赖、配置环境变量到启动ROS2节点的完整步骤,还单独说明了如何订阅RGB、深度和点云三类话题,点出深度数据以毫米为单位的处理要点,避免在三维建模与仿真中出现单位错误,并且对相机坐标系中的方向约定做了清晰注释,能有效提升视觉感知项目的落地效率与稳定性,为后续开发节省大量排查时间。 第一次把Intel RealSense插到Linux主机上准备跑ROS2的时候,我原以为这就是“装个驱动、launch一个节点”的事,结果被USB控制器、librealsense版本和realsense-ros分支轮番教育了一周。后来把D455、D435、T265都调顺之后,发现这套流程其实有规律可循,只是网上的资料太散。这篇指南把我自己踩过坑之后的完整路子整理出来,包含从环境准备、驱动编译、launch参数到Python/C++订阅点云与图像的全部代码,适合刚接触RealSense的ROS2新手,也适合从ROS1迁移过来想在Humble或Jazzy上快速起相机的人。
1. 环境准备:先把ROS2、librealsense和realsense-ros的关系理清楚
1.1 版本匹配是最大的坑,没有之一
RealSense在ROS2下的链路是:相机硬件 → librealsense SDK → realsense-ros节点 → ROS2话题。librealsense负责读硬件数据,realsense-ros负责把SDK回调包装成sensor_msgs/Image、PointCloud2、Imu这些标准ROS2消息。很多人编译报错或者启动节点直接崩溃,往往不是操作问题,而是这两个东西的版本没对齐。
realsense-ros的ROS2开发分支叫ros2-development,官方要求librealsense最低版本是2.50.0。但我的实际经验是别卡着下限跑,比如你在Ubuntu 24.04 + ROS2 Jazzy上用了系统源里的旧版librealsense,大概率会遇到rs2::error: null pointer或者failed to set power state这类特别玄学的问题。我整理了一个自己验证过的组合表,直接照抄能省很多时间:
| 操作系统 | ROS2版本 | 我测试过的稳定组合 |
|---|---|---|
| Ubuntu 22.04 | Humble | librealsense 2.55.1 + realsense-ros ros2-development分支 |
| Ubuntu 24.04 | Jazzy | librealsense 2.56.2 + realsense-ros ros2-development分支 |
| Ubuntu 20.04 | Foxy | librealsense 2.54.2 + realsense-ros ros2-development分支 |
提示:不要用apt直接装ROS2软件源里的realsense-ros,版本往往滞后,而且没人保证它在Humble/Jazzy上能把D455的IMU也拉起来。源码编译多花十分钟,后面省心很多。
有一点需要提前说清楚:ROS2本身先装好,推荐用ros2官方源的二进制版,Humble对应Ubuntu 22.04,Jazzy对应Ubuntu 24.04。如果你是新手,装完ROS2以后务必先跑通ros2 run demo_nodes_cpp talker,确认基础环境没问题再碰相机,否则后面排查问题的时候变量太多。
1.2 编译librealsense,建议带上examples
先安装编译依赖:
sudo apt install git cmake build-essential libssl-dev libusb-1.0-0-dev libglfw3-dev libgl1-mesa-dev libglu1-mesa-dev git clone https://github.com/IntelRealSense/librealsense.git cd librealsense git checkout v2.56.2 mkdir build && cd build cmake .. -DBUILD_EXAMPLES=true -DCMAKE_BUILD_TYPE=Release make -j$(nproc) sudo make install sudo ldconfig注意-j$(nproc)会让编译吃满所有核心,如果机器内存小于8GB,建议改成-j4,否则编译到一半可能被OOM杀掉。我第一台测试机器就是8GB内存开满16线程,结果librealsense编译到90%直接被系统kill,重启之后再也不敢这么干。
装完还要处理udev权限规则,这是插上相机后能看到设备的关键一步:
cd librealsense sudo ./scripts/setup_udev_rules.sh这个脚本会把当前用户加入video组、写入RealSense的USB设备规则。执行完拔插一次相机,再运行rs-enumerate-devices,理论上能看到设备型号、序列号、固件版本和相机内参。
顺带提一句,有人问D455在macOS上能做什么应用。如果你用的是macOS,直接用官方RealSense Viewer做深度预览、手势交互、空间测量、配合Unity做AR原型都没问题;但ROS2相关的开发基本还是建议回到Linux环境,macOS上跑Docker里的ROS2访问USB设备的转发链路太折腾,不值得。
1.3 编译realsense-ros,注意rosdep这条命令
驱动包本身不大,编译重点在于依赖识别:
mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src git clone https://github.com/IntelRealSense/realsense-ros.git -b ros2-development cd ~/ros2_ws rosdep install --from-paths src --ignore-src -r -y colcon build --symlink-install source install/setup.bashrosdep install这条命令的作用是扫描src底下的package.xml,把realsense-ros依赖的ROS2功能包(比如diagnostic_updater、launch_ros、cv_bridge)全部装好。如果提示找不到某个依赖,多半是前面ROS2没装完整,用rosdep check排查一下。编译完成后,source install/setup.bash建议写进~/.bashrc,不然每次新开终端都要手动source一遍。
2. 先跑通官方案例:从launch到RViz2可视化
2.1 启动节点,先别急着改参数
环境就绪后,用官方launch文件启动是最快的验证方式:
ros2 launch realsense2_camera rs_launch.py这个launch文件会启动realsense2_camera_node,并默认打开彩色流和深度流。如果你用的是D455,IMU流默认也是开着的。看到类似RealSense camera is up and running的日志,说明节点启动成功。
如果启动时报设备打不开,先别怀疑代码,大概率是两个原因:一是udev规则没生效,重新跑setup_udev_rules.sh;二是USB线插在了USB 2.0口,RealSense对带宽要求很高,必须插USB 3.0以上的蓝色口。我试过把D435插在扩展坞的USB 2.0口上,能枚举设备但启动后不到三秒节点就崩溃,日志里全是Frame didn't arrive within 5000。
2.2 熟悉话题:你最终消费的是这些数据
启动后开另一个终端,看话题列表:
ros2 topic list | grep camera关键话题通常有这些:
/camera/color/camera_info:彩色相机的内参和畸变系数/camera/color/image_raw:原始彩色图像/camera/depth/image_rect_raw:深度图,注意是16位单通道/camera/depth/color/points:RGB-D融合后的彩色点云/camera/imu/data:IMU六轴数据(仅D455、T265等含IMU的型号)
我建议你启动后先看一条camera_info:
ros2 topic echo --once /camera/color/camera_info里面能看到fx、fy、cx、cy、畸变参数,这些数值后面做相机标定、像素转三维坐标、深度图对齐都用得着。另外查一下话题消息类型:
ros2 interface show sensor_msgs/msg/Image ros2 interface show sensor_msgs/msg/PointCloud2确认类型之后,代码订阅的逻辑就清晰了。
2.3 RViz2里看图像和点云
可视化是最直观的验证:
ros2 run rviz2 rviz2打开RViz2之后,两步操作:
- 把左侧
Displays面板里的Fixed Frame设置为camera_link,如果保持默认的map,点云和图像会因为坐标系对不上而显示异常。 - 点击左下角
Add按钮,添加Image视图,通道选择/camera/color/image_raw;再添加PointCloud2视图,选择/camera/depth/color/points。
第一次看到彩色点云在RViz2里转起来的时候,基本说明整个链路已经通了。要注意的是,realsense-ros默认不一定发布点云话题,如果/camera/depth/color/points不存在,需要在启动时显式开启点云:
ros2 launch realsense2_camera rs_launch.py pointcloud.enable:=true3. 代码实战:订阅图像、点云和IMU
3.1 Python订阅彩色图并实时显示
很多新手把重点放在“怎么让相机出图”,其实出图只是起点,真正写业务代码通常是从订阅话题开始的。我用Python写的这套模板,改动最小、最容易跑起来:
import rclpy from rclpy.node import Node from sensor_msgs.msg import Image from cv_bridge import CvBridge import cv2 class RealSenseViewer(Node): def __init__(self): super().__init__('realsense_viewer') self.bridge = CvBridge() self.sub = self.create_subscription( Image, '/camera/color/image_raw', self.image_callback, 10 ) def image_callback(self, msg): cv_image = self.bridge.imgmsg_to_cv2(msg, 'bgr8') cv2.imshow('color', cv_image) cv2.waitKey(1) def main(args=None): rclpy.init(args=args) node = RealSenseViewer() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()这段代码的核心是CvBridge,它是ROS2图像消息和OpenCV Mat之间转换的桥梁。imgmsg_to_cv2(msg, 'bgr8')的第二个参数指定编码格式,彩色图用bgr8,深度图用16UC1。我见过有人不加编码参数,结果深度图显示成一片黑或者一片白,就是因为没有把16位深度数据正确解释。
需要注意一点:cv2.imshow和cv2.waitKey必须在主线程调用,所以不能把它们放进多线程回调里执行。rclpy.spin默认是单线程阻塞调用,回调执行完立即返回,不会卡住消息队列。
3.2 C++订阅点云并做体素滤波
点云数据量大、频率高,Python处理起来如果性能吃紧,建议直接用C++。下面这个节点订阅/camera/depth/color/points,再用PCL的VoxelGrid做降采样,输出到新的点云话题:
#include <rclcpp/rclcpp.hpp> #include <sensor_msgs/msg/point_cloud2.hpp> #include <pcl_conversions/pcl_conversions.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/filters/voxel_grid.h> class PointCloudDownsampler : public rclcpp::Node { public: PointCloudDownsampler() : Node("pointcloud_downsampler") { sub_ = this->create_subscription<sensor_msgs::msg::PointCloud2>( "/camera/depth/color/points", 10, [this](const sensor_msgs::msg::PointCloud2::SharedPtr msg) { pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud( new pcl::PointCloud<pcl::PointXYZRGB>()); pcl::fromROSMsg(*msg, *cloud); pcl::VoxelGrid<pcl::PointXYZRGB> voxel; voxel.setInputCloud(cloud); voxel.setLeafSize(0.01f, 0.01f, 0.01f); pcl::PointCloud<pcl::PointXYZRGB>::Ptr filtered( new pcl::PointCloud<pcl::PointXYZRGB>()); voxel.filter(*filtered); sensor_msgs::msg::PointCloud2 out_msg; pcl::toROSMsg(*filtered, out_msg); out_msg.header = msg->header; pub_->publish(out_msg); }); pub_ = this->create_publisher<sensor_msgs::msg::PointCloud2>( "/camera/depth/color/points_downsampled", 10); } private: rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr sub_; rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pub_; }; int main(int argc, char** argv) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<PointCloudDownsampler>()); rclcpp::shutdown(); return 0; }对应的CMakeLists.txt关键配置:
find_package(rclcpp REQUIRED) find_package(sensor_msgs REQUIRED) find_package(pcl_conversions REQUIRED) find_package(pcl_ros REQUIRED) add_executable(pointcloud_downsampler src/pointcloud_downsampler.cpp) ament_target_dependencies(pointcloud_downsampler rclcpp sensor_msgs pcl_conversions pcl_ros) install(TARGETS pointcloud_downsampler DESTINATION lib/${PROJECT_NAME})体素滤波这里的直觉理解就是“把三维空间切成小方块,每个方块只保留一个点”。RealSense的彩色点云一帧可能几十万个点,直接用octomap_server或者cartographer做建图,CPU扛不住。setLeafSize(0.01f, 0.01f, 0.01f)表示1cm立方体降采样,这个值在室内建图场景下已经够用,你还可以根据实际距离调整。之前我给一个移动底盘做障碍物检测,1cm降采样之后点云量降到原来的1/5,但障碍物的轮廓信息基本没丢。
3.3 launch参数:分辨率、对齐和IMU
realsense-ros支持在launch时直接覆盖参数,这是个非常实用的能力。比如需要1280x720分辨率同时进行深度对齐:
ros2 launch realsense2_camera rs_launch.py \ depth_module.profile:=1280x720x30 \ color_module.profile:=1280x720x30 \ pointcloud.enable:=true \ align_depth.enable:=truealign_depth.enable:=true会把深度图对齐到彩色图坐标系,这样深度图像素和彩色图像素一一对应,做RGB-D语义分割、目标检测时特别方便。对齐之后会多出一个话题/camera/depth/image_rect_raw,它的尺寸和彩色图一致。如果你不追求逐像素对齐,只想拿原始深度,那这个参数可以不开,能省一点CPU。
IMU如果正确启用,话题/camera/imu/data的频率应该是200Hz左右,用下面的命令验证:
ros2 topic hz /camera/imu/dataD455内置的IMU是BMI055,输出三轴加速度和三轴角速度。这个数据直接用在VIO或者自动驾驶估计器里通常还需要配合imu_filter_madgwick做姿态解算,因为原始IMU包含噪声和零偏。我用过的最省事组合是:imu_filter_madgwick+robot_localization,先发布姿态四元数,再把里程计和IMU融合出机器人的位姿。这个链路比较长,但每一步都有现成库,不需要自己造轮子。
4. 常见问题与避坑速查
4.1 相机枚举正常,但ROS2节点起不来
这个问题我遇到不止一次。rs-enumerate-devices能看到相机,说明SDK层面没问题;但ros2 launch一执行就报Device open failed。原因是realsense-ros这个进程在启动时对USB带宽的申请比SDK示例程序更激进,如果USB控制器下还挂了别的设备,带宽不够就会申请失败。
排查方法:
lsusb -t看一下RealSense挂在哪个USB控制器下面、当前链路速度是5000M还是480M。如果是480M,说明走了USB 2.0协议,换接口。另外,台式机建议插后置USB口,前置面板的USB延长线质量参差不齐,很容易导致带宽不足。
4.2 深度图有大量黑洞
深度图上的黑色区域表示“测不到距离”。常见原因有三:一是目标表面太近,低于相机的最小工作距离,比如D435最小距离约0.1米,太近会产生大量无效像素;二是目标表面是镜面或者透明材质,红外结构光反射不回来;三是相机前方有强红外干扰源,比如阳光直射。
代码层面可以用深度后处理缓解,但治标不治本。如果你是做室内机器人导航,把相机装得稍微高一点,避开正前方的地面反光,效果比调参数更明显。
4.3 IMU消息格式和频率不对
/camera/imu/data的消息类型是sensor_msgs/msg/Imu,字段包括:
orientation:四元数(D455默认可能全是0,需要自己融合)angular_velocity:角速度linear_acceleration:线性加速度
如果频率起不来,先确认你用的是D455而不是D435,D435没有IMU硬件。其次检查launch里是否开启了enable_imu,有些版本默认IMU off。最后,IMU数据对USB中断敏感,不要和点云流同时拉满,否则IMU的帧率会掉到100Hz以下。
4.4 点云建图太卡
配合八叉树地图导航时,最怕的就是点云全量灌给octomap_server。我的建议是分成三步处理:先用VoxelGrid降采样到1cm~2cm体素,再通过PassThrough滤波器裁剪掉相机5米以外的点,最后限制话题频率在5Hz左右。这样建图实时性和地图质量都在可接受范围内。
下面是一个常用的裁剪思路:
ros2 run pcl_ros passthrough --ros-args \ -r input:=/camera/depth/color/points_downsampled \ -p filter_field:=z -p filter_limit_min:=0.2 -p filter_limit_max:=5.0这条命令会把z轴方向0.2米到5米范围之外的点全部裁掉。室内场景下这个范围足够用了,还能大幅减小后续建图算法的计算量。
最后再分享一个我自己的习惯:只要插上RealSense,先跑一遍rs-enumerate-devices确认固件版本和序列号,再跑ROS2节点。固件版本过低时realsense-ros会打印警告,但很多新手不会注意那行日志,直接忽略,结果后面出现诡异的重启问题。固件升级直接用RealSense Viewer自带的Updater功能,几分钟就能搞定。真遇到解决不了的问题,先用RealSense Viewer确认相机裸奔时是否正常,这能把“硬件问题”和“ROS2集成问题”快速分开,排查路径会清晰很多。
本文还有配套的精品资源,点击获取