基于ROS系统的移动小车
期末项目内容:移动小车操作入门,老师提供了三个选题:
1)控制小车寻线,并在三条黑线的交界处停止
2)控制小车寻找aruco码,先到达aruco码处,按照固定距离到达目标点
3)控制小车寻找aruco码,通过视觉图像得到三维信息,根据目标点和aruco码的相对距离,直接到达目标点。
https://www.bilibili.com/video/BV1r24y137xX/
我尝试了1、2 两种任务,任务1失败,任务2不完全成功。
(一)小车控制介绍:
小车控制时需要打开底盘并进行底盘连接:
roslaunch jubot_driver jubot_driver.launch
这个命令会创建一个节点: /jubot_driver ,这个节点会持续接收 名为/cmd_vel 的话题的信息。(图中最后一个圈)

那么我们也可以通过 /cmd_vel 话题能够实现小车的控制。采用订阅与发布的方式,创建一个话题发布者,在 /cmd_vel 上发布对应数据类型,并打开底盘控制命令(接受信息),就能实现小车移动的控制。查询话题格式,发现/cmd_vel 的数据类型为:geometry_msgs::Twist twist; Twist的信息则在此查询:geometry_msgs/Twist Documentation (ros.org):

Vector3的的内容则是:

我们就能够采用这样的方式发布信息,并实现控制小车运动了。
linear 为线速度;angular为角度度(.z 表示绕z轴转动)
#include <geometry_msgs/Twist.h>
geometry_msgs::Twist twist;
geometry_msgs::Vector3 linear;
geometry_msgs::Vector3 angular;
linear.x=0;linear.y=0; linear.z=0;angular.x=0; angular.y=0;angular.z=0.1;
twist.linear=linear;twist.angular=angular;
ros::Subscriber arSub = node_handle.subscribe("/ar_pose_marker",1, doMsg);
cmdpub.publish(twist);
(二)任务二、三过程:
任务二的重点是通过aruco码来获取小车的位姿信息,我们可以通过
roslaunch jubot_ar_track jubot_ar_track.launch 调起aruco识别程序,通过查看话题和读取话题信息,我发现 这个程序通过 /ar_pose_marker 节点发布aruco码的位姿信息,但是发布的信息读取遇到了困难,时常报错:pose不存在;[ ] 操作符不存了。。。
花了很长的时间,终于搞清楚了如何读取:
ros::NodeHandle node_handle;
ros::Subscriber arSub = node_handle.subscribe("/ar_pose_marker",1, doMsg);
void doMsg(const ar_track_alvar_msgs::AlvarMarkers::ConstPtr& msg){
//ROS_INFO("get:%f", msg->markers[].pose.pose.position.x);
if(msg->markers.size()!=0){
int aruco_id = msg->markers[0].id;
geometry_msgs::Pose aruco_pose = msg->markers[0].pose.pose;
//可以通过aruco.pose.x /aruco.position.x 得到位置信息和四元素
}}
从/ar_pose_marker得到的数据包括: position 和 orientation ;position为以aruco码为坐标系,小车在该坐标系的坐标;orientation是四元素,使用方法如下:

通过四元素转换得到aruco码相对小车坐标,就可以控制小车移动到目标点了。
(三)任务一过程:
任务一主要是寻线和终止条件的判断,通过调用opencv 的库,在自己电脑上进行测试时,巡线部分能够较简单实现,但是上小车测试,地面的反光和分辨率却对识别带来很大的影响:
上图是小车测试结果;下图为手机拍摄图测试效果。我尝试过hsv数值修改;数值大了会导致出现很多无关的点被选择;而数值小了之后识别的路径就会被截断。


我对图像的处理操作如下:
x_size = 800
y_size = 600
frame = CV2.imread("E:/1.jpg")
compress = CV2.resize(frame, (x_size, y_size), interpolation=CV2.INTER_CUBIC)#标准模板
gs_frame = CV2.GaussianBlur(compress, (5, 5), 0)# 高斯模糊
sv_image = CV2.cvtColor(gs_frame, CV2.COLOR_BGR2HSV) #转换为hsv图像
# hsv 筛选颜色
black_lower = np.array([0, 0, 0])
black_high = np.array([180, 255, 145])
mask = CV2.inRange(hsv_image, black_lower, black_high)
#腐蚀和膨胀的核心
kernel_dilate = np.ones((6,6),dtype =np.uint8)
kernel_erosion = np.ones((3, 3), dtype=np.uint8)
#腐蚀操作,去除毛刺
n = 0
while (n < 3):
# 膨胀
dilate = CV2.dilate(mask, kernel_dilate ,4 )
# 腐蚀操作
erosion = CV2.erode(dilate, kernel_erosion, iterations=2)
mask = erosion
n = n + 1
contours, hierarchy = CV2.findContours(mask, CV2.RETR_TREE, CV2.CHAIN_APPROX_NONE) #轮廓提取
indx = ChoiceTheLength(contours)#选取出最长的轮廓
drawMyContours("find contours", mask, contours[indx], True)#并展示
如果要用于巡线,思路是选取屏幕下方符合条件的点并框选,如果选框中心靠左的位置,则左偏移(linear.z= 0.1);中间则直行,直到中心点逐渐偏左或偏右。
如果要用于判断终点;则是检测到三线的交点(Y字形),则认为即将到达目标点,可以开个计时器,时间到了就停止运动;或者移动固定的距离。
但是视觉上小车测试的效果太差了,读取的hsv图根本没法用来识别,所以放弃了这个操作。
(四)困难与总结:
1. CMakeLists.txt 在ros中是真的重要,因为在文件中错误的链接内容,还有没有链接足够的库,一直导致编译错误还发现不了。一开始在检查的时候也没有看cmakelist.txt 的东西,被这个编译卡了很久。
2. catkin_make 有时候没有报错,只是警告,但是也会导致编译失败。我意识到这一点时已经过了一天。
3.这此项目让我们了解了ROS系统相关知识,学会了控制小车移动,初步认识了坐标系转换等知识

