stm32103ZET6使用编码器(磁电增量式)

news2025/1/14 18:15:02

这里写目录标题

  • 磁电增量式编码器介绍
  • TIM定时器(编码器接口模式)
  • 一些用到的算法
    • 均值滤波
    • 冒泡排序(从小到大)
    • 一阶低通滤波
  • 编码器测数代码
    • 编码器接口HAL库函数

正点原子的电机例程(原例程用的是stm32f407,我这里改成用stm32f103zet6)

磁电增量式编码器介绍

请添加图片描述编码器基本参数:
① 分辨率:编码器每个计数单位之间产生的距离,它是编码器可以测量到的最小的距离。对于增量式编码器,分辨率表示为编码器的转轴每旋转一圈所输出的脉冲数(PPR),也称为多少线,直流有刷电机教程中所使用的编码器是 11 线的。
② 精度:编码器分辨率和精度是两个独立的概念,精度是指编码器输出的信号数据与
实际位置之间的误差,常用角分′、角秒″表示。
③ 最大响应频率:编码器每秒能输出的最大脉冲数,单位 Hz,也称为 PPS。
④ 最大转速:指编码器机械系统所能承受的最高转速。

TIM定时器(编码器接口模式)

请添加图片描述
可以看到
当编码器正转时,A波形(高电平)在前,B波形在后
当编码器反转时,A波形(高电平)在后,B波形在前

根据下图判断计数方向与编码器信号的关系
下图有三种模式
1.仅在TI1计数(2倍频)
2.仅在TI2计数(2倍频)
3.在TI1和TI2上计数(4倍频)

注意:1、选择仅在 TI1 或者 TI2 处计数,就相当于对脉冲信号进行了 2 倍频(两个边沿),
此时如果编码器输出 10 个脉冲信号,那么就会计数 20 次。2、选择的是在 TI1 和 TI2 处均计
数,就相当于对脉冲信号进行了 4 倍频,此时如果编码器输出 10 个脉冲信号,那么就会计数
40 次。因此,我们通过计数次数来计算电机速度的时候,需要除以相应的倍频系数。

结合上图,可以看出:(此文章只研究1和2模式)
当编码器正转时,不论是在模式1或者模式2,计数值都是在增加
当编码器反转时,不论是在模式1或者模式2,计数值都是在减小

请添加图片描述
接下来我们就可以通过
一分钟内计数的变化量来计算电机的速度,具体公式如下:

电机转速 = 一分钟内计数变化量 / 倍频系数 / 编码器线数 / 减速比

这里我用的电机的参数如下:
请添加图片描述

一些用到的算法

均值滤波

例子:
十次平均值
uint long average_value()
{
for(i=0;i<=9;i++)
{
value += getvalue();
}
value /=10;
}

冒泡排序(从小到大)

例子:
if(k == 10)
{
for(i=10;i<=1;i–)
{
for(j=0;j<i-1;j++)
{
if(speed_arr[j]>speed_arr[j+1]) /* 数值比较 /
{
temp = speed_arr[j]; /
数值换位 */
speed_arr[j] = speed_arr[j+1] ;
speed_arr[j+1] = temp;
}
}
}
}

一阶低通滤波

  • 公式为:Y(n)= qX(n) + (1-q)Y(n-1),其中
  • X(n)为本次采样值;
  • Y(n-1)为上次滤波输出值;
  • Y(n)为本次滤波输值,
  • q为滤波系数,
  • q值越小则上一次输出对本次输出影响越大,整体曲线越平稳,但是对于速度变化的响应也会越慢
  • 例子:

long Res_value;//先定义一个全局变量,用来存放结果
float Lv_Bo=00001;//定义一个滤波系数
long get_shuzhi()//返回一个64位的变量
{
float last_value;
float current_value;

last_value       =  Res_value;//记录上一次的值
current_value =  getvalue();//获取当前值

if(current_value !=0)//读到正确值
{
	Res_value = last_value*Lv_Bo + (1-Lv_Bo)*current_value;
}
return Res_value ;

}

请添加图片描述
结论:
q越大,响应越快,但曲线不平滑。
q越小,响应越慢,但曲线更平滑。

编码器测数代码

编码器接口HAL库函数

请添加图片描述
代码:
bsp_motor.c(测速代码)

