|
楼主
查看: 1982回复: 0
发表于 2023-7-21 11:27:22
只看该作者
送药小车代码仓库:https://gitee.com/lcsc/medical_car更好的观看体验请去:https://dri8c0qdfb.feishu.cn/wiki/UjwwwO0KZii5bykPcE4cJZafnAg
送药小车立创开源平台资料:https://oshwhub.com/li-chuang-kai-fa-ban/21-dian-sai-f-ti-zhi-neng-song-yao-xiao-che
如何计算轮子转速推荐查看资料编码器计数原理与电机测速原理——多图解析
在这里,我们选用的电机是直流减速电机带霍尔编码器接口,GD32也带硬件正交编码器。
简要介绍正交编码器(Quadrature Encoder)是一种常用于测量机械设备(比如电机、滑台,伸缩杆等)旋转或线性运动的速度和位置的传感器。它具有两个输出信号通道A和B,它们分别输出正交相位差90度的脉冲信号。通过测量这些脉冲信号,可以获得设备的运动速度和方向。关于正交编码器测速,主要有三种方法:M法、T法和M/T法。
- M法(脉冲计数法):M法是一种基于脉冲计数的测速方法。在一个固定的时间间隔内,记录编码器输出的脉冲数量,然后将脉冲数量除以时间间隔以获得脉冲频率。最后,将脉冲频率转换为实际的速度值。
- 优点:
- 实现简单,只需一个计数器和一个定时器。
- 对于低速运动,可以获得较高的分辨率。
- 缺点:
- 对于高速运动,可能会出现计数溢出的问题。
- 测速结果受定时器时间间隔影响,较长的时间间隔可能导致较低的测速分辨率。
- T法(周期测量法):T法是一种基于脉冲周期测量的测速方法。通过测量两个相邻脉冲之间的时间间隔,然后将时间间隔倒数以获得脉冲频率。最后,将脉冲频率转换为实际的速度值。
- 优点:
- 对于高速运动,可以获得较高的测速分辨率。
- 测速结果实时性较好。
- 缺点:
- 实现相对复杂,需要使用输入捕获功能或外部中断来测量脉冲间隔。
- 对于低速运动,可能会出现测量精度较低的问题。
- M/T法(脉冲计数与周期测量相结合的方法):M/T法结合了M法和T法的特点,通过同时测量脉冲计数和周期来计算速度。在低速运动时,采用M法获得较高的测速分辨率;在高速运动时,采用T法获得较高的测速实时性。根据实际速度或脉冲频率切换测速方法。
- 优点:
- 适用于宽速度范围的测量,兼具M法和T法的优点。
- 既能获得较高的测速分辨率,又能保证较好的实时性。
- 缺点:
- 实现相对复杂,需要同时使用计数器、定时器和输入捕获功能或外部中断。
M法、T法和M/T法都是常用的正交编码器测速方法,它们各有优缺点。根据实际应用需求,可以选择合适的测速方法以获得最佳的测速性能。
用伪代码的方式实现一下测速代码宏定义及公共变量
- #include "gd32fxxx.h"
- #define MOTOR_PULSE_PER_REVOLUTION 13
- #define MOTOR_REDUCTION_RATIO 20
- #define MOTOR_WHEEL_STRAIGHT_MM 64.0
- #define TIME_INTERVAL_MS 20
- #define PULSE_TO_METER_FACTOR (MOTOR_WHEEL_STRAIGHT_MM * 3.1415926 / (1000 * MOTOR_PULSE_PER_REVOLUTION * MOTOR_REDUCTION_RATIO * 4))
- volatile uint32_t pulse_count = 0;
- volatile uint32_t last_pulse_time = 0;
复制代码
1. M法(脉冲计数法)
- <pre class="ace-line ace-line old-record-id-J3ggdS5dMovTHCxmCWqc4MAjnAg"><code class="language-C" data-wrap="false">void encoder_init(void) {
- // 初始化硬件正交编码器功能,使能四倍频
- }
- void timer_init(void) {
- // 初始化定时器,设置固定的时间间隔为20ms
- // 配置中断优先级和使能中断
- }
- void TIMERx_IRQHandler(void) { // 定时器中断处理函数
- // 检查定时器更新中断标志
- if (timer_interrupt_flag_get(TIMERx, TIMER_INT_FLAG_UP) != RESET) {
- // 计算速度,单位为m/s
- float speed = (float)pulse_count / (TIME_INTERVAL_MS / 1000.0) * PULSE_TO_METER_FACTOR;
- // 重置脉冲计数
- pulse_count = 0;
- // 清除定时器更新中断标志
- timer_interrupt_flag_clear(TIMERx, TIMER_INT_FLAG_UP);
- }
- }
- int main(void) {
- // 初始化编码器
- encoder_init();
- // 初始化定时器
- timer_init();
- while (1) {
- // 处理其他任务或进入低功耗模式
- }
- }</code></pre><div class="ace-line ace-line old-record-id-ZDyudgr0RolEsTxkZQAcD9e3njb"></div>
复制代码
2. T法(周期测量法)- void encoder_init(void) {
- // 初始化硬件正交编码器功能,使能四倍频
- // 初始化输入捕获,用于测量编码器A通道的脉冲周期
- // 配置中断优先级和使能中断
- }
- void TIMERx_IRQHandler(void) { // 输入捕获中断处理函数
- // 检查输入捕获中断标志
- if (timer_interrupt_flag_get(TIMERx, TIMER_INT_FLAG_CCx) != RESET) {
- // 读取当前脉冲时间
- uint32_t current_pulse_time = timer_channel_capture_value_register_read(TIMERx, TIMER_CH_x);
- // 计算脉冲周期
- uint32_t pulse_period = current_pulse_time - last_pulse_time;
- // 更新上一次脉冲时间
- last_pulse_time = current_pulse_time;
- // 计算速度,单位为m/s
- float speed = 1.0 / pulse_period * PULSE_TO_METER_FACTOR;
- // 清除输入捕获中断标志
- timer_interrupt_flag_clear(TIMERx, TIMER_INT_FLAG_CCx);
- }
- }
- int main(void) {
- // 初始化编码器
- encoder_init();
- while (1) {
- // 处理其他任务或进入低功耗模式
- }
- }
复制代码
3. M/T法
- void encoder_init(void) {
- // 初始化硬件正交编码器功能,使能四倍频
- // 初始化输入捕获,用于测量编码器A通道的脉冲周期
- // 配置中断优先级和使能中断
- }
- void timer_init(void) {
- // 初始化定时器,设置固定的时间间隔为20ms
- // 配置中断优先级和使能中断
- }
- void TIMERx_IRQHandler(void) { // 输入捕获中断处理函数
- // 检查输入捕获中断标志
- if (timer_interrupt_flag_get(TIMERx, TIMER_INT_FLAG_CCx) != RESET) {
- // 读取当前脉冲时间
- uint32_t current_pulse_time = timer_channel_capture_value_register_read(TIMERx, TIMER_CH_x);
- // 计算脉冲周期
- uint32_t pulse_period = current_pulse_time - last_pulse_time;
- // 更新上一次脉冲时间
- last_pulse_time = current_pulse_time;
- // 增加脉冲计数
- pulse_count++;
- // 清除输入捕获中断标志
- timer_interrupt_flag_clear(TIMERx, TIMER_INT_FLAG_CCx);
- }
- }
- void TIMERy_IRQHandler(void) { // 定时器中断处理函数
- // 检查定时器更新中断标志
- if (timer_interrupt_flag_get(TIMERy, TIMER_INT_FLAG_UP) != RESET) {
- // 计算速度,单位为m/s
- float speed = (float)pulse_count / (float)pulse_period * PULSE_TO_METER_FACTOR;
- // 重置脉冲计数
- pulse_count = 0;
- // 清除定时器更新中断标志
- timer_interrupt_flag_clear(TIMERy, TIMER_INT_FLAG_UP);
- }
- }
- int main(void) {
- // 初始化编码器
- encoder_init();
- // 初始化定时器
- timer_init();
- while (1) {
- // 处理其他任务或进入低功耗模式
- }
- }
复制代码
实际使用的程序实际使用中我用的是M法测速,详情请看2_code->applications->bsp_encoder.c文件。
- 在文件开头引用了一些头文件,并定义了一些宏和结构体,包括编码器状态结构体(encoder_state_t)和uMCN的主题定义(encoder_m1_topic、encoder_m2_topic)。
- 在encoder_gpio_init函数中,对编码器的GPIO进行初始化,包括设置引脚的工作模式和复用功能。
- 在encoder_timer_init函数中,对编码器的定时器进行初始化。这些定时器用于计算编码器的计数值和测量速度。函数中分别初始化了两个编码器(M1和M2)的定时器,并配置了输入捕获通道和编码器模式。还启用了定时器中断,并将相应的中断处理函数激活。
- encoder_count_timer_init函数用于初始化一个用于计数的定时器。
- M1_TIMER_IRQHANDLER和M2_TIMER_IRQHANDLER是编码器定时器的中断处理函数。在中断处理函数中,根据定时器的计数值和重载值来判断编码器的溢出情况,并更新编码器的计数值。然后清除定时器的中断标志位。
- ENCODER_COUNT_TIMER_IRQHANDLER是计数定时器的中断处理函数。在该函数中,根据定时器的中断标志位进行编码器计数值的更新,并计算编码器的速度。最后,清除计数定时器的中断标志位。
- set_encoder_struct_to_default函数用于将编码器状态结构体初始化为默认值。
- encoder_topic_echo函数是一个用于打印编码器状态的回调函数。它通过调用mcn_copy_from_hub函数从主题中获取编码器状态,并打印相关信息。
- encoder_init函数是编码器的初始化函数。在该函数中,调用了前面提到的各个初始化函数,并将编码器状态结构体初始化为默认值。然后,通过调用mcn_advertise函数将回调函数注册为主题的广播函数,以便在接收到编码器状态更新时能够打印相关信息。
- 最后的INIT_APP_EXPORT就是让GD32启动时,自动进行编码器的初始化,这是RT-Thread一个很方便的功能。
|
|