@TOC

🌌你好!这里是 晓雨的笔记本 在所有感兴趣的领域扩展知识,感谢你的陪伴与支持~ 👋 欢迎添加好友 is_yu_ei,不定期掉落福利资讯
写在最前面
版权声明:本文为原创技术实践稿,遵循 CC 4.0 BY-SA 协议。转载请注明出处。涉及官方 SDK 的接口描述及开源项目代码,请以所链接的官方资料和仓库许可证为准。
准备接入视觉模型时,可以先做一个十秒钟的小检查:保存一帧画面,看人是不是正着、颜色是否正常、取到的是不是新图。模型分数再高,也需要可靠的输入。把这些问题提前放到取图工具里处理,后面调分类、检测或图片问答时,就少了一层猜测。
T1 的公开视觉接口提供 head_stereo_left 和 head_stereo_right 两路相机标识,以及单帧 JPEG 服务和 RTSP 视频入口。本文先让 Python 完成一次"取图---解码---方向处理---保存",再扩展到连续取帧。普通 RGB 分类任务可以先使用其中一路图像。12

图 1|从两个入口得到可用图像:JPEG 服务对应单次抓拍,RTSP 对应连续取帧,后续再交给显示或视觉模型。
这一篇先做两个顺手的小工具
camera_capture.py 负责保存一张图;camera_stream.py 负责读取有限数量的视频帧,并周期性保存采样帧到 latest_frame.jpg。先用合成图片和自己的视频测试处理流程,接入 T1 时再替换服务与视频源。两个入口共享方向配置,便于定位问题。
1 先确认"向谁取图、会收到什么"
设备接口沿用 Ubuntu 22.04 / ROS 2 Humble,消息包按 SDK 入门篇编译。图像脚本需要 requirements-core.txt 中的 Pillow;视频脚本额外安装 requirements-video.txt 中的 OpenCV。下面命令在已经克隆的 t1-pro-sdk-demos 根目录执行。15
bash
git clone https://atomgit.com/WTYuong/t1-pro-sdk-demos.git
cd t1-pro-sdk-demos
git checkout 3a62e33
python3 -m pip install -r requirements-core.txt
## 在 Ubuntu 22.04 的 ROS 终端中加载已编译的 SDK:
source /opt/ros/humble/setup.bash
source /path/to/primebot-t1-sdk/install/setup.bash
ros2 interface show aimdk_msgs/srv/CaptureJpegImage
ros2 service list -t
先把 SDK 路径替换为实际构建目录。CaptureJpegImage 的服务名用于找到机器人上的服务器,aimdk_msgs/srv/CaptureJpegImage 则定义 Python 请求和响应。查看一次 interface show,再查看 service list -t,就能把本地类型与远端入口对齐。23
| 请求字段 | 本例怎么使用 |
|---|---|
| request.header.stamp | 填写当前 ROS 时钟时间戳 |
| camera_id | head_stereo_left 或 head_stereo_right |
| timeout_ms | 等待新帧的服务端超时;本例设 1000 ms |
| 客户端等待期限 | 在服务端超时之外再留通信处理时间 |
选择 head_stereo_left 或 head_stereo_right 后,把相机 ID 和方向一起作为配置保存。首版沿用机器人当前形态,围绕同一路相机收集样本;这样复查一张图片时,能够知道它来自哪只相机、是否经过旋转。
| 先把单帧抓拍做成一个可复查的样本,再扩展到连续取帧,问题更容易分层定位。 |
|---|
2 JPEG 字节藏在响应的哪一层?
抓拍返回值可以顺着名字往下读:reply 的 jpeg 保存相机元信息,jpeg.image 是标准 CompressedImage,image.data 才是压缩字节。因此实际取图路径是 reply.jpeg.image.data。先取对这一层,再交给图像解码器。34
python
req = CaptureJpegImage.Request()
req.request.header.stamp = node.get_clock().now().to_msg()
req.camera_id = 'head_stereo_left'
req.timeout_ms = 1000
future = client.call_async(req)
## 在 main 中等待,回调内不阻塞等待:
rclpy.spin_until_future_complete(node, future, timeout_sec=4.0)
if not future.done():
raise TimeoutError('抓拍响应超时')
reply = future.result()
common = reply.response
if common.header.code != 0 or common.status.value != 1:
raise RuntimeError(common.message)
payload = bytes(reply.jpeg.image.data)
frame = decode_jpeg(payload, rotate=0)
状态检查可以写成一个简单条件:只有 code == 0 且 status.value == SUCCESS,才继续解码。用失败分支表达时,两个条件之间就是 or。配套测试分别构造了两层失败的情况,用来防止将错误响应送入解码器。
| 检查顺序 | 处理方式 |
|---|---|
| 服务可发现 | 等待上限明确;服务不存在时给出错误 |
| 响应状态成功 | 同时核对框架码和业务状态 |
| 载荷非空且为 JPEG | Pillow 实际解码,不仅检查文件后缀 |
| 尺寸和方向正确 | 记录解码尺寸,再做可选的 180° 旋转 |
| 写文件成功 | 保存图像和元信息,便于之后比对 |
decode_jpeg 位于 camera_capture.py:通过 BytesIO 打开载荷,确认 JPEG 格式后转为 RGB,再按配置旋转和保存。服务响应、图像解码、文件写入因此成为三个清楚的检查点,出错时日志可以直接指向其中一环。
3 画面能打开以后,再检查方向与颜色
T1 文档对左目画面有一条具体提示:双足形态下,head_stereo_left 的原始 RTSP 图像可能倒置,需要在应用层进行 180° 旋转。先看现场画面,再设置 --rotate 0 或 180;将方向校正固定在一个位置,就能避免后续重复处理。2