/*************************************    第三部分    编码器测速    ****************************************************/

Motor_TypeDef g_motor_data;  /*电机参数变量*/
ENCODE_TypeDef g_encode;     /*编码器参数变量*/

/**
 * @brief       电机速度计算
 * @param       encode_now:当前编码器总的计数值
 *              ms:计算速度的间隔,中断1ms进入一次,例如ms = 5即5ms计算一次速度
 * @retval      无
 */
void speed_computer(int32_t encode_now, uint8_t ms)
{
    uint8_t i = 0, j = 0;
    float temp = 0.0;
    static uint8_t sp_count = 0, k = 0; //ms(参数)ms计算一次,进行k要等于的次数的均值滤波(看231行),这个例程50ms计算一次,进行10次均值滤波
    static float speed_arr[10] = {0.0};                     /* 存储速度进行滤波运算 */

    if (sp_count == ms)                                     /* 计算一次速度 */
    {
        /* 计算电机转速 
           第一步 :计算ms毫秒内计数变化量
           第二步 ;计算1min内计数变化量:g_encode.speed * ((1000 / ms) * 60 ,
           第三步 :除以编码器旋转一圈的计数次数(倍频倍数 * 编码器分辨率)
           第四步 :除以减速比即可得出电机转速
        */
        g_encode.encode_now = encode_now;                                /* 取出编码器当前计数值 */
        g_encode.speed = (g_encode.encode_now - g_encode.encode_old);    /* 计算编码器计数值的变化量 */
        
        speed_arr[k++] = (float)(g_encode.speed * ((1000 / ms) * 60.0) / REDUCTION_RATIO / ROTO_RATIO );    /* 保存电机转速 */
        
        g_encode.encode_old = g_encode.encode_now;          /* 保存当前编码器的值 */

        /* 累计10次速度值,后续进行滤波*/
        if (k == 10)
        {
            for (i = 10; i >= 1; i--)                       /* 冒泡排序*/
            {
                for (j = 0; j < (i - 1); j++) 
                {
                    if (speed_arr[j] > speed_arr[j + 1])    /* 数值比较 */
                    { 
                        temp = speed_arr[j];                /* 数值换位 */
                        speed_arr[j] = speed_arr[j + 1];
                        speed_arr[j + 1] = temp;
                    }
                }
            }
            
            temp = 0.0;
            
            for (i = 2; i < 8; i++)                         /* 去除两边高低数据 */
            {
                temp += speed_arr[i];                       /* 将中间数值累加 */
            }
            
            temp = (float)(temp / 6);                       /*求速度平均值*/
            
            /* 一阶低通滤波
             * 公式为:Y(n)= qX(n) + (1-q)Y(n-1)
             * 其中X(n)为本次采样值;Y(n-1)为上次滤波输出值;Y(n)为本次滤波输出值,q为滤波系数
             * q值越小则上一次输出对本次输出影响越大,整体曲线越平稳,但是对于速度变化的响应也会越慢
             */
            g_motor_data.speed = (float)( ((float)0.48 * temp) + (g_motor_data.speed * (float)0.52) );
//						g_motor_data.speed = temp;
            k = 0;
        }
        sp_count = 0;
    }
    sp_count ++;

}

bsp_tim.h

/******************************** 3 公用部分 编码器程序 ************************************/

volatile int g_timx_encode_count = 0;                                   /* 溢出次数 */
int Encode_now = 0;
/**
 * @brief       定时器更新中断回调函数
 * @param        htim:定时器句柄指针
 * @note        此函数会被定时器中断函数共同调用的
 * @retval      无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM4)
    {
        if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim4))   /* 判断CR1的DIR位 */
        {
            g_timx_encode_count--;                                      /* DIR位为1,也就是递减计数 */
        }
        else
        {
            g_timx_encode_count++;                                      /* DIR位为0,也就是递增计数 */
        }
    }
    else if (htim->Instance == TIM6)
    {
         Encode_now = gtim_get_encode();                             /* 获取编码器值,用于计算速度 */

        speed_computer(Encode_now, 50);                                 /* 中位平均值滤除编码器抖动数据,50ms计算一次速度*/
    }
}

/**
 * @brief       获取编码器的值(当前编码器积累的值)
 * @param       无
 * @retval      编码器值
 */
