更新PIBot用户手册文档

pull/8/head
詹力 2024-01-28 11:39:16 +08:00
parent cc7f3b20b4
commit 4ae4558216
52 changed files with 5 additions and 0 deletions

View File

@ -0,0 +1,5 @@
## source ~/.bashrc的作用
`source ~/.bashrc` 命令的作用是重新加载当前用户的 Bash 配置文件(通常是 `~/.bashrc` 文件以应用对该文件的任何修改。Bash 配置文件包含了在每次启动新的终端会话时执行的命令,用于自定义环境变量、别名、函数和其他终端行为。
具体来说,`~/.bashrc` 是 Bash shell 在交互模式下启动时会读取的一个配置文件。当您对 `~/.bashrc` 文件进行了修改后,为了使更改生效,您可以重新加载配置文件,而这就是使用 `source` 命令的目的。

View File

@ -0,0 +1,39 @@
Firefly RK3399固件刷新与ROS安装.md 2/24/2019
1.烧写原始镜像
1.1安装 RK USB 驱动
下载 Release_DriverAssistant.zip ,解压,然后运行里面的 DriverInstall.exe 。
为了所有设备都使用更新的驱动,请先选择"驱动卸载",然后再选择"驱动安装"。
1.2连接设备
确保设备连接好电源适配器并处于通电状态。
用 Micro USB OTG 线连接好设备和主机。
按住设备上的 RECOVERY (恢复)键并保持。
短按一下 RRESET复位键。
大约两秒钟后,松开 RECOVERY 键。
注意:如果发现按了 RESET 键后还是没有发现设备,请在保持 RECOVERY 键按下的同时,长按一下
PWRKEY 键,然后才松开 RECOVERY 键。 主机应该会提示发现新硬件并配置驱动。打开设备管理器,
会见到新设备"Rockusb Device" 出现,如下图。如果没有,则需要返回上一步重新安装驱动。
1/2
Firefly RK3399固件刷新与ROS安装.md 2/24/2019
1.3烧写Ubuntu
切换至"升级固件"页。
按"固件"按钮,打开要升级的固件文件。升级工具会显示详细的固件信息。
按"升级"按钮开始升级。
注意:如果你烧写的固件 如果升级失败,可以尝试先按"擦除Flash"按钮来擦除 Flash然后再升级。
laoder版本与原来的机器的不一致请在升级固件前先执行"擦除Flash"。
2.安装ROS
ROS安装、交换分区设置、编译可以直接参考树莓派(raspberry pi 3b)安装ROS Kinetic Kame
2/2

View File

@ -0,0 +1,237 @@
1. 概述
2. 软件框架
3. Ubuntu的刷⼊、ROS的安装
4. ssh远程连接
安装 4.1 xshell
通过 连接 树莓派 Xshell
5. ⽤户主机 /RK3288/RK3399
6. 多机通讯
7. ROS驱动开发
7.1 概述
7.2 驱动开发及PID参数动态调整
7.3 ⽬录结构简介
7.4 编译与测试
编译
测试
初始化配置
开始测试
8. 校准
⽆IMU校准
IMU校准
9. ROS建图与导航
9.1 概述
9.2 建图
两种建图⽅法
保存地图
10 导航
单点导航测试
多点导航
11. 模拟器
12. Android App
12.1相关功能
12.2 显⽰视频
13. IMU的相关包使⽤
1. 概述
采⽤ 作为上位 主控 基于 开发适配导航建图算 树莓派 ⼯控机 /RK3288/RK3399/X86,
法 /TK1/TX1/TX2 ROS ROS
2. 软件框架
3. Ubuntu的刷⼊、ROS的安装
具体请参考树莓派 安装 (raspberrypi 3b/4b) ROS
tf卡启动的 nanopi(RK3399) 则同 树莓派 ⼀样操作
4. ssh远程连接
中推荐安装 远程连接 windows
xshell
树莓派 默认开启了热点 和 均为 ,可以通过连接该热点 / nanopi rk3399
ssid password pibot_ap
连接树莓派 ,树莓派 的 为 / nanopi rk3399
/ nanopi rk3399 IP 192.168.12.1
如需要关闭 模式,改为连接 ,只需要执⾏ 后重启 ap
wifi cd ~/pibot_ros && ./pibot_ap.sh stop
如需要打开 模式 开启则执⾏ 后重启 ap ,
cd ~/pibot_ros && ./pibot_ap.sh start
安装 4.1 xshell
⽹盘中下载 xshell 并安装
通过 连接 树莓派 Xshell /RK3288/RK3399
输⼊ IP 和⽤户名密码
树莓派 为 3B/3B+ pibot
、 为 firefly RK3288 RK3399
为 nanopi 3399 pi firefly
5. ⽤户主机
需要⼀个 PC 安装 ROS 环境,⽤来显⽰查看地图或者玩转模拟器等, PIBOT 提供了⼀个⼀键安装 ROS 的
脚本 ,可以直接在 下安装 pibot_install_ros
Ubuntu ROS
环境 或者 虚拟机 环境 安装 参⻅ ⽤户主机
Ubuntu Windows7/10+Vmvare+Ubuntu Ros kinetic , ROS
Windows下安装Ubuntu虚拟机及ROS相关章节
6. 多机通讯
⽤户主机与 怎么建⽴ 通讯的, 提供了⼀键配 树莓派 主机 /RK3288/RK3399/TK1/TX1/TX2/X86
置脚本 pibot_init_env ROS PIBOT
设备端
主机端
7. ROS驱动开发
7.1 概述
下位机及通过串⼝与 通讯, 提供了⼀个简单的协 树莓派 主机 /RK3288/RK3399/TK1/TX1/TX2/X86
议,通讯协议具体请参⻅协议⽂档 PIBOT
7.2 驱动开发及PID参数动态调整
PID 参数已在出⼚时候配置如需了解细节请参考PIBOT的driver的实现及动态PID调节
7.3 ⽬录结构简介
建议拷⻉提供的压缩⽂件⾄⽬标设备( 树莓派 ⼯控机 /RK3288/RK3399/X86 /TK1/TX1/TX2 )上解压或者
直接 git clone 不然会遇到⼀些问题具体问题⻅Q&A
的 ⽬录如下图 PIBOT ROS workspace
模拟器 arbotix_ros
⼯具集 pibot
驱动包 pibot_bringup pibot
⽂件 pibot_description pibot urdf
建图导航相关配置项 pibot_navigation
导航模拟器 pibot_simulator pibot
激光雷达驱动包 rplidar_ros rplidar
激光雷达驱动包 ydlidar-1.2.1 eai
7.4 编译与测试
编译
拉取最新的代码 cd ~/pibot_ros
git pull #
cd ~/pibot_ros/ros_ws
catkin_make
测试
初始化配置
重新拔插 ⼝或者重启 USB 树莓派/RK3288/RK3399
ls /dev/pibot -l
开始测试
打开终端输⼊ pibot_ ,再输⼊2次 tab 提⽰⽀持的命令
这些命令具体定义在 ~/.pibotrc ⽂件,该⽂件是 pibot_init_env 脚本⾃动⽣成的
8. 校准
⽆IMU校准
参⻅PIBOT的控制及校准
IMU校准
参⻅PIBOT的IMU校准
9. ROS建图与导航
9.1 概述
ROS 驱动中提供了 cmd_vel 的订阅及 odom 的发布,⾄此再需要⼀个激光雷达就可以完成建图了
9.2 建图
在 树莓派 运⾏ 或 /RK3288/RK3399
pibot_gmapping roslaunch pibot_navigation gmapping.launch
在⽤户主机运⾏ 或者 roslaunch pibot_navigation view_nav.launch
pibot_view
两种建图⽅法
运⾏ 或者 即可 roslaunch pibot keyboard_teleop.launch
通过键盘或者遥控⼿柄开始建图 roslaunch pibot joystick.launch
直接选择导航的点( 2D Nav Goal )开始建图
保存地图
运⾏下列命令即可(xxx为⾃定义名称
roslaunch pibot_navigation save_map.launch map_name:=xxx
或者
roscd pibot_navigation/maps/
rosrun map_server map_saver -f xxx`
可以看到⽣成2个⽂件
10 导航
单点导航测试
在 树莓派 运⾏ /RK3288/RK3399 roslaunch pibot_navigation nav.launch map_name:=xxx.yaml
在⽤户主机运⾏ 或 ,通过 提供的 pibot_view roslaunch pibot_navigation view_nav.launch
功能既可以完成导航测试(这⾥需要先指定初始位置) RViz
如果直接运⾏ ⽽不指定 参数则使⽤默认参 roslaunch pibot_navigation nav.launch
数, nav.launch ⽂件中可以设置默认使⽤的地图⽂件 map_name
多点导航
可以通过修改 中的 的 脚本完成单点和多点的导航 pibot/scripts
具体可以参考 navigation_demo.py python
使⽤rospy启动launch与定点导航
基于rospy的多点导航的python实现
11. 模拟器
PIBOT包内置了模拟器可以直接运⾏模拟导航
运⾏ 或者 roslaunch pibot_simulator nav.launch pibot_simulator
运⾏ roslaunch pibot_navigation view_nav.launch
这样⽆需⼩⻋也可以模拟导航了
12. Android App
12.1相关功能
保证⼿机跟跟 PC 或者 树莓派/RK3288/RK3399 连接同⼀个⽹络,保证⼿机能够访问
到 具体参⻅ 多机的通讯配置 roscore ( export ROS_IP=XXX.XXX.XXX.XXX )
ROS
修改 选择 的 点击 Master URI
roscore URI CONNECT
切换 与 Camera View Map View
在地图⻓按 相当于 中的 Set Pose
Rviz 2D Pose Estimate
在地图⻓按 相当于 中的 Set Goal
Rviz 2D Nav Goal
左下⾓ 可以发出 控制⼩⻋移动 Joystick
cmd_vel topic
12.2 显⽰视频
显⽰视频需要硬件摄像头⽀持同时在 PC 或者 树莓派/RK3288/RK3399 启
动 roslaunch pibot usb_camera.launch
或者直接浏览器访问192.168.12.1:8080选择相应的Topic即可
为设备地址 192.168.12.1 , 以具体设备为准
13. IMU的相关包使⽤
装有 IMU 的 PIBOT 系列⼩⻋,底层提供 IMU 的数据采集,上层提供了 IMU 的互补滤波以及融合⾥程计
和 IMU 的扩展的卡尔曼滤波包robot_pose_ekf
启动时只需相应的 with_imu 的 launch ⽂件, 具体参⻅ pibot_ 命令

View File

@ -0,0 +1,336 @@
1. 概述
2. 软件框架
3. 下位机开发环境
3.1 环境搭建
3.1.1 Arduino
3.1.2 STM32F1
3.1.3 STM32F4
下的 编译 ubuntu gcc
的下 ⽣成 ⼯程 windows cubemx( keil )
4. 通讯协议以
5. 参数配置
5.1 默认参数
界⾯配置
5.2 电机顺序与电机⽅向
5.2.1 电机顺序
两轮差分
四轮差分/四轮⻨克纳姆轮
三轮全向
5.2.2 测试电机顺序
5.2.3 电机⽅向的的确认与调整
5.2.4 电机编码器⽅向的的确认与调整
1. 概述
下位机⽀持多种主板, 、 及 等 PIBOT
Arduino Mega2560 STM32F1 STM32F4
PIBOT下位机⽀持多种运动模型(差分、全向、⻨克纳姆轮),⽆需重新烧写固件即可修改⽀持
PIBOT下位机⽀持不同的参数的机器⼈执⾏设置相关参数即可
2. 软件框架
橙⾊部分为下位机的功能模块
3. 下位机开发环境
为主控单元,使⽤ 进⾏开发,⽀ Arduino Mega 2560
持 和 环境 Windows ubuntu Visual studio code + Platform IO
STM32F1 为主控单元,使⽤ Keil 进⾏开发
为主控单元, 下使⽤ 进⾏开发 STM32F4
Ubuntu Visual studio code
3.1 环境搭建
3.1.1 Arduino
具体请参考 插件 开发 Visual Studio Code PlatformIO IDE Arduino
3.1.2 STM32F1
安装Keil开发环境
⽬录提供了 的安装包 ,安装过程不再赘述 pibot docs/software/MDK520
Keil5 MDK520.EXE
完成安装后需要继续安装Keil.STM32F1xx_DFP.2.2.0.pack
编译与烧写
编译
到 源码包中双击打开 ⼯程 编译 PIBOT
pibot.uvprojx ,
烧写程序
这⾥我们使⽤ JLink 烧写程序,连接 JLink ⾄开发板开发板上电这⾥的JLink只需要连接GND
三根线 SWDIO SWCLK
打开⼯程选项切换⾄Debug标签选择 JLink
点击 按钮, 选择 Settings SW
Port
切换⾄Flash Download标签,如下图设置
保存后即可烧写程序
同时可以看到程序运⾏指⽰灯在闪烁表⽰程序在正常运⾏了
3.1.3 STM32F4
下的 编译 ubuntu gcc
安装交叉编译器
sudo apt-get install gcc-arm-none-eabi
编译
cd stm32
make
即可完成编译
烧写程序
配置openocd使⽤jlink烧写程序
安装openocd
sudo apt-get install openocd
烧写
连接好jlink
make burn 即可完成程序烧写
的下 ⽣成 ⼯程 windows cubemx( keil )
安装Keil开发环境
提供了 的安装包 ,安装过程不再赘述 软件⼯具 ⽬录 pibot/
/MDK520 Keil5 MDK520.EXE
完成安装后需要继续安装 Keil.STM32F4xx_DFP.2.9.0.pack
编译
打开 cubemx\MDK-ARM\ ⽬录下的⼯程⽂件,编译同F1
烧写程序
同F1
4. 通讯协议以
通讯协议具体请参⻅协议⽂档
5. 参数配置
运动参数出⼚时都内置在板⼦的 EEPROM/FLASH 中, 配置完成驱动板需要重新上电⽣效
5.1 默认参数
执⾏下⾯的python脚本即可以设置默认的参数
cd ~/pibot_ros/pypibot/transport
python set_default_params.py
执⾏前使⽤pibot_view_env命令查询当前的PIBOT_MODEL和PIBOT_BOARD是否配置确认如果
不正确,使⽤ pibot_init_env 脚本配置, 具体参⻅上位机开发
界⾯配置
设备端执⾏ 端执⾏ 即可以看到配置参数的 界⾯ pibot_brinup HostUI
pibot_configure
具体参数定义说明如下
model_type 模型参数, 具体为2轮差分/三轮全向/⻨克纳姆轮
主动轮的直径 wheel_disameter
wheel_track apollo :两个主动轮的轮间距 zeus :三个轮⼦所在圆直径 hades :四个轮⼦矩形⻓
宽之和 hera 左右轮距*系数
encoder_resolution 轮⼦旋转⼀周编码器变化值的绝对值(⼀般为4*编码器分辨率如4 * 11 固件
程序做了4倍频)
电机的减速⽐ motor_ratio
计算 的间隔时间,固定值 do_pi_interval
pid 10
kp ki kd
为⼀个系数,实际 参数为 ko
PID kp/ko ki/ko kd/ko
cmd_last_time 命令激励的超时时间,即超过该时间没有新的命令会机器⼈会停⽌
底层速度限制,遥控器键盘或者导航层下发的速 max_v_liner_x max_v_liner_y max_angular_z
度会被该值限制
⽀持的 类型 imu_type
imu
motorX_exchange_flag 电机⽅向调整参数, 具体⻅下⾯说明
encoderX_exchange_flag 编码器⽅向调整参数, 具体⻅下⾯说明
5.2 电机顺序与电机⽅向
由于电机批次或者电机型号不⼀致, ⾸次接⼊PIBOT需要做电机顺序的矫正
5.2.1 电机顺序
两轮差分
/*
x
^
|
|
|
y<--------
---------------------
- -
- -
- -
- -
- -
- -
1-------------------2
*/
四轮差分/四轮⻨克纳姆轮
/*
x
^
|
|
|
y<--------
3-------------------2
- -
- -
- -
- -
- -
- -
4-------------------1
*/
三轮全向
/*
x
^
|
|
|
y<--------
1
--
- -
- -
- -
- -
- -
2-------------------3
*/
5.2.2 测试电机顺序
确认⼩⻋模型
查看 pibot_bringup 打印输出确认当前配置模型是否跟使⽤的⼀致如不⼀致则host端打
开 调整 pibot_configure
测试电机
退出之前的程序, 架空⼩⻋(便于观察)
#c#pdy根测t~h据试/opn配电ibt置机oe1的ts_t⻋r_om上so/t对po照yrps电i.bp机oy顺t1/序0t0r图0an#s观p命o察r令t是输否完是电电机机1转1会动转动
#p#yt根测h据试on配电t机置e2的st⻋_m上ot对o照rs电.p机y顺0序1图000观#察命是令否输是完电机电2转机动2会转动
#p#y测t根h试据on电配t机置e3的st⻋_如m上o果t对o有照rs电.p机y顺0序0图10观00察#是否命是令电输机完3转电动机3会转动
##py测根th试据on电配t机置e的4st⻋_如m上o果t对o有照rs电.p机y顺0序0图10观00察#是否命是令电输机完4转电动机4会转动
5.2.3 电机⽅向的的确认与调整
确保电机顺序以及⽅向正常,再测试该项
重做测试电机的测试, 根据输⼊的参数以及实际转的⽅向确认电机接线⽅向是否正确
c#p#p#ydy测tt观观~hh察试察/oopnn是是电ibtt机否否oee是1是tss_tt电电r__omm机机soo/tt11poo是是yrr否否pssi..顺逆bppoyy时时t针针1/-0t1转转0r00a0动动#n0s((命0p从从o令电电#rt输机机命完输输令出出输电轴轴完机//外外1电会侧侧机转观观1动察察会))转动
如果不⼀致按照之前的在设备端执⾏ 端执⾏ 修 pibot_brinup Host
改 参数 motorX_exchange_flag ** pibot_configure
调整完成主板需要重新上电⽣效
5.2.4 电机编码器⽅向的的确认与调整
确保电机顺序以及⽅向正常,再测试该项
重做测试电机的测试, 根据输⼊的参数以及编码器的反馈确认编码器
cp##yd观t测~h察试/opn输电ibt出机oe的1ts_t⽇r_om志so/tpoyr第psi.⼀bpoy列t的1/0t值0r0a是n0s否p在#or变命t ⼤令输完电机1会转动
p#yt观h察on输t出e的st⽇_m志otor第s.⼀py列的-1值00是0否0在#变命⼩令输完电机1会转动
如下结果
在控制电机1顺时针转动时候编码器1(第⼀列)逐渐增⼤则为正常
david@david-MS-7808:~/pibot_ros/pypibot/transport$ python test_motors.py 1000 0
[I] 2021-04-23 23:34:02.941 (0xFBE7):set pwm success
[I] 2021-04-23 23:34:02.941 (0xFBE7):****************get encoder count*****************
[I] 2021-04-23 23:34:02.948 (0xFBE7):encoder count: 0 0 0
[I] 2021-04-23 23:34:03.964 (0xFBE7):encoder count: 636.0 0.0
[I] 2021-04-23 23:34:04.472 (0xFBE7):encoder count: 1292.0 0.0
[I] 2021-04-23 23:34:04.980 (0xFBE7):encoder count: 1949.0 0.0
[I] 2021-04-23 23:34:05.488 (0xFBE7):encoder count: 2609.0 0.0
[I] 2021-04-23 23:34:05.996 (0xFBE7):encoder count: 3263.0 0.0
[I] 2021-04-23 23:34:06.504 (0xFBE7):encoder count: 3922.0 0.0
[I] 2021-04-23 23:34:07.012 (0xFBE7):encoder count: 4584.0 0.0
在控制电机1逆时针转动时候编码器1(第⼀列)逐渐减少则为正常
david@david-MS-7808:~/pibot_ros/pypibot/transport$ python test_motors.py -1000 0
[I] 2021-04-23 23:35:51.575 (0xD657):encoder count: 5402.0 0.0 0.0
[I] 2021-04-23 23:35:52.083 (0xD657):encoder count: 4772.0 0.0 0.0
[I] 2021-04-23 23:35:52.591 (0xD657):encoder count: 4122.0 0.0 0.0
[I] 2021-04-23 23:35:53.099 (0xD657):encoder count: 3473.0 0.0 0.0
[I] 2021-04-23 23:35:53.607 (0xD657):encoder count: 2822.0 0.0 0.0
依次控制各个电机,如果符合结果则⽆需调整,如果某⼀个或⼏个不符合则输⼊下⾯命令打开配置
⻚⾯调整对应编码器参数 encoderX_exchange_flag
调整完成主板需要重新上电⽣效

View File

@ -0,0 +1,285 @@
PIBOT介绍.md 6/2/2019
1. 概述
1.1 PIBOT介绍
1.2 主要应用
1.3 应用场景
1.4外观图
1.5 购买链接
2. 功能简介
3 硬件介绍
3.1 硬件清单
两轮差分Apollo
三轮全向Zeus
四轮全向麦克纳姆轮Hades
四轮差分Hera
大负载2轮差分ApolloX
4. 硬件连接
5. 软件介绍
5.1 框架图
5.2 下位机
5.3 上位机
5.4 通讯方式
5.5 支持模拟器
5.6 Android App
附录
1. 概述
1.1 PIBOT介绍
apollo、zeus、hades、hera和apolloX是派宝机器人科技专门针对ROS开发的移动差分两轮小车、全向三轮小
车、全向四麦克纳姆轮小车、差分四轮驱动和大负载差分两轮小车
1.2 主要应用
ROS机器人教学
机器人创客开发
服务机器人研发
AGV研发
1.3 应用场景
室内平坦地面
1.4外观图
1 / 20
PIBOT介绍.md 6/2/2019
2 / 20
PIBOT介绍.md 6/2/2019
3 / 20
PIBOT介绍.md 6/2/2019
4 / 20
PIBOT介绍.md 6/2/2019
5 / 20
PIBOT介绍.md 6/2/2019
6 / 20
PIBOT介绍.md 6/2/2019
1.5 购买链接
apollo zeus hades hera
2. 功能简介
速度精准控制,支持角速度和线速度并发
PID控制动态调节动态曲线显示
通过USB(USB转串口)与上位机互连通讯运动解算及PID速度环都在下位控制器完成只需要通过串口
命令发送线速度与角速度即可驱动小车底盘,同时可以直接读取小车的实时位置及实时速度信息(上位驱
动都已实现)
9轴IMU滤波算法IMU里程计卡尔曼滤波融合算法支持
基于ROS的激光雷达构建地图SLAM支持键盘、手柄或者手机APP控制建图支持RVIZ选点建图
基于ROS的室内自动导航与避障AMCL的定位
基于摄像头的远程监控及APP同步显示
手机APP控制移动、小车位置设定导航目标设定及路径显示(需要ROS上位机支持即树莓
派/RK3288/RK3399/X86工控机/TK1/TX1/TX2等)
提供完善的文档和丰富的示例
支持模拟器,无需硬件环境可以直接使用使用模拟建图与导航
开源所有代码包括底层STM32F1/STM32F4/Arduino代码上位机树莓派/RK3288/RK3399/X86工控
机/TK1/TX1/TX2中的ROS驱动代码导航配置Android App代码
3 硬件介绍
3.1 硬件清单
两轮差分Apollo
主要机械部件:
7 / 20
PIBOT介绍.md 6/2/2019
小车主体 小车底盘采用双层亚克力+双层主板亚克力。
8 / 20
PIBOT介绍.md 6/2/2019
9 / 20
PIBOT介绍.md 6/2/2019
底盘
主动轮直径65mm优质橡胶轮 2个
从动轮1.5寸尼龙万向轮 1个
减速编码器电机2个495线轮子转动一圈会产生的脉冲信号高达1980个
电子控制部件:
Arduino Mega 2560或SMT32F103主控板或SMT32F407主控板1个
TB6612电机驱动模块1个
9轴IMU模块1个
树莓派/RK3288/RK3399/X86主树莓派/RK3288/RK3399/X86工控机/TK1/TX1/TX2机内嵌ROS
系统)
其他部件:
12V、6800mAh锂电池+充电器: 1个
三轮全向Zeus
主要机械部件:
10 / 20
PIBOT介绍.md 6/2/2019
小车底盘采用4MM双层亚克力+3MM双层电路板基板的亚克力板外观简洁美观安装方便
底盘
主动轮58毫米全向轮
减速编码器电机3个 990线轮子转动一圈会产生的脉冲信号高达3960个
电子控制部件:
11 / 20
PIBOT介绍.md 6/2/2019
SMT32F407主控板1个
TB6612电机驱动模块2个
9轴IMU模块1个
树莓派/RK3288/RK3399/X86工控机内嵌ROS系统
其他部件:
12V、10000mAh锂电池+充电器: 1个
四轮全向麦克纳姆轮Hades
主要机械部件:
小车底盘采用4MM双层亚克力/3mm铝合金+3MM双层电路板基板的亚克力板外观简洁美观安装方
便
底盘
主动轮60毫米麦克纳姆轮
减速编码器电机4个 990线轮子转动一圈会产生的脉冲信号高达3960个
电子控制部件:
SMT32F407主控板1个
TB6612电机驱动模块2个
9轴IMU模块1个
树莓派/RK3288/RK3399/X86工控机内嵌ROS系统
其他部件:
12V、10000mAh锂电池+充电器: 1个
四轮差分Hera
12 / 20
PIBOT介绍.md 6/2/2019
主要机械部件:
小车底盘采用4MM双层亚克力/3mm铝合金+3MM双层电路板基板的亚克力板外观简洁美观安装方
便
底盘
主动轮85毫米橡胶轮
减速编码器电机4个 990线轮子转动一圈会产生的脉冲信号高达3960个
电子控制部件:
SMT32F407主控板1个
TB6612电机驱动模块2个
9轴IMU模块1个
树莓派/RK3288/RK3399/X86工控机内嵌ROS系统
其他部件:
12V、10000mAh锂电池+充电器: 1个
大负载2轮差分ApolloX
主要机械部件:
13 / 20
PIBOT介绍.md 6/2/2019
小车底盘采用多层亚克力或者铝合金结构,外观简洁美观,安装方便!
底盘
主动轮直径95mm优质橡胶轮 2个
从动轮78mm全向轮 1个
减速编码器电机2个990线轮子转动一圈会产生的脉冲信号高达3960个
电子控制部件:
Arduino Mega 2560单片机控制板+扩展板或SMT32F103主控板1个
WSDC2412D双路大功率驱动模块1个
9轴IMU模块1个
树莓派/RK3288/RK3399/TK1/TX1/TX2/X86主机内嵌ROS系统
其他部件:
12V/24V、10000mAh锂电池+充电器: 1个
4. 硬件连接
14 / 20
PIBOT介绍.md 6/2/2019
5. 软件介绍
5.1 框架图
15 / 20
PIBOT介绍.md 6/2/2019
5.2 下位机
下位机可以支持多种不同类型的主控单元
Arduino Mega 2560为主控单元使用Visual studio code+Platform IO进行开发支持
Windows和ubuntu环境
STM32F1为主控单元使用Keil进行开发
STM32F4为主控单元Ubuntu下使用Visual studio code进行开发
下位机可以支持不同的机器人模型,即一套程序适配两轮差分、四轮差分、三轮全向、四轮麦克纳姆
轮,只需修改相应的参数即可
5.3 上位机
采用树莓派/RK3288/RK3399/X86主机/TK1/TX1/TX2等作为上位ROS主控
16 / 20
PIBOT介绍.md 6/2/2019
5.4 通讯方式
下位机及通过串口与树莓派/RK3288/RK3399、X86主机/TK1/TX1/TX2通讯协议为较为灵活的自定义格式
方便新增命令
5.5 支持模拟器
PIBOT包内置了模拟器可以直接运行模拟导航无需小车也可以模拟导航了
5.6 Android App
PIBOT提供Android App App可以完成控制行走、显示地图和显示视频和下发导航命令等功能
附录
apollo建图视频
17 / 20
PIBOT介绍.md 6/2/2019
apollo导航视频
zeus建图视频
18 / 20
PIBOT介绍.md 6/2/2019
zeus导航视频
HadesX建图视频
19 / 20
PIBOT介绍.md 6/2/2019
HadesX导航视频
20 / 20

View File

@ -0,0 +1,253 @@
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
1. 硬件连接与安装
1.1 硬件连接示意图
1.2 安装
1.3 接线
2. 环境搭建与配置
2.1 联网配置
2.2 配置小车端环境
2.3 配置ubuntu主机环境
2.4. 安装Andriod App至手机
3. 建图与导航测试
3.1 测试硬件连接
3.2 建图
3.3 保存地图
3.4 导航
3.5 Android手机APP
1. 硬件连接与安装
1.1 硬件连接示意图
1 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
1.2 安装
PIBOT调试完成后整体交付只需要安装雷达或者摄像头支架至顶层板即可
雷达安装
2 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
思岚A1 使用M2.5螺丝拧好至固定孔位即可
思岚A2/A3 使用M3螺丝拧好至固定孔位即可不同于A1A2/A3的固定孔位使得雷达前后调转照
样可以按照遵循ROS的坐标系规定A2/A3线头执行前方
1.3 接线
电池分别接到电源板和主板给其供电
上位机ROS主板/主机供电主板分2种一种为5V供电另一种为12V的
5V供电通过底板的USB母座提供输出包括树莓派3b/3b+, nanopi(RK3399),可参考下面图
hades 树莓派 rplidar-A2
12V供电通过底板的USB母座提供输出包括Firefly(RK3399),X86工控机可参考下面图
apollo rk3399 rplidar-A1
通讯端口
下位机的通讯口主板通过usb micro口连接至主机(树莓派/RK3288/RK3399/TK1/TX1/TX2/X86
主机)(下图中黄色USB线)
3 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
雷达通讯口A1需要连接好串口板后再通过usb micro口连接至主机(树莓
派/RK3288/RK3399/TK1/TX1/TX2/X86主机)(下图中黄色USB线)
核心板上也有 STM32F1/F4 micro usb口该口作为核心板供电用不是通讯端口无需接线
2. 环境搭建与配置
4 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
硬件列表
下位机 PIBOT Arduino/STM32F1/F4主板
上位机 PIBOT 树莓派/Nanopi
用户主机 Ubuntu的PC
2.1 联网配置
树莓派上电后会释放名字为pibot_ap的无线网,该无线网密码也为pibot_ap, 树莓派IP固定为
192.168.12.1
Ubuntu主机连接到pibot_ap无线网,打开终端输入ifconfig查看ip,可以得到192.168.12.xx即可
2.2 配置小车端环境
# 打开新终端, 远程登入小车 根据提示输入密码pibot
ssh pibot@192.168.12.1 # nanopi则为ssh pi@192.168.12.1 密码为pi
cd ~/pibot_ros
# 同步最新代码 需要使用用户名密码
git pull
#这里根据提示输入小车类型控制板类型雷达类型Machine类型(选0),如下图
./pibot_init_env.sh
source ~/.bashrc
cd ~/pibot_ros/ros_ws/
# 编译PIBOT
catkin_make
source ~/.bashrc
2.3 配置ubuntu主机环境
复制pibot_ros.tar.bz2至主目录,打开终端输入
# 打开新终端
cd ~
# 解压
5 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
tar jxvf pibot_ros.tar.bz2
cd ~/pibot_ros
#这里根据提示输入小车类型控制板类型雷达类型Machine类型(选1), 小车IP(192.168.12.1)
如下图
./pibot_init_env.sh
source ~/.bashrc
cd ~/pibot_ros/ros_ws/
# 编译PIBOT
catkin_make
source ~/.bashrc
2.4. 安装Andriod App至手机
3. 建图与导航测试
3.1 测试硬件连接
用户主机 上位机 a. 在
通过ssh连接PIBOT 输入命令ls /dev/pibot -l检查主板是否连接
# 打开新终端, 远程登入小车
ssh pibot@192.168.12.1 # 根据提示输入密码`pibot`
ls /dev/pibot -l
正常连接输出如下图
b.继续输入ls /dev/ydlidar -l或者ls /dev/rplidar -l(eai输入前者思岚A1/A2/A3输入后者)
检查激光雷达是否连接
6 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
3.2 建图
通过ssh连接PIBOT , 输入运行pibot_gmapping或者roslaunch
用户主机 上位机 a.在
pibot_navigation gmapping.launch启动建图节点收到最后输出odom receiced表示正常
# 打开新终端, 远程登入小车 根据提示输入密码pibot
ssh pibot@192.168.12.1 # nanopi则为ssh pi@192.168.12.1 密码为pi
pibot_gmapping # 或者roslaunch pibot_navigation gmapping.launch
用户主机 b.在 的UBUNTU虚拟机终端输入pibot_view或者roslaunch pibot_navigation
view_nav.launch启动RViz节点,查看地图
# 打开新终端
pibot_view # 或者roslaunch pibot_navigation view_nav.launch
7 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
用户主机 上位机 c. 在
通过ssh连接PIBOT 输入pibot_control或者roslaunch pibot
keyboard_teleop.launch启动控制节点根据提示输入q/z增减速度输入i/,控制前进后退,输入
j/l控制左转右转。控制小车在房间移动同时观察虚拟机中地图构建情况
# 打开新终端, 远程登入小车
pibot_control # 或者roslaunch pibot keyboard_teleop.launch
8 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
3.3 保存地图
通过ssh连接PIBOT
用户主机 上位机 在 ,输入
# 打开新终端, 远程登入小车 根据提示输入密码pibot
ssh pibot@192.168.12.1 # nanopi则为ssh pi@192.168.12.1 密码为pi
roscd pibot_navigation/maps
#(xxx)为设置新建好的地图名称
rosrun map_server map_saver -f xxx #或者roslaunch pibot_navigation save_map.launch
map_name:=xxx
3.4 导航
Ctrl+C退出所有的程序和终端
通过ssh连接PIBOT ,输入运行pibot_navigation或者roslaunch
用户主机 上位机 a.在
pibot_navigation nav.launch map_name:=xxx.yaml启动导航节点收到最后输出odom receiced
表示正常(xxx为之前新建好的地图名称)
# 打开新终端, 远程登入小车 根据提示输入密码pibot
ssh pibot@192.168.12.1 # nanopi则为ssh pi@192.168.12.1 密码为pi
9 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
#`xxx`为之前新建好的地图名称
pibot_navigation # 或者roslaunch pibot_navigation nav.launch map_name:=xxx.yaml
用户主机 b.在 中输入pibot_view或者roslaunch pibot_navigation view_nav.launch启动RViz节
点,查看地图
# 打开新终端
pibot_view # 或者roslaunch pibot_navigation view_nav.launch
位置和方向 c.在RViz中设置小车当前
10 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
位置和方向 d.在RViz中设置小车目标 ,即可开始导航
3.5 Android手机APP
手机连接无线网pibot_ap,密码也为pibot_ap执行上面 3.2 建图或者3.4 导航
上位机 安装好apk(网盘/源码/Android App目录)至手机打开程序修改为PIBOT 的IP(192.168.12.1),点击
CONNECT
11 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
12 / 13
PIBOT使用手册-主机Ubuntu, 车载上位机树莓派&nanopi.md 7/20/2019
可以显示地图、设置位置和设置目标点、显示视频和控制行走
上位机 *显示视频需要在PIBOT 连接摄像头以及开启相关程序
usb 摄像头(不包括kinect等深度摄像头)
# 打开新终端, 远程登入小车 根据提示输入密码pibot
ssh pibot@192.168.12.1 # nanopi则为ssh pi@192.168.12.1 密码为pi
roslaunch pibot usb_camera.launch
13 / 13

View File

@ -0,0 +1,254 @@
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
1. 硬件连接与安装
1.1 硬件连接示意图
1.2 安装
1.3 接线
2. 环境搭建与配置
2.1 联网配置
2.2 配置小车端环境
2.3. 配置虚拟机环境
2.4. 安装Androd App至手机
3. 建图与导航测试
3.1 测试硬件连接
3.2 建图
3.3 保存地图
3.4 导航
3.5 Android手机APP
1. 硬件连接与安装
1.1 硬件连接示意图
1 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
1.2 安装
PIBOT调试完成后整体交付只需要安装雷达或者摄像头支架至顶层板即可
雷达安装
2 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
思岚A1 使用M2.5螺丝拧好至固定孔位即可
思岚A2/A3 使用M3螺丝拧好至固定孔位即可不同于A1A2/A3的固定孔位使得雷达前后调转照
样可以按照遵循ROS的坐标系规定A2/A3线头执行前方
1.3 接线
电池分别接到电源板和主板给其供电
上位机ROS主板/主机供电主板分2种一种为5V供电另一种为12V的
5V供电通过底板的USB母座提供输出包括树莓派3b/3b+, nanopi(RK3399),可参考下面图
hades 树莓派 rplidar-A2
12V供电通过底板的USB母座提供输出包括Firefly(RK3399),X86工控机可参考下面图
apollo rk3399 rplidar-A1
通讯端口
下位机的通讯口主板通过usb micro口连接至主机(树莓派/RK3288/RK3399/TK1/TX1/TX2/X86
主机)(下图中黄色USB线)
3 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
雷达通讯口A1需要连接好串口板后再通过usb micro口连接至主机(树莓
派/RK3288/RK3399/TK1/TX1/TX2/X86主机)(下图中黄色USB线)
核心板上也有 STM32F1/F4 micro usb口该口作为核心板供电用不是通讯端口无需接线
2. 环境搭建与配置
4 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
硬件列表
下位机 PIBOT Arduino/STM32F1/F4主板
上位机 PIBOT 树莓派/Nanopi
用户主机 Windows的PC,安装Vmvare虚拟机,和XShell工具
2.1 联网配置
树莓派上电后会释放名字为pibot_ap的无线网,该无线网密码也为pibot_ap, 树莓派IP固定为
192.168.12.1
Windows主机连接到pibot_ap无线网
Vmvare虚拟机桥接到无线网卡,具体虚拟机网络配置请参考Windows下安装Ubuntu虚拟机及ROS,配置完
成后打开终端输入ifconfig查看ip,可以得到192.168.12.xx即可
2.2 配置小车端环境
Windows下通过XShell工具远程登入ROS上位机请参考XShell怎么登陆linux
5 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
6 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
PIBOT上位机 登入用户名 登入密码 IP
树莓派3B/3B+ pibot pibot 192.168.12.1
Nanopi RK3399 pi pi 192.168.12.1
# 在Xshell登入后的终端输入
cd ~/pibot_ros
# 同步最新代码 需要使用用户名密码
git pull
#这里根据提示输入小车类型控制板类型雷达类型Machine类型(选0),如下图
./pibot_init_env.sh
source ~/.bashrc
cd ~/pibot_ros/ros_ws/
# 编译PIBOT
catkin_make
source ~/.bashrc
2.3. 配置虚拟机环境
复制pibot_ros.tar.bz2至主目录,打开终端输入
# 虚拟机中打开新终端
cd ~
# 解压
tar jxvf pibot_ros.tar.bz2
cd ~/pibot_ros
#这里根据提示输入小车类型控制板类型雷达类型Machine类型(选1), 小车IP(192.168.12.1)
如下图
./pibot_init_env.sh
source ~/.bashrc
cd ~/pibot_ros/ros_ws/
# 编译PIBOT
7 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
catkin_make
source ~/.bashrc
2.4. 安装Androd App至手机
3. 建图与导航测试
3.1 测试硬件连接
上位机 a. Windows下通过XShell工具远程登入PIBOT ,输入命令ls /dev/pibot -l检查主板是否连接
正常连接输出如下图
b.继续输入ls /dev/ydlidar -l或者ls /dev/rplidar -l(eai x4/g4输入前者思岚A1/A2/A3输入
后者),检查激光雷达是否连接
3.2 建图
上位机 a.在Windows通过XShell工具远程登入PIBOT , 输入运行pibot_gmapping或者roslaunch
pibot_navigation gmapping.launch启动建图节点收到最后输出odom receiced表示正常
# 在Xshell登入后的终端输入
pibot_gmapping # 或者roslaunch pibot_navigation gmapping.launch
8 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
b.在虚拟机终端输入pibot_view或者roslaunch pibot_navigation view_nav.launch启动RViz节
点,查看地图
# 虚拟机中打开新终端
pibot_view # 或者roslaunch pibot_navigation view_nav.launch
9 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
上位机 c. Windows下通过XShell工具远程登入PIBOT 输入pibot_control或者roslaunch pibot
keyboard_teleop.launch启动控制节点根据提示输入q/z增减速度输入i/,控制前进后退,输入
j/l控制左转右转。控制小车在房间移动同时观察虚拟机中地图构建情况
# 在Xshell登入后的终端输入
pibot_control # 或者roslaunch pibot keyboard_teleop.launch
10 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
3.3 保存地图
上位机 Windows下通过XShell工具远程登入PIBOT ,输入
11 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
# 在Xshell登入后的终端输入
roscd pibot_navigation/maps
#(xxx)为设置新建好的地图名称
rosrun map_server map_saver -f xxx #或者roslaunch pibot_navigation save_map.launch
map_name:=xxx
3.4 导航
Ctrl+C退出所有的程序和终端
上位机 a.Windows下通过XShell工具远程登入PIBOT , 输入运行pibot_navigation或者roslaunch
pibot_navigation nav.launch map_name:=xxx.yaml启动导航节点收到最后输出odom receiced
表示正常(xxx为之前新建好的地图名称)
# 在Xshell登入后的终端输入
#`xxx`为之前新建好的地图名称
pibot_navigation # 或者roslaunch pibot_navigation nav.launch map_name:=xxx.yaml
b.在虚拟机中输入pibot_view或者roslaunch pibot_navigation view_nav.launch启动RViz节
点,查看地图
12 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
# 虚拟机中打开新终端
pibot_view # 或者roslaunch pibot_navigation view_nav.launch
位置和方向 c.在b中虚拟机打开的RViz中设置小车当前
13 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
位置和方向 d.在b中虚拟机打开的RViz中设置小车目标 ,即可开始导航
3.5 Android手机APP
上位机 连接与PIBOT 同一路由器(树莓派/nanopi rk3399则为其释放热点pibot_ap),开启建图或者导航
程序
上位机 安装好apk(网盘/源码/Android App目录)至手机打开程序修改为PIBOT 的IP点击CONNECT
14 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
15 / 16
PIBOT使用手册-主机Windows, 车载上位机树莓派&nanopi.md 7/20/2019
可以显示地图、设置位置和设置目标点、显示视频和控制行走
上位机 *显示视频需要在PIBOT 连接摄像头以及开启相关程序
usb 摄像头(不包括kinect等深度摄像头)
# 在Xshell登入后的终端输入
roslaunch pibot usb_camera.launch
16 / 16

View File

@ -0,0 +1,92 @@
PIBOT常见问题Q&A.md 6/17/2019
1. ROS代码编译问题
2. 权限问题
3. 参数问题
4. 雷达启动不转health state 异常
5. timeout
6. 编译缺少包
7. 无法启动Rviz
1. ROS代码编译问题
如果从windows拷贝的源码编译可能遇到以下问题
No such file or directory
这是生成动态参数时因为windows和Linux下换行不一致导致的使用Visual Code打开
pibot\cfg\CalibrateAngular.cfg
1/4
PIBOT常见问题Q&A.md 6/17/2019
右下角点击CRLF修改为LF并保存同样的还有pibot\cfg\CalibrateLinear.cfg 和
pibot_bringup\cfg\pibot_parameter.cfg
2. 权限问题
Permission denied
问题原因与上面类似解决方法就是添加执行权限chmod +x pibot\cfg\CalibrateAngular.cfg同样的还有
pibot\cfg\CalibrateLinear.cfg 和pibot_bringup\cfg\pibot_parameter.cfg
3. 参数问题
Invalid arg
问题原因是没有设置环境变量 设置为支持的选项即可
如:export PIBOT_LIDAR=rplidar或者export PIBOT_LIDAR=eai-x4 类似还有export
PIBOT_MODEL=apollo或者export PIBOT_MODEL=zeus
需要执行pibot_init_env.sh设置环境变量
4. 雷达启动不转health state 异常
多半是你这usb供电问题或者可能电池电量不足电池需要充电
5. timeout
2/4
PIBOT常见问题Q&A.md 6/17/2019
检查UBS是否插好
ls /dev/pibot
- 另外检查波特率是否正确
roscd pibot_bringup/params
vi base_param.yaml # 包括base_param_xx.yaml
修改波特率stm32f1/arduino为115200 stm32f4都为921600
6. 编译缺少包
3/4
PIBOT常见问题Q&A.md 6/17/2019
如图类似错误,提示Could not find a package configuration file provided by "xxxx" with any,缺
少ros的相关功能包导致的,需要通过apt-get install安装,如果不知道具体名称可以使用apt-cache search
查找,
apt-cache search roslint
如 选择想要的
ros版本包安装即可
sudo apt-get install ros-kinetic-roslint
如果search结果太多可以添加"ros-{ROS_DIST}-xxx"搜索 例如缺少serial包,如果apt-cache search
serial会显示较多选项,我们可以apt-cache search ros-kinetic-serial即可以定位准确的安装的
包名称
7. 无法启动Rviz
QXcbConnection: Could not connect to display
在XShell等工具ssh登入是无法启动Rviz(pibot_view或者roslaunch pibot_navigation
view_nav.launch)的,需要在Ubuntu主机或者虚拟机建立多机通讯后(执行pibot_init_env),直接在
Ubuntu主机或者虚拟机启动
4/4

View File

@ -0,0 +1,121 @@
1.树莓派(raspberry pi 3b)安装ROS Kinetic Kame.md 10/20/2018
1.概述
2.刷入pibot_ros_kinetic镜像
3 逐步安装ROS
3.1下载ubuntu
3.2 刷入ubuntu
3.3安装ROS
4.测试
5.交换分区的设置
5.1 查看交换分区
5.2 创建交换分区
5.2.1 创建交换分区
5.2.2 设置重启生效
6.编译PIBOT驱动
6.1拷贝源码包至树莓派
6.2解压编译
1.概述
可以使用PIBOT提供镜像,预装了ROS Kinetic Kame而且已经配置好环境如果你想体验下安装过程也可
以先输入官方UBUNTU后再安装ROS可以直接跳过步骤
2.刷入pibot_ros_kinetic镜像
镜像我们需要使用工具Win32 Disk Mangaer及ROS镜像 PIBOT网盘中提供了相关工具与
点击Write按钮然后点一下Yes确定操作开始系统写入 选择镜像和相应的盘符,
1/5
1.树莓派(raspberry pi 3b)安装ROS Kinetic Kame.md 10/20/2018
等待安装完成即可
刷完看到容量变小了,不用担心,正常情况 用户名密码都是pibot
3 逐步安装ROS
使用PIBOT提供的镜像可以跳过该步骤同时实际上面生成的镜像就是执行了以下步骤而已
3.1下载ubuntu
从百度网盘或者官网下载Ubuntu mate或者Lubuntu
3.2 刷入ubuntu
使用该镜像刷入ubuntu步骤同2刷入镜像
3.3安装ROS
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main"
> /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key
421C365BD9FF1F717815A3895523BAEEB01FA116
sudo apt-get update
sudo apt-get -y install ros-kinetic-ros-base ros-kinetic-slam-gmapping ros-
kinetic-navigation ros-kinetic-xacro ros-kinetic-yocs-velocity-smoother ros-
kinetic-robot-state-publisher ros-kinetic-joint-state-publisher ros-kinetic-
teleop-twist-*
sudo rosdep init
rosdep update
2/5
1.树莓派(raspberry pi 3b)安装ROS Kinetic Kame.md 10/20/2018
echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
复制上面脚本保存至文件install_ros_kinetic.sh,执行sh install_ros_kinetic.sh
4.测试
安装完成运行roscore可以看到下面输出即可标识完成ros安装
5.交换分区的设置
5.1 查看交换分区
free -m
** 如果显示下图标识已经有2G的交换空间无需再次设置**
5.2 创建交换分区
5.2.1 创建交换分区
3/5
1.树莓派(raspberry pi 3b)安装ROS Kinetic Kame.md 10/20/2018
sudo mkdir /opt/image
cd /opt/image/
sudo touch swap
sudo dd if=/dev/zero of=/opt/image/swap bs=1024 count=2048000
sudo mkswap /opt/image/swap
sudo swapon /opt/image/swap
dd命令执行时间较长
再次查看交换分区 free -m
5.2.2 设置重启生效
sudo vim /etc/fstab 最后行添加 /opt/image/swap /swap swap defaults 0 0
重启后再次查看 free -m
6.编译PIBOT驱动
6.1拷贝源码包至树莓派
4/5
1.树莓派(raspberry pi 3b)安装ROS Kinetic Kame.md 10/20/2018
6.2解压编译
tar jxvf pibot_ros_ws.tar.bz2
cd pibot_ros/ros_ws/
catkin_make
5/5

View File

@ -0,0 +1,156 @@
10. PIBOT的driver的实现及动态PID调节.md 10/20/2018
1.PIBOT的ros中driver
1.1目标
1.2串口数据发送与接收
1.3subscribe cmd_vel
1.4publish odom
2.动态PID调节
2.1概述
2.2配置
2.3PID曲线
1.PIBOT的ros中driver
1.1目标
串口数据发送与接收
订阅cmd_vel topic下发至下位机
根据下位机的反馈发布odom topic和odom tf
1.2串口数据发送与接收
boost::shared_ptr<Transport> trans;
boost::shared_ptr<Dataframe> frame;
trans = boost::make_shared<Serial_transport>(bdg.port, bdg.buadrate);
frame = boost::make_shared<Simple_dataframe>(trans.get());
这里实现一个上位机的Serial_transport和一个Simple_dataframe即可完成
1.3subscribe cmd_vel
cmd_vel_sub = nh.subscribe(bdg.cmd_vel_topic, 1000, &BaseDriver::cmd_vel_callback,
this);
void BaseDriver::cmd_vel_callback(const geometry_msgs::Twist& vel_cmd)
{
ROS_INFO_STREAM("cmd_vel:[" << vel_cmd.linear.x << " " << vel_cmd.linear.y <<
" " << vel_cmd.angular.z << "]");
Data_holder::get()->velocity.v_liner_x = vel_cmd.linear.x*100;
Data_holder::get()->velocity.v_liner_y = vel_cmd.linear.y*100;
Data_holder::get()->velocity.v_angular_z = vel_cmd.angular.z*100;
1/5
10. PIBOT的driver的实现及动态PID调节.md 10/20/2018
need_update_speed = true;
}
void BaseDriver::update_speed()
{
if (need_update_speed)
{
ROS_INFO_STREAM("update_speed");
need_update_speed = !(frame->interact(ID_SET_VELOCITY));
}
}
代码容易理解,订阅消息,回调函数中设置标识,循环中根据标识下发设置指令
1.4publish odom
9. base_link、odom、map关系中有个odom publisher的例子基本拿过来就可以
void BaseDriver::update_odom()
{
frame->interact(ID_GET_ODOM);
ros::Time current_time = ros::Time::now();
float x = Data_holder::get()->odom.x*0.01;
float y = Data_holder::get()->odom.y*0.01;
float th = Data_holder::get()->odom.yaw*0.01;
float vxy = Data_holder::get()->odom.v_liner_x*0.01;
float vth = Data_holder::get()->odom.v_angular_z*0.01;
//ROS_INFO("odom: x=%.2f y=%.2f th=%.2f vxy=%.2f vth=%.2f", x, y ,th,
vxy,vth);
geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
//send the transform
odom_trans.header.stamp = current_time;
odom_trans.transform.translation.x = x;
odom_trans.transform.translation.y = y;
odom_trans.transform.rotation = odom_quat;
odom_broadcaster.sendTransform(odom_trans);
//publish the message
odom.header.stamp = current_time;
odom.pose.pose.position.x = x;
odom.pose.pose.position.y = y;
odom.pose.pose.orientation = odom_quat;
2/5
10. PIBOT的driver的实现及动态PID调节.md 10/20/2018
odom.twist.twist.linear.x = vxy;
odom.twist.twist.angular.z = vth;
odom_pub.publish(odom);
}
2.动态PID调节
2.1概述
底层提供了各个电机输入输出的参见协议ROS机器人底盘(3)-通讯协议
2.2配置
params.yaml打开out_pid_debug_enable
port: /dev/pibot
buadrate: 115200
base_frame: base_link
# topic
cmd_vel_topic: cmd_vel
#pid debug
out_pid_debug_enable: true
2.3PID曲线
运行roslaunch pibot_bringup bringup.launch rosrun rqt_plot rqt_plot /motor1_input
/motor1_output即可展示出实时曲线
3/5
10. PIBOT的driver的实现及动态PID调节.md 10/20/2018
控制之前最好先架起小车
为了稳定给出输入直接向cmd_vel发消息
x: 0.15
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0" -r 20
观察曲线变化输入输出基本一致输出振幅较小即可;否则打开配置页面 rosrun rqt_reconfigure
rqt_reconfigure
4/5
10. PIBOT的driver的实现及动态PID调节.md 10/20/2018
调整PID参数重复上一步操作直到输出较为稳定即可
5/5

View File

@ -0,0 +1,222 @@
11. PIBOT的控制及校准.md 10/20/2018
1. 运动控制
1.1键盘控制
1.2手柄控制
连接手柄
测试手柄
手柄控制
1.3App控制
2. 里程校准
2.1linear_calibrate
启动校准
调整参数
2.2angular_calibrate
启动校准
调整参数
3备注
1. 运动控制
1.1键盘控制
启动驱动roslaunch pibot_bringup bringup.launch,下同
roslaunch pibot keyboard_teleop.launch
Zues和差分的Apollo小车 根据提示可以控制全向的
1.2手柄控制 1/7
连接手柄
11. PIBOT的控制及校准.md 10/20/2018
连接手柄至电脑或者Raspberry Pi 3 ls /dev/input/js* -l
测试手柄
sudo jstest /dev/input/js0
手柄控制
Zeus小车 roslaunch pibot joystick.launch Apollo小车 roslaunch pibot joystick-
holonomic.launch
2/7
11. PIBOT的控制及校准.md 10/20/2018
joystick.launch
<launch>
<arg name="joy_config" default="joystick" />
<arg name="joy_dev" default="/dev/input/js0" />
<arg name="config_filepath" default="$(find pibot)/config/$(arg
joy_config).config.yaml" />
<node pkg="joy" type="joy_node" name="joy_node">
<param name="dev" value="$(arg joy_dev)" />
<param name="deadzone" value="0.3" />
<param name="autorepeat_rate" value="20" />
</node>
<node pkg="teleop_twist_joy" name="teleop_twist_joy" type="teleop_node"
output="screen">
<rosparam command="load" file="$(arg config_filepath)" />
</node>
</launch>
joystick.config.yaml
axis_linear: 1 # Left thumb stick vertical
scale_linear: 0.2
scale_linear_turbo: 1.5
3/7
11. PIBOT的控制及校准.md 10/20/2018
axis_angular: 0 # Left thumb stick horizontal 按
scale_angular: 1.0
enable_button: 6 # Left trigger button
enable_turbo_button: -1
1.3App控制
2. 里程校准
2.1linear_calibrate
启动校准
rosrun pibot calibrate_linear.py
照提示启动rqt_reconfigure
切换到calibrate_linear选项 勾选start_test即可开始测试。小车按照设置的速度(speed),向前运动设定
的距离(test_distance),误差不超过设定值(tolerance)
odom_linear_scale_correction为比例参数设为默认1即可
调整参数
4/7
11. PIBOT的控制及校准.md 10/20/2018
用尺子测量小车实际行径距离如果与test_distance相差较大则需要调整相关参数 对于2轮差分Apollo
differential.h
void get_odom(struct Odom* odom, float* motor_dis, unsigned long interval)
{
float dxy_ave = (-motor_dis[0] + motor_dis[1]) / 2.0;
float dth = (motor_dis[0] + motor_dis[1]) / (2* body_radius);
float vxy = 1000 * dxy_ave / interval;
float vth = 1000 * dth / interval;
odom->vel_x = vxy;
odom->vel_y = 0;
odom->vel_z = vth;
float dx = 0, dy = 0;
if (motor_dis[0] != motor_dis[1])
{
dx = cos(dth) * dxy_ave;
dy = -sin(dth) * dxy_ave;
odom->x += (cos(odom->z) * dx - sin(odom->z) * dy);
odom->y += (sin(odom->z) * dx + cos(odom->z) * dy);;
}
if (motor_dis[0] + motor_dis[1] != 0)
odom->z += dth;
}
单独向前是motor_dis[0] + motor_dis[1]应该为0
左轮向后motor_dis[0]为正,右轮向前为正
容易得到odom->x因为(-motor_dis[0] + motor_dis[1]) / 2.0而motor_dis[0], motor_dis[1]跟一周
编码器个数和轮子的直接相关,在假定一周编码器个数恒定情况下,即只与轮子直接相关
这也是为什么先进行linear_calibrate的原因
如果实际测量值<test_distance应该如何调整轮子直径调大调小
即例如实际行走了0.8m计算出来的为1modom->x大了即用来计算直径的参数大了应该减小直
径。
2.2angular_calibrate
启动校准
rosrun pibot calibrate_angular.py
出现 错误需要 ImportError: No module named PyKDL sudo apt-get install ros-kinetic-kdl-
parser-py
5/7
11. PIBOT的控制及校准.md 10/20/2018
按照提示启动rqt_reconfigure
切换到calibrate_angular选项 勾选start_test即可开始测试。小车按照设置的速度(speed),旋转设定的
角度(test_angle),误差不超过设定值(tolerance)
odom_linear_scale_correction为比例参数设为默认1即可
调整参数
观察设计旋转的角度如果与test_angle相差较大则需要调整相关参数 对于2轮差分Apollo
differential.h
void get_odom(struct Odom* odom, float* motor_dis, unsigned long interval)
{
float dxy_ave = (-motor_dis[0] + motor_dis[1]) / 2.0;
float dth = (motor_dis[0] + motor_dis[1]) / (2* body_radius);
float vxy = 1000 * dxy_ave / interval;
float vth = 1000 * dth / interval;
odom->vel_x = vxy;
odom->vel_y = 0;
odom->vel_z = vth;
float dx = 0, dy = 0;
if (motor_dis[0] != motor_dis[1])
{
dx = cos(dth) * dxy_ave;
dy = -sin(dth) * dxy_ave;
odom->x += (cos(odom->z) * dx - sin(odom->z) * dy);
odom->y += (sin(odom->z) * dx + cos(odom->z) * dy);;
6/7
11. PIBOT的控制及校准.md 10/20/2018
}
if (motor_dis[0] + motor_dis[1] != 0)
odom->z += dth;
}
旋转是odom->z为dth累加即 (motor_dis[0] + motor_dis[1]) / (2* body_radius)
先前完成了linear_calibrate(motor_dis[0] + motor_dis[1])就固定了现在odom->z就只与
body_radius相关且为反比关系
如果实际观察角度<test_angle应该如何调整轮子间距调大调小
即例如实际行走了345°计算出来的为360°odom->z大了即body_radius小了(反比),应该增加
body_radius。
3备注
上述为差分轮apollo的参数调整zues 、hades和hera也类似
如果实在搞不清楚应该调大参数还是调小,那就调整参数直接测试,观察结果,这样直接也同样高
效!
7/7

View File

@ -0,0 +1,86 @@
12.激光雷达简介.md 10/20/2018
1.概述
2.激光雷达
3.ROS中使用
rplidar参数分析
1.概述
机器人导航中,激光雷达、摄像机、声纳、红外线、碰撞开关等传感器就如同人的器官一样,激光雷达常用的
一种。
2.激光雷达
激光雷达通常会有一部分“盲区”。使用激光雷达返回的数据通常可以描绘出一幅极坐标图,极点位于雷达扫描
中心0-360°整周圆由扫描区域及盲区组成。在扫描区域中激光雷达在每个角度分辨率对应位置解析出的距离
值会被依次连接起来,这样,通过极坐标表示就能非常直观地看到周围物体的轮廓,激光雷达扫描范围示意图
可以参见下图。
激光雷达通常有四个性能衡量指标:测距分辨率、扫描频率(有时也用扫描周期)、角度分辨率及可视范围。
测距分辨率衡量在一个给定的距离下测距的精确程度通常与距离真实值相差在5-20mm扫描频率衡量激光
雷达完成一次完整扫描的快慢通常在10Hz及以上角度分辨率直接决定激光雷达一次完整扫描能返回多少个
样本点;可视范围指激光雷达完整扫描的广角,可视范围之外即为盲区。
3.ROS中使用
ROS中激光雷达消息为sensor_msgs/LaserScan
rosmsg show sensor_msgs/LaserScan显示如下
1/2
12.激光雷达简介.md 10/20/2018
std_msgs/Header header
uint32 seq
time stamp
string frame_id
float32 angle_min
float32 angle_max
float32 angle_increment
float32 time_increment
float32 scan_time
float32 range_min
float32 range_max
float32[] ranges
float32[] intensities
seq是消息的顺序标识发布节点在发布消息时会自动累加
stamp 是消息中与数据相关联的时间戳
frame_id 是消息中与数据相关联的参考系id
angle_min 起始角度rad
angle_max 结束角度rad
angle_increment 角度分辨率rad
time_increment 每个角度扫描时间
scan_time 扫描间隔
range_min 最小距离
range_max 最大距离
ranges 各个角度的距离
intensities 各个角度的强度
rplidar参数分析
官方的链接rplidar驱动实现
<launch> pkg="rplidar_ros" type="rplidarNode"
<node name="rplidarNode"
type="string" value="/dev/ttyUSB0"/>
output="screen"> type="int" value="115200"/>
<param name="serial_port" type="string" value="laser"/>
<param name="serial_baudrate" type="bool" value="false"/>
<param name="frame_id" type="bool" value="true"/>
<param name="inverted"
<param name="angle_compensate"
</node>
</launch>
frame_id 参考系id (如果我们urdf的模型与安装中的一致就可以直接发布到laser_link这也是我们为
什么导出laser_link的原因)
inverted 上下倒置安装使用该标记
angle_compensate 顺时针逆时针标记
2/2

View File

@ -0,0 +1,92 @@
13.move_base介绍1.md 10/20/2018
1.概述
2.指定导航点
1.概述
movebase利用actionlib包提供使得我们的机器人到达一个设置的目标点的包 先上一张官方经典的图
图中蓝色部分与特定机器人平台有关,灰色部分是可选的,白色部分是必须的
可以看到amcl和map_server都是不必须的
机器人平台输入相关传感器、里程计及tf信息
输入为一个goal(目标点坐标)
输出一个cmd_vel速度
2.指定导航点
1/4
13.move_base介绍1.md 10/20/2018
首先我们查看下MoveBaseActionGoal的定义 rosmsg show MoveBaseActionGoal
运行导航逻辑后rostopic echo /move_base/goal, 点击2D Nav Goal,输出
2/4
13.move_base介绍1.md 10/20/2018
结合上面2个输出可以看到geometry_msgs/PoseStamped中
frame_id为参考坐标系
goal为目标点的位置(pose)与姿态(orientation)
官方有个例子navigation_tutorials
#include <ros/ros.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <actionlib/client/simple_action_client.h>
#include <tf/transform_datatypes.h>
#include <boost/thread.hpp>
typedef actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>
MoveBaseClient;
void spinThread(){
ros::spin();
}
int main(int argc, char** argv){
ros::init(argc, argv, "simple_navigation_goals");
ros::NodeHandle n;
boost::thread spin_thread = boost::thread(boost::bind(&spinThread));
MoveBaseClient ac("pose_base_controller");
//give some time for connections to register
sleep(2.0);
move_base_msgs::MoveBaseGoal goal;
//we'll send a goal to the robot to move 2 meters forward
goal.target_pose.header.frame_id = "base_link";
goal.target_pose.header.stamp = ros::Time::now();
goal.target_pose.pose.position.x = 2.0;
goal.target_pose.pose.position.y = 0.2;
goal.target_pose.pose.orientation = tf::createQuaternionMsgFromYaw(M_PI);
ROS_INFO("Sending goal");
ac.sendGoal(goal);
ac.waitForResult();
if(ac.getState() == actionlib::SimpleClientGoalState::SUCCEEDED)
ROS_INFO("Hooray, the base moved 2 meters forward");
else
ROS_INFO("The base failed to move forward 2 meters for some reason");
3/4
13.move_base介绍1.md 10/20/2018
return 0;
}
以base_link为参考坐标系, 目标就是前面2.0(x = 2.0)左0.2(y = 0.2)方向选择180°(orientation =
tf::createQuaternionMsgFromYaw(M_PI))
4/4

View File

@ -0,0 +1,418 @@
14.move_base介绍2.md 10/21/2018
1.配置导航参数
最简单的配置
fake_move_base_with_out_map.launch
1.costmap_common_params_apollo.yaml
2.local_costmap_params_withoutmap.yaml
3.global_costmap_params_withoutmap.yaml
4.dwa_local_planner_params_apollo.yaml
5.move_base.yaml
6.global_planner_params.yaml
运行结果
配置分析
1.配置导航参数
前文(13.move_base介绍(1))讲了move_base的简单的基础,本文将详细分析下如何配置move_base参数
最简单的配置
再次引用该图
map_server和amcl都是不必须的我们就首先配置一个没有map的move_base
fake_move_base_with_out_map.launch
<launch>
<include file="$(find pibot_bringup)/launch/robot.launch"/>
<param name="use_sim_time" value="false" />
<include file="$(find
pibot_nav)/launch/include/move_base_with_out_map.launch.xml" />
</launch>
robot.launch为Pibot的驱动其他底盘可替换为自己的驱动
move_base_with_out_map.launch.xml
1 / 10
14.move_base介绍2.md 10/21/2018
<launch>
<arg name="model" default="$(env PIBOT_MODEL)" doc="model type [apollo, zeus]"/>
<node pkg="move_base" type="move_base" respawn="false" name="move_base"
output="screen" clear_params="true">
<rosparam file="$(find pibot_nav)/params/costmap_common_params_$(arg
model).yaml" command="load" ns="global_costmap" />
<rosparam file="$(find pibot_nav)/params/costmap_common_params_$(arg
model).yaml" command="load" ns="local_costmap" />
<rosparam file="$(find pibot_nav)/params/local_costmap_params_withoutmap.yaml"
command="load" />
<rosparam file="$(find
pibot_nav)/params/global_costmap_params_withoutmap.yaml" command="load" />
<rosparam file="$(find pibot_nav)/params/dwa_local_planner_params_$(arg
model).yaml" command="load" />
<rosparam file="$(find pibot_nav)/params/move_base_params.yaml" command="load"
/>
<rosparam file="$(find pibot_nav)/params/global_planner_params.yaml"
command="load" />
</node>
</launch>
配置文件详情
1.costmap_common_params_apollo.yaml
max_obstacle_height: 0.60 # assume something like an arm is mounted on top of the
robot
# Obstacle Cost Shaping (http://wiki.ros.org/costmap_2d/hydro/inflation)
#robot_radius: 0.16 # distance a circular robot should be clear of the obstacle
(kobuki: 0.18)
footprint: [[0.10, -0.07], [0.10, 0.18], [-0.10, 0.18], [-0.10, -0.07]]
# footprint: [[x0, y0], [x1, y1], ... [xn, yn]] # if the robot is not circular
map_type: voxel
obstacle_layer: true #true needed for disabling global path planning
enabled: 0.6
max_obstacle_height: 0.0 2 / 10
origin_z: 0.2
z_resolution: 2
z_voxels: 15
unknown_threshold: 0
mark_threshold: 1
combination_method: true
track_unknown_space:
through unknown space
obstacle_range: 2.5
14.move_base介绍2.md 10/21/2018
raytrace_range: 3.0
origin_z: 0.0
z_resolution: 0.2
z_voxels: 2
publish_voxel_map: false
observation_sources: scan
scan:
data_type: LaserScan
topic: scan
inf_is_valid: true
marking: true
clearing: true
min_obstacle_height: 0.05
max_obstacle_height: 0.35
#bump:
#data_type: PointCloud2
#topic: mobile_base/sensors/bumper_pointcloud
#marking: true
#clearing: false
#min_obstacle_height: 0.0
#max_obstacle_height: 0.15
# for debugging only, let's you see the entire voxel grid
#cost_scaling_factor and inflation_radius were now moved to the inflation_layer ns
inflation_layer:
cost_scaling_factor: 2.5 # exponential rate at which the obstacle cost drops
off (default: 10)
inflation_radius: 1.2 # max. distance from an obstacle at which costs are
incurred for planning paths.
static_layer: false
enabled:
2.local_costmap_params_withoutmap.yaml
local_costmap: type: "costmap_2d::VoxelLayer"}
global_frame: /odom type: "costmap_2d::InflationLayer"}
robot_base_frame: /base_link
update_frequency: 1.0
publish_frequency: 2.0
static_map: false
rolling_window: true
width: 4
height: 4
resolution: 0.05
transform_tolerance: 0.5
plugins:
- {name: obstacle_layer,
- {name: inflation_layer,
3 / 10
14.move_base介绍2.md 10/21/2018
3.global_costmap_params_withoutmap.yaml
global_costmap: type: "costmap_2d::VoxelLayer"}
global_frame: /map type: "costmap_2d::InflationLayer"}
robot_base_frame: /base_link
update_frequency: 1.0
publish_frequency: 0.5
static_map: false
rolling_window: true
width: 12
height: 12
resolution: 0.05
transform_tolerance: 0.5
plugins:
- {name: obstacle_layer,
- {name: inflation_layer,
4.dwa_local_planner_params_apollo.yaml
DWAPlannerROS:
# Robot Configuration Parameters - Kobuki
max_vel_x: 0.25
min_vel_x: 0.05
max_vel_y: 0
min_vel_y: 0
max_trans_vel: 0.35 # choose slightly less than the base's capability
min_trans_vel: 0.001 # this is the min trans velocity when there is negligible
rotational velocity
trans_stopped_vel: 0.05
# Warning!
# do not set min_trans_vel to 0.0 otherwise dwa will always think
translational velocities
# are non-negligible and small in place rotational velocities will be created.
max_rot_vel: 0.6 # choose slightly less than the base's capability
min_rot_vel: 0.4 # this is the min angular velocity when there is negligible
translational velocity
rot_stopped_vel: 0.1
acc_lim_x: 1 # maximum is theoretically 2.0, but we
acc_lim_theta: 1.5
acc_lim_y: 0 # diff drive robot
# Goal Tolerance Parameters
yaw_goal_tolerance: 0.2
xy_goal_tolerance: 0.15
4 / 10
14.move_base介绍2.md 10/21/2018
latch_xy_goal_tolerance: false
# Forward Simulation Parameters
sim_time: 2.0 # 1.7
vx_samples: 10 #3
vy_samples: 1 # diff drive robot, there is only one sample
vtheta_samples: 20 # 20
# Trajectory Scoring Parameters
path_distance_bias: 32.0 # 32.0 - weighting for how much it should stick
- wighting for how much it should attempt
to the global path plan - weighting for how much the controller
- how far along to place an additional
goal_distance_bias: 24.0 # 24.0 - amount of time a robot must stop in
- absolute velocity at which to start
to reach its goal - how much to scale the robot's footprint
occdist_scale: 0.4 # 0.01
should avoid obstacles
forward_point_distance: 0.325 # 0.325
scoring point
stop_time_buffer: 0.2 # 0.2
before colliding for a valid traj.
scaling_speed: 0.25 # 0.25
scaling the robot's footprint
max_scaling_factor: 0.2 # 0.2
when at speed.
# Oscillation Prevention Parameters - how far to travel before resetting
oscillation_reset_dist: 0.05 # 0.05
oscillation flags
# Debugging
publish_traj_pc : true
publish_cost_grid_pc: true
global_frame_id: odom
# Differential-drive robot configuration - necessary?
# holonomic_robot: false
5.move_base.yaml
# Move base node parameters. For full documentation of the parameters in this
file, please see
#
# http://www.ros.org/wiki/move_base
#
shutdown_costmaps: false
controller_frequency: 5.0
controller_patience: 3.0
5 / 10
14.move_base介绍2.md 10/21/2018
planner_frequency: 1.0
planner_patience: 5.0
oscillation_timeout: 10.0
oscillation_distance: 0.2
# local planner - default is trajectory rollout
base_local_planner: "dwa_local_planner/DWAPlannerROS"
base_global_planner: global_planner/GlobalPlanner #"navfn/NavfnROS" #alternatives:
, carrot_planner/CarrotPlanner
6.global_planner_params.yaml
GlobalPlanner: # Also see:
http://wiki.ros.org/global_planner
old_navfn_behavior: false # Exactly mirror behavior of
navfn, use defaults for other boolean parameters, default false
use_quadratic: true # Use the quadratic approximation
of the potential. Otherwise, use a simpler calculation, default true
use_dijkstra: true # Use dijkstra's algorithm.
Otherwise, A*, default true
use_grid_path: false # Create a path that follows the
grid boundaries. Otherwise, use a gradient descent method, default false
allow_unknown: true # Allow planner to plan through
unknown space, default true
#Needs to have
track_unknown_space: true in the obstacle / voxel layer (in costmap_commons_param)
to work
planner_window_x: 0.0 # default 0.0
planner_window_y: 0.0 # default 0.0
default_tolerance: 0.5 # If goal in obstacle, plan to the
closest point in radius default_tolerance, default 0.0
publish_scale: 100 # Scale by which the published
potential gets multiplied, default 100 # default 0.0
planner_costmap_publish_frequency: 0.0
lethal_cost: 253 # default 253
neutral_cost: 66 # default 50
cost_factor: 0.55 # Factor to multiply each cost
from costmap by, default 3.0
publish_potential: true # Publish Potential Costmap (this
is not like the navfn pointcloud2 potential), default true
运行结果
6 / 10
14.move_base介绍2.md 10/21/2018
运行roslaunch pibot_navigation fake_move_base_with_out_map.launch roslaunch
pibot_navigation view_nav_with_out_map.launch 选择2D Nav Goal导航可以看到
配置分析
可以看到move_base配置项较多涉及到cost_map及planner,分别又包括local_cost_map、
global_cost_map和local_planner、global_planner 首先看根配置(简单说就是没有前面的namespace
的)除了move_base.yaml,其他文件都是二级配置项
中在 都已经指定了 common_cost_map move_base_with_out_map.launch.xml namespace
####move_base 根配置
shutdown_costmaps 当move_base在不活动状态时是不是要关掉move_base node的 costmap
7 / 10
14.move_base介绍2.md 10/21/2018
查看源码可知move_base空闲时shutdown_costmaps为true会关掉cost_map,激活是会重新开启
默认false
controller_frequency 规划频率太大会占用CPU 这里我们设置为3 好点的处理器可以设置稍高
controller_patience
算了还是直接看源码吧
8 / 10
14.move_base介绍2.md 10/21/2018
计算速度失败就判断有没有超时,超时就切换状态
planner_frequency
容易理解这个是全局路径规划的频率如果为0即只规划一次
planner_patience 容易理解,规划路径的最大容忍时间
oscillation_timeout&oscillation_distance
陷在方圆oscillation_distance达oscillation_timeout之久认定机器人在震荡从而做异常处理
(应该容易理解吧)
base_local_planner & base_global_planner
最为重要的2个参数直接指定使用哪种局部规划和全局规划 具体类分别继承与实现
nav_core::BaseLocalPlanner和nav_core::BaseGlobalPlanner接口
9 / 10
14.move_base介绍2.md 10/21/2018
rosrun rqt_reconfigure rqt_reconfigure 查看move_base的参数
可以看到还有几个参数,一并看下
max_planning_retries 最大规划路径的重试次数 -1标识无限次
recovery_behavior_enabled 是否启用恢复机制
clearing_rotation_allowed 是否启用旋转的恢复当然是在recovery_behavior_enabled 为true
的基础上的
recovery_behaviors 一系列的恢复机制同base_local_planner & base_global_planner 具体类
继承于nav_core::RecoveryBehavior
conservative_reset_dist 清除机制的参数, 决定清除多远外的障碍
10 / 10

View File

@ -0,0 +1,86 @@
15.move_base介绍3.md 10/20/2018
1.概述
2.common
3.global_costmap
4.local_costmap 继续前文14.move_base介绍2配置了一个无需地图的move_base应用本文将介绍
下costmap
1.概述
move_base构造函数中会构造local_cost_map和global_costmap两个对象同时构造他们时会根据参数添加
相应的层
这些
参数分别在
costmap_common_params_apollo.yaml
local_costmap_params_withoutmap.yaml
global_costmap_params_withoutmap.yaml
显然第一个为共用的
2.common
1/3
15.move_base介绍3.md 10/20/2018
robot_radius 原型底盘即为半径 footprint 非原型,以旋转中心为原点,各个顶点按顺序(逆时针/顺时针都
可以)的坐标
优先会查找footprint
3.global_costmap
global_costmap: type: "costmap_2d::VoxelLayer"}
global_frame: /odom type: "costmap_2d::InflationLayer"}
robot_base_frame: /base_link
update_frequency: 1.0
publish_frequency: 0.5
static_map: false
rolling_window: true
width: 12
height: 12
resolution: 0.05
transform_tolerance: 0.5
plugins:
- {name: obstacle_layer,
- {name: inflation_layer,
global_frame 全局坐标系, 现在我们使用没有图的,这里使用/odom
robot_base_frame 机器人坐标系
update_frequency map更新的频率(说好的没有图的呢, 之前无图是没有传递加载实际的地图,还是要
到心中有图的这里指的costmap
publish_frequency map发布的频率
2/3
15.move_base介绍3.md 10/20/2018
static_map 该参数一般总是与下一个相反的, 标识使用静态地图我们没有使用这里当然是false
rolling_window true 标识地图跟随便机器人
width height resolution 地图信息
transform_tolerance tf的超时时间
plugins 图层
4.local_costmap
local_costmap: type: "costmap_2d::VoxelLayer"}
global_frame: /odom type: "costmap_2d::InflationLayer"}
robot_base_frame: /base_link
update_frequency: 1.0
publish_frequency: 2.0
static_map: false
rolling_window: true
width: 4
height: 4
resolution: 0.05
transform_tolerance: 0.5
plugins:
- {name: obstacle_layer,
- {name: inflation_layer,
与global_costmap基本一致
3/3

View File

@ -0,0 +1,232 @@
16.move_base介绍4.md 10/20/2018
1.global_planner
2.local_planner
1.global_planner
GlobalPlanner: # Also see:
http://wiki.ros.org/global_planner
old_navfn_behavior: false # Exactly mirror behavior of
navfn, use defaults for other boolean parameters, default false
use_quadratic: true # Use the quadratic approximation
of the potential. Otherwise, use a simpler calculation, default true
use_dijkstra: true # Use dijkstra's algorithm.
Otherwise, A*, default true
use_grid_path: false # Create a path that follows the
grid boundaries. Otherwise, use a gradient descent method, default false
allow_unknown: true # Allow planner to plan through
unknown space, default true
#Needs to have
track_unknown_space: true in the obstacle / voxel layer (in costmap_commons_param)
to work
planner_window_x: 0.0 # default 0.0
planner_window_y: 0.0 # default 0.0
default_tolerance: 0.5 # If goal in obstacle, plan to the
closest point in radius default_tolerance, default 0.0
publish_scale: 100 # Scale by which the published
potential gets multiplied, default 100 # default 0.0
planner_costmap_publish_frequency: 0.0
lethal_cost: 253 # default 253
neutral_cost: 66 # default 50
cost_factor: 0.55 # Factor to multiply each cost
from costmap by, default 3.0
publish_potential: true # Publish Potential Costmap (this
is not like the navfn pointcloud2 potential), default true
move_base 中的base_global_planner配置为 base_global_planner: global_planner/GlobalPlanner
1/7
16.move_base介绍4.md 10/20/2018
先看下global_planner的接口定义(前面讲过所有的实际的都是该接口的实现)
接口很简单,总共只有三个还有个重载函数,看名字就知道,一个初始化,还有个是规划路径,可以的话你也
可以实现这些接口完成你自己的global_planner目前可以使用的有三种
navfn/NavfnROS 使用Dijkstras算法代价最小的规划
global_planner/GlobalPlanner 提供更多选项支持不同配置
carrot_planner/CarrotPlanner
-allow unknown(true)
use dijkstra(true)
use quadratic(true)
use grid path(false)
old navfn behavior(false) 这些设置默认参数即可
default_tolerance 当目标点为障碍时,规划可以有一定的允许误差
lethal_cost
neutral_cost
2/7
16.move_base介绍4.md 10/20/2018
cost_factor
摘自【ROS Navigation Tuning Guide】
2.local_planner
DWAPlannerROS:
# Robot Configuration Parameters - Kobuki
max_vel_x: 0.25
3/7
16.move_base介绍4.md 10/20/2018
min_vel_x: 0.05
max_vel_y: 0
min_vel_y: 0
max_trans_vel: 0.35 # choose slightly less than the base's capability
min_trans_vel: 0.001 # this is the min trans velocity when there is negligible
rotational velocity
trans_stopped_vel: 0.05
# Warning!
# do not set min_trans_vel to 0.0 otherwise dwa will always think
translational velocities
# are non-negligible and small in place rotational velocities will be created.
max_rot_vel: 0.6 # choose slightly less than the base's capability
min_rot_vel: 0.4 # this is the min angular velocity when there is negligible
translational velocity
rot_stopped_vel: 0.1
acc_lim_x: 1 # maximum is theoretically 2.0, but we
acc_lim_theta: 1.5
acc_lim_y: 0 # diff drive robot
# Goal Tolerance Parameters
yaw_goal_tolerance: 0.2
xy_goal_tolerance: 0.15
latch_xy_goal_tolerance: true
# Forward Simulation Parameters
sim_time: 2.0 # 1.7
vx_samples: 10 #3
vy_samples: 1
vtheta_samples: 20 # 20
# Trajectory Scoring Parameters
path_distance_bias: 32.0 # 32.0 - weighting for how much it should stick
- wighting for how much it should attempt
to the global path plan - weighting for how much the controller
- how far along to place an additional
goal_distance_bias: 24.0 # 24.0 - amount of time a robot must stop in
- absolute velocity at which to start
to reach its goal - how much to scale the robot's footprint
occdist_scale: 0.4 # 0.01
should avoid obstacles
forward_point_distance: 0.325 # 0.325
scoring point
stop_time_buffer: 0.2 # 0.2
before colliding for a valid traj.
scaling_speed: 0.25 # 0.25
scaling the robot's footprint
max_scaling_factor: 0.2 # 0.2
when at speed.
# Oscillation Prevention Parameters - how far to travel before resetting
oscillation_reset_dist: 0.05 # 0.05
oscillation flags
4/7
16.move_base介绍4.md 10/20/2018
# Debugging
publish_traj_pc : true
publish_cost_grid_pc: true
global_frame_id: odom
# Differential-drive robot configuration - necessary?
# holonomic_robot: false
move_base 中的base_local_planner配置为 base_local_planner:
"dwa_local_planner/DWAPlannerROS"
同样该类实现了base_local_planner的接口我们看下接口
接口也不算复杂,字面理解分别为:
计算速度
是否到达目标点
下发全局路径
初始化 参数说明
max_vel_x min_vel_x max_vel_y min_vel_y速度限定值
5/7
16.move_base介绍4.md 10/20/2018
max_trans_vel min_trans_vel 平移速度限定值
trans_stopped_vel未使用
max_rot_vel min_rot_vel 旋转的速度限定值
rot_stopped_vel未使用
acc_lim_x acc_lim_theta acc_lim_y 加速度限定值
yaw_goal_tolerance xy_goal_tolerance 到达目标点的允许误差
latch_xy_goal_tolerance 如果为true 当机器人到达目标点后通过旋转调整姿态(方向)后,偏离了
目标点,也认为完成。这个实际应用中还是比较酷的
sim_time 模拟机器人以采样速度行走的时间,太小(<2)会导致行走不流畅,特别在遇到障碍或狭窄的空
间,因为没有足够多时间获取路径;太大(>5)会导致以僵硬的轨迹行走使得机器人不太灵活
vx_samples vy_samples vtheta_samples采样速度个数, 一般vtheta_samples大于vx_samples
vy_samples怎么不是0?查看源码即可得到答案, 最小为1,即使设置<=0也会重新置1
6/7
16.move_base介绍4.md 10/20/2018
path_distance_bias goal_distance_bias occdist_scale 轨迹代价计算
path_dist 规划最后一个点距离全局路径的距离即决定local_plan多接近global_plan
goal_distance 规格最后一个点距离local目标距离决定机器人接近目标
occdist_scale 路径中避障代价
另外还有
sim_granularity 轨迹上的点的密集程度
7/7

View File

@ -0,0 +1,47 @@
17.move_base介绍5.md 10/20/2018
1.inflation
参数 15.move_base介绍3介绍的local_costmap和global_costmap的参数 漏了其中的
plugin参数
1.inflation
先看下官方的图
红色为机器人轮廓
内部蓝色圆即为内切圆
外部蓝色圆即为外切圆
每个网格的值从0~255
Lethal(254): 网格与机器人中心重合, 肯定导致冲突
Inscribed(253): 网格外切圆与机器人内切圆重合, 同样肯定导致冲突
Possibly circumscribed: 网格外切圆与机器人外切圆外切, 可能导致冲突(机器人姿态决定),具体由
inscribed_radius inflation_radius和cost scaling factor相关
Freespace(0): 没有任何障碍,机器人可以占用该网格
1/2
17.move_base介绍5.md 10/20/2018
Unknown: 网格信息未知
上图坐标中的红色衰减曲线就标识网格离障碍的距离与cost_value的关系计算方法具体如下
参数
inflation_layer:
cost_scaling_factor: 2.5 # exponential rate at which the obstacle cost drops
off (default: 10)
inflation_radius: 1.2 # max. distance from an obstacle at which costs are
incurred for planning paths.
inflation_radius cost_value为0的网格离障碍的距离
cost_scaling_factor 衰减因子,越大上面的曲线越陡
左图 inflation_radius = 0.55 cost_scaling_factor = 5
右图 inflation_radius = 1.75 cost_scaling_factor = 2.58
2/2

View File

@ -0,0 +1,178 @@
18.如何在实际项目中应用ROS导航相关1-使用代码启动launch与定点导航.md 10/20/2018
1.引言
2.在代码中启动roslaunch和rosrun
3.代码中导航相关应用
定点导航
1.引言
我们知道启动ROS相关应用要么是roslaunch要么是rosrun实际项目中不可能每次手动去运行这些命令
单的就是写到脚本里去,但是涉及复杂的,就需要使用代码去处理
2.在代码中启动roslaunch和rosrun
直接贴出代码
import subprocess
import rospy
import rosnode
class launch_demo:
def __init__(self, cmd=None):
self.cmd = cmd
def launch(self):
self.child = subprocess.Popen(self.cmd)
return True
def shutdown(self):
self.child.terminate()
self.child.wait()
return True
if __name__ == "__main__":
rospy.init_node('launch_demo',anonymous=True)
launch_nav = launch_demo(["roslaunch", "pibot_simulator", "nav.launch"])
launch_nav.launch()
r = rospy.Rate(0.2)
r.sleep()
rospy.loginfo("switch map...")
r = rospy.Rate(1)
r.sleep()
rosnode.kill_nodes(['map_server'])
map_name =
1/4
18.如何在实际项目中应用ROS导航相关1-使用代码启动launch与定点导航.md 10/20/2018
"/home/pibot/ros_ws/src/pibot_simulator/maps/blank_map_with_obstacle.yaml"
map_node = subprocess.Popen(["rosrun", "map_server", "map_server", map_name,
"__name:=map_server"])
while not rospy.is_shutdown():
r.sleep()
上面使用python代码启动了一个PIBOT模拟器的导航然后5s后切换了一个地图
使用subprocess.Popen可以启动一个进程(roslaunch或者rosrun)
使用rosnode.kill_nodes可以杀死一个rosnode
3.代码中导航相关应用
定点导航
from launch_demo import launch_demo
import rospy
import actionlib
from actionlib_msgs.msg import *
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
from nav_msgs.msg import Path
from geometry_msgs.msg import PoseWithCovarianceStamped
from tf_conversions import transformations
from math import pi
class navigation_demo:
def __init__(self):
self.set_pose_pub = rospy.Publisher('/initialpose',
PoseWithCovarianceStamped, queue_size=5)
self.move_base = actionlib.SimpleActionClient("move_base", MoveBaseAction)
self.move_base.wait_for_server(rospy.Duration(60))
def set_pose(self, p):
if self.move_base is None:
return False
x, y, th = p
pose = PoseWithCovarianceStamped()
pose.header.stamp = rospy.Time.now()
pose.header.frame_id = 'map'
pose.pose.pose.position.x = x
pose.pose.pose.position.y = y
q = transformations.quaternion_from_euler(0.0, 0.0, th/180.0*pi)
pose.pose.pose.orientation.x = q[0]
2/4
18.如何在实际项目中应用ROS导航相关1-使用代码启动launch与定点导航.md 10/20/2018
pose.pose.pose.orientation.y = q[1]
pose.pose.pose.orientation.z = q[2]
pose.pose.pose.orientation.w = q[3]
self.set_pose_pub.publish(pose)
return True
def _done_cb(self, status, result):
rospy.loginfo("navigation done! status:%d result:%s"%(status, result))
def _active_cb(self):
rospy.loginfo("[Navi] navigation has be actived")
def _feedback_cb(self, feedback):
rospy.loginfo("[Navi] navigation feedback\r\n%s"%feedback)
def goto(self, p):
goal = MoveBaseGoal()
goal.target_pose.header.frame_id = 'map'
goal.target_pose.header.stamp = rospy.Time.now()
goal.target_pose.pose.position.x = p[0]
goal.target_pose.pose.position.y = p[1]
q = transformations.quaternion_from_euler(0.0, 0.0, p[2]/180.0*pi)
goal.target_pose.pose.orientation.x = q[0]
goal.target_pose.pose.orientation.y = q[1]
goal.target_pose.pose.orientation.z = q[2]
goal.target_pose.pose.orientation.w = q[3]
self.move_base.send_goal(goal, self._done_cb, self._active_cb,
self._feedback_cb)
return True
def cancel(self):
self.move_base.cancel_all_goals()
return True
if __name__ == "__main__":
rospy.init_node('navigation_demo',anonymous=True)
launch_nav = launch_demo(["roslaunch", "pibot_simulator", "nav.launch"])
launch_nav.launch()
r = rospy.Rate(0.2)
r.sleep()
rospy.loginfo("set pose...")
r = rospy.Rate(1)
r.sleep()
navi = navigation_demo()
navi.set_pose([-0.7,-0.4,0])
rospy.loginfo("goto goal...")
r = rospy.Rate(1)
3/4
18.如何在实际项目中应用ROS导航相关1-使用代码启动launch与定点导航.md 10/20/2018
r.sleep()
navi.goto([0.25,4, 90])
while not rospy.is_shutdown():
r.sleep()
上面完成设置机器人位置和导航到某一位置的功能
4/4

View File

@ -0,0 +1,29 @@
19.如何在实际项目中应用ROS导航相关2-使用代码调用服务.md 10/20/2018
1.调用服务
2.动态设置参数
1.调用服务
import rospy
if __name__ == "__main__":
rospy.init_node('service_demo',anonymous=True)
rospy.wait_for_service('/move_base/clear_costmaps')
self.clear_costmap = rospy.ServiceProxy('move_base/clear_costmaps', Empty)
r = rospy.Rate(0.2)
r.sleep()
rospy.loginfo("clear_costmap...")
self.clear_costmap()
上面代码完成/move_base/clear_costmaps服务的调用即等于rosservice call
/move_base/clear_costmaps
2.动态设置参数
通过rosrun rqt_reconfigure rqt_reconfigure设置参数改为使用代码调用修改
1/1

View File

@ -0,0 +1,96 @@
2.ROS的多机通讯的配置.md 9/1/2019
1.配置准备
2.多机通讯
3.PIBOT配置
4. 总结
1.配置准备
输入sudo apt-get install ros-kinetic-turtlesim下载安装turtlesim测试包完成后首先运行
roscore
再打开一个终端输入rosrun turtlesim turtlesim_node不出意外的话会出现一只乌龟
再另外一个终端输入rosrun turtlesim turtle_teleop_key 可以通过上下左右键控制乌龟运动了
2.多机通讯
想在PC上直接按键控制树莓派上的乌龟运动要如何操作呢 主机与树莓派信息如下:
Item IP Hostname
PC 192.168.31.111 robot-dekstop
树莓派 192.168.31.107 pi-desktop
1/3
2.ROS的多机通讯的配置.md 9/1/2019
相互ip可以通因为未添加hostname ip映射相互无法通过hostname联系
1. 在树莓派上运行roscore和启动turtlesim_node
2. PC上想要与之通讯首先必须设置ROS_MASTER_URI,export
ROS_MASTER_URI='http:192.168.31.107:11311'即可在PC中断输入rostopic list即可看到当
前的所有topic列表说明PC已经可以与ROS_MASTER建立通讯了
3. 这时PC端启动rosrun turtlesim turtle_teleop_key无法控制乌龟运动为什么呢这里明
明rostopic明明可以看到/turtle/cmd_vel。 键入rostopic info /turtle/cmd_vel 显示如
下 这就明显了,主机从master里面取
到的node发送/订阅的topic信息node地址为http://pi-desktop:46395, PC根本不认识pi-
desktop为何物自然无法发送数据。
4. 如何修改自然有2个方法
这个我不认识换个我认识的来修改树莓派端的ROS_IP环境变量 export
ROS_IP=192.168.31.107 再次查看topic信息
,现在就认识了,启动
rosrun turtlesim turtle_teleop_key节点发现仍然无法控制。问题是我认识你
是你不认我啊所有本地PC也需要export ROS_IP=192.168.31.111,现在就正常了
这个我不认识我现在认识下不就行了PC端修改/etc/hosts 添加一个192.168.31.107
pi-desktop,同时树莓派端一样修改/etc/hosts 添加 192.168.31.111 robot-desktop
3.PIBOT配置
PIBOT相关配置如下
ROS主机(树莓派等)
source /opt/ros/indigo/setup.bash
LOCAL_IP=`hostname -I`
export ROS_IP=`echo $LOCAL_IP`
export ROS_HOSTNAME=`echo $LOCAL_IP`
export PIBOT_MODEL=apollo
export PIBOT_LIDAR=rplidar
2/3
2.ROS的多机通讯的配置.md 9/1/2019
export ROS_MASTER_URI=`echo http://$ROS_IP:11311`
source ~/pibot_ros/ros_ws/devel/setup.bash
PC
source /opt/ros/indigo/setup.bash
LOCAL_IP=`hostname -I`
export ROS_IP=`echo $LOCAL_IP`
export ROS_HOSTNAME=`echo $LOCAL_IP`
export PIBOT_MODEL=apollo
export PIBOT_LIDAR=rplidar
export ROS_MASTER_URI=`echo http://xxx.xxx.xx.xx:11311`
source ~/pibot_ros/ros_ws/devel/setup.bash
xxx.xxx.xx.xx为ROS主机IP
4. 总结
多机通讯只能有一个master存在所有都设置ROS_MASTER_URI为http://MASTER_IP:11311, 各个终端要能相互
通讯就必须设置自己的ROS_IP。
3/3

View File

@ -0,0 +1,41 @@
20.建图-gmapping.md 10/21/2018
1.概述
当前开源的2D激光雷达slam的ROS package主要有 gmapping ros-perception/slam_gmapping ros-
perception/openslam_gmapping
Hector tu-darmstadt-ros-pkg/hector_slam
karto ros-perception/slam_karto ros-perception/open_karto skasperski/navigation_2d
cartographer googlecartographer/cartographer googlecartographer/cartographer_ros
2.建图效果
效果就取决于里面的算法实现,具体的配置文件的参数设置和内部算法实现
3.gmapping
gmapping包包含OpenSlam的Gmapping的一个ROS封装, 提供基于激光的SLAM(同时定位和创建地图)。依靠移
动机器人收集的激光和位姿数据可以创建2维栅格地图。
输入
1/4
20.建图-gmapping.md 10/21/2018
激光雷达和位姿
输出
参数
2/4
20.建图-gmapping.md 10/21/2018
3/4
20.建图-gmapping.md 10/21/2018
这里参数较多
maxUrange < 激光雷达最大测距 <= maxRange
4/4

View File

@ -0,0 +1,276 @@
21.关于运动控制方向的补充.md 7/20/2019
1.概述
2.PIBOT运动解算和PID
3.PIBOT电机方向和顺序
3.1电机方向与pwm_value值关系
3.2电机顺序
3.3 编码器
4.验证测试
4.1 测试方向
4.2测试编码器
5.程序修改
1.概述
使用PIBOT提供的小车已经完成了程序和硬件的调试如果需要移植到自己的小车可能会遇到PID调速不正
常,解算得到运动结果不一致,反解得到里程有问题等,原因就是单个电机线的方向、电机之间接线、编码器
AB相次序这里需要注意下面几点即可
a. 给定电机接口函数的输入参数正负与电机方向的关系
b.电机索引关系
c.电机转向得到编码器值正负关系
2.PIBOT运动解算和PID
void Robot::do_kinmatics(){
if (!do_kinmatics_flag){
for(int i=0;i<MOTOR_COUNT;i++){
pid[i]->clear();
encoder[i]->get_increment_count_for_dopid();
}
return;
}
static unsigned long last_millis=0;
if (Board::get()->get_tick_count()-last_millis>=Data_holder::get()-
>parameter.do_pid_interval){
last_millis = Board::get()->get_tick_count();
for(int i=0;i<MOTOR_COUNT;i++){
feedback[i] = encoder[i]->get_increment_count_for_dopid();
}
#if PID_DEBUG_OUTPUT
#if MOTOR_COUNT==2
printf("input=%ld %ld feedback=%ld %ld\r\n", long(input[0]*1000),
long(input[1]*1000),
long(feedback[0]),
long(feedback[1]));
#endif
1/7
21.关于运动控制方向的补充.md 7/20/2019
#if MOTOR_COUNT==3
printf("input=%ld %ld %ld feedback=%ld %ld %ld\r\n", long(input[0]*1000),
long(input[1]*1000), long(input[2]*1000),
long(feedback[0]),
long(feedback[1]), long(feedback[2]));
#endif
#if MOTOR_COUNT==4
printf("input=%ld %ld %ld %ld feedback=%ld %ld %ld %ld\r\n",
long(input[0]*1000), long(input[1]*1000), long(input[2]*1000),
long(input[3]*1000),
long(feedback[0]),
long(feedback[1]), long(feedback[2]), long(feedback[3]));
#endif
#endif
bool stoped=true;
for(int i=0;i<MOTOR_COUNT;i++){
if (input[i] != 0 || feedback[i] != 0){
stoped = false;
break;
}
}
short output[MOTOR_COUNT]={0};
if (stoped){
for(int i=0;i<MOTOR_COUNT;i++){
output[i] = 0;
}
do_kinmatics_flag = false;
}else{
for(int i=0;i<MOTOR_COUNT;i++){
output[i] = pid[i]->compute(Data_holder::get()-
>parameter.do_pid_interval*0.001);
}
}
for(int i=0;i<MOTOR_COUNT;i++){
Data_holder::get()->pid_data.input[i] = int(input[i]);
Data_holder::get()->pid_data.output[i] = int(feedback[i]);
}
#if PID_DEBUG_OUTPUT
#if MOTOR_COUNT==2
printf("output=%ld %ld\r\n\r\n", output[0], output[1]);
#endif
#if MOTOR_COUNT==3
printf("output=%ld %ld %ld\r\n\r\n", output[0], output[1], output[2]);
#endif
#if MOTOR_COUNT==4
printf("output=%ld %ld %ld %ld\r\n\r\n", output[0], output[1], output[2],
output[3]);
#endif
#endif
for(int i=0;i<MOTOR_COUNT;i++){
motor[i]->control(output[i]);
2/7
21.关于运动控制方向的补充.md 7/20/2019
}
if (Board::get()->get_tick_count()-
last_velocity_command_time>Data_holder::get()->parameter.cmd_last_time){
for(int i=0;i<MOTOR_COUNT;i++){
input[i] = 0;
}
}
}
}
运动控制是在Robot类中的do_kinmatics函数实现的大概流程
根据解算到各个轮子的速度转换到对应编码器的值和编码器的输出计算PID
根据PID结果控制电机
超时判断
上面说到的解算就是在Robot类中的update_velocity函数实现的
void Robot::update_velocity(){
short vx = min(max(Data_holder::get()->velocity.v_liner_x, -
(short(Data_holder::get()->parameter.max_v_liner_x))), short(Data_holder::get()-
>parameter.max_v_liner_x));
short vy = min(max(Data_holder::get()->velocity.v_liner_y, -
(short(Data_holder::get()->parameter.max_v_liner_y))), short(Data_holder::get()-
>parameter.max_v_liner_y));
short vz = min(max(Data_holder::get()->velocity.v_angular_z, -
(short(Data_holder::get()->parameter.max_v_angular_z))), short(Data_holder::get()-
>parameter.max_v_angular_z));
float vel[3]={vx/100.0, vy/100.0, vz/100.0};
float motor_speed[MOTOR_COUNT]={0};
model->motion_solver(vel, motor_speed);
for(int i=0;i<MOTOR_COUNT;i++){
input[i] = motor_speed[i]*short(Data_holder::get()-
>parameter.encoder_resolution)/(2*__PI)*short(Data_holder::get()-
>parameter.do_pid_interval)*0.001;
}
#if DEBUG_ENABLE
printf("vx=%d %d motor_speed=%ld %ld\r\n", vx, vz, long(motor_speed[0]*1000),
long(motor_speed[1]*1000));
#endif
last_velocity_command_time = Board::get()->get_tick_count();
do_kinmatics_flag = true;
}
3/7
21.关于运动控制方向的补充.md 7/20/2019
通过调用运动模型接口调用运动解算(model->motion_solver(vel, motor_speed)),完成从控制的全局速度
下发的角速度和线速度到各个轮子速度的转换最终转换为在do_pid_interval时间内编码器的变化值
3.PIBOT电机方向和顺序
3.1电机方向与pwm_value值关系
PIBOT定义所有电机控制给正值时(motor[i]->control(pwm_value)),从电机输出轴方向看电机顺时针转。
对应到差分小车apollo, 如
motor[0]->control(2000);//控制左电机向后
motor[1]->control(2000);//控制右电机向前
对与小车就是在逆时针运动
再如
motor[0]->control(-2000);//控制左电机向前
motor[1]->control(2000);//控制右电机向前
对与小车就是在向前运动
总结就是control给定正值输出轴看电机顺时针转动反之给定负值逆时针转动
3.2电机顺序
上面可以看出来motor[i]中的索引i
apollo中左电机为0右电机为1 zeus中前电机为0左电机为1右电机为2 hades和hera中右后电机为0右前
电机为1左前电机为2左后电机为3 具体编号源码kinematic_models文件夹中相应文件前右说明
4/7
21.关于运动控制方向的补充.md 7/20/2019
3.3 编码器
这里编码器值是一个程序计数值,一个方向转动编码器值会累加,反方向会减少 PIBOT定义上面2.1中给定正值
时,编码器值累加,给定负是编码器值减少
4.验证测试
如果上面有点晦涩,那直接按照如下方式测试即可 apollo为例
4.1 测试方向
do_kinmatics直接motor[0]->control(2000); return;
** 这里2000相对于PWM最大值来的Arduino最大1024 STM32最大设置为5000**
观察左电机电机是否向后
分别查看下面各个控制与实际电机转动情况 motor[0]->control(2000) 左电机向后 motor[0]-
>control(-2000) 左电机向前 motor[1]->control(2000) 右电机向前 motor[1]->control(-2000) 右电机
向后
如果得不到相应的结果,根据情况调整: a. motor[0]->control时右电机转动可能考虑左右电机接线反
了 b. motor[1]->control(xx) ,哪个电机是对的,但发现不对。可以调整电机接线,也可以调整程序的方向
控制
PIBOT提供相关的宏MOTORx_REVERSE可以不需要对换电机接线(PIN1和PIN6),达到调整方向的目的
5/7
21.关于运动控制方向的补充.md 7/20/2019
4.2测试编码器
恢复会正常程序并新增调试的输出,连接调试串口,打开串口调试工具 对应到程序的输出
观察串口调试助手中的total_count=xx yy输出xx随着左轮的向前转动越来越小反之越来越大yy随着右
轮的向前越来越大,反之越来越小
如果得不到相应的结果,根据情况调整: a. 左电机转动yy变化或者右电机转动xx变化那应该是2个电机
编码器反了,需要调换下 b. 左电机转动xx变化但相反。应该是编码器AB相接线反了右电机同理
PIBOT提供相关的宏ENCODERx_REVERSE可以不需要对换电机编码器接线PIN3和PIN4,达到调整方
向的目的
5.程序修改
6/7
21.关于运动控制方向的补充.md 7/20/2019
固件程序提供了直接的宏,针对电机反向或者编码器反向的问题 例如STM32F1电机方向反转宏,修改宏定义
STM32F4编码器方向反转宏(在param.mk文件)
7/7

View File

@ -0,0 +1,98 @@
22.IMU和里程计融合.md 10/21/2018
1.概述
2.IMU数据获取
2.1 PIBOT IMU
3.两种融合的方法
3.1 一种简单的方法
3.2 扩展的卡尔曼滤波
1.概述
实际使用中会出现轮子打滑和累计误差的情况,这里单单使用编码器得到里程计会出现一定的偏差,虽然激光
雷达会纠正,但一个准确的里程对这个系统还是较为重要
2.IMU数据获取
IMU即为 惯性测量单元,一般包含了三个单轴的加速度计和三个单轴的陀螺仪,简单理解通过加速度二次积分
就可以得到位移信息、通过角速度积分就可以得到三个角度,实时要比这个复杂许多
2.1 PIBOT IMU
PIBOT在嵌入式程序提供出原始的数据接口通过配置可以输出原始raw_imu topic
该topic类型为自定义具体如下即为
三轴加速度三轴陀螺仪和三轴磁力计的原始数据
1/7
22.IMU和里程计融合.md 10/21/2018
通过对原始数据处理得到一个/imu/data_raw数据类型为sensor_msgs/Imu
通过ROS提供的
相关包imu_tools进行滤波 可以看到complementary_filter_gain_node会订阅该topic即该topic作为输入
滤波得到最终数据(发布/imu/data topic 类型同样为sensor_msgs/Imu)
2/7
22.IMU和里程计融合.md 10/21/2018
看到得到的值波动已经较小了且静止的时候接近于0 输出该topic可以
3.两种融合的方法
3.1 一种简单的方法
从imu得到的数据为一个相对角度(主要使用yawroll和pitch 后面不会使用到),使用该角度来替代由编码器
计算得到的角度。 这个方法较为简单出现打滑时候因yaw不会受到影响即使你抬起机器人转动一定的角
度,得到的里程也能正确反映出来
3.2 扩展的卡尔曼滤波
3/7
22.IMU和里程计融合.md 10/21/2018
官方提供了个扩展的卡尔曼滤波的包robot_pose_ekfrobot_pose_ekf开启扩展卡尔曼滤波器生成机器人姿态
支持
odom编码器
imu_dataIMU
vo视觉里程计 还可以支持GPS 引用官方图片
PR2从实际初始点(绿色)溜达一圈回到初始点(绿色),编码器的里程(蓝色)发生了漂移,而使用
robot_pose_ekf融合出来的里程(红色)则跟实际位置基本重合(后面我们会针对这个测试下效果)
中间的圆是小圆放大的展示效果
4/7
22.IMU和里程计融合.md 10/21/2018
再回去看下该包的输出
发布一个topic 类型需要注意下是PoseWithCovarianceStamped并非Odometry 后面会用到这个作为显
示,所以还需要一个转换
看该topic信息可以看到odom_ekf订阅了该topic 再次查看该节点信息可以看到
他会发出一个Odometry的topic
发出一个tf
在robot_pose_ekf配置时做了些映射处理这样可以保证导航层在使用和不用imu的时候无需修改就
可以工作
5/7
22.IMU和里程计融合.md 10/21/2018
bringup.lauch或者bringup_with_imu.launch 输出的tf都为odom → base_footprint ;发出的里程也都是
odom bringup_with_imu.launch轮子的里程topic 映射为wheel_odom
这里很重要,后面的对该包的验证会使用到
下2张图展示了未使用IMU和使用IMU时候的tf tree情况, 可以看到用了一致的frame
6/7
22.IMU和里程计融合.md 10/21/2018
7/7

View File

@ -0,0 +1,52 @@
23.IMU和里程计融合与单独编码器里程计的对比测试.md 10/21/2018
1.编码器里程计测试
手动控制
2.融合IMU的里程测试
手动控制 里程计直接会作为建图或者导航的时候的输入,所以起着至关重要的做,准确性直接影
响建图和导航的效果。单独使用轮子编码器得到的里程计与融合了IMU数据的里程计最终效果如
何,我们这里做个测试来对比下。 有2种方式测试
手动控制机器人走一圈然后回到之前的原点,通过观察模拟器(RViz)中里程与初始点的偏差
程序控制机器人行走(例如走一个方形),通过观察最终机器人时间与最初原点的偏差
下面我们使用的都是第一种及标记个原点,控制机器人随机行走一段时间后再控制其回到标记的原点上,打开
RViz观察程序计算的机器人位置姿态是否回到原点
1.编码器里程计测试
手动控制
手动控制apollo"溜达"了两圈回到原点观察tf坐标系可以看到
程序显示base_link没有回到原点有一定的误差(位置和姿态都有误差)
2.融合IMU的里程测试
手动控制
大的红色箭头就是编码 未做对比我们在使用融合IMU的时候把使用编码器计算出来的里程计显示出来作对比(
器里程计)同上面我们控制apollo溜达一圈回到原点
1/4
23.IMU和里程计融合与单独编码器里程计的对比测试.md 10/21/2018
初始的时候三个是重合的
行走了一段时间
可以发现没走多远融合出来的里程(黄色框)和编码器里程(红色框)就有了较大的差距
2/4
23.IMU和里程计融合与单独编码器里程计的对比测试.md 10/21/2018
-回到原点 我们继续控制使得apollo回到原点
3/4
23.IMU和里程计融合与单独编码器里程计的对比测试.md 10/21/2018
这张图可以看出跟官方提供的图较为类似了
4/4

View File

@ -0,0 +1,154 @@
24.嵌入式部分框架设计与实现.md 10/21/2018
1.Robot类
2.Robot::init
Robot中的接口实例化
3.运动解算
4.PID
5.wheel odom
1.Robot类
先来张类关系图
在贴个main的代码
#include "robot.h"
int main(void)
{
Robot::get()->init();
while (1)
{
1/5
24.嵌入式部分框架设计与实现.md 10/21/2018
Robot::get()->check_command();
Robot::get()->do_kinmatics();
Robot::get()->calc_odom();
Robot::get()->get_imu_data();
Robot::get()->check_joystick();
}
}
Robot为一个单例模式类通过他完成大部分工作, main函数中可以看到其功能:
初始化
检测命令
运动解算
计算里程
获取imu数据
检查遥控
与之关联的类有:
Model 这是个运动模型接口类可以看到该类有几个派生类Differential Omni3 Mecanum 分别代表对
不同机器人模型的解算
Dataframe 协议接口类,通过继承该类可以使用不同的协议进行通讯,这里实现了一个
Simple_dataframe的派生类
Transport 通讯接口类,通过继承该类可以使用不同的通讯口接口通讯这里实现了一个
USART_transport的派生类显然这个USART_transport是使用串口通讯
MotorController 电机控制接口派生出一个名为CommonMotorController类控制
PID 显然是PID控制类
Encoder 是编码器读取类
Joystick ps2遥控器
IMU 惯性测量单元相关 同时Robot从notify继承实现了一个观察者模式实现对某一个或者多个事件的
观察这里是对消息的关注即收到消息会通知到Robot通过Dataframe::register_notify注册
另外有另外几个单例类
Board 该类维护bsp相关的接口
Queue 实现了一个简单的队列
Data_holder该类维护这多个与通讯业务相关的类对象
2.Robot::init
2/5
24.嵌入式部分框架设计与实现.md 10/21/2018
这里主要完成对各个部件的初始化
Robot中的接口实例化
Robot中维护的多为接口类的指针, 可以看到在下面
的代码中完成对象的实例化
根据配置的宏使用不同的运动模型, 其他接口的实例化也类似
3.运动解算
这里主要根据发送的速度得到各个轮子的转速这部分工作是在Robot::update_velocity 而该函数为作为
对设置速度消息的关注的回调,该回调函数中
void Robot::update_velocity(){
short vx = min(max(Data_holder::get()->velocity.v_liner_x, -
3/5
24.嵌入式部分框架设计与实现.md 10/21/2018
(short(Data_holder::get()->parameter.max_v_liner_x))), short(Data_holder::get()-
>parameter.max_v_liner_x));
short vy = min(max(Data_holder::get()->velocity.v_liner_y, -
(short(Data_holder::get()->parameter.max_v_liner_y))), short(Data_holder::get()-
>parameter.max_v_liner_y));
short vz = min(max(Data_holder::get()->velocity.v_angular_z, -
(short(Data_holder::get()->parameter.max_v_angular_z))), short(Data_holder::get()-
>parameter.max_v_angular_z));
float vel[3]={vx/100.0, vy/100.0, vz/100.0};
float motor_speed[MOTOR_COUNT]={0};
model->motion_solver(vel, motor_speed);
for(int i=0;i<MOTOR_COUNT;i++){
input[i] = motor_speed[i]*short(Data_holder::get()-
>parameter.encoder_resolution)/(2*__PI)*short(Data_holder::get()-
>parameter.do_pid_interval)*0.001;
}
#if DEBUG_ENABLE
printf("vx=%d %d motor_speed=%ld %ld\r\n", vx, vz, long(motor_speed[0]*1000),
long(motor_speed[1]*1000));
#endif
last_velocity_command_time = Board::get()->get_tick_count();
do_kinmatics_flag = true;
}
首先对速度限制做判断
然后通过运动模型类解算得到轮子的速度motor_speed
在转换到pid间隔时间Data_holder::get()->parameter.do_pid_interval内各个电机行径的编
码器值得到一组input(多个电机)
4.PID
采集编码器在pid间隔时间Data_holder::get()->parameter.do_pid_interval走了多少即一
组 feedback(多个电机)
4/5
24.嵌入式部分框架设计与实现.md 10/21/2018
有了输入input反馈feedback我们既可以做pid运算了, 得到的一组output即为控制电机的pwm负为
反向)
控制电机输出
5.wheel odom
计算一定间隔时间内(CALC_ODOM_INTERVAL这里固定为100ms)编码器改变值得到一组dis
反解, 通过运动模型接口转换轮子的位移到机器人的里程
5/5

View File

@ -0,0 +1,61 @@
25.PIBOT的IMU校准.md 7/20/2019
1. 校准里程计
2. 手动校准IMU
2. 自动校准IMU
3.验证
1. 校准里程计
首先按照11. PIBOT的控制及校准校准好里程计
2. 手动校准IMU
1. 启动imu bringup
roslaunch pibot_bringup bringup_with_imu.launch
2. 查看imu数据 rostopic echo /imu/data_raw
校准使得红色方框中接
近于0除了z州加速度图中实际为校准完成的结果
如果相差较大,则需要运行第三步
3. 校准
rosservice call /imu/calibrate_imu
保持小车静止状态新的窗口运行上面命令待bringup输出日志即可
4. 更新校准数据
1/3
25.PIBOT的IMU校准.md 7/20/2019
roscd pibot_bringup/launch
vi bring_with_imu.launch
把bringup校准后的输出3中图片红色部分)的数据填入的bring_with_imu.launch文件中即可
2. 自动校准IMU
支持自动校准IMU只需要设置imu/perform_calibration为ture即可, 执行roslaunch pibot_bringup
bringup_with_imu.launch后会有如下输出
这时候需要保持 静止平放 动校准需要一些数据作为输入, ,直到输出如下信息校准完成
imu
3.验证 可以看到得到一个更
重启bringup和输出imu数据前面1和2
加接近于0的一组数据
2/3
25.PIBOT的IMU校准.md 7/20/2019
3/3