图 2|用带上下标记的合成图验证 180° 旋转:通过固定标记位置检查处理前后的像素对应关系。
方向、颜色和尺寸最好分成三项配置。OpenCV 读取的视频帧采用 BGR 排列,交给 RGB 模型前做一次颜色转换;PIL.convert("RGB") 得到的图像则沿用 RGB。Resize 再把图像调整为模型尺寸。每一步只做一次,后续接模型就更直接。5
python
## OpenCV 视频帧路径:按现场需要旋转,再转换颜色。
if rotate_deg == 180:
frame = cv2.rotate(frame, cv2.ROTATE_180)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
## 若已在抓拍脚本旋转并存盘,推理脚本保持 --rotate 0。
与原手势模型衔接时,先保持整图缩放到 224×224 和原来的归一化。下一轮再用同一组 T1 样本比较整图与手部裁剪输入,观察手势尺度和背景变化带来的影响。这样可以看清收益究竟来自输入处理,还是来自模型更新。
4 从一张图,扩展到连续取帧
RTSP 地址由机器人当前 IP、端口和 camera_id 组成:rtsp://:/live_<camera_id>。公开文档的端口示例为 2554。把实际地址传给视频脚本前,可先读取本地视频,确认 FFmpeg 后端和图像保存都正常。25
bash
python3 -m pip install -r requirements-video.txt
## 先读取自己的视频,检查取帧与文件输出:
python3 camera_stream.py --input my_clip.mp4 --frames 100
## 设备连接后替换输入地址:
python3 camera_stream.py \
--input rtsp://ROBOT_IP:2554/live_head_stereo_left \
--rotate 0 --frames 100
示例先用 --rotate 0 保留原方向;确认左目画面倒置后再改为 180。脚本设置打开和读帧超时,通过 --frames 限定读取规模,每十帧保存一次采样图,并在结束时释放 VideoCapture。现场先看输出文件与 frames_read,便于确认视频路径是否工作。
python
cap = cv2.VideoCapture(url, cv2.CAP_FFMPEG, [
cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 5000,
cv2.CAP_PROP_READ_TIMEOUT_MSEC, 3000,
])
if not cap.isOpened():
raise RuntimeError('视频源未打开')
ok, frame = cap.read()
if not ok or frame is None:
raise RuntimeError('没有收到可解码帧')
这份脚本是一个便于检查的顺序取帧工具。后续接入较慢的模型时,可以把采集与推理解耦,增加容量为 1 的最新帧缓冲,并记录采集时间。如此一来,推理消费的是新画面,而不是逐帧追赶积压的视频。
5 用回放检查处理,再用设备检查链路
bash
## 以下均在 t1-pro-sdk-demos 根目录执行:
python3 camera_capture.py --mock --rotate 180
python3 -m unittest discover -s tests -v
## 已加载 ROS / SDK 环境后:
python3 camera_capture.py --live \
--camera-id head_stereo_left --rotate 0 --out results/t1.jpg
本次图像用例全部通过:640×360 合成 JPEG 成功解码,旋转后的指定像素与原图对应,空载荷和非 JPEG 载荷被正确处理。结果纳入整套 40 项单元测试。接入设备后,按返回元信息记录实际分辨率、相机 ID、字节数与取图耗时。
| 现象 | 排查路径 |
|---|---|
| 抓拍失败,视频可用 | 核对相机服务是否存在、请求类型和响应字段 |
| 视频打不开,ROS 正常 | 检查 RTSP 端口、地址、相机 ID 和 FFmpeg 后端 |
| 方向正确但颜色异常 | 核对 BGR / RGB 是否遗漏或重复转换 |
| 视频越来越滞后 | 检查采集/处理是否积压;接模型时采用最新帧缓冲并记录时间戳 |
结语
先取一张图,再处理一段视频,最后才把像素交给模型,这是一条容易复查的视觉开发路线。做完这一篇,后续应用可以复用相机选择、解码、方向和颜色处理,把注意力集中到真正需要解决的识别任务上。
源码与复现
公开仓库:WTYuong/t1-pro-sdk-demos | 本文示例版本:3a62e33
复现版本:git checkout 3a62e33(克隆并进入仓库后执行)。
本篇代码:camera_capture.py · camera_stream.py · ros_io.py
复现命令(仓库根目录):python3 camera_capture.py --mock --rotate 180 # 合成 JPEG 与方向校验
连续取帧:python3 camera_stream.py --input rtsp://ROBOT_IP:2554/live_head_stereo_left --rotate 0 --frames 100。将 ROBOT_IP 换成实际地址;需要方向校正时再改 --rotate。
资料与代码来源
1 T1 SDK 快速入门与集成指南 查看原文
2 T1 接口说明:视觉采集与 RTSP 查看原文
3 CaptureJpegImage.srv / JpegImage.msg 查看原文
4 JpegImage:压缩图像载荷层级 查看原文
5 OpenCV VideoCapture 官方文档 查看原文
技术资料核对日期:2026-10-20。配图分别展示接口通路与合成图像的方向回放。
继续开发时,可到启元机器人开发者社区(https://dev.primebot.com/)查阅 SDK、接口文档与开发案例,也欢迎分享自己的复现记录和改进思路。
hello,这里是 晓雨的笔记本。如果你喜欢我的文章,欢迎三连给我鼓励和支持:👍点赞 📁关注 💬评论,我会给大家带来更多有用有趣的文章。
原文链接 👉 【发布后更新】
欢迎大家点开下面名片,添加好友交流。