大家在学习ROS中不可避免需要使用激光雷达,高精地图、实时定位以及障碍物检测等多项技术,而这些技术都离不开光学雷达的支持,但是呢雷达这真是太贵了,大部分人是负担不起(实验室、研究所土豪可以略过),但是还是机智的大牛发明了其他手段:使用深度摄像头仿激光数据1kinect仿激光数据2, 用来测试足够了,果真人民都是机智的,后来呢人们发现扫地机器人上也有的用的激光雷达,为什么不利用起来呢,然后国外大牛就发现了Neato XV-11这款,在youtube成功将其连接到ROS上,实现了机器人的全自动导航,于是就我就作为一个搬运工把他搬过来了。。。
PS:激光雷达知识非常详细。

一、雷达介绍:







如果使用Arduino读取数据,则将激光雷达的RX和与Arudino TX、TX和Arudino RX相接,上电即可读取数据,

二、数据格式

串口通讯: 速率 115200 8N1

雷达每完整旋转一周会发送90个数据包

每个数据包中包含4个测量点的信息

每个数据包长度固定是22个字节

这样总共360°旋转一周共 1980个字节,相当于每度会有一个距离数据.

其中雷达的数据包格式如下

<start> <index> <speed_l> <speed_h> [Data 0]
[Data 1] [Data 2] [Data 3] <checksum_l> <checksum_h>

<start>:0xFA,是固定格式表明数据包开始,可用来从数据流中分割数据包。

<index>:数据包的索引号,范围从0xA0 到 0xF9 (总共89个包,每个包4个数据)。

<speed>:有两个speedL和speedH ,它们各一个字节,共同组成转速信息,大概是低6bit表示小数部分。(实际这里我不知道怎么翻译了,直接抄的雷达大叔的。)

<data>:[ Data 0] 到 [Data 3] 是四组测量数据,其中每组测量数据分别由4个字节组成,如下:

byte 0 : <distance 7:0="">  # 距离信息的0-7位

byte 1 : <“invalid data” flag> <“strength warning” flag>
<distance 13:8=""> # 错误信息标志位 , 警告位, 距离信息13-8位byte 2 : <signal
strength="" 7:0="">  # 信号强度 0-7位

byte 3 : <signal strength="" 15:8=""> # 讯号强度 8-15位

距离信息的单位是mm ,整个激光雷达的测量范围大概是15cm 到6m, 只需要把距离信息的两个字节组装成一个整数即可(注意判断无效数据位为0,如果为1意味是无效的数据,需丢弃)。

<checksum>:由两个字节组成的校验码,用来校验整个数据包是否正确,代码如下:

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

三、ROS中测试使用XV-11

环境:ubuntu14.04+indigo(完整版)

准备:雷达+转接板+调速板

第一步建立工作空间

mkdir -p ~/catkin_ws/src

cd ~/catkin_ws/src

catkin_init_workspace

第二步编译雷达驱动

cd ~/catkin_ws/src

git clone  

cd ~/catkin_ws

catkin_make这个编译一般没什么问题,编译完成后,USB插上连接电脑即可,可以在dev下查看一下端口号

ls /dev/

一般都是ttyUSB0

第三步雷达测试

打开一个终端

source ~/catkin_ws/devel/setup.bash

roscore

打开一个终端(注意使用版本2,具体原因是雷达几个版本的数据格式不一样,目前用的是2)

source ~/catkin_ws/devel/setup.bash

rosrun xv_11_laser_driver neato_laser_publisher _port:=/dev/ttyUSB0 _firmware_version:=2

打开一个终端查看雷达的信息

rosrun rviz rviz

修改Global Options的Fixed Frame为/neato_laser(手打)

点击Add,添加LaserScan

然后点云就可以出来了, 如果没出数据点云或者出现随机的红色点可能是雷达速度太快,可以调节调速板

速度跳低,则可看到数据。(图片太小不清楚可右键新窗口打开)



四、ROS中使用XV11进行hector_slam

ROS中常用的2d slam算法主要有gmapping和hector_slam,以下是这两种方法的介绍,大家觉得长可以直接卡维纳大白话版翻译。

hector_slam

需要高更新频率小测量噪声的激光扫描仪,不需要里程计,使空中无人机与地面小车在不平坦区域运行存在运
用的可能性,利用已经获得的地图对激光束点阵进行优化,估计激光点在地图的表示,和占据网格的概率,其中扫描匹配利用的是高斯牛顿的方法进行求解.
找到激光点集映射到已有地图的刚体转换(x,y,theta). ( 接触的匹配的方法还有最近邻匹配的方法(ICP)
,gmapping代码中的scanmatcher部分有两种方法选择.  
)为避免局部最小而非全局最优的(类似于多峰值模型的,局部梯度最小了,但非全局最优)出现,地图采用多分辨率的形式。导航中的状态估计可以加入惯性测
量,进行EKF滤波.

