STM32寻迹智能车

上传这个是想记录一下,原文链接暂时没有,侵删。

STM32寻迹智能车

一.电磁巡线传感器

1.归一化的概念

归一化处理,由于各个电感的性能特性存在很大差异特别是电压波动范围相差较大。因此为了给算法制定统一的标准给数据处理带来方便须对A/D传感器采集来的信号做归一化处理。
具体方法是通过公式将各传感器电压值都处理成相对该传感器最大电压和最小电压使得传感器输出电压值都保持在0到100之间。

2.归一化的原因

假设不用归一化处理时,距离中线零偏差时,电感A的值是1000,偏离赛道20厘米时,电感A值是200.当赛道电源不准时,比如输出电流由100ma变成了120ma,这时,电感A在零偏差的值和偏离赛道20厘米时候的值都会变大,设分别变成了1200和240,这时,你设定的阈值会出问题了。比如,你设置在电感A值小于等于200的时候(偏差20cm)判定丢线,电流变大之后,偏差为20cm,A电感值为240,大于了200,这时本来该判丢线,却没法判丢线了,车辆路径就相应会变化

这样的处理算法,其本质其实就是在每次车跑之前,重新快速校准偏差和电感值的对应关系。

3.归一化步骤

信号归一化的方法如下:求取电压值最大的传感器位置,然后和它周围两个传感器采样值进行加权计算即可求得小车的偏差。

Sensor_Left =   analogRead(1);    //左边电感采集值
Sensor_Middle = analogRead(2);    //中间电感采集值
Sensor_Right =  analogRead(3);    //右边电感采集值
if (Sensor_Left + Sensor_Middle + Sensor_Right > 25)
{
     sum = Sensor_Left * 1 + Sensor_Middle * 50 + Sensor_Right * 99; //归一化处理
     Sensor = sum / (Sensor_Left + Sensor_Middle + Sensor_Right);    //求偏差
}

4.算术平均值滤波

通过连续记录数个传感器值,经过排序后去掉最大值与最小值,剩余数进行平均。

//算数平均值滤波
uint16_t AD_Filter(u8 ch)     
{  
	uint16_t ADC_Value[7];
	uint16_t ADC_Average[1];
	int i,j,t;
	for(i=0;i<7;i++)
    {   
    	ADC_Value[i]= Get_ADC(ch); 
    }
    
    for(i=0;i<6;i++)    
    {
    	for(j=0;j<6-i;j++)
    	{
     		 if(ADC_Value[j]>ADC_Value[j+1])
      		{
        		t=ADC_Value[j+1];
       	 		ADC_Value[j+1]=ADC_Value[j];
       	 		ADC_Value[j]=t;
      		}
		}
    }
    
    ADC_Average[0]=(uint16_t)((ADC_Value[1]+ADC_Value[2]+ADC_Value[3]+ADC_Value[4]+ADC_Value[5])/5.0);   
    return ADC_Average[0];
}

二.线性CCD

线性CCD相当于集成了一排灰度模块的传感器。

线性CCD的核心是由一行光电二极管(每个光电二极管都有各自的积分电路,此电路统称为像素)组成的感光阵列,阵列后面有一排积分电容,光电二极管在光能量冲击下产生光电流,构成有源积分电路,那么积分电容就是用来存储光能转化后的电荷。积分电容存储的电荷越多,说明前方对应的那个感光二极管采集的光强越大,当光强接近饱和时,像素点灰度趋近于全白,呈白电平。

由此可知线性 CCD提取信号是被动的接受反射回的光线,因此采集的信号易受外界环境的影响。