View File

@ -0,0 +1,139 @@
26.如何在实际项目中应用ROS导航相关3- 多点导航巡航.md 11/29/2018
定点导航 前文 18.如何在实际项目中应用ROS导航相关1 使用程序启动一个pibot_simulator,并且完成一个
定点导航,本文对其修改完成一个多点导航航的例子
直接贴出代码 navigation_multi_demo.launch
<launch>
<node pkg="pibot" type="navigation_multi_goals.py" respawn="false"
name="navigation_multi_goals" output="screen">
<param name="goalListX" value="2.0, 1.0" />
<param name="goalListY" value="2.0, 3.0" />
<param name="goalListYaw" value="0.0, 90.0" /><!--degree-->
</node>
</launch>
分别导航至[2.0, 2.0, 0]、[1.0, 3.0, 90]
[x, y ,yaw] x, y为目标坐标yaw为目标姿态yaw(角度)
navigation_multi_goals.py
#!/usr/bin/env python
from launch_demo import launch_demo
import rospy
import actionlib
from actionlib_msgs.msg import *
from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
from nav_msgs.msg import Path
from geometry_msgs.msg import PoseWithCovarianceStamped
from tf_conversions import transformations
from math import pi
class navigation_demo:
def __init__(self):
self.set_pose_pub = rospy.Publisher('/initialpose',
PoseWithCovarianceStamped, queue_size=5)
self.move_base = actionlib.SimpleActionClient("move_base", MoveBaseAction)
self.move_base.wait_for_server(rospy.Duration(60))
def set_pose(self, p):
if self.move_base is None:
return False
x, y, th = p
pose = PoseWithCovarianceStamped()
pose.header.stamp = rospy.Time.now()
pose.header.frame_id = 'map'
pose.pose.pose.position.x = x
1/3
26.如何在实际项目中应用ROS导航相关3- 多点导航巡航.md 11/29/2018
pose.pose.pose.position.y = y
q = transformations.quaternion_from_euler(0.0, 0.0, th/180.0*pi)
pose.pose.pose.orientation.x = q[0]
pose.pose.pose.orientation.y = q[1]
pose.pose.pose.orientation.z = q[2]
pose.pose.pose.orientation.w = q[3]
self.set_pose_pub.publish(pose)
return True
def _done_cb(self, status, result):
rospy.loginfo("navigation done! status:%d result:%s"%(status, result))
def _active_cb(self):
rospy.loginfo("[Navi] navigation has be actived")
def _feedback_cb(self, feedback):
rospy.loginfo("[Navi] navigation feedback\r\n%s"%feedback)
def goto(self, p):
rospy.loginfo("[Navi] goto %s"%p)
goal = MoveBaseGoal()
goal.target_pose.header.frame_id = 'map'
goal.target_pose.header.stamp = rospy.Time.now()
goal.target_pose.pose.position.x = p[0]
goal.target_pose.pose.position.y = p[1]
q = transformations.quaternion_from_euler(0.0, 0.0, p[2]/180.0*pi)
goal.target_pose.pose.orientation.x = q[0]
goal.target_pose.pose.orientation.y = q[1]
goal.target_pose.pose.orientation.z = q[2]
goal.target_pose.pose.orientation.w = q[3]
self.move_base.send_goal(goal, self._done_cb, self._active_cb,
self._feedback_cb)
result = self.move_base.wait_for_result(rospy.Duration(60))
if not result:
self.move_base.cancel_goal()
rospy.loginfo("Timed out achieving goal")
else:
state = self.move_base.get_state()
if state == GoalStatus.SUCCEEDED:
rospy.loginfo("reach goal %s succeeded!"%p)
return True
def cancel(self):
self.move_base.cancel_all_goals()
return True
if __name__ == "__main__":
rospy.init_node('navigation_demo',anonymous=True)
goalListX = rospy.get_param('~goalListX', '2.0, 2.0')
goalListY = rospy.get_param('~goalListY', '2.0, 4.0')
2/3
26.如何在实际项目中应用ROS导航相关3- 多点导航巡航.md 11/29/2018
goalListYaw = rospy.get_param('~goalListYaw', '0, 90.0')
goals = [[float(x), float(y), float(yaw)] for (x, y, yaw) in
zip(goalListX.split(","),goalListY.split(","),goalListYaw.split(","))]
navi = navigation_demo()
r = rospy.Rate(1)
r.sleep()
for goal in goals:
navi.goto(goal)
while not rospy.is_shutdown():
r.sleep()
3/3

