RobotKernal-UESTC/Docs/Linux开发板遇到问题和解决方案/Ubuutu下USB串口冲突问题.md

11 KiB
Raw Blame History

Ubuutu下USB串口冲突问题

一、为何要设置USB设备别名

随着我们开发的机器人具备的功能越来越复杂那么需要外接的USB设备会逐渐多起来例如外接两个arduino板外接两个USB摄像头外接一个激光雷达外接一个USB接口的IMU模块等等那么如此多的USB设备在linux中的挂载点就会多起来而且慢慢的也会变得较为混乱导致我们无法分清楚哪一个设备挂载点对应哪一个设备而且即使我们在本次开机中分清楚了那么在下次linux系统开机后USB设备的挂载点也会随着系统挂载设备顺序的不同而导致设备挂载点发生变化。如下图所示当外接了很多USB设备时会导致无法分清楚挂载点与哪一个设备对应

Screenshot from 2018-01-08 11:58:27.png

从上图无法得知设备挂载点/dev/ttyACM0和/dev/ttyACM1哪一个挂载点对应哪一个arduino设备ttyUSB0和ttyUSB1对应哪一个设备也不确定因此我们就需要一种方法来保证每次开机后唯一的设备挂载点对应一个确定的设备这样我们的程序就可以正确操控相应的USB设备了。


二、什么是udev?

udev 是 Linux 内核的设备管理器。总的来说,它取代了 devfs 和 hotplug负责管理 /dev 中的设备节点。同时udev 也处理所有用户空间发生的硬件添加、删除事件以及某些特定设备所需的固件加载。与传统的顺序加载相比udev 通过并行加载内核模块提供了潜在的性能优势。异步加载模块的方式也有一个天生的缺点:无法保证每次加载模块的顺序,如果机器具有多个块设备,那么它们的设备节点可能随机变化。

udev 规则以管理员身份编写并保存在 /etc/udev/rules.d/ 目录,其文件名必须以 .rules 结尾,各种软件包提供的规则文件位于 /lib/udev/rules.d/。如果 /usr/lib 和 /etc 这两个目录中有同名文件,则 /etc 中的文件优先关于udev更为详细的介绍大家可以参考以下链接udev介绍


三、编写udev规则

在简单了解了什么是udev后我们就可以来编写属于自己USB设备的udev规则了这样我们就可以为自己的USB设备挂载点来重命名由于完整的介绍编写udev规则太过于复杂我们这里来通过大家经常用到的rplidar_ros里面的rplidar的udev规则来做举例说明如果已经下载过rplidar_ros代码的就不用重新下载如果没有下载的可以通过以下命令来下载

git clone https://github.com/robopeak/rplidar_ros.git

当下载完该代码后我们查看目录结构可以发现一个scripts的目录里面就放着rplidar相关的udev规则具体操作如下图所示

Screenshot from 2018-01-08 13:33:14.png

我们先来看rplidar.rules的内容然后来分析其编写规则

Screenshot from 2018-01-08 13:44:43.png

在规则文件里,除了以“#”开头的行(注释),所有的非空行都被视为一条规则,但是一条规则不能扩展到多行。规则都是由多个 键值对key-value pairs组成并由逗号隔开键值对可以分为 条件匹配键值对( 以下简称“匹配键 ”) 和 赋值键值对( 以下简称“赋值键 ”),一条规则可以有多条匹配键和多条赋值键。匹配键是匹配一个设备属性的所有条件,当一个设备的属性匹配了该规则里所有的匹配键,就认为这条规则生效,然后按照赋值键的内容,执行该规则的赋值。

在rplidar.rules中的KERNELATTRS{idVendor}和ATTRS{idProduct}是匹配键MODE和SYMLINK是赋值键这条规则的意思是如果有一个设备的内核设备名称是ttyUSB*(*代表任意数字)VID是10c4PID是ea60的话那么就在/dev目录下增加一个符号链接设备并命名为rplidar同时为其赋予0777的权限。

通过这条简单的规则,大家应该对 udev 规则有直观的了解。但可能会产生疑惑,为什么 KERNELATTRS{idVendor}和ATTRS{idProduct} 是匹配键,而 MODE和SYMLINK是赋值键呢这由中间的操作符 (operator) 决定。

仅当操作符是“==”或者“!=”时,其为匹配键;若为其他操作符时,都是赋值键,下面是 udev 规则的所有操作符:

“==”:比较键、值,若等于,则该条件满足;

“!=”: 比较键、值,若不等于,则该条件满足;

“=”: 对一个键赋值;

“+=”:为一个表示多个条目的键赋值。

“:=”:对一个键赋值,并拒绝之后所有对该键的改动。目的是防止后面的规则文件对该键赋值。


四、使udev规则生效

当我们编写好属于自己的udev规则后接下来就需要将其复制到指定位置然后使其生效了这里我们可以参考这两个脚本如何编写的首先看create_udev_rules.sh

#!/bin/bash
echo "remap the device serial port(ttyUSBX) to  rplidar"
echo "rplidar usb connection as /dev/rplidar , check it using the command : ls -l /dev|grep ttyUSB"
echo "start copy rplidar.rules to  /etc/udev/rules.d/"
echo "`rospack find rplidar_ros`/scripts/rplidar.rules"
sudo cp `rospack find rplidar_ros`/scripts/rplidar.rules  /etc/udev/rules.d
echo " "
echo "Restarting udev"
echo ""
sudo service udev reload
sudo service udev restart
echo "finish "