//初始化ccd
void  ccd_Init(void)
{    
//先初始化IO口
 	RCC->APB2ENR|=1<<2;    //使能PORTA口时钟 
	GPIOA->CRL&=0XFFFF0FFF;//PA3 anolog输入 	 
 	 
	GPIOA->CRL&=0X0FFFF0FF;//PA2   7 
	GPIOA->CRL|=0X20000200;//Pa2   7 推挽输出 2MHZ   
	//通道10/11设置			 
	RCC->APB2ENR|=1<<9;    //ADC1时钟使能	  
	RCC->APB2RSTR|=1<<9;   //ADC1复位
	RCC->APB2RSTR&=~(1<<9);//复位结束	    
	RCC->CFGR&=~(3<<14);   //分频因子清零	
	//SYSCLK/DIV2=12M ADC时钟设置为12M,ADC最大时钟不能超过14M!
	//否则将导致ADC准确度下降! 
	RCC->CFGR|=2<<14;      	 

	ADC1->CR1&=0XF0FFFF;   //工作模式清零
	ADC1->CR1|=0<<16;      //独立工作模式  
	ADC1->CR1&=~(1<<8);    //非扫描模式	  
	ADC1->CR2&=~(1<<1);    //单次转换模式
	ADC1->CR2&=~(7<<17);	   
	ADC1->CR2|=7<<17;	   //软件控制转换  
	ADC1->CR2|=1<<20;      //使用用外部触发(SWSTART)!!!	必须使用一个事件来触发
	ADC1->CR2&=~(1<<11);   //右对齐	 
	ADC1->SQR1&=~(0XF<<20);
	ADC1->SQR1&=0<<20;     //1个转换在规则序列中 也就是只转换规则序列1 			   
	//设置通道7的采样时间
	ADC1->SMPR2&=0XFFFF0FFF;//通道3采样时间清空	  
	ADC1->SMPR2|=7<<9;      //通道3  239.5周期,提高采样时间可以提高精确度	 

	ADC1->CR2|=1<<0;	    //开启AD转换器	 
	ADC1->CR2|=1<<3;        //使能复位校准  
	while(ADC1->CR2&1<<3);  //等待校准结束 			 
    //该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。 		 
	ADC1->CR2|=1<<2;        //开启AD校准	   
	while(ADC1->CR2&1<<2);  //等待校准结束
	delay_ms(1);
}		

1.图像二值化

线性 CCD 如果扫到白线 AD 值即为一个较高的电压值(接近 3v),扫到黑线即为一个较低的电压值。

为了准确判断黑线的位置以及减弱赛道上噪点的干扰,需将 CCD 采集的图像进行二值化处理,把模拟信号转换成 0 和 1 的二值信号,便于后期的数据处理。

2.识别直道

赛道引导线处于赛道两边,在进行边缘检测时(即扫描像素二值化之后 0 和 1 的跳变沿),可通过从中心向两边扫描的方式实现,从左右两边同时寻找从 1 跳变到 0的像素点位置,具体过程为第 64 个像素点向第 1 个像素点扫描,得到左边引导线的位置Error_L,第 65 个像素点向第 128个像素点扫描,得到右边引导线的位置 Error_R,Error_L和 Error_R 符号相反。从中心到两边分别取值为 64 ~ 1和 - 64 ~ - 1,赛道偏差 Error = Error_L + Error_R。

由于线性 CCD 采回的数据在左右两个边沿不准确,所以左右各略去 4 个点,使用 120 个点的数据,此时 60 即为期望的中心值。

3.识别弯道

由于小车驱动电机的反应具有滞后性, 高速行驶于弯道时容易出现第 64 个像素点与第 65 个像素点同时落在黑线上的情况。此时从中心到两边像素点都没有从 1 到 0 的跳变, Error 就为 0, 从而出现丢线的情况。

为了避免这种情况的发生, 采用浮动中心点的方法, 即扫描并不是固定的从第 64 和 65 个像素点向两边进行, 而是根据上一次的赛道偏差计算出本次扫描的起始位置。

当小车处于左转弯时扫描起始位置向左移动, 反之向右移动,这样可以很好地避免了因扫描中心点落在黑线上而导致的丢线。

未完成编程…
说一说看到其他组的现象,实战中光源不可控,ccd容错率低,比赛中不容易调试,可以考虑自备光源。

三.编码器