View File

@ -0,0 +1,99 @@
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
概述
ssh连接
地图显示与控制
概述
使用PIBOT系列机器人一般下面几个步骤
1. 需要通过ssh工具连接小车启动相关建图与导航程序
2. 安装ubuntu主机或者虚拟机在虚拟机启动Rviz界面观察建图情况和下发命令 这里我们想只使用
Android设备完成上面2不操作
ssh连接
安装ssh工具juicessh 网盘提供了链接
连接网络 Andoid设备连接跟小车主机路由器(树莓派自动启动热点可以直接连接树莓派释放的热点
pibot_ap)
ssh连接
点击连接,新建一个连接
1 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
选择机器人的IP新建一个认证
2 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
通过用户名密码认证
3 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
4 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
确认选择
5 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
6 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
新建好一个名为apollo的连接连接的用户名密码均为pibot
7 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
8 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
9 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
下面通过该链接连接至小车
10 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
连接成功我们可以输入命令测试usb设备连接情况了
11 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
同样我们可以输入命令启动建图
12 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
13 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
可以看到odom received表示建图程序已经启动了
14 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
地图显示与控制
通过map_nav app我们可以显示地图
输入ip连接机器人
15 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
16 / 17
27.单独使用Android设备使用PIBOT系列机器人.md 10/21/2018
显示地图
控制行走或者设置目标点即可建图
17 / 17

