在树莓派上连接思岚雷达并运行 slam 算法
硬件环境
RaspBerry PI 4B
rplidar A2 M12
软件环境
Ubuntu Server 20.04 LTS (respberry)
Ubuntu Server 20.04 LTS (工程机)
ros galactic
1. 连接雷达
1.1 连接
思岚雷达标配一个雷达、一个转接器、一根电源线、一根数据线,首先将这四部分全部连接好,电源线的USB type-A
端需要接5V
电源,这个需要自己准备,数据线的USB type-A
端直接连接树莓派。
如此连接后,在树莓派执行命令:
1 | ls /dev/ttyUSB* |
如果你的树莓派USB
接口只连接了雷达的话,应该会出现ttyUSB0
。
1.2 使用 udev 将雷达绑定为静态设备
注:这一部分不是必须的。
首先使用lsusb
命令,获取雷达的pid
和vid
:
pi@raspberrypi:~/project/slam$ lsusb
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 1058:25a1 Western Digital Technologies, Inc. Elements / My Passport (WD20NMVW)
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
获取到如上信息,那么vid
为10c4
,pid
为ea60
,如果不知道你的雷达是上面的哪一个设备,可以插拔USB
,对比插拔前后的输出,判断出是哪一个设备。
编辑udev
的rules
:
1 | vim /etc/udev/rule.d/smartcar.rules |
此处,smartcar
处的文件名可以随意取名,但是其后缀必须是.rules
。
输入内容:
1 | KERNEL=="ttyUSB?", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="0777" SYMLINK+="rplidar" |
idVendor
处填写vid
,idProduct
处填写pid
。
rplidar
处的名称也可以随意取名。
保存,执行:
1 | sudo service udev restart |
重新插拔设备,如果准确无误,执行ll /dev/rplidar
将看到:
1 | /dev/rplidar -> ttyUSB0 |
2. 安装 ros galactic
此处和《Windows 11 WSL2 安装并运行 ROS》一文的2.2
节相同,不再赘述。
3. 安装 gmapping
进入你的ROS2
工程的src
目录,执行:
1 | git clone https://github.com/Project-MANAS/slam_gmapping |
4. 运行雷达
4.1 运行雷达节点
雷达节点的代码段为:
1 | lidar_node = Node( |
serial_port
即/dev
中的设备,此处若未设置udev rules
,应写为ttyUSB0
(以实际情况为准);serial_baudrate
即波特率,A1
、A2
一般为115200
,A3
为256000
,但A2 M7
与A2 M12
比较特殊,是256000
,此处需要准确,如果不知道自己的设备波特率是多少,建议去思岚官网查询,需要注意的是,不仅此处的值要与设备对应,设备连接的转接盒上也有一个可以拨动的开关,也要与这个值相同。
运行节点:
1 | # Launch |
需要注意的是,此处还应设置两个tf
:
1 | tf1 = Node( |
否则会因找不到参考坐标系,报出如下错误:
[slam_gmapping-2] [INFO] [1660630271.191481990] [slam_gmapping]: Message Filter dropping message: frame 'laser' at time 1660630270.044 for reason 'discarding message because the queue is full'
Warning: Invalid frame ID "base_link" passed to canTransform argument target_frame - frame does not exist
4.2 雷达数据(LaserScan)解析
1 | sensor_msgs.msg.LaserScan(header=std_msgs.msg.Header(stamp=builtin_interfaces.msg.Time(sec=1667629747, nanosec=468238273), frame_id='laser'), |
雷达完成一次完整的测量其数据如上,其数据可以通过ros2 topic echo /scan
查看,但若是想查看完整的数据,还是要自己写一个订阅者,并把数据打印出来。
雷达节点会源源不断地在/scan
话题中发布如上数据,此处将一次完整的测量数据称为一帧数据,帧与帧之间可以依靠其话题头部区分:
1 | # 帧头 |
头部中包含以下信息:sec
表示本帧开始时间的秒部分,nanosec
表示本帧开始时间的纳秒部分,frame_id
表示数据的参考系。
对于话题头部之后的测量数据,
其中angle_min
、angle_max
应该很好理解,可以看到这两个值,一个接近-π
,一个接近+π
,实际上就是雷达从-π
开始扫描,一直扫描到+π
处,即为2π
,完成完整的一圈测量;angle_increment
即每两个相邻的扫描间隔的角度,这里用2π / angle_increment
,会发现这个值相当接近于 1800,而ranges
和intensities
这里我虽然用省略号省略了数据,其实这两个数组的长度也正好是 1800,显然,ranges
和intensities
每一个元素对应的值就是每次扫描采集的数据,而完整的一圈测量一共扫描了 1800 次;time_increment
也无需多言,即每两个相邻的扫描之间间隔的时间;scan_time
是完成一圈完整的测量所话费的总时长,那么显然scan_time / time_increment
的值也接近 1800;range_min
和range_max
是每次扫描的有效区间,低于或超出这个区间的值都应舍去;ranges
是扫描到雷达的距离,因为range_min
和range_max
的存在,ranges
数组中常会看到某个元素是inf
,即infinity
,无穷小或无穷大,其实就是无效数据;intensities
是每次扫描反射的激光束强度,和设备、激光扫描到的物体材质有关,但这个数据我们一半不太用到,因为建图不需要。
4.3 绘制雷达扫描数据
那么如何绘制雷达扫描出来的数据呢?
雷达以自身为圆心旋转,每隔相同的角度,不断采集物体到自身的距离。那么很显然,要想绘制出雷达感知到的环境,很显然是利用极坐标:其极角是第n
次旋转的角度n * angle_increment
,即极径即ranges[n-1]
,那么这样根据一次完整的测量数据,很容易绘制出下图:
这里放出绘图代码:
1 | # 一次扫描的数据 |
5. 运行 gmapping
gmapping
的代码段为:
1 | gmapping = Node( |
与4.1
节相同,应在LaunchDescription
的数组参数中添加gmapping
节点。
6. Rviz查看雷达数据和gmapping建图
在工程机运行rviz2:
1 | rviz2 |
在rviz2
中添加topic
,点击rviz2
软件界面左下角的“add”按钮,在窗口中选择“By topic”,局域网中设备发布的ros话题将会全部在此处显示出来。
选择LaserScan
和Map
,即订阅 /scan
和 /map
相关话题,如下图:
添加话题后,将Rviz2
的左侧边栏的Fixed Frame
选择为map
,其意为将map
帧作为固定帧,其余帧相对map
帧运动。据此可知,若选择base_link
为固定帧,即小车的帧固定,其余帧相对运动。此处可类比地图软件的“地图固定”和“跟随视角”方便理解。
Rviz2
展示数据如下图:
gmapping
节点可以正常运行后,能够发现此处存在地图漂移的问题:
因为odom
里程计话题没有正确计算和发布,地图扫描算法需要依据odom
坐标及话题建图,本文下节将进行odom
的计算和话题发布。
7. odom 计算及话题发布
待编辑
8. 可能出现的问题
待编辑