Arduino 直流电机转速控制系统

通过增量式编码器来测量电机的转速,常规的单片机的程序架构是通过定时器来实现精确的时间定时,并利用外部中断来实现对脉冲数目的计数,然后计算出一定时间内脉冲的数目,从而得到转速数值并输出。
直流电机是Arduino机器人制作中的主要动力来源,但是由于电机的参数一致性有所差别,即使是相同型号的电机在相同电压下的转速都不完全相同,而且在带负载或负载不同的情况下,更加会导致电机转速发生变化,这就会导致制作的Arduino轮式机器人不能实现直线行走,因为这是一个开环控制,没有任何反馈信号返回。如果给直流电机加上编码器作为反馈器件,也就可以测量得到电机的当前转速,如果将其与设定值计算差值,并通过PID算法计算得到新的控制信号,从而可以动态的测量和控制电机的转速,形成一个闭环控制系统。
2.1测量转速方法 测量转速方法有3种,分别为测频法(M法)、 测周法(T法)及混合法(M/T法)。 测频法是在一定时间内,通过测量旋转引起的单位时间内的脉冲数,实现对旋转轴转速测量的一种方法,适用于高、中转速的测量。该法本质上属于定时测角法,为提高测量的准确度,有时采用多标记或开齿的方法,其不确定度主要取决于时间测量和计数量化。

测周法是在转速脉冲的间隔内,用时钟脉冲来测量转速的一种方法,适合于低转速测量。该法实际上就是定角测量法,即用时标填充的方法来测量相当于某一旋转角度的时间间隔。在高、中转速时,可采用多周期平均来提高测量准确度,其不确定度主要取决于时间测量、计数量化及触发的不确定度。 混合法是在测频法的基础上,吸取测周法的优点汇集而成的一种转速测量方法。它是在转速传感器输出脉冲启动定时脉冲的同时,计取传感器输出脉冲个数和时钟脉冲个数,而当到达测量时间时,先停止对传感器输出脉冲的计数,在下一个定时脉冲启动之前再停止时钟脉冲的计数。因此,该种方法可在较宽的范围内使用。

此处,我们选择测频法来测量转速,其工作原理为:当被测信号在特定时间段T内的周期个数为N时,则被测信号的频率f=N/T。 ###2.2转速测量程序设计 利用TimerOne定时器库来实现定时,通过外部中断对电机编码器输出的脉冲进行计数,计数值除以定时时间即为一定时间内的转速。实现1秒内转速测量的程序如程序代码2所示。

#include 
long counter_val[2] = {0,0};  //定义数组,用于存放外部中断计数值
byte CurCnt = 0;    //定义当前计数器标志,用于判断当前正在计数的数组
int j=0;        //定义定时器中断标志,用于判断是否发生中断
void setup() {
  delay(2000);
  Serial.begin(115200);//初始化波特率为115200
  attachInterrupt(0, counter, RISING);//设置中断方式为上升沿
  Timer1.initialize(1000000); // 设置定时器中断时间,单位微秒,此处为1秒
  Timer1.attachInterrupt( timerIsr ); // 打开定时器中断
  interrupts();  //打开外部中断
}

void loop()
{
  long lTemp = 0; //定义临时存储数据变量
  if(j==1)   //判断是否发生定时器中断,即定时时间是否到达
   {
     j=0;   //清除定时器中断标志位
    if((CurCnt&0x01) == 0) //当前使用的是偶数计数器,则上次频率值存放在第二个元素中
     {
         lTemp = counter_val[1];  //读取数组第二个元素中的数值
         counter_val[1]=0;       //读完清除原来的数值,以便下次使用
     }
     else   //当前使用的是奇数计数器,则上次频率值存放在第一个元素中
     {
       lTemp = counter_val[0];  //读取数组第二个元素中的数值
       counter_val[0]=0;  //读完清除原来的数值,以便下次使用
     }
        Serial.print("S");    //发送帧头大写S
        Serial.print( lTemp);  //发送频率数据,并回车换行
     }
}

//外部中断处理函数
void counter()
{
    //通过当前计数器来实现对外部中断计数值存储的切换
     counter_val[CurCnt& 0x01] += 1;    //发生一次中断则加1
}

//定时器中断处理函数
void timerIsr()
{
  j=1;     //置位定时器中断标志位
  CurCnt++; //当前计数器的值加1,实现另一个计数值切换
}

验证频率测量的准确性 前面提到了Arduino的模拟输出(PWM)的频率约为490Hz,且转速测量采用的是测频法,此时用来正好来验证一下程序设计的正确性。在上面的转速测量程序中的void setup()里面delay(2000)之前增加如下代码,以产生方波。串口输出的频率测量结果如图4所示

pinMode(3,OUTPUT);
analogWrite(3,127);

转速比例控制的程序设计

#include 
#define Kp 3
#define set_point 100
long counter_val[2] = {0,0};
byte CurCnt = 0;
int j=0;
int output_value=0;
void setup()
{
  delay(2000);
  pinMode(12,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(9,OUTPUT);       //启用电机A的三个管脚,全部设置为输出状态
  digitalWrite(9, LOW);       //松开电机A的制动
  digitalWrite(12, HIGH);      //设置方向为正向旋转
  Serial.begin(115200);//初始化波特率为115200
  attachInterrupt(0, counter, RISING);//设置编码器A相位上升沿中断
  attachInterrupt(1, counter, RISING);//设置编码器B相位上升沿中断
  Timer1.initialize(10000); // 设置定时器中断时间,单位微秒
  Timer1.attachInterrupt( timerIsr ); // 打开定时器中断
  interrupts();  //打开外部中断
}
void loop()
{
  long lTemp = 0; //定义临时存储数据变量
  if(j==1)   //判断是否发生定时器中断,即定时时间是否到达
   {
     j=0;   //清除定时器中断标志位
    if((CurCnt&0x01) == 0) //当前使用的是偶数计数器,则上次频率值存放在第二个元素中
     {
         lTemp = counter_val[1];  //读取数组第二个元素中的数值
         counter_val[1]=0;       //读完清除原来的数值,以便下次使用
     }
     else   //当前使用的是奇数计数器,则上次频率值存放在第一个元素中
     {
       lTemp = counter_val[0];  //读取数组第二个元素中的数值
       counter_val[0]=0;  //读完清除原来的数值,以便下次使用
     }
        Serial.print("A");      //发送转速帧头大写A
        Serial.print( lTemp);   //发送转速数据
        output_value =( set_point -lTemp)*Kp+ output_value;  //比例计算得到控制量
        if(output_value >255)   //限制PWM在0-255范围内
        output_value =255;
if(output_value <0)   //限制PWM在0-255范围内
        output_value =0;
        analogWrite(3, output_value);      //将计算得到的控制量输出
    Serial.print("B");                //发送PWM帧头大写B
        Serial.println(output_value);       //发送PWM数据
     }
}
//外部中断处理函数
void counter()
{
     counter_val[CurCnt& 0x01] += 1;    //每一个中断加一
}
//定时器中断处理函数
void timerIsr()
{
  j=1;     //定时时间达到标志
  CurCnt++; //切换计数数组
}

你可能感兴趣的:(arduino,机器人,自我每天记录进步)