关于编码器测速的问题总结

最近接触到的问题用到编码器,所以还是学了一段时间这方面的知识,如果有问题欢迎各位朋友指教:
   所用的的器件为:
    STM32F407系列,电机型号为JGB37-520(带有霍尔编码器),电池为飞行电池,尤其要吐槽飞行电池的大电流烧了我几个LM298N电机驱动器,后面改成大功率驱动器。

    首先基本东西介绍:

  1. JBGB37-520是AB项的减速电机,每个编码器至少要用两个定时器。(关于ABZ的自行找资料)
    给出编码器原理:编码器原理

  2. 编码器的分类:
       按工作原理:光电式、磁电式和触点电刷式
       按码盘的刻孔方式:增量式和绝对式两类
       由于博主接触面还不是很广,一共就用过两个种类的编码器,都是属于光电编码器和霍尔编码器:一般由6线差分A+ A- B+ B- VCC 和GND;正交编码器:一般是5根线连接,信号线分别为A B Z VCC和GND
       编码器线数: 就是旋转一圈你的A(B)会输出多少个脉冲 ,这里的A B就是上面的输出脉冲信号线,它们转一圈发出的脉冲数一样的,不过存在90°相位差 通常都是360线的 线数越高代表编码器能够反应的位置精度越高

  3. 给出我在课题中用的电机参数
    电机参数
    霍尔编码器的基础原理1
    线速:线速度??? 基础脉冲11*减速比 我的理解是算出线速度然后除以减速比10,
    磁环触发极数: 我的理解是上边有个圆盘,里面有11个磁极,AB有两极所以22对极,参考图中给出的霍尔传感器原理。
    电机旋转一圈产生11个脉冲: what ’s the fuck ???是单极产生11个脉冲,还是两极一共产生11个脉冲,单片机的双边极性采集得到22或者44个脉冲!这就让我很迷,问过淘宝客服,他们也说不清白,简直了。刚开始计数计数清零,然后手动采集10多组旋转一圈的脉冲数值,发现脉冲数在214呈现正态分布,所以我最终取旋转一圈产生214个脉冲。
    后语:写到这214与22成10倍(减速比为10),这难道是巧合???希望有人解答。

  4. 关于F407定时器重装载值的的选取问题,这里我就比较迷,到目前为止看到了三种:
    (1)基于光电编码器:这里给出参考(光电编码器历程),之前看过一篇对原理介绍的更详细的博客,没有收藏。 拿360个栅格的光栅(线数)来举例子,一度一个光栅。因此AB两极,且程序通常又是双边采集,因此一个栅格会产生四个脉冲,360个栅格会产生360x4个脉冲,因此通常会写成:360x4-1,这样做的好处对求旋转的角度很方便:即编码器的旋转角度直接等于采集到的脉冲数除以4得到角度,然后微分得到角速度,然后由减速比得到车轮线速度(正比关系)。
    (2)基于霍尔编码器:参考这篇(霍尔编码器教程),也谢谢这位博客主给出了思路,我也是按照他的方法进行的测试,首先明确定时器计数器的最大计数值0XFFFF,这样才不会溢出,然后直接给到最大,我也是采用这种方式。(后面,发现其实也用不到那么多计数值)这位博客主给出的“齿数比”我也不知道怎么求,百度感觉就是减速比,但是与他的文章不符合,也问过淘宝客服,结果了无音讯———知道的朋友希望留言告知,哈哈!

        给出详细细节,也可以参考给出的博客主:F407的定时器描述
    编码器的总线位置
    (3)基于STM32CUEMX:目前用这个软件用来统计端口和查询端口,之前也研究过一段时间,会一点点,不过还是习惯于自己调库。给出这位博客主的博文:STM32CUBEMX的使用介绍,并没有介绍的很详细,只是串口输出的数据对我有启发。

  5. 代码部分:
       代码属于原来工程的下面的一部分,需要同时对车速度进行控制,所以对四个车轮的属性进行了采集,进行反馈,也利于后面换装麦克纳姆轮。代码有点乱,仅供参考

#include "encoder.h"
#include "sys.h"

 //使用的定时器
 //TIM1 3 4 8
