基于Arduino的捡乒乓球小车
先上演示视频:
这个小车是基于pixy视觉识别的模块来完成动作的;
根据反馈的物体面积判断距离物体的远近,反馈的横纵坐标来判断物体的方向。
拾取流程:
小车自转(寻找物体)------识别到物体,根据反馈的横纵坐标走到小球跟前------下爪抓球-----自转(寻找储存区)-----根据反馈的横纵坐标去到储存区-----放球-----again
/*****************************************组成部分*********************************************/
pixy视觉识别+全向底盘+Arduino+手爪
[size=10.5000pt]
小车外型
小车外型
pixy模块
pixy模块
此处附上pixy模块使用方法的连接和个人用的固件:
pixy模块使用方法:http://www.makerlab.me/guides/6/articles/34
最新固件:https://pan.baidu.com/s/1dfDxQtDd0mzxSvZYWn7AfQ
提取码:3247
/*****************************************组成部分*********************************************/
/****************************************各个模块测试视频************************************************/
pixy模块:
优点:比较方便,圈选你要识别的物体后它会自动进行跟踪,并反馈回物体坐标,面积信息
缺点:受光线干扰较大,选中物体的方框有点不稳定,会时常跳动
pixy模块测试视频:
全向底盘:
用的是步进电机和cnc的步进电机驱动,跑的是开环
优点:走的非常平稳,同样是开环,步进电机会比直流电机稳很多
缺点:速度太慢了。。。TAT 用的还是雕刻机CNC的驱动,楼主太菜了,莫得办法
cnc 驱动
步进电机
舵机:
这个要求不大,因为乒乓球不是很重
/****************************************各个模块测试视频************************************************/
/*******************************************遇到的问题****************************************************/
小车走到小球的面前停不下来,会出现小幅度的抖动
原因上面也说了,因为pixy这个模块受光线影响较大
所以当车到了小球的面前时,我直接跑个for循环,让手爪下去,此举可以避免底盘的抖动。
/*******************************************遇到的问题****************************************************/
代码:
有两个UNO
一个用于控制舵机和接收pixy反馈的信息 (在这称之上位机)
一个是用于控制底盘(接受到pixy反馈的信息后走至小球跟前,在这称之下位机)
上位机:
[mw_shl_code=arduino,true]#include <SPI.h>
#include <Pixy2.h>
#include<Servo.h>
#include<Wire.h>
Pixy2 pixy;
Servo taiservo;
Servo catchservo;
int signature = 0;
int x = 0; //positon x axis
int y = 0; //position y axis
unsigned int width = 0; //object's width
unsigned int height = 0; //object's height
unsigned int area = 0;
unsigned int newarea = 0;
int Xmin =130; //min x position
int Xmax =180; //max x position
int maxArea = 0;
int minArea = 0;
int Cmin=115;
int Cmax=195;
//volatile int count=0;
int c;
int n=0;
int jet=1;
int i = 0;
static boolean zhua=false;
static boolean chucun=false;
static boolean zhaoqiu=true;
int STEP=1;
void setup()
{
taiservo.attach(9);
catchservo.attach(10);
taiservo.write(65);
catchservo.write(25);
Serial.begin(9600);
pixy.init();
Wire.begin(4);
Wire.onRequest(requestEvent);
// c=6;
// requestEvent();
// delay(5);
}
void loop()
{
//scan(); // rescan();
/*********************************步骤1:找到目标球*******************************************************************/
if(STEP==1)
{
scan();
if(signature ==1&&0.85*width<height)
{
//delay(1000);
//volatile int count=0;
if(n<3){n++;}
if(n==3){
newarea = width * height;
if (x < Xmin&&0.85*width<height) //左平移
{
c=1;
requestEvent();
delay(5);
}
else if (x > Xmax&&0.85*width<height) //右平移
{
c=2;
requestEvent();
delay(5);
}
else if(Xmin<x<Xmax&&newarea <2450&&0.85*width<height) //前进
{
c=3;
requestEvent();
delay(5);
}
else if(Xmin<x<Xmax&&newarea >2650&&0.85*width<height) //后退
{
c=4;
requestEvent();
delay(5);
}
//else stop
else if(Xmin<x<Xmax&&2450<newarea<2650&&0.85*width<height) // if(Xmin<x<Xmax&&8400<newarea<9000) //最佳抓球位置
{
c=5;
requestEvent();
delay(5);
zhuaqiu();
//触发找球条件
STEP=2;
n=0;
taiservo.write(55);
delay(15);
}
}
}
else
{
c=6;
requestEvent();
delay(5);
}
}
/**********************************步骤2:行走至储存区******************************************************************/
// if(signature==2&&chucun==true)
// {
// c=3;
// requestEvent();
// delay(5);
// }
// if(signature==2)
// {
// if(chucun==true)
// {
// c=3; requestEvent();
// }
// }
if(STEP==2)
{
scan(); //另作说明
if(signature==2) //{c=3; requestEvent();}
{
newarea = width * height;
if (x < Cmin) //左平移
{
c=1;
requestEvent();
delay(5);
}
else if (x > Cmax) //右平移
{
c=2;
requestEvent();
delay(5);
}
else if(Cmin<x<Cmax&&newarea <58000) //前进 Xmin<x<Xmax&&newarea <58000
{
c=3;
requestEvent();
delay(5);
}
else if(Cmin<x<Cmax&&newarea >61500) //后退 Xmin<x<Xmax&&newarea >61500
{
c=4;
requestEvent();
delay(5);
}
else if(Cmin<x<Cmax&&58000<newarea<61500)
{
c=5;
requestEvent();
delay(5);
fangqiu();
STEP=1;
catchservo.write(25);
delay(15);
}
}
else
{
c=6;
requestEvent();
delay(5);
}
}
}
/*********************************************扫描目标球*****************************************************/
void scan()
{
uint16_t blocks;
blocks = pixy.ccc.getBlocks(); //receive data from pixy
signature = pixy.ccc.blocks.m_signature; //get object's signature
x = pixy.ccc.blocks.m_x; //get x position
y = pixy.ccc.blocks.m_y; //get y position
width = pixy.ccc.blocks.m_width; //get width
height = pixy.ccc.blocks.m_height; //get height
}
/**********************************************抓球子函数*************************************************/
void zhuaqiu()
{
int i,j;
for(i=65;i>=10;i-=1)
{
taiservo.write(i);
delay(20);
}
delay(100);
for(j=25;j<=80;j+=1)
{
catchservo.write(j);
delay(15);
}
// taiservo.write(55);
// delay(10000);
}
void fangqiu()
{
int k;
for(k=80;k>=25;k-=1)
{
catchservo.write(k);
delay(15);
}
}
void xunzhao()
{
c=6;
requestEvent();
}
void requestEvent()
{
Wire.write(c);
}[/mw_shl_code]
下位机:
[mw_shl_code=arduino,true]#include<Wire.h>
#include<TimerOne.h>
int X=0;
#define EN 8 //步进电机使能端,低电平有效
#define X_DIR 5 //X轴 步进电机方向控制
#define Y_DIR 6 //y轴
#define Z_DIR 7 //z轴
#define A_DIR 13 //A轴
#define X_STP 2 //x轴 脉冲控制
#define Y_STP 3 //y轴
#define Z_STP 4 //z轴
#define A_STP 12 //A轴
void flash()
{
static boolean output=LOW; //低电平有 HIGH
digitalWrite(X_STP,output);
digitalWrite(Y_STP,output);
digitalWrite(Z_STP,output);
digitalWrite(A_STP,output);
output=!output;
}
void setup()
{ //将步进电机用到的IO管脚设置成输出
Serial.begin(115200);
Wire.begin();
pinMode(X_DIR, OUTPUT); pinMode(X_STP, OUTPUT);
pinMode(Y_DIR, OUTPUT); pinMode(Y_STP, OUTPUT);
pinMode(Z_DIR, OUTPUT); pinMode(Z_STP, OUTPUT);
pinMode(A_DIR, OUTPUT); pinMode(A_STP, OUTPUT);
pinMode(EN, OUTPUT);
digitalWrite(EN, LOW); //低電平有效
//stepper.setSpeed(360);
//Timer1.initialize(1000);
Timer1.attachInterrupt(flash);
//MsTimer2::stop();
}
void(* resetFunc) (void) = 0;
const int A=1800;
void loop()
{
//MsTimer2::set(3,flash);
Wire.requestFrom(4,1);
while(Wire.available()>0)
{
volatile byte c=Wire.read(); // volatile储存动态易变常量
switch(c)
{
case 1: //目标球太靠右,右走
Timer1.initialize(A); //100000 micro=100ms
Timer1.attachInterrupt(flash);
zuozou();
Serial.println("1");
break;
delay(5);
case 2: //目标球靠左,左走
Timer1.initialize(A);
Timer1.attachInterrupt(flash);
youzou();
Serial.println("2");
break;
delay(5);
case 3: //无目标球,停止
Timer1.initialize(A); //100000 micro=100ms
Timer1.attachInterrupt(flash);
forward();
Serial.println("3");
break;
delay(5);
case 4:
Timer1.initialize(A); //100000 micro=100ms
Timer1.attachInterrupt(flash);
backward();
Serial.println("4");
break;
delay(5);
case 5:
Timer1.detachInterrupt();
Serial.println("5");
break;
delay(5);
case 6:
Timer1.initialize(A); //100000 micro=100ms
Timer1.attachInterrupt(flash);
ni();
Serial.println("4");
break;
delay(5);
}
}
}
void ni()
{
digitalWrite(X_DIR,LOW);
digitalWrite(Y_DIR,LOW);
digitalWrite(Z_DIR,LOW);
digitalWrite(A_DIR,LOW);
}
void forward()
{
digitalWrite(X_DIR,LOW);
digitalWrite(Y_DIR,HIGH);
digitalWrite(Z_DIR,HIGH);
digitalWrite(A_DIR,LOW);
}
void backward()
{
digitalWrite(X_DIR,HIGH);
digitalWrite(Y_DIR,LOW);
digitalWrite(Z_DIR,LOW);
digitalWrite(A_DIR,HIGH);
}
void zuozou()
{
digitalWrite(X_DIR,HIGH);
digitalWrite(Y_DIR,HIGH);
digitalWrite(Z_DIR,LOW);
digitalWrite(A_DIR,LOW);
}
void youzou()
{
digitalWrite(X_DIR,LOW);
digitalWrite(Y_DIR,LOW);
digitalWrite(Z_DIR,HIGH);
digitalWrite(A_DIR,HIGH);
}
void tingzhi()
{
digitalWrite(X_STP,HIGH);
digitalWrite(Y_STP,HIGH);
digitalWrite(Z_STP,HIGH);
digitalWrite(A_STP,HIGH);
}
void Reset()
{
resetFunc();
}
[/mw_shl_code]
心得:
以一个门外汉的身份去看别人做的东西,觉得很容易,但是当自己开始动手去做的时候才知道“知之不难,行之不易”这个道理。
这个小作品也当是用来入门单片机吧,后面有什么不足的地方看评论区继续补充。
欢迎大家一起交流
QQ:292780142
微信:18476202021