一、问题背景
在一台运行 ROS 的服务器上,通过 USB 接口连接了多种硬件设备:
- 摇操臂:通过串口转 USB 连接
- 夹爪:通过串口转 USB 连接
- 多台 Realsense 相机:通过 USB 直连
这些设备的驱动和通信脚本都已经配置好,通过固定的 /dev/ttyUSB0、/dev/ttyUSB1 等端口号进行访问。
二、故障现象
在重装显卡驱动后,系统重新分配了 USB 设备的端口号,导致以下问题:
- 原来写死在脚本中的
/dev/ttyUSB0无法接收到摇操臂的信号 - 排查发现:
ttyUSB0现在对应的是夹爪设备 - 摇操臂被分配到了
ttyUSB1 - 所有依赖固定端口号的程序都无法正常运行
三、排查过程
1. 查看 USB 串口设备的软链接信息
bash
ls -l /dev/serial/by-id/*
输出示例:
lrwxrwxrwx 1 root root 13 Mar 12 10:23 /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0MIFSS-if00-port0 -> ../../ttyUSB0
lrwxrwxrwx 1 root root 13 Mar 12 10:23 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_012345-if00-port0 -> ../../ttyUSB1
2. 确认设备对应关系
通过查看软链接指向,发现:
- 摇操臂(FTDI芯片)本该是
ttyUSB0,现在指向了ttyUSB1 - 夹爪(Silicon Labs芯片)本该是
ttyUSB1,现在指向了ttyUSB0
这就解释了为什么脚本无法正常工作------设备顺序被交换了。
四、解决方案
方案一:使用固定的设备 ID(推荐)
最可靠的解决方案是使用 /dev/serial/by-id/ 下的固定软链接,这些链接基于设备的唯一硬件 ID,不会随系统重启或驱动重装而改变。
修改代码示例:
python
# 原代码(不可靠)
serial_port = "/dev/ttyUSB0"
# 新代码(可靠)
serial_port = "/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0MIFSS-if00-port0"
方案二:重新分配端口号(临时解决)
如果需要强制重新分配端口号,可以按以下步骤操作:
-
删除现有的 USB 串口设备文件
bashsudo rm -rf /dev/ttyUSB* -
物理重新插拔设备
- 先断开所有 USB 串口设备的物理连接
- 按照想要的顺序重新插入(先插摇操臂,再插夹爪)
-
验证新分配
bashls -l /dev/ttyUSB*
五、背景知识拓展
1. 不同设备路径的区别
| 路径 | 类型 | 特点 |
|---|---|---|
/dev/ttyUSB* |
动态串口设备 | 按插入顺序动态分配,重启后可能变化 |
/dev/serial/by-id/* |
固定串口软链接 | 基于硬件 ID,永久不变 |
/dev/video* |
视频设备 | 摄像头等视频采集设备 |
/dev/tty* |
终端设备 | 包括串口、虚拟终端等 |
2. 软链接结构解析
以 /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AB0MIFSS-if00-port0 为例:
| 组成部分 | 含义 |
|---|---|
usb |
USB 设备 |
FTDI |
芯片制造商 |
FT232R |
芯片型号 |
AB0MIFSS |
设备唯一序列号 |
if00 |
接口号(多接口设备) |
port0 |
端口号 |
3. 为什么会出现端口错乱?
Linux 内核在检测到 USB 设备时,会按照以下顺序分配 ttyUSB 编号:
- 检测顺序:系统检测到 USB 设备的顺序
- 驱动加载:不同设备的驱动加载速度
- 内核处理:内核处理 USB 事件的时序
显卡驱动重装后,USB 总线会被重新扫描,设备的检测顺序可能发生变化,导致端口重新分配。
4. 最佳实践建议
-
永远不要写死
ttyUSB*端口号------这些是动态分配的 -
使用
/dev/serial/by-id/下的固定链接------基于硬件 ID,稳定可靠 -
程序中添加设备检测逻辑:
pythonimport glob import serial def find_device(vendor_id=None, product_id=None): """通过 VID/PID 查找设备""" for port in glob.glob('/dev/serial/by-id/*'): if 'FTDI' in port: # 或其他特征 return port return None -
使用 udev 规则:可以创建自定义的 udev 规则,给特定设备固定别名
六、总结
本次故障的根本原因是系统重装显卡驱动后重新扫描 USB 总线,导致串口设备的 ttyUSB 编号发生变化。通过使用 /dev/serial/by-id/ 下的固定软链接,可以有效避免此类问题,提高系统的可靠性和可维护性。
对于生产环境中的硬件集成,建议始终使用基于硬件 ID 的设备路径,而不是依赖动态分配的端口号。