void encoder_Init(void)
{
    GPIO_InitTypeDef         GPIO_InitStructure; 
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_ICInitTypeDef        TIM_ICInitStructure;
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//开启TIM4时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);//开启GPIOB时钟
	
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM3时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//开启GPIOB时钟
	
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//开启TIM3时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);//开启GPIOB时钟
	
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE);//开启TIM3时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);//开启GPIOB时钟
  
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource12,GPIO_AF_TIM4);//PB0引脚复用
    GPIO_PinAFConfig(GPIOD,GPIO_PinSource13,GPIO_AF_TIM4);//PB1引脚服用
	
	  GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_TIM3);//PB0引脚复用
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM3);//PB1引脚服用
	
	  GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_TIM1);//PB0引脚复用
    GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_TIM1);//PB1引脚服用
		
		GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM8);//PB0引脚复用
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM8);//PB1引脚服用
  /*--------------------------------------------------------------*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13; //GPIOD12,GPIOD13
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
    GPIO_Init(GPIOD,&GPIO_InitStructure); 
		
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB0,GPIOB1
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
    GPIO_Init(GPIOA,&GPIO_InitStructure); 
		
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_11; //GPIOB0,GPIOB1
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
    GPIO_Init(GPIOE,&GPIO_InitStructure); 
		
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB0,GPIOB1
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP ;
    GPIO_Init(GPIOC,&GPIO_InitStructure); 

  /*--------------------------------------------------------------*/
	
    TIM_TimeBaseStructure.TIM_Period = 65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值  不分频
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); 
 
    TIM_TimeBaseStructure.TIM_Period =65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值  不分频
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 
 
    TIM_TimeBaseStructure.TIM_Period =65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值  不分频
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 
		
		TIM_TimeBaseStructure.TIM_Period = 65535; //设置下一个更新事件装入活动的自动重装载寄存器周期的值
    TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值  不分频
    TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
/*--------------------------------------------------------------*/
    TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_ICFilter = 10;  //输入滤波器
    TIM_ICInit(TIM4, &TIM_ICInitStructure);
    TIM_ClearFlag(TIM4, TIM_FLAG_Update);  //清楚所有标志位
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //允许中断更新
    TIM4->CNT = 0;
    TIM_Cmd(TIM4, ENABLE);  //使能TIM3
		
		TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_ICFilter = 10;  //输入滤波器
    TIM_ICInit(TIM3, &TIM_ICInitStructure);
    TIM_ClearFlag(TIM3, TIM_FLAG_Update);  //清楚所有标志位
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //允许中断更新
    TIM3->CNT = 0;
    TIM_Cmd(TIM3, ENABLE);  //使能TIM3
		
		TIM_EncoderInterfaceConfig(TIM1, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_ICFilter = 10;  //输入滤波器
    TIM_ICInit(TIM1, &TIM_ICInitStructure);
    TIM_ClearFlag(TIM1, TIM_FLAG_Update);  //清楚所有标志位
    TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //允许中断更新
    TIM1->CNT = 0;
    TIM_Cmd(TIM1, ENABLE);  //使能TIM3
		
		TIM_EncoderInterfaceConfig(TIM8, TIM_EncoderMode_TI12,TIM_ICPolarity_BothEdge,TIM_ICPolarity_BothEdge);
    TIM_ICStructInit(&TIM_ICInitStructure);
    TIM_ICInitStructure.TIM_ICFilter = 10;  //输入滤波器
    TIM_ICInit(TIM8, &TIM_ICInitStructure);
    TIM_ClearFlag(TIM8, TIM_FLAG_Update);  //清楚所有标志位
    TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE); //允许中断更新
    TIM8->CNT = 0;
    TIM_Cmd(TIM8, ENABLE);  //使能TIM3
}


_EncoderStructure EncoderStructure;
void Read_Encoder(void)
{
int i;

	 EncoderStructure.Encoder_Value[0]=TIM1 -> CNT;	  //正数
	 EncoderStructure.Encoder_Value[1]=TIM3 -> CNT;	
	 EncoderStructure.Encoder_Value[2]=TIM4 -> CNT;	
	 EncoderStructure.Encoder_Value[3]=TIM8 -> CNT;
//清零
	TIM1 -> CNT=0;TIM3 -> CNT=0;
	TIM4 -> CNT=0;TIM8 -> CNT=0;
	
	for(i=0;i<=3;i++)
	{
		if(EncoderStructure.Encoder_Value[i]>6000)
		{
		  EncoderStructure.Encoder_Value[i]=EncoderStructure.Encoder_Value[i]-65535;  //负数
		}
	}
  // 屏蔽小波动
	for(i=0;i<=3;i++)
	{
		if(EncoderStructure.Encoder_Value[i]<=2&&EncoderStructure.Encoder_Value[i]>=-2)
		{EncoderStructure.Encoder_Value[i]=0;
		}
	}
}


 _WheelStructure  WheelStructure;

//10ms中断一次然后测速
//计数CNT  单项极对数为11对,产生11个脉冲 ,两个对极,双边检测,因此单旋转一圈产生44个脉冲
//实际测量旋转一圈旋转的脉冲数为
//217 214 216 214 214 215 214 212 212 218 取众数214


