3_如何获取电机实际输出的速度

使用PID有三个重要的参数:设定输出量、实际输出量、偏差量。

比如设定了控制速度,需要测量电机实际输出的速度以获取偏差值,从而进行反馈控制,使得控制更加精准。

〇、补一点电机的知识

在这里插入图片描述
(1)减速比:
减速比=输入速度/输出速度
(2)624线:
电机每转一圈输出624个脉冲
(3)416RPM
RMP,rotation per minute,每分钟416转(圈)

一、如何控制小车的速度

使用PWM调节占空比从而调节它的速度

比如:
设置自动重装载值为ARR=3000,
TIM_TimeBaseStructure.TIM_Period = 3000;
设置比较值CCR=2000,
TIM_SetCompare2(TIM2,2000);
设置计数模式为向上计数,
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
设置极性为高电平有效,
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
在这里插入图片描述
因此得到的输出速度是做到速度的(3000-2000)/3000=33.3%。

二、 如何解决不能使用三个定时器的问题

控制小车的运动需要一个定时器(TIM2);需要两个测量电机实际输出速度的定时器(TIM3、TIM4),也即是读取一定时间内编码器的脉冲数。
然后就会出现一个玄学问题,一旦使用三个及以上的定时器就会卡住,原因没找到,但找到了一个解决方法就是再使用一个系统滴答定时器,并且在系统定时器中断里执行程序。

1、系统滴答计时器与普通定时器的区别:

(1)功能上,滴答计时器只有最基本的计时器的功能;而普通计时器有更多的功能
(2)使用方面,滴答计时器一般是用于操作系统的计时
(3)在中断优先级方面,滴答定时器不受外界干扰,稳定的进入中断;而普通的定时器受中断优先级的约束,可以被高优先级中断打断。

2、如何使用系统滴答定时器:

(1)初始化定时器(初始化要放在最后,不然它初始化后就开始计时,放它后面的外设初始化等可能初始化不了)

void SysTick_Init(void)
{
	/* SystemFrequency / 100     10ms中断一次
	   SystemFrequency / 1000    1ms中断一次
	   SystemFrequency / 100000	 10us中断一次
	   SystemFrequency / 1000000 1us中断一次
	 */
	SystemCoreClockUpdate();

	if (SysTick_Config(SystemCoreClock/10 ))	 //0.1s中断一次
	{ 	
		while (1);
	}
	 	SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;// 开启滴答定时器 
}

(2)然后在stm32f10x_it.c中的 SysTick_Handler(void)函数中写出中断要执行的程序即可

void SysTick_Handler(void)
{		
		Motor_Left(1);
		Motor_Right(1);
	    GetMotorPulse();
}

三、使用正交编码器测量脉冲

1、正交编码器初始化代码:

