提示:本文适用于初学,想完成一个基础四轮车练练手者,大佬还请勿喷,不过欢迎提出意见,有纰漏之处我将及时纠正。
注:工程代码链接已贴在文末。
所用到的硬件平台:
stm32f103c8t6,舵机,电机,L298N驱动模块,OLED,电磁杆,干簧管,基础四轮车模。
所用到软件部分:
PID算法,ADC多路采集配置DMA,OLED驱动程序,环岛处理,普通GPIO口使用。
一、舵机模块
(1)舵机,三条线路,一条GND,一条VCC,一条给予PWM波输入。
控制舵机角度主要是控制PWM波的大小,不同的PWM对应不同的角度。中间角度的占空比为7.5%。这个值上下加减对应控制舵机左右角度。
(2)一个对应的PWM确定一个对应的角度。注意的是时间周期应配为20MS,频率50HZ。
(3)在设置最大主频72MHZ的情况下,计数值设为2000-1,预分频系数设为720-1。CUBEMX注意通道使能。工程代码中可以直接操纵寄存器TIM1->CCR3 = 143。(143是我设置的中间值,正常情况下应该是150)
二、电机、L298N驱动模块
(1)电机引出的两条线路不分正负,表现出来只是正反转,之后用代码普通GPIO口控制即可。LM298N左右端子分别接电机的引脚。
(2)中间排针四个口接普通GPIO口,用于stm32单片机控制其正反转。
(3)两边的口接PWM,设置占空比控制电机的转速。
三、PID算法
add.c文件中的PID控制部分
void Control(void)
{
static float EP,PWM,ISUM,LAST_EP,a = 0,sum,EP_H,LAST_EP_H,k = 0,y,z;
float L_value_shuzhi,L_value,R_vlaue_shuzhi,R_value,J;
L_value_shuzhi =adc(2);// adcbuf[0];
L_value = adc(3);//adcbuf[1];
R_value = adc(4);//adcbuf[2];
R_vlaue_shuzhi = adc(0);//adcbuf[3];
EP = R_value - L_value;//传入PID控制器的差值
ISUM += EP; //积分
PWM = P*EP + I*ISUM + D*(EP - LAST_EP); //PID控制部分
LAST_EP = EP; //记录上一次的差值
}
传入主函数控制
void main(void)
{
while(1) //用户理想值
{
TIM1->CCR3 =143+Control(); //转向控制系统,核心部分
}
}
P:proportion,比例,就是用户期望值,与传感器返回值的差值,偏差。
I:integral,积分,记录了从某一时刻开始,所有的偏差的累积。
D:derivative,微分,是偏差的偏差,相当于偏差的导数,偏差变化的速率。
四、OLED
(1)对于OLED来说一般网上有很多相关的驱动文件,和其能调用的子函数。直接加入工程,修改一下引脚就可以调用了。使用CUBEMX配置使能硬件IIC驱动IIC。
(2)链接:https://download.csdn.net/download/cubejava/21518138
五、电磁杆
(1)电磁杆我们组硬件员是自己画板去某创开板拿回来自己制好了的,但是在使用时发现两边对称的电感值不一样,相差很大。这个情况是也许是可以用卡尔曼滤波算法解决的,但是由于我目前的技术太菜,无法实现,所以很遗憾,我们不能用自己的电磁感进行循迹,而这恰恰是这个比赛最关键的一步。
(2)我们最后只能买了某龙的电磁杆,说实话,买的电磁杆质量那叫一个好。可以直接拿来用,完全可以不用滤波,效果贼好。六个电感,我们只用了4个,两个水平的用于检测直道和弯道,两个竖直的用于检测环岛。(电感检测电磁场的变化,转化为电压—电磁感应,用多路ADC采集值传回)。
(3)所以如果是初次参加比赛建议买现成的电磁杆,可用省去很多麻烦,把精力放在代码优化上。
六、ADC多路采集配置DMA
以下为cubeMX的配置
在程序中添加以下代码即可使用
uint16_t adcbuf[5];//五路ADC存储数组。
HAL_ADCEx_Calibration_Start(&hadc1 );//开启ADC。
HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adcbuf,5);//开启五路DMA
后面使用数组数据即可,如电感一的值则是adcbuf[0],电感二的值则是adcbuf[1],以此类推。
八、干簧管(用于检测停车)
(1)提到干簧管是比较难受的(不是因为难,很简单),我真的直接裂开。比赛时干簧管没有检测到。
(2)干簧管模块一般有三条线路,一条VCC(用3.3V即可,之前我接的5V,多用几次芯片就烧坏了),一条GND,还有一条信号线,用于检测电平变化。若没有检测到永磁铁时,处于高电平,检测到永磁铁后处于低电平。程序控制的方式是用一路ADC检测其传回来的值,判断其高低电平,进而写对应的用户代码。
(3)而我当时比赛时干簧管为什么没有检测到,因为我把我的干簧管检测程序写在了子函数里,并设置了一个标志位,当检测到时标志位计数,加到我的预设值后实现相应的用户代码。思路是没有问题,但是我的子函数有问题!!!原因是每次进去我的子函数里,变量值就会被刷新为0,根本达不到我的预设值!所以根本进不去我的用户代码。
(4)比赛结束后那天晚上我一直睡不着在想为什么会检测不到,最后我发现我忘记加static,静态变量了(变量只初始化一次)。所以平时写代码要多多灵活应用static啊!static,static,static,重要的东西说三遍。
八、普通GPIO口使用
这个也不用多说了,直接在cubeMX使能配置就好了。
程序里需要调用的函数一般就三个:
HAL_GPIO_WritePin (GPIOX ,GPIO_PIN_X , x(0或1));
HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
HAL_GPIO_Toggle(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
九、环岛处理
(1)环岛处理部分我使用的是比较容易想到的思路——闭环控制,即检测到标志点则卡死程序强行打角度进入环岛,好处是程序简单,容易理解,效果明显。坏处时会出现一些偶然性,受环境影响大。
(2)如图环岛的关键点,我分为四个点,刚到,即将入,刚出,即将出。分设相应的检测值范围和标志位。
具体程序处理见工程代码。
(1)怎么说呢,这次比赛也还是算尽力了吧,一个月来天天去赛道调车,基本上都是晚上11:00左右才依依不舍离开实验室。最后比赛时轮胎打滑,速度没来得及提上去,只得了三等奖,能力还不够啊!不足的地方还很多,需要学的东西还有很多,在这里就不多说了。路漫漫其修远兮,吾将上下而求索!
(2)以上写的很多只是我自己片面的想法,而且能力有限,有不足的地方欢迎指出。
(3)最后,因为自己也是花了时间和心血一点一点才完成的工程、写出的代码,所以不太想被人白嫖,故设置了付费,还请大家理解。不过博文中大部分已经进行了详细说明,只要稍微会一点stm32应该就能独立完成整个智能车了。欲速则不达,加油!
工程代码链接:https://download.csdn.net/download/cubejava/41879499