机械臂
- 一 总体思路
- 1 功能原理
- 2 硬件设备
- 3 Arduino库
- 二 代码设计
- 代码逻辑
- 0 全局参数
- 1 颜色识别
- 2 机械臂抓取
- 3 机械臂复位
- 三 展示效果
- 1 颜色识别机械臂
- 2 简易机械臂
一 总体思路
1 功能原理
- 实现功能: 识别出不同颜色的物体,机械臂夹取后并放入不同位置容器中
- 原理分析:颜色传感器识别不同颜色; 物品摆放位置固定;对每个舵机编写固定夹取路径,不同颜色转动角度不同
2 硬件设备
- TCS34725颜色传感器
- MG995舵机
- 机械臂外壳
- 开发板:UNO板,使用Arduino编码
3 Arduino库
• <Servo.h>:控制舵机,读写舵机角度
• <Adafruit_TCS34725.h>:识别颜色
本项目为基本项目,代码不涉及复杂数据结构与算法,只需要c++基本功扎实,逻辑清晰,适合Arduino小白练手。
二 代码设计
代码逻辑
项目重点划分为三部分:颜色识别,机械臂抓取,机械臂复位。
(1)使用TCS34725能够返回检测色、绿色、蓝色 (RGB) 清晰光感测值,根据RGB各个颜色占据比例区间,能够判断不同颜色;
(2)本项目待夹取物品置于在颜色传感器位置上,故所有颜色机械臂夹取路径一致,可以简单的使用循环语句设置路径,唯有转动角度不同,设为形参即可;
(3)抓取取路径只包含从开始夹取物品到松开机械爪物品落入对应容器,最后只需要单独设置机械臂的复位函数,方便调用。
0 全局参数
#include <Servo.h>
#include "Wire.h"
#include <Adafruit_TCS34725.h>
int pos1 = 0; //各舵机角度初始角度值
int pos2 = 90;
int pos3 = 90;
int pos4 = 90;
int pos5 = 0;
int speed = 20;//机械手速度,全局调控
int step = 1;//模拟舵机转动度数
//创建舵机对象
Servo myservo1;//base:基座舵机
Servo myservo2;//big_arm:大臂舵机
Servo myservo3;//fore_arm:小臂舵机
Servo myservo4;//top:顶部舵机
Servo myservo5;//paw:爪子舵机
//初始化颜色传感器对象tcs,相关参数参阅: https://learn.adafruit.com/adafruit-color-sensors/program-it
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_1X);
//在setup中初始设定舵机引脚,舵机初始角度,波特率,颜色判定等等....,略
1 颜色识别
/*
循环执行颜色判定,对于不同的颜色,串口输出不同的提示
并在检测到不同颜色时,调用对应的方法实现不同的动作。
*/
void loop() {
// 传感器返回 R、G、B 的值
uint16_t clearcol, red, green, blue;
float average, r, g, b;
delay(100); //颜色测量需要大约50ms
tcs.getRawData(&red, &green, &blue, &clearcol);
// 我写了5种颜色,分别是红色 棕色 蓝色 绿色 和黄色
// 求 RGB 的平均值
average = (red + green + blue) / 3;
r = red / average;
g = green / average;
b = blue / average;
//串口输出
//R G B的平均值应该在0.5-1.5之间,如果检测到红色,则R应高于1.0,G B在0.5-1.0之间,
Serial.print("\tClear:");
Serial.print(clearcol);
Serial.print("\tRed:");
Serial.print(r);
Serial.print("\tGreen:");
Serial.print(g);
Serial.print("\tBlue:");
Serial.print(b);
/*
通过上面的串口输出,我们可以把颜色放在颜色传感器去检测,
然后确定RGB的取值范围,就可以确定不同颜色的值,对应不
同的舵机角度了
*/
if ((r > 1.4) && (g < 0.9) && (b < 0.9)) {
Serial.print("\t红色");
catchs(60);
} else if ((r < 0.95) && (g > 1.4) && (b < 0.9)) {
Serial.print("\t绿色");
//green();
} else if ((r < 0.8) && (g < 1.2) && (b > 1.2)) {
Serial.print("\t蓝色");
catchs(120);
}
else if ((r > 1.15) && (g > 1.15) && (b < 0.7)) {
Serial.print("\t黄色");
catchs(90);
} else if ((r < 0.95) && (g > 1.1) && (b < 0.9)) {
Serial.print("\t棕色");
//brown();
}
// 当颜色符合上述的颜色
else {
Serial.print("\t未检测到红-棕-绿-蓝-黄");
// myservo.write(nonePos);
}
Serial.println("");
delay(100);
}
2 机械臂抓取
注意:编写舵机代码之前,一定先测试好每个舵机角度范围和顺逆时针方向,搞清楚后才能开始编码,设置舵机初始写入角度。
机械臂的完整抓取过程可提取为:
下放大小顶臂接近物体—>夹取—>抬高大臂—>旋转—>下方大臂—>松爪—>复位。
其中不同颜色对应动作的不同之处仅仅在于底座舵机旋转角度,故可设置一个通用的夹取函数,内置形参表示旋转角度即可。多种颜色一个函数全部实现,精简明了。
拓展:可改进为联动执行,需要运动学逆向受力分析,建立数学模型求解三元一次方程组。这样就可以根据末端舵机的状态,去计算出其他舵机需要达到的角度,实现多个舵机联动的目的,效果更加丝滑流畅。
/*
机械臂抓取函数,spin为旋转度数,不同颜色,设置不同度数
多种颜色的抓取动作,只有旋转角度不同,一个函数全部搞定
注:参数未详细调整,仅做参考。另外不同舵机转动范围可能不同,必须先验证自己舵机的角度范围与转动方向
*/
void catchs(int spin) {
//1 下放大臂小臂和顶部舵机,实践中发现:小臂,尖端,下方,然后打开爪子,最后下方大臂更合适。
for (pos2 = 90; pos2 <= 120; pos2 += 1) {
myservo2.write(pos2);
delay(speed);
}
for (pos3 = 90; pos3 >= 60; pos3 -= 1) {
myservo3.write(pos3);
delay(speed);
}
for (pos4 = 90; pos4 >= 60; pos4 -= 1) {
myservo4.write(pos4);
delay(speed);
}
//2 夹取物品
// 3 仅抬起大臂,调转方向
for (pos2 = 120; pos2 >= 90; pos2 -= 1) {
myservo2.write(pos2);
delay(speed);
}
for(int i=0;i<spin;i++){ //转动spin度,spin不同颜色可以手动设置转动区间
myservo1.write(pos1++);
delay(speed);
}
//4 大臂下放
for (pos2 = 90; pos2 <= 120; pos2 += 1) {
myservo2.write(pos2);
delay(speed);
}
//5 松爪
init_pos();//机械臂流畅复位
}
3 机械臂复位
用于机械臂一套夹取动作结束后,执行复位。不需要为每一个动作单独编写复位代码,使用servo库中read()
方法能够读取舵机角度,从而实现舵机通用角度的复位效果。
根据自己的舵机角度范围设定,一定要先测试出每一个舵机的角度范围和转向,根据实际情况设置复位,范围设置异常可能会对舵机造成严重损伤,甚至报废。
/*
读取舵机角度参数,控制舵机丝滑流畅的回到初始位置
如果不想丝滑,直接4行myservo.write(x),各舵机咻咻咻咻的就回去了
*/
void init_pos(){
pos1=myservo1.read(); //读取当前各舵机角度值
pos2=myservo2.read(); //注意:read函数只能读取对应舵机上一次write的数值
pos3=myservo3.read(); //不支持及时读取,因为本项目使用的for循环不停的write角度,故可读取到上一次的角度
pos4=myservo4.read();
//pos5=myservo5.read();//爪子初始应该是打开状态;
for(int i=pos1;i>=0;i-=step){ //1号舵机,归位0,逆时针移动,范围[0°,180°],0在右
myservo1.write(i);
delay(speed);
}
//myservo1.write(0);//设置step时,杜绝误差
for(int i=pos2;i>=90;i-=step){ //2号舵机,归位90,顺时针移动,范围[90°,180°],0°在左
myservo2.write(i);
delay(speed);
}
if(pos2<90){ //处理2号机在[0°,90°]时,2号机为大臂比较重要,所有情况全部判断
for(int i=pos2;i<=90;i+=step){
myservo2.write(i);
delay(speed);
}
}
//myservo2.write(90);//如果设置step时,杜绝误差
for(int i=pos3;i<=90;i+=step){ //3号舵机,归位90°,逆时针移动,范围[0°,90°],0°在右
myservo3.write(i);
delay(speed);
}
if(pos3>90){
for(int i=pos3;i>=90;i-=step){ //处理3号机在[90°,180°]时
myservo3.write(i);
delay(speed);
}
}
//myservo3.write(90);//如果设置step时,杜绝误差
for(int i=pos4;i<=90;i+=step){ //4号舵机,归位90°,逆时针移动,范围[0°,90°],0°在右
myservo4.write(i);
delay(speed);
} //处理4号机在[90°,180°]时
if(pos4>90){
for(int i=pos4;i>=90;i-=step){ //处理3号机在[90°,180°]时
myservo4.write(i);
delay(speed);
}
}
//myservo4.write(90);//如果设置step时,杜绝误差
//更新pos,可以将本函数中所有的i换成对应pos变量,此处就不用更新,但我觉得不如写i码量更短,哈哈哈
pos1=0;
pos2=90;
pos3=90;
pos4=90;
}
三 展示效果