【大白话翻译:hector_slam是个性能非常好的算法,通过最小二乘法匹配扫描点,且依赖高精度的激光雷达数据,但是不需要里程计。因此扫描角很小且噪声较大的kinect是不行的,匹配的时候会陷入局部点,地图非常混乱。】

gmapping

目前激光2Dslam用得最广的方法,gmapping采用的是RBPF的方法。必须得了解粒子滤波
(利用统计特性描述物理表达式下的结果)的方法,粒子滤波的方法一般需要大量的粒子来获取好的结果,但这必会引入计算的复杂度;粒子是一个依据过程的观测
逐渐更新权重与收敛的过程,这种重采样的过程必然会代入粒子耗散问题(depletion
problem),大权重粒子显著,小权重粒子会消失(有可能正确的粒子模拟可能在中间的阶段表现权重小而消失)。自适应重采样技术引入减少了粒子耗散问
题 ,
计算粒子分布的时候不单单仅依靠机器人的运动(里程计),同时将当前观测考虑进去,减少了机器人位置在粒子滤波步骤中的不确定性,(FAST-
SLAM2.0 的思想,可以适当减少粒子数)。

【大白话翻译:gmapping
是一个比较早的算法,核心思想是粒子滤波并且需要里程计,但并不要求很高性能的传感器,初学者一般都是先来玩这个,不过请注意一点,gmapping只是
mapping,定位还需要amcl这个蒙特卡洛算法包配合使用,最后才能接入navigation stack。】

gmapping需要订阅两个话题。



tf是ros中必用的部分,说简单一些,tf类中定义了两个刚体之间的旋转与平移矩阵,并且重载了乘法运算符,这样我们就可以通过相乘两个tf来沿着tf树的方向求末段执行器相对世界坐标的位置与方向。

scan是激光雷达数据,如果你有雷达可以直接使用,如果没有雷达那么可以用depthimage_to_laserscan包,这个包可以将kinect发布出来的深度图转换成激光雷达扫描数据。

gmapping需要的里程计odom便是通过我们自己发布tf树的形式告诉gmapping,而我们该如何获得这个里程计,这就需要我们自己完成这一部分了,通常做法是在移动平台上安装电机编码器与电子罗盘,在移动平台上的嵌入式单片机(此处与ros无关,我这里用的树莓派)内完成里程计的制作与PID调试,然后,再用树莓派连接单片机串口,单片机将里程计与航向角发给树莓派,树莓派上需要自己写一个node(关于里程计,高级方法是视觉里程计一类的,不过与其折腾那些不如直接上激光雷达),大致就这个流程。

接下来要说怎么在ros中使用XV-11进行hector_slam。

第一步:首先按照第三步下载XV-11驱动并测试成功

第二步:hector_slam

1、首先下载hector_slam包到你工作空间的src下

命令:

cd ~/catkin/src

git clone https://github.com/tu-darmstadt-ros-pkg/hector_slam.git

cd ..

catkin_make



2、然后在添加neato.launch文件

路径:~/catkin_ws/src/hector_slam/hector_slam_launch/launch/



在此目录下新建neato.launch文件并添加如下代码,

<?xml version="1.0"?>

<launch>

  <!--<node pkg="xv_11_laser_driver" type="neato_laser_publisher" name="xv_11_node">

    <param name="port" value="/dev/ttyUSB0" />

    <param name="firmware_version" value="2" />

    <param name="frame_id" value="laser" />

  </node>-->  

  <node pkg="tf" type="static_transform_publisher" name="map_to_odom" args="0.0 0.0 0.0 0 0 0.0 /odom /base_link 10" />

  <node pkg="tf" type="static_transform_publisher" name="base_frame_laser" args="0 0 0 0 0 0 /base_link /laser 10" />

  <!--<node pkg="rviz" type="rviz" name="rviz"

    args="-d $(find hector_slam_launch)/rviz_cfg/mapping_demo.rviz"/>-->

  <include file="$(find hector_mapping)/launch/mapping_default.launch" />

  <node pkg="rviz" type="rviz" name="rviz" args="-d rviz_cfg.rviz" />

  <include file="$(find hector_geotiff)/launch/geotiff_mapper.launch" />

