坦克智能小车第七课:编码器

Byamber

坦克智能小车第七课:编码器

概述

这是坦克智能小车的最后一课,在前面已经提到过,由于电机之间存在一定误差,即使给两个电机相同的电压,其转速一般是不一样的,这样坦克小车就无法行走直线。要想小车走直线就需要用编码器调节两个电机速度,使其速度一样。

编码器原理

编码器分为光电式编码器和霍尔编码器,我们使用的是霍尔式编码器。霍尔编码器是一种通过磁电转换将输出轴上机械几何位移转换成脉冲或数字量的传感器。霍尔编码器由霍尔码盘和霍尔元件组成。霍尔码盘是在一定直径的直板上等分地布置有不同的磁极,霍尔码盘与电机同轴,电机旋转时,霍尔元件检测输出若干脉冲信号,为判定转向,一般输出两组存在一定相位差的脉冲信号。

编码器接线说明

电机一共有6pin,其中VM和GM是控制电机的,接到L298N上,这在之前已经讲过,剩下的4pin分别是

V:霍尔编码器电源,一般接5V

G:霍尔编码器地,接GND

S1:编码器的A相输出

S2:编码器B相输出

motor

拧下亚克力面板上的四颗螺帽,将电机上编码器的四条线穿过金属面板和亚克力面板,按照下表接到esp8266 uart wifi shield上(翻译的时候把表变成连线图)

左边电机
编码器 esp8266 uart wifi shield
V 5V
G GND
S1 D2
S2 D4
右边电机
编码器 esp8266 uart wifi shield
V 5V
G GND
S1 D3
S2 D7

采集脉冲

因为编码器输出的是方波信号,可以用arduino直接读取,在上面的接线图中我们分别将编码器的A相输出接到arduino的外部中断引脚,用中断的方式采集脉冲;B相接到普通GPIO口,B相的电平用来判断正反转。通过arduino定时器把采样周期设定为10ms,采集完两个电机的脉冲后,需要用PID算法来调节电机速度。所谓PID就是对偏差进行比例、积分、微分,PID由3个单元组成,分别是比例(P)单元、积分(I)单元和微分(D)单元。在工程实践中,比例单元是必须的,所以衍生出许多组合的PID控制器,如PD、PI、PID,更多关于PID算法详细介绍请看这里。在本课中我才用增量式PI算法。在这个PI算法中把左边电机的速度作为目标速度,通过PI算法调节右边电机速度使其与左边电机速度一致。

软件

下载http://osoyoo.com/driver/tank_robot_lesson7.zip并解压文件,用arduino IDE打开tank_robot_lesson7.ino文件,下面对部分代码做简要说明。

const byte encoder0pinA = 2;//A pin -> the interrupt pin 0
const byte encoder0pinB = 4;//B pin -> the digital pin 4
const byte rencodPinA = 3;  //rencodPinA -> the interrupt pin 1
const byte rencodPinB = 7;  //rencodPinB -> the digital pin 11

上面四行代码分别定义了左右两个电机A、B相接口

 MsTimer2::set(10, control);  //use timer2 to set the 10ms timer interrupt
 MsTimer2::start();          //enable interrupt

设定采样周期为10ms,10ms到达后,进入定时器中断服务函数control中

  l_direction = true;//default -> Forward  
  r_direction = true;//default -> Forward  
  pinMode(encoder0pinB,INPUT);  
  pinMode(rencodPinB,INPUT);  
  attachInterrupt(0, lwheelSpeed, CHANGE);
  attachInterrupt(1, rwheelSpeed, CHANGE);

编码器引脚初始化,左右电机编码的A相输出分别使用arduino外部中断0和外部中断1,使用跳变沿(上升沿和下降沿)

void lwheelSpeed()
{
  int Lstate = digitalRead(encoder0pinA);
  if((encoder0PinALast == LOW) && Lstate==HIGH)
  {
    int val = digitalRead(encoder0pinB);
    if(val == LOW && l_direction)
    {
      l_direction = false; //Reverse
    }
    else if(val == HIGH && !l_direction)
    {
      l_direction = true;  //Forward
    }
  }
  encoder0PinALast = Lstate;
 
  if(!l_direction)  duration++;
  else  duration--;
}

void rwheelSpeed()
{
  int Lstate = digitalRead(rencodPinA);
  if((encoder0PinALast1 == LOW) && Lstate==HIGH)
  {
    int val = digitalRead(rencodPinB);
    if(val == LOW && r_direction)
    {
      r_direction = false; //Reverse
    }
    else if(val == HIGH && !r_direction)
    {
      r_direction = true;  //Forward
    }
  }
  encoder0PinALast1 = Lstate;
 
  if(!r_direction)  duration1++;
  else  duration1--;
}

编码器中断服务函数。当有跳变沿到来时候,会执行中断服务函数,对脉冲加1或减1

int PID_controller(int master,int slave)
{ 
  static float power,error,integralerror,lasterror;
  if(master < 0) master = -master;
  if(slave < 0) slave = -slave;
  error = master - slave;
  integralerror += error;
  power += Kp *(error-lasterror) + Ki * error;
  lasterror = error;
  return power;
}

上面的函数就是增量式PI算法的C语言实现,左右电机脉冲相减作为误差,误差的累积作为积分项,误差为0表示右边电机速度和左边电机速度一致;大于0说明右边电机比左边电机慢;小于0表示右边电机速度比左边电机快。通过PI算法会得到一个误差补给power,将这个power作用与右边电机上,就能达到调节速度的目的。

void control()
{
   sei();//enable global interrupts
   if(++i >=4)//20ms
   {
   master_pulse = duration ,duration = 0;
   slave_pulse = duration1, duration1 = 0;
   pwm = PID_controller(master_pulse,slave_pulse);
   i = 0;
   }
   int newpower1 = motorspeed+pwm;
   constrain(newpower1,0,255);
   analogWrite(ENB,newpower1),analogWrite(ENA,motorspeed);
   cli();//disenable global interrupts
}

10ms到达时候会执行定时器中断服务函数,在定时器中断服务函数中,将PI得到的误差补给作用到右边电机上。

const float Kp =20;
const float Ki =1;

测试

step 1 烧录程序:用usb线把坦克下车与pc连接起来,选择正确的板子型号和端口号,将esp8266 uart wifi shield上SW1拨到off,把程序烧录到arduino中。

step 2 在两边履带相同位置绑一条红线之类的绳子。

step 3 用手拿着安装电池盒的金属车板,打开电池盒电源,观察2个电机上绳子转的快慢是否一致。如果不一致可以通过调节Kp、Ki两个参数。

step 4 将小车放在相对粗糙的地板上,打开电源开关,小车就能走直线了。(注意:如果地板光滑,会因为摩擦过小导致小车无法走直线)

About the Author

amber administrator

Comments Are Closed!!!