基于Arduino的捡乒乓球小车-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 9569|回复: 14

基于Arduino的捡乒乓球小车

[复制链接]
发表于 2019-5-2 15:04 | 显示全部楼层 |阅读模式
本帖最后由 MT不吃鷄 于 2019-5-2 17:11 编辑
基于Arduino的捡乒乓球小车
先上演示视频:



这个小车是基于pixy视觉识别的模块来完成动作的;
根据反馈的物体面积判断距离物体的远近,反馈的横纵坐标来判断物体的方向。

拾取流程:
小车自转(寻找物体)------识别到物体,根据反馈的横纵坐标走到小球跟前------下爪抓球-----自转(寻找储存区)-----根据反馈的横纵坐标去到储存区-----放球-----again

/*****************************************组成部分*********************************************/
pixy视觉识别+全向底盘+Arduino+手爪


[size=10.5000pt]               
               

小车外型

小车外型
                                               小车外型

                     

pixy模块

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 驱动

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







发表于 2019-5-3 11:04 | 显示全部楼层
你这背景好像我们实验室,桌子底板都好像
 楼主| 发表于 2019-5-3 14:03 | 显示全部楼层
啃番薯 发表于 2019-5-3 11:04
你这背景好像我们实验室,桌子底板都好像

朋友你是哪个实验室的
发表于 2019-5-3 20:39 | 显示全部楼层
新人学习报道!
发表于 2019-5-4 16:12 | 显示全部楼层
MT不吃鷄 发表于 2019-5-3 14:03
朋友你是哪个实验室的

学校的                 
发表于 2019-5-15 16:48 | 显示全部楼层
我也觉得好像呀这实验室
发表于 2019-5-16 19:37 | 显示全部楼层
您好 我想向您请教几个问题 就是小车怎样找到篮筐的
发表于 2019-5-19 15:28 | 显示全部楼层
至于你的程序及各种调整啥的我看不明白外,我能提出的建议就是,你文本的背景设置的花,太花了看代码眼睛不好使的,就完蛋了/
 楼主| 发表于 2019-5-21 11:31 | 显示全部楼层
xu13596489881 发表于 2019-5-19 15:28
至于你的程序及各种调整啥的我看不明白外,我能提出的建议就是,你文本的背景设置的花,太花了看代码眼睛不 ...

以后改进,谢谢!
 楼主| 发表于 2019-5-21 11:33 | 显示全部楼层
cl1902345841 发表于 2019-5-16 19:37
您好 我想向您请教几个问题 就是小车怎样找到篮筐的

通过视觉的模块选中绿色区域,然后判断篮筐的面积大小走至篮筐前
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 11:48 , Processed in 0.094775 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表