[应用相关] 使用编码器获取小车速度

[复制链接]
1592|41
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:08 | 显示全部楼层 |阅读模式
本章节主要介绍编码器的原理,获取编码器脉冲值以及如何计算小车速度和路程

前言
1.软件准备:STM32CubeMx、Keil5_ MDK

2.硬件准备:STM32F103C8T6核心板、带编码器电机、TB6612电机驱动模块/L298N电机驱动、18650锂电池3节/3S航模电池、杜邦线若干

编码器简介
如图 3-1 左所示是市场上常用的编码器图片,这样编码器精度较高,但价格较高,而且体积较大,我们寻迹车用到的编码器图片如图 3-1 右所示。我们可以把编码器看做是一个能够测量小车轮子所转圈数的传感器,能够测量出轮子转的圈数便可知道小车行驶的里程。我们通过计算 1秒时间内轮子转的圈数再结合寻迹车轮子的半径, 即可计算出寻迹车的速度。获取到小车行走速度和行驶里程后我们便可通过传感器获取到的行驶里程或者小车速度控制小车的行驶里程和行驶速度。 5145764c4c8db2d8e7.png

我们用到寻迹车上的编码器一般有霍尔编码器和光电编码器,两者原理差不多,都是测量小车轮子转一圈编码器输出的脉冲数,光电编码器相比霍尔编码器精度会高些。一般我们常用的编码器都是分为 AB 双相的,双相的比单相的相比分辨率更高,而且可以获取到轮子转的方向。
7431564c4c8e8d0345.png


 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:08 | 显示全部楼层
编码器原理

如图 3-2 所示为编码器的原理,我们可以看到图中轮子转一圈 A 相输出 8 个跳边沿,单片机通过外部中断每个跳边沿计数器加 1 的话,轮子转一圈会计数到 8。如果用 AB 双相的话每个相都输出 8个跳边沿,单片机通过外部中断每个跳边沿计数器加 1 的话,轮子转一圈会计数到 8。如果用 AB 双相的话每个相都输出 8 个跳边沿,双相就输出 16 个跳边沿,精度也就会相应提高 2 倍。我们同时可以发现当 B 相上升沿的时候 A 相处于高电平状态,此时电机处于正转状态。相反,A 相上升沿的时候 B 相处于高电平状态,此时电机处于反转状态,我们便可由此判别电机的正转和反转状态。
6502564c4c8f886cfe.png
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:08 | 显示全部楼层
编码器电路
如图 3-3 所示为带编码器电机的接口图,也就是图 3-1 右所示的编码器电机,读取编码器的数据本质上就是读取编码器输出的脉冲数据, 就是通过单片机定时器和外部中断获取编码器输出的上升沿和下降沿。一般高级些的单片机内部都会有编码器读取的硬件资源,我们所使用的STM32F103C8T6 单片机就有,每个基本定时器和高级定时器都可以配置成编码器模式。如图 3-4所示,STM32F103C8T6 PA0 和 PA1 对应定时器 2 的通道 1 和通道 2,PA6 和 PA7 对应定时器 3 的通道 1 和通道 2,将定时器1 和定时器 2 配置成编码器模式即可读取到寻迹车左右两轮的速度和行驶里程。
5077164c4c9089268b.png
8969664c4c90f7df01.png
4394764c4c91618c89.png
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:09 | 显示全部楼层
编码器测速程序实现
CubeMx配置:
4659364c4c92bc79fa.png
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:09 | 显示全部楼层
PWM配置
6534764c4c93b6829d.png
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:09 | 显示全部楼层
编码器模式配置