Screenshot from 2018-01-08 14:24:40.png

接下来看删除规则的脚本内容主要就是将复制到rules.d目录下的rplidar.rules规则文件删除然后重启udev服务

#!/bin/bash
echo "delete remap the device serial port(ttyUSBX) to  rplidar"
echo "sudo rm   /etc/udev/rules.d/rplidar.rules"
sudo rm   /etc/udev/rules.d/rplidar.rules
echo " "
echo "Restarting udev"
echo ""
sudo service udev reload
sudo service udev restart
echo "finish  delete"

然后我们先来执行create_udev_rules.sh脚本使规则生效由于该脚本默认情况下需要将rplidar_ros代码放在ROS的工作空间源码目录下使用catkin_make编译后然后source devel/setup.bash然后才能执行该脚本我在这里将脚本简单修改这样就没必要在ROS工作空间的源码目录下执行了我修改后的脚本如下

#!/bin/bash
echo "remap the device serial port(ttyUSBX) to  rplidar"
echo "rplidar usb connection as /dev/rplidar , check it using the command : ls -l /dev|grep ttyUSB"
echo "start copy rplidar.rules to  /etc/udev/rules.d/"
sudo cp ./rplidar.rules  /etc/udev/rules.d
echo " "
echo "Restarting udev"
echo ""
sudo service udev reload
sudo service udev restart
echo "finish "

Screenshot from 2018-01-08 14:43:11.png

当我们不想再使用rplidar的udev规则时就可以使用delete_udev_rules.sh脚本将规则文件删除这样就不会再有相应的rplidar映射了

Screenshot from 2018-01-08 14:49:45.png

如果不想在每次创建udev规则和删除规则时重新的插拔USB设备那么可以将脚本文件做如下修改就可以了首先来看create_udev_rules.sh如何修改

#!/bin/bash
echo "remap the device serial port(ttyUSBX) to  rplidar"
echo "rplidar usb connection as /dev/rplidar , check it using the command : ls -l /dev|grep ttyUSB"
echo "start copy rplidar.rules to  /etc/udev/rules.d/"
sudo cp ./rplidar.rules  /etc/udev/rules.d
echo " "
echo "Restarting udev"
echo ""
sudo udevadm control --reload-rules
sudo service udev restart
sudo udevadm trigger
echo "finish "

Screenshot from 2018-01-08 15:27:50.png

同理可得修改后的delete_udev_rules.sh脚本内容如下

#!/bin/bash
echo "delete remap the device serial port(ttyUSBX) to  rplidar"
echo "sudo rm   /etc/udev/rules.d/rplidar.rules"
sudo rm   /etc/udev/rules.d/rplidar.rules
echo " "
echo "Restarting udev"
echo ""
sudo udevadm control --reload-rules
sudo service udev restart
sudo udevadm trigger
echo "finish  delete"

Screenshot from 2018-01-08 15:34:50.png


五、当PID/VID相同时如何编写udev规则

当主控板上外接的USB设备越來越多时难免遇到两个或多个USB设备的VIDPID相同这样的话再通过上述那个简单的rules文件就不行了我们就需要增加新的匹配键来做区分不同的USB设备

Screenshot from 2018-01-08 15:48:10.png

此时我们就需要在rplidar.rules文件中新增KERNELS匹配键这样才能与IMU模块的usb转接板区分开获取KERNELS的方式如下我们先把其他USB设备拔掉只留下RPlidar A2设备然后执行以下命令

udevadm info --attribute-walk --name=/dev/ttyUSB1 | grep KERNELS

Screenshot from 2018-01-08 16:33:14.png

然后就可以根据该KERNELS属性来修改rplidar.rules内容了这样我们就可以使用该udev规则文件来创建映射了新rplidar.rules内容如下

# set the udev rule , make the device_port be fixed by rplidar
#
KERNELS=="1-2.1", KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", SYMLINK+="rplidar"

同理可得我们可以创建IMU模块的imu.rules文件内容如下

# set the udev rule , make the device_port be fixed by rplidar
#
KERNELS=="1-2.4", KERNEL=="ttyUSB*", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE:="0777", SYMLINK+="imu"

同时我们还可以创建和imu模块规则文件配套的脚本文件具体操作如下图所示

Screenshot from 2018-01-08 16:44:12.png

那执行两个生效的脚本来查看效果:

Screenshot from 2018-01-08 17:22:43.png


六、 注意事项

使用udev规则来创建设备挂载点新的映射时需要注意不能随意更换USB设备插的USB口了因为每次更换USB口那么相应的KERNELS就换了切记我们只要不更换USB设备插的USB口那么无论开机时设备挂载顺序如何即使rplidar a2的挂载点这次开机是ttyUSB0下次开机变成ttyUSB1也不要紧因为/dev/rplidar总能正确的创建相应的映射到rplidar的挂载点上。

在执行上面的各种命令时需要在ubuntu的终端里执行不可以在windows下使用类似xShell的终端模拟软件来运行。

备忘:

ACTION=="add",KERNELS=="1-1.1:1.3",SUBSYSTEMS=="usb",MODE:="0777",SYMLINK+="USB-4G"