【R332】桌面级机械臂
作者:机器谱
本体说明 驱动及控制 运动控制 仿真设计 应用设计 |
1. 机械臂整体描述
该桌面级机械臂为模块化设计,包含主机模块1个、转台模块1个、二级摆动模块1个、可编程示教盒1个、2种末端执行器、高清摄像头,以及适配器、组装工具、备用零件等。可将模块快速组合为一个带被动关节的串联3自由度机械臂,亦可进一步加装、更换执行器、传感器来完成各类控制实验。
1.1 机械臂清单介绍
本体说明
序号 | 名称 | 备注 |
1 | 本体 | 包括转台、小臂、大臂、执行机构 |
2 | 执行机构 | 如:手爪执行器、气动执行器 |
3 | 主机 | 包括树莓派、Arduino 主控板等相关的配置 |
4 | 显示屏 | |
5 | 电子部件 | 如:摄像头、遥控模块 |
机械臂的主要配置清单
1.2 产品配置
机械臂的主要硬件图如下所示:
主要硬件图
组成的主要硬件参数如下表所示:
序号 | 图例 | 模块 | 主要参数 |
1 | 主机 | 主机包括树莓派、Cortex A53芯片的嵌入式控制器两个主控制器。 | |
2 | 转台模块 | 模块输出与电机减速比=3/4;包含总线电机1个,全金属齿轮,额定扭力=15kgf·cm;该模块结构含减速齿轮组1个,模块上开有接口,可对接二级摆动模块。 | |
3 | 二级摆动模块 | 为机械臂的关节部分,皮带-齿轮混合传动结构,多级带传动,兼具串联、并联运动特性,金属支架。 小臂可实现360°无干涉转动,末端带被动关节。 | |
4 | 气动执行器模块 | 气动驱动盒、气动喷嘴、气动喷嘴支架。 | |
5 | 手爪执行器模块 | 为一个伺服电机驱动的小手爪模块。 | |
6 | 高清摄像头 | 摄像头;120°广角,720P | |
7 | 自编程遥控模块 | 基于Arduino设计,可自主编程,提供示教编程固件,模块有2个双向摇杆模块,包含两个蓝牙4.0模块。 | |
8 | 其他配件 | 各类线材、工具、电池、电源、显示屏等 |
2. 机械臂结构说明
该机械臂结构是一个带被动关节的3自由度,包含一个1自由度的转台和带被动关节的2自由度关节。
其中2自由度关节模块分为大臂和小臂:大臂通过1.76减速比的齿轮箱进行传动,小臂通过二级带传动实现传动和被动关节设计。被动关节可以保证执行器末端运动时指向保持一个方向,其中一级带传动控制小臂转动,另一级带传动使末端方向保持一致。
如下所示是装有手爪、摄像头的实物图:
装有手爪和摄像头的实物图
其中的执行机构部分(即现在装手爪执行器的地方)还可以灵活的替换为气动部件(见下图)。
装有气动部件的实物图
3. 机械臂的电路连接
在进行电路连接前,先来了解一下主机的布局及引脚图。下图是主机的布局实物图,本实验中主要用到总线、PWM(D4)、TX2/RX2。
主机正面图
主机电路引脚图(从右向左与主机对应)
按下图所示进行机械臂的电路连接:
机械臂与主机外设的接线图
显示屏、鼠标、键盘与主机进行连接
桌面级机械臂的本体组装及电路连接详情可参考下方视频:
开机注意事项:
机械臂的状态主要包含非复位状态、复位状态(其中复位状态指的是:大臂转动到垂直状态、小臂转动到水平状态)。只有开机看到机械臂能够从非复位状态转到复位状态,后续实验才能正常进行;否则可能会出现烧录程序,但机械臂不按预期运动的现象。
机械臂非复位状态
机械臂的复位状态
4. 软硬件环境配置及使用
4.1软硬件环境介绍
机器人可利用视觉识别技术,进行识别、追踪目标、定位等功能。我们可以利用上位机来控制机器人的大脑,用下位机控制设备。
上位机是指:
人可以直接发出操控命令的计算机,一般是PC或者计算机的屏幕上显示各种信号变化(液压,水位,温度等),本实验将利用树莓派屏幕上显示视觉识别的结果。
树莓派及Ros系统
下位机是指:
直接控制设备获取设备状况的的计算机,一般是PLC/单片机之类的,本实验将利用Arduino开源主控板充当下位机进行开发。
Arduino主控板及编程IDE
上位机发出的命令首先传给下位机,下位机再根据此命令解释成相应时序信号,直接控制相应设备。下位机读取设备状态数据(一般模拟量),转化成数字信号反馈给上位机。选择合适的平台及编程环境、函数库,就可以进行机器人结构、运动控制、视觉开发等多方面的应用,控制机器人的行为。
4.2在树莓派上安装Ros及功能包
为了便于使用,本实验中已把树莓派和Arduino板子进行了组合,设计出主控盒子(又叫主机),方便使用。主控盒子已预装好操作系统、编程环境以及需要用到的功能包。
主控盒子(主机)
对树莓派上的环境配置主要包括:安装Raspberry Pi OS (32-bit) with desktop操作系统、ROS、配置ROS环境、Arduino IDE、功能包。其中本实验需要的功能包包括视觉识别库(OpenCV2.4.9)、二维码识别库(zbar)。为了方便使用,本实验已做好了树莓派镜像文件,已经预安装好上述开发环境,电路连接好后可直接使用。当把主机与显示屏连接后,找到文末资料下载中的color_experiment_ws文件夹即可。
5. 资料清单
序号 | 内容 |
1 | 样机3D文件 |
2 | 程序源代码 |
【整体打包】-【R332】桌面级机械臂-1.本体说明-资料附件.zip | 11.73MB | 下载45次 | 下载 |
驱动及控制
1. 总线舵机模式的设置
机械臂驱动采用总线式电机驱动,该总线式电机具有多种模式,如360°圆周模式、270°逆时针模式、270°顺时针模式等。总线舵机采用单总线通信方式,与传统舵机相比,最大特点就是舵机之间可串联,最多可级联255个舵机。
打开文末资料内的“总线舵机ID及模式更改资料”,为总线舵机设置模式,包括转台、大臂、小臂三部分,需要分别为总线舵机设置ID、工作模式。关于总线舵机ID设置如下图所示:
机械臂的各舵机ID图
接下来开始为机械臂的各部分设置相应的舵机模式:
各部分 | 总线舵机ID | 计划设置舵机模式 | 代表含义 |
转台 | 000 | #000PMOD2! | 舵机模式,角度最大范围270 度,方向逆时针 |
大臂 | 001 | #001PMOD2! | 同上 |
小臂 | 002 | #002PMOD2! | 同上 |
串口发送命令例子 | 代表含义 |
rm 1500 | 底盘转到1500 |
um 1500 | 大臂转到1500 |
lm 1500 | 小臂转到1500 |
/*------------------------------------------------------------------------------------ 版权说明: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-02 https://www.robotway.com/ ------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------- 实验功能:实现单关节控制机械臂控制。
实验思路:通过串口发送命令,控制机械臂单关节控制。首先程序实现读取串口发送的数据,接着处理串口发送的数据, 最后程序将处理好的数据通过Serial1发送给机械臂达到控制机械臂的效果。 注意:串口发送命令格式为[机械臂部件pwm](pwm范围是[500~2500])。 例如:rm 1500 底盘转到1500 um 1500 大臂转到1500 lm 1500 小臂转到1500
实验接线:(主要接线) 机械臂通信口-------------(机械臂)电控箱 机械爪(舵机)---------------(执行器)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */
#include <Servo.h> #define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 115200 #define RM "rm" #define UM "um" #define LM "lm" String receive_string = ""; int catch_red_numbers=0; int catch_blue_numbers=0; int current_angle=0;
void setup() { // put your setup code here, to run once: delay(1100); Serial.begin(SerialBaudrate); SERIAL_PRINTLN.begin(CTL_BAUDRATE); Bus_servo_angle_init();delay(2000); Serial.println("Please input your commond......"); }
void loop() { while(Serial.available()>0) { String commond = ""; long n_step = 0; int index = 0; commond = Serial.readStringUntil('\n'); index = commond.indexOf(' '); n_step = commond.substring(index+1).toInt(); commond = commond.substring(0,index); Serial.print(commond);Serial.print('\t'); Serial.println(n_step); if(commond==RM){ if( (n_step<500) || (n_step>2500) ){ Serial.println("Sorry,the angle is out of range!"); } else{ ArmSingleServoTo(0,n_step); } n_step=0;commond=""; } if(commond==UM){ if( (n_step<500) || (n_step>2500) ){ Serial.println("Sorry,the angle is out of range!"); } else{ ArmSingleServoTo(1,n_step); } n_step=0;commond=""; } if(commond==LM){ if( (n_step<500) || (n_step>2500) ){ Serial.println("Sorry,the angle is out of range!"); } else{ ArmSingleServoTo(2,n_step); } n_step=0;commond=""; } Serial.println(); Serial.println("------------------------------------"); } } |
/*------------------------------------------------------------------------------------ 版权说明: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-03 https://www.robotway.com/ ------------------------------*/ /* 实验功能:摇杆模块控制吸盘开关。
实验思路:读取摇杆模块数值,判断该数据,进而根据数据选取区间,编写不同区间,吸盘状态。
实验操作:将该例程下载到arduino开发版,按照实验教程接线,完成后,给模块上电,试着 滑动摇杆模块,控制吸盘开关状态。
实验接线:气动箱信号脚连接到Barsa的D12引脚 Create 2020.7.17 By:Boris.yuan */ #define Sucker_Pin 12 #define Rocker_Pin A1
void setup() { // put your setup code here, to run once: delay(1000); Serial.begin(9600); pinMode(Sucker_Pin,OUTPUT); pinMode(Rocker_Pin,INPUT); }
void loop() { // put your main code here, to run repeatedly: int receive_Rocker_data = analogRead(Rocker_Pin); Serial.println(receive_Rocker_data); if( (receive_Rocker_data<200) ) { digitalWrite(Sucker_Pin,LOW); } else if( (receive_Rocker_data>800) ) { digitalWrite(Sucker_Pin,HIGH); } delay(30); } |
/*------------------------------------------------------------------------------------ 版权说明: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-03 https://www.robotway.com/ ------------------------------*/ #define Baud 9600 #define SERIAL Serial2
void setup() { // put your setup code here, to run once: delay(1000); Serial.begin(Baud); SERIAL.begin(Baud); }
void loop() { // put your main code here, to run repeatedly: while(SERIAL.available()>0) { String receive_string = SERIAL.readStringUntil('\n'); Serial.println(receive_string); receive_string = " "; } } |
/*------------------------------------------------------------------------------------ 版权说明: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-03 https://www.robotway.com/ ------------------------------*/ void setup() { // put your setup code here, to run once: Serial.begin(9600); //蓝牙串口波特率 pinMode(A0, INPUT); //左侧上下摇杆引脚 pinMode(A1, INPUT); //左侧上下摇杆引脚 pinMode(A2, INPUT); //右侧上下摇杆引脚 pinMode(A3, INPUT); //右侧上下摇杆引脚 }
void loop() { // put your main code here, to run repeatedly: if(analogRead(A0) > 1000) { Serial.print('u'); } if(analogRead(A0) < 25) { Serial.print('d'); } if(analogRead(A1) > 1000) { Serial.print('c'); } if(analogRead(A1) < 25) { Serial.print('s'); } if(analogRead(A2) > 1000) { Serial.print('f'); } if(analogRead(A2) < 25) { Serial.print('b'); } if(analogRead(A3) > 1000) { Serial.print('r'); } if(analogRead(A3) < 25) { Serial.print('l'); } delay(10);//发送执行周期20ms,可更改,越小机械臂运动越快,但是可能导致机械臂卡顿 } |
/*------------------------------------------------------------------------------------ 版权说明: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-03 https://www.robotway.com/ ------------------------------*/ //#include <SoftwareSerial.h> #include <stdio.h> #include <string.h> //#define mySerial Serial2 //总线舵机串口,TX2,GND,5V //#define mySerial2 Serial1 //蓝牙遥控串口TX1,RX1,GND,3.3V #define CTL_BAUDRATE 115200 //总线舵机波特率 #define NOZZLE_PIN 4 //气动引脚,注意气动供电6v
#define INIT_X 1 //假设大臂长度单位为1 #define INIT_Y 1 //小臂长度与大臂长一致单位为1 #define INIT_R 1500 //底座总线舵机中间位置波特率为1500
#define DX 0.01 //每次X方向长的移动距离单位 #define DY 0.01 //每次Y方向臂长的移动距离单位 #define DR 5 //每次底座转动的单位
//SoftwareSerial mySerial(51, 4); //SoftwareSerial mySerial2(10, 11); #define mySerial2 Serial2 #define mySerial Serial1
float cur_x = INIT_X, cur_y = INIT_Y; int cur_r = INIT_R;
int alphaToUpwm(float alpha) { return 2732.0-(alpha/M_PI)*2464; /*大臂转动角度的计算公式。2464是大臂摆动180°总线舵机计算后的pwm长度。 大臂的减速比是44/25=1.76,测试的motor转动180°需要的pwm长度是1400,1.76×1400=2464; 2732是转动90°之后竖直向上对应的实际pwm值,2732=(2464/2)+1500,其中1500是大臂初始化竖直状态是电机的PWM值*/ }
int betaToLpwm(float beta) { return 1500 + (beta/M_PI)*1400; //小臂无减速比,由总线舵机直接驱动,小臂转动角度的计算公式,1400是小臂摆动180°总线舵机需要的PWM长度,1500是小臂保持水平状态的舵机PWM值 }
int armMoveTo(float x, float y) { float alpha, beta; if (!cartesianToAlphaBeta(x, y, alpha, beta)) { armServoTo(alphaToUpwm(alpha),betaToLpwm(beta)); return 0; } return 1; }
void setup() { // put your setup code here, to run once: mySerial.begin(CTL_BAUDRATE); pinMode(NOZZLE_PIN, OUTPUT); //气动引脚定义 mySerial2.begin(9600); //蓝牙串口波特率设置 nozzle_off(); //初始化气动关闭 delay(1000); Serial.begin(9600); armMoveTo(cur_x, cur_y); //初始化机械臂大臂小臂位置 cur_r = rotServoTo(cur_r); //初始化机械臂底座位置 }
void loop() { // Serial.print(Serial1.parseInt()); // put your main code here, to run repeatedly: if (mySerial2.available()) { char c = mySerial2.read(); float tx, ty; switch(c) { case 'u': // up ty = cur_y + DY; if (armMoveTo(cur_x, ty) == 0) // success { cur_y = ty; } break; case 'd': // down ty = cur_y - DY; if (armMoveTo(cur_x, ty) == 0) // success { cur_y = ty; } break; case 'f': // forward tx = cur_x + DX; if (armMoveTo(tx, cur_y) == 0) // success { cur_x = tx; } break; case 'b': // backward tx = cur_x - DX; if (armMoveTo(tx, cur_y) == 0) // success { cur_x = tx; } break; case 'l': // left cur_r = rotServoTo(cur_r - DR); break; case 'r': // right cur_r = rotServoTo(cur_r + DR); break; case 'c': // catch //Serial.println('c'); nozzle_on(); break; case 's': // release //Serial.println('s'); nozzle_off(); 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-03 https://www.robotway.com/ ------------------------------*/ #define Baud 9600 #define SERIAL Serial2 void setup() { // put your setup code here, to run once: delay(1000); Serial.begin(Baud); SERIAL.begin(Baud); } void loop() { // put your main code here, to run repeatedly: while(SERIAL.available()>0) { String receive_string = SERIAL.readStringUntil('\n'); Serial.println(receive_string); receive_string = " "; } } |
器材准备:PC机或笔记本电脑(windows操作系统、Arduino IDE)、以及下图所示的器材。
操作过程:分别为机械臂的转台、大臂、小臂设置舵机模式,并能设置其初始角度。
第一步:按下图所示连接电路。
第二步:打开文末资料内的”总线舵机ID及模式更改资料\串口调试助手\sscom5.13.1.exe”(如下图所示)。
第三步:选择端口号、波特率,并打开串口。
先在菜单栏:串口设置-打开串口设置,选择端口号、波特率,点击“ok”即可。
第一步:按下图所示把大臂的舵机线与小模块进行电路连接(注意:请先把小模块断电)。
第二步:在串口调试助手里,选择端口号、波特率并打开串口。
第三步:依次点击下边描红的命令,就可完成大臂模式的设置,同时可观察到大臂转动。
第一步:按下图所示把小臂的舵机线与小模块进行电路连接(注意:请先把小模块断电)。
第二步:在串口调试助手里,选择端口号、波特率并打开串口。
第三步:依次点击下边描红的命令,就可完成小臂模式的设置,同时可观察到小臂转动。
这样就完成了总线舵机模式的设置工作,接下来的实验都是基于这种舵机模式来完成的。
2. 机械臂的单关节控制
实现思路:串口发送命令,控制单关节控制(提示:需要先设置好舵机模式)器材准备:机械臂本体、主控盒、显示屏、键盘、鼠标(如下图所示)
装有手爪和摄像头的实物图
操作步骤:
① 下载文末资料内的参考程序color_experiment_ws\src\Contril_Single_Joint\arm_driver\arm_driver.ino:
② 打开串口,查看返回的结果值。
注意:串口发送命令格式为[部件 pwm](pwm范围是[500~2500])。
首先,设置波特率并等待程序初始化,界面如下图所示:
接下来,可以发送控制转台运动的例子,来观看结果。
输入格式如下图所示,就可以看见机械臂的转台运动:
下面是本实验中分别针对转台、大臂、小臂转动位置进行测试的结果(如下图所示)。
机械臂单关节控制实验的详细操作步骤可参考下方演示视频:
3. 操作杆方案
实现思路:通过操作杆控制气动盒的开、关; 当气动盒打开后,实现可以吸物块的功能。
实验器材及接线:如下图所示。
Birdmen手柄扩展板引脚图如下图所示:
第二步:下载文末资料内的参考程序color_experiment_ws\src\Rocker_Control_Sucker\rocker_contril_sucker\rocker_contril_sucker.ino:
第三步:左侧的摇杆,当向右按下时,气动盒开关打开,可以吸取物品(如下图所示);当向左按下时,气动盒开关关闭。
4. 机械臂的无线遥控
本实验将通过 蓝牙 结合一个可编程示教器进行无线遥控的学习,包含蓝牙通信设定、无线控制。
实验目的:测试蓝牙主从模块数据发收。
连接在Birdmen手柄扩展板上的蓝牙模块为主模块;连接在带开关通信扩展坞上的蓝牙模块为从模块。
实现思路:当蓝牙主模块发送数据(如123)时,蓝牙从模块能收到该数据(123),代表通信成功。
实验器材:如下图所示。
蓝牙通信模块及配套线
操作步骤:
第一步:先给Basra主控板下载蓝牙主模块程序。① 先按下图所示连接好电路(注意:蓝牙通信扩展坞的开关需拨到左侧)。
② 然后下载文末资料内的参考程序color_experiment_ws\src\Bluetooth_communication\Master\Master.ino:
第二步:给主机里的Arduino mega2560控制板下载蓝牙从模块程序。
① 把蓝牙通信模块与主机的Tx2/RX2接口进行连接(如下图所示)。② 然后下载文末资料内的参考程序color_experiment_ws\src\Bluetooth_communication\Salve\Salve.ino:
③ 把蓝牙从模块连接在底座上(如下图所示),然后接在主机的TX2/RX2上,可以看见蓝牙从模块的灯亮,且一闪一闪的。
第三步:观察蓝牙主从模块灯,如果看到蓝牙主从模块上的灯亮着但不闪烁,则表示蓝牙主从模块连接成功。
第四步:打开蓝牙(从)模块的Arduino串口监视器,观察是否接受到数据“123”,如果接收到相应的数据(如下图所示),则表示蓝牙主从模块通信成功。
4.2 蓝牙遥控
实验目的:通过控制手柄上的两个摇杆,来实现机械臂气动搬运物块的功能。
实现思路:当按下手柄摇杆的不同方向时,可以控制机械臂的转台、大臂、小臂朝不同方向运动(默认规定手柄左边为左侧摇杆,右边为右侧摇杆)。
当左侧摇杆按下后,控制的吸盘、小臂的情况如下图所示:
当右侧摇杆按下后,控制的转台、大臂的情况如下图所示:
实验器材:BLE4.0蓝牙模块 ×2、连接线、Basra控制板×1、与蓝牙连接的底座、机械臂本体、气动盒、连接线、主机、显示屏、鼠标、键盘
操作步骤:
第一步:先给带有摇杆模块的主控板(接蓝牙主模块)下载程序。
① 按下图所示连接电路:
② 然后下载文末资料内的参考程序color_experiment_ws\src\Wireness_Contril\Program_With_Rocker\controller\controller.ino:
③ 将蓝牙主模块按下图所示插在手柄的扩展坞上。
第二步:给主机里的Arduino mega2560控制板(与蓝牙从模块连接)下载程序。
① 先把蓝牙从模块连接在扩展底座上;并把另一端与主机的TX2/RX2进行连接。
再把气动盒的三芯线与主机的PWM进行连接(如下图所示)。
② 然后下载文末资料内的参考程序color_experiment_ws\src\Wireness_Contril\Program_With_Chassis\Black_Handle_Robot_Arm_New\Black_Handle_Robot_Arm_New.ino:
第三步:通过控制手柄上的两个摇杆,来实现机械臂气动搬运物块的功能。
蓝牙遥控机械臂实验的详细操作步骤可参考下方演示视频:
5. 资料清单
序号 | 内容 |
1 | 程序源代码 |
2 | 总线舵机ID及模式更改资料 |
【整体打包】-【R332】桌面级机械臂-2.驱动及控制-资料附件.rar | 8.39MB | 下载5次 | 下载 |
运动控制
1. 调整总线舵机的模式
实现思路:
机械臂包括转台、大臂、小臂三部分,先设置好总线舵机每个ID的工作模式。下图是计划给舵机的各部分设置的ID号:
接下来为各部分设置相应的舵机模式(见下表),并在程序里进行编程设置。
各部分 | 总线舵机ID | 计划设置舵机模式 | 代表含义 |
转台 | 000 | #000PMOD1! | 舵机模式,角度最大范围270 度,方向顺时针 |
大臂 | 001 | #001PMOD1! | 同上 |
小臂 | 002 | #002PMOD3! | 舵机模式,角度最大范围180 度,方向顺时针 |
/*------------------------------------------------------------------------------------ 版权说明: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-10 https://www.robotway.com/ ------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------- 实验功能:通过arduino的窗口监视器控制机械臂运动。
实验思路:首先编写机械臂三轴运动的逆运动算法(即通过坐标点计算舵机运动的角度); 之后编写接收arduino串口监视器中的数据,并对该数据进行简单的处理(即数据转换); 最后,将处理后的数据与机械臂运动算法结合。
实现操作:将该程序下载到arduino开发版,接着按照实验教程连接线路。 上电后,打开arduino软件的串口监视器,波特率选择115200。 等待程序运行,直到串口监视器中显示:Please input your poit: 此时,在串口监视器中输入相应的坐标,观察机械臂是否转到目标点。 例如:在窗口监视器中输入:60,60,60
实验接线:(主要接线) 机械臂通信口-------------(机械臂)电控箱 机械爪(舵机)---------------(执行器)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */
#include <Arduino.h> #include <stdio.h> #include <string.h>
#define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 115200 #define Bus_servo_Angle_inits 1500
String receive_string = ""; enum{CATCH=1,REALIZE}; int catch_red_numbers=0; int catch_blue_numbers=0; unsigned long old_time = millis(); unsigned long new_time = millis(); unsigned long old_time_blue = millis(); unsigned long new_time_blue = millis();
int value_move[3]={1500,1500,1500}; int last_value[3]={0,0,0}; int poit_sqrt = 0; int catch_number = 0; void setup() { // put your setup code here, to run once: delay(1555);
Serial.begin(SerialBaudrate);delay(1000); SERIAL_PRINTLN.begin(CTL_BAUDRATE);delay(1000); Bus_servo_angle_init();delay(3000); Pneumatic_raw_init();delay(1000); Serial.println("Please input your poit:"); }
void loop() { Input_poit_by_Serial(); }
void Input_poit_by_Serial() { while(Serial.available()>0) { Serial.println("------------------------------------------"); String receive_string =""; String string_division = ""; int comma_first,comma_second; int receive_data[3]={0,0,0}; int string_test = 0; receive_string = Serial.readStringUntil('\n'); string_test = receive_string.toInt(); comma_first = receive_string.indexOf(','); receive_data[0] = receive_string.substring(0,comma_first).toInt(); string_division = receive_string.substring(comma_first+1); comma_second = string_division.indexOf(','); receive_data[1] = string_division.substring(0,comma_second).toInt(); receive_data[2] = string_division.substring(comma_second+1).toInt(); poit_sqrt = sqrt(receive_data[0] * receive_data[0] + receive_data[1] * receive_data[1] + receive_data[2]*receive_data[2]); Serial.println("The coordinates you entered are:"); Serial.print("(");Serial.print(receive_string);Serial.println(")");Serial.println(" "); if( (comma_first>0) && (comma_second>0) ) { if( (receive_data[0] >220) || (receive_data[1] >220) || (receive_data[2] >230) || (poit_sqrt>220) ) { Serial.println("Sorry,The poit is wrong!!!"); } else{ calculate_postion(receive_data[0],receive_data[1],receive_data[2]);delay(2000); } } else{ Serial.println("Sorry,The poit is wrong!!!"); }Serial.println(" "); } } |
器材准备:
PC机或笔记本电脑(windows操作系统、Arduino IDE)、以及下图所示的其它器材。
1.1修改转台的舵机模式
第一步:按下图所示连接好电路。
第二步:打开文末资料内的”总线舵机ID及模式更改资料\串口调试助手\sscom5.13.1.exe”(如下图所示)。
第三步:选择端口号、波特率,并打开串口。
先在菜单栏:串口设置---打开串口设置,选择端口号、波特率,点击“ok”即可。
接着点击“打开串口”,并点击“扩展”。
这里我们已经把命令设置好了,只需要依次点击描红的命令(如下图所示),就可以完成转台模式的设置,即可观察到转台转动到指定角度。
1.2 修改大臂的舵机模式
第一步:按下图所示把大臂的舵机线与小模块进行电路连接(注意:请先把小模块断电)。
其中,大臂的舵机线如下图所示。
第二步:在串口调试助手里,选择端口号、波特率并打开串口。
第三步:依次点击下边描红的命令,就可完成大臂模式的设置,同时可观察到大臂转动。
1.3 修改小臂的舵机模式
第一步:按下图所示把小臂的舵机线与小模块进行电路连接(注意:请先把小模块断电)。
第二步:在串口调试助手里,选择端口号、波特率并打开串口。
第三步:依次点击下边描红的命令,就可完成小臂模式的设置,同时可观察到小臂转动。
这样就完成了总线舵机模式的修改工作。
2. 机械臂运动学控制
控制机械臂最基本的方法是对其建立运动学模型,对于3轴的串联机械臂来说,运动学模型其本质就是给定空间3D坐标,利用运动学算法进行控制。
2.1分析-串联的运动学
串联的运动学有两种:一种是正运动学,一种是逆运动学。
串联的正运动学,简单来说是指确定每个关节舵机转动的角度,从而确定端点位置。这种方法在调试时对于少量自由度的机械臂比较实用,但是当自由度增加时,调试复杂程度也会随之增加。比如下方这个3自由度(不含执行器),我们只需要确定其3个关节上的舵机转动角度α,θ,β,即可确定执行端的位置(暂时不考虑臂长的因素)。
串联的逆运动学,简单来说是指确定端点的位置,然后通过算法计算出各关节需要转动的角度,自动调整到合适的位置。结合本项目中的3轴机械臂(见下图),我们将采用逆运动学算法进行设计,控制运动。
2.2设计-逆运动学算法
这里给大家介绍一种3自由度(不含执行器)逆运动学算法。
为方便大家的理解,算法总共分为5步:第一步为建立空间坐标系;第二步、第三步为简化模型运动学计算;第四步为末端坐标变换的补偿计算;第五步为总体总结。
第一步:将以转台旋转中心为原点建立空间坐标系如下:
从上图可知:O点为转台旋转轴和大臂旋转轴的交点,A点为大臂的末端,C点为小臂的旋转中心,AC方向为小臂的旋转轴,B点为小臂的末端。
第二步:为了方便计算,我们这里将AC段压缩为0,这样可以简化模型如下:
关于AC段运动的计算,我们在后面的步骤中通过简单的坐标变化进行计算。
第三步:进行简化模型的运动学计算。通过前面的了解,我们知道逆运动是通过坐标来计算出各个关节转动的角度。这里我们可以通过以下步骤建立几何模型和建立运动学算法:
① 将末端B点坐标设置为(x,y,z),作B点在xy平面的投影B1(x,y,0),如下所示:
② 计算转台旋转角度与末端坐标的关系。作O点到B1点的辅助线OB1(下图中两点距离为d),则OB1为OA和AB在xy平面的投影。这里我们假设OX为转台的初始角度,则∠XOB(即图中α)为转台旋转角度,如下所示:
则通过三角函数可得出以下关系:
公式1:
d为OB1,可通过两点距离公式得出如下关系:
公式2:
将“公式1”与“公式2”结合,可得转台旋转角度α与末端坐标的关系如下:
公式3:
③ 计算大臂和小臂夹角与坐标之间的关系。作O点和B点之间的辅助线OB(图中两点距离为e),则可知∠OAB(即图中β)为大臂小臂的夹角,如下所示:
从图示关系中,利用三角函数可得以下关系:
公式4:
上式中注意a,b分别为大臂和小臂的长度,可通过实际的进行测量,为已知量;
其中e可根据空间两点距离公式可得:
公式5:
将“公式4”与“公式5”结合可得大小臂夹角β与末端坐标之间的关系如下:
公式6:
④ 计算大臂旋转角度与末端坐标之间的关系。从图中可知∠AOB1(即图中θ)为大臂旋转角度:
如图所示可得以下关系:
公式7:
其中∠AOB在△AOB中利用三角函数求出:
公式8:
其中e的值可通过“公式5”得出;
“公式7”的∠AOB1在△OBB1用三角函数求出:
公式9:
其中z为BB1长度;
综上“公式7”、“公式8”、“公式9”可得大臂转动角度θ与末端坐标关系:
公式10:
⑤ 接下来计算小臂转动角度与末端坐标之间的关系。求小臂转动角度,我们需要先来看下小臂的0°所在位置,如下所示:
由上图可知,复位时大臂与小臂夹角θ=90°;而实际的小臂初始角度为Z轴正方向,则此时小臂转动角度λ=90°。那么进行运动后,可如下图所示得出小臂转动与末端坐标之间的关系:
如图可知:
其中θ为大臂角度,β关系满足
图中θ为大臂转动角度,图中进行了一个简单的几何关系变化。
最后得到简化模型的运动算法:
转台:
大臂:
小臂:
其中θ为大臂角度,β关系满足
注意:a为大臂长度,b为小臂长度,可通过利用配套游标卡尺测量得出;x,y,z为末端需要达到的实际坐标值。
第四步:接下来计算AC段在运动过程中对各关节角度的影响,这里采用一种利用坐标变化的补偿计算。
这里我们先来说下补偿计算的作用。在第二步中我们说到第三步的算法是将AC段压缩为0进行的计算,那么我们在第三步中利用真实的坐标点来计算各关节角度将会有一定的误差。补偿算法就是将实际的坐标转化为与第三步中所得算法一致的坐标,得出变换后的坐标之后,在带入到第三步的算法中进行计算,这样就可以获得准确的各个关节转动的角度。接下来我们详细了解一下具体的计算:
① 我们先在xy平面进行投影,如下所示:
图中0A为大臂,AC为大臂末端与小臂旋转中心的间距,CB为小臂;末端点B坐标为(x,y)。
② 为方便计算,作OA延长线AB1,且作B点在该延长线的垂线BB1,相交于点B1,设B1坐标为(x1,y1);
则从图中可知,转台转动角度为γ(这里默认OX为转台初始位置,因为我们这里通过坐标点求角度,所以γ为未知量);
③ 如下图所示作辅助线,B1点、B点作x轴平行线,且B1点作与B点平行线的垂线,如下所示:
通过平行线定理和余角定理可得:
公式11:∠B1=γ;
④ 接下来作连接OB、及B点在X轴垂线的辅助线,计算转台转动角度,如下图所示:
图中c为大臂末端和小臂旋转中心间距,可通过配套游标卡尺测量,为已知量;
从图中可得:
公式12:
依余弦定理可得:
公式13:
在△BOB1中,依余弦定理可得:
公式14:
其中OB依两点距离公式可得:
公式15:
合并“公式14”、“公式15”,可得:
公式16:
合并“公式12”、“公式13”、公式16,可计算出γ角度:
公式17:
⑤ 接下来如下图所示,在△HBB1中,利用三角函数计算出B1(x1,y1):
公式18:
公式19:
⑥ 通过前面的步骤,我们已经计算出第一象限的中的坐标变换,但是在第一象限中的转动范围只有0-90°,实际转动范围是0-180°,所以接下来我们计算第二象限的坐标变换,同前面的步骤,可做如下图:
从图中可知,当位于90°-180°之间时(即图中第二象限),可得转台旋转角度关系如下:
公式20:
公式21:
通过上面步骤,不难在第二象限中得出B点经过坐标变换后得到的B1坐标关系如下:
公式22:
公式23:
⑦ 这里咱们将补充中需要的公式进行总结,如下:
当
当
到这里我们完成了坐标变化的补偿计算。
第五步:大家已经了解了运动学计算和补偿计算,那么这两个算法如何搭配使用呢?如何利用算法进行机械臂的逆运动控制呢?这里我们可以通过一个简单的流程来理解。
2.3 实验操作
实现思路:在上位机上通过软件串口监视器,键盘输入机械臂的目标坐标位置,将从起始位置转到目标位置。
如从串口输入坐标(60,60,60),从坐标点(90,90,90)转动到坐标点(60,60,60)。程序中设置起始位置为(90,90,90)。
【备注:上面的坐标点单位为mm】
器材准备:机械臂本体、主控盒、显示屏、键盘、鼠标(如下图所示)
操作步骤:
① 下载文末资料内的参考程序color_experiment_ws\src\InputPoit_By_SerialPort\InputPoit_By_SerialPort.ino:
② 打开串口,选择好波特率和结束符(如下图所示)。
输入机械臂的目标坐标点,查看返回的结果值。【提示:坐标点的范围为0-220】
③ 现在希望机械臂转动到空间位置点(60,60,60),具体的操步骤如下(注意坐标间隔用英文字符“,”,最后一个坐标没有字符):
点击“发送”后,可以观察到机械臂从原位置转动到目标位置点,同时串口显示目标位置,如下图所示:
当然大家也可以尝试让机械臂转动到(80,80,80),看看效果。
机械臂运动学控制实验的详细操作步骤可参考下方演示视频:
3. 机械臂气动搬运
实现思路:利用串联机械臂的逆运动学算法,实现机械臂的气动搬运。实验场景如下图所示,其中两个物块代表所需要搬运的物品。
操作步骤:
① 下载文末资料内的参考程序color_experiment_ws\src\Pneumatic_manipulator_handling\Pneumatic_manipulator_handling.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-10 https://www.robotway.com/ ------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------- 实验功能:实现气动搬运物体(抓取两个物体,并放置两个物体)。 实验思路:通过三自由度机械臂算法求出机械臂的逆运动算法(通过坐标点计算出机械臂转动角度),接着 结合气动吸盘,规划机械臂运动路径(即添加坐标点),将物块抓取并搬运到指定目标点。搬运 结束后,在打开的窗口中可自行输入机械臂要要运行的轨迹点坐标。 实验接线:(主要接线) 机械臂通信口-------------(机械臂)电控箱 气动机械臂---------------(执行器)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */ #include <Arduino.h> #include <stdio.h> #include <string.h> #define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 115200 #define Bus_servo_Angle_inits 1500 String receive_string = ""; enum{CATCH=1,REALIZE}; int catch_red_numbers=0; int catch_blue_numbers=0; unsigned long old_time = millis(); unsigned long new_time = millis(); unsigned long old_time_blue = millis(); unsigned long new_time_blue = millis(); int value_move[3]={1500,1500,1500}; int last_value[3]={0,0,0}; int poit_sqrt = 0; int catch_number = 0; void setup() { // put your setup code here, to run once: delay(1555); Serial.begin(SerialBaudrate);delay(1000); SERIAL_PRINTLN.begin(CTL_BAUDRATE);delay(1000); Bus_servo_angle_init();delay(3000); Pneumatic_raw_init();delay(1000); } void loop() { raw_translation(); delay(8000); } void raw_translation() { //catch the first sth calculate_postion(60,130,40);delay(3000); catch_and_realize_something(0);delay(3000);//catch calculate_postion(60,110,80);delay(3000); calculate_postion(-120,110,80);delay(3000); calculate_postion(-120,130,2);delay(3000); catch_and_realize_something(1);delay(3000);//relize calculate_postion(-120,130,70);delay(3000); calculate_postion(-100,90,70);delay(3000); //catch the second sth calculate_postion(60,100,70);delay(3000); calculate_postion(60,130,10);delay(3000);//down catch_and_realize_something(0);delay(3000);//catch calculate_postion(60,110,80);delay(3000); calculate_postion(-120,110,80);delay(3000); calculate_postion(-120,130,37);delay(3000); catch_and_realize_something(1);delay(3000);//relize calculate_postion(-120,130,70);delay(3000); calculate_postion(-100,90,70);delay(3000); Bus_servo_angle_init();delay(2000); delay(1000); } |
/*------------------------------------------------------------------------------------ 版权说明: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-10 https://www.robotway.com/ ------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------- 实验功能:实现气动搬运物体(抓取两个物体,并放置两个物体)。 实验思路:通过三自由度机械臂算法求出机械臂的逆运动算法(通过坐标点计算出机械臂转动角度),接着 结合气动吸盘,规划机械臂运动路径(即添加坐标点),将物块抓取并搬运到指定目标点。搬运 结束后,在打开的窗口中可自行输入机械臂要要运行的轨迹点坐标。 实验接线:(主要接线) 机械臂通信口-------------(机械臂)电控箱 气动机械臂---------------(执行器)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */ #include <Arduino.h> #include <stdio.h> #include <string.h> #define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 115200 #define Bus_servo_Angle_inits 1500 String receive_string = ""; enum{CATCH=1,REALIZE}; int catch_red_numbers=0; int catch_blue_numbers=0; unsigned long old_time = millis(); unsigned long new_time = millis(); unsigned long old_time_blue = millis(); unsigned long new_time_blue = millis(); int value_move[3]={1500,1500,1500}; int last_value[3]={0,0,0}; int poit_sqrt = 0; int catch_number = 0; void setup() { // put your setup code here, to run once: delay(1555); Serial.begin(SerialBaudrate);delay(1000); SERIAL_PRINTLN.begin(CTL_BAUDRATE);delay(1000); Bus_servo_angle_init();delay(3000); Pneumatic_raw_init();delay(1000); } void loop() { raw_translation(); delay(8000); } void raw_translation() { //catch the first sth calculate_postion(60,130,40);delay(3000); catch_and_realize_something(0);delay(3000);//catch calculate_postion(60,110,80);delay(3000); calculate_postion(-120,110,80);delay(3000); calculate_postion(-120,130,2);delay(3000); catch_and_realize_something(1);delay(3000);//relize calculate_postion(-120,130,70);delay(3000); calculate_postion(-100,90,70);delay(3000); //catch the second sth calculate_postion(60,100,70);delay(3000); calculate_postion(60,130,10);delay(3000);//down catch_and_realize_something(0);delay(3000);//catch calculate_postion(60,110,80);delay(3000); calculate_postion(-120,110,80);delay(3000); calculate_postion(-120,130,37);delay(3000); catch_and_realize_something(1);delay(3000);//relize calculate_postion(-120,130,70);delay(3000); calculate_postion(-100,90,70);delay(3000); Bus_servo_angle_init();delay(2000); delay(1000); } |
/*------------------------------------------------------------------------------------ 版权说明: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-10 https://www.robotway.com/ ------------------------------*/ /* ----------------------------------------------------------------------------------------------------------------- 实验功能:在串口监视器中查看舵机当前转动的角度。 实验思路:首先编写驱动总线舵机驱动程序,之后,编写接收串口监视器数据的程序,接着将 串口监视器接收的数据进行简单的处理,最后,将处理后的数据与机械臂各部位结合。 实验操作:将该例程下载到arduino开发版,接着按照实验教程连接线路,完成后,打开arduino 的串口监视器,结合总线舵机使用教程,观察串口监视器中的数据是否与总线舵机教程相符。 例如串口监视器输出的:总线舵机000:#000P1114!表示的意思是: 地址为000的总线舵机(即转台)当前的pwm值为1114(即55度)。 度数angle与pwm的关系式为: angle = map(pwm,500,2500,0,180) 实验接线:(主要接线) 机械臂通信口-------------(机械臂)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */ #include <stdio.h> #include <string.h> #define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 115200 #define Bus_servo_Angle_inits 1500 String receive_string=""; int value_move[3]={1500,1500,1500}; int last_value[3]={0,0,0}; void setup() { // put your setup code here, to run once: delay(1100); Serial.begin(SerialBaudrate); SERIAL_PRINTLN.begin(CTL_BAUDRATE); Serial.println("Start!"); Bus_servo_angle_init();delay(2000); int ss = map(1114,500,2500,0,180); Serial.println(ss); } void loop() { for(int i=0;i<3;i++) { arm_action_test(i); for(int j=0;j<3;j++){ Read_get_busservo_angle_current(j); delay(1000); } delay(5000); } } |
② 程序下载完成后,观察气动搬运效果。正常情况下机械臂会先搬运上边的物块,放置在一侧;接着再搬运下边的物块,并把其放置在刚才的第一个物块上。
4. 机械臂手爪搬运
实现思路:利用串联机械臂的逆运动学算法,实现机械手爪搬运两个物块。实验场景如下图所示,其中两个物块代表所需要搬运的物品。操作步骤:
① 下载文末资料内的参考程序color_experiment_ws\src\Manipulator_handling\Manipulator_handling.ino:
② 程序下载完成后,观察搬运效果。
机械臂手爪搬运实验的详细操作步骤可参考下方演示视频:
5. 机械臂位置反馈
实现思路:让机械臂做3组动作,然后返回其相应的舵机的pwm值。其中机械臂包括转台、大臂、小臂三部分,实验中将分别返回控制其三部分的舵机Pwm值。
每个总线舵机的pwm范围为:500~2500,每个舵机模式为:180度。
所以舵机的实际角度与pwm映射关系为:angle=map(要输入的pwm值:500,2500,0,180)。明白了此原理后,接下来就可以实际操作了。
操作步骤:
① 下载文末资料内的参考程序color_experiment_ws\src\Get_BusServo_Current_Angle\Get_BusServo_Current_Angle.ino:
② 打开串口,查看返回的结果值(如下图所示)。
6. 资料清单
序号 | 内容 |
1 | 程序源代码 |
2 | 总线舵机ID及模式更改资料 |
【整体打包】-【R332】桌面级机械臂-3.运动控制-资料附件.rar | 8.39MB | 下载8次 | 下载 |
仿真设计
1. Ros概述
ROS是一个适用于机器人编程的框架,这个框架把原本松散的零部件耦合在了一起,为它们提供了通信架构。ROS虽然叫做操作系统,但并非Windows、Mac那样通常意义的操作系统,它只是连接了操作系统和你开发的ROS应用程序,所以它也算是一个中间件,基于ROS的应用程序之间建立起了沟通的桥梁,所以也是运行在Linux上的运行时环境,在这个环境上,机器人的感知、决策、控制算法可以更好的组织和运行。
对于关键词(框架、中间件、操作系统、运行时环境)都可以用来描述ROS的特性,作为初学者我们不必深究这些概念,随着你越来越多的使用ROS,就能够体会到它的作用。
Ros作为编程框架,介于应用程序与操作系统的中间件,它主要是把我们写的源代码,进行编译、链接后,方便在系统上运行。 Ros采用的是Catkin编译系统(这里的编译其实包含了编译、链接两个步骤)。
Catkin编译系统的工作原理当我们写完代码,执行一次catkin_make进行编译,调用系统自动完成编译和链接过程,构建生成目标文件
把源文件放在文末资料中的color_experiment_ws\src路径下(其中color_experiment_ws为工作空间),那就可以使用下面的命令进行编译:
$ cd ~/color_experiment_ws #回到工作空间,catkin_make必须在工作空间下执行 $ catkin_make #开始编译 $ source ~/color_experiment_ws/devel/setup.bash #刷新坏境 |
配置分类 | 备注 |
硬件环境 | PC机 |
软件环境 | Ubuntu18.04操作系统 ROS(melodic) |
知识补充: 释义1:命令中的“myself_ws”即为新创建的ros工作空间名,可根据个人喜好更改。 例如将“myself_ws”改为“yourself_ws”,则此时的命令为: mkdir -p ~/yourself_ws/src 释义2:命令中的src表示代码空间(Source Space),该名称不能更改,为ros固定 的命名方式。 |
①打开新终端,并输入:cd ~/myself_ws ,之后按下Enter ②接着在上述终端中输入:catkin_make,接着按下Enter,等待编译完成。该过程大致需要30秒。 ③最后在该终端中输入:echo “source ~/myself_ws/devel/setup.bash” >> ~/.bashrc,接着按下Enter 此时,便完成了永久上传创建的ros工作空间至环境变量中。 |
知识补充: 临时上传ros工作空间至环境变量:最后在该终端中输入:source devel/setup.bash ,接着按下Enter 释义:此时我们可在该终端中加载urdf模型及moveit,但该方法仅适用于此终端,一旦重新开启新终端后,如果想加载urdf模型及movei,需要再次在终端输入:source devel/setup.bash,故为了方便运行该ros工作空间中的程序,需要使用该方法将创建的ros工作空间永久添加到环境变量中。 |
知识拓展: ROS中机器人模型包含大量的部件,这些部件统称之为link,每一个link上面对应着一个frame, 即一个坐标系。link和frame概念是绑定在一起的,像上图pr2模型中我们可以看到有很多的frame,错综复杂的铺置在机器人的各个link上。维护各个坐标系之间的关系,就要靠着tf tree来处理,维护着各个坐标系之间的联通。 |
<robot name="test_robot"> //命名 <link name="link1" /> <link name="link2" /> <link name="link3" /> <link name="link4" /> <joint name="joint1" type="continuous"> <parent link="link1"/> //父节点 <child link="link2"/> //子节点 </joint> //关节 <joint name="joint2" type="continuous"> <parent link="link1"/> <child link="link3"/> </joint> <joint name="joint3" type="continuous"> <parent link="link3"/> <child link="link4"/> </joint> </robot> |
编译后,color_experiment_ws文件夹会自动生成build、devel两个文件(如下图所示)。
本节内容主要介绍了ROS开发时常常使用的工具,分别是RVIZ、URDF、STL(可由外部导入)、TF、Gazebo、Moveit!, 这些是我们开发时经常用到的工具。RViz是可视化工具,是将接收到的信息呈现出来;STL文件可以定义复杂的模型;gazebo是一种最常用的ROS仿真工具,也是目前仿真ROS效果最好的工具;rqt则是非常好用的数据流可视化工具,有了它我们可以直观的看到消息的通信架构和流通路径;moveit!是目前为止应用最广泛的开源操作软件。
1.2.1 RVIZ-机器人可视化工具在ROS开发中,这是一个常用工具,基本上的调试和开发都离不开这个工具—RViZ(the Robit Visualization tool)机器人可视化工具。可视化的作用是直观的,它极大的方便了监控和调试等操作,这个工具将在后续实验中陆续用到。
RVIZ启动界面
RVIZ启动后的界面
urdf文件是机器人模型的描述文件,以.urdf结尾。它定义了机器人的连杆和关节的信息,以及它们之间的位置、角度等信息,通过urdf文件可以将机器人的物理连接信息表示出来,并且可视化调试和仿真中显示,这里简单带大家了解一下URDF文件。
在理解urdf文件之前,我们需要先了解一种表达方式。机器人可以由link和joint进行描述,link可以简单理解为连杆,joint可以简单理解为关节。机器人的表达除了表示link和joint数量之外,还需要对link与joint的关系进行描述。
如下图所示, 机器人由一个根link(link1)向上, 分别出现了两个分支–link2和link3, 分别由joint连接link:
因此,典型的机器人描述如下所示,包含robot、link、joint,利用URDF文件进行模型描述,这里URDF可以简单理解为表达上述机器人表达方式的一种xmal文件。如下所示做一个简单的格式举例:
那么如何表达一个link呢?下面我们来看一下。
Link
link的图形化表示
下面我们来看一下描述link的属性和子元素:
属性
name(必需)
link的名字
子元素
<inertial> (可选)
连杆的惯性特性
<origin> (可选,defaults to identity if not specified)
定义相对于连杆坐标系的惯性参考系的参考坐标,该坐标必需定义在连杆重心处,其坐标轴可与惯性主轴不平行。
xyz (可选,默认为零向量)
表示 x,y,zx,y,zx,y,z 方向的偏置,单位为米。
rpy(可选: defaults to identity if not specified)
表示坐标轴在RPY方向上的旋转,单位为弧度。
<mass>连杆的质量属性
<inertia>
3×3旋转惯性矩阵,由六个独立的量组成:ixx, ixy, ixz, iyy, iyz, izz。
<visual> (可选)
连杆的可视化属性。用于指定连杆显示的形状(矩形、圆柱体等),同一连杆可以存在多个visual元素,连杆的形状为多个元素两个形成。一般情况下模型较为复杂可以通过soildwork绘制后生成stl调用,简单的形状如添加末端执行器等可以直接编写。同时可以在此处可根据理论模型和实际模型差距调整几何形状的位置。
<namel> (可选)
连杆几何形状的名字。
<origin> (可选,defaults to identity if not specified)
相对于连杆的坐标系的几何形状坐标系。
xyz (optional: defaults to zero vector)
表示x,y,zx,y,zx,y,z 方向的偏置,单位为米。
rpy (optional: defaults to identity if not specified)
表示坐标轴在RPY方向上的旋转,单位为弧度。
<geometry> (必需)
可视化对象的形状,可以是下面的其中一种:
<box>
矩形,元素包含长、宽、高。原点在中心。
<cylinder>
-圆柱体,元素包含半径、长度。原点中心。
<sphere>
球体,元素包含半径。原点在中心。
<mesh>
网格,由文件决定,同时提供 scale ,用于界定其边界。推荐使用 Collada .dae 文件, 也支持.stl文件,但必须为一个本地文件。
<material> (可选)
可视化组件的材料。可以在link标签外定义,但必需在robot标签内,在link标签外定义时,需引用link的名字。
<color>(可选)
颜色,由 red/green/blue/alpha 组成,大小范围在 [0,1] 内。
<texture>(可选)
材料属性,由文件定义。
<collision> (可选)
连杆的碰撞属性。碰撞属性和连杆的可视化属性不同,简单的碰撞模型经常用来简化计算。同一个连杆可以有多个碰撞属性标签,连杆的碰撞属性表示由其定义的几何图形集构成。
name (可选)
指定连杆几何形状的名称
<origin> (可选,defaults to identity if not specified)
碰撞组件的参考坐标系相对于连杆坐标系的参考坐标系。
xyz (可选, 默认零向量)
表示x,y,zx,y,zx,y,z 方向的偏置,单位为米。
rpy (可选, defaults to identity if not specified)
表示坐标轴在RPY方向上的旋转,单位为弧度。
<geometry>
与上述geometry元素描述相同
示例:
Joint
joint的图形化表示
下面我们来看一下描述joint的属性和子元素:
属性
name (必需的)
指定joint的名字(唯一的)
type (必需的)
指定joint的类型,有下列选项:
revolute:可以绕着一个轴旋转的铰链关节,有最大值和最小值限制。
continuous:连续型的铰链关节,可以绕一个轴旋转,没有最大值和最小值限制。
prismatic:滑动关节,可以沿着一个轴滑动,有最大值和最小值限制。
fixed:这不是一个实际的关节,因为它无法运动,所有的自由度都被锁定。这种类型的关节不需要指定轴、动力学特征、标度和最大值最小值限制。
floating:这是一个具有6个自由度的关节。
planar:此关节在一个平面内运动,垂线是运动轴。
子元素
<origin> (可选,defaults to identity if not specified)
从parent link到child link的变换,joint位于child link的原点,修改该参数可以调整连杆的位置,可用在调整实际模型与理论模型误差,但不建议大幅度修改,因为该参数影响连杆stl的位置,容易影响碰撞检测效果,具体如图所示。
xyz (可选: 默认为零向量)
代表x,y,zx,y,zx,y,z轴方向上的偏移,单位米。
rpy (可选: 默认为零向量)
代表绕着固定轴旋转的角度:roll绕着x轴,pitch绕着y轴,yaw绕着z轴,用弧度表示。
<parent> (必需)
parent link的名字是一个强制的属性。
link
parent link的名字,是这个link在机器人结构树中的名字。
<child> (必需)
child link的名字是一个强制的属性。
link
child link的名字,是这个link在机器人结构树中的名字。
<axis>(可选: 默认为(1,0,0))
joint的axis轴在joint的坐标系中。这是旋转轴(revolute joint),prismatic joint移动的轴,是planar joint的标准平面。这个轴在joint坐标系中被指定。修改该参数可以调整关节的旋转所绕着的轴,常用于调整旋转方向,若模型旋向与实际相反,只需乘-1即可。固定(fixed)和浮动(floating)类型的joint不需要用到这个元素。
xyz(必需)
代表轴向量的x,y,zx,y,zx,y,z分量,为标准化的向量。
<calibration> (可选)
joint的参考点,用来矫正joint的绝对位置。
rising (可选)
当joint正向运动时,参考点会触发一个上升沿。
falling (可选)
当joint正向运动时,参考点会触发一个下降沿。
<dynamics>(可选)
该元素用来指定joint的物理性能。它的值被用来描述joint的建模性能,尤其是在仿真的时候。
damping(可选,默认为0)
joint的阻尼值。(移动关节为N⋅sm\frac{N\cdot s}{m}mN⋅s,旋转关节为N⋅m⋅srad\frac{N\cdot m\cdot s}{rad}radN⋅m⋅s)
friction(可选,默认为0)
joint的摩擦力值(移动关节为NNN,旋转关节为N⋅mN \cdot mN⋅m)
<limit> (当关节为旋转或移动关节时为必需)
该元素为关节运动学约束。
lower (可选, 默认为0)
指定joint运动范围下界的属性(revolute joint的单位为弧度,prismatic joint的单位为米),连续型的joint忽略该属性。
upper (可选, 默认为0)
指定joint运动范围上界的属性(revolute joint的单位为弧度,prismatic joint的单位为米),连续型的joint忽略该属性。
effort (必需)
该属性指定了joint运行时的最大的力。详见安全限制.
velocity (required)
该属性指定了joint运行时的最大的速度。详见安全限制.
<mimic>(可选)
这个标签用于指定已定义的joint来模仿已存在的joint。这个joint的值可以用以下公式计算,此元素不在move_group中启用,使用会导致报错。
value = multiplier * other_joint_value + offset.
joint(可选)
需要模仿的joint的名字。
multiplier(可选)
指定上述公式中的乘数因子。
offset(可选)
指定上述公式中的偏移项。默认值为0(revolute joint的单位为弧度,prismatic joint的单位为米)。
<safety_controller> (可选)
该元素为安全控制限制,此元素下数据会读入到move_group中,但实际上时无效,move_group会跳过此处限制直接读取limit下的参数内容,同时设置该元素有几率会导致规划失败。
soft_lower_limit (可选, 默认为0)
该属性指定了joint安全控制边界的下界,是joint安全控制的起始限制点。这个值需要大于上述的limit中的lower值。更多细节详见安全限制。
soft_upper_limit (可选, 默认为0)
该属性指定了joint安全控制边界的上界,是joint安全控制的起始限制点。这个值需要小于上述的limit中的upper值。更多细节详见安全限制。
k_position(可选, 默认为0)
本属性用于说明位置和速度之间的关系。更多细节详见安全限制。
k_velocity(必需)
本属性用于说明力和速度之间的关系。更多细节详见安全限制。
示例:
1.2.3 STL-3D模型文件
STL文件是3D模型文件,机器人的URDF或仿真环境通常会引用这类文件,它们描述了机器人的三维模型。相比URDF文件简单定义的型状,STL文件可以定义复杂的模型,可以直接从solidworks或其他建模软件导出机器人装配模型,从而显示出更加精确的外形。
1.2.4 Rqt-数据流可视化工具
rqt是一个基于qt开发的可视化工具,拥有扩展性好、灵活易用、跨平台等特点,主要作用和RViz一致都是可视化,但是和RViz相比,rqt要高级一个层次。常见的命令有:
rqt_graph:显示通信架构
rqt_plot:绘制曲线
rqt_console:查看日志
1.2.5 TF-坐标转换
TF是ROS世界里一个基本的也很重要的概念,所谓TF(TransForm),就是坐标转换。在现实生活中,我们做出各种行为模式都可以在很短的时间里完成(比如拿起身边的物品),但是在机器人的世界里,则远远没有那么简单。观察下图,我们来分析一下抓取物品需要做到什么?而TF又起到什么样的作用?
当机器人的"眼睛"(如摄像头)获取一组关于物体的坐标方位数据后,但是相对于来说,这个坐标只是相对于安装的摄像头,并不直接适用于机械手爪执行,那么物体相对于头部和手臂之间的坐标转换,就是TF。
坐标变换包括位置和姿态两个方面的变换,ROS中的tf是一个可以让用户随时记录多个坐标系的软件包。tf保持缓存的树形结构中的坐标系之间的关系,并且允许用户可以在任何期望的时间点以及任何两个坐标系之间转换点、矢量等。
1.2.6 Gazebo-机器人仿真工具
Gazebo是一个机器人仿真工具、模拟器,也是一个独立的开源机器人仿真平台,可以进行机器人的运动学、动力学仿真,能够模拟机器人常用的传感器(如激光雷达、摄像头、IMU等),也可以加载自定义的环境和场景。 Gazebo提供高保真度的物理模拟,其提供一整套传感器模型,以及对用户和程序非常友好的交互方式。
通常一些不依赖于具体硬件的算法和场景都可以在Gazebo上仿真,例如图像识别、传感器数据融合处理、路径规划、SLAM等任务完全可以在Gazebo上仿真实现,大大减轻了对硬件的依赖。
1.2.7 Moveit!-运动导航
Moveit!是目前针对移动操作最先进的软件。它结合了运动规划、操纵、三维感知、运动学、控制和导航的最新进展;它提供了一个易于使用的平台,能开发先进的机器人应用程序,评估新的机器人设计和建筑集成的机器人产品,它广泛应用于工业,商业,研发和其他领域。MOVEit!是最广泛使用的开源软件的操作,并已被用于超过65个机器人。
MOVEit!的使用通过为用户提供接口来调用它,包括C++、Python、GUI三种接口。ROS中的move_group节点充当整合器,整合多个独立组件,提供ROS风格的Action和service。move_group通过ROS topic和action与机器人通讯,获取机器人的位置、节点等状态,获取数据再传递给机器人的控制器。为了便于理解Moveit!,请查看下面的系统架构图(见下图)。
Moveit!系统架构图
2. 创建Ros工作空间并编译
本节实验主要是学会为源代码创建新的Ros工作空间并能进行编译。需要提前准备好硬件、并配置好软件环境(如下表所示)。
2.1为源码创建空间并编译
第一步:打开终端,创建工作空间。
打开终端:同时按下Crtl+Alt+T ,接着按下Enter,等待终端打开;
新建ros工作空间:在终端中输入mkdir -p ~/myself_ws/src ,接着按下 Enter,(如下图所示)。
第二步:编译ros工作空间。
在上述的终端中输入:cd ~/myself_ws,接着按下Enter;
接着在该终端继续输入:catkin_make,接着按下Enter,等待创建的ros工作空间编译完成(大致需要15秒左右)。
编译完成后在终端中输入ls ,接着按下Enter,查看ros工作空间是否编译完成。如果编译完成,myself_ws下会出现三个文件夹分别为:(build编译空间Build Space)、devel(开发空间Development Space)、src(代码空间Source Space),否则检查输入命令是否有误。
第三步:拷贝源代码并编译URDF及moveit文件。
① 复制功能包:将文末资料中的ats_arm02和my_robot_arm两个功能包复制粘贴到PC机的/myself_ws/src 文件夹中。
② 编译: 打开终端,并在终端中输入:cd ~/myself_ws,之后按下Enter;接着在上述终端中输入:catkin_make,接着按下Enter,等待编译完成,该过程大致需要30秒。
2.2 设置环境变量
在计算机操作系统里面设置环境变量其实就是设置一定的文件路径,让计算机执行命令的时候方便找到。所以ROS中环境变量就是为了让计算机更方便的找到文件所在的路径来执行。这里介绍两种将ros工作空间添加到环境变量的方法,主要介绍其中常见的一种。
永久上传创建的ros工作空间至环境变量中:
详细的操作过程可以查看下方演示视频:
3. URDF与仿真
本节实验主要是把URDF模型在rviz中显示出来,并通过tf观察建立起坐标系;同时通过改变joint来观察机械臂朝向变化。
系统架构图(侧重看URDF、TF部分)
机械臂的URDF文件介绍
机械臂的实物图如下,我们将把其拆分为底座、转台、大臂、小臂四大部件,进行建模。
首先构建base_link作为底座的父坐标系,然后在base_link基础上,再构建转台、大臂和小臂的link. 最后不同的link之间通过joint来连接,它们分别是base_spin_joint、pin_first_swing_joint、first_second_swing_joint(生成的文件如下图所示)。
3.1 在tf中观察各Link建立的坐标
下面主要通过在Ros中加载URDF文件(详细见文末资料中的Source_Urdf_Moveit_File\ats_arm02\urdf),在Rviz中显示并调试。 在终端输入roslaunch ats_arm02 display.launch命令后,启动Rviz,设置3个参数(如下图所示)。
设置3个参数
各Link的tf图
下面是各个link的坐标系图。
base_link
first_swing_Link
second_swing_Link
spin_Link
3.2 查看TF树
输入rosrun rqt_tf_tree rqt_tf_tree,可以看到以下tf 树,观察link之间的关系:
3.3 改变joint来观察机械臂的朝向变化
移动joint_state_publisher进度条(见下图),可以临时改变joint的朝向。
为了便于查看各个joint的变化情况,每次都要先点 center,让其保持正中位置,然后调整其中一个joint来观察变化。下面是通过调整进度调条后,改变joint朝向后的状态图。
改变base_spin_joint值
改变spin_first_swing_joint值
改变first_second_swing_joint值
关于joint改变的朝向变化,具体可以查看下方演示视频:
4. 路径规划
move_group介绍
move_group是MoveIt的核心部分,可以综合机器人的各独立组件,为用户提供一系列需要的动作指令和服务。从moveit!架构图中我们可以看到,move_group类似于一个积分器,本身并不具备丰富的功能,主要做各功能包、插件的集成。它通过消息或服务的形式接收机器人上传的点云信息、joints的状态消息、还有机器人的tf tree,另外还需要ROS的参数服务器提供机器人的运动学参数,这些参数会在使用 setup assistant的过程中根据机器人的URDF模型文件,创建生成(SRDF和配置文件)。下图是以move_group为中心提供的服务:
move_group节点集成器
用户可以通过以下三种方式之一来访问move_group提供的操作和服务:
在C++中 --使用move_group_interface包,提供一个易于设置C++接口move_group;
在 Python中 --使用moveit_commander包;
通过 GUI -- 使用运动规划插件到Rviz(ROS可视化工具)。
下面我们将采用GUI方式来进行运动规划。在进行运动规划前,需要先利用MoveIt Setup Assistant图形化界面进行配置。
4.1生成Ros包
要使用MoveIt控制我们的机器人,需要配置一个ROS的软件包。MoveIt提供了一个图形化工具MoveIt Setup Assistant可以快捷的进行配置。MoveIt Setup Assistant是一个图形界面的工具,帮助配置MoveIt所需的ROS包。Setup Assistant会根据用户导入的机器人的urdf模型,生成SRDF( Semantic Robot Description Format)文件,从而生成一个MoveIt!的功能包,来完成机器人的互动、可视化和仿真。
下面我们将加载上面内容介绍的URDF模型,并完成相应的配置。
首先运行setup assistant,命令如下:roslaunch moveit_setup_assistant setup_assistant.launch
请打开终端(Crtl+Alt+T),启动后输入上面的命令,效果如下图所示:
第一步:加载URDF,请点击Create New MoveIt! Configuration Package。
点浏览后,找到文末资料中的Source_Urdf_Moveit_File\ats_arm02\urdf\ats_arm02.urdf文件即可。
第二步:Self-Collisions自碰撞
默认的碰撞免检矩阵生成器搜索机器人所有关节,这个碰撞免检矩阵是可以安全地关闭检查,从而减少行动规划的处理时间
在某些关节会关闭碰撞检查,比如总是碰撞,从不碰撞,在默认的位置碰撞,或在运动学链条上的相邻处
采样密度指定了多少个随机机器人位置来检查碰撞。更高的密度需要更多的计算时间,而较低的密度就要减少关闭的检查节点
默认值是10000个碰撞检查。碰撞检查是并行完成,以减少处理时间
由于只有三个joint,所以需要去掉第一个对勾。
第三步:virtual joint 虚拟关节。虚拟关节就是定义一个关节将机器人与世界链接起来,针对本实验中来说,假如放在桌子上,那么与桌子的连接就算一个虚拟关节,这个关节类型是fixed(固定)的;而base_link是与这个虚拟关节相连的,作为child_link。明白了原理后,请按照下面三幅图进行虚拟关节的设置。
图1
图2
第四步:planning group规划组。MoveIt通过定义规划组(planning group)来定义机械臂的各个部分(如手臂、末端执行器等)。简单的说就是定义某些关节为一个组合并起一个名字,下面我们将定义一个my_new_arm的组、选择运动学算法、选择规划组的方式。
请点击Planning Groups----Add Group按钮;
请输入组名,在运动学求解器上选择kdl_kinematics_plugin/KDLKinematicsPlugin ,创建规划组有四种方法:Add Joints、Add Links、Add Kin. Chain、Add Subgroups,这里选用Add Kin. Chain。具体见下图:
接下来选择机械臂的底座base_link为BaseLink的second_swing_Link作为TipLink。设置结果如下图:
这样就完成了一个规划组的配置(见下图)。
第五步:Robot Poses机器人姿态设置。下面我们将添加一个预设的姿态并命名为my_new_arm_pose。这里的预设姿态为0(这里是弧度制,0代表90度),当然大家也可以按需进行调整滑动条,进行姿态的预设。
下图为配置好的预设姿态结果。
第六步: End Effectors末端执行器。在大多数情况下,我们会给机械臂安装末端执行器,可以是夹持器,也可以是真空吸盘,甚至可以是3d打印机的挤出头。本实验中的URDF中暂且没有画执行器,就先不用设置了。
第七步:Passive Joints 被动关节。被动关节就是无法主动运动的关节,也可以理解为从动关节,这样moveit在规划运动时,这些关节是无法主动控制的。由于这里没有被动关节,所以这一步也可以跳过。
第八步:选择Ros Control(Ros 控制)。ROS Control是ROS官方提供的针对控制机器人的一套硬件驱动框架,针对不懂得运动执行器提供不同的驱动接口,再这之上又加入了一个硬件抽象层统一接入ROS,包含了一系列ROS包。这里我们可以通过ROS Control面板为关节添加simulation(模拟控制器),就可以通过MoveIt模拟机械臂的运动。
点击Auto Add FollowJointsTrajectory Controllers For Each Planning Group按钮后,会自动添加。生成的Controller如下图所示:
第九步:选择模拟器simulation。点击Generate URDF按钮后,会自动生成URDF文件。
第十步:选择3D Perception(3D感知) 。主要为添加传感器,这里暂且没有用到传感器,可以先跳过。
第十一步:选择Author Information(作者信息),方便后续发布用。
第十二步:选择Configuration Files(配置文件)。请先点击Browse选择一个文件夹,然后点Generate Package按钮即可。
针对项目,我们选择是文末资料中的Source_Urdf_Moveit_File\my_robot_arm文件夹,生成的文件如下图所示。
4.2 运动规划
对于运动规划这部分,我们可以采用随机规划路径、也可以手动规划路径。
4.2.1 随机规划路径
第一步:打开终端,输入命令 roslaunch my_robot_arm demo.launch,并等待RVIZ启动。
启动后,利用鼠标来调整视图。鼠标的操作方法,一般常用的就是“shift+鼠标左键”转换视角,“鼠标左键”平移视角,“滚轮”缩放大小。
第二步:接下来我们选择面板Planning---Goal State,选择<random valid>。
第三步:点Plan按钮,就可以看见效果(如下图所示)。
第四步:点Execute按钮,执行效果如下图所示。
如果想停止执行效果,请再次按下Plan按钮。
随机规划路径的操作方式,具体可参考下方演示视频:
4.2.2 手动规划路径
下面我们将分别对机械臂的转台、大臂、小臂分别进行手动规划,并执行查看其效果。
第一步:打开终端,输入命令 roslaunch my_robot_arm demo.launch,并等待RVIZ启动并调整好视图。
第二步:接下来我们选择面板Planning,选中Allow Approx IK solutions(如下图所示)。
第三步:手动设置转台的路径。
点击蓝色的圆圈,设置转动的目标位置(见下图),点Plan按钮规划路径;然后点面板上的Execute按钮后观察效果;再次点击Plan按钮后停止。
第四步:手动设置大臂的路径。
点击红色的箭头,设置转动的目标位置(见下图),点Plan按钮规划路径;然后点面板上的Execute按钮后观察效果;再次点击Plan按钮后停止。
第五步:手动设置小臂的路径。
点击绿色的圆圈,设置转动的目标位置(见下图),点Plan按钮规划路径;然后点面板上的Execute按钮后观察效果;再次点击Plan按钮后停止。
手动规划路径的操作方式,具体可参考下方演示视频:
5. 资料清单
序号 | 内容 |
1 | 程序源代码 |
【整体打包】-【R332】桌面级机械臂-4.仿真设计-资料附件.rar | 8.37MB | 下载9次 | 下载 |
应用设计
本节内容将基于机器视觉带着大家进行应用实训。机器视觉是人工智能正在快速发展的一个分支,简单说来机器视觉就是用机器代替人眼来做测量和判断。机器视觉系统是通过机器视觉产品(即图像摄取装置,分CMOS和CCD两种)将被摄取目标转换成图像信号,传送给专用的图像处理系统,得到被摄目标的形态信息,根据像素分布和亮度、颜色等信息,转变成数字化信号;图像系统对这些信号进行各种运算来抽取目标的特征,进而根据判别的结果来控制现场的设备动作。
机器视觉基础主要包含二维码识别、颜色识别等。
机器视觉系统最基本的特点就是提高生产的灵活性和自动化程度,在一些不适于人工作业的危险工作环境或者人工视觉难以满足要求的场合,常用机器视觉来替代人工视觉。同时在大批量重复性工业生产过程中,用机器视觉检测方法可以大大提高生产的效率和自动化程度。下面我们将结合机器视觉基础,基于开源的机器人平台,进行桌面机械臂搬运、颜色追踪等应用开发。
1. 视觉二维码识别
二维码的应用渗透到生活的方方面面,如手机购物、微信登录等,二维码常见的分类有矩阵式、堆叠式/行排式。具有代表性的矩阵式二维条码有Code One、Maxi Code、QR Code、 Data Matrix等;具有代表性的行排式二维条码有Code 16K、Code 49、PDF417等。
二维码的使用分为:生成二维码、识别已生成的二维码。这里我们主要是识别已生成的二维码(矩阵式二维码)。
设计思路
本实验主要是基于矩阵式二维码进行识别。QR码(Quick Response Code)是在正方形二维矩阵内通过黑白标识编码二进制位从而编码数据。
实现思路
利用摄像头采集二维码信息,利用zbar库识别后把结果显示在屏幕上。
器材准备
生成QR Code码(可以用在线编辑器生成)、摄像头。
下面是为本实验准备的二维码:(当然大家也可以直接打开微信二维码进行识别)
操作步骤
第一步:连接电路。将摄像头与控制盒进行连接。
第二步:下载文末资料中的参考程序color_experiment_ws\src\qr_detection\scripts\QrCode_Detection.py:
#!/usr/bin/env python #!coding=utf-8
#write by leo at 2018.04.26 #function: #1, Get live_video from the webcam. #2, With ROS publish Image_info (to yolo and so on). #3, Convert directly, don't need to save pic at local. # import the necessary packages import simple_barcode_detection import zbar from PIL import Image import rospy from sensor_msgs.msg import Image as lll import cv2 import numpy as np from cv_bridge import CvBridge, CvBridgeError import sys import time
# create a reader scanner = zbar.ImageScanner() # configure the reader scanner.parse_config('enable') font=cv2.FONT_HERSHEY_SIMPLEX camera=cv2.VideoCapture(0)
def webcamImagePub(): # init ros_node rospy.init_node('webcam_puber', anonymous=True)
img_pub = rospy.Publisher('webcam/image_raw', lll, queue_size=2) rate = rospy.Rate(20) # 5hz
scaling_factor = 0.5
bridge = CvBridge()
if not camera.isOpened(): sys.stdout.write("Webcam is not available!") return -1
count = 0 # loop until press 'esc' or 'q' while not rospy.is_shutdown(): # get a frame and show (ret, frame) = camera.read() box = simple_barcode_detection.detect(frame) if np.all(box != None): min=np.min(box,axis=0) max=np.max(box,axis=0) roi=frame[min[1]-10:max[1]+10,min[0]-10:max[0]+10] #print roi.shape roi=cv2.cvtColor(roi,cv2.COLOR_BGR2RGB) pil= Image.fromarray(frame).convert('L') width, height = pil.size raw = pil.tobytes()
zarimage = zbar.Image(width, height, 'Y800', raw)
scanner.scan(zarimage)
for symbol in zarimage:
print 'decoded', symbol.type, 'symbol', '"%s"' %symbol.data cv2.drawContours(frame, [box], -1, (0, 255, 0), 2) cv2.putText(frame,symbol.data,(20,100),font,1,(0,255,0),4)
# resize the frame if ret: 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) # print '** start ***' rate.sleep()
if __name__ == '__main__': try: webcamImagePub() except rospy.ROSInterruptException: pass # except IndexError: #pass # except VIDEOIOERROR: #pass # except Unabletostopthestream: #pass finally: webcamImagePub() |
#!/usr/bin/env python #!coding=utf-8 import rospy from sensor_msgs.msg import Image import cv2 from cv_bridge import CvBridge import numpy as np
#red,green,blue[h,s,v] lower_blue=np.array([50,143,146]) upper_blue=np.array([124,255,255]) lower_red=np.array([0,200,55]) upper_red=np.array([10,255,130]) lower_green=np.array([40,43,46]) upper_green=np.array([77,255,255])
Video = cv2.VideoCapture(0) ret = Video.set(3, 640) # 设置帧宽 ret = Video.set(4, 480) # 设置帧高 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 talker(): pub = rospy.Publisher('/tutorial/image', Image, queue_size=1) rospy.init_node('talker', anonymous=True) rate = rospy.Rate(30) bridge = CvBridge() #Video = cv2.VideoCapture(1) while not rospy.is_shutdown(): if Video.isOpened() is True: 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) 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) # 显示圆心位置
#cv2.drawContours(img,contours,-1,(0,0,255),3) cv2.waitKey(3) pub.publish(bridge.cv2_to_imgmsg(frame, "bgr8")) rate.sleep()
if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass |
#!/usr/bin/env python #!coding=utf-8
#write by leo at 2018.04.26 #function: #1, Get live_video from the webcam. #2, With ROS publish Image_info (to yolo and so on). #3, Convert directly, don't need to save pic at local.
import rospy from sensor_msgs.msg import Image import cv2 import numpy as np from cv_bridge import CvBridge, CvBridgeError import sys import time cap = cv2.VideoCapture(0)
#lower_blue=np.array([50,143,146]) #upper_blue=np.array([124,255,255])
lower_blue=np.array([40,143,146]) upper_blue=np.array([124,255,255])
lower_red=np.array([0,200,55]) upper_red=np.array([10,255,130]) lower_green=np.array([40,43,46]) upper_green=np.array([77,255,255]) font = cv2.FONT_HERSHEY_SIMPLEX
def areaCal(contour):
area = 0 for i in range(len(contour)): area += cv2.contourArea(contour[i])
return area
def webcamImagePub(): # init ros_node rospy.init_node('webcam_puber', anonymous=True) # queue_size should be small in order to make it 'real_time' # or the node will pub the past_frame img_pub = rospy.Publisher('webcam/image_raw', Image, queue_size=2) rate = rospy.Rate(20) # 5hz
# define picture to_down' coefficient of ratio scaling_factor = 0.5 # the 'CVBridge' is a python_class, must have a instance. # That means "cv2_to_imgmsg() must be called with CvBridge instance" bridge = CvBridge()
if not cap.isOpened(): sys.stdout.write("Webcam is not available!") return -1
count = 0 # loop until press 'esc' or 'q' while not rospy.is_shutdown(): # get a frame and show ret, frame = cap.read() # cv2.imshow('Capture', frame) # change to hsv model hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) #cv2.imshow("imageHSV",hsv) # get mask mask = cv2.inRange(hsv, lower_blue, upper_blue) # cv2.imshow('Mask', mask) # detect blue res = cv2.bitwise_and(frame, frame, mask=mask) # cv2.imshow('Result', res) # cv2.imshow('SOURCE', frame)
image,contours,hierarchv = cv2.findContours(mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) blue_area=areaCal(contours)
hsvs = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) masks = cv2.inRange(hsvs, lower_red, upper_red) ress = cv2.bitwise_and(frame, frame, mask=masks) images,contourss,hierarchvs = cv2.findContours(masks,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) red_area=areaCal(contourss)
hsvss = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) maskss = cv2.inRange(hsvss, lower_green, upper_green) resss = cv2.bitwise_and(frame, frame, mask=maskss) imagess,contoursss,hierarchvss = cv2.findContours(maskss,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) green_area=areaCal(contoursss) print "blue=",blue_area,"red=",red_area,"green=",green_area,"the color is blue" if(areaCal(contours)>300): #print("the color is blue") #print "blue=",blue_area,"red=",red_area,"green=",green_area,"the color is blue" text = 'the color is blue' cv2.putText(frame, text, (10, 30), font, 1, (255, 0, 0), 2, cv2.LINE_AA, 0) else : if(areaCal(contourss)>3500): #print ("Thered",areaCal(contours)) #print "blue=",blue_area,"red=",red_area,"green=",green_area,"the color is blue red" text = 'the color is red' cv2.putText(frame, text, (10, 60), font, 1, (0, 0, 255), 2, cv2.LINE_AA, 0) else: if(areaCal(contoursss)>3500): #print "blue=",blue_area,"red=",red_area,"green=",green_area,"the color is blue green" text = 'the color is green' cv2.putText(frame, text, (10, 90), font, 1, (0, 255, 0), 2, cv2.LINE_AA, 0) else: qwer=0
# resize the frame if ret: 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()
if __name__ == '__main__': try: webcamImagePub() except rospy.ROSInterruptException: pass # except IndexError: # pass # except VIDEOIOERROR: # pass # except Unabletostopthestream: # pass finally: webcamImagePub() |
/* _______________________________________________________________________________________________________________________________________________________ 【实验功能】 ros中键盘控制机械臂运动。
【实验接线】 按照实验教程接线即可。
【实验思路】 上位机(树莓派)通过串口发布按键信息,下位机(Arduino)接收到信息后,进行简单数据处理,最后将处理后的数据 转换为机械臂相应的动作。
【实验操作】 按照实验教程接线。将该程序下载到arduino开发板,接着启动上位机(树莓派)程序。完成后,按下键盘不同的按键,观察 机械臂动作。键盘指令包含: u:控制转台左转 o:控制转台右转 j:控制大臂前伸 l:控制转台后缩 m:控制小臂上抬 >:控制小臂下降
create :2020.7.17 By Boris.yuan _________________________________________________________________________________________________________________________________________ */
//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 <Servo.h> #include <ros.h> #include <ros/time.h> #include <geometry_msgs/Vector3.h> #define mySerial Serial1 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);
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 sudu = 0.87;
void setup() { delay(1000); mySerial.begin(115200); Serial.begin(57600); Bus_servo_angle_init(); delay(1500); nh.initNode(); nh.subscribe(vel_cmd_sub); nh.advertise(pose_feedback_pub); nh.advertise(vel_feedback_pub); position_read_time = millis(); message_pub_time = millis(); }
void loop() { 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(); } |
/* ----------------------------------------------------------------------------------------------------------------- 实验功能:【实现颜色分拣。】
实验思路:【上位机(即树莓派)检测到颜色(红色、蓝色),通过串口将发送不同颜色物体的字符串。下位机 Arduino Mega2560接收到字符串后,执行相应的动作指令。本实验中,上位机检测到红色后, 通过串口发送“reds”,当下位机Arduino接收到“reds”后,将红色物体向左边搬运。同理, 上位机检测到蓝色后,串口发送“blues”,下位机接收到“blues”后,将物体搬运到机械臂的右侧。 】
实验操作:【将该程序下载到Arduino开发板后,按照教程启动上位机程序,将红色或者蓝色物体置于机械臂指定 位置后,观察机械臂抓取不同颜色物体的动作是否与教程视频中相吻合。】
实验接线(主要接线): 机械臂通信口-------------(机械臂)电控箱 机械爪(舵机)---------------(执行器)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */ #include <Servo.h> #include <stdio.h> #include <string.h> #define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 57600 String receive_string = ""; int catch_red_numbers=0; int catch_blue_numbers=0; unsigned long old_time = millis(); unsigned long new_time = millis();
void setup() { // put your setup code here, to run once: delay(1100); Serial.begin(SerialBaudrate); SERIAL_PRINTLN.begin(CTL_BAUDRATE); Bus_servo_angle_init();delay(2000); // Arm_Catch_Red_Object();delay(1000); // Arm_Catch_Blue_Object();delay(1000); }
void loop() { // put your main code here, to run repeatedly: while(Serial.available()>0) { new_time = millis(); receive_string=Serial.readStringUntil('\n'); if(receive_string=="reds") { catch_red_numbers++; new_time = millis(); // if(catch_red_numbers == 1) // { // //old_time = millis(); // receive_string=""; // //Arm_Catch_Red_Object(); // } // if(catch_red_numbers == 2) // { // //old_time = millis(); // receive_string=""; // //Arm_Catch_Red_Object(); // } if(catch_red_numbers == 1) { old_time = millis();receive_string=""; Arm_Catch_Red_Object(); }
if(new_time-old_time>25000) { catch_red_numbers=0; } }
if(receive_string=="blues") { catch_blue_numbers++; new_time = millis(); // if(catch_blue_numbers == 3) // { // //old_time = millis(); // receive_string=""; // //Arm_Catch_Blue_Object(); // } // if(catch_blue_numbers == 3) // { // //old_time = millis(); // receive_string=""; // //Arm_Catch_Blue_Object(); // } if(catch_blue_numbers == 1) { old_time = millis();receive_string=""; Arm_Catch_Blue_Object(); } if(new_time-old_time>26000) { catch_blue_numbers=0; } } receive_string = ""; } } |
/* ----------------------------------------------------------------------------------------------------------------- 实验功能:【实现颜色(红色)追踪】
实验思路:【上位机实现颜色检测(红色物体),检测到红色物体后,提取红色物体的中心点坐标想(x,y),之后通过 检测红色物体的面积大小进而判断红色物体距离摄像头的远近(本实验通过比较检测红色物体面积的当前值 与设定红色物体面积的标定值进行比较,进而判断红色物体距离摄像头的远近),判断结束后,会将红色 物体的中心点坐标(即(x,y))及红色物体距离摄像头远近的数值(本实验使用的是字符串)通过串口发送 给下位机Arduino Mega2560,下位机接收到数据后,机械臂按照该数据进行相应的动作。】
实验操作:【将该程序下载到Arduino开发板,之后按照教程启动树莓派例程,观察机械臂是否会跟随红色物体移动。】
实验接线(主要接线): 机械臂通信口-------------(机械臂)电控箱 机械爪(舵机)---------------(执行器)电控箱 其他线路按照教材图片连接。 Created 2020.7.16 By:Boris.yuan ----------------------------------------------------------------------------------------------------------------- */
#include <Servo.h> #include <stdio.h> #include <string.h>
#define CTL_BAUDRATE 115200 //总线舵机波特率 #define SERIAL_PRINTLN Serial1 #define SerialBaudrate 57600 #define RGB_LED_NUMBERS 3 #define Bus_servo_Angle_inits 1500
String receive_string = ""; enum{RED=1,GREEN,BLUE,NONE_COLOR}; int catch_red_numbers=0; int catch_blue_numbers=0; unsigned long old_time = millis(); unsigned long new_time = millis();
int value_move[3]={1500,1500,1500}; int last_value[3]={0,0,0};
void setup() { // put your setup code here, to run once: delay(1100); Serial.begin(SerialBaudrate); SERIAL_PRINTLN.begin(CTL_BAUDRATE); Bus_servo_angle_init();delay(2000); }
void loop() {
while(Serial.available()>0) { new_time = millis(); receive_string=Serial.readStringUntil('\n'); if( (receive_string.length())<3 && (receive_string.length())>8 ) { 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=""; SpacePosition[0] = receive_string.indexOf('-'); for(int j=0;j<SpacePosition[0];j++) { numbers_left++; if(receive_string[j] == 'c'){receive_string[j]='0';} x_data+=receive_string[j]; }
if(receive_string == "back") { value_move[1] -=3; } else if(receive_string == "forward") { value_move[1] +=3; } for(int k=SpacePosition[0]+1,m=0;k<receive_string.length();k++,m++) { numbers_right++; if(receive_string[k] == 'c'){receive_string[k]='0';} if(numbers_right>=4) { ; } else { y_data+=receive_string[k]; } } Data_one=x_data.toInt(); Data_two=y_data.toInt(); if(Data_one !=0 ) { last_value[0] = Data_one; } if(Data_one ==0) { Data_one = last_value[0]; }
if(Data_two !=0 ) { last_value[2] = Data_two; } if(Data_two ==0) { Data_two = last_value[2]; } if( Data_one<=280 ){value_move[0] +=2;} if( Data_one>=368 ){value_move[0] -=2;} if( Data_two<=230-50 ){value_move[2] +=2;} if( Data_two>=250+50 ){value_move[2] -=2;}
ArmServoTos(value_move[0],value_move[1],value_move[2],50);//delay(100); } receive_string = ""; } } |
第三步:运行程序并查看效果。
打开终端,输入roslaunch qr_detection qr_detection_experiment.launch命令(见下图),等待程序的运行启动界面。
界面启动后,添加Image并设置其参数,设置成功后出现以下三幅图像画面。
图1
图2
图3
把QR code类型的二维码放置在摄像头下,稍后就会在界面上显示结果(如下图所示)。
2. 形状识别—识别圆形
实现思路
利用摄像头采集图片信息,识别圆形后显示出圆的中心坐标。
器材准备
主要用到:摄像头、红色和绿色两种圆形图(如下图所示)。
操作步骤
第一步:连接电路,将摄像头与主机进行连接。(温馨提示:做实验时,大家可以灵活的把摄像头放置在圆形图片上方,以便于采集图形信息)
第二步:下载文末资料中的参考程序color_experiment_ws\src\shape_detection\scripts\shape_detection_victory.py:
第三步:运行程序并查看效果。
打开终端,输入roslaunch shape_detection shape_detection_experiment.launch 命令(见下图), 等待程序的运行启动界面。
界面启动后,添加Image并设置其参数,设置成功后出现以下三幅图像画面。
图1
图2
图3
放置待识别的圆形图(请把物品放置在摄像头可以采集到的区域),然后就可以在界面上看到识别结果。下图是分别识别红色圆形、蓝色圆形,并显示识别出圆心的中心坐标x、y的值。
3. 颜色识别(红绿蓝)
实现思路
摄像头采集红绿蓝的物品颜色后,利用开源视觉库进行识别,把结果显示在屏幕上。
颜色识别算法
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色彩范围
操作步骤
第一步:连接电路。将摄像头与主机进行连接。
第二步:下载文末资料中的参考程序color_experiment_ws\src\color_detection\scripts\color_test_one.py:
第三步:运行程序并查看效果。
打开终端,输入roslaunch color_detection color_detectioning.launch命令(见下图), 等待程序的运行启动界面。
界面启动后,添加Image并设置其参数,设置成功后出现以下三幅图像画面。
图1
图2
图3
放置物品(请把物品放置在摄像头可以采集到的区域),然后摄像头开始识别颜色并在界面上显示识别结果。
下面以蓝色物品为例,当摄像头识别到蓝色物品后,在界面上显示结果(the color is blue)。
蓝色物块检测环境
检测后返回的结果
大家也可以试着去放置红色、绿色物品(识别结果见下面两幅图)。
红色物块检测环境
检测后返回的结果
检测后返回的结果
绿色物块检测环境
4. 键盘控制运动
实现思路
按下键盘上指定的键,实现底盘、大臂、小臂朝不同方向运动。
操作步骤
第一步:下载文末资料中的参考程序color_experiment_ws\src\visual_recognition\arduino_program\keyboard_control_arm\keyboard_control_arm.ino:
第二步:打开终端,输入roslaunch visual_recognition keyboard_control_arm.launch 命令(见下图), 等待程序的运行启动界面。
终端
第三步:尝试按下【u、o、j、l、m、.】观察运动,包括底盘左转、右转、大臂向前伸、大臂向后伸、小臂下降、小臂上升。(注意:字母均为小写;按键盘时,稍微有点间隔,给转动留时间)
键盘上字母 | 按下字母后对应操作 |
u | 底盘左转 |
o | 底盘右转 |
j | 机械大臂向前伸 |
l | 机械大臂向后伸 |
m | 小臂下降 |
. | 小臂上升 |
键盘命令及含义表
键盘控制机械臂运动实验的具体操作方法,可参考下方演示视频:
5. 颜色识别与搬运
实现思路
摄像头识别不同颜色的物块后,机械爪开始抓取把其搬运到不同的区域。
操作步骤
第一步:连接电路(见下图)。
第二步:下载文末资料中的参考程序color_experiment_ws\src\color_sorting\arduino_program\arm_driver\arm_driver.ino:
第三步:运行程序并查看效果。
打开终端(Alt+ctrl+T),输入roslaunch color_sorting open_camera_and_get_ImageTopic.launch 命令即可(见下图)。
打开第二个终端(shift+ctrl+T),输入rviz命令,等待rviz界面启动。
界面启动后,添加Image并设置其参数,设置成功后出现以下三幅图像画面。
图1
图2
图3
放置物品(请把物品放置在摄像头可以采集到的区域),然后开始识别并进行搬运。下面以红色物品为例,当摄像头识别到红色物品后,机械爪把此物品搬运到一侧;当摄像头识别到蓝色物品时,把蓝色物品搬运到另一侧。这样就完成了按颜色识别物品并搬运的过程。
提示:颜色识别搬运时,待识别的物品周边尽量不要有过多的杂色,否则会误识别颜色。
机械臂颜色识别与搬运实验的具体操作方法,可参考下方演示视频:
6. 颜色识别与追踪
实现思路
摄像头采集到物品颜色后,随着该颜色物品的运动而缓慢移动,实现追踪效果。
器材准备
本实验中需要用到的器材如下图所示,其中键盘、鼠标、红色物品可自行准备。
操作步骤
第一步:下载文末资料中的参考程序color_experiment_ws\src\color_tracking\arduino_program\color_tracking_Red_Things\color_tracking_Red_Things.ino:
第二步:打开终端(Alt+ctrl+T),输入roslaunch color_tracking color_tracking_sth.launch 命令即可,见下图。
打开第二个终端(shift+ctrl+T),输入rviz命令,等待rviz界面启动。
界面启动后,添加Image并设置其参数,设置成功后出现以下三幅图像画面。
图1
图2
图3
第三步:放置物品(请把物品放置在摄像头可以采集到的区域),开始追踪。下面以红色物品为例,当摄像头识别到红色物品后,随着红色物品的运动缓慢移动,实现追踪效果。(注意:受硬件的影响,物品的移动速度要稍微慢一些)。
图1
图2 上位机采集的图片信息及画轮廓
7. 资料清单
序号 | 内容 |
1 | 程序源代码 |
【整体打包】-【R332】桌面级机械臂-5.应用设计-资料附件.rar | 7.67MB | 下载9次 | 下载 |
|