基于arduino及PS2的麦克纳姆轮遥控小车

实物展示

1、选材用料

 

 

 

 2、设计原理

 2、代码展示

#include <PS2X_lib.h>
#include<AFMotor.h>
#define PS2_DAT        13  //14    //对应的串口初始化,arduino板上的引脚分别连接PS2上对应的标识引脚
#define PS2_CMD        11  //15     //引脚可按自己连接更改
#define PS2_SEL        10  //16
#define PS2_CLK        12  //17

AF_DCMotor motor1(1);//对四个电机的连接建立
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);

#define pressures   true
#define rumble      true

PS2X ps2x; // create PS2 Controller Class

int error = 0;//以防出错的检测
byte type = 0;
byte vibrate = 0;

void (* resetFunc) (void) = 0;
/*检测连接是否建立,在无显示屏,无法print情况下无用*/
void setup(){
  Serial.begin(115200);//串口通讯波特率的建立
  motor1.setSpeed(200); //四个电机速度的设置(200可按实际需要速度更改)
  motor2.setSpeed(200); 
  motor3.setSpeed(200); 
  motor4.setSpeed(200); 
  
  delay(500);  //added delay to give wireless ps2 module some time to startup, before configuring it
   
  error = ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, pressures, rumble);
  
  if(error == 0){
    Serial.print("Found Controller, configured successful ");
    Serial.print("pressures = ");
  if (pressures)
    Serial.println("true ");
  else
    Serial.println("false");
  Serial.print("rumble = ");
  if (rumble)
    Serial.println("true)");
  else
    Serial.println("false");
    Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;");
    Serial.println("holding L1 or R1 will print out the analog stick values.");
    Serial.println("Note: Go to www.billporter.info for updates and to report bugs.");
  }  
  else if(error == 1)
    Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit www.billporter.info for troubleshooting tips");
   
  else if(error == 2)
    Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit www.billporter.info for troubleshooting tips");

  else if(error == 3)
    Serial.println("Controller refusing to enter Pressures mode, may not support it. ");
  
  type = ps2x.readType(); 
  switch(type) {
    case 0:
      Serial.println("Unknown Controller type found ");
      break;
    case 1:
      Serial.println("DualShock Controller found ");
      break;
    case 2:
      Serial.println("GuitarHero Controller found ");
      break;
  case 3:
      Serial.println("Wireless Sony DualShock Controller found ");
      break;
   }

}

/*前后左右函数*/
void STOP(){
 motor1.run(RELEASE); 
 motor2.run(RELEASE); 
 motor3.run(RELEASE); 
 motor4.run(RELEASE); 
}//停止函数

void RUN(){
  motor1.run(FORWARD); 
 motor2.run(FORWARD); 
 motor3.run(FORWARD); 
 motor4.run(FORWARD); 
}//可调速的前进函数

void LEFT(){
 motor1.run(FORWARD); 
 motor2.run(BACKWARD); 
 motor3.run(BACKWARD); 
 motor4.run(FORWARD); 
}//可调速的左转函数

void RIGHT(){
 motor1.run(BACKWARD); 
 motor2.run(FORWARD); 
 motor3.run(FORWARD); 
 motor4.run(BACKWARD); 
}//可调速的右转函数

void BACK(){
  motor1.run(BACKWARD); 
 motor2.run(BACKWARD); 
 motor3.run(BACKWARD); 
 motor4.run(BACKWARD); 
}//可调速的后退函数

void zishun(){
  motor1.run(FORWARD); 
 motor2.run(BACKWARD); 
 motor3.run(FORWARD); 
 motor4.run(BACKWARD); 
}//顺时针自转

void zini(){
  motor1.run(BACKWARD); 
 motor2.run(FORWARD); 
 motor3.run(BACKWARD); 
 motor4.run(FORWARD); 
}//逆时针自转

