元器件 · 传感器
树莓派相机模块
插在树莓派 CSI 排线接口而不是 USB 上的相机:独立的数据通道、自动对焦,也是大多数机器人视觉代码默认使用的传感器。
它是什么
经扁平排线连到树莓派 CSI 口的小传感器板——一个专用的相机接口,不是 USB。这个区别就是它存在的全部理由。机器人上的树莓派,USB 总线上一般已经挂着 Wi-Fi、通往电机控制的串口,可能还有存储;而相机再往同一条总线上推 1080p,事情就到此为止了。
对机器人,相机是改变可能性的传感器。本站其他传感器都报一个数——距离、角度、反射率。相机报一个场景,机器人才能认出东西是什么,而不只知道有东西。
工作原理
光照到传感器上,传感器经 MIPI CSI-2(一条高速短距串行链路)把原始行数据送进树莓派的图像管线。树莓派把这些原始行变成可用的图像:去马赛克、白平衡、曝光、镜头暗角校正。在当前的 Raspberry Pi OS 上,这套工作由 libcamera 负责,命令行工具是 rpicam-apps,Python 里则是 Picamera2。
两件事对机器人要紧:
**管线花时间。**帧不是曝光瞬间就有的。代码看到它时几十毫秒过去,动的车已经动了。视觉闭环必须按这个延迟设计。
**曝光默认是自动的,除非你把它关掉。**自动曝光会很乐意在两帧之间改变你场景的亮度——拍照这样没问题,但对一个按亮度做阈值判断的视觉算法却是灾难。光线稳定之后,就把曝光和增益锁定。
什么时候用它
机器人要回答标量传感器答不了的问题时请相机:
- 基准标记——ArUco 标签从单帧就能给出完整的 3D 位姿,这是让机器人获得一次真实位置修正最便宜的办法。
- 现实世界循迹——相机看到几米外的线,反射阵列只看到正下方。
- 物体识别——分出椅子和墙,而不是对两个都读 40 cm。
- 视觉 SLAM——只用图像建图。
别为了避障去加一台相机。VL53L0X 用三行代码、20 ms 就能回答“有多远”;相机回答得一样的问题,既更差、更慢,又要多花上百倍的努力。
接线与坑
- **排线是有方向的。**银色触点要朝向板子连接器的触点——在树莓派上就是蓝色背胶朝向 USB/网口那一侧。插反了相机根本不会被识别,而且不会损坏任何东西。
- **先挑起连接器卡扣。**把塑料卡环轻轻挑起,让排线端端正正地插到底,再把卡扣压回去。排线如果只是斜着插进去,枚举就会时断时续,看起来像是相机坏了。
- **树莓派 5 和 Zero 需要 22 针排线。**相机是同一个,连接器更窄。下单前先确认。
- **让排线远离电机走线。**这是一条没有屏蔽的高速链路,就跑在你塞满底盘的其它线路旁边;串进来的电机噪声表现为画面损坏。
- **电源预算要留够。**除了树莓派自身的消耗外大约还要 250 mA,而且必须来自同一路供电——在这路供电上省钱,就会欠压并损坏 SD 卡。
- **对焦是一个设置项,不只是一个对焦环。**在三代模块上,画面反差低时自动对焦会来回搜索。对一台总是看差不多距离的机器人,设定一个固定的对焦位置,别让它在机动到一半时来回搜索。
选哪个模块
| 模块 | 传感器 | 分辨率 | 对焦 | 对机器人的意义 |
|---|---|---|---|---|
| 二代 | IMX219 | 8 MP | 定焦(可手动调) | 便宜、可预测,不会自动对焦来回搜索 |
| 三代 | IMX708 | 11.9 MP | 自动对焦 | 通用最优;机器人用锁对焦 |
| 三代广角 | IMX708 | 11.9 MP | 自动对焦 | 102° 视场——导航好得多 |
| HQ | IMX477 | 12.3 MP | C/CS 镜头座 | 要特定镜头或远距离 |
| 全局快门 | IMX296 | 1.6 MP | C/CS 镜头座 | 动的机器人最要紧的那款 |
全局快门值得好好解释,因为卷帘快门是机器人视觉最被低估的问题。
卷帘快门传感器逐行曝光而不是一起,帧顶比帧底早拍几毫秒。相机静止看不见。90°/s 转弯的机器人上,帧顶帧底是不同角度拍的,直竖线出来歪的。
对标记位姿管线来说,这不是个表面问题。ArUco 标签的角点被用来解算它的位姿,如果这四个角点是在机器人略有差别的四个朝向下拍到的,算出来的位姿就是错的,而且再怎么标定也纠正不过来。如果你的机器人要一边走一边看,全局快门传感器就是解法,代价是分辨率降低,这值得。
让图像可用的设置
自动曝光、增益和白平衡对摄影来说非常好,对视觉算法却主动有害。它们会在帧与帧之间改变你的阈值所依赖的数值,而这与场景本身无关。
from picamera2 import Picamera2
import time
picam = Picamera2()
# Small frames. 640x480 is plenty for markers and lines, and the pipeline
# cost scales with pixel count.
config = picam.create_video_configuration(
main={"size": (640, 480), "format": "RGB888"},
controls={"FrameDurationLimits": (33333, 33333)}, # lock to 30 fps
)
picam.configure(config)
picam.start()
# Let auto-exposure settle on the real scene, then freeze it.
time.sleep(2)
metadata = picam.capture_metadata()
picam.set_controls({
"AeEnable": False,
"AwbEnable": False,
"ExposureTime": metadata["ExposureTime"],
"AnalogueGain": metadata["AnalogueGain"],
"AfMode": 0, # manual focus
"LensPosition": 2.0, # dioptres: 1/distance_in_metres
})
while True:
frame = picam.capture_array()
# ... your processing here
LensPosition 的单位是屈光度——也就是对焦距离(以米计)的倒数。看约 50 cm 处的物体要设 2.0;看一米处设 1.0;无穷远是 0.0。显式设定它可以避免自动对焦在机动途中来回搜索——在一台运动着的机器人上,这会在最不该出现的时刻糊上一两秒画面。
分辨率、帧率和延迟
分辨率是大家留太高、代价超预期的设置。
| 分辨率 | 像素 | 相对算力 | 适合 |
|---|---|---|---|
| 320×240 | 77 k | 1× | 循迹、色块跟踪 |
| 640×480 | 307 k | 4× | ArUco 标记,大多数机器人视觉 |
| 1280×720 | 922 k | 12× | 小的或较远的标记 |
| 1920×1080 | 2.07 M | 27× | 录像,实时控制很少 |
树莓派 4 在 640×480 下跑 ArUco 检测,能轻松维持 30 fps。同样这套流程在 1920×1080 下会掉到个位数帧率,而且每帧在处理完时都已经过期。用能分辨你所关心特征的最小分辨率,同时记住检测距离是随分辨率变化的——如果标记在远处检测不到,那么提高分辨率确实有用,代价照付。
延迟预算
这就是视觉控制难的原因,值得写出来:
| 阶段 | 典型 |
|---|---|
| 曝光 | 10–30 ms |
| 传感器读出和 CSI 传输 | 5–15 ms |
| ISP 管线(去马赛克、白平衡) | 5–10 ms |
| 你的处理 | 10–50 ms |
| 图像到决策合计 | 30–105 ms |
0.3 m/s 的车这窗口走 1–3 cm;90°/s 转弯转 3–9°。视觉闭环作用的是车刚才在哪,按实时测量调参必振荡。
实践答案和别处一样:视觉环跑得比控制环慢,用它修正更快的航位推算,或按已知延迟前向预测再用。这正是路径跟踪给 GPS 搭的结构,原因一样。
装到车上
**刚性。**相机标定产出内参——焦距、主点、畸变——是那台相机在那个支架上的属性。车一加速就晃的支架让标定持续失效,位姿估计无故漂。
**远离电机线。**CSI 排线是无屏蔽高速差分链路。挨着电机线跑,开关噪声耦合进去就是花帧或相机间歇消失。
**排线做应力释放。**扁线连接器不为反复弯折设计,车还一直振。两端附近把排线贴住,别让振动把连接器晃松。
朝下偏一点(地面车)。指地平线的相机大半像素拍天花板。下偏十到二十度,有用的场景——前面地面和上面的东西——进画面中央。
故障排查
| 现象 | 可能原因 | 解决 |
|---|---|---|
| 相机完全认不出 | 排线插反 | 银触点对板子触点 |
| 时认时不认 | 排线没插到底摆正 | 挑起卡扣重插到底压好 |
树莓派 5 上 No cameras available |
线宽错 | 树莓派 5 和 Zero 要 22-pin 线 |
| 花帧撕裂 | 电机噪声上排线 | 远离电机线走;做应力释放 |
| 逐帧亮度变 | 自动曝光还开着 | 静置后锁曝光增益 |
| 机动途中画面变糊 | 自动对焦来回搜索 | 设 AfMode: 0 并固定 LensPosition |
| 转弯竖线歪 | 卷帘快门 | 转慢点,或全局快门模块 |
| 负载一高帧率就崩 | 分辨率太高 | 降到 640×480;查是否过热降频 |
| 位姿估计无缘无故漂移 | 相机支架在晃动 | 刚性安装;重新标定 |
| 相机一开树莓派就重启 | 电源扛不住多出的约 250 mA | 换更好的电源——这会损坏 SD 卡 |
相机还是测距传感器?
| 相机 | VL53L0X / HC-SR04 | |
|---|---|---|
| 回答 | 那里有什么、在哪、什么朝向 | 最近的东西多远 |
| 延迟 | 30–100 ms | 20–60 ms,无处理 |
| 代码 | 一整条视觉管线 | 三行 |
| 算力 | 树莓派级处理器 | 任何单片机 |
| 失效 | 黑、逆光、运动模糊 | 特定表面(见各页) |
| 成本 | $25–50 加树莓派 | $2–5 |
说句实在话:**别为了避障去加相机。**测距传感器回答这个问题更快、更便宜、也更可靠,而花在让相机干这件事上的每一小时,都是从只有相机才能干的事情上挪走的。
需要知道某个东西是什么、或者自己在哪的时候,就该加一台相机——识别物体、读取基准标记以获得一次绝对位姿修正,或者跟着一条机器人能提前几米看到的线,而不是只看着脚下。这些事情用标量传感器确实做不到,而它们值得你搭起整条管线。
探索关联图谱
用到它的搭建
包含 树莓派相机模块 的项目、学习路线和模拟器。
问题
树莓派相机模块 常见问题
做机器人,树莓派相机比 USB 摄像头更好吗?
对机器人来说通常是的,原因在于接口而不是画质。CSI 相机有一条直通树莓派图像管线的专用数据通道,所以它不会和 Wi-Fi 适配器或串口链路争抢 USB 带宽,帧时序也可预测得多。只有在你需要长线缆的时候——CSI 排线又短又脆弱——或者希望相机不作任何改动就能在笔记本电脑上使用时,USB 摄像头才是更好的选择。
做机器人该买哪款树莓派相机?
大多数机器人选三代相机模块——有自动对焦,有广角版可选,而且有最新的软件支持。如果机器人需要看到离自己很近的东西,就选广角(102°)版,而做导航时几乎总是如此。如果机器人要一边快速移动一边观察,就改选全局快门相机;如果需要特定的镜头,就选 HQ 相机。如果定焦能满足需要,二代相机模块依然够用,而且更便宜。
为什么机器人转弯时相机画面会歪斜?
这是卷帘快门造成的。传感器是一行一行读出的,而不是一次性捕获整帧,所以在读出期间移动的东西,在每一行上被记录的位置都略有不同——转弯会把直线剪切成斜线。这之所以要紧,是因为位姿估计假设每一帧对应同一个瞬间。可以缩短曝光时间,测量时让机器人放慢速度,或者使用一次性捕获所有行的全局快门相机。
树莓派 5 用的是同一种相机排线吗?
不是,而且这一点几乎把所有人都坑过。树莓派 5 和 Zero 系列使用更窄的 22 针连接器,而树莓派 4 及更早的型号使用大多数相机随附的 15 针连接器。相机本身是一样的;你需要合适的排线或转接头。树莓派 5 还有两个相机接口,所以可以接一对双目相机。
我还该用 picamera 库吗?
不该——原来的 picamera 模块是基于一套在当前 Raspberry Pi OS 上已经不存在的相机软件栈编写的。请使用 Picamera2(其底层是 libcamera),或者在相机作为 V4L2 设备出现后通过 OpenCV 采集图像。大多数几年前的教程都是为这个已经废弃的 API 写的,这也是复制来的代码片段在全新安装的系统上立刻报错的常见原因。
延伸阅读