更新PIBot用户手册文档
parent
cc7f3b20b4
commit
4ae4558216
|
@ -0,0 +1,5 @@
|
|||
## source ~/.bashrc的作用
|
||||
|
||||
`source ~/.bashrc` 命令的作用是重新加载当前用户的 Bash 配置文件(通常是 `~/.bashrc` 文件),以应用对该文件的任何修改。Bash 配置文件包含了在每次启动新的终端会话时执行的命令,用于自定义环境变量、别名、函数和其他终端行为。
|
||||
|
||||
具体来说,`~/.bashrc` 是 Bash shell 在交互模式下启动时会读取的一个配置文件。当您对 `~/.bashrc` 文件进行了修改后,为了使更改生效,您可以重新加载配置文件,而这就是使用 `source` 命令的目的。
|
|
@ -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
|
||||
|
|
@ -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_ 命令
|
||||
|
|
@ -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图00,0观#察命是令否输是完电,机电2转机动2会转动
|
||||
#p#y测t根h试据on电配t机置e3的s(t⻋_如m上o果t对o有照r)s电.p机y顺0序0图1,0观00察#是否命是令电输机完3,转电动机3会转动
|
||||
##py测根th试据on电配t机置e的4s(t⻋_如m上o果t对o有照r)s电.p机y顺0序0图1,0观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/t,poyr第psi.⼀bpoy列t的1/0t值0r0a是n0s否p在#or变命t ⼤令输完,电机1会转动
|
||||
p#yt观h察on输t出e的st⽇_m志ot,or第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
|
||||
调整完成主板需要重新上电⽣效
|
||||
|
|
@ -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
|
||||
|
|
@ -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螺丝拧好至固定孔位即可,不同于A1,A2/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
|
||||
|
|
@ -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螺丝拧好至固定孔位即可,不同于A1,A2/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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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,计算出来的为1m,odom->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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 使用Dijkstra’s算法代价最小的规划
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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得到的数据为一个相对角度(主要使用yaw,roll和pitch 后面不会使用到),使用该角度来替代由编码器
|
||||
计算得到的角度。 这个方法较为简单,出现打滑时候因yaw不会受到影响,即使你抬起机器人转动一定的角
|
||||
度,得到的里程也能正确反映出来
|
||||
|
||||
3.2 扩展的卡尔曼滤波
|
||||
|
||||
3/7
|
||||
22.IMU和里程计融合.md 10/21/2018
|
||||
|
||||
官方提供了个扩展的卡尔曼滤波的包robot_pose_ekf,robot_pose_ekf开启扩展卡尔曼滤波器生成机器人姿态,
|
||||
支持
|
||||
|
||||
odom(编码器)
|
||||
imu_data(IMU)
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 跨平台无论你是用的windows,ubuntu或者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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.15,y方向设置-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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
4. 如何使用SolidWorks软件导出URDF机器人模型文件.md 10/20/2018
|
||||
|
||||
1.简述
|
||||
2.安装sw_urdf_exporter插件
|
||||
3.Rviz展示机器人模型
|
||||
4.备注
|
||||
|
||||
1.简述
|
||||
|
||||
ROS中通过RVIZ可视化机器人导航情况,其中,URDF(Unified 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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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个Frame,base_link和laser_link; 显示的红绿蓝分别代表
|
||||
x,y,z轴。
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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和里程计融合与单独编码器里程计的对比测试
|
||||
|
|
@ -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
|
||||
|
|
@ -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螺丝拧好至固定孔位即可,不同于A1,A2/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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 跨平台无论你是用的windows,ubuntu或者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
|
||||
|
Loading…
Reference in New Issue