从 ROS 订阅视频话题到本地可视化与 RTMP 推流全流程实战
🧩 一、背景介绍
在机器人系统中,摄像头采集的视频数据通常以 ROS 话题的形式发布,例如:
/camera/image_raw
但是:
- 如果我们希望在浏览器或其他终端设备上实时观看,需要将 ROS 图像转换为网络可访问的视频流;
- 而如果希望在局域网或服务器上做视频转发或云端监控,就需要进一步将视频流推送到 RTMP 服务(如 Nginx)。
本篇将完整讲解以下三步流程👇:
[ROS Topic] → [Flask + OpenCV 本地视频流] → [FFmpeg 推流到 RTMP (Nginx)]
最终效果是:
✅ 你可以在浏览器打开
http://192.168.1.200:5000/stream
看到实时视频流;
✅ 同时,在另一台机器(例如 192.168.1.201
)上通过
rtmp://192.168.1.201/live/stream
也能看到相同的视频。
其实是感知主控没网,下载不了web-server;而运控端有网,但不让下载ros。所以只能通过这种方式来实现视频流的迁移。这是无可奈何的方法
🧠 二、系统架构图
┌──────────────────────────────┐
│ ROS Node │
│ └── 订阅 /camera/image_raw │
└──────────────┬───────────────┘│▼
┌──────────────────────────────┐
│ Flask Web Server │
│ └── 使用 OpenCV 转 MJPEG 流 │
│ └── 提供 http://192.168.1.200:5000/stream │
└──────────────┬───────────────┘│▼
┌──────────────────────────────┐
│ FFmpeg 推流模块 │
│ └── 读取 MJPEG 流 │
│ └── 编码为 H.264 (x264) │
│ └── 推送到 RTMP (Nginx) │
│ rtmp://192.168.1.201/live/stream │
└──────────────────────────────┘
⚙️ 三、从 ROS 话题获取视频并发布本地 HTTP 流
首先,用 Python 写一个 ROS + Flask 节点:
#!/usr/bin/env python3
import rospy
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
from flask import Flask, Responseapp = Flask(__name__)
bridge = CvBridge()
frame = Nonedef image_callback(msg):global frameframe = bridge.imgmsg_to_cv2(msg, "bgr8")@app.route('/stream')
def stream():def generate():global framewhile True:if frame is None:continueret, jpeg = cv2.imencode('.jpg', frame)if not ret:continueyield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n\r\n')return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')if __name__ == '__main__':rospy.init_node('video_stream_node')rospy.Subscriber('/camera/image_raw', Image, image_callback)app.run(host='0.0.0.0', port=5000)
保存为 ros_stream.py
,运行后即可访问:
http://192.168.1.200:5000/stream
在浏览器中会显示实时视频画面 🎥。
🌐 四、部署 Nginx RTMP 服务器
在另一台主机(192.168.1.201
)上安装并配置 RTMP 服务:
1️⃣ 安装 Nginx + RTMP 模块
sudo apt install nginx libnginx-mod-rtmp
2️⃣ 修改配置文件 /etc/nginx/nginx.conf
在 http {}
外层添加:
rtmp {server {listen 1935;chunk_size 4096;application live {live on;record off;}}
}
重启 nginx:
sudo systemctl restart nginx
验证端口是否监听:
sudo netstat -tulnp | grep 1935
若输出:
tcp 0 0 0.0.0.0:1935 0.0.0.0:* LISTEN nginx: master
说明 RTMP 服务正常启动 ✅
📡 五、FFmpeg 推流到 RTMP 服务器
在 ROS 视频流所在的机器 (192.168.1.200
) 上执行:
ffmpeg -fflags nobuffer -flags low_delay -framedrop \-i "http://192.168.1.200:5000/stream" \-c:v libx264 -preset ultrafast -tune zerolatency \-r 25 -g 25 -keyint_min 25 -sc_threshold 0 \-f flv "rtmp://192.168.1.201/live/stream"
解释:
参数 | 作用 |
---|---|
-fflags nobuffer | 禁用输入缓冲,提高实时性 |
-flags low_delay | 启用低延迟模式 |
-tune zerolatency | x264 零延迟调优 |
-preset ultrafast | 最高速编码,适合实时推流 |
-g 25 | 每 25 帧插入关键帧 |
-f flv | 指定 RTMP 推流使用 FLV 容器 |
🎬 六、在客户端查看视频流
现在你已经完成推流,可以通过多种方式观看:
✅ VLC
打开网络串流:
rtmp://192.168.1.201/live/stream
✅ 浏览器 HLS 播放(可扩展)
在 nginx.conf
中开启 HLS 输出:
application live {live on;hls on;hls_path /tmp/hls;hls_fragment 1;
}
然后通过:
http://192.168.1.201/hls/stream.m3u8
即可在网页中播放。
⚡ 七、降低延迟的优化技巧
优化点 | 方法 |
---|---|
FFmpeg 编码缓冲 | 使用 -tune zerolatency + -preset ultrafast |
HTTP 输入缓冲 | 添加 -fflags nobuffer |
关键帧间隔 | 设置 -g 25 、-sc_threshold 0 |
降低分辨率/帧率 | -s 1280x720 -r 20 可减少 CPU 延迟 |
关闭 B 帧 | x264 默认无 B 帧时延更低 |
RTMP 客户端缓冲 | VLC 中设置最小缓冲 100~300ms |
实际测试结果:
实际测试效果其实并不好,大概会延迟8s左右,如果要实现实时性,这篇文档并不能给出好的效果,建议还是用背景中写的一些方法。
🧠 八、完整流程总结
阶段 | 工具 | 协议 | 功能 |
---|---|---|---|
Step 1 | ROS + cv_bridge | ROS Topic | 获取相机视频 |
Step 2 | Flask + OpenCV | HTTP MJPEG | 发布网页流 |
Step 3 | FFmpeg | RTMP (FLV) | 推送至流媒体服务器 |
Step 4 | Nginx RTMP | RTMP/HLS | 提供局域网访问 |
最终形成一个低延迟、可扩展的视频传输链路:
ROS Topic → Flask (MJPEG) → FFmpeg (H.264) → RTMP Server → VLC/网页播放
✨ 九、结语
通过上述方法,我们成功实现了:
- 从 ROS 摄像头话题实时采集视频;
- 在本地搭建 HTTP 视频流可视化;
- 使用 FFmpeg 转码推送至局域网 RTMP 服务器;
- 并最终实现低延迟的多端观看。
这一方案在智能机器人、远程监控、室内导航测试等场景中非常实用。