/*直接引用示例库,在按下某一按键后,会print 提示,
在提示后加入需要运行的代码,即可实现需要效果,也
可删去多余部分,在此小编直接引用*/
void loop() {
  
  if(error == 1){ //skip loop if no controller found
    resetFunc();
  }
  
  if(type == 2){ //Guitar Hero Controller
    ps2x.read_gamepad();          //read controller 
   
    if(ps2x.ButtonPressed(GREEN_FRET))
      Serial.println("Green Fret Pressed");
    if(ps2x.ButtonPressed(RED_FRET))
      Serial.println("Red Fret Pressed");
    if(ps2x.ButtonPressed(YELLOW_FRET))
      Serial.println("Yellow Fret Pressed");
    if(ps2x.ButtonPressed(BLUE_FRET))
      Serial.println("Blue Fret Pressed");
    if(ps2x.ButtonPressed(ORANGE_FRET))
      Serial.println("Orange Fret Pressed"); 

    if(ps2x.ButtonPressed(STAR_POWER))
      Serial.println("Star Power Command");
    
    if(ps2x.Button(UP_STRUM))          //will be TRUE as long as button is pressed
      Serial.println("Up Strum");
    if(ps2x.Button(DOWN_STRUM))
      Serial.println("DOWN Strum");
 
    if(ps2x.Button(PSB_START))         //will be TRUE as long as button is pressed
      Serial.println("Start is being held");
    if(ps2x.Button(PSB_SELECT))
      Serial.println("Select is being held");
    
    if(ps2x.Button(ORANGE_FRET)) {     // print stick value IF TRUE
      Serial.print("Wammy Bar Position:");
      Serial.println(ps2x.Analog(WHAMMY_BAR), DEC); 
    } 
  }
  else { //DualShock Controller
    ps2x.read_gamepad(false, vibrate); //read controller and set large motor to spin at 'vibrate' speed
    
    if(ps2x.Button(PSB_START))         //will be TRUE as long as button is pressed
      Serial.println("Start is being held");
    if(ps2x.Button(PSB_SELECT))
      Serial.println("Select is being held");      

    if(ps2x.Button(PSB_PAD_UP)) {      //will be TRUE as long as button is pressed
      Serial.print("Up held this hard: ");
      RUN();//按压PSB_PAD_UP键后执行RUN()函数
      Serial.println(ps2x.Analog(PSAB_PAD_UP), DEC);
    }
    if(ps2x.Button(PSB_PAD_RIGHT)){
      Serial.print("Right held this hard: ");
      RIGHT();//按压PSB_PAD_RIGHT键后执行 RIGHT()函数右转
      Serial.println(ps2x.Analog(PSAB_PAD_RIGHT), DEC);
    }
    if(ps2x.Button(PSB_PAD_LEFT)){
      Serial.print("LEFT held this hard: ");
      LEFT();//按压PSB_PAD_LEFTT键后执行LEFT()函数左转
      Serial.println(ps2x.Analog(PSAB_PAD_LEFT), DEC);
    }
    if(ps2x.Button(PSB_PAD_DOWN)){
      Serial.print("DOWN held this hard: ");
      BACK();//按压PSB_PAD_DOWN键后执行 BACK()函数后退
      Serial.println(ps2x.Analog(PSAB_PAD_DOWN), DEC);
    }   
    if(ps2x.Button(PSB_R3)){
      Serial.print("DOWN held this hard: ");
      zishun();//按压PSB_R3键后执行zishun()函数顺时针自转
      Serial.println(ps2x.Analog(PSAB_PAD_DOWN), DEC);
    }   
    if(ps2x.Button(PSB_L3)){
      Serial.print("DOWN held this hard: ");
      zini();//按压PSB_L3键后执行 zini()函数逆时针自转
      Serial.println(ps2x.Analog(PSAB_PAD_DOWN), DEC);
    }   

     /*以下函数在实作中没有用到*/
    vibrate = ps2x.Analog(PSAB_CROSS);  //this will set the large motor vibrate speed based on how hard you press the blue (X) button
    if (ps2x.NewButtonState()) {        //will be TRUE if any button changes state (on to off, or off to on)
      if(ps2x.Button(PSB_L3))
        Serial.println("L3 pressed");
      if(ps2x.Button(PSB_R3))
        Serial.println("R3 pressed");
      if(ps2x.Button(PSB_L2))
        Serial.println("L2 pressed");
      if(ps2x.Button(PSB_R2))
        Serial.println("R2 pressed");
      if(ps2x.Button(PSB_TRIANGLE))
        Serial.println("Triangle pressed");   
        STOP();      
    }

    if(ps2x.ButtonPressed(PSB_CIRCLE))               //will be TRUE if button was JUST pressed
      Serial.println("Circle just pressed");
    if(ps2x.NewButtonState(PSB_CROSS))               //will be TRUE if button was JUST pressed OR released
      Serial.println("X just changed");
    if(ps2x.ButtonReleased(PSB_SQUARE))              //will be TRUE if button was JUST released
      Serial.println("Square just released");     

    if(ps2x.Button(PSB_L1) || ps2x.Button(PSB_R1)) { //print stick values if either is TRUE
      Serial.print("Stick Values:");
      Serial.print(ps2x.Analog(PSS_LY), DEC); //Left stick, Y axis. Other options: LX, RY, RX  
      Serial.print(",");
      Serial.print(ps2x.Analog(PSS_LX), DEC); 
      Serial.print(",");
      Serial.print(ps2x.Analog(PSS_RY), DEC); 
      Serial.print(",");
      Serial.println(ps2x.Analog(PSS_RX), DEC); 
    }     
  }
  delay(50);  
}

 3、结构原理

