文章目录

  • 理论
  • 实现
    • 电机正反转
      • motor1_control
    • 脉冲数统计
      • motor2_encoder
    • 转速计算
      • motor03_vel

理论

重点在于脉冲数量的计数

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 ++;elsecount --;
// 当B跳变到高电压时if(A == low) 正转count ++;elsecount --;
// 当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() {//两个引脚都设置为 OUTPUTpinMode(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倍频计数实现//手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比 * 2if(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/minSerial.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();//获取当前速度,通过串口输出
}

Arduino 电机测速相关推荐

  1. STM32C8T6编码器电机测速与arduino光电模块测速

    文章目录 前言 一.STM32编码器测速 定时器配置 配置四倍频 二.arduino光电测速 前言 前面也是只学习了四倍频测速的原理,并没有真正实验过,今天正好看到编码器电机了,就尝试一下 一.STM ...

  2. STM32应用开发实践教程:智能小车电机测速模块的应用开发

    3.4.1 任务分析 本任务要求设计一个可实现智能小车电机测速的应用程序,具体要点如下. ① 取一个电机作为测速对象. ② 支持按键控制,使用 4 个按键,功能描述如下:  Key1 控制电机正转, ...

  3. 二、读取编码器数值实现电机测速—1 、配置串口通信;

    读取编码器数值实现电机测速 电机系列 二.读取编码器数值实现电机测速 1 .配置串口通信: usart.h usart.c main.c 2 .配置编码器: 电机系列 二.读取编码器数值实现电机测速 ...

  4. 基于MSP430f5529 编码电机测速 接收脉冲数 PWM调速 CCS编译器 代码分析

    前言:2022年TI杯大学生电子设计竞赛,小车跟随行驶系统(C题)要求:设计一套小车跟随行驶系统,采用TI的MCU,由一辆领头小车和一辆跟随小车组成,要求小车具有循迹功能,且速度在0.3~1m/s可调 ...

  5. 07 TIM编码器和AB相电机测速

    AB相编码器 编码器分为光学和磁极. 编码器的性能指标单位:分辨率(转一圈输出多少个信号值) 精度390线的编码器:A相390个方波,B相390个方波.相位相差90°,实现4倍频,所以实现390*4= ...

  6. 寻迹小车 FOLLOWME—— 电机测速及转速控制

    寻迹小车 FOLLOWME-- 之五:电机测速及转速控制 此篇涉及电机的测速和转速控制. 寻迹小车 FollowMe -- 之五:电机测速及转速控制 作者:Hanker 前面已完成了车的主体,控制部分 ...

  7. matlab 测速电机,一种精准的电机测速方法与流程

    本发明涉及电机测速领域,特别地,涉及一种精准的电机测速方法. 背景技术: 通常对电机进行转速测量,需要在电机内部安装某些传感器,但会降低电机运行可靠性,甚至某些电机无法在内部加装传感器.目前国内外常用 ...

  8. stm32编码器电机测速(hal库)

    记录一下今天参考别人的代码实现了四个电机的测速. 编码器被广泛应用于电机测速,实现电机闭环控制.所以不论是自己做小车还是后续参加各种比赛,必须要学会编码器测速. 一.参数 编码电机其实就是一个带有编码 ...

  9. STM32 CubeMax 编码器电机测速 原理与实现

    编码器电机测速 部分参考:https://blog.csdn.net/lzzzzzzm/article/details/119416134 其他参考部分见图片水印 1. 编码器种类及原理 常见的编码器 ...

  10. STM32之增量式编码器电机测速

    STM32之增量式编码器电机测速 编码器 编码器种类 按监测原理分类 光电编码器 霍尔编码器 按输出信号分类 增量式编码器 绝对式编码器 编码器参数 分辨率 精度 最大响应频率 信号输出形式 编码器倍 ...

最新文章

  1. 基于Teamplate Workflow组件的工组流开发概述
  2. ML之FE:特征工程中的特征拼接处理(常用于横向拼接自变量特征和因变量特征)(daiding)
  3. 爱奇艺android投屏,手机爱奇艺APP怎么将视频投屏上笔记本电脑?
  4. 修改Cmder命令提示符
  5. c++中内敛函数_C/C++求职者必备 23 道面试题,一道试题一份信心
  6. 个人博客建站方案推荐
  7. Django学习笔记《一》初始化pycharm和mysql数据库及相关环境
  8. .net导出为powerpoint的一些参考代码
  9. CCF201909-4 推荐系统(100分)【模拟】
  10. mysql语句中单引号、双引号、反引号用法与区别
  11. 什么是智能DNS云解析?云解析如何实现智能解析效果?
  12. 优秀自我简介200字_优秀学生自我介绍200字范文
  13. dwcs6中php为什么浏览不了,dreamweaver CS6编辑浏览器列表无法添加浏览器的解决处理...
  14. redo synch writes在什么情况下发生
  15. 时序预测方法——指数平滑法(Holt-Winters)
  16. Python爬虫—爬取网易云音乐【热歌榜】歌曲的精彩评论(写入txt文本文件或者MySQL数据库)
  17. 【表白神器】Python超火隐藏表白图 你能看出来吗?【附源码】
  18. Wannafly挑战赛11 A B D【规律+逆元+字符串hash】
  19. kubernetes HPA使用及测试
  20. 2012年建模A题 葡萄酒的评价

热门文章

  1. 鼠标不能再Linux命令界面滚动,电脑鼠标不能上下滚动的解决方法
  2. CSS - 选择器优先级介绍
  3. Pr全套视频教程 PR 全套零基础从入门到精通视频教程
  4. 1273-宣传墙(第九届河南省ICPC省赛 --状压DP)
  5. django orm级联_django系列5.3--ORM数据库的多表操作
  6. Office 右键新建没有office选项怎么办
  7. 我的世界服务端java路径_我的世界服务端java路径咋样设置?
  8. 获取深户股市列表api_获取股票api
  9. 银行软件测试测试用例,银行测试用例设计经验总结,应该怎样去设计测试用例?...
  10. python 根据地址求经纬度 谷歌_利用google地图根据地址批量获取经纬度