Arduino 电机测速

理论

重点在于脉冲数量的计数
在这里插入图片描述

AB相增量式编码器测速原理
测速 = 位移(统计方波个数)/时间

  1. 编码器组成: A相 + B相
  2. A相与B相都会规律输出电信号(方波脉冲) == 低电压 + 高电压 当前电机参数: 一圈输出11个脉冲信息(每隔33°左右)
  3. 输出有固定规律: AB相相互延迟 1/4 个周期
    正转: B提前1/4周期(看下降沿)
    反转: B延迟1/4周期

比如: 时间为2秒,测得脉冲个数为22个,由于一圈输出脉冲11个,所以转了2圈,转速为2r/2s = 1转/秒,由此可以计算出速度值。
测速核心统计方波个数:计算从低电压到高电压的数量,count计算的是跳变(上升沿或者下降沿)个数,这样就能计算出脉冲个数。

// 每输出一次脉冲信号,计数两次
// 当A跳变到高电压时
	if(B == high) B为高电平说明电机正转
		count ++;
	else 否则B为低电压说明电机反转
		count --;
// 当A跳变到低电压时
	if(B == low) B为低电压说明为电机正转
		count ++;
	else
		count --;
// 当B跳变到高电压时
	if(A == low) 正转
		count ++;
	else
		count --;
// 当B跳变到低电压时
	if(A == high) 反转
		count ++;
	else (A == low)
		count --;

总结:

  • 只统计单项的单跳变是单倍频计数(一个周期计数一次)
  • 统计单项的两次跳变(比如都统计A的两次或者B的两次)是双倍频计数(一个周期计数两次)
  • 统计双项的两次跳变四双倍频计数(一个周期计数四次)
  • 精度依次提高。

练习
四倍频 10000次/2秒
脉冲数 = 10000 / 4 = 2500
圈 = 2500 / 11
转速为 = 2500 / 11 /2
在这里插入图片描述
在这里插入图片描述

实现

电机正反转

motor1_control

/*
 * 需求: 让电机正转3秒,停止3秒,反转3秒,停止3秒
 * 电机转动控制
 *  1.定义接线中电机对应的引脚
 *  2.setup 中设置引脚为输出模式
 *  3.loop中控制电机转向和转速
 * 
 */

int DIRA_LEFT = 4;//控制转向
int PWMA_LEFT = 5;//控制转速

void setup() {
  //两个引脚都设置为 OUTPUT
  pinMode(DIRA_LEFT,OUTPUT);
  pinMode(PWMA_LEFT,OUTPUT);
}

void loop() {
  //先正向转动3秒
  digitalWrite(DIRA_LEFT,HIGH);
  analogWrite(PWMA_LEFT,100);
  delay(3000);
  //停止3秒
  digitalWrite(DIRA_LEFT,HIGH);
  analogWrite(PWMA_LEFT,0);
  delay(3000);
  //再反向转动3秒
  digitalWrite(DIRA_LEFT,LOW);
  analogWrite(PWMA_LEFT,100);
  delay(3000);
  //停止3秒
  digitalWrite(DIRA_LEFT,LOW);
  analogWrite(PWMA_LEFT,0);
  delay(3000);

  /*
   * 注意: 
   * 1.可以通过将DIRA设置为HIGH或LOW来控制电机转向,但是哪个标志位正转或反转需要根据需求判断,转向是相对的。
   * 2.PWM的取值为 [0,255],该值可自己设置。
   * 
   */
}
}

脉冲数统计

核心知识点:attachInterrupt()函数

/*
 * 测速实现:
 *  阶段1:脉冲数统计
 *  阶段2:速度计算
 * 
 * 阶段1:
 *  1.定义所使用的中断引脚,以及计数器(使用 volatile 修饰)
 *  2.setup 中设置波特率,将引脚设置为输入模式
 *  3.使用 attachInterupt() 函数为引脚添加中断出发时机以及中断函数
 *  4.中断函数编写计算算法,并打印
 *    A.单频统计只需要统计单相上升沿或下降沿
 *    B.2倍频统计需要统计单相的上升沿和下降沿
 *    C.4倍频统计需要统计两相的上升沿和下降沿
 *  5.上传并查看结果
 *  
 * 
 */
