当前位置:网站首页>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();//获取当前速度,通过串口输出
}
边栏推荐
- SPSS-unary regression practice
- Common methods of js's new Function()
- adb shell input keyevent 模拟按键事件
- After encountering MapStruct, the conversion between PO, DTO and VO objects is no longer handwritten
- Three ways to set a specific device UWP XAML view
- 【ubuntu20.04安装MySQL以及MySQL-workbench可视化工具】
- 【2022杭电多校5 1012题 Buy Figurines】STL的运用
- 88.(cesium之家)cesium聚合图
- 数电快速入门(一)(BCD码和三种基本逻辑运算的介绍)
- Oreo domain name authorization verification system v1.0.6 public open source version website source code
猜你喜欢
27.降维
拒绝服务攻击DDoS介绍与防范
用 Excel 爬取网络数据的四个小案例
开发deepstram的自定义插件,使用gst-dseaxmple插件进行扩充,实现deepstream图像输出前的预处理,实现图像自定义绘制图(精四)
Interviewer: How is the expired key in Redis deleted?
After encountering MapStruct, the conversion between PO, DTO and VO objects is no longer handwritten
STM32MP157A驱动开发 | 01- 板载LED作为系统心跳指示灯
3. Byte stream and character stream of IO stream
88.(cesium之家)cesium聚合图
Chapter7 : Network-Driven Drug Discovery
随机推荐
拒绝服务攻击DDoS介绍与防范
js data type, throttling/anti-shake, click event delegation optimization, transition animation
[2022 Nioke Duo School 5 A Question Don't Starve] DP
2、字符集-编码-解码
Comic | Two weeks after the boss laid me off, he hired me back and doubled my salary!
OD-Model【6】:YOLOv2
SAP ABAP OData 服务如何支持 $select 有选择性地仅读取部分模型字段值试读版
【2022杭电多校5 1003 Slipper】多个超级源点+最短路
[2022 Hangzhou Electric Multi-School 5 1003 Slipper] Multiple Super Source Points + Shortest Path
IPV6地址
LayaBox---TypeScript---Problems encountered at first contact
After the tester with 10 years of service "naked resignation" from the big factory...
3、IO流之字节流和字符流
经验分享|盘点企业进行知识管理时的困惑类型
proe和creo的区别有哪些
web漏洞扫描器-awvs
27.降维
JdbcTemplate概述和测试
Codeforces Round #811 (Div. 3)
Dotnet using WMI software acquisition system installation