(参考这位大佬的

void TIM3_PWM_Init(u16 arr,u16 psc)
{ 	
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
  TIM_ICInitTypeDef TIM_ICInitStructure;  
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能定时器3的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PA端口时钟
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOB
  
  TIM_DeInit(TIM3);
	
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频器 
  TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM向上计数  
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3
  TIM_ICStructInit(&TIM_ICInitStructure);
  TIM_ICInitStructure.TIM_ICFilter = 10;
  TIM_ICInit(TIM3, &TIM_ICInitStructure);
  TIM_ClearFlag(TIM3, TIM_FLAG_Update);//清除TIM的更新标志位
  TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
  //Reset counter
  TIM_SetCounter(TIM3,0);
  TIM_Cmd(TIM3, ENABLE);	
}

2、关于正交编码器定时器初始化的几点:

(1)使用编码器模式只能使用每个定时器的通道1(CH1)和通道2(CH2)
因为定时器在捕获时,
在这里插入图片描述
而正交编码器在计数时,使用的是TI1和TI2,其对应的信号是TI1FP1和TI2FP2,所以只能用CH1和CH2。

(2) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
这里上拉输入和浮空输入都可以,没啥大的影响。

(3) TIM_TimeBaseStructure.TIM_Period = arr;

之前学习的时候,看到很多大佬说自动重装值是编码器的线数,如果使用四倍频的话,那就是该值就是 “线数×4-1”。

后来研究了一下,没什么道理,与定时器模式不同,在编码器获取脉冲值的模式中,自动重装载值是一个上限,超过了就会溢出,如果数值太大溢出,那么我们读取的脉冲数就是错的,所以我们只要保证设置的值一定大于固定时间内的脉冲数即可。当然了,最稳妥的,难道不是65535吗?

(4) TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3

定时器使用双通道检测模式(TIM_EncoderMode_TI12)
小车电机上的编码器会输出两列脉冲波,A相波和B相波(如下图)。

定时器的编码器有三种模式:
TIM_EncoderMode_TI1在TI1的边沿计数,只计A相脉冲
TIM_EncoderMode_TI2在TI2的边沿计数,只计B相脉冲
TIM_EncoderMode_TI12在TI1和TI2的边沿计数,计A、B相脉冲

使用模式3的话测出来的数据会更准确。
在这里插入图片描述
在这里插入图片描述

使用上升沿计数模式( TIM_ICPolarity_Rising)
测量A、B两列波的上升沿,那么实际脉冲值=获得的脉冲值/2

那为什么不使用双边沿呢,使用双边沿四倍频不是更精准了?因为不知道什么问题,使用双边沿得到的脉冲值大到离谱,如下图,左边的脉冲值是上升沿检测的,右边的脉冲值是双边沿检测的😅大概40m/s,按照一般高速的120公里时速计算,每分钟就是2000米,每秒就是33米多一点,如果双边沿脉冲检测的是对的,那么这小车上个高速绰绰有余。(奇怪的知识又增加了)
在这里插入图片描述

四、数据处理

实际脉冲值:
实际脉冲值=获得的脉冲值/2

对应的速度值:
在这里插入图片描述
代码块:

//小车参数的定义
#define span 0.1    //系统定时0.1s,每0.1秒计算一次
#define dis_circle  3.14*6.5   //小车轮胎直径6.5cm
#define everyciecle_encode   624//转一圈的编码器数=编码器的线数

//读取电机脉冲
void GetMotorPulse(void)
{
	LeftMotorPulse = (short)TIM3->CNT/2;
	TIM3->CNT = 0;
    RightMotorPulse = (short)TIM4->CNT/2;
	TIM4->CNT = 0;
	
	LeftMotorSpeed=LeftMotorPulse/everyciecle_encode*dis_circle/span; //得到左车轮的速度
	RightMotorSpeed=RightMotorPulse/everyciecle_encode*dis_circle/span; //得到右车轮的速度
		
	printf("Pulse:%d  %d\r\n",LeftMotorPulse,RightMotorPulse);
	printf("Speed:%f  %f\r\n",LeftMotorSpeed,RightMotorSpeed);
}

💜附上测试程序:STM32F1.ControlDetect

版权声明:本文为CSDN博主「刘一五」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Jessaly/article/details/122477109

刘一五

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

4.8 51单片机-PCF8591(ADC/DAC)转换芯片

4.8 PCF8591(ADC/DAC)转换芯片 4.8.1 原理图 当前实验板上没有PCF8591芯片,这里采用外接模块的形式使用。 图4-8-1 PCF8591模块实物图 图4-8-2 原理图 通过原理图得到的重要信息:

基础实验——485传感器修改地址

RS-485仅是一个电气标准,描述了接口的物理层,像协议、时序、串行或并行数据以及链路全部由设计者或更高层协议定义。 RS-485定义的是使用平衡(也称作差分)多点传输线的驱动器&#xff

stm32+AMG8833+VS2015 C#上位机图像显示

设计思路 本人在电场工作,于是想做一款热成像智能控制小车,通过网上查找资料,发现AMG8833这款芯片可以说是物美价廉,基本能够实现想要的功能。因为我有一定的单片机开发基础&#xff0c