立创梁山派开发板-21年电赛F题-送药小车-如何计算小车轮子转速(GD32F4)

[复制链接]

10

主题

10

帖子

71

积分

一粒轻沙

Rank: 1

积分
71
楼主
查看: 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法都是常用的正交编码器测速方法,它们各有优缺点。根据实际应用需求,可以选择合适的测速方法以获得最佳的测速性能。
用伪代码的方式实现一下测速代码宏定义及公共变量
  1. #include "gd32fxxx.h"

  2. #define MOTOR_PULSE_PER_REVOLUTION 13
  3. #define MOTOR_REDUCTION_RATIO 20
  4. #define MOTOR_WHEEL_STRAIGHT_MM 64.0
  5. #define TIME_INTERVAL_MS 20

  6. #define PULSE_TO_METER_FACTOR (MOTOR_WHEEL_STRAIGHT_MM * 3.1415926 / (1000 * MOTOR_PULSE_PER_REVOLUTION * MOTOR_REDUCTION_RATIO * 4))

  7. volatile uint32_t pulse_count = 0;
  8. volatile uint32_t last_pulse_time = 0;
复制代码

1. M法(脉冲计数法)
  1. <pre class="ace-line ace-line old-record-id-J3ggdS5dMovTHCxmCWqc4MAjnAg"><code class="language-C" data-wrap="false">void encoder_init(void) {
  2.     // 初始化硬件正交编码器功能,使能四倍频
  3. }

  4. void timer_init(void) {
  5.     // 初始化定时器,设置固定的时间间隔为20ms
  6.     // 配置中断优先级和使能中断
  7. }

  8. void TIMERx_IRQHandler(void) { // 定时器中断处理函数
  9.     // 检查定时器更新中断标志
  10.     if (timer_interrupt_flag_get(TIMERx, TIMER_INT_FLAG_UP) != RESET) {
  11.         // 计算速度,单位为m/s
  12.         float speed = (float)pulse_count / (TIME_INTERVAL_MS / 1000.0) * PULSE_TO_METER_FACTOR;
  13.         // 重置脉冲计数
  14.         pulse_count = 0;
  15.         // 清除定时器更新中断标志
  16.         timer_interrupt_flag_clear(TIMERx, TIMER_INT_FLAG_UP);
  17.     }
  18. }

  19. int main(void) {
  20.     // 初始化编码器
  21.     encoder_init();
  22.     // 初始化定时器
  23.     timer_init();
  24.     while (1) {
  25.         // 处理其他任务或进入低功耗模式
  26.     }
  27. }</code></pre><div class="ace-line ace-line old-record-id-ZDyudgr0RolEsTxkZQAcD9e3njb"></div>
复制代码

2. T法(周期测量法)
  1. void encoder_init(void) {
  2.     // 初始化硬件正交编码器功能,使能四倍频
  3.     // 初始化输入捕获,用于测量编码器A通道的脉冲周期
  4.     // 配置中断优先级和使能中断
  5. }

  6. void TIMERx_IRQHandler(void) { // 输入捕获中断处理函数
  7.     // 检查输入捕获中断标志
  8.     if (timer_interrupt_flag_get(TIMERx, TIMER_INT_FLAG_CCx) != RESET) {
  9.         // 读取当前脉冲时间
  10.         uint32_t current_pulse_time = timer_channel_capture_value_register_read(TIMERx, TIMER_CH_x);
  11.         // 计算脉冲周期
  12.         uint32_t pulse_period = current_pulse_time - last_pulse_time;
  13.         // 更新上一次脉冲时间
  14.         last_pulse_time = current_pulse_time;

  15.         // 计算速度,单位为m/s
  16.         float speed = 1.0 / pulse_period * PULSE_TO_METER_FACTOR;
  17.         // 清除输入捕获中断标志
  18.         timer_interrupt_flag_clear(TIMERx, TIMER_INT_FLAG_CCx);
  19.     }
  20. }

  21. int main(void) {
  22.     // 初始化编码器
  23.     encoder_init();
  24.     while (1) {
  25.         // 处理其他任务或进入低功耗模式
  26.     }
  27. }
复制代码

3. M/T法
  1. void encoder_init(void) {
  2.     // 初始化硬件正交编码器功能,使能四倍频
  3.     // 初始化输入捕获,用于测量编码器A通道的脉冲周期
  4.     // 配置中断优先级和使能中断
  5. }

  6. void timer_init(void) {
  7.     // 初始化定时器,设置固定的时间间隔为20ms
  8.     // 配置中断优先级和使能中断
  9. }

  10. void TIMERx_IRQHandler(void) { // 输入捕获中断处理函数
  11.     // 检查输入捕获中断标志
  12.     if (timer_interrupt_flag_get(TIMERx, TIMER_INT_FLAG_CCx) != RESET) {
  13.         // 读取当前脉冲时间
  14.         uint32_t current_pulse_time = timer_channel_capture_value_register_read(TIMERx, TIMER_CH_x);
  15.         // 计算脉冲周期
  16.         uint32_t pulse_period = current_pulse_time - last_pulse_time;
  17.         // 更新上一次脉冲时间
  18.         last_pulse_time = current_pulse_time;
  19.         // 增加脉冲计数
  20.         pulse_count++;

  21.         // 清除输入捕获中断标志
  22.         timer_interrupt_flag_clear(TIMERx, TIMER_INT_FLAG_CCx);
  23.     }
  24. }

  25. void TIMERy_IRQHandler(void) { // 定时器中断处理函数
  26.     // 检查定时器更新中断标志
  27.     if (timer_interrupt_flag_get(TIMERy, TIMER_INT_FLAG_UP) != RESET) {
  28.         // 计算速度,单位为m/s
  29.         float speed = (float)pulse_count / (float)pulse_period * PULSE_TO_METER_FACTOR;
  30.         // 重置脉冲计数
  31.         pulse_count = 0;
  32.         // 清除定时器更新中断标志
  33.         timer_interrupt_flag_clear(TIMERy, TIMER_INT_FLAG_UP);
  34.     }
  35. }

  36. int main(void) {
  37.     // 初始化编码器
  38.     encoder_init();
  39.     // 初始化定时器
  40.     timer_init();
  41.     while (1) {
  42.         // 处理其他任务或进入低功耗模式
  43.     }
  44. }
复制代码



实际使用的程序实际使用中我用的是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一个很方便的功能。




快速回复 返回顶部 返回列表