可以用于测速的模块很多,比如加速度计、激光、超声波、编码器等等,由于我们对小车速度的测量精度要求不高,因此我们可以借助小车套件里面的码盘配上测速模块对其速度进行测量。 ##准备材料 测速模块 网上的测速模块有很多种外观,但是其工作原理类似,下面列出来了几种常见的测速模块,这些测速模块接线类似。
测速模块的安装
驱动模块的安装需要由尼龙柱支撑
测速模块讲解
测速模块的工作原理比较简单,如下图所示,在于电机同轴的码盘上有很多开孔(光栅),编码器相当于光敏元件。码盘随着小车轮子的运动转动时,码盘(光栅)会不断遮挡光敏元件发出的光波,这时候编码器就会根据光栅的遮挡不断的产生方波信号,方波信号会从“OUT”引脚输出,我们只需不断检测“OUT”引脚的输出,根据方波信号的周期简介计算出小车运行的速度。小车上使用的码盘(光栅)精度不高,在某些高精度的编码器上光栅会更加密集,测量效果会更好。
attachInterrupt(interrupt, function, mode)
可以对中断引脚初始化,其中 interrupt: 要初始化的外部中断编号,由上表可知我们Arduino UNO只能使用外部中断0和外部中断1; function: 中断服务函数的名字,即当外部中断被触发时,将会自动调用这个函数; mode: 中断触发的方式,可选方式如下
测试代码如下
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);
}
}
看了这个代码大家应该就明白为什么之前我建议大家将每个功能都封装成函数了,封装成函数很利于程序的移植。
测速效果
借助于蓝牙串口助手我们可以清楚的看到小车左右轮的转速如图,由于电机自身的误差和摩擦力的作用,因此两个轮子的转速不相等,但是差别不是很大。