3.1建模

通过建模模拟车体基本构架,便于结构设计、无实物展示展示和组装;

 

  1.                            (b)

3.1  小车sw模型

   小车简介:小车将采用钢质小车地盘作为车身;以直流减速电机提供动力;麦克纳姆轮作为车轮;采取可装卸推球凹槽。

  

  1.                                 (b)

            图3.2 小球推槽结构

小球推槽结构简介:采用螺母结构固定于车身,竞速赛道时取下提高速度和可操控性,凹槽采取两段圆弧结构,大圆弧加宽凹槽宽度,使小车在遥控行驶的过程中更易捕获小球,凹槽中的小圆弧用于在推动小球时限制小球的较大范围运动,使在推小球到指定区域外时,小车减速,小球依靠惯性运动进入区域时,对小球的运动角度把握的更精准。

3.2 麦克纳姆轮原理

3.2.1 物理原理

3.2.1.1两种轮

对于四轮小车来说,一套麦克纳姆轮至少应含有两种不同的轮子,民间叫法有很多,百度上将他们称为:“麦克纳姆轮左和麦克纳姆轮右”,下面简称左轮和右轮;

左轮:正转可以向左前方运动,反转向右后方的轮(注意是运动方向,与给地面摩擦力的方向相反)

右轮:正转向右前方运动,反转向左后方的轮

当主轮转动时会带动周边轮的转动,而周边轮与主轮的转动成一定角度,所以可以产生一个斜向的力。因此多个麦轮组合使用、分别控制就可以使小车在主轮方向不变的情况下实现全向移动了(如果一时没想清楚就请继续看下面对各个运动状态的分析)。

对于左右轮旋转会产生什么方向的力是关键,你可以尝试着进行受力分析,这里就没必要讲解了,只需要大脑里稍微想象应该一下就明白了(或者直接记上面的结论也是一样的)

核心就是利用安装的对称性抵消不需要的力,叠加目标速度方向的力。

3.2.1.2一种可行的安装方式

对于四轮小车来说;一般来说的安装方式是:

图为一辆麦轮小车的俯视图,其中左前方和右后方的轮为左轮,其余两个为右轮

3.2.2 控制原理

3.2.2.1 前进/后退

以上述方式安装的麦轮同时正转时,前部两个轮与后方两个轮的侧方的力都可以抵消,只剩下向前方的力。小车就可以向前运动

图为,向前时横向移动的力。

后退反转即可,分析同理。

3.2.2.2 左右平移

原理是将小车同一侧的两个麦轮以相反的方向旋转,这样沿前后方的力就会抵消,而横向的力有恰好是同一方向。

例如向左移动就所有右轮全反转,左轮全正转。

3.2.2.3 自旋

原理就是一侧的轮子向后,另一侧的轮子向前;这样对于同一侧的两个轮子来说,横向的力还是可以抵消

如左侧两个轮子正转,右侧两个轮子反转就可以使小车顺势针旋转,反之则逆时针。

以上这几种运动方式需要注意的是,四个轮子虽然转的方向不同,但是转速需要严格相同,不然运动就不再准确。

3.3 控制逻辑

通过一套完备的代码,保证小车运行的速度和灵活性,通过手柄按键,控制实现前后左右、原地掉头,45°斜向的移动。

图3.3.1 PS2手柄按键示意图

图3.3.2连接示意图

      

(a)初始化设置

 

  1. 部分行进函数

图3.3.3 部分代码展示

代码简介:对arduino于PS2手柄连接Pin口进行初始话设置;如用L298n,则对控制L298n的IO口进行设置,将前后左右、原地掉头,45°斜向的移动封装为函数,当接收到PS2手柄的通讯信息时,按照不同按键的信息进行调用,实现移动。

3.4 组装

根据建模结果,由钢制车架(亚克力板)、直流电机、小轮胎、电池盒、电池等搭建小车,连接arduino板与推动足球的3D打印支架

 

 

图3.3.4 目前完成部分图

物联沃分享整理
物联沃-IOTWORD物联网 » 基于arduino及PS2的麦克纳姆轮遥控小车

发表评论