|
【R333】桌面级全向底盘
本体说明 驱动及控制 传感器使用 机器视觉 slam导航 |
底盘本体为一个采用半独立刚性悬挂的四驱全向底盘。
1. 底盘概述
该底盘是一款模块化的桌面级应用型底盘,基于应用级软件架构设计、应用级硬件系统设计、典型应用型底盘机械系统设计。
本体说明
硬件配置及主要参数表
序号 | 图例 | 模块 | 主要参数 |
1 | 主机×1 | 尺寸:216mmx200mmx62mm。 主控板:上位机采用ROS系统设计,下位机采用Arduino硬件系统设计;两个主控制器,包括一款基于Cortex A53芯片的嵌入式控制器,预装ROS系统,集成电机驱动、传感器扩展电路。 主机接口:电源接口×1 、USB2.0接口×2、USB3.0接口×2、HDMI接口×1、网口×1、TTL串口×1、2510插头GPIO接口×32、航空头GPIO接口×14、航空头TTL串口x1、总线接口x4、步进电机接口x4。 支持USB/Wifi/ Bluetooth通信。 工作电压:12V | |
2 | 悬架模块×1 | 尺寸:(长×宽×高)380x290x120mm。 半独立悬挂。 支持四轮安装。 安装8个超声测距传感器。 | |
3 | 轮模块×4 | 尺寸:(长×宽×高)105mm×60mm×85mm。 内部安装高精度电机,可扩展为摆动模块。 结构:支持扩展步进电机、编码器直流电机、伺服电机。 | |
4 | 激光雷达×1 | 激光雷达测距范围0.15-12m,360°无死角扫描,测距分辨率<0.5mm,角度分辨率<1°,测量频率最小值不小于1500hz,测量频率最大值8000hz,扫描频率最小值1hz,扫描频率最大值10hz。 含支架。 | |
5 | 高清摄像头×1 | RGB像素:1080P 静态拍照分辨率:1280*720/640*480 接口:USB2.0 输入电压:5v 麦克风:双立体麦克风 音频采样率:16KHz | |
6 | 其他配件包×1 | 各类线材、工具、电池、电源、显示屏等。 |
4. 认识底盘的电器接口
① 常用接口说明:
底盘尺寸示意图
底盘常用电气接口
② 扩展电气接口说明:
左侧扩展接口对应图
车头扩展接口对应图
车尾扩展接口对应图
5. 底盘简化的系统框图
底盘的系统框图
桌面级底盘的本体组装及电路连接详情可参考下方演示视频:
6. 资料清单
序号 | 内容 |
1 | 样机三维文件 |
2 | Ros基础入门学习资料 |
【整体打包】-【R333】桌面级黑手底盘-1.本体说明-资料附件.rar | 50.82MB | 下载7次 | 下载 |
驱动及控制
1. 控制底盘单个轮子
实现思路
控制底盘的四个轮子分别转动。
下面我们先来了解一下底盘的总线舵机ID号。
底盘的头部、尾部
底盘上总线舵机ID号
操作步骤
① 下载文末资料中的参考程序Base_Experiment\Chassis_Drive\Control_single_bus_steering_gear\Control_single_bus_steering_gear.ino:
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-08-16 https://www.robotway.com/ ------------------------------*/ /* *Control single bus steering grar * * /////////////////////////// * head * ID:001 ID:000 * ___________ * | | * | | * | | * | | * | | * | | * |___________| * ID:003 ID:002 * Rear end * * 2020.8.7 by boris ////////////////////////// */
#define mySerial Serial2 //Serial port used by bus steering gear #define Serial_Baud_Rate 115200 //Serial Port baud rate #define BusServoSerialBaud 115200 //Bus steering gear baud rate #define wheel_speed_forward 0.08 //wheel forward speed #define wheel_speed_back -0.08 //wheel back speed #define wheel_speed_stop 0.0 //wheel stop speed enum{FORWARD_LEFT=1,FORWARD_RIGHT,BACK_LEFT,BACK_RIGHT,STOP};//wheel named
void setup(){ delay(1100);Serial.begin(Serial_Baud_Rate); mySerial.begin(BusServoSerialBaud);delay(1000); }
void loop(){ control_bus_steering_gear_test();// control single bus steering gear }
void control_bus_steering_gear_test() { Car_Move(FORWARD_LEFT,wheel_speed_forward);delay(1000);Stop_car(); //left front wheel forward Car_Move(FORWARD_LEFT,wheel_speed_back);delay(1000);Stop_car(); //left front wheel backword
Car_Move(FORWARD_RIGHT,wheel_speed_forward);delay(1000);Stop_car(); //right front wheel forward Car_Move(FORWARD_RIGHT,wheel_speed_back);delay(1000);Stop_car(); //right front wheel backword
Car_Move(BACK_LEFT,wheel_speed_forward);delay(1000);Stop_car(); //left rear wheel forward Car_Move(BACK_LEFT,wheel_speed_back);delay(1000);Stop_car(); //left rear wheel backword
Car_Move(BACK_RIGHT,wheel_speed_forward);delay(1000);Stop_car(); //right rear wheel backword Car_Move(BACK_RIGHT,wheel_speed_back);delay(1000);Stop_car(); //right rear wheel backword } |
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-08-16 https://www.robotway.com/ ------------------------------*/ /* * car action test (include:forward, back, turnleft,turnright, left translation, right translation) * * * IP address of bus steering gear * head * ID:001 ID:000 * ___________ * | | * | | * | | * | | * | | * | | * |___________| * ID:003 ID:002 * Rear end * * 2020.8.7 by boris //////////////////////////////// */ #define ActionDelayTimes 1000 #define mySerial Serial2 #define BusServoSerialBaud 115200
#define wheel_speed_forward 0.08 //car forward speed #define wheel_speed_back -0.08 //car back speed #define wheel_speed_stop 0.0 //car stop speed #define wheel_speed_left 0.08 //car turnleft speed #define wheel_speed_right -0.08 //car turnright speed #define wheel_speed_left_translation 0.08 //speed of car left translation #define wheel_speed_right_translation -0.08 //speed of car right translation enum{FORWARD=1,BACK,LEFT,RIGHT,LEFT_TRANSLATION,RIGHT_TRANSLATION,STOP}; //the movement state of the car float wheel_Speed[4]={0,0,0,0}; char cmd_return[200];
void setup() { delay(1000); Serial.begin(57600);delay(1000);//open serial mySerial.begin(BusServoSerialBaud);delay(1000);//open serial2 car_move_test(); }
void loop() { //car_move_test();//car action test //car_stop(); }
void car_move_test()//car action test { Car_Move(FORWARD);delay(ActionDelayTimes); // forward car_stop(); Car_Move(BACK);delay(ActionDelayTimes); // back car_stop(); Car_Move(LEFT_TRANSLATION);delay(ActionDelayTimes); //left translation car_stop(); Car_Move(RIGHT_TRANSLATION);delay(ActionDelayTimes);//right translation car_stop(); Car_Move(LEFT);delay(ActionDelayTimes); //turnleft car_stop(); Car_Move(RIGHT);delay(ActionDelayTimes); //turn right car_stop(); } |
② 将底盘轮子朝上,观察每个轮子的转动效果(实验效果可参考下方演示视频)。
2. 底盘的基本运动
实现思路
实现底盘前进、后退、左平移、右平移、左转、右转的功能。
操作步骤
① 下载文末资料中的参考程序Base_Experiment\Chassis_Drive\Control_Car_Movement\Control_Car_Movement.ino:
② 观察底盘的运动情况(实验效果可参考下方演示视频)。
3. 资料清单
序号 | 内容 |
1 | 程序源代码 |
【整体打包】-【R333】桌面级全向底盘-2.驱动及控制-资料附件.rar | 12.67KB | 下载7次 | 下载 |
传感器使用
1. 底盘循迹-灰度传感器
实现思路
实现全向底盘可以按指定路线进行行走的功能(实验场景如下图所示)。
操作步骤
① 连接电路。如下图所示是全向底盘上的4个灰度接线,但本实验中我们只需要用到头部的两个灰度传感器即可。
底盘的头部、尾部
头部的灰度传感器:接42、44号引脚
② 下载文末资料中的参考程序Base_Experiment\Tracking_Car\Tracking_Car.ino:
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-08-16 https://www.robotway.com/ ------------------------------*/ /* *Tracking Car * * head * pin:42 pin:44 * ___________ * | | * | | * | | * | | * |___________| * pin:36 pin:34 * Rear end * 2020.8.7 by boris */ #define mySerial Serial2 //statement serial2 #define Sensor_Numbers 4 //numbers of sensor #define Serial_Baud_Rate 115200 //serial port baud rate #define BusServoSerialBaud 115200//bus steering gear baud rate #define wheel_speed_forward 0.06 //car forward speed #define wheel_speed_back -0.06 //car back speed #define wheel_speed_stop 0.0 //car stop speed #define wheel_speed_left 0.1 //car turnleft speed #define wheel_speed_right -0.1 //car turnright speed enum{FORWARD=1,BACK,LEFT,RIGHT,STOP};//the movement state of the car
const int Gray_Sensor_Pin[Sensor_Numbers] = {44,42,36,34};//define gray sensor pin const int GraySensorNumbers = sizeof(Gray_Sensor_Pin)/sizeof(Gray_Sensor_Pin[0]); char cmd_return[200];
void setup(){ delay(1100);Serial.begin(Serial_Baud_Rate); Gray_Sensor_Init(); mySerial.begin(BusServoSerialBaud);delay(1000); }
void loop(){ tracking_car();//car tracking //Car_Move(STOP); }
void Gray_Sensor_Init()//sensor init { for(int i=0;i<Sensor_Numbers;i++) { pinMode(Gray_Sensor_Pin[i],INPUT);delay(2); } delay(200); }
void tracking_car() //car tracking { int sensor_data[Sensor_Numbers] = {0,0,0,0}; int sum = 0; for(int i=0;i<Sensor_Numbers-2;i++) { sensor_data[i] = digitalRead(Gray_Sensor_Pin[i]); sum |= sensor_data[i] << i; } switch(sum) { case 0: Car_Move(FORWARD); break; case 1: Car_Move(LEFT); break; case 2: Car_Move(RIGHT); break; case 3: Car_Move(FORWARD); break; default: break; } } |
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-08-16 https://www.robotway.com/ ------------------------------*/ /* * Avoid obstacles car * * * 车身位置及传感器接线: * * // Y * // | * // | * // | t:53 t:8 * // | (ID:003) e:A15 e:9 (ID:001) * // | t:37 A - - - - - - - - - - - - - - - - - -Y: t:39 * // | e:38 | | e:40 * // | | | 车头 * // | t:31 | | t:45 * // | e:32 Z - - - - - - - - - - - - - - - - - -X: e:46 * // | (ID:002) t:47 t:A13 (ID:000) * // | e:48 e:A14 * // 0-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- X * //////////////////////////////////////////////////////////////////////////////////////////////// * * 2020.8.7 by boris */ #define mySerial Serial2 //Serial port used by bus steering gear #define Serial_Baud_Rate 115200 //Serial Port baud rate #define BusServoSerialBaud 115200 //Bus steering gear baud rate #define wheel_speed_forward 0.04 //car forward speed #define wheel_speed_forward_up 0.06 //forward speed up #define wheel_speed_back -0.04 //car back speed #define wheel_speed_stop 0.0 //car stop speed #define wheel_speed_left 0.1 //car turnleft speed #define wheel_speed_right -0.1 //car turnright speed #define wheel_speed_left_translation 0.04 //speed of car left translation #define wheel_speed_right_translation -0.04 //speed of car right translation enum{FORWARD=1,BACK,LEFT,RIGHT,LEFT_TRANSLATION,RIGHT_TRANSLATION,STOP,FORWARD_UP}; //the movement state of the car char cmd_return[200]; float wheel_Speed[4]={0,0,0,0};
void setup(){ delay(1100);Serial.begin(Serial_Baud_Rate); init_Ultrasonic();delay(200); mySerial.begin(BusServoSerialBaud);delay(1000); }
void loop(){ //Car_Move(STOP);delay(1000); car_move(); //get_ultrasonic_sensor_data(); } |
③ 观察全向底盘循迹的效果(实验效果可参考下方演示视频)。
2. 底盘避障-超声测距
实现思路
实现全向底盘可以在模拟的场景下进行避障然后行进的功能(实验场景如下图所示)。
操作步骤
① 连接电路,如下图所示是全向底盘的8个超声测距接线图。
② 下载文末资料中的参考程序Base_Experiment\Avoiding_Obstacles_Car\Avoiding_Obstacles_Car.ino:
③ 观察全向底盘避障的效果(实验效果可参考下方演示视频)。
3. 资料清单
序号 | 内容 |
1 | 程序源代码 |
【整体打包】-【R333】桌面级全向底盘-3.传感器使用-资料附件.rar | 12.63KB | 下载3次 | 下载 |
机器视觉
机器视觉是人工智能正在快速发展的一个分支,简单说来机器视觉就是用机器代替人眼来做测量和判断。机器视觉系统是通过机器视觉产品(即图像摄取装置,分CMOS和CCD两种)将被摄取目标转换成图像信号,传送给专用的图像处理系统,得到被摄目标的形态信息,根据像素分布和亮度、颜色等信息,转变成数字化信号;图像系统对这些信号进行各种运算来抽取目标的特征,进而根据判别的结果来控制现场的设备动作。
机器视觉基础主要包含形状识别、颜色检测、颜色追踪、二维码识别等。
机器视觉系统最基本的特点就是提高生产的灵活性和自动化程度,在一些不适于人工作业的危险工作环境或者人工视觉难以满足要求的场合,常用机器视觉来替代人工视觉。同时在大批量重复性工业生产过程中,用机器视觉检测方法可以大大提高生产的效率和自动化程度。接下来我们将结合机器视觉基础,基于开源的机器人平台,进行形状识别、颜色检测、颜色追踪的应用开发。
1. 形状识别-识别圆形
实现思路
利用摄像头采集图片信息识别圆形,在界面上显示出圆的圆心坐标。
器材准备
摄像头、红色和绿色两种圆形图(如下图所示)。
操作步骤
① 下载文末资料中的参考程序visual_experiment_ws\src\shape_detection\scripts\shape_detection_victory.py:
#!/usr/bin/env python #!-*-coding=utf-8 -*-
import rospy from sensor_msgs.msg import Image import cv2 import numpy as np from cv_bridge import CvBridge, CvBridgeError import sys import time
lower_blue=np.array([100,43,46]) upper_blue=np.array([124,255,255]) #lower_blue=np.array([14,143,80]) #upper_blue=np.array([23,255,200])
#lower_red=np.array([0,200,55]) #upper_red=np.array([10,255,130]) #lower_red=np.array([0,150,55]) #upper_red=np.array([10,255,130]) lower_red=np.array([0,43,46]) upper_red=np.array([10,255,255])
lower_green=np.array([40,43,46]) upper_green=np.array([77,255,255]) font = cv2.FONT_HERSHEY_SIMPLEX # 设置字体样式 kernel = np.ones((5, 5), np.uint8) # 卷积核 img_pub = rospy.Publisher('webcam/image_raw', Image, queue_size=2)
#cap = cv2.VideoCapture(0)
def areaCal(contour):
area = 0 for i in range(len(contour)): area += cv2.contourArea(contour[i]) return area
def image_callback(msg): bridge = CvBridge() frame = bridge.imgmsg_to_cv2(msg, "bgr8") #cv2.imshow("source", frame) #ret, frame = Video.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转换为灰色通道 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 转换为HSV空间 mask = cv2.inRange(hsv, lower_red, upper_red) # 设定掩膜取值范围 [消除噪声] #mask = cv2.inRange(hsv, lower_green, upper_green) # 设定掩膜取值范围 [消除噪声] opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) # 形态学开运算 [消除噪声] bila = cv2.bilateralFilter(mask, 10, 200, 200) # 双边滤波消除噪声 [消除噪声] edges = cv2.Canny(opening, 50, 100) # 边缘识别 [消除噪声] mask_green = cv2.inRange(hsv,lower_green,upper_green) opening_green = cv2.morphologyEx(mask_green, cv2.MORPH_OPEN, kernel) bila_green = cv2.bilateralFilter(mask_green, 10, 200, 200) edges_green = cv2.Canny(opening_green, 50, 100) images,contourss,hierarchvs = cv2.findContours(edges_green,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) image,contours,hierarchv = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) scaling_factor = 0.5 print "area_red=",areaCal(contours),"area_green=",areaCal(contourss) if(areaCal(contours)>50): #circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=20, minRadius=20, maxRadius=500) circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=20, minRadius=0, maxRadius=0) if circles is not None: # 如果识别出圆 #print "I found the red circle" for circle in circles[0]: x_red=int(circle[0]) y_red=int(circle[1]) r_red=int(circle[2]) cv2.circle(frame, (x_red, y_red), r_red, (0, 0, 255), 3) # 标记圆 cv2.circle(frame, (x_red, y_red), 3, (255, 255, 0), -1) # 标记圆心 text = 'circle' + 'x: '+str(x_red)+' y: '+str(y_red) cv2.putText(frame, text, (10, 30), font, 1, (0, 255, 0), 2, cv2.LINE_AA, 0) # 显示圆心位置
if(areaCal(contourss)>1000): #circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=20, minRadius=20, maxRadius=500) circles = cv2.HoughCircles(edges_green, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=20, minRadius=0, maxRadius=0) if circles is not None: # 如果识别出圆 #print "I found the green circle" for circle in circles[0]: x_red=int(circle[0]) y_red=int(circle[1]) r_red=int(circle[2]) cv2.circle(frame, (x_red, y_red), r_red, (0, 0, 255), 3) # 标记圆 cv2.circle(frame, (x_red, y_red), 3, (255, 255, 0), -1) # 标记圆心 text = 'circle' + 'x: '+str(x_red)+' y: '+str(y_red) cv2.putText(frame, text, (10, 60), font, 1, (0, 255, 0), 2, cv2.LINE_AA, 0) # 显示圆心位置 frame = cv2.resize(frame,None,fx=scaling_factor,fy=scaling_factor,interpolation=cv2.INTER_AREA) msg = bridge.cv2_to_imgmsg(frame, encoding="bgr8") img_pub.publish(msg) cv2.waitKey(3) ''' rate = rospy.Rate(20) # 5hz scaling_factor = 0.5 bridge = CvBridge() count = 0 while not rospy.is_shutdown(): if (True): count = count + 1 else: rospy.loginfo("Capturing image failed.") if count == 2: count = 0 frame = cv2.resize(frame,None,fx=scaling_factor,fy=scaling_factor,interpolation=cv2.INTER_AREA) msg = bridge.cv2_to_imgmsg(frame, encoding="bgr8") img_pub.publish(msg) rate.sleep() ''' def webcamImagePub(): rospy.init_node('webcam_puber', anonymous=True) # img_pub = rospy.Publisher('webcam/image_raw', Image, queue_size=2) rospy.Subscriber("/image_raw", Image, image_callback) rospy.spin()
if __name__ == '__main__': try: webcamImagePub() except rospy.ROSInterruptException: pass finally: webcamImagePub() |
#!/usr/bin/env python #!-*-coding=utf-8 -*-
import rospy from sensor_msgs.msg import Image from cv_bridge import CvBridge, CvBridgeError import cv2 import numpy as np import serial import time import sys from std_msgs.msg import UInt16 from std_msgs.msg import String
img_pub = rospy.Publisher('webcam/image_raw', Image, queue_size=2) lower_blue=np.array([100,43,46]) upper_blue=np.array([124,255,255])
lower_red=np.array([0,43,46]) upper_red=np.array([10,255,255])
lower_green=np.array([40,43,46]) upper_green=np.array([77,255,255]) font = cv2.FONT_HERSHEY_SIMPLEX kernel = np.ones((5, 5), np.uint8)
def areaCal(contour): area = 0 for i in range(len(contour)): area += cv2.contourArea(contour[i]) return area
def image_callback(msg): bridge = CvBridge() img = bridge.imgmsg_to_cv2(msg, "bgr8") #cv2.imshow("source", img) hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV) mask_red = cv2.inRange(hsv,lower_red,upper_red) res = cv2.bitwise_and(img,img,mask=mask_red) scaling_factor = 0.5 # cv2.imshow("res",res) image,contours,hierarchv = cv2.findContours(mask_red,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # print "red=",areaCal(contours) if (areaCal(contours)>5000): # print "red color" text_red = 'the color is red' cv2.putText(img, text_red, (10, 30), font, 1, (0, 0, 255), 2, cv2.LINE_AA, 0) mask_blue= cv2.inRange(hsv, lower_blue, upper_blue) res_yellow = cv2.bitwise_and(img,img,mask=mask_blue) image,contours,hierarchv = cv2.findContours(mask_blue,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) if(areaCal(contours)>8000): text_blue = 'the color is blue' # print "blue color" cv2.putText(img, text_blue, (10, 60), font, 1, (255, 0, 0), 2, cv2.LINE_AA, 0) mask_green=cv2.inRange(hsv, lower_green,upper_green) res_green = cv2.bitwise_and(img,img,mask=mask_blue) image,contours,hierarchv = cv2.findContours(mask_green,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) if (areaCal(contours)>5000): text_green = 'the color is green' cv2.putText(img, text_green, (10, 90), font, 1, (0, 255, 0), 2, cv2.LINE_AA, 0) # print ("green color") # else: # print( "NONE COLOR" ) frame = cv2.resize(img,None,fx=scaling_factor,fy=scaling_factor,interpolation=cv2.INTER_AREA) msg = bridge.cv2_to_imgmsg(frame, encoding="bgr8") img_pub.publish(msg) cv2.waitKey(3) # cv2.waitKey(1)
def main(): rospy.init_node('image_listener',anonymous=True) #image_topic = "/camera/color/image_raw" #rospy.Subscriber("/camera/color/image_raw", Image, image_callback) rospy.Subscriber("/image_raw", Image, image_callback) rospy.spin()
if __name__ == '__main__': main() |
#!/usr/bin/env python #!coding=utf-8
import rospy from sensor_msgs.msg import Image from cv_bridge import CvBridge, CvBridgeError import cv2 import numpy as np import serial import time import sys from std_msgs.msg import UInt16 from std_msgs.msg import String
#lower_blue=np.array([50,143,146]) #upper_blue=np.array([124,255,255]) #lower_blue=np.array([14,143,80]) #upper_blue=np.array([23,255,200]) lower_blue=np.array([100,43,46]) upper_blue=np.array([124,255,255])
#lower_red=np.array([0,200,55]) #upper_red=np.array([10,255,130]) #lower_red=np.array([0,150,55]) #upper_red=np.array([10,255,130]) lower_red=np.array([0,43,46]) upper_red=np.array([10,255,255])
lower_green=np.array([40,43,46]) upper_green=np.array([77,255,255])
#ser = serial.Serial('/dev/ttyACM0', 57600, timeout=2.0) #2020.8.28 source value=0.5 img_pub = rospy.Publisher('webcam/image_raw', Image, queue_size=2) data_pub= rospy.Publisher('Color_center_point',String,queue_size=20)
red_flag=0 blue_flag=0 image_flags = 1 cx_string="" cy_string="" cx_cy_string="" send_data = "" count=0 scaling_factor = 1.0 #2020.8.28 source value=0.5
def areaCal(contour): area = 0 for i in range(len(contour)): area += cv2.contourArea(contour[i]) return area
def image_callback(msg): global count,send_data bridge = CvBridge() frame = bridge.imgmsg_to_cv2(msg, "bgr8") #cv2.imshow("source", frame) hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) mask_red = cv2.inRange(hsv,lower_red,upper_red) res = cv2.bitwise_and(frame,frame,mask=mask_red) #cv2.imshow("res",res) image,contours,hierarchv = cv2.findContours(mask_red,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) print "mianji=",areaCal(contours) if (areaCal(contours)>5000): if(areaCal(contours)>80000): send_data="back" #ser.write("back\n") #print "back" elif( areaCal(contours)<50000 and areaCal(contours)>5000 ): send_data="forward" #ser.write("forward\n") #print "forward" else: send_data="state" if len(contours) > 0: c = max(contours, key=cv2.contourArea) #print "c=",c cnt=contours[0] cv2.drawContours(frame, c, -1, (0, 0, 255), 3)#画轮廓 #cv2.imshow("drawcontours",frame) M = cv2.moments(c) if M["m00"] !=0: cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) cv2.circle(frame, (cx,cy), 8, (0, 255, 0), -1) print "center=",center,"cx=",cx,"cy=",cy cx_string=str(cx) cy_string=str(cy) #cx_cy_string=(cx_string + "-" + cy_string + "\n").encode('utf-8') #cx_cy_string=(cx_string + "-" + cy_string + "-" + send_data + "\n").encode('utf-8') cx_cy_string=(cx_string + "-" + cy_string + "+" + send_data ).encode('utf-8') print (cx_cy_string) data_pub.publish(cx_cy_string) else: cx=0 cy=0 else: print "no red color something" #cv2.imshow("chanle", img) # if cv2.waitKey(1) & 0xFF == ord('q'): # #break # #continue if(image_flags==1): count = count+1 else: rospy.loginfo("Capturing image failed.") if count == 2: count = 0 frame = cv2.resize(frame,None,fx=scaling_factor,fy=scaling_factor,interpolation=cv2.INTER_AREA) msg = bridge.cv2_to_imgmsg(frame, encoding="bgr8") img_pub.publish(msg) cv2.waitKey(1)
def main(): rospy.init_node('image_listener',anonymous=True) # img_pub = rospy.Publisher('webcam/image_raw', Image, queue_size=2) rospy.Subscriber("/image_raw", Image, image_callback) rospy.spin()
if __name__ == '__main__': main() |
/* * rosserial Publisher Example * Prints "hello world!" */
#include <ros.h> #include <std_msgs/String.h> #include <Arduino.h> #include <stdio.h> #include <string.h> #define CTL_BAUDRATE 115200 //总线舵机波特率 #define mySerial Serial2 #define SerialBaudrate 57600 #define RGB_LED_NUMBERS 3 #define Bus_servo_Angle_inits 1500 #define ActionDelayTimes 1500 // #define wheel_speed_forward 0.08 //car forward speed #define wheel_speed_back -0.08 //car back speed #define wheel_speed_stop 0.0 //car stop speed #define wheel_speed_left 0.1 //car turnleft speed #define wheel_speed_right -0.1 //car turnright speed #define wheel_speed_left_translation 0.08 //speed of car left translation #define wheel_speed_right_translation -0.08 //speed of car right translation char cmd_return[200];
String receive_string="hello"; ros::NodeHandle nh; void messageCb( const std_msgs::String &toggle_msg){ receive_string=toggle_msg.data; }
ros::Subscriber<std_msgs::String> sub("Color_center_point", &messageCb ); enum{FORWARD=1,BACK,LEFT,RIGHT,LEFT_TRANSLATION,RIGHT_TRANSLATION,STOP}; //the movement state of the car
void setup() { delay(1100); Serial.begin(SerialBaudrate); mySerial.begin(CTL_BAUDRATE); nh.initNode(); nh.subscribe(sub); }
void loop() { if( (receive_string.length())<5 && (receive_string.length())>15 ) { for(int i=0;i<1;i++){ break; } } else{ int SpacePosition[2] = {0,0}; int Data_one = 0; int Data_two = 0; int numbers_left=0 ,numbers_right=0; char num_left[32] = {}; char num_right[32] = {}; String x_data=""; String y_data=""; String z_data=""; String new_string = ""; SpacePosition[0] = receive_string.indexOf('-'); x_data = receive_string.substring(0,SpacePosition[0]); //if(x_data.length()>=4){break;} new_string = receive_string.substring(SpacePosition[0]+1); SpacePosition[1] = new_string.indexOf('+'); y_data = new_string.substring(0,SpacePosition[1]); z_data = new_string.substring(SpacePosition[1]+1);
Data_one = x_data.toInt(); Data_two = y_data.toInt(); //if( (Data_one<=120) && (z_data =="state") ){Car_Move(LEFT_TRANSLATION);} if( (Data_one<=280) && (Data_one>=20)){Car_Move(LEFT_TRANSLATION);} else if ( (Data_one>=360) && (Data_one<=600) ){Car_Move(RIGHT_TRANSLATION);} else if( z_data == "forward" ){Car_Move(FORWARD);} else if( z_data == "back" ){Car_Move(BACK );} else {Car_Move(STOP);}
receive_string = ""; x_data=""; y_data=""; z_data=""; new_string=""; } nh.spinOnce(); delay(100); } |
② 打开第一个终端(Ctrl+Alt+T)并输入:roslaunch astra_camera astra.launch 命令(见下图), 等待程序的运行。
打开第二个终端(Ctrl+Shift+T)并输入命令:roslaunch shape_detection shape_detection_experiment.launch,等待界面的启动。
③ 放置待识别的圆形图(请把物品放置在摄像头可以采集到的区域),就可以在界面上看到识别结果。如下图所示是分别识别出红色圆形、绿色圆形轮廓,并显示识别出圆心的中心坐标x、y的值。
2. 颜色识别(红绿蓝)
实现思路
当把物品放置在摄像头前时,摄像头采集到物品的颜色后,利用开源视觉库进行识别,然后在界面上显示识别颜色的结果(本实验中准备了红、绿、蓝三种物品)。
颜色识别算法的核心原理
RGB和HSV彩色模型:
数字图像处理通常采用RGB(红、绿、蓝)和HSV(色调、饱和度、亮度)两种彩色模型,RGB虽然表示比较至直观,但R、G、B数值和色彩三属性并没有直接关系,模型通道并不能很好的反映出物体具体的颜色信息,而HSV模型更符合我们描述和解释颜色的方式,使用HSV的彩色描述会更加直观。
RGB和HSV的区别:
①. RGB模型
三维坐标:
RGB:三原色(Red, Green, Blue)
原点到白色顶点的中轴线是灰度线,r、g、b三分量相等,强度可以由三分量的向量表示。
用RGB来理解色彩、深浅、明暗变化。
色彩变化: 三个坐标轴RGB最大分量顶点与黄紫青YMC色顶点的连线
深浅变化:RGB顶点和CMY顶点到原点和白色顶点的中轴线的距离
明暗变化:中轴线的点的位置,到原点,就偏暗,到白色顶点就偏亮
②. HSV模型
倒锥形模型:
这个模型就是按色彩、深浅、明暗来描述的。
H是色彩
S是深浅, S = 0时,只有灰度
V是明暗,表示色彩的明亮程度,但与光强无直接联系。
③. RGB与HSV的联系
从上面的直观的理解,把RGB三维坐标的中轴线立起来,并扁化,就能形成HSV的锥形模型了。
但V与强度无直接关系,因为它只选取了RGB的一个最大分量。而RGB则能反映光照强度(或灰度)的变化。
v = max(r, g, b)
由RGB到HSV的转换:
④.HSV色彩范围
操作步骤
① 连接电路:请把摄像头与主机进行连接。
② 下载文末资料中的参考程序visual_experiment_ws\src\color_detection\scripts\color_test_one.py:
③ 打开终端(Ctrl+Alt+T),输入roslaunch astra_camera astra.launch(见下图), 等待程序的运行。
打开第二个终端(Ctrl+Shift+T)输入命令:roslaunch color_detection color_detectioning.launch,等待程序的运行。
④ 界面启动后,放置物品(请把物品放置在摄像头可以采集到的区域),然后摄像头开始识别并在界面上显示识别结果。
下面以蓝色物品为例,当摄像头识别到蓝色物品后,在界面显示结果(the color is blue)。
大家也可以试着去放置红色、绿色物品,分别识别出的结果如下图所示。当摄像头识别到红色物品后,在界面显示结果(the color is red);当摄像头识别到绿色物品后,在界面显示结果(the color is green)。
检测到红色物品
检测到绿色物品
3. 颜色追踪
实现思路
摄像头采集到红色物品后,通过串口通信来发布消息,黑手底盘订阅消息后进行相应的运动。
器材准备
实验中需要用到的器材如下图所示(其中用红色的灭火器来代表待追踪的物体)。
操作步骤
① 下载文末资料中Ros通信的参考程序visual_experiment_ws\src\color_tracking\scripts\ros_arduino_translation_test.py:
下载文末资料中控制黑手底盘运动的参考程序visual_experiment_ws\src\color_tracking\arduino_program\Color_Tracking_Arduino_Program\Color_Tracking_Arduino_Program.ino:
② 打开终端(Alt+ctrl+T),输入roslaunch astra_camera astra.launch命令即可,见下图。
打开第二个终端(shift+ctrl+T),输入roslaunch color_tracking camera_calibration.launch 命令,见下图。
③ 移动灭火器,观察黑手底盘跟随灭火器运动的效果。
【注意:请把灭火器放置在摄像头可采集到的区域内;受硬件的影响,移动灭火器的速度建议稍微慢点,可以先把灭火器移动到一个位置,然后观察底盘追踪的效果】
我们可以在rviz界面里看到摄像头采集到红色目标的中心坐标及面积,供追踪使用(如下图所示)。
同时可以观察到底盘进行追踪红色的灭火器,直到运动到靠近灭火器的地方(具体的实验效果可参考下方演示视频)
4. 资料清单
序号 | 内容 |
1 | 程序源代码 |
【整体打包】-【R333】桌面级黑手底盘-4.机器视觉-资料附件.rar | 30.1MB | 下载4次 | 下载 |
slam导航
机器人的智能移动可应用于物流行业、交通行业,本节内容我们将利用键盘控制全向底盘运动完成slam建图,并能在已建好的地图里进行自主导航。
1. 键盘控制底盘运动
实现思路
按下键盘上指定的键,实现全向底盘前进、后退、转向、平移;还可以灵活的设置底盘的角速度、线速度来调整底盘的运动。下面是本实验中规划的控制全向底盘的键盘命令(如下表所示),大家可以先熟悉一下,稍后会在控制底盘时用到。
分类 | 运动指令 | |
键盘快捷键 | 指令含义 | |
基本运动 | i | 前进 |
, | 后退 | |
j | 左转 | |
l | 右转 | |
平移 | J | 左平移 |
L | 右平移 | |
调整角速度与线速度 | q | 增大底盘最大速度的10%(包含角速度与线速度) |
z | 减小底盘最大速度的10%(包含角速度与线速度) | |
调整线速度 | w | 仅仅增大底盘线速度的10% |
x | 仅仅减小底盘线速度的10% | |
调整角速度 | e | 仅仅增大底盘角速度的10% |
c | 仅仅减小底盘角速度的10% | |
除了上面按键之外的其它按键(如:k) | 停止 | |
Ctrl+c | 程序结束 |
键盘命令及含义表
操作步骤
① 下载文末资料中的参考程序fourmacnum_car_ws\src\Arduino_Program\black_handle_BaseCar\black_handle_BaseCar.ino:
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-08-22 https://www.robotway.com/ ------------------------------*/ //DUE控制版需要启用USE_USBON或USE_NATIVE_USB,UNO不需要 //#define USE_USBCON //PAOGRAMMING PORT //#define USE_NATIVE_USB //NATIVE USB PORT #define ActionDelayTimes 1500 //#include <SoftwareSerial.h> #include <ros.h> #include <ros/time.h> //#include<ServoTimer2.h> #include <geometry_msgs/Vector3.h> #define mySerial Serial2 #define BusServoSerialBaud 115200 ros::NodeHandle nh; void XYRun(double vx, double vy, double w); void messageCb( const geometry_msgs::Vector3& vel_cmd) {XYSetVel(vel_cmd.x, vel_cmd.y, vel_cmd.z);} ros::Subscriber<geometry_msgs::Vector3> vel_cmd_sub("vel_cmd", &messageCb );
geometry_msgs::Vector3 pose_message; ros::Publisher pose_feedback_pub("pose_feedback",&pose_message); geometry_msgs::Vector3 vel_message; ros::Publisher vel_feedback_pub("vel_feedback",&vel_message);
//ServoTimer2 myServo[4];//statement servo //int servo_port[4] = {10, 11, 12, 13};//x,y,z,a //float value_init[4] = {88, 88, 96, 86};//x,y,z,a const int kMessagePubRate = 5; unsigned long message_pub_time = 0; const int kReadMotorDeltaT = 5; unsigned long position_read_time = 0; float current_x = 0,current_y = 0,current_a = 0; float current_vx = 0,current_vy = 0,current_va = 0; float wheel_Speed[4]={0,0,0,0}; char cmd_return[200]; float sudu = 0.87;
//SoftwareSerial mySerial(51, 9);
void setup() { delay(1000); Serial.begin(57600);delay(1000); mySerial.begin(BusServoSerialBaud);delay(1000); //Servo_Action_Test();delay(1500); nh.initNode(); nh.subscribe(vel_cmd_sub); nh.advertise(pose_feedback_pub); nh.advertise(vel_feedback_pub); XYSetVel(0.0,0.0,0.0); position_read_time = millis(); message_pub_time = millis(); }
void loop() { // unsigned long times = millis(); // set_Single_servo_Vel(1600);delay(5000); // Serial.println(millis()-times); // set_Single_servo_Vel(1500); // while(1){ // delay(100); // } if(millis()>position_read_time) { XYread(); position_read_time+=kReadMotorDeltaT; }
if(millis()>message_pub_time) { pose_message.x = current_x; pose_message.y = current_y; pose_message.z = current_a; vel_message.x = current_vx; vel_message.y = current_vy; vel_message.z = current_va; pose_feedback_pub.publish(&pose_message); vel_feedback_pub.publish(&vel_message); message_pub_time+=1000.0/kMessagePubRate; } nh.spinOnce(); } |
/*------------------------------------------------------------------------------------ 版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved. Distributed under MIT license.See file LICENSE for detail or copy at https://opensource.org/licenses/MIT by 机器谱 2023-08-22 https://www.robotway.com/ ------------------------------*/ //DUE控制版需要启用USE_USBON或USE_NATIVE_USB,UNO不需要 //#define USE_USBCON //PAOGRAMMING PORT //#define USE_NATIVE_USB //NATIVE USB PORT #define ActionDelayTimes 1500 //#include <SoftwareSerial.h> #include <ros.h> #include <ros/time.h> //#include<ServoTimer2.h> #include <geometry_msgs/Vector3.h> #define mySerial Serial2 #define BusServoSerialBaud 115200 ros::NodeHandle nh; void XYRun(double vx, double vy, double w); void messageCb( const geometry_msgs::Vector3& vel_cmd) {XYSetVel(vel_cmd.x, vel_cmd.y, vel_cmd.z);} ros::Subscriber<geometry_msgs::Vector3> vel_cmd_sub("vel_cmd", &messageCb );
geometry_msgs::Vector3 pose_message; ros::Publisher pose_feedback_pub("pose_feedback",&pose_message); geometry_msgs::Vector3 vel_message; ros::Publisher vel_feedback_pub("vel_feedback",&vel_message);
//ServoTimer2 myServo[4];//statement servo //int servo_port[4] = {10, 11, 12, 13};//x,y,z,a //float value_init[4] = {88, 88, 96, 86};//x,y,z,a const int kMessagePubRate = 5; unsigned long message_pub_time = 0; const int kReadMotorDeltaT = 5; unsigned long position_read_time = 0; float current_x = 0,current_y = 0,current_a = 0; float current_vx = 0,current_vy = 0,current_va = 0; float wheel_Speed[4]={0,0,0,0}; char cmd_return[200]; float sudu = 0.87;
//SoftwareSerial mySerial(51, 9);
void setup() { delay(1000); Serial.begin(57600);delay(1000); mySerial.begin(BusServoSerialBaud);delay(1000); //Servo_Action_Test();delay(1500); nh.initNode(); nh.subscribe(vel_cmd_sub); nh.advertise(pose_feedback_pub); nh.advertise(vel_feedback_pub); XYSetVel(0.0,0.0,0.0); position_read_time = millis(); message_pub_time = millis(); }
void loop() { // unsigned long times = millis(); // set_Single_servo_Vel(1600);delay(5000); // Serial.println(millis()-times); // set_Single_servo_Vel(1500); // while(1){ // delay(100); // } if(millis()>position_read_time) { XYread(); position_read_time+=kReadMotorDeltaT; }
if(millis()>message_pub_time) { pose_message.x = current_x; pose_message.y = current_y; pose_message.z = current_a; vel_message.x = current_vx; vel_message.y = current_vy; vel_message.z = current_va; pose_feedback_pub.publish(&pose_message); vel_feedback_pub.publish(&vel_message); message_pub_time+=1000.0/kMessagePubRate; } nh.spinOnce(); } |
② 启动雷达、键盘、rosserial程序包。打开终端,输入roslaunch robot_navigation_control robot.launch命令(见下图),等待程序的运行启动界面。
终端
成功启动后,可以在终端看到底盘的当前speed(线速度)为0.04,turn(角速度)为0.08。
注意:speed指的是线速度的大小(包含方向),turn指的是角速度的大小(包含方向)。
启动后的界面(如下图所示):
③ 尝试按下键盘命令,控制底盘的运动。 (注意:请先保证终端是激活状态;按键盘时,稍微有点间隔,给底盘转动的时间)。
假如现在希望先增加底盘的最大速度,再降低底盘的最大速度,则分别按下键盘上q、z命令,则可以观察到终端的结果(如下图所示)。
更多命令大家可自行尝试。
2. 底盘同步定位与建图-SLAM
实现思路
利用Gmapping算法对底盘所在的未知环境进行建立地图、同步定位、最后保存此地图。此地图可供后续底盘导航使用。
器材准备
操作步骤
① 下载文末资料中的参考程序fourmacnum_car_ws\src\Arduino_Program\black_handle_BaseCar\black_handle_BaseCar.ino:
② 启动雷达、键盘、rosserial。
打开终端并输入命令:roslaunch robot_navigation_control robot_car.launch,见下图。
③ 启动构建地图服务。
重新打开新终端并输入:roslaunch four_macnum_slam four_macnum_slam.launch,见下图。
界面启动后,在rviz中选择“map”,可以在可视化界面看到有地图出现。
下面根据实际环境进行建图:按下键盘相关指令(键盘指令请参考上述内容--键盘命令及含义表)来控制全向底盘在场地内遍地运行,直至在Rviz内可以看到构建地图的轮廓,如下图所示(注意实际地图环境不同,出现的轮廓也会不同)。
底盘在未知环境中建立地图
④ 为了后续在已知环境中运行底盘,需把此次建立的地图进行保存。需要先进入保存地图的文件夹,然后给地图命名,保存即可。
下面是实验中的具体操作:
重新打开新终端(ctrl+shift+t)并输入:cd fourmacnum_car_ws/src/four_macnum_navigation/maps
之后输入:rosrun map_server map_saver -f ./your_map_name
返回下图红色框的信息,代表成功保存地图。
查看生成的地图文件
上面我们用map_server来保存地图,这里我们来简单了解和查看一下刚才保存的地图文件(见下图)。其中:map_server是一个和地图相关的功能包,它可以将已知地图发布出来,供导航和其它功能使用,也可以保存SLAM建立的地图。
地图文件,通常为pgm格式
地图的描述文件,通常为yaml格式
打开your_map_name.pgm(见下图):
打开your_map_name.yaml ,各个参数如下图所示:
其中占据的概率 occ = (255-color_avg)/255.0,color_avg为RGB三个通道的平均值。
3. 底盘导航—Navigation
实现思路
加载上述实验中建好的地图,全向底盘进行姿态估计并实现导航效果。
操作步骤
① 将创建的地图应用到导航程序中。这里我们需要修改下four_macnum_navigation.launch文件,将your_map_name.yaml 添加到此launch文件中。步骤如下:
打开终端并输入:cd fourmacnum_car_ws/src/four_macnum_navigation/launch
之后再输入:gedit four_macnum_navigation.launch
② 启动雷达、键盘、rosserial。
在该终端继续输入:roslaunch robot_navigation_control test_one.launch
③ 打开新终端并输入:roslaunch four_macnum_navigation four_macnum_navigation.launch(见下图)。
启动界面后,可以看到许多红色的箭头,这代表底盘的姿态估计还不准确。
④ 开始调整底盘的姿态。
使用2D Pose Estimate 标定底盘位于地图中的初始位置及车头指向(见下图)。
⑤ 鼠标点击有roslaunch robot_navigation_control test_one.launch的终端后,尝试按下键盘命令控制全向底盘运动(键盘指令请参考上述内容--键盘命令及含义表),最好尽可能多的消除地图中的箭头【这一步作用是确定底盘在地图中的实际位置以及车头指向】。
下面是多次按下键盘命令控制底盘运动后,箭头逐渐减少的效果。
下图是最后调整完的底盘姿态(即底盘在地图中的实际位置以及车头指向)。
⑥ 尝试给定全向底盘目标位置。
【使用2D Nav Goal】,这里的目标位置包含了目标点及车头指向,在可视化界面点击且转动箭头防线,机器人会移动到指定点的位置。
全向底盘地图自构建和SLAM导航实验效果,可参考下方演示视频:
4. 资料清单
序号 | 内容 |
1 | 程序源代码 |
【整体打包】-【R333】桌面级全向底盘-5.slam导航-资料附件.rar | 14.0MB | 下载7次 | 下载 |