</launch>



3、然后在添加新的mapping_default.launch文件(记得备份原mapping_default.launch文件)

路径:~/catkin_ws/src/hector_slam/hector_mapping/launch



其中的mapping_default_old.launch为备份文件。

新mapping_default.launch代码如下

<?xml version="1.0"?>

<launch>

  <arg name="tf_map_scanmatch_transform_frame_name" default="/scanmatcher_frame" />

  <arg name="base_frame" default="base_link" />

  <arg name="odom_frame" default="base_link" />

  <arg name="pub_map_odom_transform" default="true" />

  <arg name="scan_subscriber_queue_size" default="5" />

  <arg name="scan_topic" default="scan" />

  <arg name="map_size" default="2048" />

  <node pkg="hector_mapping" type="hector_mapping" name="hector_mapping" output="screen">

    <!-- Frame names -->

    <param name="map_frame" value="map" />

    <param name="base_frame" value="$(arg base_frame)" />

    <param name="odom_frame" value="$(arg base_frame)" />

    <!-- Tf use -->

    <param name="use_tf_scan_transformation" value="true" />

    <param name="use_tf_pose_start_estimate" value="false" />

    <param name="pub_map_odom_transform" value="$(arg pub_map_odom_transform)" />

    <!-- Map size / start point -->

    <param name="map_resolution" value="0.050" />

    <param name="map_size" value="$(arg map_size)" />

    <param name="map_start_x" value="0.5" />

    <param name="map_start_y" value="0.5" />

    <param name="map_multi_res_levels" value="2" />    

    <!-- Map update parameters -->

    <param name="update_factor_free" value="0.4" />

    <param name="update_factor_occupied" value="0.7" />    

    <param name="map_update_distance_thresh" value="0.2" />

    <param name="map_update_angle_thresh" value="0.9" />

    <param name="laser_z_min_value" value = "-1.0" />

    <param name="laser_z_max_value" value = "1.0" />    

    <!-- Advertising config -->

    <param name="advertise_map_service" value="true" />

    <param name="scan_subscriber_queue_size" value="$(arg scan_subscriber_queue_size)" />

    <param name="scan_topic" value="$(arg scan_topic)" />

    <!-- Debug parameters -->

    <!--

      <param name="output_timing" value="false" />

      <param name="pub_drawings" value="true" />

      <param name="pub_debug_output" value="true" />

    -->

    <param name="tf_map_scanmatch_transform_frame_name" value="$(arg tf_map_scanmatch_transform_frame_name)" />

  </node>

  <!--<node pkg="tf" type="static_transform_publisher"
name="map_nav_broadcaster" args="0 0 0 0 0 0 map nav 100" />-->

</launch>



4、最后修改xv11驱动文件的neato_laser_publisher.cpp文件

路径:~/catkin_ws/src/xv_11_laser_driver/src

修改neato_laser_publisher.cpp文件

找到

priv_nh.param("frame_id", frame_id, std::string("neato_laser"));

修改为

priv_nh.param(“frame_id“, frame_id, std::string(“laser“));



5、最后编译文件

cd ~/catkin

catkin_make

6、运行测试

roscore

rosrun xv_11_laser_driver neato_laser_publisher _port:=/dev/ttyUSB0 _firmware_version:=2

roslaunch hector_slam_launch neato.launch

 



def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

def checksum(data):    """Compute and return the checksum as an int."""

    # group the data by word, little-endian

    data_list = []

    for t in range(10):

        data_list.append( data[2*t] + (data[2*t+1]<<8) )     # compute the checksum on 32 bits

    chk32 = 0

    for d in data_list:

        chk32 = (chk32 << 1) + d

     # return a value wrapped around on 15bits, and truncated to still fit into 15 bits

    checksum = (chk32 & 0x7FFF) + ( chk32 >> 15 ) # wrap around to fit into 15 bits

    checksum = checksum & 0x7FFF # truncate to 15 bits

    return int( checksum )

喜欢
0

分享:

 
要评论请先登录 或者 注册

 

本站精华
  • 运动规划 (Motion Planning): MoveIt! 与 OMPL

  • 当维基遇上易科

  • 机智机器人团队招募

  • 几个移动机器人lab资料 相关链接,实验室、原理、基础知识