1387064c4c947ba375.png
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:09 | 显示全部楼层
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:10 | 显示全部楼层
GPIO配置:
  1. void MX_GPIO_Init(void)
  2. {

  3.   GPIO_InitTypeDef GPIO_InitStruct = {0};

  4.   /* GPIO Ports Clock Enable */
  5.   __HAL_RCC_GPIOD_CLK_ENABLE();
  6.   __HAL_RCC_GPIOA_CLK_ENABLE();
  7.   __HAL_RCC_GPIOB_CLK_ENABLE();

  8.   /*Configure GPIO pin Output Level */
  9.   HAL_GPIO_WritePin(GPIOB, IN4_Pin|IN3_Pin|IN2_Pin|IN1_Pin, GPIO_PIN_RESET);

  10.   /*Configure GPIO pins : PBPin PBPin PBPin PBPin */
  11.   GPIO_InitStruct.Pin = IN4_Pin|IN3_Pin|IN2_Pin|IN1_Pin;
  12.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  13.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  14.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  15.   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  16. }
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:10 | 显示全部楼层
定时器配置:
  1. void MX_TIM2_Init(void)
  2. {

  3.   /* USER CODE BEGIN TIM2_Init 0 */

  4.   /* USER CODE END TIM2_Init 0 */

  5.   TIM_Encoder_InitTypeDef sConfig = {0};
  6.   TIM_MasterConfigTypeDef sMasterConfig = {0};

  7.   /* USER CODE BEGIN TIM2_Init 1 */

  8.   /* USER CODE END TIM2_Init 1 */
  9.   htim2.Instance = TIM2;
  10.   htim2.Init.Prescaler = 0;
  11.   htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  12.   htim2.Init.Period = 65535;
  13.   htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  14.   htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  15.   sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  16.   sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  17.   sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  18.   sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  19.   sConfig.IC1Filter = 0;
  20.   sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  21.   sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  22.   sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  23.   sConfig.IC2Filter = 0;
  24.   if (HAL_TIM_Encoder_Init(&htim2, &sConfig) != HAL_OK)
  25.   {
  26.     Error_Handler();
  27.   }
  28.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  29.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  30.   if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  31.   {
  32.     Error_Handler();
  33.   }
  34.   /* USER CODE BEGIN TIM2_Init 2 */

  35.   /* USER CODE END TIM2_Init 2 */

  36. }
  37. /* TIM4 init function */
  38. void MX_TIM4_Init(void)
  39. {

  40.   /* USER CODE BEGIN TIM4_Init 0 */

  41.   /* USER CODE END TIM4_Init 0 */

  42.   TIM_Encoder_InitTypeDef sConfig = {0};
  43.   TIM_MasterConfigTypeDef sMasterConfig = {0};

  44.   /* USER CODE BEGIN TIM4_Init 1 */

  45.   /* USER CODE END TIM4_Init 1 */
  46.   htim4.Instance = TIM4;
  47.   htim4.Init.Prescaler = 0;
  48.   htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  49.   htim4.Init.Period = 65535;
  50.   htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  51.   htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  52.   sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
  53.   sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  54.   sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  55.   sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  56.   sConfig.IC1Filter = 0;
  57.   sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  58.   sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  59.   sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  60.   sConfig.IC2Filter = 0;
  61.   if (HAL_TIM_Encoder_Init(&htim4, &sConfig) != HAL_OK)
  62.   {
  63.     Error_Handler();
  64.   }
  65.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  66.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  67.   if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  68.   {
  69.     Error_Handler();
  70.   }
  71.   /* USER CODE BEGIN TIM4_Init 2 */

  72.   /* USER CODE END TIM4_Init 2 */

  73. }
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:11 | 显示全部楼层
编码器程序:

电机选取加速比为1:30。编码器线数为11。

轮胎转一圈产生30x11个脉冲,单片机编码器计数器计数为30x11x4。