int gtim_get_encode(void)
{
    return ( int32_t )__HAL_TIM_GET_COUNTER(&htim4) + g_timx_encode_count * 65536;       /* 当前计数值+之前累计编码器的值=总的编码器值 */
}

初始化的函数我就不放出来了,把我测速部分用到的HAL库的配置部分给大家看,
首先时钟树配置
请添加图片描述

定时器4来配置定时器编码器模式,用来计数编码器的脉冲数。
在这里插入图片描述
Encoder Mode 模式为Encoder Mode T1 and T12相当于4分频。
请添加图片描述

开启更新中断。

定时器6配置成基础定时器更新中断,中断时间为1ms.
计算公式:(999+1)*(71+1)/72M = 1ms
请添加图片描述

请添加图片描述
开启定时器6的中断

这里来介绍下此例程的流程:
首先初始化定时器4(编码器模式)和定时器6(基础更新中断),然后在初始化函数里开启定时器运行与开启中断。
每1ms发送一次中断(由定时器6产生),定时器4的中断回调函数里执行判断电机正转还是反转,正转就++,反转就–,50ms来计算一次电机的转速,然后
进行10次滤波,所以一共500ms来获取一次电机的转速值,又因为用了一阶低通滤波(因为电子产品本身就会存在误差,所以用这个算法)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/501595.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ClickHouse:对不同类型Join的支持

ClickHouse 是一个流行的开源实时分析数据库&#xff0c;旨在为需要在大量数据上进行超低延迟分析查询的用例提供最佳性能。为了在分析应用程序中实现最佳性能&#xff0c;通常需要将表组合在一起进行数据非规范化处理。扁平化表通过避免联接来帮助最小化查询延迟&#xff0c;以…

从零开始学【网络安全】

前言&#xff1a;网络安全如何从零开始学习&#xff0c;少走弯路&#xff1f; 目录&#xff1a; 一&#xff0c;怎么入门&#xff1f; 1、Web 安全相关概念&#xff08;2 周&#xff09;2、熟悉渗透相关工具&#xff08;3 周&#xff09;3、渗透实战操作&#xff08;5 周&…

DevData Talks | 思码逸陆春蕊:研发效能度量落地的难点与计策

本期 DevData Talks 直播活动邀请到的重磅嘉宾是思码逸高级咨询专家陆春蕊老师。陆春蕊老师曾就职于Oracle&#xff0c;在软件质量、项目管理方面有着丰富的经验&#xff0c;在思码逸为上百家客户提供了研发效能体系、数据分析、实践落地等方面的咨询。 陆春蕊老师与我们聊了聊…

QML绘图便捷接口类Convenient API

在绘制矩形时&#xff0c;我们提供了一个便捷的接口&#xff0c;而不需要调用stroke或者fill来完成。 3.import QtQuick 2.0 4. 5.Canvas { 6. id: root 7. width: 120; height: 120 8. onPaint: { 9. var ctx getContext("2d") 10. ctx.fi…

了解进程控制

目录 1、基本概念 2、操作系统内核 2.1支撑功能 2.2资源管理功能 3、进程的创建 3.1进程的层次结构 3.2进程图 3.3引起创建进程的事件 3.4进程的创建 4、进程的终止 4.1引起进程终止的事件 4.2进程的终止过程 5、进程阻塞与唤醒 5.1引起进程阻塞和唤醒的事件 5.2进…

老测试告诉你自动化测试需要考虑什么?

写在前面 这篇文章译自著名测试专家James Bach的《Test Automation Snake Oil》一文&#xff0c;是笔者在学习和研究探索性测试时偶然发现的一篇较有意义的文章&#xff0c;很好地解答了我们对自动化测试的疑惑。 比如万能的自动化测试是否可以替代一切&#xff0c;还给我们提…

什么是多相流?在熟悉工业中常见的两相及多相流的分类及特点

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

安全工程与运营

安全工程与运营 系统安全工程系统安全工程重要性安全工程系统安全工程理论基础 成立成熟度模型、系统安全工程能力成熟度模型能力成熟度模型&#xff08;Capability Maturity Model&#xff09;能力成熟度模型基本思想系统安全工程能力成熟度模型SSE-CMM的作用SSE-CMM体系结构域…

第9章:创建和管理表