View File

@ -0,0 +1,117 @@
28.如何在实际项目中应用ROS导航相关4-获取机器人位置坐标.md 1/19/2019
通过pibot_simulator我们看下地图与坐标的关系以及如何获取机器人在地图中的实时坐标
1.PIBOT模拟器
1.1模拟器使用
启动模拟器 roslaunch pibot_simulator nav.launch 启动Rviz roslaunch pibot_navigation
view_nav.launch
1.2地图
roscd pibot_simulator/launch
cat nav.launch
可以看到加载的是pibot_simulator/maps/test_map.yaml
image: test_map.pgm
resolution: 0.050000
origin: [-13.800000, -12.200000, 0.000000]
negate: 0
occupied_thresh: 0.9
free_thresh: 0.196
test_map.yaml 指定了使用的地图文件
1.3坐标&分辨率
修改test_map.yaml中的origin项 origin: [0.00000, 0.00000, 0.000000] 重新启动模拟器和Rviz
1/4
28.如何在实际项目中应用ROS导航相关4-获取机器人位置坐标.md 1/19/2019
通过Rviz中地图与直接打开的地图对比可以看到机器人再地图的最左下角也就是说最左下角是地图的原点
(0,0), 方向正左为0度
选中Rviz工具栏中的Publish Point
分别移动鼠标至地图的四个角,通过状态栏卡可看到实时的坐标显示分别为
左下角 (0 ,0,0)
右下角 (28.8 ,0,0)
右上角 (28.8 ,27,0)
左上角 (0 ,27,0)
可以看到上图中的display 中的Map下显示地图的信息
2/4
28.如何在实际项目中应用ROS导航相关4-获取机器人位置坐标.md 1/19/2019
这里0.05就是地图分辨率了 一个像素表示0.05m,实际该该地图对应大小即为(576 * 0.05, 544 * 0.05)即(28.8,
27.2),对应到上面四个角的坐标我们就可以知道x,y 坐标系如何了实际跟我们高中几何坐标系一样横向x轴向
左增大纵向y轴向上增大, 角度也同样为跟x的夹角
1.4初始位置
1.3开头我们设置origin: [0.00000, 0.00000, 0.000000],现在我们修改回去origin: [-13.800000,
-12.200000, 0.000000]再次重新启动模拟器,对比位置我们可以看到现在的坐标原点跑到了中间,但是坐标
系仍是一样横向x轴向左增大纵向y轴向上增大, 角度也同样为跟x的夹角
2.通过代码获取实时位置
通过base_link与map坐标的tf我们就可以得到机器人实时位置之际贴代码
#!/usr/bin/env python
import rospy
from tf_conversions import transformations
from math import pi
import tf
class Robot:
def __init__(self):
self.tf_listener = tf.TransformListener()
try:
self.tf_listener.waitForTransform('/map', '/base_link', rospy.Time(),
rospy.Duration(1.0))
except (tf.Exception, tf.ConnectivityException, tf.LookupException):
return
def get_pos(self):
try:
(trans, rot) = self.tf_listener.lookupTransform('/map', '/base_link',
rospy.Time(0))
except (tf.LookupException, tf.ConnectivityException,
tf.ExtrapolationException):
rospy.loginfo("tf Error")
return None
euler = transformations.euler_from_quaternion(rot)
#print euler[2] / pi * 180
x = trans[0]
y = trans[1]
th = euler[2] / pi * 180
return (x, y, th)
if __name__ == "__main__":
rospy.init_node('get_pos_demo',anonymous=True)
robot = Robot()
r = rospy.Rate(100)
3/4
28.如何在实际项目中应用ROS导航相关4-获取机器人位置坐标.md 1/19/2019
r.sleep()
while not rospy.is_shutdown():
print robot.get_pos()
r.sleep()
4/4

