第十七届智能车竞赛--极速越野组总结报告
第十七届智能车竞赛–极速越野组总结报告
文章目录
- 第十七届智能车竞赛--极速越野组总结报告
- 任务简析
- 所用模块及功能
- 整体框架和思路
- 部分源码分析
- 小车控制线程入口函数:
- PID 代码:
- 踩坑指南
- 导航方案的选择
- 坐标系的对齐问题
- MPU6050 角度问题
- PID 调试问题
- MPU6050 采样频率问题
- 小车遥控失控问题
- Flash 保存和读取经纬度
- 小车进弯侧翻
- 代码缺陷
- 总结
任务简析
本次极速越野组要求小车沿着操场赛道运行一周。
根据赛题提供的可用传感器列表,可大致锁定使用 GPS 导航或采用摄像头巡线跑法。经过简单的验证后,选择采用 GPS 作为小车的运动导航。因为摄像头在强光环境下的巡线效果十分不明显,而 GPS 则受影响较小,虽然路线误差大于使用摄像头巡线,但是稳定可靠。
所用模块及功能
本次小车所用模块如下:
- 双频 GPS 卫星定位模块:用于获取经纬度等信息
- Lora 无线串口:用于小车紧急制动
- MPU6050 六轴陀螺仪:用于检测小车姿态信息
- LCD 显示屏:显示各种信息
- SD5 模拟舵机:用于转向控制
- 有感无刷电机:提供动力
整体框架和思路
整个小车程序设计的主要框架为多传感器数据联合导航。
先将导航点信息录入单片机的 Flash ,之后在运行过程中根据小车自身位置信息逐点寻迹,也就是通过算法计算出自身位置与导航点之间的方位角信息和距离信息,得到这些信息后由 CPU 进行判断,若与导航点之间的距离小于一定值则表示这个点已到达,再切换到下一个点,直至完成一圈的导航。
以上是最初的大体方案,也是整个小车控制代码的主体框架,再此之上还加入了丢点算法,后期也移植了 RT-Thread 操作系统。以下是最后参赛的代码框架流程:
整个小车是基于 RT-Thread 操作系统的,所以整个任务是由六个线程完成。
- 初始化线程:优先级最高,在此线程中完成各模块和传感器的初始化,初始化完后会将 Flash 中的导航点信息导入数组,之后等待GPS模块传回有效信息,定位成功后再初始化舵机和电机并释放信号量,使其余线程开始运行,执行完操作后,该线程会被销毁。
- 角度读取线程:该线程优先级较高,主要负责读取 MPU6050 返回的角度信息,即俯仰角、横滚角和航向角。其中航向角用于小车行驶方向纠正,横滚角和俯仰角负责判断小车姿态,若这两个角大于一定值,则代表小车翻车,应立即刹车。
- 角度控制线程:该线程优先级较低,作用为控制舵机的转向,根据 MPU6050 传回的航向角(yaw)和设置的转向值(Turn_set)做 PID 积分,积分限幅后得到的 PWM 值输入舵机控制,由此来控制小车的转向。
- GPS 解析线程:该线程优先级较高,主要负责解析 GPS 传回的信息,即解析经纬度等。
- 遥控刹车线程:该线程优先级较高,负责接收刹车信号,接收到后立即刹车。
- 小车控制线程:该线程优先级较低,主要负责计算各种导航信息,首先是计算小车自身位置和导航点位置的距离和角度,之后与小车航向角对齐后解算出设置的转向值(Turn_set),还要判断距离信息来进行导航点的切换。其中丢点算法也在其中,当判断到已经错过了该导航点,则会自动切换到下下个导航点,防止小车掉头回到丢掉的导航点。
这六个线程的优先级不同,其中各种信息的接收线程优先级较高,即角度读取、GPS 解析、遥控刹车这三个线程的优先级较高,主要原因是为了不错漏任何一条重要信息,确保信息的及时和小车的安全。角度控制和小车整体控制这两个线程的优先级较低,主要是为了有导航信息返回时能及时中断这两个控制线程,及时更新信息来确保导航的实时性。
部分源码分析
源码 GitHub 仓库:https://github.com/hg0720/Smart_car.git
小车控制线程入口函数:
//------------------------------------------------------------------------------------------
// @brief 小车控制线程入口
// @param parameter 参数
// @return void
// Sample usage:
//------------------------------------------------------------------------------------------
void Car_thread_entry(void *parameter)
{static rt_err_t result; //信号量标志位 static int count=0; //导航点计数值float distance; //两点距离float azimuth_N; //两点方位角float last_turn=0; //上次设置的转向角result = rt_sem_take(Car_sem, RT_WAITING_FOREVER);//获取信号量,由初始化线程释放while(result == RT_EOK)//接收到一次该信号便进入while循环{distance = get_two_points_distance(gps_tau1201.latitude, gps_tau1201.longitude, test_latitude[count], test_longitude[count]);//计算距离azimuth_N = get_two_points_azimuth(gps_tau1201.latitude, gps_tau1201.longitude,test_latitude[count], test_longitude[count]);//计算方位角//两点距离小于两米则寻下一个点if ((distance <= 2)){count++;//导航点加一}/* 两次转向的差值过大且距离在10米内则判定成错过一个导航点 */else if(((Turn_set - last_turn >= 35) || (Turn_set - last_turn <= -35)) && (distance <= 10)){Turn_set = last_turn;//转角设置不变count++;//导航点加一}/* 正常寻迹 */else{last_turn = Turn_set;//保存转弯信息Turn_set = F_Angle - azimuth_N;//将方位角和航向角做差得出转角设置值}/* 进入弯道切换 PID 和速度 */if((count == Turn_Point1) || (count == Turn_Point2)){Servo_Turn_PID_Init(); //弯道 PIDMotor_Control(Turn_Speed);}/* 进入直道切换 PID 和速度 */if((count == Stright_Point1) || (count == Stright_Point2)){Servo__Stright_PID_Init(); //直道 PIDMotor_Control(Stright_Speed);}/* 导航结束则关闭舵机和电机 */if(count == Point_Num){Motor_Control(0);pwm_disable(Servo_PWM_TIM);}/* 将信息输出到 LCD 屏 */lcd_showint8(45, 7, count); //输出导航点计数lcd_showfloat(45, 8, distance, 3, 3); //输出两点距离信息lcd_showfloat(45, 9, Turn_set, 3, 3); //输出设定角度信息systick_delay_ms(10);}
}
PID 代码:
/************************************************
函数名称 : PID_Loc
功 能 : PID位置(Location)计算
参 数 : SetValue ------ 设置值(期望值)ActualValue --- 实际值(反馈值)PID ----------- PID数据结构
返 回 值 : PIDLoc -------- PID位置
作 者 : strongerHuang
*************************************************/float PID_Loc(float SetValue, float ActualValue, PID_TypeDef *PID)
{float err; //差值float PWM; //PWM输出err = SetValue - ActualValue; //设置值减去实际值为差值if(err <= -300) //判断差值是否小于等于-300{err = 360 + err; //给差值加上360}if(err >= 300) //判断差值是否大于等于 300{err = err - 360; //差值减去360}PID->Ek = err; //计算当前误差PID->LocSum += PID->Ek; //累计误差PWM = PID->Kp * PID->Ek + (PID->Ki * PID->LocSum) + PID->Kd* (PID->Ek1 - PID->Ek); //位置式 PID 控制PID->Ek1 = PID->Ek; //保存上一次偏差return PWM; //返回PWM输出值
}
踩坑指南
导航方案的选择
在选用 GPS 做导航时,还有一个非常重要的选择,就是小车自身姿态信息的获取。
这其中有两种途径来进行这个信息的获取,因为只需要进行角度的修正,所以可以用 GPS 传回来的方位角来做小车行驶方向;另一种就是利用 MPU6050 进行角速度积分来获取小车的航向角。
一开始选择的是利用 GPS 传回来的方位角,因为 MPU6050 的姿态解算过于复杂,且精度较低,噪声干扰太大。后来写出第一版程序后到操场实测,发现小车行驶轨迹诡异,而且反应很慢,之后研究发现,GPS 传回的方位角是由小车行驶的速度和经纬度信息的变化通过公式计算得出,这里面就存在一些问题,就是在小车具有一定速度之前,这个方位角晃动很大,不具有使用价值,再者便是 GPS 回传信息很慢,在这个过程中小车容易偏航。
最后还是采用了 GPS 加 MPU6050 来进行导航的方案,至于 MPU6050 角度的获取和滤波解算等,都可移植官方的 dmp 库来使用。
坐标系的对齐问题
一开始第一次下地实测的时候,发现小车总是漫无目的的乱跑,未按照导航点的轨迹行驶,后来通过解析导航点之间的角度发现,通过经纬度计算出来的角度是 WGS84 坐标系,而 MPU6050 是以车头方向为 0 度设置的载体坐标系,两个角度均不能直接使用,需要进行计算和转换。
后来翻阅了大量资料,大概了解了通过旋转矩阵来转换坐标系的方法:https://blog.csdn.net/m0_46430715/article/details/123298911?spm=1001.2014.3001.5501 ;之后经过实地操作,发现算法难度太大,于是简单粗暴的直接将 MPU6050 航向角加上小车行驶方向的方位角来对齐解算出来的方向角,发现效果还行,小车能正常行驶,但由于一些干扰会导致 GPS 信号,导致计算出的初始角度可能会有偏差,所以小车有些许摇摆或跑偏。
最后确定的方案为:小车向正北方向发车(标准操场直道均为南北朝向),这时小车自身与第一个导航点之间的方位角就为 0 度,直接与 MPU6050 的初始角度相同,以此完成坐标对齐。
MPU6050 角度问题
后续调试过程中发现,当小车行驶完半圈,在进入第二条直道时,总是会莫名开始转圈,一开始以为是丢掉了导航点,后来发现是 MPU6050 转到 180 度时,其航向角的差值过大,往左右轻微摇摆就能达到 360 度左右的差值,PID 计算出的 PWM 值传入舵机后会持续往一个方向打死,由此导致小车不停转圈。
最后将偏航角的范围改为 0-360 度,并在 PID 积分器中对较大的差值做了处理(参见上述 PID 代码),完美解决。
PID 调试问题
在 PID 参数调试过程中也是充满坎坷,在直道上跑的很稳的参数拿到弯道跑的一塌糊涂,同理,适合弯道的参数也不适合跑直道(因为刚开始是弯道和直道分开调试,主要原因是操场人太多,只能测一小段),在漫长的调试过后反而搞出了一套不伦不类的参数,效果仅仅是勉强跑完全程,而且速度很慢。
最后,调试了两套 PID 参数,一套用于直道行驶,一套用于弯道行驶,并给弯道和直道都设置了单独的速度,根据进入弯道或进入直道的导航点来进行切换,也算勉强解决了问题。
MPU6050 采样频率问题
一开始使用 MPU6050 时,都是使用的默认参数,当时的采样频率是 100Hz ,在低速时并未发现问题,后来提速过程中发现,只要转弯太快或是小车行驶过程中发生漂移,就会出现剧烈的摆尾或是大幅度震荡,直至冲出赛道。
最初出现这个问题依旧是怀疑 PID 的参数不对,经过一段时间的调参后还是会出现这个问题,于是将注意力转移到了 MPU6050 的身上,在不停改配置测试之后,终于发现,在采样值过高时,转弯过快会出现角度丢失的问题,低速时不是很明显,在转过 180 度后只会丢掉几度,但一旦速度过快,这个丢失的范围就越来越大,最严重的一次甚至丢失了 50 度!
这些丢失的角度就导致小车的行驶方向航向角错误,随着 PID 的误差积分,就会出现甩尾或是冲突赛道等现象。
最后将采样率下调到了 10Hz ,与 GPS 的采样率保持一致,小车终于能平稳运行。
小车遥控失控问题
在调试过程中常常遇到小车翻车或是撞墙的问题,于是便加入了遥控,用来控制小车紧急刹车。但是在使用过程中,发现遥控有很大概率失灵,且失灵时小车还有很大概率失控(为此付出惨痛代价)。
在一系列排查之后终于发现了问题,因为小车一开始的遥控停车是直接放在中断中进行,在接收到停车信号后会立即关闭电机和舵机的 PWM 输出,这一点从逻辑上并没有什么问题,但是小车上是移植了 RT-Thread 操作系统的,在操作系统中,中断是高于任何线程的,所以在中断中不能有过多操作,一般在中断中仅进行一些信号量的释放或是保存信息,直接在中断中执行过多操作可能会引起整个系统的混乱。
后来,仅在中断中进行了标志位置一的操作,并给停车单独创建了线程,当标志位为一就立即刹车,其余则不做处理,之后也解决了这个问题。
Flash 保存和读取经纬度
详见:https://blog.csdn.net/m0_46430715/article/details/123782816?spm=1001.2014.3001.5501
小车进弯侧翻
初步怀疑是小车自重不够且速度过快导致,可以适当增重或是减小转弯角度。
代码缺陷
由于当时是第一次使用 RT-Thread 操作系统,所以代码当中还保留了很多逻辑编程的习惯,这给整个系统带来了一些不稳定的安全隐患和隐形 BUG :
- 在程序中使用了大量全局变量,且在写入和读取时未对资源做出保护,在使用系统时应当避免使用大量全局变量,就算使用了,也需要进行互斥来保护这些共享资源。
- 对整个系统线程的优先级和时间片划分还有待商榷,对信号量、互斥量等 IPC 机制的使用混乱。
总结
本次大赛取得了区二等奖的成绩,没能冲进国赛还是很遗憾的,主要还是水平不够吧,但是在做车的过程中学会了很多,也对嵌入式操作系统有了一定的概念,写下这篇文章算是对本次大赛的一次总结反思吧,进国赛的愿望就托付给各位学弟学妹了,大家共勉!
第十七届智能车竞赛--极速越野组总结报告相关推荐
- 17届智能车竞赛-极速越野 | 山东大学(威海)-越野三队
学校:山东大学(威海) 队伍名称:越野三队 参赛队员:郑睿.茅陈昕.余海波 带队教师:王小利刘萍萍 01 引 言 第十七届全国大学生智能车竞赛将于 2022年七至八月 ...
- 无刷电机驱动复习--智能车竞赛极速越野组复盘(1)
本篇博文的主要目的一方面是为我之后的答辩提供参考,另一方面是为现在正在参加智能车竞赛的同学们提供一个参考.首先先对无刷电机进行介绍,然后会讲一讲具体的驱动原理,最后讲一讲其他的驱动方式.语言方面可能不 ...
- 第十七届智能车竞赛LOGO设计
简 介: 在(2021-12-27)进行的修改和确认.在上述第三种方案基础上,给出了选择,修改意见. 关键词: 智能车竞赛,LOG #mermaid-svg-1MVTFcNiESAORYcp .lab ...
- 测试第十七届智能车竞赛信标灯-初步测试
简 介: 本文对于来自于龙邱通过对于来自LQ发送过来的新版的信标灯进行测试,可以看到: 它可以与以往的信标灯控制器进行兼容: 通过光电检测,可以检测到上面经过的车模物体:无线车模上面安装磁铁. 关键词 ...
- 智能车竞赛秘书处与RT-Thread关于第十七届智能车竞赛第一次会议
§01 网络会议 一.背景介绍 全国大学生智能车 在第十六届竞赛中 与 RT-Thread 进行了合作,做众多比赛MCU平台上,鼓励参赛同学们在智能嵌入式设计中引入 嵌入式OS系统,来提高设计系统 ...
- 第十六届智能车竞赛视觉AI组相关议题讨论
简 介: 本文记录了在2021-7-5上午与逐飞就第十六届智能车竞赛视觉AI组比赛的相关议题进行讨论的过程. 关键词: 智能车竞赛,视觉AI,逐飞 §01 讨论议题 一.议题列表 1.AI视觉组比赛流 ...
- 第十七届智能车竞赛何时开始呀?
§01 十七届何时开始? 卓大大,在想是否可以来一个17届的前瞻?简单的对各组别超前预告一下今年留有一些遗憾,已经等不及冲击十七届啦! 卓大大好,我是一个马上大三的学生了.今年参加的基础四轮, ...
- 第十七届智能车竞赛英飞凌专题培训 ——英飞凌无刷电机驱动专题
§01 无刷电机驱动 第十七届全国大学生智能汽车竞赛已开启,根据规则,四轮摄像头组.无线充电组.平衡单车组及全模型组都需要采用英飞凌AURIX™系列MCU作为主控,其中平衡单车组允许增加动量轮,动 ...
- 第十七届智能车竞赛个分赛区承办学校
简 介: 第十七届全国大学生智能车竞赛各分赛区承办学校相关信息. 关键词: 智能车竞赛,第十七届,承办学校 §01 分赛区承办学校 全国大学生智能车竞赛分为五个分赛区.三个省赛区,分别为:东北赛区 ...
最新文章
- javascript-数据类型,json与数组,获取非行间样式
- C#之Action和Func的用法(转自 https://www.cnblogs.com/LipeiNet/p/4694225.html)
- 能赢球只拿12分也行 麦蒂明言不和姚明争老大
- excel只对筛选后的结果单独用公式
- ASP.NET 2.0(C#) - Themes(主题)
- Java中UDP协议的基本原理和简单用法
- 帝国cms 打开打开转换表文件失败!
- 创龙28377d历程_C28x系列的28069、28377D的PWM使用经验
- 为Eclipse安装主题插件
- centos7.2 开发 部署 .net core
- mysql支持啥系统_快速的掌握MySQL支持的操作系统
- 训练集,验证集,测试集
- rgmii clock delay
- 第044篇:VBA之三种单元格引用小结(Range、Cells、[单元格区域])
- python爬取xici的免费代理、并验证(重点、清楚)
- JNI:Fatal signal 11 (SIGSEGV) at 0x00000008 (code=1)错误的一点认识
- elasticsearch数据迁移
- Java数据结构---hashMap
- win10安装开启telnet服务及使用
- 你们知道第一个发Nature的中国人是谁吗?