#define Dt_10ms 1e-3
void EnANWh_Velocity(void)
{
	u8 i;
  //读取编码器的值
	EncoderStructure.Encoder_Value[0]=TIM1 -> CNT; 
	EncoderStructure.Encoder_Value[1]=TIM3 -> CNT;
	EncoderStructure.Encoder_Value[2]=TIM4 -> CNT;
	EncoderStructure.Encoder_Value[3]=TIM8 -> CNT;
	
	//大端取反得负数
	for(i=0;i<=3;i++)
		{
			if(EncoderStructure.Encoder_Value[i]>6000)
			{
				EncoderStructure.Encoder_Value[i]=EncoderStructure.Encoder_Value[i]-65535;  
			}
		}	
	
	//清零
	TIM1 -> CNT=0;TIM3 -> CNT=0;
	TIM4 -> CNT=0;TIM8 -> CNT=0;
	
	// 屏蔽小波动
	for(i=0;i<=3;i++)
	{
		if(EncoderStructure.Encoder_Value[i]<=2&&EncoderStructure.Encoder_Value[i]>=-2)
		{
			EncoderStructure.Encoder_Value[i]=0;	
		}
	}
	
	
	//得到旋转的圈数
		for(i=0;i<=3;i++)
	{
	EncoderStructure.Motor_CylNum[i]=EncoderStructure.Encoder_Value[i]/214;
	
	//得到电机的角速度,车轮的角速度,减速比为10
  EncoderStructure.Motor_angVel[i]=EncoderStructure.Motor_CylNum[i]*200*pai; //单位(rad/s) 
  WheelStructure.Wheel_angVel[i]=EncoderStructure.Motor_angVel[i]/10	;	
	
  //得到车轮的线速度
  WheelStructure.Wheel_linVel[i]=WheelStructure.Wheel_angVel[i]*WheelStructure.wheel_R;
		
	//得到车轮的行驶距离
WheelStructure.Wheel_distance[i]=WheelStructure.wheel_perimeter*EncoderStructure.Motor_CylNum[i]/10;
	}
	
}

低通滤波器
//float L_filter(float value,float a)
//{
//	static float Last_value;
//	value=Last_value*(256-a)/256+a*value/256;
//	Last_value=value;
//	return value;
//}

#define TIM9_arr 839
#define TIM9_psc 999
//得到快关频率 100hz

//车轮速度专用解算定时器
//10ms中断一次

void TIM9_EnAnWh_Vel(void)
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE);  ///使能TIM2时钟
	
  TIM_TimeBaseInitStructure.TIM_Period = TIM9_arr; 	//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=TIM9_psc;  //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 

	TIM_TimeBaseInit(TIM9,&TIM_TimeBaseInitStructure);//初始化TIM7
	
	TIM_ITConfig(TIM9,TIM_IT_Update,ENABLE); //允许定时器2更新中断
	TIM_Cmd(TIM9,ENABLE); //使能定时器9
	
	NVIC_InitStructure.NVIC_IRQChannel=TIM1_BRK_TIM9_IRQn; //定时器2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; //子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
}

s16 TIM9_flag;
void  TIM1_BRK_TIM9_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM9,TIM_IT_Update)!=RESET)
    {  
			
			  EnANWh_Velocity();
			
		
			
       TIM_ClearITPendingBit(TIM9,TIM_IT_Update); 
		}
}



测得的数据如下:
测得数据
差不多就这吧!

V12/12

  • 24
    点赞
  • 137
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要避免编码器测速不准的问题,您可以考虑以下几个方法: 1. 使用固定帧率:确保输入视频的帧率与编码器的目标帧率一致。如果输入视频的帧率不稳定或者与目标帧率不匹配,编码器测速可能会出现偏差。 2. 设置适当的码率:根据视频内容和目标质量要求,选择合适的码率。过高的码率可能会导致编码器速度过慢,而过低的码率可能会导致编码器速度过快。找到一个平衡点可以提高测速的准确性。 3. 关闭硬件加速:如果您使用的是硬件加速编码器,尝试关闭它并使用软件编码器进行测试。硬件加速可能会影响编码器的性能和测速准确性。 4. 确认输入参数:确保输入视频的分辨率、帧率等参数正确。如果输入参数与实际不符,编码器测速结果可能会产生误差。 5. 多次测试取平均值:进行多次测试,并计算平均值以获得更准确的测速结果。单次测速可能会受到其他系统资源占用、运行时环境等因素的影响,通过多次测试可以减小误差。 6. 使用专业的测速工具:考虑使用专门的编码器测速工具,这些工具通常会提供更准确的测速结果,并考虑了各种因素对编码器性能的影响。例如,x264 提供了一个名为 x264_bench 的工具来进行编码器性能测试。 通过以上方法,您可以提高编码器测速的准确性,并确保得到更可靠的结果。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值