docker 容器中,当需要读取外接usb 设备,每次插拔 sub 设备,设备编号一直变化怎么办?
docker 容器中,当需要读取外接usb 设备,每次插拔 sub 设备,设备编号一直变化怎么办?这会影响程序的读取。因此每次创建容器时候,设备编号就固定在 容器中了。比如:
在容器中运行:
root@h-pc:~/ros_ws# lsusb
Bus 002 Device 002: ID 174c:3074 ASMedia Technology Inc. ASM1074 SuperSpeed hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 031: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x UART Bridge / myAVR mySmartUSB light
Bus 001 Device 030: ID 2bc5:0502
Bus 001 Device 029: ID 2bc5:0403
Bus 001 Device 028: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
Bus 001 Device 002: ID 174c:2074 ASMedia Technology Inc. ASM1074 High-Speed hub
Bus 001 Device 005: ID 8087:0032 Intel Corp.
Bus 001 Device 004: ID 26ce:01a2
Bus 001 Device 003: ID 17ef:608c Lenovo
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
我们重新插拔 usb 设备后,设备编号 变为了 029 等等。
而 我们在容器中:
root@h-pc:~/ros_ws# ls -l /dev/bus/usb/001/
total 0
crw-rw-r-- 1 root root 189, 0 Jun 19 17:58 001
crw-rw-r-- 1 root root 189, 1 Jun 19 17:58 002
crw-rw-r-- 1 root root 189, 2 Jun 19 17:58 003
crw-rw-r-- 1 root root 189, 3 Jun 19 17:58 004
crw-rw-r-- 1 root root 189, 4 Jun 19 17:58 005
crw-rw-r-- 1 root root 189, 6 Jun 19 17:58 007
crw-rw-r-- 1 root root 189, 7 Jun 20 13:33 008
crw-rw-r-- 1 root root 189, 8 Jun 20 13:33 009
crw-rw-r-- 1 root root 189, 10 Jun 19 17:58 011
发现并没有这个设备编号。这会导致启动 launch 文件时候报错:
[ INFO] [1718863863.439322079]: Query device
[ INFO] [1718863863.539486552]: Device connected: (name, Astra) (uri, 2bc5/0403@1/29) (vendor, Orbbec)
[ INFO] [1718863863.539548038]: Trying to open device: 2bc5/0403@1/29
[ INFO] [1718863863.639677743]: OBCameraNodeFactory::onDeviceConnected Open device start
[ INFO] [1718863863.642548470]: OBCameraNodeFactory::onDeviceConnected Open device done, STATUS 4099
[ERROR] [1718863863.642597179]: Failed to open device: 2bc5/0403@1/29 Could not open "2bc5/0403@1/29": USB device not found!
解释:Bus 001 Device 026: ID 2bc5:0403
分别都是什么意思?
在 lsusb
命令的输出中,每行表示一个已连接的 USB 设备。以下是每个部分的含义:
详细说明
- Bus 001:
表示设备连接到的 USB 总线。USB 端口可以分布在多个总线(bus)上,每个总线有一个编号。这里,设备连接到总线编号为 001 的 USB 总线上。 - Device 026:
表示设备在该总线上的设备编号。每个连接到同一总线的设备都有一个唯一的编号。这里,该设备的编号为 026。 - ID 2bc5:0403:
表示设备的供应商 ID 和产品 ID。这两个值用于唯一标识设备的类型和制造商。
2bc5: 供应商 ID (Vendor ID),表示设备的制造商。这里,2bc5 是 Orbbec 的供应商 ID。
0403: 产品 ID (Product ID),表示特定型号的设备。这里,0403 是 Orbbec Astra 的产品 ID。
检查设备信息
你可以使用 lsusb -v
获取详细的设备信息。lsusb -v
命令输出非常详细,包含了设备的各种描述符信息。
lsusb -v -s 001:026
其中 -s 001:026
指定总线编号和设备编号,可以替换成实际的编号。
root@h-pc:~/ros_ws# ls /dev/bus/usb
001 002
检查usb 总线是否存在。
让我们重新梳理下我们遇到的问题:
这个问题是我在 容器 中 使用 usb 设备遇到的问题,接上回:https://www.cnblogs.com/odesey/p/18256384
问题:
假设我已经启动了一个容器了,但是我把 usb 设备进行了 插拔,设备编号发生了改变。即:
在容器内: ls /dev/bus/usb/001/026
ls: cannot access '/dev/bus/usb/001/026': No such file or directory
在宿主机:ls -l /dev/bus/usb/001/026
crw-rw-r-- 1 root root 189, 25 6月 20 13:53 /dev/bus/usb/001/026在容器内: ls /dev/bus/usb/001/026
ls:无法访问“/dev/bus/usb/001/026”:没有这样的文件或目录
在宿主机:ls -l /dev/bus/usb/001/026
crw-rw-r-- 1 根根 189, 25 6月 20 13:53 /dev/bus/usb/001/026
容器内只有:
ls /dev/bus/usb/001
001 002 003 004 005 007 008 009 011ls /dev/总线/usb/001
001 002 003 004 005 007 008 009 011
这是怎么回事呢?
解决:
在你将 USB 设备插拔后,宿主机会分配一个新的设备编号,而 Docker 容器启动时仅映射了当时存在的设备编号。这导致了容器内不能访问新的设备编号对应的设备文件。
以下是如何解决这个问题的几种方法:
方法 1: 重新启动容器
最简单的方法是重新启动 Docker 容器,使其重新映射当前所有的 USB 设备:
docker restart <container_name>
方法 2: 使用 --device-cgroup-rule
使用 --device-cgroup-rule 来允许容器访问任何 USB 设备,而不是特定的设备文件:
docker run --device-cgroup-rule='c 189:* rmw' -it --name ros_container ubuntu:18.04 /bin/bash
这将允许容器访问所有的 USB 设备,即使设备编号发生变化。
方法 3: 使用 --privileged 模式
--privileged 模式允许容器访问主机上的所有设备,这也解决了 USB 设备编号变化的问题:
docker run --privileged -it --name ros_container ubuntu:18.04 /bin/bash
方法 4: 使用 --device 映射整个 USB 总线
需要创建容器时候使用,这种方法确保所有设备在插拔后仍然可访问:
docker run --device=/dev/bus/usb -it --name ros_container ubuntu:18.04 /bin/bash
方法 5: 重新映射设备到正在运行的容器
如果不想重启容器,可以使用 docker exec 命令将新的设备映射到正在运行的容器。首先找到设备的路径:
在宿主机上运行以下命令以确定新设备路径:
ls -l /dev/bus/usb/001/
然后,将新设备路径映射到容器内:
docker exec -it ros_container bash -c "mount --bind /dev/bus/usb /dev/bus/usb"
方法 6: 使用 Udev 规则和固定设备路径
确保在 Udev 规则中创建固定的符号链接,以便在设备插拔后路径不变。例如:
- 创建 Udev 规则:
编辑或创建 /etc/udev/rules.d/99-usb-devices.rules 文件,添加以下内容:
# Orbbec Astra camera
SUBSYSTEM=="usb", ATTR{idProduct}=="0403", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_pro"
# IMU device (假设供应商 ID 和产品 ID 为 1234 和 5678)
SUBSYSTEM=="usb", ATTR{idProduct}=="5678", ATTR{idVendor}=="1234", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="imu_device"
-
重新加载 Udev 规则:
sudo udevadm control --reload-rules
sudo udevadm trigger -
验证符号链接:
插入设备后,验证符号链接是否创建成功:
ls -l /dev/astra_pro
ls -l /dev/imu_device
-
启动 Docker 容器时映射这些符号链接:
docker run --device=/dev/astra_pro --device=/dev/imu_device -it --name ros_container ubuntu:18.04 /bin/bash
方法 7: 动态添加设备到容器
通过以下命令动态添加设备到已经运行的容器中:
docker update --device-add /dev/bus/usb/001/026 <container_name>
总结
为了避免每次插拔 USB 设备后重新启动容器或手动重新映射设备,最推荐的方法是使用 --device=/dev/bus/usb 或 --privileged 模式启动容器。这两种方法都可以确保容器能够访问所有 USB 设备,而不受设备编号变化的影响。如果你更注重安全性,可以结合 Udev 规则为设备创建固定的符号链接。
首先,我们不想重新创建一个容器,因此:方法 2、3、4 不再适用。
方法1 值得推荐。
方法5 看起来也不错。
方法6 Udev 不能在 容器内执行。我们需要在宿主机执行该操作。然后在启动容器时候就可以加载进去了。
我们就暂时使用 方法1 吧。
接 https://www.cnblogs.com/odesey/p/18256384
在容器中 执行脚本: ./scripts/create_udev_rules
报错:
udev does not support containers, not started
在宿主机下:
-
那我们把
src/ros_astra_camera/56-orbbec-usb.rules
该文件 copy 到宿主机下:sudo cp 56-orbbec-usb.rules /etc/udev/rules.d/
-
重新加载 Udev 规则:
sudo udevadm control --reload-rules
sudo udevadm trigger -
验证符号链接:
插入设备后,验证符号链接是否创建成功:
ls -l /dev/astra_pro
lrwxrwxrwx 1 root root 15 6月 20 15:41 /dev/astra_pro -> bus/usb/001/029
ls -l /dev/astrauvc
lrwxrwxrwx 1 root root 15 6月 20 15:41 /dev/astrauvc -> bus/usb/001/030
然后我们重启容器:
docker restart <container_name>
在容器内:
/dev/astra_pro 和 /dev/astrauvc
我们依旧找不到
不行我们就做个软链接吧:
ln -s bus/usb/001/029 /dev/astra_pro
ln -s bus/usb/001/030 /dev/astrauvc
后面有问题再说。
我们打包容器为镜像后,再启动容器这些问题都可以解决了。