初始化文档

main
杨明 2023-12-05 18:42:49 +08:00
commit 1857caf68c
233 changed files with 4597 additions and 0 deletions

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/autolabor-m1-doc.iml" filepath="$PROJECT_DIR$/.idea/autolabor-m1-doc.iml" />
</modules>
</component>
</project>

6
Writerside/c.list Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE categories
SYSTEM "https://resources.jetbrains.com/writerside/1.0/categories.dtd">
<categories>
<category id="wrs" name="Writerside documentation" order="1"/>
</categories>

10
Writerside/hi.tree Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE instance-profile
SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd">
<instance-profile id="hi"
name="Help Instance"
start-page="Default-topic.md">
<toc-element topic="Default-topic.md"/>
</instance-profile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

View File

@ -0,0 +1,79 @@
# This is the first topic
<!--Writerside adds this topic when you create a new documentation project.
You can use it as a sandbox to play with Writerside features, and remove it from the TOC when you don't need it anymore.
If you want to re-add it for your experiments, click + to create a new topic, choose Topic from Template, and select the
"Starter" template.-->
## Add new topics
You can create empty topics, or choose a template for different types of content that contains some boilerplate structure to help you get started:
![Create new topic options](new_topic_options.png){ border-effect="line" thumbnail="true" width="321"}
## Write content
%product% supports two types of markup: Markdown and XML.
When you create a new help article, you can choose between two topic types, but this doesn't mean you have to stick to a single format.
You can author content in Markdown and extend it with semantic attributes or inject entire XML elements.
For example, this is how you inject a procedure:
<procedure title="Inject a procedure" id="inject-a-procedure">
<step>
<p>Start typing <code>procedure</code> and select a procedure type from the completion suggestions:</p>
<img src="completion_procedure.png" alt="completion suggestions for procedure" border-effect="line"/>
</step>
<step>
<p>Press <shortcut>Tab</shortcut> or <shortcut>Enter</shortcut> to insert the markup.</p>
</step>
</procedure>
## Add interactive elements
### Tabs
To add switchable content, use tabs (start typing `tabs` on a new line).
<tabs>
<tab title="Markdown">
<code-block lang="plain text">![Alt Text](new_topic_options.png){ width=450 }</code-block>
</tab>
<tab title="Semantic markup">
<code-block lang="xml">
<![CDATA[<img src="new_topic_options.png" alt="Alt text" width="450px"/>]]></code-block>
</tab>
</tabs>
### Collapsible blocks
Besides injecting entire XML elements, you can use attributes to configure the behavior of certain elements.
For example, you can collapse a chapter that contains non-essential information like this:
#### Supplementary info {collapsible="true"}
Content under such header will be collapsed by default, but you can modify the behavior by adding the following attribute:
`default-state="expanded"`
## Convert selection to XML
If you need to extend an element with more functions, you can convert selected content from Markdown to semantic markup.
For example, if you want to merge cells in a table, it's much easier to convert it to XML than do this in Markdown.
Position the caret anywhere in the table and press <shortcut>Alt+Enter</shortcut>:
<img src="convert_table_to_xml.png" alt="Convert table to XML" width="706" border-effect="line"/>
## Feedback and support
Please report any issues, usability improvements, or feature requests to our
<a href="https://youtrack.jetbrains.com/newIssue?project=WRS">YouTrack project</a>
(you will need to register).
You are welcome to join our
<a href="https://jb.gg/WRS_Slack">public Slack workspace</a>.
Before you do, please read our [Code of conduct](https://plugins.jetbrains.com/plugin/20158-writerside/docs/writerside-code-of-conduct.html).
We assume that youve read and acknowledged it before joining.
You can also always email us at [writerside@jetbrains.com](mailto:writerside@jetbrains.com).
<seealso>
<category ref="wrs">
<a href="https://plugins.jetbrains.com/plugin/20158-writerside/docs/markup-reference.html">Markup reference</a>
<a href="https://plugins.jetbrains.com/plugin/20158-writerside/docs/manage-table-of-contents.html">Reorder topics in the TOC</a>
<a href="https://plugins.jetbrains.com/plugin/20158-writerside/docs/local-build.html">Build and publish</a>
<a href="https://plugins.jetbrains.com/plugin/20158-writerside/docs/configure-search.html">Configure Search</a>
</category>
</seealso>

5
Writerside/v.list Normal file
View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE vars SYSTEM "https://resources.jetbrains.com/writerside/1.0/vars.dtd">
<vars>
<var name="product" value="Writerside"/>
</vars>

10
Writerside/writerside.cfg Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ihp SYSTEM "https://resources.jetbrains.com/writerside/1.0/ihp.dtd">
<ihp version="2.0">
<topics dir="topics" web-path="topics"/>
<images dir="images" web-path="images"/>
<categories src="c.list"/>
<vars src="v.list"/>
<instance src="hi.tree"/>
</ihp>

231
chassis/controlRule.md Normal file
View File

@ -0,0 +1,231 @@
### <mark>注意</mark>
* 在使用串口发送指令时,底层超过 **1s** 没有接收到上位机的指令会向上位机返回错误状态码FF (error状态:连接超时此时用户如果想要重新控制车辆需要发送Reset命令
* 在调试发送指令时先发一条Reset命令然后接着发指令如:
0x 55 AA 02 02 05 00 FA
0x 55 AA 09 00 01 00 04 00 00 00 00 00 00 F3
0x 55 AA 02 02 05 00 FA
0x 55 AA 09 00 01 00 04 00 00 00 00 00 00 F3
...
#### 串口测试速度指令样例
##### 左转
55 AA 02 02 05 00 FA 55 AA 09 00 01 00 00 00 04 00 00 00 00 F3
##### 右转
55 AA 02 02 05 00 FA 55 AA 09 00 01 00 04 00 00 00 00 00 00 F3
##### 前进
55 AA 02 02 05 00 FA 55 AA 09 00 01 00 04 00 04 00 00 00 00 F7
##### 后退
55 AA 02 02 05 00 FA 55 AA 09 00 01 FF FA FF FA 00 00 00 00 F7
下面列出几个常用的控制指令示例:
# 控制车轮速度指令
假设请求左车轮速度为0.1m/s传输数据帧内容为
0x 55 AA 09 00 01 00 04 00 00 00 00 00 00 F3
<table>
<tbody>
<tr align="center">
<td rowspan="2">Header</td>
<td rowspan="2">Length</td>
<td rowspan="2">Sequence</td>
<td colspan="5">Payload</td>
<td rowspan="2">Checksum </td>
</tr>
<tr align="center">
<td width="10%">MsgID</td>
<td colspan="4">PARAM</td>
</tr>
<tr align="center">
<td>55 AA</td>
<td>09</td>
<td>00</td>
<td>01</td>
<td>00 04</td>
<td>00 00</td>
<td>00 00</td>
<td>00 00</td>
<td>F3</td>
</tr>
<tr align="center">
<td>起始标志位</td>
<td>有效数据的长度为9个字节</td>
<td>第一个指令</td>
<td>控制车轮速度指令</td>
<td>左车轮</td>
<td>右车轮</td>
<td colspan="2">固定不变为0</td>
<td>异或校验码</td>
</tr>
</tbody>
</table>
如上文所说通常我们所说的速度是以m/s为单位的速度而指令中车轮速度的参数实际上是单位时间内期望编码器的计数在此需要将指令的速度Vm/s结合AP1下位机的物理参数进行计算下面是车轮速度换算方法。
M1下位机物理参数如下表
名称 | 参数 | 说明
:-------------: | :-------------: | :-------------
wheel_diameter | 0.25 | 车轮直径,单位:米
encoder_resolution | 1600 | 编码器转一圈产生的脉冲数
PID_RATE | 50 | PID调节PWM值的频率
假设我们给M1左轮V=0.1m/s的速度右轮速度的计算指令与此相同<a name="caculate-method">计算方法</a>如下:
车轮转动一圈,移动的距离为轮子的周长:
distance_one_round
=wheel_diameter*π
=wheel_diameter*3.1415926
=0.25*3.1415926
车轮转动一圈,编码器产生的脉冲数为:
wheel_encoder_resolution
=1*encoder_resolution
=1*1600
=1600
编码器与车轮连接在一起车轮转1圈编码器转1圈编码器的脉冲数可以理解为编码器计数编码器自转一圈计数1600则车轮转一圈编码器计数为1600。
所以AP1每移动1米产生脉冲数编码器的计数为:
ticks_per_meter
=(1/distance_one_round)*wheel_encoder_resolution
=1/(0.25*3.1415926)*1600
=2037.18
又因为PID的频率是1秒钟50次所以指令的参数计算方法为:
int(V*ticks_per_meter/PID_RATE)
=int(0.1*2037.18/50)
=4
用户可直接使用0.1m/s速度的计算结果来对应自己期望的速度换算成相应的数值如0.2m/s为81m/s为40等。
接着将计算出来的4换算为16进制为
00 04
则速度指令的PARAM为
00 04 00 00 00 00 00 00
注:如果用户发送的速度超过了最大速度,则会按最大速度行进。
# 获取电池电量指令
0x 55 AA 02 01 02 00 FE
<table>
<tbody>
<tr align="center">
<td rowspan="2">Header</td>
<td rowspan="2">Length</td>
<td rowspan="2">Sequence</td>
<td colspan="2">Payload</td>
<td rowspan="2">Checksum </td>
</tr>
<tr align="center">
<td>MsgID</td>
<td>PARAM</td>
</tr>
<tr align="center">
<td>55 AA</td>
<td>02</td>
<td>01</td>
<td>02</td>
<td>00</td>
<td>FE</td>
</tr>
<tr align="center">
<td>起始标志位</td>
<td>有效数据的长度为2个字节</td>
<td>第二个指令</td>
<td>小车的电量请求</td>
<td>参数为00</td>
<td>异或校验码</td>
</tr>
</tbody>
</table>
# 重置状态指令
当接收到下位机发送的错误状态码FF时需要将AP1状态重置Reset),才能重新恢复控制,指令如下:
0x 55 AA 02 02 05 00 FA
<table>
<tbody>
<tr align="center">
<td rowspan="2">Header</td>
<td rowspan="2">Length</td>
<td rowspan="2">Sequence</td>
<td colspan="2">Payload</td>
<td rowspan="2">Checksum </td>
</tr>
<tr align="center">
<td>MsgID</td>
<td>PARAM</td>
</tr>
<tr align="center">
<td>55 AA</td>
<td>02</td>
<td>02</td>
<td>05</td>
<td>00</td>
<td>FA</td>
</tr>
<tr align="center">
<td>起始标志位</td>
<td>有效数据的长度为2个字节</td>
<td>第三个指令</td>
<td>重置状态指令</td>
<td>参数为00</td>
<td>异或校验码</td>
</tr>
</tbody>
</table>
# 清除编码器计数指令
0x 55 AA 02 03 06 00 F8
<table>
<tbody>
<tr align="center">
<td rowspan="2">Header</td>
<td rowspan="2">Length</td>
<td rowspan="2">Sequence</td>
<td colspan="2">Payload</td>
<td rowspan="2">Checksum </td>
</tr>
<tr align="center">
<td>MsgID</td>
<td>PARAM</td>
</tr>
<tr align="center">
<td>55 AA</td>
<td>02</td>
<td>03</td>
<td>06</td>
<td>00</td>
<td>F8</td>
</tr>
<tr align="center">
<td>起始标志位</td>
<td>有效数据的长度为2个字节</td>
<td>第四个指令</td>
<td>请求清除编码器计数</td>
<td>参数为00</td>
<td>异或校验码</td>
</tr>
</tbody>
</table>

BIN
chassis/imgs/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

BIN
chassis/imgs/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

BIN
chassis/imgs/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
chassis/imgs/ps1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

BIN
chassis/imgs/ps2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

108
chassis/intro.md Normal file
View File

@ -0,0 +1,108 @@
# 产品参数
<table>
<tr>
<td width="20%">尺寸</td>
<td>726*617*326mm</td>
<td>净重</td>
<td>40kg</td>
</tr>
<tr>
<td>负载</td>
<td>50kg</td>
<td>电池</td>
<td>24v 磷酸铁锂电池</td>
</tr>
<tr>
<td>电池容量</td>
<td>20AH</td>
<td>续航时间</td>
<td>4h</td>
</tr>
<tr>
<td>辅助电源</td>
<td>24v</td>
<td>最大速度</td>
<td>0.8m/s</td>
</tr>
<tr>
<td>驱动方式</td>
<td>四驱</td>
<td>转向方式</td>
<td>差速转向</td>
</tr>
<tr>
<td>通信接口</td>
<td>RS232串口</br>115200bps</td>
<td>编码器精度</td>
<td>物理400线</br>逻辑1600线四倍频</td>
</tr>
<tr>
<td>PID控制频率</td>
<td>50Hz</td>
<td>适用地形</td>
<td>全地形</td>
</tr>
<tr>
<td>垂直越障能力</td>
<td>8cm</td>
<td>爬坡能力</td>
<td>25°</td>
</tr>
<tr>
<td>支持系统</td>
<td>Windows/Linux</td>
<td>支持平台</td>
<td>x86/arm</td>
</tr>
<tr>
<td>手柄控制</td>
<td>20m</td>
<td>手柄通讯</td>
<td>2.4Ghz</td>
</tr>
</table>
# 产品尺寸
![4.png](imgs/4.png)
# 控制手柄参数
该手柄有自动休眠功能长时间不操作省电模块被激活后自动进入休眠模式按下START键即可唤醒手柄。
参数名称 | 参数内容
:-------------: | :-------------:
电池 | AAA7号电池\*2
使用时间 | 约10小时
无线频率 | 2.4GHz
接收范围 | 20m
* * *
# 控制面板使用
![1.png](imgs/1.png)
控制面板清单:
名称 | 个数 | 说明
:-------------: | :-------------: | :-------------:
紧急停止 | 1 | 在紧急情况下请按下该紧急停止按钮
电源 | 1 | 电源开启关闭按钮
充电口 | 1 | 为内置电池充电
电量显示 | 1 | 以百分比的形式显示M1电池电量
遥控/上位机 | 1 | 切换手柄控制与上位机控制按钮
RS232 | 1 | 使用串口数据线连接下位机与上位机,进行通信
* * *
# 熔断式保险或空气开关
当M1意外出现短路、过载的情况时熔断式保险或空气开关会主动切断电池的回路。
![2.png](imgs/2.png)

View File

@ -0,0 +1,55 @@
## 里程计标定
里程计标定也叫里程计校准,即在当前运行环境下重新计算运动模型,得到里程计的运动模型参数,此教程**只适用于使用ROS控制M1机器人**的用户。
Autolabor M1 出厂时已做过标定了,在一般运行环境下(地毯、水泥、普通瓷砖等平坦路面)不用重新做标定,但如果您的运行环境是非一般环境,机器人可能就需要重新做标定,比如【经过打磨过的】并且还有【镜面效果】的水泥路面,或摩擦力较大路面,如果您在使用导航套件建图时效果不佳,也可以进行标定。
对于不太确定产品是否需要标定的用户,可先进行里程计测试,根据测试结果来判断机器人是否需要标定。
## 检测机器人是否需要标定
### 操作步骤:
1. 启动键盘控制使用ROS键盘控制AP1原地转360度
2. 打开一个新的terminal运行
```
$ rviz
```
3. rviz窗口打开后将fixed frame选择为odom
4. 关闭其他所有勾选只保留grid和tf如没有grid左下角add新增
5. 打开tf下拉出来frames的内容关闭其他所有勾选保留baselink和odom
6. rviz右侧界面可看两个重合的坐标系(baselink和odom)
7. 对机器人(实体)的四个轮子做标记,标记此时车的位置
8. 键盘控制机器人原地360度转一圈请必须记住此时旋转的方向标定会用到控制机器人回到刚刚标记的位置重合保持机器人与标记的初始位置方向一致
9. 观察rviz中的2个坐标系是否重合
如果不重合,表示需要标定。
如果基本重合,表示不需要标定。
## 对机器人进行里程计标定
### 操作步骤:
1. 打开一个新的terminal
2. 运行
```
$ rosrun tf tf_echo /odom /base_link
```
3. 在出现的数据中查找in RPY (degree)[0,0,X]
4. 查看X值如果刚刚键盘控制车时是顺时针转用360-X如果是逆时针转用360+X得到Y
5. 计算 model_param*Y/360得到计算结果
6. 打开启动的launch文件找到Autolabor M1驱动部分autolabor_pro1_driver找到model_param参数将model_param改为上一步得到的结果
7. 保存、关闭launch文件
8. 关闭terminal中的运行的launch(ctrl+c)如果rviz关闭时弹出窗口询问是否需要保存点击【without saving】不保存
9. 再次重复【检测机器人是否需要标定】的操作
如果不重合,表示需要标定,如果基本重合,表示不需要标定,重复以上【标定】工作,一次次的进行,直到基本重合。
注意除第一次标定时使用的model_param为驱动中的原始值之后每一次的标定操作中model_param为上一次标定计算的model_param结果第5步

50
chassis/overview.md Normal file
View File

@ -0,0 +1,50 @@
# 产品概述
Autolabor M1机器人移动开发平台是一款集模块化、简洁化、可定制为一身的机器人底盘适用于机器人教育培训、科学研究和产品开发等。具有通过性强、负载能力大、精度高、动力足、续航长和扩展性高等特点可跨平台开发支持多种应用场景。
* * *
# 主要特性
* 可靠耐用,操作简单,无需组装,开机直接使用
* 高通过性,爬坡能力十足,续航能力强
* 高精度工业级编码器,定位精准
* 高负载负重可达50KG
* 适应场景广泛,室内外均可使用
* 兼容多个系统,支持多种开发平台、控制方式
* 支持ROS开发提供ROS驱动包与运动模型
* 雷达、摄像头、惯导等多种传感器随意组合搭配,拆装方便
* 使用文档齐全,配备后续用户支持,并不断更新开发教程
* * *
# 产品清单
名称 | 数量 | 备注
:---:|:---:|---
M1移动平台 | 1辆 |
M1充电器 | 1个 |
M1控制手柄| 1套 | 含电池
串口数据线 | 1根 |
配套工具 | 1套 |
* * *
# 使用提醒
* 产品边缘锋利,请小心接触,避免划伤
* 产品表面为金属材质,请勿与电路板直接接触 * 操控平台时避免速度过快,引起碰撞 * 搬运时以及设置作业时,请勿落下或倒置
* 非专业人员,请不要私自拆卸 * 不使用非原厂标配的电池、电源、充电座 * 运行时请勿用手触碰
* 不要在有水的地方,存在腐蚀性、易燃性气体的环境内和靠近可燃性物质的地方使用 * 不要放置在加热器或者大型卷线电阻器等发热体周围
* 切勿将电机直接与商用电源连接
* * *
# 电池安全
* 请在有人看护的状态下充电,若人员离开请拔掉充电插头
* 充电器在充电工作时,会向外界散发一定的热量,充电器与产品应放在通风干燥的环境中使用 * 正常充电时,充电指示灯为红色,当转为绿色时为充满
* 停止充电时,应先拔下插头,然后取下电池端插头
* 产品长期不用,需三个月至半年补充一次电
* 产品电池不可将电完全用完,否则会严重受损,容易造成不可修复
* * *

198
chassis/protocolRule.md Normal file
View File

@ -0,0 +1,198 @@
## 协议概述
本协议是一种用于M1上位机与下位机之间通信的自定义通信协议波特率为115200以16进制格式传输。上位机向下位机发送请求commands下位机在收到来自上位机的请求后作出相应的反应并返回应答feedback至上位机帧结构说明如下。
## 帧结构说明
数据帧分为5个部分起始标志位/帧头Header数据长度Length序列号Sequence有效载荷数据Payload检验码Checksum
数据帧结构如下表所示:
<table>
<tr align="center">
<th style="background-color:#374F7F;color:#fff;">Name</th>
<td>Header</td>
<td>Length</td>
<td>Sequence</td>
<td>Payload</td>
<td>Checksum</td>
</tr>
<tr align="center">
<th style="background-color:#374F7F;color:#fff;">Size</th>
<td>2 Byte</td>
<td>1 Byte</td>
<td>1 Byte</td>
<td>N Bytes</td>
<td>1 Byte</td>
</tr>
</table>
### 起始标志位/帧头Header
起始标志位即为我们常说的帧头以固定不变的“55AA”作为起始标志位标志着一帧的开始。
### 数据长度Length
数据长度其值表示数据包Payload的长度。
### 序列号Sequence
帧的序列号从0开始范围为0~255消息的发送端每发送一个帧将该字段的值加1接收端可以根据该字段是否连续判断是否有丢包的情况发生。
### 有效载荷数据Payload
有效载荷数据即实际数据内容考虑到数据传输效率与可扩展性本协议将Payload的长度设计为非固定长度可适应不同的消息类型。
Payload分为两部分MsgID和PARAM。
<table>
<tbody>
<tr align="center">
<td colspan="2">Payload</td>
</tr>
<tr>
<td align="center">MsgID</td>
<td align="center">PARAM</td>
</tr>
<tr>
<td>指令的类型</td>
<td>指令的内容</td>
</tr>
</tbody>
</table>
Payload指令如下表所示:
<table>
<tbody>
<tr align="center">
<th width="5%">Name</th>
<th>Type</th>
<th>MsgID</th>
<th colspan="4" width="35%">PARAM</th>
<th colspan="2">Description</th>
</tr>
<tr align="center">
<td rowspan="4">车轮速度指令</td>
<td rowspan="2">发送</td>
<td rowspan="2">01</td>
<td width="8%">00 00</td>
<td width="8%">00 00</td>
<td width="8%">00 00</td>
<td width="8%">00 00</td>
<td rowspan="2" align="left" colspan="2">
* M1是2电机4轮驱动模式左/右两侧的两个轮子各由一个电机驱动。因此左边的两个轮子的速度是相同的,在发送左/右轮速度时仅需发送一个轮子的速度每个轮速是2个字节
* PARAM中前2个字节设置左轮的速度接着2个字节设置右轮的速度后面4个字节全部置0用于后续拓展四轮控制
* 当我们改变车轮的速度的时候其实是改变车轮编码器的计数例如给左轮一个0.1m/s前进的速度即给出的指令是0004则需要通过计算将车轮速度转化为编码器的计数后面会讲到如何计算
</td>
</tr>
<tr align="center">
<td>左轮</td>
<td>右轮</td>
<td colspan="2">空字段,用于后续扩展</td>
</tr>
<tr align="center">
<td rowspan="2">接收</td>
<td rowspan="2">01</td>
<td width="8%">00 04</td>
<td width="8%">00 00</td>
<td width="8%">00 00</td>
<td width="8%">00 00</td>
<td align="left" colspan="2" rowspan="2">返回的数据是当前时刻车轮编码器的累计计数</td>
</tr>
<tr align="center">
<td>左轮</td>
<td>右轮</td>
<td colspan="2">空字段,用于后续扩展</td>
</tr>
<tr align="center">
<td rowspan="2">电池电量指令</td>
<td>发送</td>
<td>02</td>
<td colspan="4">00</td>
<td colspan="2" align="left">请求电池电量</td>
</tr>
<tr align="center">
<td>接收</td>
<td>02</td>
<td colspan="4">32</td>
<td align="left" colspan="2">返回的数据为0~100电量区间的16进制表达例如电量为50则返回32</td>
</tr>
<tr align="center">
<td rowspan="3">重置状态指令</td>
<td>发送</td>
<td>05</td>
<td colspan="4">00</td>
<td align="left" colspan="2">
**<mark>当上位机在发送指令后接收到错误状态信息FF时必须发送重置指令重置后才能恢复对下位机的控制</mark>**
</td>
</tr>
<tr align="center">
<td rowspan="2">接收</td>
<td>05</td>
<td colspan="4">01</td>
<td align="left" colspan="2">返回操作成功</td>
</tr>
<tr align="center">
<td>05</td>
<td colspan="4">00</td>
<td align="left" colspan="2">返回操作失败</td>
</tr>
<tr align="center">
<td rowspan="3">清除编码器计数指令</td>
<td>发送</td>
<td>06</td>
<td colspan="4">00</td>
<td align="left" colspan="2">对编码器计数清零</td>
</tr>
<tr align="center">
<td rowspan="2">接收</td>
<td>06</td>
<td colspan="4">01</td>
<td align="left" colspan="2">返回操作成功</td>
</tr>
<tr align="center">
<td>06</td>
<td colspan="4">00</td>
<td align="left" colspan="2">返回操作失败</td>
</tr>
<tr align="center">
<td rowspan="4">错误状态指令</td>
<td rowspan="4">接收</td>
<td>FF</td>
<td colspan="4">01</td>
<td align="left">电池没电</td>
<td rowspan="4" width="20%" align="left" valign="top">当上位机向发送请求,下位机发生错误无法执行指令时,会向上位机返回相应的错误信息</td>
</tr>
<tr align="center">
<td>FF</td>
<td colspan="4">02</td>
<td align="left">电流过大</td>
</tr>
<tr align="center">
<td>FF</td>
<td colspan="4">03</td>
<td align="left">串口通信故障</td>
</tr>
<tr align="center">
<td>FF</td>
<td colspan="4">04</td>
<td align="left">车轮卡死</td>
</tr>
</tbody>
</table>
错误状态处理流程图:
<div style="width:60%">
![Flow](imgs/autolaborPro1-flow.jpg)
</div>
### 检验码(Checksum)
为保证上位机与下位机所传输数据的无误性与一致性本协议采用异或XOR校验的方式根据具体发送的指令生成异或校验码校验的数据包括帧头55AA用户可以使用在线的[异或校验计算器](http://www.ip33.com/bcc.html)来计算,如下图所示**计算结果Hex**即时我们所需的异或校验码。
<div style="width:80%">
![XOR](imgs/autolaborPro1-xor.png)
</div>

140
chassis/sendCommand.md Normal file
View File

@ -0,0 +1,140 @@
# ROS驱动包(ROS Driver Package)
ROS驱动包(ROS Driver Package)是为使用ROS开发的用户提供上位机与下位机通讯的驱动包[下载](http://www.autolabor.com.cn/download))。
### 订阅的话题
/cmd\_vel ([geometry_msgs/Twist]( http://download.autolabor.com.cn/ROS/AutolaborPro1_ROS_Driver_Package20190107.zip))
控制下位机运动的速度指令
### 发布的话题
/wheel\_odom ([nav_msgs/Odometry](http://docs.ros.org/api/nav_msgs/html/msg/Odometry.html))
轮速里程计
/remaining\_battery ([std_msgs::Int32](http://docs.ros.org/hydro/api/std_msgs/html/msg/Int32.html))
剩余电池电量
/current([std_msgs::Float32](http://docs.ros.org/hydro/api/std_msgs/html/msg/Float32.html))
电流信息
/voltage([std_msgs::Float32](http://docs.ros.org/hydro/api/std_msgs/html/msg/Float32.html))
电压信息
### 参数
| 参数 | 名称 | 备注 |
| :------------ | :------------ | :------------ |
| ~port_name (str, default: /dev/ttyUSB0) | 串口名称 | |
| ~odom_frame (str, default: odom) | odom | |
| ~base_frame (str, default: base_link) | base_link | |
| ~baud_rate (int, default: 115200) | 波特率 | |
| ~control_rate (int, default: 10) | 控制频率 | |
| ~sensor_rate (int, default: 5) | 请求电池电量频率 | |
| ~reduction_ratio (double, default: 1) | 减速比 | |
| ~encoder_resolution (double, default: 1600) | 编码器的编码数 | |
| ~wheel_diameter (double, default: 0.25) | 车轮直径 | |
| ~model_param_cw (double, default:0.8) | 顺时针旋转运动模型参数 | | ~model_param_acw (double, default:0.8) | 逆时针旋转运动模型参数 | | ~pid_rate (double, default:50.0) | PID控制周期 | |
### TF转换
odom → base_link
### 驱动示例
<launch>
<node name="autolabor_driver" pkg="autolabor_pro1_driver" type="autolabor_pro1_driver" output="screen">
<param name="port_name" value="/dev/ttyUSB0" />
<param name="odom_frame" value="odom" />
<param name="base_frame" value="base_link" />
<param name="baud_rate" value="115200" />
<param name="control_rate" value="10" />
<param name="sensor_rate" value="5" />
<param name="reduction_ratio" value="1.0" />
<param name="encoder_resolution" value="1600.0" />
<param name="wheel_diameter" value="0.25" />
<param name="model_param_cw" value="0.8" />
<param name="model_param_acw" value="0.8" />
<param name="pid_rate" value="50.0" />
<param name="maximum_encoding" value="32.0" />
</node>
</launch>
### 驱动启动方法
1. 将ROS驱动放入工作空间catkin_ws/src
2. 回到catkin_ws根目录下
3. 打开terminal
3. 执行
source /opt/ros/kinetic/setup.bash
4. 执行
caktin_make
5. 执行
source devel/setup.bash
6. 执行
roslaunch autolabor_pro1_driver driver.launch
驱动启动后,可以给小车发送速度指令。此时须将底盘架起来,车轮悬空。
### 速度指令发送
1. 打开一个terminal
2. 执行 rostopic list 查看有无/cmd_vel 话题
3. 如果有的话,执行:
rostopic pub -r 10 /cmd_vel geometry_msgs/Twist -- '[0.0,0.0,0.0]' '[0.0,0.0,1]'
此时车轮会开始转动如果想要停止的话必须在执行命令的terminal中执行Ctrl+C停止发送。
关于指令参数的详细介绍请参考ROS官网 [Using rostopic pub](http://wiki.ros.org/ROS/Tutorials/UnderstandingTopics)
### 常见问题
* 如果启动的时无法找到端口,请使用以下命令,查看是否存在该目录 /dev/ttyUSB0
ll /dev/ttyUSB0
* 若不存在,请检查:
1. 串口线是否插好
2. 小车电源是否打开
3. 如果上述都没有问题请查询是否是其他的USB端口如/dev/ttyUSB1或/dev/ttyUSB2
* 如果存在目录,但启动显示权限不足,请使用以下命令进行赋权
sudo chmod 666 /dev/ttyUSB0
# ROS驱动调试助手
该驱动还为用户提供了一个调试的节点,可以使用该节点直接向驱动板发送串口指令,进行指令速度与实际运行速度的校准或调整模型等工作。
### 使用步骤
1. 创建并启动launch文件示例如下
<launch>
<node name="driver" pkg="autolabor_pro1_driver" type="sim_autolabor_pro1_driver" output="screen">
<param name="port_name" value="/dev/ttyUSB0" />
<param name="baud_rate" value="115200" />
<param name="control_rate" value="10" />
<param name="pid_rate" value="50" />
</node>
</launch>
2. 打开新终端,输入以下命令,打开操作界面(如下图)
rosrun rqt_reconfigure rqt_reconfigure
![](imgs/autolaborPro1-rqt.png)
3. 调整 left\_wheel 与 right\_wheel的数值可独立控制左/右轮转速,勾选 run\_flag即可向小车发送速度指令
调整的数值为一个PID控制周期下预期编码器的变化数详细的计算方法可参见控制车轮速度指令中的**[编码器计数计算方法](controlRule#caculate-method)**

120
chassis/use.md Normal file
View File

@ -0,0 +1,120 @@
# 控制方式
AP1可以使用手柄控制和上位机控制两种方式来控制下面将分别概述这两种控制方式。
## 手柄控制
### 概述
我们配送一个控制手柄用于控制M1移动。
### 使用说明
M1出厂默认控制模式为手柄控制在控制面板中可切换控制模式将M1控制面板中的上位机控制按钮置于“手柄”方向切换到手柄控制模式即可操作手柄的按键控制移动平台。
#### 手柄操作步骤
1. 打开手柄电源切换至ON模式手柄顶部POWER指示灯亮起
2. 使用MODE键开启控制模式顶部MODE LED指示灯为红色时才能使用手柄进行控制
3. 先按住手柄顶部两个【1】键然后按照下文使用说明操作
![PS2 control](imgs/ps1.jpg)
![PS2 control](imgs/ps2.jpg)
#### 手柄按键说明
我们对于M1的手柄控制设计有几种速度档位如下表。
<table>
<tr align="center">
<th>档位</th>
<td>0档</td>
<td>1档</td>
<td>2档</td>
<td>3档</td>
</tr>
<tr align="center">
<th>速度</th>
<td>25%</td>
<td>50%</td>
<td>75%</td>
<td>100%</td>
</tr>
</table>
<table>
<tr>
<th>按键</th>
<th>功能</th>
<th width="50%">备注</th>
</tr>
<tr>
<td>左区4控制键</td>
<td>前/后/左/右控制运动方向</td>
<td></td>
</tr>
<tr>
<td>SELECT/最低速度档</td>
<td>设置运动速度为最低档位</td>
<td>设置速度档位为0档</td>
</tr>
<tr>
<td>START/最高速度档</td>
<td>设置运动速度为最高档位</td>
<td>设置速度档位为3档</td>
</tr>
<tr>
<td>左侧摇杆键</td>
<td>控制M1的运动方向和速度</td>
<td>使用摇杆灵活的控制运动角度与速度,不必受限于正前/后/左/右控制摇杆推动的力度来控制速度档位的调节将摇杆向前方直接推到低则直接是3档向后方直接推到底则直接设置为0档</td>
</tr>
<tr>
<td>右侧摇杆键</td>
<td>无功能</td>
<td>现阶段暂未使用</td>
</tr>
<tr>
<td>右区控制键-上</td>
<td>线速度增加</td>
<td>调整速度档位每按一下速度提高一档最高不得高于最高档如3档</td>
</tr>
<tr>
<td>右区控制键-下</td>
<td>线速度减少</td>
<td>每按一下速度降低一档最低不得高于最底档如0档</td>
</tr>
<tr>
<td>右区控制键-左/右</td>
<td>角速度增加/减少</td>
<td>同线速度</td>
</tr>
<tr>
<td>顶部名为“1”的按键</td>
<td>运动控制使能</td>
<td>在遥控底盘运动、调整线速度、调整角速度的时候,这两个按钮应该处于被按下的状态</td>
</tr>
<tr>
<td>顶部名为“2”的按键</td>
<td>配对测试按键</td>
<td>当按下其中任意一个名为“2”的按键后且配对的机器人为下位机模式此时机器人会发出蜂鸣音提示</td>
</tr>
</table>
* * *
## 上位机控制
### 概述
使用M1配送的串口数据线与上位机相连按照预定义的**协议规则**,向下位机(底层硬件平台)发送指令,控制移动平台。
### 使用说明
将AP1控制面板中的上位机控制按钮置于开启状态使用接口线将下位机与上位机连接起来向下位机发送指令。
### 指令发送方式
用户可按照自己的开发场景,选择不同的方式发送指令:
* 使用**串口调试助手**直接向下位机发送指令
* 基于ROS开发可使用我们提供的**ROS驱动包**与下位机通信发送接收指令

185
conf.json Normal file
View File

@ -0,0 +1,185 @@
{
"_readme": [
"注uri 必须保障唯一性,若有重复引用,请使用 rewrite 注册新的 uri",
"由于设计缺陷rewrite 时只能自定义最后一级路径",
"uri 格式 1 - 相对文档a/b/c",
"uri 格式 2 - 相对站内:/usedoc/ap1/a/b/c",
"uri 格式 3 - 绝对地址https://www.baidu.com",
"_readme 本身并没有什么软用"
],
"index": "navigationKit/receivingGuide/inspection",
"rewrite": {
},
"plugin": {
"emoji": false,
"taskList": true,
"tex": true,
"flowChart": true,
"sequenceDiagram": true
},
"menu": {
"toc": {
"startLevel": "1",
"overLevel": "1"
},
"items": [
{
"name": "Autolabor M1",
"isOpen": true,
"items": [
{
"name": "收货指南",
"items": [
{
"name": "检查",
"uri": "navigationKit/receivingGuide/inspection"
}
]
},
{
"name": "快速开始",
"items": [
{
"name": "手柄控制",
"uri": "navigationKit/quickStart/handle"
},
{
"name": "键盘控制",
"uri": "navigationKit/quickStart/keyboard"
},
{
"name": "2DSLAM建图",
"uri": "navigationKit/quickStart/2dSlam"
},
{
"name": "3DSLAM建图",
"uri": "navigationKit/quickStart/3dSlam"
},
{
"name": "RTK循迹",
"items": [
{
"name": "安装设置",
"uri": "navigationKit/quickStart/rtk/install"
},
{
"name": "使用步骤",
"uri": "navigationKit/quickStart/rtk/use"
},
{
"name": "工作原理",
"uri": "navigationKit/quickStart/rtk/principle"
},
{
"name": "开发引导",
"uri": "navigationKit/quickStart/rtk/develop"
},
{
"name": "常见问题",
"uri": "navigationKit/quickStart/rtk/faq"
}
]
},
{
"name": "深度相机",
"items": [
{
"name": "简介",
"uri": "navigationKit/quickStart/vzense/intro"
},
{
"name": "使用",
"uri": "navigationKit/quickStart/vzense/use"
}
]
},
{
"name": "远程连接",
"items": [
{
"name": "远程连接",
"uri": "navigationKit/quickStart/remote/remoteConnect"
},
{
"name": "远程控制-键盘插件",
"uri": "navigationKit/quickStart/remote/keyboardPlugin"
},
{
"name": "常见问题",
"uri": "navigationKit/quickStart/remote/qa"
}
]
},
{
"name": "里程计标定",
"uri": "navigationKit/quickStart/robotCalibration"
}
]
},
{
"name": "二次开发",
"items": [
{
"name": "系统目录",
"uri": "navigationKit/development/osIntro"
},
{
"name": "激光SLAM",
"uri": "navigationKit/development/slamintro"
},
{
"name": "激光SLAM-多点导航",
"uri": "navigationKit/development/multiGoalintro"
},
{
"name": "RTK循迹",
"uri": "navigationKit/development/rtkintro"
},
{
"name": "视觉SLAM",
"uri": "navigationKit/development/vslamintro"
},
{
"name": "YOLO识别",
"uri": "navigationKit/development/yolo"
}
]
}
]
},
{
"name": "Autolabor M1底盘",
"items": [
{
"name": "产品概述",
"uri": "chassis/overview"
},
{
"name": "产品参数",
"uri": "chassis/intro"
},
{
"name": "产品使用",
"uri": "chassis/use"
},
{
"name": "串口协议",
"uri": "chassis/protocolRule"
},
{
"name": "串口指令",
"uri": "chassis/controlRule"
},
{
"name": "ROS驱动",
"uri": "chassis/sendCommand"
},
{
"name": "里程计标定",
"uri": "chassis/odomCalibration"
}
]
}
]
}
}

210
conf.pre.json Normal file
View File

@ -0,0 +1,210 @@
{
"_readme": [
"注uri 必须保障唯一性,若有重复引用,请使用 rewrite 注册新的 uri",
"由于设计缺陷rewrite 时只能自定义最后一级路径",
"uri 格式 1 - 相对文档a/b/c",
"uri 格式 2 - 相对站内:/usedoc/ap1/a/b/c",
"uri 格式 3 - 绝对地址https://www.baidu.com",
"_readme 本身并没有什么软用"
],
"index": "navigationKit/receivingGuide/inspection",
"rewrite": {
},
"plugin": {
"emoji": false,
"taskList": true,
"tex": true,
"flowChart": true,
"sequenceDiagram": true
},
"menu": {
"toc": {
"startLevel": "1",
"overLevel": "1"
},
"items": [
{
"name": "Autolabor M1",
"isOpen": true,
"items": [
{
"name": "收货指南",
"items": [
{
"name": "检查",
"uri": "navigationKit/receivingGuide/inspection"
},
{
"_remark": "TODO",
"name": "清点",
"uri": "navigationKit/receivingGuide/inventory"
}
]
},
{
"_remark": "TODO",
"name": "组装测试",
"items": [
{
"name": "组装",
"uri": "navigationKit/assembly/install"
},
{
"name": "测试",
"uri": "navigationKit/assembly/test"
}
]
},
{
"name": "快速开始",
"items": [
{
"name": "手柄控制",
"uri": "navigationKit/quickStart/handle"
},
{
"name": "键盘控制",
"uri": "navigationKit/quickStart/keyboard"
},
{
"name": "2DSLAM建图",
"uri": "navigationKit/quickStart/2dSlam"
},
{
"name": "3DSLAM建图",
"uri": "navigationKit/quickStart/3dSlam"
},
{
"name": "RTK循迹",
"items": [
{
"name": "安装设置",
"uri": "navigationKit/quickStart/rtk/install"
},
{
"name": "使用步骤",
"uri": "navigationKit/quickStart/rtk/use"
},
{
"name": "工作原理",
"uri": "navigationKit/quickStart/rtk/principle"
},
{
"name": "开发引导",
"uri": "navigationKit/quickStart/rtk/develop"
},
{
"name": "常见问题",
"uri": "navigationKit/quickStart/rtk/faq"
}
]
},
{
"name": "深度相机",
"items": [
{
"name": "简介",
"uri": "navigationKit/quickStart/vzense/intro"
},
{
"name": "使用",
"uri": "navigationKit/quickStart/vzense/use"
}
]
},
{
"name": "远程连接",
"items": [
{
"name": "远程连接",
"uri": "navigationKit/quickStart/remote/remoteConnect"
},
{
"name": "远程控制-键盘插件",
"uri": "navigationKit/quickStart/remote/keyboardPlugin"
},
{
"name": "常见问题",
"uri": "navigationKit/quickStart/remote/qa"
}
]
},
{
"name": "里程计标定",
"uri": "navigationKit/quickStart/robotCalibration"
}
]
},
{
"name": "二次开发",
"items": [
{
"name": "系统目录",
"uri": "navigationKit/development/osIntro"
},
{
"name": "激光SLAM",
"uri": "navigationKit/development/slamintro"
},
{
"name": "激光SLAM-多点导航",
"uri": "navigationKit/development/multiGoalintro"
},
{
"name": "RTK循迹",
"uri": "navigationKit/development/rtkintro"
},
{
"name": "视觉SLAM",
"uri": "navigationKit/development/vslamintro"
},
{
"name": "YOLO识别",
"uri": "navigationKit/development/yolo"
}
]
},
{
"_remark": "TODO deviceInfo",
"name": "设备资料",
"items": [
]
}
]
},
{
"name": "Autolabor M1底盘",
"items": [
{
"name": "产品概述",
"uri": "chassis/overview"
},
{
"name": "产品参数",
"uri": "chassis/intro"
},
{
"name": "产品使用",
"uri": "chassis/use"
},
{
"name": "串口协议",
"uri": "chassis/protocolRule"
},
{
"name": "串口指令",
"uri": "chassis/controlRule"
},
{
"name": "ROS驱动",
"uri": "chassis/sendCommand"
},
{
"name": "里程计标定",
"uri": "chassis/odomCalibration"
}
]
}
]
}
}

57
mulu.md Normal file
View File

@ -0,0 +1,57 @@
#Autolabor M1navigationKit
##收货指南receivingGuide
###检查receipt
###清点
##组装测试
###组装
###测试
##快速开始quickStart
###手柄控制handle
###键盘控制keyboard
###2DSLAM建图2dSlam
###3DSLAM建图3dSlam
###RTK循迹rtk
安装设置install
使用步骤use
工作原理principle
开发引导develop
常见问题faq
###深度相机vzense
简介intro
使用use
###远程连接remote
远程连接remoteConnect
远程控制-键盘插件keyboardPlugin
常见问题qa
###里程计标定robotCalibration
##二次开发development
###系统目录OSintro
###激光SLAMslamintro
###激光SLAM-多点导航(multiGoalintro)
###RTK循迹rtkintro
###视觉SLAMvslamintro
###YOLO识别(yolo)
##设备资料
#Autolabor M1底盘chassis
##产品概述overview
##产品参数intro
##产品使用use
##串口协议protocolRule
##串口指令controlRule
##ROS驱动sendCommand
##里程计标定odomCalibration

View File

@ -0,0 +1,202 @@
.
├── script
│   ├── 3d_suit
│   ├── box_suit
│   ├── common
│   ├── simulation
│   ├── test
│   └── track_suit
└── src
├── autolabor_description
│   ├── launch
│   ├── meshes
│   ├── rviz
│   └── urdf
├── driver
│   ├── car
│   │   ├── autolabor_canbus_driver
│   │   └── autolabor_pro1_driver
│   ├── depth_camera
│   │   ├── pico_zense_driver
│   │   └── zed_wrapper
│   ├── imu
│   │   ├── ah100b
│   │   ├── ch104m //ch104m IMU 驱动
│   │   ├── rviz_imu_plugin
│   │   └── tl740d
│   ├── lidar
│   │   ├── ldlidar_stl_ros
│   │   ├── rslidar_sdk
│   │   └── wr_fslidar
│   └── location
│   ├── al_rtk_ros_driver
│   └── marvelmind
│  
├── launch
│   ├── autolabor_navigation_launch
│   │   ├── cmake-build-debug
│   │   ├── launch
│   │   ├── map
│   │   ├── params
│   │   └── rviz
│   ├── autolabor_test_launch
│   │   ├── launch
│   │   └── rviz
│   └── rtk_tracking
│   ├── log
│   └── resources
├── mapping
│   ├── cartographer //cartographer GPU 加速版本
│   │   ├── bazel
│   │   ├── cartographer
│   │   ├── cmake
│   │   ├── configuration_files
│   │   ├── docs
│   │   └── scripts
│   └── cartographer_ros
│   ├── cartographer_ros
│   ├── cartographer_ros_msgs
│   ├── cartographer_rviz
│   ├── docs
│   └── scripts
├── navigation
│   ├── amcl
│   │   ├── cfg
│   │   ├── examples
│   │   ├── include
│   │   ├── src
│   │   └── test
│   ├── base_local_planner
│   │   ├── cfg
│   │   ├── include
│   │   ├── msg
│   │   ├── src
│   │   └── test
│   ├── carrot_planner
│   │   ├── include
│   │   └── src
│   ├── clear_costmap_recovery
│   │   ├── include
│   │   ├── src
│   │   └── test
│   ├── costmap_2d
│   │   ├── cfg
│   │   ├── include
│   │   ├── launch
│   │   ├── msg
│   │   ├── plugins
│   │   ├── src
│   │   └── test
│   ├── dwa_local_planner
│   │   ├── cfg
│   │   ├── include
│   │   └── src
│   ├── fake_localization
│   ├── global_planner
│   │   ├── cfg
│   │   ├── include
│   │   └── src
│   ├── location_fusion
│   │   ├── include
│   │   └── src
│   ├── loop_path_planner
│   │   ├── include
│   │   └── src
│   ├── map_server
│   │   ├── include
│   │   ├── scripts
│   │   ├── src
│   │   └── test
│   ├── move_base
│   │   ├── cfg
│   │   ├── include
│   │   └── src
│   ├── move_slow_and_clear
│   │   ├── include
│   │   └── src
│   ├── nav_core
│   │   └── include
│   ├── navfn
│   │   ├── include
│   │   ├── src
│   │   ├── srv
│   │   └── test
│   ├── navigation
│   ├── path_rviz_plugin
│   │   ├── include
│   │   └── src
│   ├── path_server
│   │   ├── include
│   │   ├── path_data
│   │   ├── src
│   │   └── srv
│   ├── record_path_planner
│   │   ├── include
│   │   └── src
│   ├── rotate_recovery
│   │   ├── include
│   │   └── src
│   ├── teb_local_planner
│   │   ├── cfg
│   │   ├── cmake_modules
│   │   ├── include
│   │   ├── launch
│   │   ├── msg
│   │   ├── scripts
│   │   ├── src
│   │   └── test
│   └── voxel_grid
│   ├── include
│   ├── src
│   └── test
├── simulation
│   ├── autolabor_simulation_base
│   │   ├── include
│   │   ├── launch
│   │   ├── rviz
│   │   ├── script
│   │   └── src
│   ├── autolabor_simulation_lidar
│   │   ├── include
│   │   ├── launch
│   │   ├── rviz
│   │   └── src
│   ├── autolabor_simulation_location
│   │   ├── include
│   │   └── src
│   ├── autolabor_simulation_object
│   │   ├── include
│   │   ├── launch
│   │   ├── rviz
│   │   └── src
│   └── autolabor_simulation_stage
│   ├── include
│   ├── launch
│   ├── map
│   ├── src
│   └── srv
└── tool
├── autolabor_keyboard_control
│   ├── include
│   └── src
├── cartographer_initialpose
│   ├── include
│   └── src
├── joy_to_twist
│   └── src
├── laser_proc
│   ├── include
│   └── src
├── navi_multi_goals_pub_rviz_plugin
│   └── src
├── rviz_autolabor_calibration
│   ├── include
│   └── src
├── rviz_keyboard_twist
│   ├── include
│   └── src
└── rviz_navigation_tools
├── include
└── src
206 directories

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -0,0 +1,602 @@
# 激光SLAM-多点导航
本文介绍SLAM导航中*多点导航功能包*的开发思路。
### 开发背景:
在使用 ROS Navigation & RViz 进行 2D Nav Goal 导航的时候,我们会遇到这些情况:
1. 给定导航的目标点只能设置一个,当有多点任务时需要等待一个个任务结束后,再次手动给目标
2. 无法暂停或取消任务
3. 任务不可循环
### 开发目的:
完成多目标点导航,可以对导航环节进行操控,如可循环、取消、重置任务等。
### 开发思路:
1. 2D Nav Goal 的单点导航是如何实现的?
我们可以知道导航目标是通过 RViz 工具栏中 2D Nav Goal发布出去的。
通过查看 RViz的配置文件或者 Panels->Add New Panel-> Tool Property 可以了解当使用2D Nav Goal 在地图上拉了一个箭头(给定目标点时),其实是向话题 /move_base_simple/goal 发布了 [geometry_msgs/PoseStamped](http://docs.ros.org/en/api/geometry_msgs/html/msg/PoseStamped.html) 类型的消息,这个是目标点的位姿,包含坐标点与朝向。
根据 NodeGraph 可以看到话题 /move_base_simple/goal 被导航包的节点 /move_base 订阅了,进而发给 Navigation 中的各个话题,完成导航。
![](./imgs/multi1.png)
2. 如何基于单点实现多点导航?
要在单点基础上实现多目标点导航的话就要设计一个关于多个导航目标点消息geometry_msgs/PoseStamped的数据结构并对多个目标点进行处理完成导航。
实现多点的方法有多种,在不打破 ROS Navigation 包的完整性的前提下我选择在2D Nav Goal的 RViz节点和 /move_base 节点中间添加了一个话题 /move_base_simple/goal_temp将原本发送给 /move_base_simple/goal 的消息,转发给/move_base_simple/goal_temp通过此话题来积攒多个 2D Nav Goal任务队列并根据任务完成的状态反馈按顺序将每个导航目标点消息 geometry_msgs/PoseStamped 再发送给话题/move_base_simple/goal以完成多任务中的单次目标点的导航如下图示
![](./imgs/multi2.jpeg)
3. 如何来发布多点任务?
像 2D Nav Goal 一样,我们也可以在 RViz 中开发可视化的操作栏,这要使用到 RViz plugin ROS中的可视化工具绝大部分都是基于Qt进行开发的此前古月居有过详细介绍可参考[这篇文章](https://zhuanlan.zhihu.com/p/39390512)。
### 最终效果
首先,我们来看一下最终的实现效果。
MultiNaviGoalsPanel是多点SLAM导航任务的可视化操作区包括任务点列表、循环、重置、取消、开始任务。
通过 RViz plugin 设计的Mark Display能够显示的目标点的标号及箭头朝向
![](./imgs/multi3.png)
#### 代码实现
#### 1. 头文件 multi_navi_goal_panel.h
Qt说明
* 文字编辑——QLineEdit
* 按键——QPushButton
* 列表——QTableWidget
* 复选框——QCheckBox
* 文字显示——QString
ROS说明:
Publisher
* 发送每个目标点消息给/move_base_simple/goal的goal_pub_
* 发送取消指令消息给/move_base/cancel的cancel_pub_
* 发送文字和箭头标记的mark_pub_。
Subsrciber
* 订阅来自rviz中2D Nav Goal的导航目标点消息的goal_sub_
* 订阅目前导航状态的status_sub_
```
#ifndef MULTI_NAVI_GOAL_PANEL_H
#define MULTI_NAVI_GOAL_PANEL_H
#include <string>
#include <ros/ros.h>
#include <ros/console.h>
#include <tf2_ros/transform_listener.h>
#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
#include <rviz/panel.h>//plugin基类的头文件
#include <QPushButton>//Qt按钮
#include <QTableWidget>//Qt表格
#include <QCheckBox>//Qt复选框
#include <visualization_msgs/Marker.h>
#include <geometry_msgs/PoseArray.h>
#include <geometry_msgs/Point.h>
#include <geometry_msgs/PoseStamped.h>
#include <std_msgs/String.h>
#include <actionlib_msgs/GoalStatus.h>
#include <actionlib_msgs/GoalStatusArray.h>
#include <tf/transform_datatypes.h>
namespace navi_multi_goals_pub_rviz_plugin {
class MultiNaviGoalsPanel : public rviz::Panel {
Q_OBJECT
public:
explicit MultiNaviGoalsPanel(QWidget *parent = 0);
virtual void load(const rviz::Config &config);
virtual void save(rviz::Config config) const;
public Q_SLOTS:
void setMaxNumGoal(const QString &maxNumGoal);//设置最大可设置的目标点数量
void writePose(geometry_msgs::Pose pose);//将目标点位姿写入任务列表
void markPose(const geometry_msgs::PoseStamped::ConstPtr &pose);//在地图中标记目标位姿
void deleteMark();//删除标记
protected Q_SLOTS:
void updateMaxNumGoal(); // 更新最大可设置的目标点数量
void initPoseTable(); // 初始化目标点表格
void updatePoseTable(); // 更新目标点表格
void startNavi(); // 开始第一个目标点任务导航
void cancelNavi(); // 取消现在进行中的导航
void addPose();
void goalCntCB(const geometry_msgs::PoseStamped::ConstPtr &pose); // 目标数量子回调函数
void statusCB(const actionlib_msgs::GoalStatusArray::ConstPtr &statuses); // 状态子回调函数
void checkCycle(); // 确认循环
void completeNavi(); // 第一个任务到达后,继续进行剩下任务点的导航任务
void cycleNavi();
bool checkGoal(std::vector<actionlib_msgs::GoalStatus> status_list); // 检查是否到达目标点
static void startSpin(); // 启用ROS订阅
protected:
QLineEdit *output_maxNumGoal_editor_;
QPushButton *output_maxNumGoal_button_, *output_reset_button_, *output_startNavi_button_, *output_cancel_button_, *output_addPoint_button_;
QTableWidget *poseArray_table_;
QCheckBox *cycle_checkbox_;
QString output_maxNumGoal_;
// The ROS node handle.
ros::NodeHandle nh_;
ros::Publisher goal_pub_, cancel_pub_, marker_pub_, init_goal_pub_;
ros::Subscriber goal_sub_, status_sub_;
tf2_ros::Buffer tfBuffer_;
tf2_ros::TransformListener tfListener_;
// 多目标点任务栏定义
int maxNumGoal_;
int curGoalIdx_ = 0, cycleCnt_ = 0;
bool permit_ = false, cycle_ = false, arrived_ = false;
geometry_msgs::PoseArray pose_array_;
actionlib_msgs::GoalID cur_goalid_;
};
} // end namespace navi-multi-goals-pub-rviz-plugin
#endif // MULTI_NAVI_GOAL_PANEL_H
```
#### 2. cpp文件 multi_navi_goal_panel.cpp
```
#include <cstdio>
#include <ros/console.h>
#include <fstream>
#include <sstream>
#include <QPainter>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QLabel>
#include <QTimer>
#include <QDebug>
#include <QtWidgets/QTableWidget>
#include <QtWidgets/qheaderview.h>
#include "multi_navi_goal_panel.h"
namespace navi_multi_goals_pub_rviz_plugin {
MultiNaviGoalsPanel::MultiNaviGoalsPanel(QWidget *parent)
: rviz::Panel(parent), nh_(), maxNumGoal_(1), tfListener_(tfBuffer_) {
goal_sub_ = nh_.subscribe<geometry_msgs::PoseStamped>("move_base_simple/goal_temp", 100,
boost::bind(&MultiNaviGoalsPanel::goalCntCB, this, _1));
status_sub_ = nh_.subscribe<actionlib_msgs::GoalStatusArray>("move_base/status", 1,
boost::bind(&MultiNaviGoalsPanel::statusCB, this,
_1));
goal_pub_ = nh_.advertise<geometry_msgs::PoseStamped>("move_base_simple/goal", 1);
init_goal_pub_ = nh_.advertise<geometry_msgs::PoseStamped>("move_base_simple/goal_temp", 1);
cancel_pub_ = nh_.advertise<actionlib_msgs::GoalID>("move_base/cancel", 1);
marker_pub_ = nh_.advertise<visualization_msgs::Marker>("visualization_marker", 1);
QVBoxLayout *root_layout = new QVBoxLayout;
// create a panel about "maxNumGoal"
QHBoxLayout *maxNumGoal_layout = new QHBoxLayout;
maxNumGoal_layout->addWidget(new QLabel("目标最大数量"));
output_maxNumGoal_editor_ = new QLineEdit;
maxNumGoal_layout->addWidget(output_maxNumGoal_editor_);
output_maxNumGoal_button_ = new QPushButton("确定");
maxNumGoal_layout->addWidget(output_maxNumGoal_button_);
root_layout->addLayout(maxNumGoal_layout);
QHBoxLayout *second_row_layout = new QHBoxLayout;
cycle_checkbox_ = new QCheckBox("循环");
second_row_layout->addWidget(cycle_checkbox_);
output_addPoint_button_ = new QPushButton("添加机器人当前位置");
second_row_layout->addWidget(output_addPoint_button_);
root_layout->addLayout(second_row_layout);
// creat a QTable to contain the poseArray
poseArray_table_ = new QTableWidget;
initPoseTable();
root_layout->addWidget(poseArray_table_);
//creat a manipulate layout
QHBoxLayout *manipulate_layout = new QHBoxLayout;
output_reset_button_ = new QPushButton("重置");
manipulate_layout->addWidget(output_reset_button_);
output_cancel_button_ = new QPushButton("取消");
manipulate_layout->addWidget(output_cancel_button_);
output_startNavi_button_ = new QPushButton("开始导航!");
manipulate_layout->addWidget(output_startNavi_button_);
root_layout->addLayout(manipulate_layout);
setLayout(root_layout);
// set a Qtimer to start a spin for subscriptions
QTimer *output_timer = new QTimer(this);
output_timer->start(200);
// 设置信号与槽的连接
connect(output_maxNumGoal_button_, SIGNAL(clicked()), this,
SLOT(updateMaxNumGoal()));
connect(output_maxNumGoal_button_, SIGNAL(clicked()), this,
SLOT(updatePoseTable()));
connect(output_reset_button_, SIGNAL(clicked()), this, SLOT(initPoseTable()));
connect(output_cancel_button_, SIGNAL(clicked()), this, SLOT(cancelNavi()));
connect(output_startNavi_button_, SIGNAL(clicked()), this, SLOT(startNavi()));
connect(output_addPoint_button_, SIGNAL(clicked()), this, SLOT(addPose()));
connect(cycle_checkbox_, SIGNAL(clicked(bool)), this, SLOT(checkCycle()));
connect(output_timer, SIGNAL(timeout()), this, SLOT(startSpin()));
}
// 更新maxNumGoal命名
void MultiNaviGoalsPanel::updateMaxNumGoal() {
setMaxNumGoal(output_maxNumGoal_editor_->text());
}
// set up the maximum number of goals
void MultiNaviGoalsPanel::setMaxNumGoal(const QString &new_maxNumGoal) {
// 检查maxNumGoal是否发生改变.
if (new_maxNumGoal != output_maxNumGoal_) {
output_maxNumGoal_ = new_maxNumGoal;
// 如果命名为空,不发布任何信息
if (output_maxNumGoal_ == "") {
nh_.setParam("maxNumGoal_", 1);
maxNumGoal_ = 1;
} else {
// velocity_publisher_ = nh_.advertise<geometry_msgs::Twist>(output_maxNumGoal_.toStdString(), 1);
nh_.setParam("maxNumGoal_", output_maxNumGoal_.toInt());
maxNumGoal_ = output_maxNumGoal_.toInt();
}
Q_EMIT configChanged();
}
}
// initialize the table of pose
void MultiNaviGoalsPanel::initPoseTable() {
ROS_INFO("Initialize");
curGoalIdx_ = 0, cycleCnt_ = 0;
permit_ = false, cycle_ = false;
poseArray_table_->clear();
pose_array_.poses.clear();
deleteMark();
poseArray_table_->setRowCount(maxNumGoal_);
poseArray_table_->setColumnCount(3);
poseArray_table_->setEditTriggers(QAbstractItemView::NoEditTriggers);
poseArray_table_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
QStringList pose_header;
pose_header << "x" << "y" << "yaw";
poseArray_table_->setHorizontalHeaderLabels(pose_header);
cycle_checkbox_->setCheckState(Qt::Unchecked);
}
// delete marks in the map
void MultiNaviGoalsPanel::deleteMark() {
visualization_msgs::Marker marker_delete;
marker_delete.action = visualization_msgs::Marker::DELETEALL;
marker_pub_.publish(marker_delete);
}
//update the table of pose
void MultiNaviGoalsPanel::updatePoseTable() {
poseArray_table_->setRowCount(maxNumGoal_);
// pose_array_.poses.resize(maxNumGoal_);
QStringList pose_header;
pose_header << "x" << "y" << "yaw";
poseArray_table_->setHorizontalHeaderLabels(pose_header);
poseArray_table_->show();
}
// call back function for counting goals
void MultiNaviGoalsPanel::goalCntCB(const geometry_msgs::PoseStamped::ConstPtr &pose) {
if (pose_array_.poses.size() < maxNumGoal_) {
pose_array_.poses.push_back(pose->pose);
pose_array_.header.frame_id = pose->header.frame_id;
writePose(pose->pose);
markPose(pose);
} else {
ROS_ERROR("Beyond the maximum number of goals: %d", maxNumGoal_);
}
}
// write the poses into the table
void MultiNaviGoalsPanel::writePose(geometry_msgs::Pose pose) {
poseArray_table_->setItem(pose_array_.poses.size() - 1, 0,
new QTableWidgetItem(QString::number(pose.position.x, 'f', 2)));
poseArray_table_->setItem(pose_array_.poses.size() - 1, 1,
new QTableWidgetItem(QString::number(pose.position.y, 'f', 2)));
poseArray_table_->setItem(pose_array_.poses.size() - 1, 2,
new QTableWidgetItem(
QString::number(tf::getYaw(pose.orientation) * 180.0 / 3.14, 'f', 2)));
}
// when setting a Navi Goal, it will set a mark on the map
void MultiNaviGoalsPanel::markPose(const geometry_msgs::PoseStamped::ConstPtr &pose) {
if (ros::ok()) {
visualization_msgs::Marker arrow;
visualization_msgs::Marker number;
arrow.header.frame_id = number.header.frame_id = pose->header.frame_id;
arrow.ns = "navi_point_arrow";
number.ns = "navi_point_number";
arrow.action = number.action = visualization_msgs::Marker::ADD;
arrow.type = visualization_msgs::Marker::ARROW;
number.type = visualization_msgs::Marker::TEXT_VIEW_FACING;
arrow.pose = number.pose = pose->pose;
number.pose.position.z += 1.0;
arrow.scale.x = 1.0;
arrow.scale.y = 0.2;
number.scale.z = 1.0;
arrow.color.r = number.color.r = 1.0f;
arrow.color.g = number.color.g = 0.98f;
arrow.color.b = number.color.b = 0.80f;
arrow.color.a = number.color.a = 1.0;
arrow.id = number.id = pose_array_.poses.size();
number.text = std::to_string(pose_array_.poses.size());
marker_pub_.publish(arrow);
marker_pub_.publish(number);
}
}
// check whether it is in the cycling situation
void MultiNaviGoalsPanel::checkCycle() {
cycle_ = cycle_checkbox_->isChecked();
}
void MultiNaviGoalsPanel::addPose() {
std::string target_frame = "map";
std::string child_frame = "base_link";
if (tfBuffer_.canTransform(target_frame, child_frame, ros::Time(0), ros::Duration(4.0))) {
geometry_msgs::TransformStamped transformStamped = tfBuffer_.lookupTransform(target_frame, child_frame,
ros::Time(0),
ros::Duration(4.0));
geometry_msgs::PoseStamped pose;
pose.header.frame_id = target_frame;
pose.header.stamp = transformStamped.header.stamp;
pose.pose.position.x = transformStamped.transform.translation.x;
pose.pose.position.y = transformStamped.transform.translation.y;
pose.pose.position.z = transformStamped.transform.translation.z;
pose.pose.orientation = transformStamped.transform.rotation;
init_goal_pub_.publish(pose);
}
}
// start to navigate, and only command the first goal
void MultiNaviGoalsPanel::startNavi() {
curGoalIdx_ = curGoalIdx_ % pose_array_.poses.size();
if (!pose_array_.poses.empty() && curGoalIdx_ < maxNumGoal_) {
geometry_msgs::PoseStamped goal;
goal.header = pose_array_.header;
goal.pose = pose_array_.poses.at(curGoalIdx_);
goal_pub_.publish(goal);
ROS_INFO("Navi to the Goal%d", curGoalIdx_ + 1);
poseArray_table_->item(curGoalIdx_, 0)->setBackgroundColor(QColor(255, 69, 0));
poseArray_table_->item(curGoalIdx_, 1)->setBackgroundColor(QColor(255, 69, 0));
poseArray_table_->item(curGoalIdx_, 2)->setBackgroundColor(QColor(255, 69, 0));
curGoalIdx_ += 1;
permit_ = true;
} else {
ROS_ERROR("Something Wrong");
}
}
// complete the remaining goals
void MultiNaviGoalsPanel::completeNavi() {
if (curGoalIdx_ < pose_array_.poses.size()) {
geometry_msgs::PoseStamped goal;
goal.header = pose_array_.header;
goal.pose = pose_array_.poses.at(curGoalIdx_);
goal_pub_.publish(goal);
ROS_INFO("Navi to the Goal%d", curGoalIdx_ + 1);
poseArray_table_->item(curGoalIdx_, 0)->setBackgroundColor(QColor(255, 69, 0));
poseArray_table_->item(curGoalIdx_, 1)->setBackgroundColor(QColor(255, 69, 0));
poseArray_table_->item(curGoalIdx_, 2)->setBackgroundColor(QColor(255, 69, 0));
curGoalIdx_ += 1;
permit_ = true;
} else {
ROS_ERROR("All goals are completed");
permit_ = false;
}
}
// command the goals cyclically
void MultiNaviGoalsPanel::cycleNavi() {
if (permit_) {
geometry_msgs::PoseStamped goal;
goal.header = pose_array_.header;
goal.pose = pose_array_.poses.at(curGoalIdx_ % pose_array_.poses.size());
goal_pub_.publish(goal);
ROS_INFO("Navi to the Goal%lu, in the %dth cycle", curGoalIdx_ % pose_array_.poses.size() + 1,
cycleCnt_ + 1);
bool even = ((cycleCnt_ + 1) % 2 != 0);
QColor color_table;
if (even) color_table = QColor(255, 69, 0); else color_table = QColor(100, 149, 237);
poseArray_table_->item(curGoalIdx_ % pose_array_.poses.size(), 0)->setBackgroundColor(color_table);
poseArray_table_->item(curGoalIdx_ % pose_array_.poses.size(), 1)->setBackgroundColor(color_table);
poseArray_table_->item(curGoalIdx_ % pose_array_.poses.size(), 2)->setBackgroundColor(color_table);
curGoalIdx_ += 1;
cycleCnt_ = curGoalIdx_ / pose_array_.poses.size();
}
}
// cancel the current command
void MultiNaviGoalsPanel::cancelNavi() {
if (!cur_goalid_.id.empty()) {
cancel_pub_.publish(cur_goalid_);
ROS_ERROR("Navigation have been canceled");
}
}
// call back for listening current state
void MultiNaviGoalsPanel::statusCB(const actionlib_msgs::GoalStatusArray::ConstPtr &statuses) {
bool arrived_pre = arrived_;
arrived_ = checkGoal(statuses->status_list);
// if (arrived_) { ROS_ERROR("%d,%d", int(arrived_), int(arrived_pre)); }
if (arrived_ && arrived_ != arrived_pre && ros::ok() && permit_) {
if (cycle_) cycleNavi();
else completeNavi();
}
}
//check the current state of goal
bool MultiNaviGoalsPanel::checkGoal(std::vector<actionlib_msgs::GoalStatus> status_list) {
bool done;
if (!status_list.empty()) {
for (auto &i : status_list) {
if (i.status == 3) {
done = true;
// ROS_INFO("completed Goal%d", curGoalIdx_);
} else if (i.status == 4) {
// ROS_ERROR("Goal%d is Invalid, Navi to Next Goal%d", curGoalIdx_, curGoalIdx_ + 1);
return false;
} else if (i.status == 0) {
done = true;
} else if (i.status == 1) {
cur_goalid_ = i.goal_id;
done = false;
} else done = false;
}
} else {
// ROS_INFO("Please input the Navi Goal");
done = false;
}
return done;
}
// spin for subscribing
void MultiNaviGoalsPanel::startSpin() {
if (ros::ok()) {
ros::spinOnce();
}
}
//读取目标点
void MultiNaviGoalsPanel::load(const rviz::Config &config) {
Panel::load(config);
QString goal_number;
if (config.mapGetString("multi_goal_panel_number", &goal_number)) {
output_maxNumGoal_editor_->setText(goal_number);
updateMaxNumGoal();
updatePoseTable();
QString goal_var;
if (config.mapGetString("multi_goal_panel_data", &goal_var)) {
QStringList goal_list = goal_var.split("|");
for (int i = 0; i < goal_list.size(); i++) {
QStringList goal = goal_list.at(i).split(",");
geometry_msgs::PoseStamped pose;
pose.header.frame_id = "map";
pose.header.stamp = ros::Time::now();
pose.pose.position.x = goal.at(0).toDouble();
pose.pose.position.y = goal.at(1).toDouble();
pose.pose.position.z = 0.0;
pose.pose.orientation = tf::createQuaternionMsgFromYaw(goal.at(2).toDouble() / 180 * 3.14);
init_goal_pub_.publish(pose);
}
}
bool cycle_flag;
if (config.mapGetBool("multi_goal_panel_cycle", &cycle_flag)) {
if (cycle_flag) {
cycle_checkbox_->setCheckState(Qt::CheckState::Checked);
} else {
cycle_checkbox_->setCheckState(Qt::CheckState::Unchecked);
}
checkCycle();
}
}
}
//保存目标点
void MultiNaviGoalsPanel::save(rviz::Config config) const {
Panel::save(config);
config.mapSetValue("multi_goal_panel_cycle", cycle_checkbox_->isChecked());
if (!output_maxNumGoal_editor_->text().isEmpty()) {
int goal_number = output_maxNumGoal_editor_->text().toInt();
if (goal_number > 0) {
config.mapSetValue("multi_goal_panel_number", goal_number);
QString *goal_list = new QString();
for (int i = 0; i < pose_array_.poses.size(); i++) {
for (int j = 0; j < 3; j++) {
goal_list->append(poseArray_table_->item(i, j)->text());
if (j != 2) {
goal_list->append(",");
}
}
if (i != goal_number - 1) {
goal_list->append("|");
}
}
if (!goal_list->isEmpty()) {
config.mapSetValue("multi_goal_panel_data", *goal_list);
}
}
}
}
} // end namespace navi-multi-goals-pub-rviz-plugin
// 声明此类是一个rviz的插件
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(navi_multi_goals_pub_rviz_plugin::MultiNaviGoalsPanel, rviz::Panel)
```

View File

@ -0,0 +1,256 @@
# Autolabor M1 系统目录
## 简介
Autolabor M1 搭载的是 Autolabor 推出的Autolabor OS机器人操作系统基于 ubuntu20.04 与 ROS Noetic 开发而成,包含 ROS Noetic 、常用
ROS包Cartographer、Gmapping、Navigation ···机器人底盘与传感器驱动包机器人仿真应用以及激光SLAM导航、自动巡迹导航应用等。
解决了繁琐的 ROS 环境安装问题节省时间成本降低技术壁垒。使用该系统学习ROS、开发机器人、实现自己的算法做一些有趣的事情。
秉持小而实用的原则对原生的Ubuntu系统的功能模块进行了精简在原有的基础上增加了ROS开发中使用到的常见功能包。
既保证了简洁性,又增加了实用性。按需构建,有非常良好的可扩展性。
OS在M1上已经实现了机器人自主导航2D/3D SLAM 单目标/多目标、RTK自动循迹、远程遥控等功能。
## 系统目录
```
catkin_ws
├── script //执行脚本
├── src //源码
catkin_ws/src/driver/
├── car
│ ├── autolabor_canbus_driver //PM1底盘驱动
│ └── autolabor_pro1_driver //AP1/M1底盘驱动
├── depth_camera
│ ├── zed_wrapper //ZED 相机驱动
│ └── pico_zense_driver //Vzense DCAM710 相机驱动
├── imu
│ ├── ah100b //ah100b,ah200c 惯导驱动
│ ├── ch104m //ch104m IMU 驱动
│ ├── rviz_imu_plugin //rviz 惯导可视化插件,显示惯导图像
│ └── tl740d //tl740d 转角仪驱动
├── lidar
│ ├── ldlidar_stl_ros //乐动19 雷达驱动
│ ├── rslidar //速腾RS16、RS-Helios雷达驱动
│ └── wr_fslidar // 砝石FS-D10 雷达驱动
└── location
└── al_rtk_ros_driver // RTK570 接收机驱动
└── marvelmind // marvelmind 定位标签驱动
catkin_ws/src/launch/
├── autolabor_navigation_launch //导航launch
│ ├── launch
│ │ ├── real_environment //实车launch
│ │ │ ├── first_generation_base.launch //单雷达版本-传感器驱动
│ │ │ ├── first_generation_mapping.launch //单雷达版本-建图
│ │ │ ├── first_generation_navigation.launch //单雷达版本-导航
│ │ │ ├── rtk_tracking_basic_base.launch//RTK版本-传感器驱动
│ │ │ ├── rtk_tracking.launch //RTK版本-循迹
│ │ │ ├── second_generation_advanced_base.launch //双雷达版本-传感器驱动(含定位标签)
│ │ │ ├── second_generation_basic_base.launch //双雷达版本-传感器驱动
│ │ │ ├── second_generation_mapping.launch //双雷达版本-建图
│ │ │ ├── second_generation_multigoal_navigation.launch //双雷达版本-多点导航
│ │ │ ├── second_generation_navigation.launch //双雷达版本-导航
│ │ │ ├── second_generation_tracking.launch //双雷达版本-循迹
│ │ │ ├── third_generation_base.launch //多线雷达版本-传感器驱动
│ │ │ ├── third_generation_cartographer_3d.launch //多线雷达版本-建图
│ │ │ └── third_generation_navigation_3d.launch //多线雷达版本-导航
│ │ └── simulated_environment //模拟器launch
│ │ ├── common_stage_simulation.launch //模拟场景地图
│ │ ├── first_generation_base_simulation.launch
│ │ ├── first_generation_mapping_simulation.launch
│ │ ├── first_generation_navigation_simulation.launch
│ │ ├── second_generation_advanced_base_simulation.launch
│ │ ├── second_generation_basic_base_simulation.launch
│ │ ├── second_generation_driver_simulation.launch
│ │ ├── second_generation_mapping_simulation.launch
│ │ ├── second_generation_multicar_following_simulation.launch //多车跟随
│ │ ├── second_generation_multicar_navigation_simulation.launch //多车导航
│ │ ├── second_generation_multigoal_navigation_simulation.launch //多目标导航
│ │ ├── second_generation_navigation_simulation.launch
│ │ └── second_generation_tracking_simulation.launch
│ ├── map //建图保存的地图文件
│ │ ├── map_3d.pbstream //3d slam 地图文件,用于定位,不可修改(保存地图后生成)
│ │ ├── map_3d.pgm //3d slam 地图文件,用于导航,可修改(保存地图后生成)
│ │ ├── map_3d.yaml //map_3d.pgm 的解释文件(保存地图后生成)
│ │ ├── map.pbstream //2d slam 地图文件,用于定位导航,不可修改
│ │ ├── map_simulation.pbstream //模拟器地图文件
│ │ ├── simulation_stage.png //模拟器环境地图
│ │ └── simulation_stage.yaml //simulation_stage.png 的解释文件
│ ├── params //配置文件
│ │ ├── cartographer
│ │ │ ├── first_generation_location.lua //单雷达版本-定位
│ │ │ ├── first_generation_mapping.lua //单雷达版本-建图
│ │ │ ├── map_builder.lua //cartographer 配置文件
│ │ │ ├── map_builder_server.lua //cartographer 配置文件
│ │ │ ├── pose_graph.lua //cartographer 配置文件
│ │ │ ├── second_generation_location.lua //双雷达版本-定位
│ │ │ ├── second_generation_mapping.lua //双雷达版本-建图
│ │ │ ├── second_generation_multicar_location.lua //双雷达版本-多车定位(模拟器)
│ │ │ ├── third_generation_location.lua //多线雷达版本-定位
│ │ │ ├── third_generation_mapping.lua //多线雷达版本-建图
│ │ │ ├── trajectory_builder_2d.lua //cartographer 配置文件
│ │ │ ├── trajectory_builder_3d.lua //cartographer 配置文件
│ │ │ └── trajectory_builder.lua //cartographer 配置文件
│ │ ├── common
│ │ │ ├── back_lidar_config.yaml //后雷达过滤配置文件
│ │ │ └── front_lidar_config.yaml //前雷达过滤配置文件
│ │ └── navigation
│ │ ├── costmap //代价地图配置文件
│ │ │ ├── 3d_global_costmap_params.yaml //3d slam 全局代价地图
│ │ │ ├── 3d_local_costmap_params.yaml //3d slam 局部代价地图
│ │ │ ├── one_laser_global_costmap_params.yaml //单雷达版本-全局代价地图
│ │ │ ├── one_laser_local_costmap_params.yaml //单雷达版本-局部代价地图
│ │ │ ├── two_laser_global_costmap_params_for_tracking.yaml //双雷达版本-循迹-全局代价地图
│ │ │ ├── two_laser_global_costmap_params.yaml //双雷达版本-全局代价地图
│ │ │ └── two_laser_local_costmap_params.yaml //双雷达版本-局部代价地图
│ │ ├── global_planer //全局规划配置文件
│ │ │ ├── global_planner_params.yaml //用于导航
│ │ │ ├── navfn_params.yaml //用于多车(模拟器)
│ │ │ └── tracking_planner_params.yaml //用于循迹
│ │ ├── local_planer //局部规划配置文件
│ │ │ ├── navigation_teb_local_planner_params.yaml //用于导航
│ │ │ └── tracking_teb_local_planner_params.yaml //用于循迹
│ │ └── move_base //ROS Navigation 配置文件
│ │ ├── navigation_move_base.yaml //用于导航
│ │ └── tracking_move_base.yaml //用于循迹
│ └── rviz //rviz配置文件
│ ├── 3d_mapping.rviz //3d slam-建图
│ ├── 3d_navigation.rviz //3d slam-导航
│ ├── first_generation_create_map.rviz //单雷达版本-建图
│ ├── first_generation_navigation.rviz //单雷达版本-导航
│ ├── second_generation_create_map.rviz //双雷达版本-建图
│ ├── second_generation_multicar_navigation.rviz //双雷达版本-多车导航(模拟器)
│ ├── second_generation_multigoal_navigation.rviz //双雷达版本-多点导航
│ ├── second_generation_navigation.rviz //双雷达版本-导航
│ └── second_generation_tracking.rviz //双雷达版本-循迹
└── autolabor_test_launch //测试
├── launch
│ ├── car_test.launch //键盘遥控
│ ├── 2dlidar_test.launch //单线激光雷达测试
│ ├── 3dlidar_test.launch //多线激光雷达测试
│ ├── imu_test.launch //惯导测试
│ ├── kinect2_test.launch //深度相机测试
│ ├── robot_calibration.launch //AP1标定
│ └── rtk_test.launch //RTK测试
│ └── tag_test.launch //定位标签测试
└── rviz //rviz配置文件
├── 3dlidar.rviz //多线激光雷达测试
├── car.rviz //键盘遥控
├── fslidar.rviz //单线雷达测试
├── robot_calibration.rviz //AP1标定
└── rtk_test.rviz //RTK测试
└── tag.rviz //定位标签测试
catkin_ws/src/mapping/ //建图源码
├── cartographer //cartographer GPU 加速版本
├── cartographer_ros//cartographer算法源码-适配ROS
catkin_ws/src/navigation/ //ROS Navigation 源码
├── amcl //粒子滤波定位
├── base_local_planner //局部规划
├── carrot_planner //全局规划
├── clear_costmap_recovery //恢复行为
├── costmap_2d //障碍物地图
├── dwa_local_planner //dwa局部规划
├── fake_localization //OS中未使用
├── global_planner //全局规划
├── location_fusion //定位融合(定位标签/RTK+里程计)
├── loop_path_planner //循迹全局规划
├── map_server //地图服务
├── move_base //movebase
├── move_slow_and_clear //恢复行为
├── multi_car_goal //多车-前车向后车发目标(模拟器)
├── nav_core //导航核心接口
├── navfn //全局规划
├── navigation //ROS Navigation元
├── path_rviz_plugin //循迹rviz插件功能按钮组
├── path_server //用于循迹中录制路线
│ ├── path_data
│ │ ├── default_path.path //循迹中保存的路径数据
├── record_path_planner //循迹全局规划
├── rotate_recovery //恢复行为
├── teb_local_planner //局部规划
└── voxel_grid //用于障碍物地图
catkin_ws/src/simulation/ //模拟器
├── autolabor_description //机器人模型
│ ├── launch
│ │ ├── display_autolabor_mini.launch
│ │ └── display_autolabor_pro1.launch
│ ├── meshes
│ │ ├── autolabor_mini.stl
│ │ ├── pro1_body_color.dae //AP1模型文件-主体
│ │ └── pro1_wheel_color.dae //AP1模型文件-轮子
│ ├── rviz
│ │ └── urdf.rviz
│ └── urdf
│ ├── autolabor_mini.urdf
│ ├── second_generation_model.xacro //双雷达版本-机器人描述(类似urdf)
│ └── third_generation_model.xacro //多线雷达版本-机器人描述(类似urdf)
├── autolabor_simulation_base //机器人底盘模拟
├── autolabor_simulation_lidar //雷达模拟
├── autolabor_simulation_location //定位标签模拟
├── autolabor_simulation_object //运动物体模拟
└── autolabor_simulation_stage //场景模拟
catkin_ws/src/tool/
├── autolabor_keyboard_control //键盘控制(必须要插实体键盘)
├── cartographer_initialpose //cartographer初始化定位
├── laser_proc //雷达驱动
├── joy_to_twist //手柄转速度驱动包
├── navi_multi_goals_pub_rviz_plugin //rviz插件-多点导航
├── rviz_autolabor_calibration //rviz插件-AP1标定
├── rviz_keyboard_twist //rviz插件-键盘控制
└── rviz_navigation_tools //rviz插件-多车导航工具,切换机器人(模拟器)
script //执行脚本
├── 3d_suit //多线雷达版本
│ ├── create_map_start
│ ├── create_map_stop
│ ├── navigation_start
│ └── navigation_stop
├── box_suit //单雷达版本
│ ├── create_map_start
│ ├── create_map_stop
│ ├── navigation_start
│ └── navigation_stop
├── common
│ ├── add_keyboard_udev //用于键盘控制,加键盘权限
│ └── rebuild //工作空间编译脚本
├── simulation //模拟器
│ ├── create_map_start
│ ├── create_map_stop
│ ├── navigation_start
│ ├── navigation_stop
│ ├── tracking_start
│ └── tracking_stop
├── test //测试
│ ├── car_test
│ ├── fslidar_test
│ ├── imu_test
│ ├── kinect2_test
│ ├── pico_test
│ ├── robot_calibration
│ ├── rplidar_test
│ ├── rslidar_test
│ └── tag_test
└── track_suit //双雷达版本
├── create_map_start
├── create_map_stop
├── navigation_start
├── navigation_stop
├── tracking_start //开始循迹
└── tracking_stop //停止循迹
```

View File

@ -0,0 +1,100 @@
# RTK循迹
## 目录
* 功能介绍
* 传感器介绍
* 实现原理
## 功能介绍
RTK循迹是机器人循着用户提前录好的轨迹自主行走行走过程中机器人能够自动躲避障碍物自动规划路线达到目标点。
RTK循迹要求配置好RTK接收机然后控制车端完成循迹。
RTK接收机的数据与里程计的数据融合定位得到M1在环境中的坐标点控制M1运动边走边记录当前坐标点的位置将这些坐标点记录为文件保存下来得到路径文件。
在开始循迹的时候程序加载路径文件RTK接收机定位数据与里程计融合定位匹配路径数据机器人按照指定路径行走。
## 传感器介绍
RTK循迹 用到的传感器有:
* 单线激光雷达x2安装在M1车内前后底部
* RTK接收机安装在M1套件内
* 编码器/轮速里程计x2安装在车体内部前侧
* 电机x2安装在车体内部中间位置
![](imgs/rtk_intro_1.png)
## 实现原理
![](imgs/rtk_intro_2.png)
| 步骤 | 输入数据 | 操作 | 输出数据 | 使用ROS包 |
|-----|------------------|--------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------|
| 1 | | RTK接收机 | 定位数据 | marvelmind驱动包(catkin_ws\src\driver\location\al_rtk_ros_driver) |
| 2 | 里程计数据 定位数据 | 定位融合 | 当前机器人在环境中的位姿 | location_fusion(catkin_ws\src\navigation\location_fusion) |
| 3 | 当前机器人位姿 | 控制机器人运动,录制路径 | 路径数据 | path_server(catkin_ws\src\navigation\path_server) |
| 4 | 路径数据 | 读取录制的路径数据,执行循迹操作 | 目标机器人位姿 | path_server(catkin_ws\src\navigation\path_server) [move_base](http://wiki.ros.org/move_base/) |
| 5 | 当前机器人位姿 目标机器人位姿 | 根据机器人当前位姿,进行全局规划路线 | 路径数据(初步预估导航路线) | [global_planner(dijkstra) ](http://wiki.ros.org/global_planner) |
| 6 | 路径数据 前雷达数据 后雷达数据 | 根据规划路径开始导航,进行过程中随着实际环境、障碍物变化,进行局部路径规划,实时避障 | 局部路径规划 避障 | [costmap_2d ](http://wiki.ros.org/cost_map)[teb_local_planner](http://wiki.ros.org/teb_local_planner) |
| 7 | 速度信息 /cmd_vel | 向底层发送速度命令 | | [move_base](http://wiki.ros.org/move_base/) |
定位循迹 launch配置示例
```
<!-- RTK定位驱动 -->
<node pkg="al_rtk_ros_driver" type="al_rtk_ros_driver" name="al_ros_driver">
<remap from="/al_ros_driver/location_pos" to="location_pos"/>
<param name="map_frame" value="map"/>
<param name="serial_device" value="/dev/box_3"/>
<param name="badurate" value="115200"/>
<param name="domain" value="rtk.ntrip.qxwz.com"/>
<param name="account" value="qxnsv0012"/>
<param name="password" value="7fd7069"/>
<param name="publish_pos_fix_only" value="true"/>
<param name="auto_use_first_fix_as_enu_origin" value="true" />
<param name="origin_config_file" value="$(find autolabor_navigation_launch)/params/rtk_tracking_enu_origin.json"/>
</node>
<!-- 定位与里程计定位融合 -->
<node name="location_fusion" pkg="location_fusion" type="simple_fusion">
<param name="map_frame" value="map"/>
<param name="odom_frame" value="odom"/>
<param name="buffer_size" value="50"/>
<param name="tag_frame" value="lidar_front" />
<param name="distance_interval" value="0.1"/>
<param name="rate" value="200"/>
</node>
<!-- 录制路径 -->
<node name="path_saver" pkg="path_server" type="record_path_node">
<param name="map_frame" value="map"/>
<param name="base_link_frame" value="base_link"/>
<param name="odom_topic" value="odom"/>
<param name="distance_interval" value="0.2"/>
</node>
<!-- 加载路径 -->
<node name="path_loader" pkg="path_server" type="load_path_node">
<param name="map_frame" value="map"/>
<param name="path_file" value="default_path"/>
</node>
<!-- 导航模块 -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/move_base/rtk_tracking_move_base.yaml" command="load" />
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/costmap/two_laser_global_costmap_params_for_tracking.yaml" command="load" ns="global_costmap"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/costmap/two_laser_local_costmap_params.yaml" command="load" ns="local_costmap"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/global_planer/tracking_planner_params.yaml" command="load" ns="LoopPathPlanner"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/local_planer/tracking_teb_local_planner_params.yaml" command="load" ns="TebLocalPlannerROS"/>
</node>
```

View File

@ -0,0 +1,246 @@
# 激光SLAM
## 目录
* SLAM介绍
* 基于激光SLAM的机器人自主导航
* Autolabor M1 SLAM 导航功能详解
* 2D SLAM 实现原理
* 3D SLAM 实现原理
## SLAM介绍
**SLAM**,全称是 Simultaneous Localization and Mapping同时定位与地图构建。
SLAM 技术解决的是,我在哪里?(**定位**Localization )我周围是什么样?(**建图**Mapping这两个问题。
移动设备从未知环境中的某一点开始运动,根据传感器获取到的数据,即时计算获取传感器的位置并绘制周围的环境。
![](imgs/slamintro1.jpg)
SLAM 的应用很广泛,有扫地机器人、无人驾驶汽车、无人机,三维场景重建等。
根据使用传感器不同SLAM的可分为激光SLAM和视觉SLAMVSLAM本文介绍的是激光SLAM对视觉SLAM相关知识感兴趣的可以[看这里](/usedoc/navigationKit2/version_two/development/vslamintro)。
## 基于激光SLAM的机器人自主导航
激光SLAM导航指的是使用激光雷达采集的数据进行建图与定位SLAM并在构建的环境地图中自动导航规划+控制)。
![](imgs/slamintro2.png)
地图构建完成后,给出目标点,根据当前位置信息与已知环境地图规划出可行走路径,控制机器人运动,最终达到目标点。
![](imgs/slamintro3.png)
以上只是简单概述了机器人导航的流程,但 SLAM 与 路径规划/Planning 都是很复杂的内容,在此不做过多的探讨。
## Autolabor M1 SLAM 导航功能详解
Autolabor SLAM 导航使用的是谷歌开源的 [Cartographer](https://github.com/googlecartographer/cartographer_ros)支持多平台和传感器配置提供2D和3D实时同步定位与建图使用回环检测消除建图产生的累积误差建图效果更好在国内外众多机器人应用上得到了广泛使用。
> Cartographer is a system that provides real-time simultaneous localization and mapping (SLAM) in 2D and 3D across multiple platforms and sensor configurations.
![](imgs/slamintro4.gif)
### 2D SLAM 实现原理
#### 传感器介绍
2D SLAM 用到的传感器有:
* 单线激光雷达x2安装在M1内部前后位置
* 编码器/轮速里程计x2安装在车体内部前侧
![](imgs/software_intro_6.png)
#### 2D SLAM 建图
![](imgs/software_intro_1.jpg)
| 步骤 | 输入数据 | 操作 | 输出数据 | 使用ROS包 |
|-----|------------------|--------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------|
|1 | 左编码器数据 右编码器数据 | 使用运动模型计算编码器数据得到符合ROS标准的里程计数据| 里程计数据| [autolabor_pro_driver](http://www.autolabor.com.cn/usedoc/ap1/sendCommand)|
|2 | 前雷达数据 后雷达数据 | 过滤并融合前后激光雷达采集的数据,得到机器人周围环境点云数据| 雷达点云数据 | [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/) [laser_filters](http://wiki.ros.org/laser_filters) |
|3 | 里程计数据 点云数据 | 点云匹配| 相对位姿数据 子地图数据 | [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/)|
|4 | 相对位姿数据 子地图数据 | 回环检测,得到子地图拼接的全地图数据 | 地图数据 | [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/)|
Cartographer 2D 建图 launch配置示例
```
<!-- 建图节点 -->
<node name="cartographer_node" pkg="cartographer_ros"
type="cartographer_node" args="
-configuration_directory $(find autolabor_navigation_launch)/params/cartographer
-configuration_basename second_generation_mapping.lua"
output="screen">
</node>
<node name="cartographer_occupancy_grid_node" pkg="cartographer_ros" type="cartographer_occupancy_grid_node" args="-resolution 0.05" />
```
#### 2D SLAM 导航
![](imgs/software_intro_2.jpg)
| 步骤 | 输入数据 | 操作 | 输出数据 | 使用ROS包 |
|-----|------------------|--------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------|
|1 | 左编码器数据 右编码器数据 | 使用运动模型计算编码器数据得到符合ROS标准的里程计数据| 里程计数据| [autolabor_pro_driver](http://www.autolabor.com.cn/usedoc/ap1/sendCommand)|
|2|前雷达数据 后雷达数据 里程计数据 地图数据|将机器人的实时数据与已构建的地图进行匹配|当前机器人在环境中的位姿| [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/)|
|3| 目标机器人位姿| 给机器人制定一个目标点| |[move_base](http://wiki.ros.org/move_base/)|
|4| 当前机器人位姿 地图数据 目标机器人位姿| 根据机器人当前位姿与地图数据,进行全局规划路线| 路径数据(初步预估导航路线) |[global_planner(dijkstra) ](http://wiki.ros.org/global_planner)|
|5| 路径数据 地图数据 前雷达数据 后雷达数据 | 根据规划路径开始导航,进行过程中随着实际环境、障碍物变化,进行局部路径规划,实时避障| 局部路径规划 避障 |[costmap_2d ](http://wiki.ros.org/cost_map)[teb_local_planner](http://wiki.ros.org/teb_local_planner)|
|6| 速度信息 /cmd_vel | 向底层发送速度命令 | |[move_base](http://wiki.ros.org/move_base/)|
说明:
1. move_base, global_planner(dijkstra), costmap_2d 这些功能包(package)都从属于
Navigation 导航这个大的功能包集teb_local_planner 是navigation包的一个插件。
2. 机器人导航过程中,会按照周围环境、实时障碍物做调整不断规划调整路径,向底层发布指令,步骤五和六是一个多次的过程,并非一次就结束了。
Cartographer 2D 导航 launch配置示例
```
<!-- 定位模块 -->
<node name="cartographer_node" pkg="cartographer_ros"
type="cartographer_node" args="
-configuration_directory $(find autolabor_navigation_launch)/params/cartographer
-configuration_basename second_generation_location.lua
-load_state_filename $(find autolabor_navigation_launch)/map/map.pbstream"
output="screen">
</node>
<node name="cartographer_occupancy_grid_node" pkg="cartographer_ros"
type="cartographer_occupancy_grid_node" args="
-resolution 0.05
--noinclude_unfrozen_submaps"
output="screen">
</node>
<node pkg="cartographer_initialpose" type="cartographer_initialpose" name="cartographer_initialpose" >
<param name="configuration_directory" value="$(find autolabor_navigation_launch)/params/cartographer" />
<param name="configuration_basename" value="second_generation_location.lua" />
</node>
<!-- 导航模块 -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/move_base/navigation_move_base.yaml" command="load" />
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/costmap/two_laser_global_costmap_params.yaml" command="load" ns="global_costmap"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/costmap/two_laser_local_costmap_params.yaml" command="load" ns="local_costmap"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/global_planer/global_planner_params.yaml" command="load" ns="GlobalPlanner"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/local_planer/navigation_teb_local_planner_params.yaml" command="load" ns="TebLocalPlannerROS"/>
</node>
```
### 3D SLAM 实现原理
#### 传感器介绍
3D SLAM 用到的传感器有:
* 多线激光雷达安装在M1套件内
* 单线激光雷达x2安装在M1内部前后位置
* 编码器/轮速里程计x2安装在车体内部前侧
* 惯导安装在M1套件内
![](imgs/software_intro_7.png)
#### 3D SLAM 建图
![](imgs/software_intro_4.jpg)
| 步骤 | 输入数据 | 操作 | 输出数据 | 使用ROS包 |
|-----|------------------|--------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------|
|1 | 左编码器数据 右编码器数据 | 使用运动模型计算编码器数据得到符合ROS标准的里程计数据| 里程计数据| [autolabor_pro_driver](http://www.autolabor.com.cn/usedoc/ap1/sendCommand)|
|2 | 多线雷达数据 惯导数据 里程计数据 | 点云匹配| 相对位姿数据 子地图数据 | [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/)|
|3 | 相对位姿数据 子地图数据 | 回环检测,得到子地图拼接的全地图数据 | 地图数据 | [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/)|
Cartographer 3D 建图 launch配置示例
```
<!-- 建图节点 -->
<node name="cartographer_node" pkg="cartographer_ros"
type="cartographer_node" args="
-configuration_directory $(find autolabor_navigation_launch)/params/cartographer
-configuration_basename third_generation_mapping.lua"
output="screen">
<remap from="points2" to="rslidar_points" />
</node>
<node name="cartographer_occupancy_grid_node" pkg="cartographer_ros" type="cartographer_occupancy_grid_node" args="-resolution 0.05" />
<node name="rviz" pkg="rviz" type="rviz" args="-d $(find autolabor_navigation_launch)/rviz/3d_mapping.rviz" />
```
#### 3D SLAM 导航
![](imgs/software_intro_5.jpg)
| 步骤 | 输入数据 | 操作 | 输出数据 | 使用ROS包 |
|-----|------------------|--------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------|
|1 | 左编码器数据 右编码器数据 | 使用运动模型计算编码器数据得到符合ROS标准的里程计数据| 里程计数据| [autolabor_pro_driver](http://www.autolabor.com.cn/usedoc/ap1/sendCommand)|
|2|多线雷达数据 惯导数据 里程计数据 地图数据|将机器人的实时数据与已构建的地图进行匹配|当前机器人在环境中的位姿| [cartographer_ros](https://google-cartographer-ros.readthedocs.io/en/latest/)|
|3| 目标机器人位姿| 给机器人制定一个目标点| |[move_base](http://wiki.ros.org/move_base/)|
|4| 当前机器人位姿 地图数据 目标机器人位姿| 根据机器人当前位姿与地图数据,进行全局规划路线| 路径数据(初步预估导航路线) |[global_planner(dijkstra) ](http://wiki.ros.org/global_planner)|
|5| 路径数据 地图数据 前雷达数据 后雷达数据 多线雷达数据 | 根据规划路径开始导航,进行过程中随着实际环境、障碍物变化,进行局部路径规划,实时避障| 局部路径规划 避障 |[costmap_2d ](http://wiki.ros.org/cost_map)[teb_local_planner](http://wiki.ros.org/teb_local_planner)|
|6| 速度信息 /cmd_vel | 向底层发送速度命令 | |[move_base](http://wiki.ros.org/move_base/)|
说明:
1. move_base, global_planner(dijkstra), costmap_2d 这些功能包(package)都从属于
Navigation 导航这个大的功能包集teb_local_planner 是navigation包的一个插件。
2. 机器人导航过程中,会按照周围环境、实时障碍物做调整不断规划调整路径,向底层发布指令,步骤五和六是一个多次的过程,并非一次就结束了。
Cartographer 3D 导航 launch配置示例
```
<!-- 定位模块 -->
<node name="cartographer_node" pkg="cartographer_ros"
type="cartographer_node" args="
-configuration_directory $(find autolabor_navigation_launch)/params/cartographer
-configuration_basename third_generation_location.lua
-load_state_filename $(find autolabor_navigation_launch)/map/map_3d.pbstream"
output="screen">
<remap from="points2" to="rslidar_points" />
</node>
<node pkg="map_server" type="map_server" name="map_server" args="$(find autolabor_navigation_launch)/map/map_3d.yaml" />
<node pkg="cartographer_initialpose" type="cartographer_initialpose" name="cartographer_initialpose" >
<param name="configuration_directory" value="$(find autolabor_navigation_launch)/params/cartographer" />
<param name="configuration_basename" value="third_generation_location.lua" />
</node>
<!-- 导航模块 -->
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/move_base/navigation_move_base.yaml" command="load" />
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/costmap/3d_global_costmap_params.yaml" command="load" ns="global_costmap"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/costmap/3d_local_costmap_params.yaml" command="load" ns="local_costmap"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/global_planer/global_planner_params.yaml" command="load" ns="GlobalPlanner"/>
<rosparam file="$(find autolabor_navigation_launch)/params/navigation/local_planer/navigation_teb_local_planner_params.yaml" command="load" ns="TebLocalPlannerROS"/>
<param name="TebLocalPlannerROS/xy_goal_tolerance" value="0.2" />
<param name="TebLocalPlannerROS/yaw_goal_tolerance" value="0.5" />
</node>
```

View File

@ -0,0 +1,106 @@
# 视觉SLAM
>本文主要对SLAM技术进行介绍叙述了VSLAM的框架及关键技术和方法并总结了目前已有的VSLAM系统和相关资料。
## 目录
1. SLAM技术介绍
2. VSLAM系统及相关资料介绍
3. SLAM框架
1. SLAM的基本过程
2. VSLAM的主要模块
## 1.SLAM技术介绍
**SLAM**全称是Simultaneous Localization and Mapping即**同时定位与建图**,指机器人在自身位置不确定的条件下,在完全未知环境中创建地图同时利用地图进行自主定位和导航。因此可知SLAM的主要工作是**定位**以及**建图**。
![](./imgs/slamintro1.jpg)
目前SLAM有很多实现方法根据使用传感器不同主要分为
**1激光雷达传感器**
**2视觉传感器**
**视觉SLAM**Visual SLAM 简称VSLAM包括使用**单目SLAM**、**双目SLAM**和以**Kinect**为代表的深度摄像头的**RGB-D SLAM**。相比使用激光雷达传感器,视觉传感器的成本更低,因此也越来越受青睐。
目前SLAM被广泛应用于无人驾驶汽车、无人机、VR和AR等领域。
下面是宾大的教授kumar做的特别有名的一个demo是在无人机上利用二维激光雷达做的SLAM。
![](./imgs/vslam1.gif)
## 2.VSLAM系统及相关资料介绍
目前在VSLAM领域实现较好的SLAM系统有
- 单目、双目SLAM:
(1).**PTAM**(Parallel Tracking And Mapping):2007年很流行的SLAM项目是**第一个**使用BA完成**实时SLAM**的系统。但其缺乏**回环检测和重定位等功能**只能作用于小规模的场景且稳定性也不是很高。http://www.robots.ox.ac.uk/~gk/PTAM/
(2).**DTAM**(Dense tracking and mapping in real-time):2011年Direct SLAM方法的**鼻祖**;
(3).**LSD-SLAM**(Large-Scale Direct Monocular SLAM):2014年一个半稠密SLAM系统。http://vision.in.tum.de/research/vslam/lsdslam
(4).**ORB-SLAM**:2015年一个比较完整的基于特征点的SLAM系统。
http://webdiis.unizar.es/~raulmur/orbslam/
- RGB-D SLAM:
(1).**KinectFusion** 2011
http://www.microsoft.com/en-us/research/project/kinectfusion-project-page/
(2).**RGBD-SLAM2** 2014 http://felixendres.github.io/rgbdslam_v2/
(3).**ElasticFusion** 2015 http://www.imperial.ac.uk/dyson-robotics-lab/downloads/elastic-fusion/
当然除了上述项目的学习外对于slam的学习入门有一些经典教材如
**Multiple View Geometry in Computer Vision (Second Edition)**
http://www.robots.ox.ac.uk/~vgg/hzbook/)也有中文版为《计算机视觉中的多视图几何》。
**Robotics Vision and Control**http://www.petercorke.com/RVC/ 本书是面向实践的,详细介绍了机器人和机器视觉。也有了中文版,叫做《机器人学、机器视觉与控制》。
此外对于slam入门学习推荐高博的**一起做RGB-D SLAM**其博客为半闲居士http://www.cnblogs.com/gaoxiang12/p/4633316.html
## 3.SLAM框架
### 3.1 SLAM的基本过程
描述为:机器人在未知环境中从一个未知位置开始移动,在移动过程中根据位置估计和传感器数据进行自身定位,同时建造增量式地图。
1**定位(localization)**:机器人必须知道自己在环境中位置。
2**建图(mapping)**:机器人必须记录环境中特征的位置(如果知道自己的位置)
3**SLAM**:机器人在定位的同时建立环境地图。其基本原理是运过概率统计的方法,通过多特征匹配来达到定位和减少定位误差的。
![](./imgs/vslam2.png)
### 3.2 VSLAM的主要模块
视觉SLAM主要分为几个模块**数据采集、视觉里程计(Visual Odometry)、后端优化、建图(Mapping)、闭环检测(Loop closure detection)**。如下图所示:
![](./imgs/vslam3.png)
#### 3.2.1 视觉里程计
视觉里程计是利用一个图像序列或者一个视频流,计算摄像机的方向和位置的过程。一般包括图像获取后、畸变校正、特征检测匹配或者直接匹配对应像素、通过对极几何原理估计相机的旋转矩阵和平移向量。
![](./imgs/vslam4.jpg)
#### 3.2.2 后端优化
理论上来说如果视觉里程计模块估计的相机的旋转矩阵R和平移向量t都正确就能得到完美的定位和建图了。但实际试验中我们得到的数据往往有很多噪声且由于传感器的精度、错误的匹配等都对造成结果有误差。并且由于我们是只把新的一帧与前一个关键帧进行比较当某一帧的结果有误差时就会对后面的结果产生**累计误差**,导致误差越来越大。**为了解决这个问题,引入后端优化**。
后端优化一般采用捆集调整(BA)、卡尔曼滤波(EKF)、图优化等方式来解决。其中**基于图优化的后端优化**效果最好。Graph-based SLAM一般使用g2o求解器进行图优化计算。
![](./imgs/vslam5.jpg)
#### 3.2.3 闭环检测
后端优化可能得到一个比较优的解,但当运动回到某一个之前去过的地方,如果能认出这个地方,找到并与当时的关键帧进行比较,就可以得到比单用后端优化更准确更高效的结果。闭环检测就是要解决这个问题。
闭环检测有两种方式,一是根据估计出来的相机的位置,看是否与之前否个位置邻近;**另外一种是根据图像,自动识别出来这个场景之前到过,并找到当时的关键帧**。目前常用的后一种方法其实是一个非监督的模式识别问题。比较常用的方法是采用Bag-of-Words(BOW)**ORB-SLAM**就是使用这个方法来进行**闭环检测**。
![](./imgs/vslam6.jpg)

View File

@ -0,0 +1,45 @@
## YOLO V5
## 简介
YOLOv5 是革命性的“You Only Look Once”物体检测模型的第五次迭代旨在实时提供高速、高精度的结果。
这个强大的深度学习框架基于 PyTorch 构建,因其多功能性、易用性和高性能而广受欢迎。我们的文档将指导您完成安装过程,解释模型的架构细微差别,展示各种用例,并提供一系列详细的教程。这些资源将帮助您在计算机视觉项目中充分发挥 YOLOv5 的潜力。让我们开始吧!
![](imgs/yolo.jpeg)
## 教程
这是综合教程的汇编,将指导您了解 YOLOv5 的不同方面。
[快速入门](https://docs.ultralytics.com/yolov5/quickstart_tutorial/)
[训练自定义数据](https://docs.ultralytics.com/yolov5/tutorials/train_custom_data/) 推荐:了解如何在自定义数据集上训练 YOLOv5 模型。
[获得最佳训练结果的技巧](https://docs.ultralytics.com/yolov5/tutorials/tips_for_best_training_results/):发现优化模型训练过程的实用技巧。
[多 GPU 训练](https://docs.ultralytics.com/yolov5/tutorials/multi_gpu_training/):了解如何利用多个 GPU 来加快训练速度。
[PyTorch Hub](https://docs.ultralytics.com/yolov5/tutorials/pytorch_hub_model_loading/):学习通过 PyTorch Hub 加载预训练模型。
[TFLite、ONNX、CoreML、TensorRT 导出](https://docs.ultralytics.com/yolov5/tutorials/model_export/):了解如何将模型导出为不同的格式。
[NVIDIA Jetson 平台部署](https://docs.ultralytics.com/yolov5/tutorials/running_on_jetson_nano/):了解如何在 NVIDIA Jetson 平台上部署 YOLOv5 模型。
[测试时间增强 (TTA)](https://docs.ultralytics.com/yolov5/tutorials/test_time_augmentation/):探索如何使用 TTA 来提高模型的预测准确性。
[模型集成](https://docs.ultralytics.com/yolov5/tutorials/model_ensembling/):学习组合多个模型以提高性能的策略。
[模型剪枝/稀疏性](https://docs.ultralytics.com/yolov5/tutorials/model_pruning_and_sparsity/):了解剪枝和稀疏性概念,以及如何创建更有效的模型。
[超参数演化](https://docs.ultralytics.com/yolov5/tutorials/hyperparameter_evolution/):探索自动超参数调整的过程,以获得更好的模型性能。
[使用冻结层进行迁移学习](https://docs.ultralytics.com/yolov5/tutorials/transfer_learning_with_frozen_layers/):了解如何通过冻结 YOLOv5 中的层来实现迁移学习。
[架构总结](https://docs.ultralytics.com/yolov5/tutorials/architecture_description/) 深入研究 YOLOv5 模型的结构细节。
[Roboflow数据集](https://docs.ultralytics.com/yolov5/tutorials/roboflow_datasets_integration/):了解如何利用 Roboflow 进行数据集管理、标记和主动学习。
[ClearML 日志记录](https://docs.ultralytics.com/yolov5/tutorials/clearml_logging_integration/) :了解如何集成 ClearML 以在模型训练期间进行高效日志记录。
[YOLOv5 与 Neural Magic](https://docs.ultralytics.com/yolov5/tutorials/neural_magic_pruning_quantization/):了解如何使用 Neural Magic 的 Deepsparse 来修剪和量化您的 YOLOv5 模型。
[Comet 日志记录](https://docs.ultralytics.com/yolov5/tutorials/comet_logging_integration/) 新功能:探索如何利用 Comet 改进模型训练日志记录。

View File

@ -0,0 +1,119 @@
# 规格参数
## 上位机Orin NX
| 项目 | 规格 |
|-----|-----------|
|算力 | 100TOPS |
|GPU | 32个Tensor核心1024核 |
|CPU|8核Arm Cortex-A78 2GHz|
|内存|16GB 128位LPDDR5 102.4GB/s|
|存储|128GB|
|视频编解码|H.265|
|PCIe|1X4+3X1 PCIe4.0|
|显示接口|HDMI2. 1 1x8K30DP|
|网卡|内置|
|I/O|USB3.0/SCI/I2C/CAN/DMIC/ DSPK/I2S/GPIOs|
## 显示器
| 项目 | 规格 |
|-----|-----------|
| 尺寸 | 13.3 英寸 |
| 比例 | 16: 9 |
| 分辨率 | 1920*1080 |
| 亮度 | 300 cd/m² |
| 对比度 | 800: 1 |
| 触摸 | 否 |
##多线激光雷达RS-Helios-16p
| 项目 | 规格 |
|----------|--------------------------|
| 线数 | 16 |
| 激光波长 | 905nm |
| 激光安全等级 | Class 1 人眼安全 |
| 测距能力 | 150m(90m@10% NIST) |
| 盲区 | ≤0.2m |
| 精度(典型值) | ±2cm(1m to 100m)<br>±3cm(0.1m to 1m)<br>±3cm(100m to 150m) |
| 水平视场角 | 360° |
| 垂直视场角 | 30°-15°~15°) |
| 水平角分辨率 | 0.2°/0.4° |
| 垂直角分辨率 | 均匀 2° |
| 帧率 | 10Hz/20 Hz |
| 转速 | 600/1200rpm (10/20Hz) |
| 出点数 | ~288,000pts/s(单回波)<br> ~576,000pts/s(双回波) |
| UDP数据包内容 | 三维空间坐标<br>反射强度<br>时间戳等 |
| 输出数据协议 | UDP packets over Ethernet |
| 时间同步 | ≈ GPRMC with 1PPSPTP&gPTP |
##卫星接收机RTK580
| 项目 | 规格 |
|----------|--------------------------|
| 通道 | 1408 通道 |
| 频点 | 5星多频<br>BDS、GPS、QZSS、GLONASS、Galileo |
| 单点定位 (RMS) | 平面 1.5m 高程 2.5m |
| RTK(RMS) |平面 0.8cm+1ppm ;高程 1.5cm+1ppm |
| 冷启动 |<30s |
| RTK 初始化时间 | <5s( ) |
| 初始化可靠性 | >99.9% |
|差分通讯方式| 免DTU模块|
| 差分数据 | RTCM 3.X |
| 数据格式 | NMEA 0183 Unicore |
| 定位测向数据更新率 | 20Hz |
| 原始观测量数据更新率 | 20Hz |
| 定向精度 (RMS) | 0.1° /1m 基线 |
| 时间精度 (RMS) |20ns |
| 速度精度 (RMS) | 0.03m/s |
| 双天线接口 | SMA公头外螺内孔 * 2 |
| 通讯接口 | USB-Type-C |
| 供电接口 | USB-Type-C/XT-30 |
| 供电电压 | 5-15V |
| 功耗 |2.4W |
| 尺寸 | 93mmx69mmx37mm |
| 重量 | 153g |
##惯导CH104M
| 项目 | 规格 |
|----------|--------------------------|
|姿态角 | 横滚/俯仰0.2°<br>偏航静态0.2°°<br>偏航动态3° |
|陀螺仪 |测量范围±2000°/s <br>分辨率0.01°/s<br>零偏稳定性1.76°/hr |
|加速度计|测量范围±8G <br>分辨率1uG<br>零偏稳定性30uG |
|磁传感器|测量范围±8G(Gauss)<br>非线性度±0.1%<br>分辨率0.25mG|
## 激光雷达LD19
| 参数名称 | 单位 | 最小值 | 典型值 | 最大值 | 备注 |
|:------|:-----|:------|:-----|:----|:-----------------------------------------|
| 测距范围 | m | 0.02 | - | 12 | 70%目标反射率 |
| 扫描频率 | Hz | 5 | 10 | 13 | 外部提供PWM 控速 |
| 测距频率 | Hz | - | 4500 | - | 固定频率 |
| 测距精度 | mm | - | - | - | 测距小于0.3m时,有数据输出。</br>测距数据变化趋势与实际距离变化趋势一致 |
| 测距精度 | m | -45 | - | 45 | 测距范围在300mm到12000mm时测量100次的平均值(70%漫反射面) |
| 测距标准差 | mm | - | 10 | - | 测距范围在300mm到12000mm时 |
| 测量分辨率 | mm | - | 15 | - | |
| 角度误差 | ° | - | - | 2 | |
| 角度分辨率 | ° | - | 1 | - | |
| 抗环境光 | KLux | - | - | 30 | |
| 整机寿命 | h | 10000 | - | - | |
## 深度相机Vzense DCAM710
| 项目 | 规格 |
|----------|--------------------------|
| 尺寸 | 103mmx33mmx22mm |
| 传感器 | ToF CCD+RGB |
| 激光发射器 | 940nm VCSEL |
| ToF分辨率帧率 | 640X480, Max.30fps |
| RGB分辨率帧率 | 1920X1080, 30fps |
| ToF FOV | 69°(H) × 51°(V) |
| RGB FOV | 73°(H)X42°(V) |
| 输出格式 | RAW12(深度,IR), MJPEG(RGB) |
| 检测距离 | 0.35m~4.4m |
| 工作温度 | -10℃~50℃ |

View File

@ -0,0 +1,131 @@
# 建图导航
## 操作步骤
### 一、连接键鼠
鼠标键盘建议使用普通成套的无线键鼠,避免使用机械键盘、游戏鼠标,请不要同时使用有线、无线鼠标或键盘。
### 二、启动系统
打开电源,打开工控机,等待 Autolabor OS 系统启动,输入密码 autolabor字母全部是小写回车。
![](imgs/login.png)
### 三、建图导航
#### 准备工作:
1. 将 M1 控制模式切换到上位机控制,右转打开急停开关,确保急停开关没有被按下
2. 禁用工控机无线功能
在建图过程中如果工控机连接了无线网络当M1走出无线网络的覆盖区域后网络连接会自动断开这将导致M1控制失控所以在开始建图之前为了避免失控需要将工控机的无线网络连接关闭。
3. 进入桌面 SLAM建图导航 - > 2d 文件夹,首次使用时文件如下图,双击打开后点击 trust and launch程序第一次打开后会重命名。
![](imgs/slam-8.png)
![](imgs/slam-9.png)
#### 1. 点击【开始建图】
RVIZ工具打开能够看到地图中的 M1。
使用键盘的上下左右控制 M1 行走,边走边建图,可以看到环境地图随着 M1 的行走不断被构建更新。
地图中红色是前置激光雷达实时数据黄色是后置激光雷达实时数据颜色从白至黑为障碍物的几率是0%~100%。
![](imgs/slam-3.png)
##### 键盘控制说明
键盘控制功能在系统中已经安装好,无需安装可直接使用。
有2种键盘控制的方式
一、直接在电脑上插上键鼠,直接控制机器人(默认模式)
数字键【1/2】线速度增加/减少数字键【3/4】角速度增加/减少数字键【9】启用数字键【0】禁用。
二、使用键盘插件,用另一台电脑远程控制机器人
![](imgs/key_control.png)
如使用该插件则需将机器人上的键鼠拔掉否则远程给出的速度指令会被机器人上的键鼠指令覆盖如插上了不使用键鼠控制程序默认向底盘发送速度指令为0机器人会停止不动。
如想远程操控机器人,请查看[这里](/usedoc/navigationKit2/version_two/network/setting)。
#### 2. 建图完毕,点击【保存并停止建图】
最终建图结果示意:
<img style="width:37%;" src="imgs/slam-4.png"/>
完成建图后一定要点击【保存并停止建图】关闭窗口不能直接关闭RVIZ窗口或者关闭终端否则地图文件无法正确保存导航时会报错。
地图文件存储路径为catkin_ws\src\launch\autolabor_navigation_launch\map
2D SLAM 地图文件:
```
map.pbstream: 地图文件,用于定位导航,不可修改
```
如有多个环境,想保存不同的地图,在不同的环境使用:
1. 最后一次成功保存的地图会自动存储为以上的名称,而过去建好的地图会用时间戳重命名,备份在同一路径下,地图的名称可以自定义。
2. 如果要切换地图,可以将现有地图重命名,将要替换的地图文件分别修改为以上文件的名称,之后需要再次切换的时候,依然更换地图名称即可。
注:如修改文件名,请务必保证名称正确无误。
#### 3. 点击【开始导航】
RVIZ工具打开建好的地图会自动加载。
<img style="width:50%;" src="imgs/slam-5.png"/>
##### 初始化定位
导航之前,要对机器人做初始化定位,设置方式有两种:
一、使用键盘控制机器人走到初始建图位置,注意车头、车尾方向与之前基本保持一致;
二、使用2D Pose Estimate点击 2D Pose Estimate2D nav goal左边根据机器人现在的所在位置对比地图找到机器人在地图中大概的位置根据真实车头的朝向方向指向车头朝向的方向拉动鼠标。
设置后控制机器人行走一段距离,当激光雷达的数据与实时的环境匹配成功时,即为机器人找到在地图中的初始定位。
<img style="width:50%;" src="imgs/slam-6.gif"/>
如上图所示,进行初始化定位操作后,机器人突然跳到了一个新地点,并且地图中的地点与真实环境中机器人位置一致,即为初始化成功,可进行下一步操作。
##### 关闭键盘控制
<b style="color:red;">点击数字键【0】关闭键盘控制功能</b>非小键盘0控制方式将切换至导航程序控制如不切换机器人接收到目标点命令后将不会自动导航行走。
##### 给定目标点
点击 2D Nav Goal指定目标位置拉动鼠标箭头方向是最终车辆运行至目标的车头朝向鼠标松开这样就完成了目标点的指定。
目标点给定后在地图中我们能看到一条线这是规划好的路径。根据这个规划好的路径M1 向目标点行走。
当M1已经到达目标点后再一次使 2D Nav Goal 设置目标位置M1将去到下一个目标点。
<img style="width:50%;" src="imgs/slam-7.gif"/>
### 四、点击【终止导航】,停止导航功能,导航结束。
之后在同一环境中需要再次使用导航功能时,无须重复建图,点击【开始导航】即可。
[>>常见问题](/usedoc/navigationKit2/common/q_a/doc2)
## 附录 - 软件功能图
![](imgs/software_intro_1.jpg)
![](imgs/software_intro_2.jpg)

View File

@ -0,0 +1,150 @@
# 建图导航
## 操作步骤
### 一、连接键鼠
鼠标键盘建议使用普通成套的无线键鼠,避免使用机械键盘、游戏鼠标,请不要同时使用有线、无线鼠标或键盘。
### 二、启动系统
打开电源,打开工控机,等待 Autolabor OS 系统启动,输入密码 autolabor字母全部是小写回车。
![](imgs/login.png)
### 三、建图导航
#### 准备工作:
1. 将 M1 控制模式切换到上位机控制,右转打开急停开关,确保急停开关没有被按下
2. 禁用工控机无线功能
在建图过程中如果工控机连接了无线网络当M1走出无线网络的覆盖区域后网络连接会自动断开这将导致M1控制失控所以在开始建图之前为了避免失控需要将工控机的无线网络连接关闭。
3. 进入桌面 SLAM建图导航 - > 3d 文件夹,首次使用时文件如下图,双击打开后点击 trust and launch程序第一次打开后会重命名。
![](imgs/slam-8.png)
![](imgs/slam-9.png)
#### 1. 点击【开始建图】
RVIZ工具打开能够看到地图中的 M1。
使用键盘的上下左右控制 M1 行走,边走边建图,可以看到环境地图随着 AP1 的行走不断被构建更新。
地图中黑色是障碍物白色是可行走区域彩色的激光点是激光雷达实时数据颜色从白至黑为障碍物的几率是0%~100%。
![](imgs/3d_slam1.png)
##### 键盘控制说明
键盘控制功能在系统中已经安装好,无需安装可直接使用。
有2种键盘控制的方式
一、直接在电脑上插上键鼠,直接控制机器人(默认模式)
数字键【1/2】线速度增加/减少数字键【3/4】角速度增加/减少数字键【9】启用数字键【0】禁用。
二、使用键盘插件,用另一台电脑远程控制机器人
![](imgs/key_control.png)
如使用该插件则需将机器人上的键鼠拔掉否则远程给出的速度指令会被机器人上的键鼠指令覆盖如插上了不使用键鼠控制程序默认向底盘发送速度指令为0机器人会停止不动。
如想远程操控机器人,请查看[这里](/usedoc/navigationKit2/version_two/network/setting)。
#### 2. 建图完毕,点击【保存并停止建图】
最终建图结果示意:
![](imgs/3d_slam3.png)
完成建图后一定要点击【保存并停止建图】关闭窗口不能直接关闭RVIZ窗口或者关闭终端否则地图文件无法正确保存导航时会报错。
地图文件存储路径为catkin_ws\src\launch\autolabor_navigation_launch\map
3D SLAM 地图文件:
```
map_3d.pbstream: 地图文件,用于定位,不可修改
map_3d.pgm地图文件用于导航可修改
map_3d.yaml pgm 文件的解释文件
```
如有多个环境,想保存不同的地图,在不同的环境使用:
1. 最后一次成功保存的地图会自动存储为以上的名称,而过去建好的地图会用时间戳重命名,备份在同一路径下,地图的名称可以自定义。
2. 如果要切换地图,可以将现有地图重命名,将要替换的地图文件分别修改为以上文件的名称,之后需要再次切换的时候,依然更换地图名称即可。
注:如修改文件名,请务必保证名称正确无误。
#### 3. 修地图
注:此步骤非必须操作,如建图结果与真实环境基本一致,可跳至下一步。
建图完成后,对照真实的建图环境,对地图进行修改,涂抹掉临时障碍物,增加真实环境中实际存在的障碍物,也可以自定义机器人的行走区域。
为避免修改错误无法返回,建议在修改前对要文件做一个备份。
右键点击map_3d.pgm选择Krita软件打开选择画笔工具白色涂掉障碍物黑色新增障碍物。
![](imgs/3d_slam4.png)
![](imgs/3d_slam5.png)
![](imgs/3d_slam6.png)
地图修改完毕后,保存关闭。
#### 4. 点击【开始导航】
RVIZ工具打开建好的地图会自动加载。
![](imgs/3d_slam7.png)
##### 初始化定位
导航之前,要对机器人做初始化定位,设置方式有两种:
一、使用键盘控制机器人走到初始建图位置,注意车头、车尾方向与之前基本保持一致;
二、使用2D Pose Estimate点击 2D Pose Estimate2D nav goal左边根据机器人现在的所在位置对比地图找到机器人在地图中大概的位置根据真实车头的朝向方向指向车头朝向的方向拉动鼠标。
设置后控制机器人行走一段距离,当激光雷达的数据与实时的环境匹配成功时,即为机器人找到在地图中的初始定位。
![](imgs/3d_slam8.gif)
如上图所示,进行初始化定位操作后,机器人突然跳到了一个新地点,并且地图中的地点与真实环境中机器人位置一致,即为初始化成功,可进行下一步操作。
##### 关闭键盘控制
<b style="color:red;">点击数字键【0】关闭键盘控制功能</b>非小键盘0或取消键盘插件的【激活键盘控制】关闭后控制方式将切换至导航程序如不切换机器人接收到目标点命令后将不会自动导航行走。
##### 给定目标点
点击 2D Nav Goal指定目标位置拉动鼠标箭头方向是最终车辆运行至目标的车头朝向鼠标松开这样就完成了目标点的指定。
目标点给定后在地图中我们能看到一条线这是规划好的路径。根据这个规划好的路径M1向目标点行走。
当M1已经到达目标点后再一次使 2D Nav Goal 设置目标位置M1将去到下一个目标点。
### 四、点击【终止导航】,停止导航功能,导航结束。
之后在同一环境中需要再次使用导航功能时,无须重复建图,点击【开始导航】即可。
## 附录 - 软件功能图
![](imgs/software_intro_4.jpg)
![](imgs/software_intro_5.jpg)

View File

@ -0,0 +1,101 @@
## 手柄控制
### 概述
我们配送一个控制手柄用于控制M1移动。
### 使用说明
M1出厂默认控制模式为手柄控制在控制面板中可切换控制模式将M1控制面板中的上位机控制按钮置于“手柄”方向切换到手柄控制模式即可操作手柄的按键控制移动平台。
#### 手柄操作步骤
1. 打开手柄电源切换至ON模式手柄顶部POWER指示灯亮起
2. 使用MODE键开启控制模式顶部MODE LED指示灯为红色时才能使用手柄进行控制
3. 先按住手柄顶部两个【1】键然后按照下文使用说明操作
![PS2 control](imgs/ps1.jpg)
![PS2 control](imgs/ps2.jpg)
#### 手柄按键说明
我们对于M1的手柄控制设计有几种速度档位如下表。
<table>
<tr align="center">
<th>档位</th>
<td>0档</td>
<td>1档</td>
<td>2档</td>
<td>3档</td>
</tr>
<tr align="center">
<th>速度</th>
<td>25%</td>
<td>50%</td>
<td>75%</td>
<td>100%</td>
</tr>
</table>
<table>
<tr>
<th>按键</th>
<th>功能</th>
<th width="50%">备注</th>
</tr>
<tr>
<td>左区4控制键</td>
<td>前/后/左/右控制运动方向</td>
<td></td>
</tr>
<tr>
<td>SELECT/最低速度档</td>
<td>设置运动速度为最低档位</td>
<td>设置速度档位为0档</td>
</tr>
<tr>
<td>START/最高速度档</td>
<td>设置运动速度为最高档位</td>
<td>设置速度档位为3档</td>
</tr>
<tr>
<td>左侧摇杆键</td>
<td>控制M1的运动方向和速度</td>
<td>使用摇杆灵活的控制运动角度与速度,不必受限于正前/后/左/右控制摇杆推动的力度来控制速度档位的调节将摇杆向前方直接推到低则直接是3档向后方直接推到底则直接设置为0档</td>
</tr>
<tr>
<td>右侧摇杆键</td>
<td>无功能</td>
<td>现阶段暂未使用</td>
</tr>
<tr>
<td>右区控制键-上</td>
<td>线速度增加</td>
<td>调整速度档位每按一下速度提高一档最高不得高于最高档如3档</td>
</tr>
<tr>
<td>右区控制键-下</td>
<td>线速度减少</td>
<td>每按一下速度降低一档最低不得高于最底档如0档</td>
</tr>
<tr>
<td>右区控制键-左/右</td>
<td>角速度增加/减少</td>
<td>同线速度</td>
</tr>
<tr>
<td>顶部名为“1”的按键</td>
<td>运动控制使能</td>
<td>在遥控底盘运动、调整线速度、调整角速度的时候,这两个按钮应该处于被按下的状态</td>
</tr>
<tr>
<td>顶部名为“2”的按键</td>
<td>配对测试按键</td>
<td>当按下其中任意一个名为“2”的按键后且配对的机器人为下位机模式此时机器人会发出蜂鸣音提示</td>
</tr>
</table>

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Some files were not shown because too many files have changed in this diff Show More