View File

@ -0,0 +1,98 @@
29.cartographer的测试.md 2/23/2019
1.cartorgrapher介绍
无需过多介绍大名鼎鼎google开源的slam算法
2.硬件需求
先贴段官方的
64-bit, modern CPU (e.g. 3rd generation i7)
16 GB RAM
Ubuntu 14.04 (Trusty) and 16.04 (Xenial)
gcc version 4.8.4 and 5.4.0
当然越高端越好无论CPU和内存经测试树莓派是没法玩了测试了2款RK3399(Ubuntu16)设备可以跑起来
3.环境准备与编译
3.1安装依赖包
sudo apt-get install -y google-mock libboost-all-dev libeigen3-dev libgflags-dev
libgoogle-glog-dev liblua5.2-dev libprotobuf-dev libsuitesparse-dev libwebp-dev
ninja-build protobuf-compiler python-sphinx ros-kinetic-tf2-eigen libatlas-base-
dev libsuitesparse-dev liblapack-dev ros-kinetic-eigen-conversions
3.2编译
编译ceres-solver
cd pibot_ros
tar jxvf cartographer.tar.bz2
cd ~/pibot_ros/cartographer/ceres-solver-1.11.0/build
cmake ..
make j4 # 这里如果出错(见下图),可以多次执行
sudo make install
1/4
29.cartographer的测试.md 2/23/2019
继续cartographer
cd ~/pibot_ros/cartographer/cartographer/build
cmake .. -G Ninja
ninja
ninja test
sudo ninja install
编译cartorgrapher ros 包
cd ~/pibot_ros/cartographer/cartographer_ros
catkin_make
4.测试cartographer
cartograper不依赖里程计所以不需要发出odom tf,这里我们启动robot_without_odom.launch
roslaunch pibot_bringup robot_without_odom.launch
这里包括了启动雷达
另外一个终端
source ~/pibot_ros/cartographer/cartographer_ros/devel/setup.bash
roslaunch pibot_navigation cartographer.launch
2/4
29.cartographer的测试.md 2/23/2019
另外一个终端通过控制小车就可以cartographer建图了
roslaunch pibot keyboard.launch
我们先看下tf tree,虚拟机中rosrun rqt_tf_tree rqt_tf_tree可以看到一幅支持的tf tree了
5.查看建图
可是说好的建图的图呢没图说个jb.jpg,同样我们在rviz显示建图结果
roslaunch pibot_navigation view_cartographer.launch
3/4
29.cartographer的测试.md 2/23/2019
6.疑问
测试 对于4. cartographer中 有的同学要问为什么不把这个2个写到一个launch,这个问题问的好,博主试着写到
一起,启动不正常 ,还不知道具体原因,有兴趣的可以一起探讨
附上之前的launch文件
<launch>
<include file="$(find pibot_bringup)/launch/robot_without_odom.launch"/>
<include file="$(find cartographer_ros)/launch/lidar_2d.launch"/>
</launch>
4/4

View File

@ -0,0 +1,149 @@
3. 使用Visual Studio Code+PlatformIO IDE开发Arduino.md 10/20/2018
1.简述
2.安装Visual Studio Code PlatformIO
3.测试
4.问题与高级功能
5.注意点
6.总结
1.简述
介绍如何使用VScode直接开发Arduino程序避免使用Arduino IDE时的没有代码提示功能文件关系不清
晰、头文件打开不方便等问题及使用Visual Stdio集成插件的庞大安装工程同时Visual Studio Code插件
PlatformIO IDE开发Arduino 跨平台无论你是用的windowsubuntu或者mac都可以玩转。
2.安装Visual Studio Code PlatformIO
https://code.visualstudio.com/ 页面下载安装vscode
安装完成vscode启动扩展页面下搜索platformio即可找到,选择第一个Platformio IDE安装即可
(这里需要耐心等待一会)
安装完成重新加载后左下角会多一个小房子图标点击后即可显示Platformio IDE主页
3.测试
1/5
3. 使用Visual Studio Code+PlatformIO IDE开发Arduino.md 10/20/2018
选择New Project创建工程选择相应的Board我这里使用Mega2560输入2560找到对应的Board
修改main.cpp
#include <Arduino.h>
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delayMicroseconds(1000);
digitalWrite(13, LOW);
delay(1000);
}
编译与下载
同样左下角有一堆按钮 选择upload即可完成下载
指示灯也可以正常闪烁,可以看到我们连端口都没有选择就完成了下载的工作,非常方便。
4.问题与高级功能
Arduino IDE有库管理功能可以下载到需要的库。这里还要方便例如我们想使用TimerOne输出PWM,
#include <Arduino.h>
#include <TimerOne.h>
void setup() {
Timer1.initialize(40);
}
void loop() {
Timer1.pwm(11, 512);
}
Arduino IDE我们会这样写然库管理搜索下载TimerOne库在这里我们只需要在配置文件platformio.ini加上
下面一句即可
lib_deps = TimerOne
2/5
3. 使用Visual Studio Code+PlatformIO IDE开发Arduino.md 10/20/2018
选择编译按钮编译,我们可以看到输出信息
找到了TimerOne库并下载至.piolibdeps文件夹下
5.注意点
接上面我们也可以把下载好的TimerOne库直接放置在lib目录下也就无需添加lib_deps。
我们不想在main里面直接使用TimerOne的pwm我们想自己写一个motor库motor库会使用到
TimerOne
motor.h
#ifndef TEST_MOTOR_H_
#define TEST_MOTOR_H_
class Motor{
public:
void init(unsigned char fre);
void pwm(unsigned short);
};
#endif
motor.cpp
3/5
3. 使用Visual Studio Code+PlatformIO IDE开发Arduino.md 10/20/2018
#include "motor.h"
#include "TimerOne.h"
void Motor::init(unsigned char fre) {
Timer1.initialize(1000.0/fre);
}
void Motor::pwm(unsigned short val) {
Timer1.pwm(11,val);
}
main.cpp
#include <Arduino.h>
#include <motor.h>
Motor motor1;
void setup() {
motor1.init(15);
}
void loop() {
motor1.pwm(512);
}
4/5
3. 使用Visual Studio Code+PlatformIO IDE开发Arduino.md 10/20/2018
编译完成提示找不到TimerOne.h头文件
可以看到Library Dependency Graph没有TimerOne
两种解决方法
1. main.cpp include头文件TimerOne.h这个比较low英文main中根本就没有使用到TimerOne
2. 之前的办法添加lib_deps = TimerOne
6.总结
至此可以看到使用VSCode集成的PlatformIO IDE插件开发与查看Arduino的代码都非常方便
5/5

View File

@ -0,0 +1,178 @@
30.laser_filters的使用.md 2/24/2019
1.雷达数据屏蔽需求
使用激光雷达建图和导航,实际应用中,不可能保证雷达在最上层(即车体不会遮挡雷达),一般应用为雷达放在下面,
对于360°的雷达,车体就会遮挡从而导致车体被人作为障碍物,那如何解决这个问题
2.简单实现
我们只需要订阅激光雷达的数据,把相应角度的数据屏蔽即可,然后在重新发布新的数据
#!/usr/bin/env python
from __future__ import print_function
import sys
import rospy
from sensor_msgs.msg import LaserScan
from std_msgs.msg import String
class DoFilter:
def __init__(self):
self.sub = rospy.Subscriber("scan", LaserScan, self.callback)
self.pub = rospy.Publisher("filteredscan", LaserScan, queue_size=10)
def callback(self, data):
newdata = data
newdata.ranges = list(data.ranges)
newdata.intensities = list(data.intensities)
for x in range(120,130):
newdata.ranges[x]=0
newdata.intensities[x]=0
self.pub.publish(newdata)
rospy.loginfo(data)
if __name__ == '__main__':
rospy.init_node('LidarFilter', anonymous=False)
lidar = DoFilter()
rospy.spin()
3.laser_filters包
3.1 LaserScanBoxFilter
先贴地址 laser_filters - ROS Wiki,改包提供了强大的功能,这里我们使用一个简单的例子看下如何屏蔽
1/6
30.laser_filters的使用.md 2/24/2019
我们先写个模拟的激光雷达,假设1m范围内为障碍物
代码贴上如下,不在赘述
#!/usr/bin/env python
from __future__ import print_function
import sys
import rospy
from sensor_msgs.msg import LaserScan
from std_msgs.msg import String
class ScanSimulator:
def __init__(self):
self.pub = rospy.Publisher("scan", LaserScan, queue_size=10)
self.newdata = LaserScan()
self.newdata.ranges = [0]*360
self.newdata.intensities = [0]*360
self.newdata.header.frame_id="laser_link"
self.newdata.angle_min=-1.57
2/6
30.laser_filters的使用.md 2/24/2019
self.newdata.angle_max=1.57
self.newdata.angle_increment=3.14*2 / 360
self.newdata.time_increment=(1 / 100) / (360);
self.newdata.range_min = 0.0;
self.newdata.range_max = 100.0;
def pub(self):
self.newdata.header.stamp=rospy.Time.now()
for x in range(0,360):
self.newdata.ranges[x]=1
self.newdata.intensities[x]=1
self.pub.publish(self.newdata)
if __name__ == "__main__":
rospy.init_node('scan_simulator',anonymous=True)
r = rospy.Rate(100)
lidar = ScanSimulator()
while not rospy.is_shutdown():
lidar.pub()
r.sleep()
下面看下如何使用该包
box_filter_example.launch
<launch>
<node pkg="laser_filters" type="scan_to_scan_filter_chain" output="screen"
name="laser_filter">
<rosparam command="load" file="$(find pibot_bringup)/params/box_filter.yaml"
/>
</node>
</launch>
box_filter.yaml
scan_filter_chain:
- name: box_filter
type: laser_filters/LaserScanBoxFilter
params:
box_frame: laser_link
min_x: -1.0
max_x: 1.0
min_y: -0.5
max_y: 0.5
3/6
30.laser_filters的使用.md 2/24/2019
min_z: -0.1
max_z: 0.1
使用该配置,我们屏蔽min_至max_ 形成的box里面的数据, 我们通过启动该launch后查看数据的filter_scan见下
box_filter.yaml
scan_filter_chain:
- name: box_filter
type: laser_filters/LaserScanBoxFilter
params:
box_frame: laser_link
min_x: -1.0
max_x: 1.0
min_y: -0.75
max_y: 0.5
min_z: -0.1
max_z: 0.1
4/6
30.laser_filters的使用.md 2/24/2019
使用该配置效果如下图
3.2 scan_to_scan_filter_chain
5/6
30.laser_filters的使用.md 2/24/2019
可以看到该node为一个链,通过插件可以配置多层的过滤,可以配置下列滤波器
LaserScanBoxFilter 无视一个区块内的数据(常用于无视机器人本体对激光雷达数据的干扰)
ScanShadowsFilter 针对物体边沿的扫描和识别
InterpolationFilter 插值滤波
LaserScanIntensityFilter 设定强度阈值,超出则设置为nan
LaserScanRangeFilter 设定距离阈值,超出则设置为nan
LaserScanAngularBoundsFilter 将设定的角度外的扫描数据删除
LaserScanAngularBoundsFilterInPlace 不会删除目标角度扇区外的数据,但会把对应扫描的距离值设为最
大距离阈值+1
LaserArrayFilter 使用中值过滤器等对距离和强度进行过滤
6/6

View File

@ -0,0 +1,28 @@
31.一种简单的基于激光雷达的跟随.md 2/24/2019
本文利用ros_simple_follower在pibot小车上实现简单的基于激光雷达的跟随
ros_simple_follower简介
直接上源码ros_simple_follower
A very simple implementation for ROS to make a mobile robot follow a target. Either using a Laser Range
Finder to follow the closest object 容易理解就是实现一个基于雷达的跟随,跟随目标为距离最近的物体
应用在PIBOT
几点修改
基于激光雷达肯定需要订阅雷达数据定位到laserTracker.py,我们把/hokuyo_base/scan改为/scan
既然是跟随需要发出速度出来定位到follower.py,我们只需要修改/cmd_vel/yolo改为/cmd_vel
可以看到follower.py订阅了个joy,显然还有一个遥控器控制是否开启跟随,我们直接默认开
启,follower.py两行注释即可
测试
首先开启roslaunch pibot_bringup robot.launch
这个robot会开启bringup和lidar
开启跟随roslaunch simple_follower laser_follower.launch 这样小车就会跟谁距离他最近的物
体了,注意这里是物体,谁近他跟谁,包括固定障碍物,这个比较悲剧了,不然就不会设计中加入遥控
器了,有条件的盆友可以插入和遥控器试试效果
1/1

View File

