STM32寻迹智能车
上传这个是为了记录,原文链接暂时没有,侵删。
STM32寻迹智能车
一.电磁巡线传感器
1.归一化的概念
综合处理,由于各电感的性能特性,特别是电压波动范围差异很大。因此,为了为算法制定统一的标准,方便数据处理A/D传感器采集来的信号做归一化处理。 具体方法是将每个传感器的电压值处理成最大电压和最小电压,使传感器的输出电压值保持在0到100之间。
2.归一化的原因
假设离中线零偏差时,电感A值为1000,偏离轨道20cm时,电感A值为200。当轨道电源不准时时,如输出电流为100ma变成了120ma,此时,当零偏差值和20cm偏离轨道时,电感A的值将变大,设置将分别变为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。
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; //返回速度控制值
}