我的第一辆智能车—电磁循迹智能车 提示:本文适用于初学,想完成一个基础四轮车练练手者,大佬还请勿喷,不过欢迎提出意见,有纰漏之处我将及时纠正。
注:工程代码链接已贴在文末。
文章图片
前言: 所用到的硬件平台:
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控制部分
使用的是位置式PID
文章图片
代码如下:
void Control(void)
{
static float EP,PWM,ISUM,LAST_EP,a = 0,sum,EP_H,LAST_EP_H,k = 0,y,z;
floatL_value_shuzhi,L_value,R_vlaue_shuzhi,R_value,J;
L_value_shuzhi =adc(2);
// adcbuf[0];
L_value = https://www.it610.com/article/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)所以如果是初次参加比赛建议买现成的电磁杆,可用省去很多麻烦,把精力放在代码优化上。
【智能车|电磁循迹智能车基于stm32cubeMX、HAL库—我的第一辆智能车】六、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
推荐阅读
- iot|PDF之父、Adobe联合创始人离世,乔布斯收购未果给了他第一桶金
- spring|Java最全面试题之Spring篇
- 资讯|第一位女性商业程序员玛丽库姆斯去世,享年 93 岁
- 为什么你问问题,别人都已读不回()
- #|HAL库_源码阅读
- 电路小课堂|聊聊实际使用的电源转化电路,分享一些不同场合下的转3.3V电路
- 分布式|基 Spring Boot 2.x 构建的商城系统
- 单片机原理及应用 - 指令系统
- 面向对象死了吗()