int motor_A = 21;//中端口是2
int motor_B = 20;//中断口是3
volatile int count = 0;//如果是正转,那么每计数一次自增1,如果是反转,那么每计数一次自减1 

void count_A(){
  //单频计数实现
  //手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比
  /*if(digitalRead(motor_A) == HIGH){

    if(digitalRead(motor_B) == LOW){//A 高 B 低
      count++;  
    } else {//A 高 B 高
      count--;  
    }
  }*/

  //2倍频计数实现
  //手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比 * 2
  if(digitalRead(motor_A) == HIGH){

    if(digitalRead(motor_B) == HIGH){//A 高 B 高
      count++;  
    } else {//A 高 B 低
      count--;  
    }

  } else {

    if(digitalRead(motor_B) == LOW){//A 低 B 低
      count++;  
    } else {//A 低 B 高
      count--;  
    }  
  }
}

//与A实现类似
//4倍频计数实现
//手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比 * 4
void count_B(){
  if(digitalRead(motor_B) == HIGH){

    if(digitalRead(motor_A) == LOW){//B 高 A 低
      count++;
    } else {//B 高 A 高
      count--;
    }


  } else {

    if(digitalRead(motor_A) == HIGH){//B 低 A 高
      count++;
    } else {//B 低 A 低
      count--;
    }
  }
}

void setup() {
  Serial.begin(57600);//设置波特率  
  pinMode(motor_A,INPUT);
  pinMode(motor_B,INPUT);
  attachInterrupt(2,count_A,CHANGE);//当电平发生改变时触发中断函数
  //四倍频统计需要为B相也添加中断
  attachInterrupt(3,count_B,CHANGE);
}

void loop() {
  //测试计数器输出
  delay(2000);
  Serial.println(count);
}

电机正转动1圈——>3931

  1. 输出轴转动1圈
  2. 四倍频计数

由于一直减速比为90

  1. 输入轴转了90圈,则编码器转了90圈
  2. 90 * 11 = 990(编码器转一圈得到11个脉冲)
  3. 990 * 4 = 3960 (4倍频)

motor2_encoder

单倍频
轮子转一圈,由减速比为90,可知,电机转90圈,则编码器转90圈,一圈脉冲是11个,应该有990脉冲

/*
 * 实现脉冲计数
 * 
 * 流程:
 *  1.将使用的引脚封装为变量,封装计数变量
 *  2.setup中设置引脚的操作模式(读取编码器相关引脚的输出信号INPUT),设置波特率(结果输出到上位机)
 *  3.为引脚添加中断事件
 *  4.计数逻辑实现
 *  5.要输出到上位机
 */

// 1.将使用的引脚封装为变量,封装计数变量
int encoder_A = 21;// 中断口2
int encoder_B = 20;// 中端口3
volatile int count = 0;

// 4.计数逻辑实现
void count_a(){
  // 先判断A是否跳变到高电压
  if(digitalRead(encoder_A) == HIGH){
    // 再判断B的电压
    if(digitalRead(encoder_B) == HIGH){
      count ++;
     } else {
       count --;
     }
  }
}
void setup() {
  // put your setup code here, to run once:
  // 设置波特率
  Serial.begin(57600);
  // 2.setup中设置引脚的操作模式(INPUT)
  pinMode(encoder_A,INPUT);// 从引脚读数据
  pinMode(encoder_B,INPUT);
  // 3.为引脚添加中断函数
  // 参数1: 中断口 参数2: 回调函数 参数3: 触发时机
  attachInterrupt(2,count_a,CHANGE);// 单倍频或双倍频只需要为编码器的A相添加中断函数,CHANGE 当引脚电平发生改变时,触发中断
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
  Serial.println(count);
}

双倍频
轮子转一圈,由减速比为90,可知,电机转90圈,则编码器转90圈,一圈脉冲是11个,应该有990脉冲,990*2 = 1980左右

