草庐IT

基于STM32cubemx的电磁寻迹智能车

dyy_hh 2023-07-15 原文

作为大一的小菜花,参加的校内智能车比赛结束了,今天为这段时间做做总结。

一.硬件部分

必需:STM32F103C6T6(或者STM32F103C8T6),舵机(MG 996R),电机(TT马达 130电机),L298n驱动,电磁杆(可以自己制作),干簧管,两节18650电池,基础四轮车模。

辅助:OLED,HC-05蓝牙模块。

二.软件部分

必需:ADC多路采集的DMA配置,定时器PWM波输出,普通GPIO口,滤波,归一化,差比和,PID算法。

辅助:OLED驱动,串口打印。

1.舵机

三根线:VCC,GND,信号线。 我们给VCC接的6V。信号线接相应PWM波输出口。

舵机调中值:可以使用编码器调节占空比,看舵机一共能够转动多少占空比的范围(注意!舵机不是可以360度旋转的)。然后取最中间的占空比输出给舵机。小菜花当时是设置的20ms为周期,Counter Period 设置的20000-1。

2.电机

开环控制两个电机:两个PWM波输出,四个普通GPIO口控制高低电平。

3.L298n驱动

可以自己去了解如何接线,这里推荐一篇小菜花看到的文章http://t.csdn.cn/Anzwy

4.干簧管

用于在终点处停车。话不多说,上链接https://share.weiyun.com/2m5eUtRv

5.电磁杆的电感值采集

我们组开始准备使用两个水平电感,两个竖直电感;但是最后由于种种原因,我们使用了两个水平电感,两个内八字电感。(最后我们环岛没有进,所以就只使用了两个水平电感完成了最基础的寻迹)。

ADC给四个电感 开了四路DMA采集 ,分别是A1,A2,A3,A4。

如果有条件可以在电磁杆中间使用第五路电感,就是小菜花开的A5(虽然我最后没有用到)。

还有两路ADC 是采集 两个干簧管的IO口 高低电平。

 

6.一些算法

软件滤波

滤波(Wave filtering)是将信号中特定波段频率滤除的操作,很大程度上保证了采集到的数据的稳定与真实,是抑制和防止干扰的一项重要措施。

这是参考的别的博主的一种滤波算法:  中位算数平均滤波 。  即结合了中位值滤波和算数平均值滤波的一种算法。

这一篇文章非常详细的讲了滤波http://t.csdn.cn/a8DbF

void Get_ADC(void)	//得到的ADC电压存储在ADC_Val中
{
	int num = 0,count = 0;
	
	for(num = 0; num < 10; num++)
	{
		HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adci,7);//开启七路DMA
		HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adcj,7);
		HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adck,7);
		for(count = 0; count < 7;count++)//取中值
		{
			if (adci[count] > adcj[count])
				{
					adctmp[count] = adci[count]; adci[count] = adcj[count]; adcj[count] = adctmp[count];
				 }
				 if (adck[count] > adcj[count]) 
					 adctmp[count] = adcj[count];
				 else if(adck[count] >adci[count]) 
					 adctmp[count] = adck[count]; 
				 else 
					adctmp[count] = adci[count];
				 sum1[count]+=adctmp[count];
		 }
	 }
	for(count = 0; count < 7;count++)
	 {    
			AD_Val[count]=sum1[count]/10; 
			sum1[count]=0;
	 }
}

归一化

关于为什么要使用归一化:我看到的很多文章说使用归一化可以提高对不同赛道的适应性。我的理解是,在不同的赛道只用去测每一路电感的最大最小值就可以正常跑了,对于特殊元素的特征值不用再去测量。大大提高了对不同赛道的适应性。

归一化的定义:将数据映射到0-1范围之内处理,可以更快速便捷地观察数据。

归一化的公式:(X - Min) /  (Max - Min).

其中 X为某一路电感 滤波后的ADC值;Min / Max为某一路电感 滤波后ADC采集到的最小 / 大值。

