简介:在嵌入式视觉应用中,GMSL2接口以高带宽、低延迟特性成为Jetson平台车载相机接入的主流方案。该驱动资源包面向Jetson开发者与ROS使用者,解决GMSL2相机在JetPack环境下的驱动编译、参数配置与图像话题获取问题。资源共281个文件,包体仅104KB,包含cmake构建配置、make脚本、launch启动文件、Python测试节点、bash环境设置及catkin工作空间元文件,可支撑从环境准备到节点集成的完整流程。目前已有1584人学习下载。包内my_publisher与my_subscriber示例展示了图像话题的发布订阅写法,便于快速验证驱动;目录结构保留catkin_workspace、built_by等元信息,适合对照理解ROS工作空间组织方式。对于正在搭建GMSL2相机驱动环境、需要参考源码结构或排查编译问题的开发者,这套资源提供了紧凑而可用的参考样板。
1. 拿到 Jetson 的 GMLS2 相机驱动包,我第一反应是装完就能出图像,结果翻车了
拿到 GMLS2 相机驱动包时,我以为和普通摄像头一样,把驱动文件放进系统、重启、打开 /dev/video0 就能采图。结果按说明走到第三步就卡住:内核模块编译报错、设备树 overlay 没生效、传感器一直枚举不到。折腾两天才摸清,GMLS2 在 Jetson 平台上不是一款用户态驱动,而是需要围绕 JetPack 的 L4T 内核做模块编译、设备树配置和 I2C 参数联调的 CSI 相机驱动。它适合正要把工业相机集成到 Jetson 视觉设备、做产线视觉、或者维护机器人视觉模组的开发者。这篇笔记会告诉你驱动包为什么不是装完即用、哪些参数必须改、以及排错时先查哪条链路。
2. 先看懂驱动构成:GMLS2 驱动不是一个 .so,是一整套内核模块链
2.1 驱动包里到底有什么
解压 GMLS2 驱动包后,目录结构大致是这几块:
| 目录 / 文件 | 作用 |
|---|---|
module/ | 内核模块源码,对应 sensor 驱动主体 |
dts/ | 设备树 overlay 源文件示例,用于声明外接 sensor |
firmware/ | 相机初始化寄存器序列与固件描述 |
scripts/ | 一键编译与安装脚本,但版本匹配做得不足 |
README.md | 版本配对表与接线说明 |
很多人拿到包就直接跑scripts/install.sh,以为脚本会帮你处理一切,实际脚本只做了「编译模块 + 拷贝 .ko 到系统目录」两件事,设备树 overlay 和 I2C 地址都没法自动适配,必须根据你的 Jetson 型号和相机接口手动改。
2.2 为什么不能把它当普通摄像头驱动用
GMLS2 相机走的是 CSI 接口,Jetson 上 CSI 相机驱动要注册进 Linux 的 media 子系统,由 v4l2 subdevice 节点暴露给用户态。这不是apt install能解决的,必须是内核模块。内核模块有个硬性约束:编译时的内核版本和运行时的内核版本必须一致。Jetson 的内核由 JetPack 决定,同一个相机驱动包,在 JetPack 5.0 上编译出的 .ko,放到 JetPack 5.1 上直接 refuse to load,因为 vermagic 对不上。
我一般会先确认目标板当前内核版本:
uname -r # 输出类似 5.15.0-tegra cat /proc/device-tree/model # 输出类似 Jetson Xavier NX Developer Kit第一个命令告诉你内核版本,第二个命令告诉你板卡型号。实际项目里,很多开发者把驱动包在 PC 上交叉编译完直接扔到 Jetson 上,insmod 报Invalid module format或version magic mismatch,问题就出在没核对这两项。正确做法是:在 Jetson 板端查看版本,下载对应版本的驱动源码,再编译。
2.3 整条工作链路:从开机到出图是谁在起作用
GMLS2 相机在 Jetson 上从上电到出图像,要经过一条完整的链路,任何一环断了都会导致无图。
- bootloader 加载设备树,内核读
compatible字段匹配驱动 - I2C 控制器探测 sensor 地址,命中后触发驱动
probe - 驱动执行初始化寄存器序列,配置 CSI lane 数和时钟
- 驱动注册 v4l2 subdevice,建立 media controller 拓扑
- 用户态程序打开
/dev/video0,申请 buffer,开始采图
第 1 步和第 2 步是失败重灾区。设备树 overlay 没打进去,内核根本不知道有这颗 sensor;I2C 地址对不上,probe 直接失败,dmesg里看不到probe success。
我排查这类问题有个固定动作,先看 dmesg 里有没有 sensor 字样:
dmesg | grep -i gmls2 dmesg | grep -i "i2c.*probe\|csi.*probe"如果输出为空,说明设备树 overlay 没生效,或者 I2C 探测根本没走到。如果看到probe failed,一般是地址冲突或者 reset GPIO 配置不对。这一条链路上,设备树和 I2C 是优先排查的对象,模块源码本身比较少出问题,因为出厂前厂商已经调过一遍。
3. 不要急着编译:版本配对矩阵决定你能不能省下两天时间
3.1 先从三张表里找出你的组合
GMLS2 相机驱动在不同 JetPack 版本上的编译结果不通用,其原因不在驱动源码本身,而在 Linux 内核接口。比如struct v4l2_subdev_ops在不同内核版本里字段有变化,device_tree的reset-gpios解析接口也不同。
版本配对的基本逻辑是:
| Jetson 平台 | JetPack 版本 | L4T 内核版本 | GMLS2 驱动包版本 |
|---|---|---|---|
| 较新模组 | 5.1+ | 5.10/5.15 | 随包标注为 v2.x 及以上 |
| 较旧模组 | 4.6 | 4.9 | v1.x |
注意,驱动包版本不是越高越好。我见过有人拿着新版本驱动包去配旧 JetPack,结果驱动里用了一个v4l2_async_nf_add_i2c新接口,旧内核根本不存在这个符号,编译直接失败。反向操作更常见:旧驱动包配新内核,probe时报unknown field错误。
建议拿到驱动包先做一件事:打开 README 或module/Makefile,看它声明的内核版本 range,再用uname -r比对当前系统。范围以外,不要抱侥幸心理,老老实实换 JetPack 版本,或者找厂商要适配版本。
3.2 完整编译安装过程(板载编译版)
我通常用板载编译而不是 PC 交叉编译,原因很简单:交叉编译器版本与内核工具链不一致时,会出现ftsafe等诡异问题,玄学难查。板载编译只要把内核源码放到位就能跑。
首先准备内核源码,JetPack 自带源码包,一般位于/usr/src/目录。
ls /usr/src/ # 期望看到类似 linux-tegra 或 kernel-5.15 的目录 cd /usr/src/linux-tegra然后导出编译环境变量:
export ARCH=arm64 export LOCALVERSION=-tegra export CROSS_COMPILE= make tegra_defconfig make modules_prepare这里有几个关键参数要说明:
ARCH=arm64:Jetson 是 64 位 ARM 架构,不设会默认 x86,编译出的模块直接报废。LOCALVERSION=-tegra:这个必须和内核 release 版本后缀一致,否则编译后的模块 vermagic 对不上。怎么看?uname -r里的-tegra后缀就是它。CROSS_COMPILE=:板载编译时留空,如果你是 PC 交叉编译,要填成aarch64-linux-gnu-前缀。
modules_prepare的作用是生成模块编译所需的头文件和符号表,不跑这一步直接编译驱动,会报一堆missing include。
接下来进入驱动源码目录编译:
cd /path/to/gmls2_driver/module make -j4如果一切顺利,会生成*.ko文件。完成后先不要急着安装,直接加载测试:
sudo insmod gmls2.ko dmesg | tail -50看到gmls2 probe success或类似日志,说明驱动本身是活的。如果insmod报Exec format error,九成是 ARCH 或 LOCALVERSION 没配对。
3.3 安装驱动并确认设备节点
确认 probe 成功后,再进入正式安装环节:
sudo cp gmls2.ko /lib/modules/$(uname -r)/kernel/drivers/media/i2c/ sudo depmod -a sudo modprobe gmls2三步的含义分别是:把模块放进标准模块目录、刷新模块依赖、按名加载。注意modprobe比insmod好用的地方在于它会自动处理依赖,如果 gmls2 依赖别的符号,modprobe 会帮你先加载依赖模块。
此时检查 v4l2 设备:
v4l2-ctl --list-devices如果看到类似gmls2 (platform:tegra-capture)这样的条目,说明 v4l2 已经识别到。没有看到,问题大概率不在驱动,而在设备树 overlay 或者 I2C 配置,进入下一章处理。
4. 设备树与 I2C 参数配置:决定相机能否被枚举到的关键一步
4.1 为什么 Jetson 非要 overlay 不可
Jetson 的外设配置不是传统 ARM 那种直接把 device tree 编死到 dtb,而是用configfs动态叠加 overlay。GMLS2 相机作为 sensor,必须有一个 overlay 声明它的存在,包括它挂在哪个 I2C 总线上、使用哪些 GPIO、占哪几个 CSI lane。
没有 overlay,Jetson 内核的 tegra-capture 驱动根本不知道有 sensor 存在,/dev/video0大概率不存在。很多开发者编译完驱动发现找不到设备,问题就出在这一步。
overlay 源文件一般在驱动包的dts/目录下,文件名形如tegra234-gmls2-overlay.dts。拿到后需要针对自己的模组做修改。
4.2 一个可复现的 overlay 修改示例
以下是我在项目里用过的最小可用 overlay,关键位置都有注释:
/dts-v1/; /plugin/; / { overlay-name = "GMLS2 Sensor"; compatible = "nvidia,p3768"; fragment@0 { target-path = "/i2c@3180000"; __overlay__ { #address-cells = <1>; #size-cells = <0>; status = "okay"; gmls2: gmls2@10 { compatible = "vendor,gmls2"; reg = <0x10>; reset-gpio = <&gpio 41 GPIO_ACTIVE_HIGH>; pwdn-gpio = <&gpio 42 GPIO_ACTIVE_HIGH>; clock-frequency = <24000000>; port { gmls2_out: endpoint { remote-endpoint = <&csi_csi_port0>; >dtc -@ -I dts -O dtb -o gmls2.dtbo gmls2.dts sudo cp gmls2.dtbo /boot/部分新版本 Jetson 还支持在模块目录里合并,具体看你的 JetPack 的 bootloader 版本。
4.3 I2C 地址冲突与时钟频率的坑
overlay 里写的 I2C 地址是不是能正常工作,要实际扫总线。Jetson 上常用i2cdetect工具确认设备地址:
i2cdetect -y -r 7这里的 7 是 I2C 总线号,不同模组不一样,可以通过ls /dev/i2c-*确认。如果扫描结果里在多处出现地址,说明总线配置有问题;如果扫描地址和你写在 dts 里的reg不一致,以扫描结果为准,但要排除板子上其他器件的地址冲突。
地址冲突的表现是:probe 不报错,但寄存器读写返回的数据完全不对。原因往往是有两个器件共用了同一个 I2C 地址,一个的应答干扰了另一个的访问。排查办法是断开其他器件,只保留 GMLS2 再扫一次。
时钟频率也要注意。Jetson 的 I2C 时钟默认可能是 100kHz 或 400kHz,GMLS2 这类 sensor 一般要求时钟频率不能超过额定值。改错会导致 sensor 寄存器偶发读写失败,表现为有时出图有时黑屏。稳妥的做法是保持 schema 里的clock-frequency与相机规格书一致,不要想当然地「超频」。
5. 避坑与排查:五条实测踩坑记录,按现象、原因、解决排序
5.1 编译模块报错:missing include、unknown field满天飞
现象:编译 gmls2.ko 时,报一堆头文件缺失,或者指向struct里不存在的字段。
原因:有坑坐实了,内核源码没有先执行modules_prepare,编译驱动时缺少生成好的头文件与符号表。更常见的是内核源码版本和当前运行内核不一致——源码包是 5.10,系统跑的是 5.15。
解决:回到/usr/src/linux-tegra(或对应源码目录),先确认当前目录的源码分支版本和uname -r一致,再执行make tegra_defconfig && make modules_prepare。每次切换内核源码版本后,都要重跑这两步,不能缓。
5.2 overlay 编译成功并拷贝到 /boot,但 dmesg 里完全没有 gmls2 字样
现象:重启后dmesg | grep gmls2为空,/dev/video0不出现。
原因:新版本 Jetson 的 bootloader 只从kernel-dtb目录加载 overlay,或者需要指定 overlay 名称列表,光把 dtbo 拷到 /boot 下并不会自动加载。
解决:先确认你的载板对应的 overlay 加载方式。常见做法是修改/boot/extlinux/extlinux.conf,在FDT或OVERLAYS字段加上 gmls2。修改后重启再检查。注意,extlinux.conf 里FDT指定的基础 dtb 文件也要对,基础 dtb 对应错,overlay 打不进去。
5.3 i2cdetect 能扫到地址,但 probe 就是失败
现象:I2C 扫描能看到地址,dmesg 出现probe failed或chip ID mismatch。
原因:最常见的是reg地址写对了,但 reset GPIO 和 pwdn GPIO 顺序颠倒。GMLS2 在 probe 时会先拉高 reset 释放复位,再读寄存器验证 ID。GPIO 接反,读到的寄存器全为零,自然验证不通过。
解决:用逻辑分析仪或者连续打印 GPIO 电平的方式,确认 reset 引脚在 probe 时确实出现过电平翻转。我一般会先在 dts 里把 reset-gpio 严格对照载板原理图走一遍,名字都写上注释,不靠猜。
5.4 图像能出但花屏、条纹、颜色偏绿
现象:v4l2-ctl 能看到设备,但采集到的图像是花的,或者条纹状。
原因:CSI>v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=GRBG10 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=1 --stream-to=/tmp/gmls2.raw
pixelformat按 GMLS2 规格书填,常见的是 raw Bayer 格式。这里GRBG10表示 10 位 Bayer 排列,如果格式不对,采出来的图会错位严重,哪怕是真数据也像花屏。
然后用 Python 把这个 raw 文件转成可视图像:
import numpy as np import cv2 raw = np.fromfile('/tmp/gmls2.raw', dtype=np.uint8) img = raw.reshape(1080, 1920) img16 = (img.astype(np.uint16) * 4).astype(np.uint16) color = cv2.cvtColor(img16, cv2.COLOR_BayerGRBG2BGR) cv2.imwrite('/tmp/gmls2.png', color)这段代码做的事很简单:把裸数据按宽高重排成二维数组,做一个 10 位到 16 位的左移,再用 OpenCV 做 Bayer 去马赛克。如果你是RGGB排列,把cv2.COLOR_BayerGRBG2BGR换成cv2.COLOR_BayerRGGB2BGR。
正常出图后,再验证一下 media 拓扑是否完整:
media-ctl -p /dev/media0看输出里有没有gmls2节点以及它到tegra-capture的 link 状态。链路不生效的典型现象是 v4l2-ctl 能打开设备但streamon超时,这时问题多数在 CSI 虚通道配置。
从那以后,我每次接 Jetson 相机驱动项目,都强制先走一遍版本配对矩阵、再动设备树 overlay、最后才编译模块,顺序反了就是给自己挖坑。希望帮到你。
本文还有配套的精品资源,点击获取