当前位置:网站首页>Arduino 电机测速
Arduino 电机测速
2022-08-04 21:11:00 【2021 Nqq】
理论
重点在于脉冲数量的计数
AB相增量式编码器测速原理
测速 = 位移(统计方波个数)/时间
- 编码器组成: A相 + B相
- A相与B相都会规律输出电信号(方波脉冲) == 低电压 + 高电压 当前电机参数: 一圈输出11个脉冲信息(每隔33°左右)
- 输出有固定规律: 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圈
- 四倍频计数
由于一直减速比为90
- 输入轴转了90圈,则编码器转了90圈
- 90 * 11 = 990(编码器转一圈得到11个脉冲)
- 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();//获取当前速度,通过串口输出
}
边栏推荐
猜你喜欢
2022年江苏省大学生电子设计竞赛(TI杯)B题 飞机 省级一等奖记录 “一个摆烂人的独白”
[2022 Hangzhou Electric Power Multi-School 5 1012 Questions Buy Figurines] Application of STL
SPSS-System Clustering Hand Calculation Practice
DGL安装教程
[Teach you to use the serial port idle interrupt of the STM32HAL library]
数电快速入门(四)(组合逻辑电路的分析以及设计的介绍)
8 年产品经验,我总结了这些持续高效研发实践经验 · 协同篇
PowerCLi 导入License到vCenter 7
DSPE-PEG-Aldehyde,DSPE-PEG-CHO,磷脂-聚乙二醇-醛基一种疏水18碳磷脂
mysql基础
随机推荐
OD-Model [6]: YOLOv2
拼多多开放平台订单信息查询接口【pdd.order.basic.list.get订单基础信息列表查询接口(根据成交时间)】代码对接教程
PCBA scheme design - kitchen voice scale chip scheme
[Teach you to use the serial port idle interrupt of the STM32HAL library]
Hands-on Deep Learning_NiN
【1403. 非递增顺序的最小子序列】
PowerCLi 导入License到vCenter 7
bracket matching
Three ways to set a specific device UWP XAML view
Big capital has begun to flee the crypto space?
如何最简单、通俗地理解爬虫的Scrapy框架?
Cryptography Series: PEM and PKCS7, PKCS8, PKCS12
dotnet 通过 WMI 获取系统安装软件
deepstream多相机显示布局
After encountering MapStruct, the conversion between PO, DTO and VO objects is no longer handwritten
Some problems with passing parameters of meta and params in routing (can be passed but not passed, empty, collocation, click to pass multiple parameters to report an error)
【2022杭电多校5 1003 Slipper】多个超级源点+最短路
数据仓库(1)什么是数据仓库,数仓有什么特点
【ubuntu20.04安装MySQL以及MySQL-workbench可视化工具】
MATLAB中readtimetable函数用法