使用XV-11激光雷达做hector_slam的更多相关文章

  1. AtCoder Grand Contest 11~17 做题小记

    原文链接https://www.cnblogs.com/zhouzhendong/p/AtCoder-Grand-Contest-from-11-to-20.html UPD(2018-11-16): ...

  2. HDU 2016.11.12 做题感想

    细数一下这两天做过的值得总结的一些题Orz...... HDU 2571 简单dp,但是一开始WA了一发.原因很简单:没有考虑仔细. 如果指向该点的所有点权值都为负数,那就错了(我一开始默认初始值为0 ...

  3. 2019.11.21 做OJ题的反思

    1.利用二分法查找数组元素(适用于有序数组) #include<stdio.h> int BinarySearch(int a[],int n,int key); ]; int main( ...

  4. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  5. 欠了好久的CRM帖子,双11来读。

    又一年双11了,觉得天猫双11越来越没特色了. 从折扣,音符旋律到红包,今年15年却找不出往年的热度,只是商家还是一样的急,备着活动目标计划,做着库存价格打标视觉设计这种苦逼的日子. 欠了好久的CRM ...

  6. 用K-Means聚类分析做客户分群

    聚类指的是把集合,分组成多个类,每个类中的对象都是彼此相似的.K-means是聚类中最常用的方法之一,它是基于点与点距离的相似度来计算最佳类别归属. 在使用该方法前,要注意(1)对数据异常值的处理:( ...

  7. ELK7.11.2版本安装部署及ElastAlert告警相关配置

    文档开篇,我还是要说一遍,虽然我在文档内容中也会说好多遍,但是希望大家不要嫌我墨迹: 请多看官方文档,请多看命令行报错信息,请多看日志信息,很多时候它们比百度.比必应.比谷歌有用: 请不要嫌麻烦,打开 ...

  8. C# 11 对 ref 和 struct 的改进

    前言 C# 11 中即将到来一个可以让重视性能的开发者狂喜的重量级特性,这个特性主要是围绕着一个重要底层性能设施 ref 和 struct 的一系列改进. 但是这部分的改进涉及的内容较多,不一定能在 ...

  9. 【转】70个经典的 Shell 脚本面试问题

    我们为你的面试准备选择了 70 个你可能遇到的 shell 脚面问题及解答.了解脚本或至少知道基础知识对系统管理员来说至关重要,它也有助于你在工作环境中自动完成很多任务.在过去的几年里,我们注意到所有 ...

随机推荐

  1. hibernate的一对多配置

    首先是“一”的 Customer.java package com.xiaostudy.domain; import java.util.HashSet; import java.util.Set; ...

  2. chrome关闭后还在进程中运行

    1.网上搜到信息: 设置 “即使关闭浏览器也后台运行” 取消打勾 2.然后我找了一下,应该是这个选项:“关闭 Google Chrome 后继续运行后台应用” 3. 4. 5.

  3. NFV及vIMS的部署实施

    随着5G和物联网等领域的快速发展,移动数据业务飞速增长,而传统电信网络基于专用硬件的架构和封闭式的网元,已经成为运营商拓展新业务的严重障碍.NFV能够根据用户和业务需求灵活动态地进行网络资源配置,实现 ...

  4. Oracle数据库常用监控语句

    --在某个用户下找所有的索引 select user_indexes.table_name, user_indexes.index_name,uniqueness, column_name from ...

  5. BZOJ 1492 [NOI2007]货币兑换Cash:斜率优化dp + cdq分治

    传送门 题意 初始时你有 $ s $ 元,接下来有 $ n $ 天. 在第 $ i $ 天,A券的价值为 $ A[i] $ ,B券的价值为 $ B[i] $ . 在第 $ i $ 天,你可以进行两种操 ...

  6. 图片与路径(Path)的应用

    图片的应用:软盘样式的保存按钮,笔记本样式的编辑按钮:只能用图片 路径(Path)的应用:异形轮廓(各种气泡框,普通控件无法描述):异形线条(普通控件无法描述):图片(不建议,因为展现效果不好,比如: ...

  7. Confluence 6 配置系统属性

    在这个页面中描述 Confluence 启动时如何设置 Java 属性和其他选项. 请查看 How to fix out of memory errors by increasing availabl ...

  8. 在web.xml中配置spring配置文件的路径

    <context-param>     <param-name>contextConfigLocation</param-name>     <param-v ...

  9. Django的 CBV和FBV

    FBV CBV 回顾多重继承和Mixin 回到顶部 FBV FBV(function base views) 就是在视图里使用函数处理请求. 在之前django的学习中,我们一直使用的是这种方式,所以 ...

  10. Qt中切换窗口功能的实现

    两条语句就能够实现了: this->newNC.setWindowFlags(Qt::WindowStaysOnTopHint); this->newNC.show(); mark一下,防 ...