/*归一化后每一路ADC的值*/
uint16_t AD_left1;
uint16_t AD_left2;
uint16_t AD_right1;
uint16_t AD_right2;

/*归一化算法*/
void GuiYi_ADC(void)
{
	AD_left1 = (uint16_t) (100 * (AD_Val[1] - AD_left1_min) / (AD_left1_max - AD_left1_min));
	AD_left2 = (uint16_t) (100 * (AD_Val[2] - AD_left2_min) / (AD_left2_max - AD_left2_min));
	AD_right1 = (uint16_t) (100 * (AD_Val[3] - AD_right1_min) / (AD_right1_max - AD_right1_min));
	AD_right2 = (uint16_t) (100 * (AD_Val[4] - AD_right2_min) / (AD_right2_max - AD_right2_min));
}

可以进行适度放大(一般是放大100倍),使车能够更容易的根据电磁值判断路况。

差比和

电磁智能车 是根据电磁杆上电感 采集到的值判断路况,可以说电磁杆上的电感就是一辆电磁智能车的眼睛。差比和值能够让车更直观的判断路况。

差比和公式:(L-R)/(L+R)。差比和值范围0-1。

原理:当电感离中心磁感线越近,采集到的值就越大,反之越小。

所以当差比和值为负的时候,可以判断到车向左偏移了,为正则向右偏移了(公式里面的左右交换了则反之)。

int16_t ad_1_sum;
int16_t ad_1_diff;
double count_1;
double position_1;

/*差比和算法*/
void ChaBiHe_ADC(void)
{
	ad_1_sum = (int16_t)AD_left2 + (int16_t)AD_right1+1;
	ad_1_diff = (int16_t)AD_left2 - (int16_t)AD_right1;
	count_1 = (double)ad_1_diff  / ad_1_sum;
	position_1  = count_1 * 100;
	
	HAL_Delay(5);
}

小菜花给差比和值乘了100,这里的100可以自行修改,根据需要调整。

PID算法

小菜花采用的位置式PD控制舵机。

小菜花将差比和值乘100后(就是上段代码中的position)直接传给PD算法中作为误差error。

那么为什么可以这样呢,因为我把 差比和值为0作为目标,采集回来的差比和值作为实际值,那么误差error就是    实际值-目标值。   注意,我的目标值为0,所以error可以直接为采集回的差比和实际值。

typedef struct 
{
	double  PID_P;         /*  比例常数 */
	double  PID_D;         /*  微分常数 */
	
	
	double  LastError;          /*  前一项误差 */
	double  PrevError;          /*  前第二项误差 */
}PID;

double PID_Vertical (PID *pp)
{
	double dError1, Error1;

	Error1 = position_1;//差比和的值作为error	/*目标值为差比和值为0,所以可以直接将差比和实际所得值作为error*/

	dError1 = pp->LastError - pp->PrevError;
	pp->PrevError = pp->LastError;
    pp->LastError = Error1;
	
	PWMValue1 = pp->PID_P * Error1 + pp->PID_D * dError1;
	
	return	PWMValue1;
}

然后就开始调P和D,如果P D调得好,车就跑得很丝滑。小菜花建议可以使用分段PD来调车,亲测有效!车跑起来确实丝滑。

PD算法返回的值传给

__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,PWMValue1+745);

745是小菜花当时找到的舵机中值,PWMValue1可正可负,调节舵机可以左右转动。

7.特殊元素

十字路口

小菜花当时是用两路水平电感寻迹,到了十字路口也不会出现误判,能够正常行进。

三岔路口

当两路水平电感采集的归一化后的值  出现同时突然下降时,强制打角。

环岛检测

 小菜花是设置了三个标志位,到赛道上去采集阈值。当三个标志位都满足时,进行了强制打角(可以在此时切换为内八字电感寻迹,由内八电感的PD算法输出占空比拐弯进环岛。不推荐强制打角,会降低对不同赛道的适应性)。