光电编码器是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。光电编码器是由光码盘和光电检测装置组成。光码盘是在一定直径的圆板上等分地开通若干个长方形孔。由于光电码盘与电动机同轴,电动机旋转时,检测装置检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。
霍尔编码器是一种通过磁电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。霍尔编码器是由霍尔码盘和霍尔元件组成。霍尔码盘是在一定直径的圆板上等分地布置有不同的磁极。霍尔码盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。

四倍频
通过软件的方法实现四倍频时。正常情况下使用M法测速的时候,会通过测量单位时间内A相输出的脉冲数来得到速度信息。常规的方法,只测量A相(或B相)的上升沿或者下降沿,这样就只能计数3次。而四倍频的方法是测量A相和B相编码器的上升沿和下降沿。这样在同样的时间内,可以计数12次(3个1234的循环)。这就是软件四倍频的原理。

采集编码器数据
STM32定时器初始化为编码器模式

//把TIM2初始化为编码器接口模式
void Encoder_Init_TIM2(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  
	TIM_ICInitTypeDef TIM_ICInitStructure;  
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能定时器2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PA端口时钟


	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;	//端口配置 PA0 PA1 PA2 PA3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);					      //根据设定参数初始化GPIOA

	NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;  //定时器2中断
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;  //使能IRQ通道
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级1 
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;       //响应优先级3
	NVIC_Init(&NVIC_InitStruct);

	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 预分频器 
	TIM_TimeBaseStructure.TIM_Period = ENCODER_TIM_PERIOD; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//边沿计数模式 
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);  //初始化定时器

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

	TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
	TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
	TIM_ICInit(TIM2, &TIM_ICInitStructure);//根据 TIM_ICInitStruct 的参数初始化外设	TIMx

	TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除TIM的更新标志位
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能定时器中断
	TIM_SetCounter(TIM2,0);
	TIM_Cmd(TIM2, ENABLE); //使能定时器
}

四.速度闭环控制

速度闭环控制即有速度反馈的控制,由编码器提供速度反馈。将反馈速度(即当前速度)与目标速度进行比较,目标速度大于当前速度则加大速度,目标速度与当前速度之差越大则加速度越大。

添加一个定时 10ms 读取编码器数值的函数10ms读取一次编码器(即100HZ)。电机减速比为20,电机自带的霍尔编码器精度为13,AB双相组合得到4倍频,则转1圈编码器读数为20*13*4=1040。电机转速=编码器读数*100/1040 (r/s)。由于电机本身误差,转1圈编码器读数可能不等于1040,这时可以添加误差系数。

int Velocity_FeedbackControl(int TargetVelocity, int CurrentVelocity) 
{
    int Bias; //定义相关变量 
    static int ControlVelocity, Last_bias; //静态变量,函数调用结束后其值依然存在 
    Bias=TargetVelocity-CurrentVelocity; //求速度偏差 
    ControlVelocity+=Velcity_Kp*(Bias-Last_bias)+Velcity_Ki*Bias; //增量式 PI 控制器;Velcity_Kp*(Bias-Last_bias)作用为限制加速度;Velcity_Ki*Bias,速度控制值由 Bias 不断积分得到,偏差越大加速度越大 
    Last_bias=Bias; return ControlVelocity; //返回速度控制值 
}

版权声明:本文为CSDN博主「不是很彳亍」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_52487424/article/details/121916209

生成海报
点赞 0

不是很彳亍

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

暂无评论

发表评论

相关推荐

基于51单片机的智能台灯设计

一.硬件方案 本文介绍了一种基于PWM调光的智能台灯设计。把单片机技术和PWM调光技术结合起来实现台灯光强的调节。即在不改变PWM方波周期的前提下,利用单片机控制PWM的占空比,从而来改变电压的大小实现灯光亮度的调

Arduino智能小车 ———电机驱动

L298N电机驱动代码,完成功能:前进、后退,控制时间相同。 问题:给进相同PWM数值,两边转速并不一致,无法回到原点。 int IN12;//电机1两个控

STM32蓝牙后驱小车详细制作教程

前言 忙里偷闲,出于兴趣做了一个STM32蓝牙小车,看了很多教程,要么讲的不清不楚,要么付费下载源码,实属烦人,遂写下本教程,完全开源免费&#x