给 USB 摄像头固定一个永久设备名:Ubuntu/Linux udev 规则实战
⚡ 效率提升 硬件 ubuntu 14

TL;DR:做机器视觉时,USB 摄像头的 /dev/videoN 编号会随重启或插拔漂移,双目系统里左右摄像头一错位程序就崩。用 Linux 的 udev 规则按硬件属性(厂商/产品 ID + 序列号)给它绑一个固定的软链接(比如 /dev/4k_camera),从此编号怎么变都不影响。下面是查指纹 → 写规则 → 激活 → 代码适配的完整流程,命令可直接抄。

这个问题有多烦

做机器人 / 机器视觉 / 自动化的应该都中过招:昨天代码跑得好好的,今天一重启、或者顺手拔插了下 USB,摄像头就从 /dev/video0 变成了 /dev/video2,程序直接报错。对于要靠编号区分左右目的双目系统,这更是灾难。

根治办法是用 udev 给摄像头办一张「身份证」——按它的硬件属性绑一个固定路径,不管编号怎么跳,这个路径始终指向它。一句话概括就是把「会变的编号」和「你代码里写的名字」解耦:

                    会随重启/插拔漂移
  USB 摄像头 ──内核分配──▶ /dev/video0 或 video2 或 video10 …
      │                              ▲
      │ 硬件属性(idVendor/idProduct/  │ udev 规则按属性匹配后
      │ serial + index==0)           │ 创建固定软链接
      └──────────────────────────────┘
                    ▼
        /dev/4k_camera  ← 代码里只认这个,永不漂移

第一步:把摄像头的底细查清楚

要让系统在一堆设备里认出它,先拿到它的硬件「指纹」。

1. 找到它的 USB ID

插上摄像头,终端执行:

lsusb

在输出里找到你的摄像头,记下 ID,比如:

Bus 001 Device 029: ID eba4:6579 Image+ 4K HD Camera

关键信息:

  • idVendor(厂商 ID)eba4
  • idProduct(产品 ID)6579

2. 区分「真假」视频流(关键)

现在的摄像头插上去常会生成两个设备节点(比如 video10video11):一个是真正出图的视频流,另一个只是元数据。我们只想绑那个能出图的。装个工具查一下:

sudo apt update && sudo apt install v4l-utils

分别查询(假设是 video10 / video11):

v4l2-ctl --list-formats-ext -d /dev/video10

如果输出了 [MJPG][YUYV] 这类格式列表,那它就是真身。

3. 拿到它的 udev 属性

确定真身(比如 /dev/video10)后,列出它全部 udev 属性:

udevadm info -a -n /dev/video10

在长长的列表里往上翻,重点看这两行:

  • ATTRS{serial}序列号,区分同型号硬件的最稳依据。
  • ATTR{index}接口索引,通常 index=="0" 就是视频流,用它过滤掉没用的元数据节点。

第二步:编写 udev 规则

/etc/udev/rules.d/ 下建个规则文件:

sudo nano /etc/udev/rules.d/89-my-camera.rules

把下面这行按你查到的信息改好填进去:

# 替换成你的 idVendor / idProduct / serial 和想要的别名
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="eba4", ATTRS{idProduct}=="6579", ATTRS{serial}=="DH-0000001", ATTR{index}=="0", MODE="0666", SYMLINK+="4k_camera"

逐项解释(这几处最容易踩坑):

  • ATTRS{serial}=="...":有序列号就一定加上,这是最稳的匹配条件。
  • ATTR{index}=="0"很重要,确保别名只绑到视频流,不会绑到那个没用的元数据节点。
  • MODE="0666":顺手把权限放开,省得以后跑代码报 Permission Denied
  • SYMLINK+="4k_camera":这就是固定别名,以后直接用 /dev/4k_camera

保存退出(Ctrl+XYEnter)。

第三步:激活规则

让系统重新加载规则:

sudo udevadm control --reload-rules

然后拔掉摄像头重新插上,或者手动触发一次:

sudo udevadm trigger

验证一下:

ls -l /dev/4k_camera

看到类似这样的输出就成了——无论系统怎么变,/dev/4k_camera 永远指向你的设备:

lrwxrwxrwx 1 root root 7 ... /dev/4k_camera -> video10

第四步:代码里怎么用

有些版本的 OpenCV 不认字符串路径(如 /dev/4k_camera),只认数字 ID(0、1、2…)。所以加一个把软链接解析成数字 ID 的小函数:

import cv2
import os
import sys

def get_camera_id_from_symlink(symlink_path):
    """
    把咱们设置的固定别名(如 /dev/4k_camera)解析成 OpenCV 能认的数字 ID
    """
    if not os.path.exists(symlink_path):
        print(f"❌ 哎呀,找不到 {symlink_path}!是不是没插好或者规则没生效?")
        return None

    # 获取它指向的真实路径 (例如 /dev/video10)
    real_path = os.path.realpath(symlink_path)
    try:
        # 从 /dev/video10 中提取出 10
        device_id = int(real_path.replace("/dev/video", ""))
        print(f"✅ 成功连接:{symlink_path} -> {real_path} (ID: {device_id})")
        return device_id
    except ValueError:
        print("❌ 解析失败,这看起来不像是一个标准的 video 设备。")
        return None

# ==================== 使用示例 ====================
if __name__ == "__main__":
    # 指定我们在 udev 规则里起的名字
    MY_CAMERA = "/dev/4k_camera"

    # 1. 转换 ID
    cam_id = get_camera_id_from_symlink(MY_CAMERA)

    if cam_id is not None:
        # 2. 正常初始化
        cap = cv2.VideoCapture(cam_id, cv2.CAP_V4L2)
        # 这里可以加你的 set 宽高逻辑
        if cap.isOpened():
            print("🚀 摄像头启动成功!开始干活!")
            cap.release()
        else:
            print("💀 无法打开摄像头,可能被占用了?")

思路很简单:os.path.realpath 拿到软链接指向的真实 /dev/videoN,再把末尾数字抠出来交给 cv2.VideoCapture。这样代码里始终写固定别名,底层编号怎么漂都无所谓。

常见问题(FAQ)

Q:两个一模一样的摄像头、又都没有序列号,怎么办? A:只能靠物理接口区分。在 udevadm info 里找 KERNELS=="..." 这一行(代表物理 USB 端口路径),把规则里的 ATTRS{serial} 换成 KERNELS=="1-2.1"。副作用是:以后左摄像头必须插固定那个口,插错就不灵了。

Q:规则不生效? A:检查三点——①文件名必须以 .rules 结尾;②仔细看有没有拼写错误,尤其逗号和引号;③记得 reload-rules 并重新插拔设备。

Q:跑代码提示 Permission Denied A:看规则里加 MODE="0666" 了没。不加的话默认只有 root 能用,加上就所有用户可读写了。

小结

udev 规则的本质,是把「内核给设备分配的临时编号」和「你按硬件属性指定的固定名字」解耦——和我那个 折叠 GNOME 托盘图标的扩展 里对付「随机 ID」的思路异曲同工:不依赖会变的东西,只认稳定的标识。配一次,机器人上的摄像头就再也不会乱跳了。这类内网设备开发场景,通常还要配合远程访问,可以顺带看看 用 FRP 给内网设备做 SSH 穿透

给 USB 摄像头固定一个永久设备名:Ubuntu/Linux udev 规则实战
http://localhost:8090/archives/ju-jue-she-bei-luan-tiao-ubuntu-linux-yong-jiu-gu-ding-usb-she-xiang-tou-id-zhong-ji-zhi-nan
作者
fnidore
发布于
更新于
许可