@ -0,0 +1,521 @@
32.PIBOT通讯协议的python解析实现.md 6/2/2019
前文ROS机器人底盘(3)-通讯协议定义了PIBOT的通讯协议该协议较为简单且不与ROS相关同时可以扩展
文使用python实现一个收发可以同时使用在windows或者linux
1. dataHolder
python的struct包可以用来打包和解包字节流针对协议我们定义一个dataHolder包用于打包所有的协议,下面
直接上代码
import struct
# main board
class MessageID:
ID_GET_VERSION = 0
ID_SET_ROBOT_PARAMETER = 1
ID_GET_ROBOT_PARAMETER = 2
ID_INIT_ODOM = 3
ID_SET_VEL = 4
ID_GET_ODOM = 5
ID_GET_PID_DEBUG = 6
ID_GET_IMU = 7
class RobotMessage:
def pack(self):
return b''
def unpack(self):
return True
class RobotFirmwareInfo(RobotMessage):
def __init__(self):
self.version = ''
self.build_time = ''
def unpack(self, data):
try:
upk = struct.unpack('16s16s', bytes(data))
except:
return False
[self.version, self.build_time] = upk
return True
class RobotParameters():
def __init__(self, wheel_diameter=0, \
wheel_track=0, \
encoder_resolution=0, \
do_pid_interval=0, \
kp=0, \
ki=0, \
kd=0, \
ko=0, \
1/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
cmd_last_time=0, \
max_v_liner_x=0, \
max_v_liner_y=0, \
max_v_angular_z=0, \
imu_type=0, \
):
self.wheel_diameter = wheel_diameter
self.wheel_track = wheel_track
self.encoder_resolution = encoder_resolution
self.do_pid_interval = do_pid_interval
self.kp = kp
self.ki = ki
self.kd = kd
self.ko = ko
self.cmd_last_time = cmd_last_time
self.max_v_liner_x = max_v_liner_x
self.max_v_liner_y = max_v_liner_y
self.max_v_angular_z = max_v_angular_z
self.imu_type = imu_type
self.reserve = b"012345678901234567890123456789"
robotParam = RobotParameters()
class GetRobotParameters(RobotMessage):
def __init__(self):
self.param = robotParam
def unpack(self, data):
upk = struct.unpack('<3H1B8H1B%ds'%(64-(3*2+1+8*2+1)), bytes(data))
[self.param.wheel_diameter,
self.param.wheel_track,
self.param.encoder_resolution,
self.param.do_pid_interval,
self.param.kp,
self.param.ki,
self.param.kd,
self.param.ko,
self.param.cmd_last_time,
self.param.max_v_liner_x,
self.param.max_v_liner_y,
self.param.max_v_angular_z,
self.param.imu_type,
self.param.reserve] = upk
return True
class SetRobotParameters(RobotMessage):
def __init__(self):
self.param = robotParam
def pack(self):
data = [self.param.wheel_diameter,
self.param.wheel_track,
self.param.encoder_resolution,
2/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
self.param.do_pid_interval,
self.param.kp,
self.param.ki,
self.param.kd,
self.param.ko,
self.param.cmd_last_time,
self.param.max_v_liner_x,
self.param.max_v_liner_y,
self.param.max_v_angular_z,
self.param.imu_type,
self.param.reserve]
pk = struct.pack('<3H1B8H1B%ds'%(64-(3*2+1+8*2+1)), *data)
return pk
def unpack(self, data):
return True
class RobotVel(RobotMessage):
def __init__(self):
self.v_liner_x = 0
self.v_liner_y = 0
self.v_angular_z = 0
def pack(self):
data = [self.v_liner_x,
self.v_liner_y,
self.v_angular_z]
pk = struct.pack('3h', *data)
return pk
def unpack(self, data):
return True
#todo the rest of the message classes
class RobotOdom(RobotMessage):
def __init__(self):
self.v_liner_x = 0
self.v_liner_y = 0
self.v_angular_z = 0
self.x = 0
self.y = 0
self.yaw = 0
def unpack(self, data):
try:
upk = struct.unpack('<3H2l1H', bytes(data))
except:
return False
[self.v_liner_x,
self.v_liner_y,
self.v_angular_z,
self.x,
self.y,
3/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
self.yaw] = upk
return True
class RobotPIDData(RobotMessage):
pass
class RobotIMU(RobotMessage):
def __init__(self):
self.imu = [0]*9
def unpack(self, data):
try:
upk = struct.unpack('<9f', bytes(data))
except:
return False
self.imu = upk
return True
BoardDataDict = {MessageID.ID_GET_VERSION:RobotFirmwareInfo(),
MessageID.ID_GET_ROBOT_PARAMETER:GetRobotParameters(),
MessageID.ID_SET_ROBOT_PARAMETER:SetRobotParameters(),
MessageID.ID_SET_VEL:RobotVel(),
MessageID.ID_GET_ODOM:RobotOdom(),
MessageID.ID_GET_PID_DEBUG: RobotPIDData(),
MessageID.ID_GET_IMU: RobotIMU(),
}
每条协议定义一个类该类都从RobotMessage继承而来针对协议读写类比分别实现pack和unpack接
BoardDataDict为id到该类对象的映射绑定
新增协议只需要新增实现一个从RobotMessage继承的类同时添加到BoardDataDict映射表中即可
具体可以参考IMU数据
2 transport
import sys
sys.path.append("..")
import pypibot
from pypibot import log
import serial
import threading
import struct
import time
from dataholder import MessageID, BoardDataDict
FIX_HEAD = 0x5a
class Recstate():
4/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
WAITING_HD = 0
WAITING_MSG_ID = 1
RECEIVE_LEN = 2
RECEIVE_PACKAGE = 3
RECEIVE_CHECK = 4
def checksum(d):
sum = 0
for i in d:
sum += ord(i)
sum = sum&0xff
return sum
class Transport:
def __init__(self, port, baudrate=921600):
self._Port = port
self._Baudrate = baudrate
self._KeepRunning = False
self.receive_state = Recstate.WAITING_HD
self.rev_msg = []
self.rev_data = []
self.wait_event = threading.Event()
def getDataHolder(self):
return BoardDataDict
def start(self):
try:
self._Serial = serial.Serial(port=self._Port, baudrate=self._Baudrate,
timeout=0.2)
self._KeepRunning = True
self._ReceiverThread = threading.Thread(target=self._Listen)
self._ReceiverThread.setDaemon(True)
self._ReceiverThread.start()
return True
except:
return False
def Stop(self):
self._KeepRunning = False
time.sleep(0.1)
self._Serial.close()
def _Listen(self):
while self._KeepRunning:
if self.receiveFiniteStates(self._Serial.read()):
self.packageAnalysis()
def receiveFiniteStates(self, s):
if len(s) == 0:
return False
val = s[0]
if self.receive_state == Recstate.WAITING_HD:
5/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
if ord(val) == FIX_HEAD:
log.debug('got head')
self.rev_msg = []
self.rev_data =[]
self.rev_msg.append(val)
self.receive_state = Recstate.WAITING_MSG_ID
elif self.receive_state == Recstate.WAITING_MSG_ID:
log.debug('got msg id')
self.rev_msg.append(val)
self.receive_state = Recstate.RECEIVE_LEN
elif self.receive_state == Recstate.RECEIVE_LEN:
log.debug('got len:%d', ord(val))
self.rev_msg.append(val)
if ord(val) == 0:
self.receive_state = Recstate.RECEIVE_CHECK
else:
self.receive_state = Recstate.RECEIVE_PACKAGE
elif self.receive_state == Recstate.RECEIVE_PACKAGE:
self.rev_data.append(val)
if len(self.rev_data) == ord(self.rev_msg[-1]):
self.rev_msg.extend(self.rev_data)
self.receive_state = Recstate.RECEIVE_CHECK
elif self.receive_state == Recstate.RECEIVE_CHECK:
log.debug('got check')
self.receive_state = Recstate.WAITING_HD
if ord(val) == checksum(self.rev_msg):
log.debug('got a complete message')
return True
else:
self.receive_state = Recstate.WAITING_HD
# continue receiving
return False
def packageAnalysis(self):
in_msg_id = ord(self.rev_msg[1])
log.debug(bytes(self.rev_data))
if BoardDataDict[in_msg_id].unpack(''.join(self.rev_data)):
self.res_id = in_msg_id
if in_msg_id<100:
self.set_response()
else:#notify
log.debug('msg %d'%self.rev_msg[4],'data incoming')
pass
else:
log.debug ('error unpacking pkg')
def request(self, id, timeout=0.5):
if not self.write(id):
log.debug ('Serial send error!')
return False
if self.wait_for_response(timeout):
if id == self.res_id:
6/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
log.debug ('OK')
else:
log.error ('Got unmatched response!')
else:
log.error ('Request got no response!')
return False
# clear response
self.res_id = None
return True
def write(self, id):
self._Serial.write(self.make_command(id))
return True
def wait_for_response(self, timeout):
self.wait_event.clear()
return self.wait_event.wait(timeout)
def set_response(self):
self.wait_event.set()
def make_command(self, id):
data = BoardDataDict[id].pack()
l = [FIX_HEAD, id, len(data)]
head = struct.pack("3B", *l)
body = head + data
return body + chr(checksum(body))
if __name__ == '__main__':
mboard = Transport('com1')
if not mboard.start():
import sys
sys.exit()
p = mboard.request(MessageID.ID_GET_VERSION)
log.i("result=%s"%p)
利用serial包完成串口通讯
打开接收线程用来处理回复消息
可以看到在发送消息的时make_command中通过data = BoardDataDict[id].pack()打包得到数据
同事在接收消息的时packageAnalysis中通过
BoardDataDict[in_msg_id].unpack(''.join(self.rev_data)解包得到数据
测试
main.py
import platform
import sys
sys.path.append("..")
7/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
import pypibot
from pypibot import log
from transport import Transport
from dataholder import MessageID
import params
port="com10"
TEST_SET_PARAM = True
pypibot.enableGlobalExcept()
#log.enableFileLog(log_dir + "ros_$(Date8)_$(filenumber2).log")
log.setLevel("i")
if __name__ == '__main__':
mboard = Transport(port, params.pibotBaud)
if not mboard.start():
log.error("can not open %s"%port)
sys.exit()
DataHolder = mboard.getDataHolder()
log.info("****************get robot version*****************")
boardVersion = DataHolder[MessageID.ID_GET_VERSION]
p = mboard.request(MessageID.ID_GET_VERSION)
if p:
log.info("firmware version:%s buildtime:%s\r\n"%
(boardVersion.version.decode(), boardVersion.build_time.decode()))
else:
log.error('request firmware version err\r\n')
quit(1)
# get robot parameter
robotParam = DataHolder[MessageID.ID_GET_ROBOT_PARAMETER]
p = mboard.request(MessageID.ID_GET_ROBOT_PARAMETER)
if p:
log.info("wheel_diameter:%d wheel_track:%d encoder_resolution:%d" \
%(robotParam.param.wheel_diameter, \
robotParam.param.wheel_track, \
robotParam.param.encoder_resolution
))
log.info("do_pid_interval:%d kp:%d ki:%d kd:%d ko:%d" \
%(robotParam.param.do_pid_interval, \
robotParam.param.kp, \
robotParam.param.ki, \
robotParam.param.kd, \
robotParam.param.ko))
log.info("cmd_last_time:%d imu_type:%d" \
%(robotParam.param.cmd_last_time,\
robotParam.param.imu_type
))
log.info("max_v:%d %d %d\r\n" \
8/9
32.PIBOT通讯协议的python解析实现.md 6/2/2019
%(robotParam.param.max_v_liner_x,\
robotParam.param.max_v_liner_y, \
robotParam.param.max_v_angular_z
))
else:
log.error('request get param err\r\n')
quit(1)
if TEST_SET_PARAM:
log.info("****************set robot parameter*****************")
DataHolder[MessageID.ID_SET_ROBOT_PARAMETER].param = params.pibotParam
p = mboard.request(MessageID.ID_SET_ROBOT_PARAMETER)
if p:
log.info('request set parameter success')
else:
log.error('request set parameter err')
quit(1)
log.info("****************get odom&imu*****************")
while True:
robotOdom = DataHolder[MessageID.ID_GET_ODOM]
p = mboard.request(MessageID.ID_GET_ODOM)
if p:
log.info('request get odom success vx=%d vy=%d vangular=%d x=%d y=%d
yaw=%d'%(robotOdom.v_liner_x, \
robotOdom.v_liner_y, \
robotOdom.v_angular_z, \
robotOdom.x, \
robotOdom.y, \
robotOdom.yaw))
else:
log.error('request get odom err')
quit(1)
robotIMU = DataHolder[MessageID.ID_GET_IMU].imu %f %f %f\n
p = mboard.request(MessageID.ID_GET_IMU)
if p:
log.info('request get imu success \nimu=[%f %f %f\n
%f %f %f]\n'%(robotIMU[0], robotIMU[1], robotIMU[2], \
robotIMU[3], robotIMU[4], robotIMU[5], \
robotIMU[6], robotIMU[7], robotIMU[8]))
else:
log.error('request get imu err')
quit(1)
9/9

View File

@ -0,0 +1,114 @@
33.ROS程序开机自启动.md 6/2/2019
1.概述
写了这么多ROS的launch脚本怎么才能开机就启动呢本文介绍1种方法使用ROS的 robot_upstart包,下面以
pibot_bringuppackage的·bringup.launch`演示
2.robot_upstart包
2.1 安装
使用pibot_install_ros.sh一键安装工具即可安装所有pibot所需要的ROS包如需单独安装sudo apt-get
install ros-{ROS_DIST}-robot-upstart
即为 或者 ROS_DIST indigo kinetic
2.2 设置
rosrun robot_upstart install pibot_bringup/launch/bringup.launch
pibot@pibot-desktop:~$ rosrun robot_upstart install
pibot_bringup/launch/bringup.launch
/lib/systemd/systemd
Preparing to install files to the following paths:
/etc/ros/kinetic/pibot.d/.installed_files
/etc/ros/kinetic/pibot.d/bringup.launch
/etc/systemd/system/multi-user.target.wants/pibot.service
/lib/systemd/system/pibot.service
/usr/sbin/pibot-start
/usr/sbin/pibot-stop
Now calling: /usr/bin/sudo /opt/ros/kinetic/lib/robot_upstart/mutate_files
Filesystem operation succeeded.
** To complete installation please run the following command:
sudo systemctl daemon-reload && sudo systemctl start pibot
照着提示操作 sudo systemctl daemon-reload && sudo systemctl start pibot
2.3 测试
ps -aux | grep pibot_bringup查看进程
pibot@pibot-desktop:~/pibot_ros$ ps -aux | grep pibot_bringup
pibot 15971 96.3 0.7 85620 7488 ? Rsl 23:26 1:13
/home/pibot/pibot_ros/ros_ws/devel/lib/pibot_bringup/pibot_driver
__name:=pibot_driver __log:=/tmp/0656ed38-5ba5-11e9-be9a-
b827ebff3168/pibot_driver-2.log
1/3
33.ROS程序开机自启动.md 6/2/2019
已经可以查到该进程了
rosnode list查看node
pibot@pibot-desktop:~$ rosnode list
/pibot_driver
/rosout
也可以看到2个node
roslaunch pibot keyboard_teleop.launch启动键盘控制程序也可以支持控制小车了
为了验证程序是否开启启动了重启后再次重复2.3的测试步骤即可
2.4 停止以及取消开机启动
启动&停止
sudo service pibot start
sudo service pibot stop
取消
rosrun robot_upstart uninstall pibot
3 robot_upstart服务名称
可以看到上面的service名称为pibot通过查看源码可以看到
job_name = args.job or pkg.split('_', 1)[0]
job_name取了args.job或者包名的下划线前面上面例子args.job为空则用了pibot_bringup下划线前面即
pibot
我们只需要指定job参数即可自定义
2/3
33.ROS程序开机自启动.md 6/2/2019
rosrun robot_upstart install pibot_bringup/launch/bringup.launch --job=mybringup
我们就可以使用sudo service mybringup start启动服务
4.总结
添加启动项
rosrun robot_upstart install pibot_bringup/launch/bringup.launch --job=mybringup
sudo systemctl daemon-reload
删除启动项
rosrun robot_upstart uninstall mybringup
当次启动服务
sudo service mybringup start
当次关闭服务
sudo service mybringup stop
查看服务状态
sudo service mybringup status
3/3

View File

@ -0,0 +1,90 @@
34.laser_filters的使用(2).md 6/2/2019
前面ROS机器人底盘(30)-laser_filters的使用1讲了如何使用laser_filters,本文在hades上试下如何使用
laser_filters屏蔽车身的摄像头支架
1.车体支架干扰
首先启动建图
pibot_gmapping
虚拟机启动RVIZ
pibot_view
可以看到受到摄像头支架干扰,雷达在车体上检测到障碍,这会直接干扰到建图和导航
2.屏蔽支架干扰
2.1添加laser_filters
添加ROS机器人底盘(30)-laser_filters的使用1这里的2个文件放置下面目录
pibot_bringup/launch/box_filter_example.launch
pibot_bringup/params/box_filter.yaml
根据车上宽度(200mm)和长度(300mm)分别修改box_filter.yaml中的参数
1/4
34.laser_filters的使用(2).md 6/2/2019
设置稍微超出一点 x方向我们设置-0.15之0.15y方向设置-0.2至0.2
scan_filter_chain:
- name: box_filter
type: laser_filters/LaserScanBoxFilter
params:
box_frame: laser_link
min_x: -0.15
max_x: 0.15
min_y: -0.20
max_y: 0.20
min_z: -0.1
max_z: 0.1
这里z值忽略
添加该launch文件到robot.launch
查看laser_filter发出topic rosnode info laser_filter可以找到发出的scan_filtered的topic
2.2测试添加效果
重新启动pibot_gmapping
如果提示下图错误需要安装laser_filters包, 使用pibot_install_ros.sh或者sudo apt-get
install ros-kinetic-laser-filters
2/4
34.laser_filters的使用(2).md 6/2/2019
重新打开pibot_view,如果不出意外应该跟上面一样原因这里我们修改添加laser_filters发出来的
topic还未使用修改rviz中scan topic
修改后可以看到车体再无干扰点出现
2.3修改订阅参数
刚才修改的只是Rviz的订阅参数真正导航和建图还是用的之前就的scan
可以通过rosnode info xxx查看订阅node订阅topic情况,如rosnode info xxx
查看下scant topic的信息
rostopic info /scan
可以看到除了被rviz订阅外还被
laser_filter,move_base和slam_gmapping订阅我们只需要把move_base和slam_gmapping中订阅scan的
3/4
34.laser_filters的使用(2).md 6/2/2019
topic改为laser_filter发出的topic
替换scan为scan_filtered 具体都是配置不知道我们可以查找下
roscd pibot_navigation
grep -rn scan
修改相应文件并重启pibot_gmapping即可
4/4

View File

@ -0,0 +1,83 @@
35.pibot_init_env介绍.md 6/2/2019
1.多机通讯
因车载主机限制(没有屏幕或者性能有限无法启动Rviz查看建图等信息ROS多机的通讯配置一文讲了具体
的原理细节即配置一个主机和一个从机把从机的ROS_MASTER_URI指向主机即可一般我把车载的主机配
置指定为主机而用于显示的PC或者虚拟机配置为从机。PIBOT提供了一键配置的脚本按照提示选择即可
PIBOT使用手册可以看到
对于车载的主机
machice type为0即为主机
对于用于显示的主机或者虚拟机
machice type为非0即为从机同是作为从机需要新增一个配置主机IP
2.pibot_init_env脚本
具体看下pibot_init_env做了什么
添加PIBOT_ENV_INITIALIZED环境变量
添加source ~/.pibotrc至~/.basrc 根据PIBOT_ENV_INITIALIZED是否定义保证source
~/.pibotrc只被添加一次 执行pibot_init_env后~/.bashrc文件如下118 119行
1/4
35.pibot_init_env介绍.md 6/2/2019
添加udev rules 可以在usb插入根据PID/VID生成/dev/pibot和/dev/rplidar等软连接而不需指定
具体的/dev/ttyUSBn
根据驱动板型号设置波特率 最终执行python ros_ws/src/pibot_bringup/scripts/set_baud.py
115200或者python ros_ws/src/pibot_bringup/scripts/set_baud.py 921600
添加~/.pibotrc文件
source /opt/ros/kinetic/setup.bash
LOCAL_IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | awk -F/
'{print $1}'`
export ROS_IP=`echo $LOCAL_IP`
export ROS_HOSTNAME=`echo $LOCAL_IP`
export PIBOT_MODEL=hades
export PIBOT_LIDAR=rplidar
export PIBOT_BOARD=stm32f4
export ROS_MASTER_URI=http://`echo $LOCAL_IP`:11311
source ~/pibot_ros/ros_ws/devel/setup.bash
/opt/ros/kinetic/setup.bash 生效ROS的环境变量,如果没安装好ROS会报该文件不存在的错误
2/4
35.pibot_init_env介绍.md 6/2/2019
第二行给LOCAL_IP赋值我们尝试直接在终端输出该变量echo $LOCAL_IP
可以看到得到的
值跟ifconfig查看的一致可以知道该值为当前IP
第三四行分别ROS_IP和ROS_HOSTNAME 这里使用的本地IP
第五六七行分别模型名称,雷达名称,驱动板名称
对于模型名称pibot_bringup/launch/model.launch会使用到该变量
从而加载对应的模型文件
对于雷达名称pibot_bringup/launch/robot.launch会使用到该变量
从而加载对于的雷达文件
驱动板名称尚未使用(只在运行是设置波特率)
export ROS_MASTER_URI=xxxx 这里是主机和从机的唯一区别的地方,
3/4
35.pibot_init_env介绍.md 6/2/2019
对于主机可以看到直接使用本地的IP,export ROS_MASTER_URI=http://`echo
$LOCAL_IP`:11311
对于从机直接使用的手动如输入的IP,export ROS_MASTER_URI=http://192.168.2.231:11311
最后一行即为生效PIBOT驱动包的环境变量 这里需要编译,不然会提示文件不存在
3.总结
其实如ROS多机的通讯配置所讲就是设置了ROS_IP ROS_HOSTNAME和ROS_MASTER_URI三个环境变量前2个主
机从机都是本机IP后一个主机为本机IP从机为主机IP我们可以输出这几个变量或者使用pibot_view_env
查看
4/4

View File

@ -0,0 +1,31 @@
36.USB摄像头使用.md 6/2/2019
USB摄像头
PIBOT机器人小车可以接入 USB摄像头作为监控摄像头可以实时传输视频流
查看 USB摄像头节点 ls /dev/video* -al
修改节点名称
roscd pibot/launch/
vi usb_camera.launch
启动 USB摄像头
roslaunch pibot usb_camera.launch
1/4
36.USB摄像头使用.md 6/2/2019
查看视频流
电脑端(多机通讯) rosrun image_view image_view image=:/usb_cam/image_raw
手机app启动app输入小车主机ip建立连接后可以直接看到视频流
2/4
36.USB摄像头使用.md 6/2/2019
3/4
36.USB摄像头使用.md 6/2/2019
4/4

View File

@ -0,0 +1,38 @@
37.使用探索模式实现指定区域建图.md 6/2/2019
1.frontier_exploration
使用frontier_exploration可以实现指定区域自动探索建图
2.安装
sudo apt-get install ros-kinectic-frontier-exploration完成安装
其他ros版本请安装对应的包也可以使用pibot_install_ros.sh脚本可以一键完成安装
3.启动frontier_exploration
roslaunch pibot_navigation frontier_exploration.launch
可以看到frontier_exploration.launch包含其他文件其中有个gmapping,这里frontier_exploration只
是一个探索算法真正SLAM算法用的是gmmapping
4.选择区域
roslaunch pibot_navigation view_nav.launch
1/3
37.使用探索模式实现指定区域建图.md 6/2/2019
添加topic的监控
选择区域
选择publish point,在地图选择若干点构成一个封闭的多边形
2/3
37.使用探索模式实现指定区域建图.md 6/2/2019
启动探索 选择publish point在上面多边形内随机选择一点点击即可开始探索
3/3

View File

@ -0,0 +1,76 @@
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
1.简述
2.安装sw_urdf_exporter插件
3.Rviz展示机器人模型
4.备注
1.简述
ROS中通过RVIZ可视化机器人导航情况其中URDFUnified Robot Description Format是统一的机器人描
述文件此类文件通过HTML格式定义机器人模型Robot Model的相关信息连杆、关节名称运动学
参数、动力学参数、可视化模型、碰撞检测模型等。
例如下面的URDF文件定义了机器人头部关节
<joint name="head_swivel" type="continuous">
<parent link="base_link"/>
<child link="head"/>
<axis xyz="0 0 1"/>
<origin xyz="0 0 0.3"/>
</joint>
当机器人比较复杂时URDF文件会很长不可能全部手动定义。那么如何快速建立URDF文件来描述自己的机
器人?
2.安装sw_urdf_exporter插件
ROS目前在SolidWorks中提供了自动生成URDF文件的插件。官网地址[http://wiki.ros.org/sw_urdf_exporter]
安装成功后SW中会多出一个SW2URDF插件勾选启用
1/6
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
3D模型建立完后点击菜单栏里file->Export as URDF
进入URDF配置界面在左侧URDF Exporter中输入连杆名称选择属于该连杆的模型文件子连杆数。这里简
单的将除激光雷达意外部分全部定义为base_link 它将有1个子连杆激光雷达
2/6
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
这时base_link下会多出一个Empty_link点击编辑。
给连杆起名为laser_link关节名称为laser_link_joint选择激光雷达3d模型没有子连杆然后点击Preview
and Export...
3/6
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
随后会出现窗口设置具体连杆关节参数,可直接跳过。
完成后, 会生产一个URDF package里面launch文件夹中有display.launch可以直接在ROS中运行。
3.Rviz展示机器人模型
启动Rviz即可展示机器人模型roslaunch pibot_description display.launch SW 2014下生产的launch
文件运行可能会报以下错误:
4/6
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
原因是路径有误没有robots文件夹生产的urdf文件在urdf文件夹中手动修改即可。 另外可能出现找不到
3d模型文件的错误原因是meshes文件夹中模型文件后缀大小写和urdf文件中不匹配手动修改匹配即可。
5/6
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
重新启动后显示如下
4.备注
这里我们定义了2个link一个是底盘base_link, 另一个是laser_link导航中我们需要用到这2个link
6/6

View File

@ -0,0 +1,174 @@
5. 编码器与PID控制.md 10/20/2018
1.编码器基础
1.1绝对式编码器
1.2增量式编码器
1.3 4倍频
1.4 测试代码
2.PID控制
2.1PID控制的原理
比例P控制
积分I控制
微分D控制
2.2实际模型
2.3具体实现
1.编码器基础
对机器人实现位置和速度的控制需要使用传感器获取机器人运动的信息,编码器是常用的方式。常见的编码器
有增量式编码器和绝对式编码器。
1.1绝对式编码器
绝对式编码器通过对码盘上的各个位置设计特定的编码,可以输出转动轴的绝对位置信息。
1.2增量式编码器
增量式编码器无法直接得到轴的绝对位置信息。对于轴的每一圈转动,增量式编码器提供一定数量的脉冲(通
常编码器参数中用n线/n脉冲来表示。通过测量脉冲的数量可以得到旋转的角度或者测量单位时间内的脉冲
数可以得到轴的转速。
1/5
5. 编码器与PID控制.md 10/20/2018
但这里涉及到方向的问题单纯从一相的脉冲输出无法判断轴的旋转方向所以通常增量式编码器通过输出AB
两相脉冲实现方向的测量。AB两相脉冲之间相差90°。
例如上图中的增量式编码器脉冲当编码器正转时B相脉冲的上升沿对应A相高电平而当编码器反转时B
相上升沿对应A向低电平。如此判断旋转方向。
旋转编码器很重要的参数是其每圈输出的脉冲数以400线的编码器为例每转动一圈AB相各输出400个脉
冲。通过4倍频可以实现更高的测量精度。
1.3 4倍频
所谓4倍频就是对AB相的上升沿和下降沿均作检测这样在一个周期内有四种状态如下
旋转方向为正向时A相上升沿对应B相低电平 -> B相上升沿对应A相高电平 -> A相下降沿对应B相高电平 -> B
相下降沿对应A相低电平
旋转方向为反向时B相上升沿对应A相低电平 -> A相上升沿对应B相高电平 -> B相下降沿对应A相高电平 -> A
相下降沿对应B相低电平
1.4 测试代码
具体代码实现可以通过Arduino中的中断判断CHANGE很容易实现4倍频。
#define Phase_A 2 //定义A相引脚
#define Phase_B 3 //定义B相引脚
unsigned long time;
long Position = 0;
boolean A = false;
boolean B = false;
2/5
5. 编码器与PID控制.md 10/20/2018
void setup()
{
pinMode(Phase_A, INPUT_PULLUP);//内部上拉,防止信号干扰
pinMode(Phase_B, INPUT_PULLUP);
attachInterrupt(0, Interrupt_A, CHANGE);//检测上升沿、下降沿
attachInterrupt(1, Interrupt_B, CHANGE);//检测上升沿、下降沿
Serial.begin(115200); //初始化Arduino串口
}
void loop(){
time = millis();
if(time%30 == 0){
Serial.println(Position,DEC);
}
}
void Interrupt_A(){
if(A == false){
if( B == false){Position = Position + 1;A == true;}
else{Position = Position - 1;A == true;}
}
else{
if( B == false){Position = Position - 1;A == false;}
else{Position = Position + 1;A == false;}
}
}
void Interrupt_B(){
if(B == false){
if( A == false){Position = Position - 1;B == true;}
else{Position = Position + 1;A == true;}
}
else{
if( A == false){Position = Position + 1;B == false;}
else{Position = Position - 1;B == false;}
}
}
2.PID控制
2.1PID控制的原理
在工程实际中PID控制是应用最为广泛控制方式。PID为比例、积分、微分的缩写。当被控制的系统的结构和
参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数
必须依靠经验和现场调试来确定这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象﹐
或不能通过有效的测量手段来获得系统参数时最适合用PID控制技术。PID控制实际中也有PI和PD控制。
PID控制器就是根据系统的误差利用比例、积分、微分计算出控制量来对系统进行控制。
3/5
5. 编码器与PID控制.md 10/20/2018
比例P控制
比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。当仅有比例控制时系统输出
存在稳态误差Steady-state error
积分I控制
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存
在稳态误差则称这个控制系统是有稳态误差的或简称有差系统System with Steady-state Error。为了消除
稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增
大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减
小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。
微分D控制
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。 自动控制系统在克服误
差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,
具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误
差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作
用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控
制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大
惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。
2.2实际模型
对于3WD全向小车在对整体运动进行解算后得到各个轮子的期望转速Vd以这个转速作为PID控制的输入
输出为控制电机的PWM值反馈则为通过编码器反馈值计算出的实际速度V。闭环系统如下
在实际操作中由于是通过程序进行PID的计算无法达到连续的积分和微分计算所以用离散处理。给定PID
控制频率周期性地进行PID控制计算。
主要参数:
pid_interval 控制周期 ms
kp 比例系数
ki 积分系数
4/5
5. 编码器与PID控制.md 10/20/2018
kd 微分系数
ko 统一放大缩小比例
2.3具体实现
见下PIBOT Firmware代码分析
5/5

View File

@ -0,0 +1,208 @@
6. PIBOT的通讯协议.md 10/20/2018
1.说明
2.硬件板卡通讯协议
2.1消息定义
消息头
校验码
3.1消息id及消息体
1.说明
底盘驱动板Ardunio Mega2560/STM32与树莓派通过USB串口通讯
2.硬件板卡通讯协议
板卡通讯协议使用串口进行通信,每条消息包含消息头和消息体校验码三部分,所有数字采用小端序传递
2.1消息定义
每条消息都由 [消息头]+[消息体]+[校验码] 3部分组成,每条消息最大长度为3+255+1字节,消息体最大255字节
消息头
消息头C风格定义如下:
struct head
{
unsigned char flag;// 头部标记,固定值:0X5A
unsigned char msg_id;// 消息ID,表示消息具体作用,决定消息体具体格式
unsigned char length;// 消息体长度
}
校验码
校验码固定占一个字节空间,为消息头+消息体 每个字节相加得到的结果取低8位的值
3.1消息id及消息体
msg id name
0 请求:固件版本
1 请求:设置机器人配置参数
2 请求:获取机器人配置参数
3 请求:初始化里程
4 请求:设置机器人实时速度
1/4
6. PIBOT的通讯协议.md 10/20/2018
msg id name
5 请求:获取里程计信息
6 请求:获取PID调速数据
请求:固件信息
方向:MASTER->Board
内容:
回应:固件信息
方向:Board->MASTER
内容:
{
char version[16]; //固件版本
char time[16];//构建时间
}
请求:设置机器人配置参数(出厂配置)
方向:MASTER->Board
内容:
{
union
{
char buff[64];
struct
{
unsigned short wheel_diameter; //轮子直径 mm
unsigned short wheel_track; //差分:轮距, 三全向轮:直径,四
全向:前后轮距+左右轮距 mm
unsigned short encoder_resolution; //编码器分辨率
unsigned char do_pid_interval; //pid间隔 (ms)
unsigned short kp;
unsigned short ki;
unsigned short kd;
unsigned short ko; //pid参数比例
unsigned short cmd_last_time; //命令持久时间ms 超过该时间会自
动停止运动
}
}
}
回应:设置机器人配置参数
方向:Board->MASTER
内容:无
2/4
6. PIBOT的通讯协议.md 10/20/2018
请求:获取机器人配置参数(出厂配置)
方向:MASTER->Board
内容:
方向:MASTER->Board
内容:同设置
请求:初始化里程
方向:MASTER->Board
内容:
回应:初始化里程结果
方向:Board->MASTER
内容:无
请求:设置机器人实时速度
方向:MASTER->Board
内容:
{
short v_liner_x; //线速度 前>0 cm/s
short v_liner_y; //差分轮 为0 cm/s
short v_angular_z; //角速度 左>0 0.01rad/s 100 means 1 rad/s
}
回应:设置机器人实时速度结果
方向:Board->MASTER
内容:无
请求:里程计信息
方向:MASTER->Board
内容:
回应:里程计信息
方向:Board->MASTER
内容:
{
short v_liner_x; //线速度 前>0 后<0 cm/s
short v_liner_y; //差分轮 为0 cm/s
short v_angular_z; //角速度 左>0 右<0 0.01rad/s 100 means 1 rad/s
long x; //里程计坐标x cm (这里long为4字节下同)
long y; //里程计坐标y cm
short yaw; //里程计航角 0.01rad 100 means 1 rad
}
3/4
6. PIBOT的通讯协议.md 10/20/2018
请求:获取PID调速数据
方向:MASTER->Board
内容:
回应:PID调速数据
方向:Board->MASTER
内容:
{
long input[4]; //各轮子驱动输入值
long output[4]; //个轮子输出值
}
4/4

View File

@ -0,0 +1,510 @@
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
1.包列表
2.main分析
3.robot文件分析
3.1robot.h
3.2robot.cpp关键代码
初始化
电机及编码器相关初始化
通讯相关初始化
上位命令处理
运动处理
计算里程位置姿态
1.包列表
包 说明 备注
AFMotor Adafruit shield电机驱动板驱动
Board 板子资源接口及Mega2560中的实现
DataHolder 关键数据存储类
Encoder 编码器接口及Mega2560中编码器实现
KinematicModels 机器人模型类 针对不同型号机型的解算
Motor 电机驱动接口类及AF电机驱动与一般驱动板的实现
PID PID运算类
Transport 通讯接口类与实现
robot robot调度类
1/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
2.main分析
直接贴源码,无需赘述
#include "Arduino.h"
#include "robot.h"
void setup()
{
Robot::get()->init();
}
void loop()
{
Robot::get()->check_command();
Robot::get()->do_kinmatics();
Robot::get()->calc_odom();
}
3.robot文件分析
3.1robot.h
#ifndef PIBOT_ROBOT_H_
#define PIBOT_ROBOT_H_
#include "dataframe.h"
//根据机器人模型选择相应的解算类头文件
#if ROBOT_MODEL == ROBOT_MODEL_DIFF//差分轮
#include "differential.h"
#endif
#if ROBOT_MODEL == ROBOT_OMNI_3//全向三轮
#include "omni3.h"
#endif
class MotorController;//电机控制器接口类
class Encoder; //编码器接口类
class PID; //PID运算接口类
class Transport; //通讯接口类
class Dataframe; //通讯协议数据包接口类
class Model; //机器人模型接口类
class Robot:public Notify{
public:
//单例模式
static Robot* get(){
static Robot robot;
2/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
return &robot;
}
//初始化
void init();
//检测上位机命令
void check_command();
//运动学控制下发的速度转换为各个轮子速度平且进行PID控制
void do_kinmatics();
//根据个轮子编码器反馈给出机器人位置姿态及实时速度信息
void calc_odom();
//Notify接口实现 针对某些消息关注的回调函数
void update(const MESSAGE_ID id, void* data);
private:
Robot(){}
void init_motor();//初始化电机相关
void init_trans();//初始化通讯相关
private:
void clear_odom();//清除累计的位置姿态信息
void update_velocity();//更新下发的实时控制速度
private:
MotorController* motor[MOTOR_COUNT];//电机控制器接口
Encoder* encoder[MOTOR_COUNT];//编码器接口
PID* pid[MOTOR_COUNT];
float input[MOTOR_COUNT];//PID间隔时间内期望的encoder增加或减少的
个数
float feedback[MOTOR_COUNT];//PID间隔时间内反馈的encoder增加或减少
的个数
Transport* trans;//通讯接口
Dataframe* frame;//通讯数据包接口
Model* model;//机器人模型接口
bool do_kinmatics_flag; //进行运动控制的标记
Odom odom;//机器人位置姿态实时速度信息
unsigned long last_velocity_command_time;//上次下发速度的时间点
};
#endif
3.2robot.cpp关键代码
初始化
void Robot::init(){
Data_holder::get()->load_parameter();//加载EPPROM中的配置机器人信息
3/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
#if DEBUG_ENABLE
Board::get()->usart_debug_init();//调试串口初始化 printf通过串口3输出
#endif
printf("RobotParameters: %d %d %d %d %d %d %d %d %d %d %d %d\r\n",
Data_holder::get()->parameter.wheel_diameter, Data_holder::get()-
>parameter.wheel_track, Data_holder::get()->parameter.encoder_resolution,
Data_holder::get()->parameter.do_pid_interval, Data_holder::get()-
>parameter.kp, Data_holder::get()->parameter.ki, Data_holder::get()->parameter.kd,
Data_holder::get()->parameter.ko,
Data_holder::get()->parameter.cmd_last_time, Data_holder::get()-
>parameter.max_v_liner_x, Data_holder::get()->parameter.max_v_liner_y,
Data_holder::get()->parameter.max_v_angular_z);
printf("init_motor\r\n");
init_motor();
printf("init_trans\r\n");
init_trans();
printf("pibot startup\r\n");
}
电机及编码器相关初始化
void Robot::init_motor(){
#if MOTOR_COUNT>0//根据配置的个数定义编码器、电机控制器及PID的具体实现MOTOR_COUNT在
具体的机器人模型中定义
#if MOTOR_CONTROLLER == COMMON_CONTROLLER//根据使用的电机控制器选择电机控制器的实
现 这里CommonMotorController与AFSMotorController都MotorController接口类的实现
static CommonMotorController motor1(MOTOR_1_PWM_PIN, MOTOR_1_DIR_A_PIN,
MOTOR_1_DIR_B_PIN, MAX_PWM_VALUE);
#elif MOTOR_CONTROLLER == AF_SHIELD_CONTROLLER
static AFSMotorController motor1(MOTOR_1_PORT_NUM, MAX_PWM_VALUE);
#endif
//EncoderImp是Encoder接口类的实现
static EncoderImp encoder1(MOTOR_1_ENCODER_A_PIN, MOTOR_1_ENCODER_B_PIN);
//PID计算接口给定参数为期望的数据地址、反馈的数据地址以及各个PID参数值
static PID pid1(&input[0], &feedback[0], float(Data_holder::get()-
>parameter.kp)/Data_holder::get()->parameter.ko,
float(Data_holder::get()-
>parameter.ki)/Data_holder::get()->parameter.ko,
float(Data_holder::get()-
>parameter.kd)/Data_holder::get()->parameter.ko , MAX_PWM_VALUE);
#endif
#if MOTOR_COUNT>1
#if MOTOR_CONTROLLER == COMMON_CONTROLLER
4/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
static CommonMotorController motor2(MOTOR_2_PWM_PIN, MOTOR_2_DIR_A_PIN,
MOTOR_2_DIR_B_PIN, MAX_PWM_VALUE);
#elif MOTOR_CONTROLLER == AF_SHIELD_CONTROLLER
static AFSMotorController motor2(MOTOR_2_PORT_NUM, MAX_PWM_VALUE);
#endif
static EncoderImp encoder2(MOTOR_2_ENCODER_A_PIN, MOTOR_2_ENCODER_B_PIN);
static PID pid2(&input[1], &feedback[1], float(Data_holder::get()-
>parameter.kp)/Data_holder::get()->parameter.ko,
float(Data_holder::get()-
>parameter.ki)/Data_holder::get()->parameter.ko,
float(Data_holder::get()-
>parameter.kd)/Data_holder::get()->parameter.ko , MAX_PWM_VALUE);
#endif
#if MOTOR_COUNT>2
#if MOTOR_CONTROLLER == COMMON_CONTROLLER
static CommonMotorController motor3(MOTOR_3_PWM_PIN, MOTOR_3_DIR_A_PIN,
MOTOR_3_DIR_B_PIN, MAX_PWM_VALUE);
#elif MOTOR_CONTROLLER == AF_SHIELD_CONTROLLER
static AFSMotorController motor3(MOTOR_3_PORT_NUM, MAX_PWM_VALUE);
#endif
static EncoderImp encoder3(MOTOR_3_ENCODER_A_PIN, MOTOR_3_ENCODER_B_PIN);
static PID pid3(&input[2], &feedback[2], float(Data_holder::get()-
>parameter.kp)/Data_holder::get()->parameter.ko,
float(Data_holder::get()-
>parameter.ki)/Data_holder::get()->parameter.ko,
float(Data_holder::get()-
>parameter.kd)/Data_holder::get()->parameter.ko , MAX_PWM_VALUE);
#endif
#if MOTOR_COUNT>0
motor[0] = &motor1;//接口指向具体实现
encoder[0] = &encoder1;
pid[0] = &pid1;
#endif
#if MOTOR_COUNT>1
motor[1] = &motor2;
encoder[1] = &encoder2;
pid[1] = &pid2;
#endif
#if MOTOR_COUNT>2
motor[2] = &motor3;
encoder[2] = &encoder3;
pid[2] = &pid3;
#endif
#if ROBOT_MODEL == ROBOT_MODEL_DIFF//根据配置的机器人模型选择解算类的实现,这个
Differential与Omni3是运动解算接口Model的实现
static Differential diff(Data_holder::get()->parameter.wheel_diameter*0.0005,
Data_holder::get()->parameter.wheel_track*0.0005);
model = &diff;
5/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
#endif
#if ROBOT_MODEL == ROBOT_OMNI_3
static Omni3 omni3(Data_holder::get()->parameter.wheel_diameter*0.0005,
Data_holder::get()->parameter.wheel_track*0.0005);
model = &omni3;
#endif
//初始化电机驱动器
for (int i=0;i<MOTOR_COUNT;i++){
motor[i]->init();
}
do_kinmatics_flag = false;
memset(&odom, 0 , sizeof(odom));
memset(&input, 0 , sizeof(input));
memset(&feedback, 0 , sizeof(feedback));
last_velocity_command_time = 0;
}
通讯相关初始化
void Robot::init_trans(){
static USART_transport _trans(MASTER_USART, 115200);//使用串口作为通讯接口
static Simple_dataframe _frame(&_trans);//使用Simple_dataframe协议数据包实现
数据打包解包
trans = &_trans;
frame = &_frame;
trans->init();
frame->init();
//注册相关消息的通知, 收到该消息this->update回调会被调用
frame->register_notify(ID_SET_ROBOT_PARAMTER, this);
frame->register_notify(ID_CLEAR_ODOM, this);
frame->register_notify(ID_SET_VELOCITY, this);
}
上位命令处理
void Robot::check_command(){
unsigned char ch=0;
if (trans->read(ch)){//从通讯口中读取数据
//printf("%02x ", ch);
if (frame->data_recv(ch)){//使用数据包接收和解析数据
//printf("\r\n");
frame->data_parse();
6/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
}
}
}
运动处理
void Robot::do_kinmatics(){
if (!do_kinmatics_flag){//该标记收到上位的命令会置true 超时停止会置false
for(int i=0;i<MOTOR_COUNT;i++){
pid[i]->clear();//停止后清除pid变量值
encoder[i]->get_increment_count_for_dopid();//读取掉停止时编码器的变化
放置手动转动电机导致下次启动pid时的异常
}
return;
}
static unsigned long last_millis=0;
//根据配置PID间隔时间进行pid运算
if (Board::get()->get_tick_count()-last_millis>=Data_holder::get()-
>parameter.do_pid_interval){
last_millis = Board::get()->get_tick_count();
//得到PID间隔时间反馈编码器的值
for(int i=0;i<MOTOR_COUNT;i++){
feedback[i] = encoder[i]->get_increment_count_for_dopid();
}
#ifdef PID_DEBUG_OUTPUT
printf("input=%ld %ld %ld feedback=%ld %ld %ld\r\n", long(input[0]*1000),
long(input[1]*1000), long(input[2]*1000),
long(feedback[0]),
long(feedback[1]), long(feedback[2]));
#endif
//判断超时则无需继续PID运算
bool stoped=true;
for(int i=0;i<MOTOR_COUNT;i++){
if (input[i] != 0 || feedback[i] != 0){
stoped = false;
break;
}
}
short output[MOTOR_COUNT]={0};
if (stoped){
for(int i=0;i<MOTOR_COUNT;i++){
output[i] = 0;
}
do_kinmatics_flag = false;
}else{
//计算得到输出PWM input[i]在update回调通知中给定
for(int i=0;i<MOTOR_COUNT;i++){
output[i] = pid[i]->compute(Data_holder::get()-
7/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
>parameter.do_pid_interval*0.001);
}
}
#ifdef PID_DEBUG_OUTPUT
printf("output=%ld %ld %ld\r\n\r\n", output[0], output[1], output[2]);
#endif
//控制各个电机
for(int i=0;i<MOTOR_COUNT;i++){
motor[i]->control(output[i]);
}
//超时判断
if (Board::get()->get_tick_count()-
last_velocity_command_time>Data_holder::get()->parameter.cmd_last_time){
for(int i=0;i<MOTOR_COUNT;i++){
input[i] = 0;
}
}
}
}
计算里程位置姿态
void Robot::calc_odom(){
static unsigned long last_millis=0;
//每间CALC_ODOM_INTERVAL隔时间计算
if (Board::get()->get_tick_count()-last_millis>=CALC_ODOM_INTERVAL){
last_millis = Board::get()->get_tick_count();
#ifdef ODOM_DEBUG_OUTPUT
long total_count[MOTOR_COUNT]={0};
for(int i=0;i<MOTOR_COUNT;i++){
total_count[i] = encoder[i]->get_total_count();
}
printf("total_count=%ld %ld\r\n", total_count[0], total_count[1]);
#endif
float dis[MOTOR_COUNT] = {0};
//得到间隔时间内个轮子行径的距离(m)
for(int i=0;i<MOTOR_COUNT;i++){
dis[i] = encoder[i]-
>get_increment_count_for_odom()*__PI*Data_holder::get()-
>parameter.wheel_diameter*0.001/Data_holder::get()->parameter.encoder_resolution;
#ifdef ODOM_DEBUG_OUTPUT
printf(" %ld ", long(dis[i]*1000000));
#endif
}
//通过使用的模型接口到当前里程信息
model->get_odom(&odom, dis, CALC_ODOM_INTERVAL);
8/9
7. PIBOT的Firmware的代码分析(1).md 10/20/2018
#ifdef ODOM_DEBUG_OUTPUT
printf(" x=%ld y=%ld yaw=%ld", long(odom.x*1000), long(odom.y*1000),
long(odom.z*1000));
printf("\r\n");
#endif
//更新至数据存储中
Data_holder::get()->odom.v_liner_x = odom.vel_x*100;
Data_holder::get()->odom.v_liner_y = odom.vel_y*100;
Data_holder::get()->odom.v_angular_z = odom.vel_z*100;
Data_holder::get()->odom.x = odom.x*100;
Data_holder::get()->odom.y = odom.y*100;
Data_holder::get()->odom.yaw = odom.z*100;
}
}
9/9

View File

@ -0,0 +1,564 @@
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
1.PID
2.通讯部分
2.1Transport接口
2.2数据报接口
3.bsp接口及实现
4.通讯实现类
5.协议实现类
6.slave的实现
7.总结
1.PID
直接贴出代码,应该不难理解 pid.h
#ifndef PIBOT_PID_H_
#define PIBOT_PID_H_
class PID{
public:
PID(float* input, float* feedback, float kp, float ki, float kd, unsigned
short max_output);
short compute(float interval);
void clear();
private:
float kp;
float ki;
float kd;
float max_output;
float* input;
float* feedback;
float error;
float integra;
float derivative;
float previous_error;
};
#endif
pid.cpp
#include "pid.h"
#include "board.h"
#include <stdio.h>
1 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
PID::PID(float* _input, float* _feedback, float _kp, float _ki, float _kd,
unsigned short _max_output)
:input(_input), feedback(_feedback), kp(_kp), ki(_ki), kd(_kd),
max_output(_max_output*1.0){
clear();
printf("pid=%ld %ld %ld\r\n", long(kp*1000), long(ki*1000),
long(kd*1000));
}
void PID::clear(){
error = integra = derivative = previous_error =0;
}
short PID::compute(float interval){
error = *input - *feedback;
integra = integra + error*interval;
derivative = (error - previous_error) / interval;
previous_error = error;
if (ki != 0)
#ifdef PID_DEBUG_OUTPUT
printf("integra=%ld max_output=%ld %ld\r\n", long(integra*1000),
long(-(max_output/ki*1000)), long(max_output/ki*1000));
#endif
if (integra < -(max_output/ki))
{
printf("integra clear-\r\n");
integra = -(max_output/ki);
}
if (integra > max_output/ki)
{
printf("integra clear+\r\n");
integra = max_output/ki;
}
float val = error*kp + integra*ki + derivative*kd;
if (val < -max_output)
val = -max_output+1;
else if (val > max_output)
val = max_output-1;
#ifdef PID_DEBUG_OUTPUT
printf("error=%ld integra=%ld derivative=%ld val=%ld\r\n",
long(error*1000), long(integra*1000), long(derivative*1000), long(val*1000));
#endif
return val;
}
2 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
2.通讯部分
2.1Transport接口
#ifndef PIBOT_TRANSPORT_H_
#define PIBOT_TRANSPORT_H_
class Transport{
public:
virtual bool init()=0;
virtual bool read(unsigned char& ch)=0;
virtual void write(unsigned char* data, unsigned char len) = 0;
};
#endif
2.2数据报接口
#ifndef PIBOT_DATA_FRAME_H_
#define PIBOT_DATA_FRAME_H_
enum MESSAGE_ID{
ID_GET_VERSION = 0,
ID_SET_ROBOT_PARAMTER = 1,
ID_GET_ROBOT_PARAMTER = 2,
ID_CLEAR_ODOM = 3,
ID_SET_VELOCITY = 4,
ID_GET_ODOM = 5,
ID_GET_PID_DATA = 6,
ID_MESSGAE_MAX
};
//消息关注回调接口
class Notify{
public:
virtual void update(const MESSAGE_ID id, void* data) = 0;
};
class Dataframe{
public:
virtual bool init()=0;
virtual void register_notify(const MESSAGE_ID id, Notify* _nf)=0;//注册对
某个消息的关注
virtual bool data_recv(unsigned char c)=0;//处理接收数据返回true 表示接收
到完整的数据包
virtual bool data_parse()=0;//解析包
3 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
virtual bool interact(const MESSAGE_ID id)=0;//master调用用来发送数据、接收
响应
};
#endif
3.bsp接口及实现
基本根据接口名字定义就知道功能,不再赘述
#ifndef PIBOT_BOARD_H_
#define PIBOT_BOARD_H_
class Queue;
enum USART_NUMBER
{
USART_0 = 0,
USART_1,
USART_2,
USART_3,
USART_4,
USART_5,
USART_6,
};
#define PIN_MODE_INPUT 0x0
#define PIN_MODE_OUTPUT 0x1
#define PIN_MODE_INPUT_PULLUP 0x2
class Board{
public:
virtual void enable_irq()=0;
virtual void disable_irq()=0;
virtual void usart_debug_init()=0;
virtual void usart_init(unsigned char num, unsigned long buad)=0;
virtual Queue* usart_getDataQueue(unsigned char num)=0;
virtual void usart_write(unsigned char num, unsigned char ch)=0;
virtual void usart_write(unsigned char num, unsigned char* data, unsigned char
len)=0;
virtual void set_config(unsigned char* data, unsigned short len)=0;
virtual void get_config(unsigned char* data, unsigned short len)=0;
virtual void pin_init(unsigned char pin, unsigned char mode)=0;
virtual void pin_write(unsigned char pin, unsigned char level)=0;
virtual unsigned char pin_read(unsigned char pin)=0;
virtual void pwm_init(unsigned char khz)=0;
4 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
virtual void pwm_output(unsigned char pin, unsigned short pwm)=0;
virtual unsigned long get_tick_count()=0;
static Board* get();
};
#endif
附上Mega2560的实现board_mega2560
#include "board_mega2560.h"
#include "Arduino.h"
#include "EEPROM.h"
#include "TimerOne.h"
Board_Mega2560 Board_Mega2560::board;
#define CONFIG_EEPROM_BASE 0
int serial_puts(char c, struct __file*){
Serial3.write(c);
return c;
}
void printf_begin(void){
fdevopen(&serial_puts, 0);
}
Board* Board::get(){
return &Board_Mega2560::board;
}
Board_Mega2560::Board_Mega2560(){
}
Board_Mega2560::~Board_Mega2560(){
}
void Board_Mega2560::enable_irq(){
interrupts();
}
void Board_Mega2560::disable_irq(){
noInterrupts();
}
void Board_Mega2560::usart_debug_init()
{
5 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
usart_init(USART_3, 115200);
printf_begin();
}
void Board_Mega2560::usart_init(unsigned char num, unsigned long buad){
if (num == (unsigned char)USART_0){
printf("uart0 start\r\n");
Serial.begin(buad);
}else if (num == (unsigned char)USART_3){
Serial3.begin(buad);
}
}
Queue* Board_Mega2560::usart_getDataQueue(unsigned char num){
if (num == (unsigned char)USART_0){
return &usart1_queue;
}
return 0;
}
void Board_Mega2560::usart_write(unsigned char num, unsigned char ch){
if (num == (unsigned char)USART_0){
Serial.write(ch);
}
}
void Board_Mega2560::usart_write(unsigned char num, unsigned char* data, unsigned
char len){
if (num == (unsigned char)USART_0){
Serial.write((char*)data, len);
}else if (num == (unsigned char)USART_3){
Serial3.write((char*)data, len);
}
}
void serialEvent(){
if (Serial.available()){
if (!Board::get()->usart_getDataQueue(USART_0)->put(Serial.read())){
//err
}
}
}
void Board_Mega2560::set_config(unsigned char* data, unsigned short len){
for(unsigned short i=0; i<len;i++){
EEPROM.write(CONFIG_EEPROM_BASE+i, data[i]);
delayMicroseconds(2);
}
}
void Board_Mega2560::get_config(unsigned char* data, unsigned short len){
6 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
for(unsigned short i=0; i<len;i++){
data[i] = EEPROM.read(CONFIG_EEPROM_BASE+i);
delayMicroseconds(2);
}
}
void Board_Mega2560::pin_init(unsigned char pin, unsigned char mode){
pinMode(pin, mode);
}
void Board_Mega2560::pin_write(unsigned char pin, unsigned char level){
digitalWrite(pin, level);
}
unsigned char Board_Mega2560::pin_read(unsigned char pin){
return digitalRead(pin);
}
void Board_Mega2560::pwm_init(unsigned char khz){
Timer1.initialize(1000.0/khz);
}
void Board_Mega2560::pwm_output(unsigned char pin, unsigned short value){
if (value== 0)
Timer1.disablePwm(pin);
Timer1.pwm(pin, value);
}
unsigned long Board_Mega2560::get_tick_count(){
return millis();
}
4.通讯实现类
下面看下通讯类的具体实现usart_transport实现了init read write接口 usart_transport.h
#ifndef PIBOT_USART_TRANSPORT_H_
#define PIBOT_USART_TRANSPORT_H_
#include "transport.h"
class USART_transport:public Transport{
public:
USART_transport(unsigned char num, unsigned long buad);
bool init();
bool read(unsigned char& ch);
void write(unsigned char* data, unsigned char len);
private:
unsigned char usart_num;
unsigned long usart_buad;
7 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
};
#endif
usart_transport.cpp
#include "usart_transport.h"
#include "board.h"
#include <stdio.h>
#include "queue.h"
USART_transport::USART_transport(unsigned char num, unsigned long
buad):usart_num(num), usart_buad(buad){
}
bool USART_transport::init()
{
//printf("port=%d %ld\r\n", usart_num, usart_buad);
Board::get()->usart_init(usart_num, usart_buad);//这里根据bsp的库来实现串口读写
return true;
}
bool USART_transport::read(unsigned char& ch){
return Board::get()->usart_getDataQueue(usart_num)->get(ch);
}
void USART_transport::write(unsigned char* data, unsigned char len){
Board::get()->usart_write(usart_num, data, len);
}
5.协议实现类
继续看下dataframe的实现simple_dataframe simple_dataframe.h定义了simple_dataframe的共用的数据
结构
#ifndef PIBOT_SIMPLE_DATAFRAME_H_
#define PIBOT_SIMPLE_DATAFRAME_H_
#include "dataframe.h"
#include <string.h>
static const unsigned short MESSAGE_BUFFER_SIZE = 255;
#define FIX_HEAD 0x5A
struct Head{
8 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
unsigned char flag;// 头部标记,固定值:0X5A
unsigned char msg_id;// 消息ID,表示消息具体作用,决定消息体具体格式
unsigned char length;// 消息体长度
};
struct Message{
struct Head head;
unsigned char data[MESSAGE_BUFFER_SIZE];
unsigned char check;
unsigned char recv_count;//已经接收的字节数
Message(){}
Message(unsigned char msg_id, unsigned char* data=0,unsigned char len=0){
head.flag = FIX_HEAD;
head.msg_id = msg_id;
head.length = recv_count = len;
check = 0;
if (data != 0 && len !=0)
memcpy(this->data, data, len);
unsigned char* _send_buffer = (unsigned char*)this;
unsigned int i = 0;
for (i = 0; i < sizeof(head)+head.length; i++)
check += _send_buffer[i];
_send_buffer[sizeof(head)+head.length] = check;
}
};
enum RECEIVE_STATE{
STATE_RECV_FIX=0,
STATE_RECV_ID,
STATE_RECV_LEN,
STATE_RECV_DATA,
STATE_RECV_CHECK
};
#endif
6.slave的实现
#ifndef PIBOT_SIMPLE_DATAFRAME_SLAVE_H_
#define PIBOT_SIMPLE_DATAFRAME_SLAVE_H_
#include "simple_dataframe.h"
class Transport;
9 / 10
8. PIBOT的Firmware的代码分析(2).md 10/20/2018
class Simple_dataframe : public Dataframe{
public:
Simple_dataframe(Transport* trans=0);
void register_notify(const MESSAGE_ID id, Notify* _nf){
if (id >= ID_MESSGAE_MAX)
return;
nf[id] = _nf;
}
bool init();
bool data_recv(unsigned char c);
bool data_parse();
bool interact(const MESSAGE_ID id){return true;};
private:
bool send_message(const MESSAGE_ID id);
bool send_message(const MESSAGE_ID id, unsigned char* data, unsigned char
len);
bool send_message(Message* msg);
private:
Notify* nf[ID_MESSGAE_MAX];
Message active_rx_msg;
RECEIVE_STATE recv_state;
Transport* trans;
};
#endif
7.总结
这里使用的通讯接口及数据接口类使得更换通讯口或者通讯协议变得简单只按照接口实现transport和
dataframe即可替换目前使用的串口及目前简单的通讯协议。同时底层的接口也解耦更换STM32或者启动的
板子也变得极为容易
10 / 10

View File

@ -0,0 +1,59 @@
9. base_link、odom、map关系.md 10/20/2018
1.base_link和laser_link
2.odom
3.map
1.base_link和laser_link
前文三轮全向机器人底盘(4)-3D仿真模型一文中我们使用URDF创建了一个底盘模型,我们添加显示TF属性
后显示 TF 这里显示出URFD文件中定义好的2个Framebase_link和laser_link; 显示的红绿蓝分别代表
xyz轴。
tf view 启动新窗口输入rosrun tf tf_echo base_link laser_link tf view 可以看到打印出
laser_link到base_link的tf 这里Translation和Rotation即为位置与姿态的转换其中Rotation
以三种不同的形式展现出来而这些关系定义实在URDF文件中可以找到,具体在哪发布出来的,输入以下
命令
rosrun tf view_frames
evince frames.pdf
可以看到是robot_state_publisher发布base_link到laser_link的TF
2.odom
官方有个教程http://wiki.ros.org/navigation/Tutorials/RobotSetup/Odom, https://github.com/ros-
planning/navigation_tutorials 这个包这里可以看到
cd ~/pibot/ros_ws/src
git clone https://github.com/ros-planning/navigation_tutorials.git
cd ..
catkin_make
rosrun odometry_publisher_tutorial odometry_publisher
可以看到多出一个Frame并且在运动。切换Fixed Frame为Odom同时添加Odom topic 可以看到base_link
做圆周运动,线速度和角速度可以在代码中看到
double vx = 0.1;
double vy = -0.1;
double vth = 0.1;
这里速度并非下发给机器人速度而是机器人反馈的速度对于PIBOT解算都是在下位机完成
直接串口读取出x y th即可
1/2
9. base_link、odom、map关系.md 10/20/2018
Odom可以理解全局一张超级大地图机器人运动就发布base_link到Odom的tf,这样就好似机器人在这张大图
上运动了。
3.map
前面说到Odom是一张大的地图 那map就可以理解为这个大的地图中的一部分。导航启动机器人不是处于地图
中的正确位置时,我们会通过 设置机器人的位置姿态其实是根据机器人当前位置把map"贴到"odom这张
大图上,即修改odom与map的tf关系以达到设置机器人位置姿态 同样amcl也是如此事实上2D Pose Estimate
就是amcl包中订阅的topic的处理
2/2

View File

@ -0,0 +1,90 @@
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
1.Windows下安装Uubuntu虚拟机
1.1 Windows下安装Vmware
1.2 虚拟机安装Ubuntu
2. 安装ROS
3. 网络连接
4. windows安装Xmanager(xshell、xftp)
1.Windows下安装Uubuntu虚拟机
安装文件在网盘中 pibot\pibot 镜像在网盘或者官网下载 docs\software\Vmware Ubuntn
1.1 Windows下安装Vmware
基本下一步,不再赘述
1.2 虚拟机安装Ubuntu
a.打开虚拟机,选择创建新的虚拟机
1 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
b. 选择下载好的ubuntu镜像
c.填入信息继续
2 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
d.选择文件存放位置,继续选择最大磁盘大小,建议(30G)
3 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
e.点击完成继续,虚拟机会重启开始安装(我安装的是gnome桌面的ubuntu可能显示不一样)
f.安装完成
2. 安装ROS
使用PIBOT ROS安装脚本即可完成ROS的一键安装
4 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
cd ~/pibot_ros
./pibot_install_ros.sh
source ~/.bashrc
3. 网络连接
虚拟机选择桥接模式
a. 右击右下角图标
5 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
b.网络连接选择桥接模式
c.设置桥接网卡,点击菜单编辑选择虚拟网络编辑器
点击更改设置
6 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
择需要桥接的网卡,确定即可
7 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
4. windows安装Xmanager(xshell、xftp)
安装文件在网盘中 安装过程不再赘述 pibot\pibot docs\software\Xmanager-v5.0.0547.zip,
通过xshell连接至树莓派
8 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
9 / 10
Windows下安装Ubuntu虚拟机及ROS.md 9/21/2018
通过xftp传输文件
10 / 10

View File

@ -0,0 +1,232 @@
1. 概述
2. 软件框架
3. Ubuntu的刷入、ROS的安装
4. ssh远程连接
4.1 安装xshell
通过 Xshell 连接 树莓派/RK3288/RK3399
Windows 通过 xshell 传输文件
5. 用户主机
6. 多机通讯
7. ROS驱动开发
7.1 概述
7.2 驱动开发及PID参数动态调整
7.3 目录结构简介
7.4 编译与测试
配置
树莓派/RK3288/RK3399
控制PC
编译
测试
初始化配置
开始测试
8. 校准
无IMU校准
IMU校准
9. ROS建图与导航
9.1 概述
9.2 建图
两种建图方法
保存地图
10 导航
单点导航测试
多点导航
11. 模拟器
12. Android App
12.1相关功能
12.2 显示视频
13. IMU的相关包使用
1. 概述
采用 树莓派/RK3288/RK3399/X86工控机/TK1/TX1/TX2 作为上位 ROS 主控,基于 ROS 开发适配导航建图算法
2. 软件框架
3. Ubuntu的刷入、ROS的安装
具体请参考树莓派(raspberry pi 3b)安装ROS Kinetic Kame与Firefly RK3288/3399固件刷新与ROS安装
tf卡启动的 nanopi(RK3399) 则同 树莓派 一样操作
4. ssh远程连接
windows 中推荐安装 xshell 远程连接
树莓派/ nanopi rk3399 默认开启了热点 ssid 和 password 均为 pibot_ap ,可以通过连接该热点连接
树莓派/ nanopi rk3399 ,树莓派/ nanopi rk3399 的IP为 192.168.12.1
如需要关闭ap模式改为连接wifi只需要执行 rm ~/.pibot_ap 后重启
如需要打开ap模式, 开启则执行 touch ~/.pibot_ap 后重启
4.1 安装xshell
网盘中下载 xshell 并安装
通过 Xshell 连接 树莓派/RK3288/RK3399
输入 IP 和用户名密码
树莓派3B/3B+ 为 pibot
firefly RK3288、RK3399 为 firefly
nanopi 3399 为 pi
Windows 通过 xshell 传输文件
5. 用户主机
需要一个 PC 安装 ROS 环境,用来显示查看地图或者玩转模拟器等, PIBOT 提供了一个一键安装 ROS 的
脚本 pibot_install_ros ,可以直接在 Ubuntu 下安装 ROS
用户主机 环境 Ubuntu 16.04 或者 Windows7/10+Vmvare+Ubuntu16.04 虚拟机 Ros kinetic 环境, 安装 ROS 参
见Windows下安装Ubuntu虚拟机及ROS相关章节
6. 多机通讯
用户主机与 树莓派/RK3288/RK3399/TK1/TX1/TX2/X86主机 怎么建立 ROS 通讯的, PIBOT 提供了一键配置脚
本 pibot_init_env
如需了解细节
请参考ROS多机的通讯配置pibot_init_env介绍即可
7. ROS驱动开发
7.1 概述
下位机及通过串口与 树莓派/RK3288/RK3399/TK1/TX1/TX2/X86主机 通讯, PIBOT 提供了一个简单的协议,
通讯协议具体请参见ROS机器人底盘(3)-通讯协议,同时 PIBOT 提供两个一个 ROS 无关的串口控制接口
具体可以参见PIBOT通讯协议的python解析实现
7.2 驱动开发及PID参数动态调整
PID 参数已在出厂时候配置如需了解细节请参考ROS机器人底盘(10)-PIBOT的driver的实现及动态
PID调节
7.3 目录结构简介
建议拷贝提供的压缩文件至目标设备( 树莓派/RK3288/RK3399/X86工控机/TK1/TX1/TX2 )上解压或者直
接 git clone 不然会遇到一些问题具体问题见Q&A
PIBOT 的 ROS workspace 目录如下图
arbotix_ros 模拟器
pibot 工具集
pibot_bringup pibot驱动包
pibot_description pibot urdf文件
pibot_navigation 建图导航相关配置项
pibot_simulator pibot导航模拟器
rplidar_ros rplidar激光雷达驱动包
ydlidar-1.2.1 eai激光雷达驱动包
7.4 编译与测试
配置
以 apollo 车型安装 rplidar 为例
树莓派/RK3288/RK3399
控制PC
编译
cd ~/pibot_ros/ros_ws
catkin_make
测试
初始化配置
重新拔插USB口或者重启 树莓派/RK3288/RK3399
ls /dev/pibot -l
开始测试
在 树莓派/RK3288/RK3399 运行 pibot_bringup 或 roslaunch pibot_bringup bringup.launch
在用户主机运行 pibot_configure 或 rosrun rqt_reconfigure rqt_reconfigure 可以查看和修改内置
的配置信息,运行 pibot_control 或 roslaunch pibot keyboard_teleop.launch 即可通过键盘控制小
车运动
同时支持小米等手柄的接入,运行 roslaunch pibot joystick.launch 即可
8. 校准
无IMU校准
参见ROS机器人底盘(11)-PIBOT的控制及校准
IMU校准
参见ROS机器人底盘(25)-PIBOT的IMU校准
9. ROS建图与导航
9.1 概述
ROS 驱动中提供了 cmd_vel 的订阅及 odom 的发布,至此再需要一个激光雷达就可以完成建图了
9.2 建图
在 树莓派/RK3288/RK3399 运行 pibot_gmapping 或 roslaunch pibot_navigation gmapping.launch
在用户主机运行 roslaunch pibot_navigation view_nav.launch 或者 pibot_view
两种建图方法
运行 roslaunch pibot keyboard_teleop.launch 或者 roslaunch pibot joystick.launch 即可通过键
盘或者遥控手柄开始建图
直接选择导航的点( 2D Nav Goal )开始建图
保存地图
运行下列命令即可(xxx为自定义名称
roslaunch pibot_navigation save_map.launch map_name:=xxx
或者
roscd pibot_navigation/maps/
rosrun map_server map_saver -f xxx`
可以看到生成2个文件
10 导航
单点导航测试
在 树莓派/RK3288/RK3399 运行 roslaunch pibot_navigation nav.launch map_name:=xxx.yaml
在用户主机运行 pibot_view 或 roslaunch pibot_navigation view_nav.launch ,通过 RViz 提供的功能既
可以完成导航测试(这里需要先指定初始位置)
如果直接运行 roslaunch pibot_navigation nav.launch 而不指定 map_name 参数则使用默认参
数, nav.launch 文件中可以设置默认使用的地图文件
多点导航
可以通过修改 pibot/scripts 中的 navigation_demo.py 的 python 脚本完成单点和多点的导航
具体可以参考ROS机器人底盘(18)-如何在实际项目中应用ROS导航相关1
11. 模拟器
PIBOT包内置了模拟器可以直接运行模拟导航
运行 roslaunch pibot_simulator nav.launch 或者 pibot_simulator
运行 roslaunch pibot_navigation view_nav.launch
这样无需小车也可以模拟导航了
12. Android App
12.1相关功能
保证手机跟跟 PC 或者 树莓派/RK3288/RK3399 连接同一个网络,保证手机能够访问
到 roscore ( export ROS_IP=XXX.XXX.XXX.XXX )具体参见ROS多机的通讯配置
修改 Master URI 选择 roscore 的 URI 点击 CONNECT
切换 Camera View 与 Map View
Set Pose 在地图长按 相当于 Rviz 中的 2D Pose Estimate
Set Goal 在地图长按 相当于 Rviz 中的 2D Nav Goal
左下角 Joystick 可以发出 cmd_vel topic 控制小车移动
12.2 显示视频
显示视频需要硬件摄像头支持同时在 PC 或者 树莓派/RK3288/RK3399 启
动 roslaunch pibot usb_camera.launch
13. IMU的相关包使用
装有 IMU 的 PIBOT 系列小车,底层提供 IMU 的数据采集,上层提供了 IMU 的互补滤波以及融合里程计
和 IMU 的扩展的卡尔曼滤波包robot_pose_ekf
启动时只需相应的 with_imu 的 launch 文件,例如
roslaunch pibot_bringup bringup_with_imu.launch 或者 pibot_bringup_with_imu
roslaunch pibot_navigation gammping_with_imu.launch 或者 pibot_gmapping_with_imu
roslaunch pibot_navigation nav_with_imu.launch 或者 pibot_naviagtion_with_imu
具体可以参考ROS机器人底盘(22)-IMU和里程计融合
ROS机器人底盘(23)-IMU和里程计融合与单独编码器里程计的对比测试

View File

@ -0,0 +1,192 @@
PIBOT下位机开发.md 6/30/2019
1. 概述
2. 软件框架
3. 下位机开发环境
3.1 环境搭建
Arduino
STM32F1
STM32F4
3.2 环境配置
Arduino
STM32F1
STM32F4
4. 代码分析
5. 参数配置
5.1 默认参数
apollo
zeus
apolloX
hades
hera
hadesX
5.2 配置参数
6. 电机方向和编码器方向软件调整
1. 概述
PIBOT下位机支持多种主板Arduino Mega2560、STM32F1及STM32F4等
PIBOT下位机支持多种运动模型(差分、全向、麦克纳姆轮),只需要调整编译参数即可
PIBOT下位机支持不同的参数的机器人执行设置相关参数即可
2. 软件框架
1/9
PIBOT下位机开发.md 6/30/2019
橙色部分为下位机的功能模块
3. 下位机开发环境
Arduino Mega 2560为主控单元使用Visual studio code+Platform IO进行开发支持Windows
和ubuntu环境
STM32F1为主控单元使用Keil进行开发
STM32F4为主控单元Ubuntu下使用Visual studio code进行开发
3.1 环境搭建
Arduino
具体请参考Visual Studio Code插件PlatformIO IDE开发Arduino
2/9
PIBOT下位机开发.md 6/30/2019
STM32F1
具体请参考PIBOT的STM32F1的环境配置与编译
STM32F4
具体请参考PIBOT的ubuntu下stm32 C/C++模版及配置编译
3.2 环境配置
Arduino
使用Visual studio code打开附带下位代码文件夹在platformio.ini修改相应模型以及使用的电机控制
apollo
models =
-D ROBOT_MODEL=ROBOT_MODEL_DIFF
-D MOTOR_CONTROLLER=COMMON_CONTROLLER
zeus
models =
-D ROBOT_MODEL=ROBOT_OMNI_3
-D MOTOR_CONTROLLER=COMMON_CONTROLLER
STM32F1
3/9
PIBOT下位机开发.md 6/30/2019
同Arduino
STM32F4
在param.mk的修改相关配置下图分别代表四种车型选择一种即可
4. 代码分析
以Arduino为例的代码分析具体请参考ROS机器人底盘(7)-Firmware的代码分析(1) ROS机器人底盘(8)-Firmware
的代码分析(2) ROS机器人底盘(24)-嵌入式部分框架设计与实现
5. 参数配置
运动参数出厂时都内置在板子的EEPROM/FLASH中
5.1 默认参数
默认参数出厂会固化到板子,用户可以根据主机环境校准微调,可以参考PIBOT的控制及校准
apollo
4/9
PIBOT下位机开发.md 6/30/2019
Arduino
STM32F1/STM32F4
zeus
5/9
PIBOT下位机开发.md 6/30/2019
STM32F4
apolloX
STM32F4
hades
6/9
PIBOT下位机开发.md 6/30/2019
STM32F4(轮子直径58MM)
STM32F4(轮子直径76MM)
hera
7/9
PIBOT下位机开发.md 6/30/2019
STM32F4
hadesX
STM32F4
wheel_disameter 轮子直径
8/9
PIBOT下位机开发.md 6/30/2019
wheel_track apollo:轮距 zeus:轮子所在圆直径 hades:轮子矩形长宽之和 hera 左右轮距*系数
减速比 encoder_resolution 轮子旋转一周编码器变化值的绝对值(一般为4编码器分辨率如4 * 11 *90
固件程序做了4倍频)
do_pi_interval 计算pid的间隔时间固定值10
kp ki kd
ko 为一个系数实际P I D参数为kp/ko ki/ko kd/ko
cmd_last_time 命令激励的超时时间,即超过该时间没有新的命令会机器人会停止
max_v_liner_x max_v_liner_y max_angular_z 底层速度限制,遥控器键盘或者导航层下发的速度
会被该值限制
imu_type 固定值69
5.2 配置参数
配置参数需要通过ROS上位机的界面配置
pibot_bringup
pibot_configure
或者
roslaunch pibot_bringup bringup.launch
rosrun rqt_reconfigure rqt_reconfigure
6. 电机方向和编码器方向软件调整
移植PIBOT下位机或者由于电机或编码器接线问题导致电机控制时一直转。原因是给定方向的PWM值导致编
码器一直反向变化,所以需要调整电机线或者编码器接线,通过设置软件编译参数也可以达到同样效果具体可
以参见关于运动控制方向的补充
9/9

View File

@ -0,0 +1,239 @@
PIBOT使用手册.md 7/20/2019
1. 硬件连接与安装
1.1 硬件连接示意图
1.2 安装
1.3 接线
2. 环境搭建与配置
2.1 配置
2.2 联网配置
2.3 PIBOT代码包编译与配置
3. 建图与导航测试
3.1 测试硬件连接
3.2 建图
3.3 保存地图
3.4 导航
3.5 Android手机APP
3.6 模拟器
1. 硬件连接与安装
1.1 硬件连接示意图
1 / 16
PIBOT使用手册.md 7/20/2019
1.2 安装
PIBOT调试完成后整体交付只需要安装雷达或者摄像头支架至顶层板即可
雷达安装
思岚A1 使用M2.5螺丝拧好至固定孔位即可
2 / 16
PIBOT使用手册.md 7/20/2019
思岚A2/A3 使用M3螺丝拧好至固定孔位即可不同于A1A2/A3的固定孔位使得雷达前后调转照
样可以按照遵循ROS的坐标系规定A2/A3线头执行前方
1.3 接线
电池分别接到电源板和主板给其供电
上位机ROS主板/主机供电主板分2种一种为5V供电另一种为12V的
5V供电通过底板的USB母座提供输出包括树莓派3b/3b+, nanopi(RK3399),可参考下面图
hades 树莓派 rplidar-A2
12V供电通过底板的USB母座提供输出包括Firefly(RK3399),X86工控机可参考下面图
apollo rk3399 rplidar-A1
通讯端口
下位机的通讯口主板通过usb micro口连接至主机(树莓派/RK3288/RK3399/TK1/TX1/TX2/X86
主机)(下图中黄色USB线)
雷达通讯口A1需要连接好串口板后再通过usb micro口连接至主机(树莓
派/RK3288/RK3399/TK1/TX1/TX2/X86主机)(下图中黄色USB线)
核心板上也有 STM32F1/F4 micro usb口该口作为核心板供电用不是通讯端口无需接线
3 / 16
PIBOT使用手册.md 7/20/2019
4 / 16
PIBOT使用手册.md 7/20/2019
2. 环境搭建与配置
下位机 PIBOT Arduino/STM32F1/F4主板
上位机 PIBOT 树莓派/RK3399/X86工控机/TK1/TX1/TX2等车上的安装ROS的主机
用户主机 用户使用的Windows或者Ubuntu的PC
2.1 配置
用户主机 这里 以Windows主机为例
1. 安装XShell工具用于远程登入ROS上位机请参考XShell怎么登陆linux
树莓派3B/3B+用户名密码均为pibot
firefly RK3288、RK3399用户名密码均为firefly
nanopi RK3399用户名密码均为pi**
2. 使用Vmware安装Ubuntu虚拟机以及,请参考Windows下安装Ubuntu虚拟机及ROS
3. 安装Androd App至手机
2.2 联网配置
上位机 PIBOT 联网有两类方式,一类是释放释放无线网的,另外一种是需要连接至路由器的
释放无线网,(树莓派3b/3b+nanopi(RK3399)上电后会释放出无线网络名称和密码为pibot_ap,这
用户主机通过无线连接该网络 种情况PIBOT上位机固定IP为192.168.12.1)
5 / 16
PIBOT使用手册.md 7/20/2019
不释放无线网,(firefly(RK3399),X86工控机,TK1,TX1,TX2等需要通过HDMI接口连接显示器在通过鼠
上位机 标键盘操作连接至可用的路由器同时记下PIBOT 的IP地址假定这里获取的为192.168.2.35)
用户主机通过无线网/有线网连接到该路由器
对于用户主机Windows需要配置虚拟机网卡连接为桥接模式保障ubuntu中IP跟主机IP一个网段
能互相 通 请参考 ping , Windows下安装Ubuntu虚拟机及ROS,桥接网络的配置
2.3 PIBOT代码包编译与配置
上位机 用户主机 拷贝升级或者从git仓库clone代码到主目录这里PIBOT和 都需要配置和编译
复制pibot_ros.tar.bz2至主目录,打开终端输入
tar jxvf pibot_ros.tar.bz2
cd ~/pibot_ros/
git pull # 拉取最新的代码 需连接外网
./pibot_init_env.sh #这里根据提示输入小车类型控制板类型雷达类型Machine类型
source ~/.bashrc
端(树莓派/RK3399等
上位机 用户主机 如果是车型为hades 雷达类型rplidar, PIBOT (虚拟机)配置
上位机 分别如下 PIBOT
用户主机(虚拟机)
(树莓派/RK3399等
上位机 用户主机 编译 分别在 PIBOT (虚拟机)编译
cd ~/pibot_ros/ros_ws/
catkin_make
source ~/.bashrc
6 / 16
PIBOT使用手册.md 7/20/2019
3. 建图与导航测试
3.1 测试硬件连接
通过ssh连接PIBOT
用户主机 上位机 a.在 输入命令ls /dev/pibot -l检查主板是否连接
7 / 16
PIBOT使用手册.md 7/20/2019
正常连接输出如下图
b.继续输入ls /dev/ydlidar -l或者ls /dev/rplidar -l(eai输入前者思岚A1/A2/A3输入后者)
检查激光雷达是否连接
3.2 建图
通过ssh连接PIBOT , 输入运行pibot_gmapping或者roslaunch
用户主机 上位机 a.在
pibot_navigation gmapping.launch启动建图节点收到最后输出odom receiced表示正常
8 / 16
PIBOT使用手册.md 7/20/2019
用户主机 b.在 的UBUNTU虚拟机终端输入pibtt_view或者roslaunch pibot_navigation
view_nav.launch启动RViz节点,查看地图
9 / 16
PIBOT使用手册.md 7/20/2019
通过ssh连接PIBOT
用户主机 上位机 c. 在 输入pibot_control或者roslaunch pibot
keyboard_teleop.launch启动控制节点根据提示输入q/z增减速度输入i/,控制前进后退,输入
j/l控制左转右转。控制小车在房间移动同时观察虚拟机中地图构建情况
10 / 16
PIBOT使用手册.md 7/20/2019
3.3 保存地图
通过ssh连接PIBOT ,输入
用户主机 上位机 在
roslaunch pibot_navigation save_map.launch map_name:=xxx
11 / 16
PIBOT使用手册.md 7/20/2019
或者
roscd pibot_navigation/maps #(xxx)为设置新建好的地图名称
rosrun map_server map_saver -f xxx
3.4 导航
a.接上面继续输入运行pibot_navigation或者roslaunch pibot_navigation nav.launch
map_name:=xxx.yaml启动导航节点收到最后输出odom receiced表示正常(xxx为之前新建好的地图
名称)
这里需要先退出gmapping建图
b.在控制PC中输入pibot_view或者roslaunch pibot_navigation view_nav.launch启动RViz节
点,查看地图
12 / 16
PIBOT使用手册.md 7/20/2019
位置和方向 c.在RViz中设置小车当前
13 / 16
PIBOT使用手册.md 7/20/2019
位置和方向 d.在RViz中设置小车目标 ,即可开始导航
3.5 Android手机APP
上位机 连接与PIBOT 同一路由器(树莓派/nanopi rk3399则为其释放热点pibot_ap),开启建图或者导航
程序
上位机 安装好apk(网盘/源码/Android App目录)至手机打开程序修改为PIBOT 的IP点击CONNECT
14 / 16
PIBOT使用手册.md 7/20/2019
15 / 16
PIBOT使用手册.md 7/20/2019
可以显示地图、设置位置和设置目标点、显示视频和控制行走
上位机 *显示视频需要在PIBOT 连接摄像头以及开启相关程序
usb 摄像头 roslaunch pibot usb_camera.launch
3.6 模拟器
PIBOT包内置了模拟器可以直接运行模拟导航 模拟器需要配置为车载主机模式
运行pibot_simulator或者roslaunch pibot_simulator nav.launch
运行pibot_simulator或者roslaunch pibot_navigation view_nav.launch 这样无需小车也可以模
拟导航了
16 / 16

View File

@ -0,0 +1,41 @@
PIBOT的STM32F1的环境配置与编译.md 2/24/2019
安装Keil开发环境
pibot docs/software/MDK520目录提供了Keil5的安装包MDK520.EXE安装过程不再赘述
完成安装后需要继续安装Keil.STM32F1xx_DFP.2.2.0.pack
编译与烧写
编译
到PIBOT源码包中双击打开pibot.uvprojx工程,编译
烧写程序
这里我们使用JLink烧写程序连接JLink至开发板开发板上电这里的JLink只需要连接GND SWDIO
三根线 SWCLK 打开工程选项切换至Debug标签选择JLink
1/5
PIBOT的STM32F1的环境配置与编译.md 2/24/2019
2/5
PIBOT的STM32F1的环境配置与编译.md 2/24/2019
点击Settings按钮Port选择SW
3/5
PIBOT的STM32F1的环境配置与编译.md 2/24/2019
切换至Flash Download标签,如下图设置
4/5
PIBOT的STM32F1的环境配置与编译.md 2/24/2019
保存后即可烧写程序
同时可以看到程序运行指示灯在闪烁表示程序在正常运行了
5/5

View File

@ -0,0 +1,22 @@
PIBOT的ubuntu下stm32F4 C&C++模版及配置编译.md 2/24/2019
模版
编译
1. 安装交叉编译器 sudo apt-get install gcc-arm-none-eabi
2. 编译 进入工程目录
make
即可完成编译
烧写程序
配置openocd使用jlink烧写程序 1.安装openocd sudo apt-get install openocd 2.烧写 连接好jlink make
burn即可完成程序烧写
1/1

View File

@ -0,0 +1,142 @@
Visual Studio Code插件PlatformIO IDE开发Arduino.md 2/24/2019
概述
本文介绍如何使用VScode 直接开发Arduino 程序避免使用Arduino IDE时的没有代码提示功能文件关
系不清晰、头文件打开不方便等问题及使用Visual Stdio集成插件的庞大安装工程同时Visual Studio
Code插件PlatformIO IDE开发Arduino 跨平台无论你是用的windowsubuntu或者mac都可以玩转。
安装Visual Studio Code PlatformIO
https://code.visualstudio.com/页面下载安装vscode
安装完成vscode启动扩展页面下搜索platformio即可找到,选择第一个Platformio IDE安装即可这里
需要耐心等待一会)
安装完成重新加载后左下角会多一个小房子图标点击后即可显示Platformio IDE主页
测试
选择New Project创建工程选择相应的Board我这里使用Mega2560输入2560找到对应的Board
修改main.cpp
#include <Arduino.h>
1/5
Visual Studio Code插件PlatformIO IDE开发Arduino.md 2/24/2019
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delayMicroseconds(1000);
digitalWrite(13, LOW);
delay(1000);
}
编译与下载
同样左下角有一堆按钮 选择upload即可完成下载
指示灯也可以正常闪烁,可以看到我们连端口都没有选择就完成了下载的工作,非常方便。
问题与高级功能
arduino IDE有库管理功能可以下载到需要的库。这里还要方便例如我们想使用TimerOne输出PWM,
#include <Arduino.h>
#include <TimerOne.h>
void setup() {
Timer1.initialize(40);
}
void loop() {
Timer1.pwm(11, 512);
}
Arduino IDE我们会这样写然库管理搜索下载TimerOne库在这里我们只需要在配置文件platformio.ini加上下
面一句即可
lib_deps = TimerOne
2/5
Visual Studio Code插件PlatformIO IDE开发Arduino.md 2/24/2019
选择编译按钮编译,我们可以看到输出信息
找到了TimerOne库并下载至.piolibdeps文件夹下
注意点
接上面我们也可以把下载好的TimerOne库直接放置在lib目录下也就无需添加lib_deps。
我们不想在main里面直接使用TimerOne的pwm我们想自己写一个motor库motor库会使用到
TimerOne
motor.h
#ifndef TEST_MOTOR_H_
#define TEST_MOTOR_H_
class Motor{
public:
void init(unsigned char fre);
void pwm(unsigned short);
};
#endif
motor.cpp
3/5
Visual Studio Code插件PlatformIO IDE开发Arduino.md 2/24/2019
#include "motor.h"
#include "TimerOne.h"
void Motor::init(unsigned char fre) {
Timer1.initialize(1000.0/fre);
}
void Motor::pwm(unsigned short val) {
Timer1.pwm(11,val);
}
main.cpp
#include <Arduino.h>
#include <motor.h>
Motor motor1;
void setup() {
motor1.init(15);
}
void loop() {
motor1.pwm(512);
}
4/5
Visual Studio Code插件PlatformIO IDE开发Arduino.md 2/24/2019
编译完成提示找不到TimerOne.h头文件
可以看到Library Dependency Graph没有TimerOne
两种解决方法
1. main.cpp include头文件TimerOne.h这个比较low英文main中根本就没有使用到TimerOne
2. 之前的办法添加lib_deps = TimerOne
总结
至此可以看到使用VSCode集成的PlatformIO IDE插件开发与查看arduino的代码都非常方便
5/5