简介:这份资源面向在英伟达Jetson平台上开发计算机视觉与自动驾驶应用的工程师,提供GMSL2相机驱动的完整ROS工作空间源码,用于解决高带宽、低延迟图像传输场景下的相机接入与调试问题。GMSL2相比传统MIPI CSI-2具备更高数据速率和更长线缆支持,适合远程部署的车载摄像头与ADAS系统。资源包共281个文件,约104KB,以cmake与make构建脚本为主体,辅以sh、bash、zsh等环境配置脚本,以及py、cpp、c源码和launch启动文件,构成可直接编译的catkin工作空间,另含rosinstall依赖清单与stamp、marks等编译标记文件。目前已有1583人学习下载。读者可据此完成驱动编译、参数配置与话题数据验证,并进一步将相机流接入目标检测、图像处理或SLAM节点,快速搭建可复用的Jetson GMSL2相机开发环境。
1. 拿到一块 Jetson 板子,第一件事为什么是死磕 GMLS2 相机驱动
如果你手上有一块 Jetson 开发板,不管是 Nano、Xavier NX 还是 Orin 系列,插上一颗 GMLS2 接口的相机模组,大概率会遇到一个很现实的问题:系统能识别到设备,但/dev/video*节点死活出不来,或者出来了却抓不到图。这不是你接线的问题,也不是相机坏了,而是 Jetson 平台上的相机驱动链路和普通 x86 Linux 完全不是一回事。Jetson 走的是 NVIDIA 自己的 ISP 和 VI(Video Input)通路,GMLS2 这类模组需要通过设备树(Device Tree)正确声明,再配合内核驱动和 Argus 或 V4L2 接口才能出流。这篇笔记就是围绕这套驱动链路,把从设备树配置到实际抓图验证的完整流程拆开讲清楚。适合正在 Jetson 上做视觉项目、被相机驱动卡住的开发者,也适合想提前了解 Jetson 相机接入边界的熟手。
2. GMLS2 驱动链路拆解:从设备树到 /dev/video 节点
2.1 Jetson 相机通路的三个关键层
在 Jetson 上接一颗 GMLS2 模组,数据流大致经过三层:物理接口层(MIPI CSI-2 信号)、内核驱动层(设备树 + sensor 驱动 + VI 驱动)、用户空间层(Argus / V4L2)。很多人只盯着 sensor 驱动看,结果忽略了设备树里 CSI 通道的映射关系,最后表现为“驱动加载了但没节点”。
常见做法是先把模组的 I2C 地址、MIPI 通道号、lane 数量、时钟频率这几个参数确认清楚。GMLS2 模组通常通过 I2C 配置寄存器,地址常见为0x10或0x36,具体看模组规格。MIPI 这边要确认是 2-lane 还是 4-lane,以及接在 Jetson 的哪个 CSI 端口上。这些信息如果和设备树里写的不一致,内核启动阶段就会报tegra-csi相关的 probe 失败。
我一般会先跑一遍dmesg | grep -i csi和dmesg | grep -i i2c,看内核有没有在启动时尝试探测这颗 sensor。如果连 I2C 通信都没建立,后面所有步骤都是白搭。
2.2 设备树里必须改对的四个节点
Jetson 的设备树文件通常位于内核源码的hardware/nvidia/platform/t19x/或t23x/目录下,具体路径取决于你的 L4T 版本。GMLS2 模组需要改的节点主要有四个:
i2c@节点下的 sensor 子节点:声明 compatible、reg、时钟、GPIOtegra-camera-platform节点:声明模组位置和方向vi@或csi@节点:配置通道和 lanegpio节点:复位和电源使能引脚
下面是一个典型的 sensor 子节点配置片段,以 2-lane 模式为例:
i2c@3180000 { status = "okay"; gmls2_sensor: gmls2@10 { compatible = "nvidia,gmls2"; reg = <0x10>; devnode = "video0"; /* 时钟和 GPIO */ clocks = <&bpmp_clk TEGRA194_CLK_EXTPERIPH1>; clock-names = "extperiph1"; reset-gpios = <&tegra_main_gpio CAM0_PWDN GPIO_ACTIVE_HIGH>; vana-supply = <&p2822_avdd_cam_2v8>; vif-supply = <&p2822_vdd_1v8_cam>; vdig-supply = <&p2822_vdd_cam_hv_2v8>; /* MIPI 配置 */ mode0 { num_lanes = "2"; tegra_sinterface = "serial_a"; mclk_khz = "24000"; pix_clk_hz = "148500000"; active_w = "1920"; active_h = "1080"; pixel_t = "bayer_rggb"; line_length = "2200"; embedded_metadata_height = "2"; }; }; };这段配置里几个参数容易翻车:tegra_sinterface要和实际接的 CSI 端口对应,serial_a对应 CSI-A;pix_clk_hz要和模组实际输出匹配,写大了会花屏,写小了会丢帧;embedded_metadata_height如果模组不支持嵌入行,要设为0,否则驱动会多读几行导致图像错位。
2.3 编译和刷写设备树
改完设备树后,需要重新编译并刷写。常见做法是在 L4T 内核源码目录下执行:
# 编译设备树 make dtbs # 生成的 dtb 文件在 arch/arm64/boot/dts/ 下 # 刷写到 Jetson 的 boot 分区 sudo cp arch/arm64/boot/dts/tegra194-p3668-0001-p3509-0000.dtb /boot/ sudo reboot重启后先别急着抓图,先确认设备树有没有生效:
# 查看设备树中 sensor 节点是否存在 ls /proc/device-tree/i2c@3180000/gmls2@10/ # 查看内核日志中 sensor probe 情况 dmesg | grep -i gmls2如果看到gmls2: probing和gmls2: registered之类的日志,说明驱动已经绑定成功。如果只有gmls2: probe failed,大概率是 I2C 地址不对或者电源 GPIO 没配对。
3. 驱动加载与出流验证:从 v4l2-ctl 到实际抓帧
3.1 确认 /dev/video 节点和格式支持
驱动 probe 成功后,用户空间应该能看到/dev/video0节点。先用v4l2-ctl确认设备能力:
# 列出所有 video 设备 ls /dev/video* # 查看设备支持的格式 v4l2-ctl -d /dev/video0 --list-formats-ext正常输出里应该能看到RG10或YUYV之类的格式,以及对应的分辨率列表。如果--list-formats-ext返回空,说明 VI 通道没配对,回到设备树检查tegra_sinterface和num_lanes。
3.2 用 v4l2-ctl 抓一帧原始图
确认格式后,可以直接抓一帧保存下来:
# 抓取一帧 1920x1080 的 RAW 图 v4l2-ctl -d /dev/video0 \ --set-fmt-video=width=1920,height=1080,pixelformat=RG10 \ --stream-mmap=4 \ --stream-to=frame.raw \ --stream-count=1 # 查看文件大小,1920x1080 RG10 应该是 1920*1080*2 字节左右 ls -lh frame.raw这里--stream-mmap=4表示用 4 个缓冲区做内存映射,--stream-count=1表示抓一帧就停。如果抓出来的文件大小明显偏小,比如只有几百 KB,说明丢帧或者格式没设对。
3.3 RAW 图转可视格式
RAW 图不能直接看,需要用工具转成 PNG 或 JPEG。常见做法是用raw2rgb或者自己写个简单的 Python 脚本:
import numpy as np from PIL import Image # 读取 RAW 数据,RG10 是 16 位存储但只有低 10 位有效 raw = np.fromfile('frame.raw', dtype=np.uint16) raw = raw.reshape((1080, 1920)) # 右移 2 位得到 8 位数据 img = (raw >> 2).astype(np.uint8) # 保存为灰度图先看效果 Image.fromarray(img).save('frame_gray.png') print('saved frame_gray.png')这段脚本把 10 位 RAW 数据右移 2 位转成 8 位灰度图。如果图像整体偏绿或偏紫,说明 Bayer 排列顺序和设备树里写的pixel_t不一致,需要改成bayer_bggr或bayer_gbrg再试。
3.4 用 Argus 接口出流
如果项目里用的是 NVIDIA Argus 而不是 V4L2,可以用argus_camera工具验证:
# 启动 Argus 相机预览 argus_camera --device=0Argus 对设备树的要求更严格,特别是tegra-camera-platform节点里的modules配置。如果 Argus 报No cameras available,但 V4L2 能出图,基本可以确定是tegra-camera-platform里的badge和position没写对。
4. 避坑排查:GMLS2 驱动调试中最容易翻车的五个点
4.1 现象:dmesg 里 I2C 通信失败,报 -121 或 -6
原因通常是 I2C 地址写错,或者模组供电没起来。GMLS2 的 I2C 地址有的是 7 位0x10,有的是0x36,设备树里写的是 7 位地址。另外检查vana-supply和vdig-supply对应的 regulator 有没有在设备树里使能。
解决:先用i2cdetect -y -r 0扫描 I2C 总线,确认模组地址能出现。如果扫不到,用万用表量一下模组供电引脚,确认 2.8V 和 1.8V 都正常。
4.2 现象:/dev/video0 存在但 --list-formats-ext 为空
原因一般是 VI 通道没配对,或者tegra_sinterface和实际 CSI 端口不一致。Jetson 的 CSI 端口映射在不同载板上可能不同,比如 CSI-A 在有的板子上对应serial_a,有的对应serial_c。
解决:查载板原理图确认 CSI 通道号,然后逐个试serial_a到serial_f,每次改完重新编译设备树并重启。
4.3 现象:抓图花屏或颜色错乱
原因通常是pix_clk_hz和模组实际输出不匹配,或者 Bayer 排列顺序写错。GMLS2 常见输出是 RGGB,但有些模组出厂配置是 BGGR。
解决:先用v4l2-ctl --get-fmt-video确认当前格式,然后调整设备树里的pixel_t。如果花屏是横向条纹,优先调pix_clk_hz,每次增减 10% 试。
4.4 现象:Argus 报 No cameras available 但 V4L2 正常
原因是tegra-camera-platform节点里的modules配置不完整。Argus 需要知道模组的位置(position)和 badge 信息,这些在 V4L2 通路里不是必须的。
解决:在tegra-camera-platform下补全modules节点,确保badge和position与设备树里 sensor 节点对应。常见做法是参考同平台其他模组的配置模板改。
4.5 现象:驱动加载后系统随机重启或内核 panic
原因通常是 GPIO 冲突或者时钟配置错误。Jetson 的 GPIO 引脚复用很复杂,如果复位引脚和别的外设冲突,内核在操作 GPIO 时可能触发异常。
解决:检查设备树里reset-gpios用的引脚有没有被其他节点占用。用cat /sys/kernel/debug/gpio查看当前 GPIO 占用情况,确认没有冲突后再重新编译设备树。
5. 进阶技巧:用 GStreamer 管道做低延迟预览和录制
驱动调通之后,实际项目里更常用 GStreamer 做预览和录制,因为延迟比 Argus 低,而且管道灵活。下面是一个典型的低延迟预览管道:
# Jetson 上低延迟预览 GMLS2 相机 gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! \ 'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1' ! \ nvvidconv ! \ nv3dsink sync=false这里nvarguscamerasrc走的是 Argus 底层,但 GStreamer 管道比直接调 Argus API 更容易做后续处理。sync=false是降低延迟的关键,表示不按时间戳同步,直接渲染。
如果要同时录制和预览,可以用tee分支:
gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! \ 'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1' ! \ tee name=t \ t. ! queue ! nvvidconv ! nv3dsink sync=false \ t. ! queue ! nvvidconv ! nvv4l2h264enc bitrate=8000000 ! \ h264parse ! qtmux ! filesink location=record.mp4这个管道一边预览一边录 H.264,bitrate=8000000是 8 Mbps,实际项目里根据存储和网络带宽调整。queue是必须的,否则两个分支会互相阻塞。
我一般会在管道跑起来之后用gst-launch-1.0 -v看每个元素的协商结果,确认nvarguscamerasrc输出的格式和分辨率是不是和预期一致。如果预览窗口黑屏但管道没报错,大概率是nv3dsink的显示后端没配对,换成nvoverlaysink或ximagesink试试。
从那以后我每次调新模组,都会先把v4l2-ctl --list-formats-ext和dmesg | grep -i csi跑一遍,确认底层通路没问题再上 GStreamer,省得在管道里瞎猜。希望帮到你。
本文还有配套的精品资源,点击获取