/*
 * 实现脉冲计数
 * 
 * 流程:
 *  1.将使用的引脚封装为变量,封装计数变量
 *  2.setup中设置引脚的操作模式(读取编码器相关引脚的输出信号INPUT),设置波特率(结果输出到上位机)
 *  3.为引脚添加中断事件
 *  4.计数逻辑实现
 *  5.要输出到上位机
 */

// 1.将使用的引脚封装为变量,封装计数变量
int encoder_A = 21;// 中断口2
int encoder_B = 20;// 中端口3
volatile int count = 0;

// 4.计数逻辑实现
void count_a(){
  // 先判断A是否跳变到高电压
  if(digitalRead(encoder_A) == HIGH){
    // 再判断B的电压
    if(digitalRead(encoder_B) == HIGH){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   } else {
    // 再判断B的电压
    if(digitalRead(encoder_B) == LOW){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   }
 }
void setup() {
  // put your setup code here, to run once:
  // 设置波特率
  Serial.begin(57600);
  // 2.setup中设置引脚的操作模式(INPUT)
  pinMode(encoder_A,INPUT);// 从引脚读数据
  pinMode(encoder_B,INPUT);
  // 3.为引脚添加中断函数
  // 参数1: 中断口 参数2: 回调函数 参数3: 触发时机
  attachInterrupt(2,count_a,CHANGE);// 单倍频或双倍频只需要为编码器的A相添加中断函数,CHANGE 当引脚电平发生改变时,触发中断
  
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
  Serial.println(count);
}

四倍频
1980 * 2 = 3960

/*
 * 实现脉冲计数
 * 
 * 流程:
 *  1.将使用的引脚封装为变量,封装计数变量
 *  2.setup中设置引脚的操作模式(读取编码器相关引脚的输出信号INPUT),设置波特率(结果输出到上位机)
 *  3.为引脚添加中断事件
 *  4.计数逻辑实现
 *  5.要输出到上位机
 */

// 1.将使用的引脚封装为变量,封装计数变量
int encoder_A = 21;// 中断口2
int encoder_B = 20;// 中断口3
volatile int count = 0;

// 4.计数逻辑实现
void count_a(){
  // 先判断A是否跳变到高电压
  if(digitalRead(encoder_A) == HIGH){
    // 再判断B的电压
    if(digitalRead(encoder_B) == HIGH){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   } else {
    // 再判断B的电压
    if(digitalRead(encoder_B) == LOW){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   }
 }

void count_b(){
  if(digitalRead(encoder_B) == HIGH)
  {
    if(digitalRead(encoder_A) == LOW)
    {
      count ++;
    }
    else
    {
      count --;
    }
  }
  else
  {
    if(digitalRead(encoder_A) == HIGH)
    {
      count ++;
    }
    else
    {
      count --;
    }
  }
 }
 
void setup() {
  // put your setup code here, to run once:
  // 设置波特率
  Serial.begin(57600);
  // 2.setup中设置引脚的操作模式(INPUT)
  pinMode(encoder_A,INPUT);// 从引脚读数据
  pinMode(encoder_B,INPUT);
  // 3.为引脚添加中断函数
  // 参数1: 中断口 参数2: 回调函数 参数3: 触发时机
  attachInterrupt(2,count_a,CHANGE);// 单倍频或双倍频只需要为编码器的A相添加中断函数,CHANGE 当引脚电平发生改变时,触发中断
  attachInterrupt(3,count_b,CHANGE);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
  Serial.println(count);
}

转速计算

int reducation = 90;//减速比,根据电机参数设置,比如 15 | 30 | 60
int pulse = 11; //编码器旋转一圈产生的脉冲数该值需要参考商家电机参数
int per_round = pulse * reducation * 4;//车轮旋转一圈产生的脉冲数 
long start_time = millis();//一个计算周期的开始时刻,初始值为 millis();
long interval_time = 50;//一个计算周期 50ms
double current_vel;

//获取当前转速的函数
void get_current_vel(){
  long right_now = millis();  
  long past_time = right_now - start_time;//计算逝去的时间
  if(past_time >= interval_time){//如果逝去时间大于等于一个计算周期
    //1.禁止中断
    noInterrupts();
    //2.计算转速 转速单位可以是秒,也可以是分钟... 自定义即可
    current_vel = (double)count / per_round / past_time * 1000 * 60;
    //3.重置计数器
    count = 0;
    //4.重置开始时间
    start_time = right_now;
    //5.重启中断
    interrupts();

    Serial.println(current_vel);
  }
}
void loop() {
  delay(10);
  get_current_vel();
}

motor03_vel

编码实现

/*
 * 实现脉冲计数
 * 
 * 流程:
 *  1.将使用的引脚封装为变量,封装计数变量
 *  2.setup中设置引脚的操作模式(读取编码器相关引脚的输出信号INPUT),设置波特率(结果输出到上位机)
 *  3.为引脚添加中断事件
 *  4.计数逻辑实现
 *  5.要输出到上位机
 */

// 1.将使用的引脚封装为变量,封装计数变量
int encoder_A = 21;// 中断口2
int encoder_B = 20;// 中断口3
volatile int count = 0;

// 4.计数逻辑实现
void count_a(){
  // 先判断A是否跳变到高电压
  if(digitalRead(encoder_A) == HIGH){
    // 再判断B的电压
    if(digitalRead(encoder_B) == HIGH){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   } else {
    // 再判断B的电压
    if(digitalRead(encoder_B) == LOW){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   }
 }

void count_b(){
  if(digitalRead(encoder_B) == HIGH)
  {
    if(digitalRead(encoder_A) == LOW){
      count ++;
    } else {
      count --;
    }
  }
  else
  {
    if(digitalRead(encoder_A) == HIGH){
      count ++;
    } else {
      count --;
    }
  }
 }
 
void setup() {
  // put your setup code here, to run once:
  // 设置波特率
  Serial.begin(57600);
  // 2.setup中设置引脚的操作模式(INPUT)
  pinMode(encoder_A,INPUT);// 从引脚读数据
  pinMode(encoder_B,INPUT);
  // 3.为引脚添加中断函数
  // 参数1: 中断口 参数2: 回调函数 参数3: 触发时机
  attachInterrupt(2,count_a,CHANGE);// 单倍频或双倍频只需要为编码器的A相添加中断函数,CHANGE 当引脚电平发生改变时,触发中断
  attachInterrupt(3,count_b,CHANGE);// //当电平发生改变时触发中断函数
}

// 测试流程:
/*
 * 1. 封装变量 --- 开始时间、单位时间、减速比、一圈输出的脉冲数、使用的N倍频测速
 * 2. 实现逻辑
 *  2.1 获取时间时间戳(当前时间)
 *  2.2 if(当前时间 - 开始时间 >= 单位时间){
 *        // 取消中断
 *        // 计算转速(count)
 *        // count置零
 *        // 将开始时间重置为当前时间,进行重新测速
 *        // 重启中断
 *  }
 *  
 */
long start_time = millis();//一个计算周期的开始时刻,初始值为millis();
int interval_time = 50;//一个计算周期 50ms
int per_round = 90 * 11 * 4;// 四倍频,90 的 减速比,电机转1圈是11个脉冲
void get_current_vel(){
  // 获取当前的时间
  long right_now = millis();
  // 判断逝去的时间是否大于单位时间
  long past_time = right_now - start_time;
  if(past_time >= interval_time){
    // 取消中断,中断里面做着count++ 或者count--,取消中断不再做count++或者--的操作
    noInterrupts();
    // 计算转速(count),数据类型转换
    double vel = (double)count / per_round / past_time * 1000 * 60;// 转的圈数/时间*1000*60,将原来的r/ms转换成r/min
    Serial.println(vel);
    // count置零
    count = 0;
    // 将开始时间重置为当前时间,进行重新测速
    start_time = right_now;
    // 重启中断进行重新测速
    interrupts();
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  // delay(2000);
  // Serial.println(count);
  get_current_vel();//获取当前速度,通过串口输出
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NqqGOGO

你的鼓励是我学习的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值