Arduino智能小车系列教程7——小车测速

Arduino智能小车系列教程    
  1. Arduino智能小车系列教程1——拼装篇
  2. Arduino智能小车系列教程2——测试篇
  3. Arduino智能小车系列教程3——调速篇
  4. Arduino智能小车系列教程4——超声波避障
  5. Arduino智能小车系列教程5——蓝牙小车
  6. Arduino智能小车系列教程6——循迹小车
  7. Arduino智能小车系列教程7——小车测速

文章目录
  • 测速模块的安装
  • 测速模块讲解
  • 测试代码如下
  • 测速效果

可以用于测速的模块很多,比如加速度计、激光、超声波、编码器等等,由于我们对小车速度的测量精度要求不高,因此我们可以借助小车套件里面的码盘配上测速模块对其速度进行测量。  

准备材料

  测速模块   网上的测速模块有很多种外观,但是其工作原理类似,下面列出来了几种常见的测速模块,这些测速模块接线类似。  
以上两种模块只能对一路电机的速度进行测量,下面这个可以同时测量两路电机,但是由于其间距固定,在安装时可能不兼容不同小车底盘,因此为了安装方便,在此篇教程中使用第一种模块。  
  固定铜柱   建议铜柱长度30CM,大小为M3  
由于铜柱导电,有些电路板如果固定孔设计不当的话很容易导致电路板烧坏,尤其是在以后的项目中可能会用到更高的电压,因此在这里我建议大家可以准备一些尼龙柱来固定电路板。尼龙柱的尺寸跟铜柱尺寸相同M3,30CM。大家也可以顺便买一点尼龙螺丝。  
除此之外还需要准备杜邦线  

测速模块的安装

  驱动模块的安装需要由尼龙柱支撑  
驱动模块安装时需要注意,不能影响轮子的正常工作,不能触碰到轮轴上的码盘。 编码器上有三个引脚分别是“VCC”,“GND”,“OUT”。左右两边两个测速模块的“VCC”引脚接电源或开发板的“5V”或“3.3V”引脚,“GND”接电源或开发板的“GND”引脚,左边测速模块“OUT”接开发板的“3”引脚,右边测速模块“OUT”接开发板的“2”引脚。引脚接错的话可以再随后调试过程中换过来,也可以在代码里更改。   最终接线图如下:

测速模块讲解

  测速模块的工作原理比较简单,如下图所示,在于电机同轴的码盘上有很多开孔(光栅),编码器相当于光敏元件。码盘随着小车轮子的运动转动时,码盘(光栅)会不断遮挡光敏元件发出的光波,这时候编码器就会根据光栅的遮挡不断的产生方波信号,方波信号会从“OUT”引脚输出,我们只需不断检测“OUT”引脚的输出,根据方波信号的周期简介计算出小车运行的速度。小车上使用的码盘(光栅)精度不高,在某些高精度的编码器上光栅会更加密集,测量效果会更好。  
由于要不断检测编码器输出端的输出,因此我们需要借助Arduino的外部中断来读取编码器的输出。Arduino开发板外部中断对应的引脚如下:  
型号 int0 int1 int2 int3 int4 int5
UNO\Ethernet 2 3
Mega2560 2 3 21 20 19 18
Leonardo 3 2 0 1
Due All All All All All All
  由表中可以知道在此我们使用的Arduino UNO只有“2”,“3”引脚可以触发外部中断,因此在接线的时候我们便将左右两边的输出“OUT”引脚分别接在“2”“3”引脚上。   在程序初始化阶段中调用函数attachInterrupt(interrupt, function, mode)可以对中断引脚初始化,其中 interrupt: 要初始化的外部中断编号,由上表可知我们Arduino UNO只能使用外部中断0和外部中断1; function: 中断服务函数的名字,即当外部中断被触发时,将会自动调用这个函数; mode: 中断触发的方式,可选方式如下  
mode 含义
LOW 低电平触发
CHANGE 电平变化,高电平变低电平、低电平变高电平
RISING 上升沿触发
FALLING 下降沿触发
HIGH 高电平触发(该中断模式仅适用于Arduino due)

测试代码如下

 
int leftCounter=0,  rightCounter=0;
unsigned long time = 0, old_time = 0; // 时间标记
unsigned long time1 = 0; // 时间标记
float lv,rv;//左、右轮速度

#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4

int leftMotor1 = 4;
int leftMotor2 = 5;
int rightMotor1 = 6;
int rightMotor2 = 7;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); 
  attachInterrupt(0,RightCount_CallBack, FALLING);
  attachInterrupt(1,LeftCount_CallBack, FALLING);

  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  SpeedDetection();
  if(Serial.available()>0)
  {
    char cmd = Serial.read();

    Serial.print(cmd);
    motorRun(cmd);

  }  
}
/*
 * *速度计算
 */
bool SpeedDetection()
{
  time = millis();//以毫秒为单位,计算当前时间 
  if(abs(time - old_time) >= 1000) // 如果计时时间已达1秒
  {  
    detachInterrupt(0); // 关闭外部中断0
    detachInterrupt(1); // 关闭外部中断1
    //把每一秒钟编码器码盘计得的脉冲数,换算为当前转速值
    //转速单位是每分钟多少转,即r/min。这个编码器码盘为20个空洞。
    lv =(float)leftCounter*60/20;//小车车轮电机转速
    rv =(float)rightCounter*60/20;//小车车轮电机转速
    Serial.print("left:");
    Serial.print(lv);//向上位计算机上传左车轮电机当前转速的高、低字节
    Serial.print("     right:");
    Serial.println(rv);//向上位计算机上传左车轮电机当前转速的高、低字节
    //恢复到编码器测速的初始状态
    leftCounter = 0;   //把脉冲计数值清零,以便计算下一秒的脉冲计数
    rightCounter = 0;
    old_time=  millis();     // 记录每秒测速时的时间节点   
    attachInterrupt(0, RightCount_CallBack,FALLING); // 重新开放外部中断0
    attachInterrupt(1, LeftCount_CallBack,FALLING); // 重新开放外部中断0
    return 1;
  }
  else
    return 0;
}
/*
 * *右轮编码器中断服务函数
 */
void RightCount_CallBack()
{
  rightCounter++;
}
/*
 * *左轮编码器中断服务函数
 */
void LeftCount_CallBack()
{
  leftCounter++;
}
/*
 * *小车运动控制函数
 */
void motorRun(int cmd)
{
  switch(cmd){
    case FORWARD:
      Serial.println("FORWARD"); //输出状态
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case BACKWARD:
      Serial.println("BACKWARD"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNLEFT:
      Serial.println("TURN  LEFT"); //输出状态
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      Serial.println("TURN  RIGHT"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      Serial.println("STOP"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}
看了这个代码大家应该就明白为什么之前我建议大家将每个功能都封装成函数了,封装成函数很利于程序的移植。  

测速效果

  借助于蓝牙串口助手我们可以清楚的看到小车左右轮的转速如图,由于电机自身的误差和摩擦力的作用,因此两个轮子的转速不相等,但是差别不是很大。  
欢迎各位有兴趣的朋友加入Q群1:789127261点评、交流