一、数据库的创建修改和删除 1.SQL的分类 DDL&#xff1a;数据定义语言 create创建、alter修改、drop删除、rename重命名、truncate清空 DML&#xff1a;数据操作语言 insert、delete、update、select DCL&#xff1a;数据控制语言 commit提交、rollback回滚、savepoint保存…

Spot CEO:我们为什么选择Babylon.js而不是Three.js

为现代网络开发令人兴奋的事情之一是底层平台的快速发展。 WebAssembly、WebGL、WebGPU、Web Worker 等正在解锁以前典型 Web 产品无法想象的体验。 在过去的几年里&#xff0c;我们看到像 Figma 这样的产品利用这一点创造了极具吸引力的业务和产品。 推荐&#xff1a;用 NSDT设…

前端-01Html5基本知识

1 基本 1.1 第一个前端程序 内容 <html><head><title>我的网页</title></head><body>Hello,我的第一个网页</body> </html>使用浏览器打开 1.2 工具安装 浏览器 谷歌浏览器 清缓存 ctrlshiftdelete vscode 生成浏览器文…

cubic 的 tcp friendliness 与拐点控制

TCP CUBIC 应该是迄今为止综合表现最优秀的算法&#xff0c;其中有两个亮点&#xff0c;一个是 RTT 无关性&#xff0c;另一个是可扩展性。RTT 无关性表现在 CUBIC 的 cwnd 表达式中没有 RTT 因子&#xff0c;而可扩展性则来自于曲线本身&#xff1a; 随着 BDP 增加&#xff0…

音视频技术开发周刊 | 292

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 谷歌将 AI 芯片团队并入云计算部门 追赶微软和亚马逊 OpenAI推出的ChatGPT获得一定成功&#xff0c;微软是OpenAI的重要投资者&#xff0c;它将ChatGPT植入必应搜索&#…

【16】SCI易中期刊推荐——计算机 | 人工智能领域(中科院2区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

【IO】零拷贝、mmap、sendfile

文章目录 前言一、普通IO二、mmap三、sendfile1. Linux2.1的sendfile2. Linux2.4的sendfile 四、总结与扩展1. 结论2. 解释、扩展 参考 前言 概念&#xff1a; 没有发生CPU拷贝数据&#xff0c;都是DMA&#xff08;直接内存访问&#xff09;拷贝 优势&#xff1a; 减少内核态…

《算经》中的百钱买百鸡问题,你会做吗?试下看看(39)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 欢迎和猫妹一起&#xff0c;趣味学Python。 今日主题 你知道我国历史上有个王朝叫北魏吗&#xff1f; 北魏&#xff08;386年—534年&#xff09;&#xff0c;南北朝时期北…

HashMap 简述

文章目录 前言一、HashMap的数据结构二、HashMap存储数据的大致过程1 哈希值2 什么是哈希冲突?3 为何有两种数据结构? 三、HashMap常用知识总结 前言 HashMap 是开发中常用的一种数据结构,通常用做返回值,计算比对等,会经常用到; 一、HashMap的数据结构 jdk8之后,数据结构是…

时至今日,Pascal系列Turbo Pascal 5.0依旧是我心中永远的神

从DOS时代到Windows时代&#xff0c;从桌面应用到Web应用&#xff0c;每一个时代都有它特定的编程工具 在我看来&#xff0c;DOS时代的编程语言&#xff0c;Pascal必占一席之地。 尤其是Turbo Pascal系列的最后一个版本——Turbo Pascal 5.0&#xff0c;更是我心目中永不褪色的…

nginx企业级高性能配置优化

一、基础配置优化 1、CPU亲和性优化 1.1、推荐直接将配置项设置成auto (worker_cpu_affinity)&#xff0c;即采用了Nginx推荐的CPU绑核策略方式。 1.2、手动绑定&#xff0c;将worker线程数量与CPU核心数一一绑定方式设置&#xff0c;设置成auto Nginx会自动识别并按照推荐策略…

New Bing 全面开放?我看未必

前段时间大家应该都被ChatGPT刷屏了&#xff0c;其实就回答来说New Bing 才是最厉害的&#xff0c;因为它底层使用了ChatGPT 并且可以支持联网查询数据&#xff0c;回答中还能支持看到出处&#xff0c;方便确认其真实性。 New Bing 是微软基于 OpenAI ChatGPT 技术开发的新一代…