然后用水平电感跑环岛。出环的时候检测两条磁感线重合的位置,到赛道测阈值,设置标志位,满足条件则强制直行,延时控制出环岛(依旧不推荐强制控制,但小菜花的能力有限,只能想到这个办法)。

出库

小菜花当时的思路是:开机就强制打角,延时控制直到出库,随后正常水平巡线。(所以出库函数只运行一遍!)

void chuKu(void)
{
	uint16_t i;
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,7000);//先低速跑电机
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,7000);
	for(i=155;i<192;i++)
	{
	Value1=i+745;
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,Value1);
		HAL_Delay(15);
	}
	
}

用了一个for循环(也可以不用,直接输出占空比)。for循环 使舵机打角时不会突然一下就转到相应角度,而是更加丝滑地转过去。

入库

干簧管经过终点磁铁,会由高电平变为低电平。因此,检测到干簧管的IO口有一个电平变化,标志位加一。在while循环里面标志位变为一的时候进行强制打角,延时控制入库,舵机转回中值,电机停止转动。

/*干簧管标志位*/
static uint16_t ganflag = 0;

if ((AD_Val[0]<300||AD_Val[6]<300) )//干簧管检测出入库
{
	ganflag++;
	HAL_Delay(100);

	if ((AD_Val[0]<300||AD_Val[6]<300) && ganflag == 1)
	{
		ruKu();
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);//电机GPIO口高低电平
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,745);
	}
}

void ruKu(void)
{
	uint16_t i;
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1,7000);//先低速跑电机
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2,7000);
	for(i=150;i<220;i++)
	{
	Value1=i+745;
	__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3,Value1);
		HAL_Delay(15);
	}
	
}

干簧管标志位检测可以放在中断里面更好,检测到一次下降沿,触发一次中断,标志位加一。

小菜花放在while循环里面会存在一个问题:过一次磁铁,标志位不只加一(我猜测是干簧管检测到一次电平变化,但是代码已经刷过好几遍了,所以标志位加的次数不定)。所以,我加了一个延时,HAL_Delay(100),试了一下,可以过一次干簧管,标志位加一。

8. 一些建议

电磁杆 

我们当时是自己做了电磁杆,但是由于其中有一路重要电感不能用,所以最终放弃,买了一个电磁杆(所以被迫使用水平+内八字电感)。   建议大家有条件的可以买一个电磁杆,不要把过多的时间都放在修电磁杆上面了!!!

OLED

可以用OLED来显示数据,观察起来很方便。 不幸的是,小菜花当时的OLED不知道为啥用不了,

插核心板上面一点反应都没有!!!

HC-05蓝牙模块

可以使用空闲中断,用蓝牙与手机通信,直接在手机上面调节PD值,十分方便。

Debug

如果你跟我一样,不幸地 OLED用不了,蓝牙串口不,打印那么就用Debug看变量的值吧!!!

文末

还有一个电脑端上位机VOFA+,推荐使用(由于小菜花能力和时间有限,没有深入了解这个VOFA+),读者可以自己去了解使用。

有关基于STM32cubemx的电磁寻迹智能车的更多相关文章

  1. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  2. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  3. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  4. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  5. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  6. ruby - 在 Rakefile 中动态生成 Rake 测试任务(基于现有的测试文件) - 2

    我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n

  7. ruby - 如何使用 Ruby 基于字母数字字符串生成颜色? - 2

    我想要像“嘿那里”这样的东西变成,例如,#316583。我希望将任意长度的字符串“归结”为十六进制颜色。我不知道从哪里开始。我在想,每个字符串的MD5散列都是不同的-但如何将该散列转换为十六进制颜色数字? 最佳答案 你可以只取几位前几位:require'digest/md5'color=Digest::MD5.hexdigest('Mytext')[0..5] 关于ruby-如何使用Ruby基于字母数字字符串生成颜色?,我们在StackOverflow上找到一个类似的问题:

  8. 玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一) - 2

    前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型

  9. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  10. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

随机推荐