电机速度为电机脉冲值x单片机编码器计数器计数。
  1. /* USER CODE BEGIN PTD */
  2. #define ABS(X) (X>0) ? (X=X) : (X=-X)

  3. #define IN4(H)         if(H)\
  4.                           HAL_GPIO_WritePin(IN4_GPIO_Port,IN4_Pin,GPIO_PIN_SET);\
  5.                  else HAL_GPIO_WritePin(IN4_GPIO_Port,IN4_Pin, GPIO_PIN_RESET);
  6. #define IN3(H)  if(H)\
  7.                            HAL_GPIO_WritePin(IN3_GPIO_Port,IN3_Pin,GPIO_PIN_SET);\
  8.                  else HAL_GPIO_WritePin(IN3_GPIO_Port,IN3_Pin, GPIO_PIN_RESET);
  9. #define IN2(H)  if(H)\
  10.                            HAL_GPIO_WritePin(IN2_GPIO_Port,IN2_Pin,GPIO_PIN_SET);\
  11.                  else HAL_GPIO_WritePin(IN2_GPIO_Port,IN2_Pin, GPIO_PIN_RESET);
  12. #define IN1(H)  if(H)\
  13.                            HAL_GPIO_WritePin(IN1_GPIO_Port,IN1_Pin,GPIO_PIN_SET);\
  14.                  else HAL_GPIO_WritePin(IN1_GPIO_Port,IN1_Pin, GPIO_PIN_RESET);
  15. /* USER CODE END PTD */

  16. /* Private define ------------------------------------------------------------*/
  17. /* USER CODE BEGIN PD */
  18. /* USER CODE END PD */

  19. /* Private macro -------------------------------------------------------------*/
  20. /* USER CODE BEGIN PM */

  21. int32_t Encoder1Count = 0;
  22. int32_t Encoder2Count = 0;

  23. int64_t total_EncCount1 = 0;
  24. int64_t total_EncCount2 = 0;

  25. float Motor1Speed = 0.0;
  26. float Motor2Speed = 0.0;

  27. uint32_t uwtick_8ms_delay = 0;

  28. /* USER CODE END PM */
  29. /***********************************************************************************************
  30. 函 数 名:void Get_Encoder(void)
  31. 功    能:
  32. 说    明:获取编码器反馈 8ms采样
  33. ************************************************************************************************/

  34. void Get_Encoder(void) //获取编码器反馈
  35. {
  36.         Encoder1Count =  - (short)__HAL_TIM_GetCounter(&htim2);//获取电机脉冲值
  37.         Encoder2Count =  - (short)__HAL_TIM_GetCounter(&htim4);//获取电机脉冲值
  38.        
  39.         total_EncCount1 += Encoder1Count;        //位置累加
  40.   total_EncCount2 += Encoder2Count;
  41.        
  42.         __HAL_TIM_SetCounter(&htim2,0);                        //清除编码器值
  43.         __HAL_TIM_SetCounter(&htim4,0);                        //清除编码器值
  44.        
  45.        
  46.         Motor1Speed = (float)Encoder1Count*100/30.0/11/4;//计算速度
  47.         Motor2Speed = (float)Encoder2Count*100/30.0/11/4;

  48. }
  49. /**************************************************************************
  50. 函 数 名:void set_speed(int16_t left_speed,int16_t right_speed)
  51. 功    能:速度设定函数
  52. 入口参数:左右轮速度
  53. **************************************************************************/
  54. void set_speed(int16_t left_speed,int16_t right_speed)//设定电机转速
  55. {
  56.         if(left_speed>0)   {IN1(1);IN2(0);}
  57.         else                                                    {IN1(0);IN2(1);}
  58.         if(right_speed>0)  {IN3(0);IN4(1);}
  59.         else                                                    {IN3(1);IN4(0);}
  60.        
  61.         __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, ABS(left_speed));
  62.         __HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_4, ABS(right_speed));
  63. }
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:11 | 显示全部楼层
主程序:
  1. int main(void)
  2. {
  3.   /* USER CODE BEGIN 1 */

  4.   /* USER CODE END 1 */

  5.   /* MCU Configuration--------------------------------------------------------*/

  6.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  7.   HAL_Init();

  8.   /* USER CODE BEGIN Init */

  9.   /* USER CODE END Init */

  10.   /* Configure the system clock */
  11.   SystemClock_Config();

  12.   /* USER CODE BEGIN SysInit */

  13.   /* USER CODE END SysInit */

  14.   /* Initialize all configured peripherals */
  15.   MX_GPIO_Init();
  16.   MX_TIM2_Init();
  17.   MX_TIM4_Init();
  18.   MX_TIM1_Init();
  19.   /* USER CODE BEGIN 2 */
  20.         //开启PWM
  21.         HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  22.         HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);

  23.         //开启Encoder
  24.         HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);
  25.     HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
  26.        
  27.   /* USER CODE END 2 */

  28.   /* Infinite loop */
  29.   /* USER CODE BEGIN WHILE */
  30.   while (1)
  31.   {
  32.                 set_speed(500,500);
  33.                 if(uwTick - uwtick_8ms_delay > 8)
  34.                 {
  35.                         Get_Encoder();
  36.                         uwtick_8ms_delay = uwTick;
  37.                 }

  38.     /* USER CODE END WHILE */

  39.     /* USER CODE BEGIN 3 */
  40.   }
  41.   /* USER CODE END 3 */
  42. }
 楼主| 烟雨蒙蒙520 发表于 2023-7-29 16:11 | 显示全部楼层
具体实现
正向转动轮子,编码器脉冲读取正常,速度转换正常。
beacherblack 发表于 2023-9-9 17:29 | 显示全部楼层
单片机测速传感器如何使用?              
alvpeg 发表于 2023-9-9 18:04 | 显示全部楼层
常见的编码器类型包括光学编码器、磁编码器和霍尔编码器。
mattlincoln 发表于 2023-9-9 18:27 | 显示全部楼层
如何用编码器测速度               
qiufengsd 发表于 2023-9-9 20:05 | 显示全部楼层
如何用单片机测量光电编码器的脉冲,以实现测量电机转速?
tabmone 发表于 2023-9-9 20:14 | 显示全部楼层
通过编码器的脉冲信号,可以计算出小车的速度。脉冲信号的数量与小车轮子的旋转次数成正比,因此可以通过统计单位时间内的脉冲数量来计算速度。可以设置一个定时器,在每个固定时间间隔内统计脉冲数量,并根据脉冲数量和时间间隔来计算速度。
elsaflower 发表于 2023-9-9 21:39 | 显示全部楼层
编码器是一种将旋转运动转换为数字信号的设备,常见的编码器有增量式编码器和绝对式编码器。增量式编码器每转动一定角度输出一个脉冲信号,绝对式编码器每转动一定角度输出一个固定的信号。根据实际需求选择合适的编码器。
Henryko 发表于 2023-9-11 22:22 | 显示全部楼层
编码器测速度低速时候准吗?
geraldbetty 发表于 2023-9-12 17:03 | 显示全部楼层
编码器是将运动或转速转化为电信号的设备。你可以选择线性编码器(用于测量位移)或旋转编码器
您需要登录后才可以回帖 登录 | 注册

本版积分规则

41

主题

720

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部