前言:
经过三天两夜的比赛,最终我们还是取得了不错的成绩,只有第4问出了一点点问题,球没吹到最顶端。当时我们以为这个是最简单的问题,只要目标值给大点就没问题。但最终还是败在了这一问上,电压不够没吹到。抛开不谈,这次比赛确实让我学到了不少。打比赛最重要的还得是硬件要做好。我们光电机就换了4版,搭硬件花费的时间远远大于。
我会将完整代码放到我主页的资源中,需要的同学可以去下载参考参考。
其中一个是stm32的代码,另一个是串口屏的工程。
第一部分 项目概述
1.1 基本功能
(1)小球置于圆管底部,启动后3秒内控制小球向上到达BC段,并维持5秒以上。
(2)当小球维持在BC段时,用长形纸板(宽度为风机直径的三分之一)遮挡风机的进风,小球继续稳定维持在BC段。
(3)以C点的坐标为0cm、B点的坐标为15cm;用串口触摸屏设定小球的高度位置(单位:cm),启动后使小球稳定地处于指定的高度8秒以上,上下波动不超过±1cm。
(4) 小球置于圆管底部,启动后4秒内控制小球向上到达圆管顶部处A端,且不跳离,维持8秒以上。
1.2 发挥要求
(5)用串口实时显示小球的高度位置及小球维持状态的计时并用进度条提示完成进度。
(6)小球置于圆管底部,启动后 20秒内控制小球完成如下运动:向上到达 AB段并维持3.5秒,再向下到达CD段并维持3.5秒;再向上到达AB段并维持3.5秒,再向下到达CD段并维持3.5秒;再向上冲出圆管。
(7)自主发挥:在串口屏通过任意拉动滑块控制小球高度,用屏幕滑块实现滑块与小球位置的实时互连。使得小球的位置实时追踪手指的位置。
第二部分 系统方案
2.1 主控系统选择
方案一:采用stm32单片机,STM32F103mini是一款基于ARM Cortex-M 内核的STM32系列的32位的微控制器。体积小且功能强大,板载资源丰富。100ASK_STM32F103_MINI,搭载意法半导体(ST)的STM32F103处理器,STM32最经典的单片机处理器,拥有主频为72MHz的Cortex-M3微控制器内核。
方案二:采用esp32单片机,主要用于物联网(IoT)应用,具有集成的Wi-Fi和蓝牙功能,适用于智能家居、传感器网络、远程控制等场景。不适合算法控制。
STM32适用于广泛的嵌入式系统和微控制器应用,而ESP32则更适合于需要Wi-Fi和蓝牙功能的物联网应用。综上所示,stm32更适合我们的控制
2.2电源选择
方案一:锂电池是一类由锂金属或锂合金为正/负极材料、使用非水电解质溶液的电池。1912年锂金属电池最早由Gilbert N. Lewis提出并研究。20世纪70年代时,M. S. Whittingham提出并开始研究锂离子电池。由于锂金属的化学特性非常活泼,使得锂金属的加工、保存、使用,对环境要求非常高。随着科学技术的发展,锂电池已经成为了主流。锂离子电池具有寿命长,额定电压高,自放电率低和具有高功率承受力等优点。
方案二:铅酸电池是一种电极主要由铅及其氧化物制成,电解液是硫酸溶液的蓄电池。铅酸电池具有安全可靠,无自由酸,自带泄气系统,维护简单可靠性高等优点。
综合考虑,锂电池可重复充电多次使用,寿命更长更加耐用,较铅酸电池成本低且额定电压更高,故本系统中选择锂电池作为电源使用。
2.3电机选择方案
方案1:采用2万至2.7万转的大力矩直流电机。风洞控制系统使用直流电机来控制风扇有几个原因:
1. 可控性:直流电机具有较高的控制精度和灵活性。通过调节电压、电流或脉宽调制(PWM),可以精确地控制直流电机的转速和转矩,从而调节风扇产生的风速。
2. 响应速度快:直流电机具有快速启动和停止的特性,响应速度快。这对于需要频繁调节风速的风洞控制系统来说非常重要,可以快速实现所需的风速变化。
3. 高效性:直流电机通常具有较高的效率,能够将电能有效地转化为机械能。在长时间运行的风洞实验中,高效的电机可以降低能源消耗和运行成本。
4. 可靠性:直流电机结构相对简单,可靠性较高,同时具有较长的使用寿命。这对于长时间运行的风洞系统来说尤为重要,可以减少因设备故障导致的停机时间和维修成本。
5. 适应性:直流电机的尺寸和功率范围广泛,可以根据具体的风洞设计需求选择合适的电机型号和参数。同时,直流电机可以与各种控制系统和传感器集成,实现更复杂的风洞控制功能。
方案2:采用空心杯高速电机。虽然使用空心杯电机在风洞控制系统中有许多优点,但也存在一些缺点:
复杂性和成本: 空心杯电机通常需要更复杂的制造工艺和更高的成本。相比于普通的风扇,空心杯电机的设计和制造可能更为复杂,这可能导致成本上升。
2. 维护和维修困难:由于空心杯电机的设计较为复杂,其维护和维修可能会更加困难。例如,如果叶片损坏或松动,可能需要更多的时间和成本来修复。
3. 空心杯设计限制:空心杯的设计可能受到一定的限制,如叶片形状、数量和角度等。这可能会限制风扇的性能和调节范围,使其在特定实验条件下可能不够灵活或适用。
4. 噪音和振动:虽然空心杯设计可以降低风扇运行时的噪音和振动,但在某些情况下仍然可能存在。特别是在高速运行或叶片设计不当的情况下,可能会产生较大的噪音和振动。
5. 能效和性能损失:由于空心杯设计需要考虑到空气流动的复杂性,可能会导致一定程度的能效和性能损失。与简单的风扇相比,空心杯电机可能需要更多的功率来产生相同的风量。
方案3:采用直线电机。直线电机可自由控制杆的伸长和缩短,其速度为8cm每秒,最大伸缩长度为15cm,行程大。
虽然空心杯电机在风洞控制系统中具有一些优点,但也存在复杂性、成本、维护困难、设计限制、噪音振动和能效性能损失等缺点,需要在设计和选择时进行综合考虑。
综上所述,直流电机在风洞控制系统中具有较高的控制性能、响应速度、效率、可靠性和适应性,因此我们采用了大力矩的直流电机。
2.4最终系统选择
综上所述,我们设计的简易风洞控制系统由硬件和软件部分组成。硬件部分为主控MCU、测距模块、小型直流风扇、长度大于50cm的透明PC管、直流电源、被控乒乓球组成,数据实时显示模块。软件部分包括模块驱动代码,USART HMI开发软件,Keil5开发软件。主控MCU采用STM32RCT6开发板,测距模块采用激光测距模块,采用正点原子串口激光测距,PWM输出采用TB6612模块,数据显示采用串口屏,电源降压采用LM4015降压模块。
由串口激光模块实时检测乒乓球的实时位置,并实时发送给主控MCU STM32RCT6,再利用PID控制算法将返回的乒乓球坐标与目标值进行计算得出PWM输出值,通过RCT6数据通过TB6612发送给直流风机,从而达到控制乒乓球的目的。
2.5通信系统
2.5.1单片机与串口激光测距模块的通信
通信方式我们采用的是modBus工作模式,Modbus是一种通信协议,常用于工业自动化领域中设备之间的通信。在单片机与激光测距模块之间使用Modbus通信。需要确定使用的通信模式、连接物理层、了解Modbus帧结构、配置串口参数、编写代码实现通信协议,最后添加错误处理机制。这样,单片机就能和激光测距模块进行可靠的通信了。
通过实现Modbus通信协议,单片机可以与激光测距模块进行可靠的通信,并实现数据的读取和控制功能。
所用配置代码如下:
void Modbus_DataGet(void)
{
uint16_t data=0;
uint8_t i=0;
uint8_t res=0;
printf("\r\nModbus模式测试\r\n\r\n");
res = Ms53l0m_WData(Tof_Info.id,MEAUMODE_REG,HISPEED_DMODE); /*设置高速测量模式*/
if(!res) printf("测量模式设置 OK\r\n");
else
{
printf("测量模式设置 ERROR:%d\r\n",res);
}
while(1)
{
res = Ms53l0m_RData(Tof_Info.id,MEAUDATA_REG,2,&data);
if(!res)
{
printf("\r\n距离:%d mm\r\n",data);
}
delay_ms(100);
i++;
if(i==3)
{
i=0; LED0=!LED0;
}
}
}
2.5.2 单片机与USART HMI串口屏的通信
单片机与串口屏的通信也可以通过串口实现。串口屏通常是一个带有显示屏的设备,可以通过串口与单片机进行通信,实现显示、输入等功能。
通常的步骤包括:
1. 连接物理层:确保单片机的串口引脚与串口屏的串口引脚正确连接。
2. 串口参数配置: 在单片机上配置串口参数,包括波特率、数据位、停止位和校验位等,以确保与串口屏的通信参数一致。
3. 协议通信:了解串口屏的通信协议,包括命令格式、数据格式等。根据协议规范,编写单片机的代码,实现向串口屏发送命令、接收并解析串口屏返回的数据。
4. 功能实现:根据具体需求,在单片机上编写代码,实现与串口屏的交互功能,例如显示文本、图像,接收触摸输入等。
5. 错误处理:添加适当的错误处理机制,以处理通信中可能出现的错误情况,例如超时、数据校验错误等。
通过串口通信,单片机可以与串口屏进行双向数据传输,实现各种交互功能。
图2.1 串口屏与STM32通信
2.6运动部分
简易风洞控制系统由圆管、连接部与直流风机构成,如下图所示。透明PC圆管竖直放置,长度50cm,内径大于40mm且内壁平滑,小球(直径4cm黄
色乒乓球)可在其中上下运动,管体外壁有A、B、C、D等长标志线,BC段有1cm间隔的短标志线。可从圆管外部观察管内小球的位置。连接部实现风机与圆管的气密性连接,圆管底部有防止小球落入风机的细铁丝。并以适当的方式实时显示小球的高度位置及小球维持状态的计时。控制系统通过调节风机的转速实现小球在风洞中的位置控制。
在这个简易风洞控制系统中,小球在透明PC圆管内上下运动,通过调节直流风机的转速来控制小球的位置。系统主要包括圆管、连接部和直流风机。
1. 圆管:透明PC圆管竖直放置,长度50cm,内径大于40mm且内壁平滑。管体外壁有标志线,包括A、B、C、D等长标志线,BC段有1cm间隔的短标志线。这些标志线有助于观察小球在圆管内的位置。
2. 连接部:实现风机与圆管的气密性连接,确保风洞系统能够产生稳定的气流。连接部设计合适的尺寸和结构,以适配风机和圆管的连接,并防止小球意外进入风机内部。
3. 直流风机:通过调节转速来控制风洞内的气流速度,从而影响小球在圆管内的位置。直流风机应具备一定的功率和转速范围,以满足实验中不同风速条件的需求。
在运动系统中,小球的运动受到风洞内气流的影响。通过调节风机的转速,可以改变气流的速度和流向,从而控制小球在圆管内的上下运动。实时显示小球的高度位置及小球维持状态的计时,可以通过传感器或摄像头等设备来实现,并将数据反馈给控制系统,以实现对风机转速的实时调节。
整个系统的设计应考虑到气流的稳定性、小球的运动轨迹观测、连接部的气密性、安全性等因素,以确保实验结果的准确性和可靠性。
运动系统如下:
图2.2 风洞系统
第三部分 理论分析与参数计算
3.1 控制算法的论证与选择
方案一:采用模糊控制算法,模糊控制算法有许多良好的特性,它不需要事先知道对象的数学模型,具有系统响应快、超调小、过渡时间短等优点,但编程复杂,数据处理量大。
方案二:采用PID算法,按比例、积分、微分的函数关系,进行运算,将其运算结果用以输出控制。优点是控制精度高,是控制系统非常普遍的运算方法。对于本系统的控制已足够精确。
3.2 理论分析与计算
3.2.1 球的位置检测
我们采用高精度的激光测距模块不断采集球的位置信息并用串口屏实时地显示出来。本系统采用一个直流电机作为动力系统。通过当前位置信息和目标位置的差得到的差值用于闭环控制系统滚动的线路确保小球按照要求达到指定点位。
3.2.2 控制算法分析
采用PID算法来控制风机的电压及速度。开始工作后,激光测距模块实时检测小球坐标,并与之前的位置比较使得小球的状态趋向理想位置。在PID中pid分别表示小球的位置比例(P)、位置误差(I)、位置积分(D)。
P:对小球的当前位置偏差e(t)进行调整,系数越大调节速度越快,减小误差,但是过大的比例,会造成电机速度状态的突变,从而导致木板状态不稳定。
I:加入积分调节,可以消除系统的稳态误差,提高无误差度。系统的稳定性下降,动态响应变慢。
D:微分调节反应的是小球的角速度,可以预见偏差变化的趋势具有可预见性因而可以产生超前调节,加入微分调节可以改善系统的动态性能。
PID控制器由比例单元(P)、积分单元(I)和微分单元(D)组成。其PID控制器的传递函数为:
其中为PID控制器的比例,积分和微分参数。
第四部分 硬件设计与软件设计
4.1系统设计
图4-1
4.2 电路设计
4.2.1系统总体框图
简易风洞控制系统由硬件和软件部分组成。硬件部分为主控MCU、测距模块、小型直流风扇、长度大于50cm的透明PC管、直流电源、被控乒乓球组成,数据实时显示模块。软件部分包括模块驱动代码,USART HMI开发软件,Keil5开发软件。主控MCU采用STM32RCT6开发板,测距模块采用激光测距模块,采用正点原子串口激光测距,PWM输出采用TB6612模块,数据显示采用串口屏,电源降压采用LM4015降压模块,如图4-2所示:
图4-2系统总体框图
4.2.2整体电路设计
由STM32单片机控制模块、激光测距模块、串口屏显示屛模块以及直流电机模块组成的闭环控制系统,如图4-3所示:
图4-3电路设计
4.3 硬件设计
4.3.1 STM32F103开发板
STM32 开发板具有高性能、低成本、低功耗等诸多优点,广泛应用于嵌入式开发。STM32F103 在维持 STM32 家族开发板原有优点的同时,提供了充足的 GPIO 接口、高级定时器等功能,故本系统以此为核心搭建整体框架。
串口屏直接与 STM32F103 相连,显示必要的输出信息以及交互界面,降低本系统使用门槛。使用串口屏设计软件,包括输入,输出,显示等功能,简化系统硬件结构。
4.3.2 直流电机
我们使用的直流电机是3.3v到7v。之前试过的不管是3.3v直流电机还是空心杯电机效果都没供5v时精确稳定。转速在23000左右,能够实现小球的在管子中的自由移动。而且直流电机控制简单,具有高度的灵活性,运行平稳,整个系统的机械应力低,控制过程高动态。启动和调速性能好,调速范围广,过载能力强,受电磁干扰影响小。
4.3.3 tb6612控制模块
tb6612是东芝半导体的一款驱动电机的IC。一个TB6612可以驱动两个电机,每一个驱动都有两个逻辑输入引脚,一个输出引脚和一个PWM引脚。可以通过给两个逻辑输入引脚不同的电平来控制电机的运行状态,通过PWM输入引脚实现电机调速。电机调速的原理比较简单,只需要给TB6612FNG的PWM输入引脚输入10KHz的PWM波。调节占空比即可调节转速。
图4-4tb6612电路
4.3.5激光测距模块
我们采用的是atk ms53l0m激光测距模块。ATK MS53L0M激光测距模块不仅具备高精度和稳定性,而且在设计上考虑了多种应用场景的需求。其广泛的工作温度范围和优良的环境适应性,使其可以在各种恶劣的工作环境下可靠运行。此外,该模块还支持多种通信接口,如UART和I2C,方便用户与微控制器或其他设备进行连接和通信。
图4-5 atk ms53l0m激光测距模块
4.3.6串口屏
串口屏是带有串口通信的TFT彩色液晶屏显示控制模组,可以连接PLC、变频器、温控仪表、数据采集模块等外部设备,利用显示屏显示相关数据,通过触摸屏、按键、鼠标等输入单元写入参数或输入操作指令,进而实现用户与机器进行信息交互。本项目使用能够清晰展示数据变化,增强人机交互。
4.4 软件设计
本系统以STM32F103单片机为控制器,采用C语言对单片机进行编程。主程序主要起导向和决策的作用,它控制整个系统协调稳定的运作,系统各种功能主要通过调用具体的子程序来实现。
4.5 用户交互界面
我们通过串口屏来实现用户与STM32单片机的交互。如图4-6所示,通过数字键盘来设定高度,然后点击发送后将设定好的高度发送到单片机,然后单片机接收到该数据后再发送回串口屏再显示到串口屏界面,让我们确定发送并接收到数据的准确性。单片机接收到设定好的数据后开始使用PID算法控制小球,并将小球的实时高度显示到串口屏中,当设定的高度与实时高度误差在1cm以内的时候,串口屏内部的定时器会开始计时,并实时显示到串口屏的time中。
图4-6 串口屏显示
第五部分 PID调参部分经验
我们使用的是位置式PID。由于误差最大是500(mm),然后PWM初值给到5455的时候小球正好被吹起来,PWM上限是7200(看定时器的自动重装载值),就可以算出Kp的大致值为(7200-5455)/500=3.49。然后细调到在目标值上下小幅度摆动,上下误差大概在60左右。然后给Kd赋值。刚开始我们给的d值比较小,就导致一直没啥变化。后来偶然给了个比较大的值,发现效果出奇的好,直到给到35时,发现基本可以维持在目标点。但有时会有误差,于是我们继续给Ki赋值。刚开始我们的积分限幅比较大,限幅值为1000,还没有积分清零的代码,就导致刚开始误差很大时积分累加的特别大,现象就是小球会先超出目标点,然后 i 值慢慢减小,小球慢慢回到目标点。最后我们发现问题后将限幅缩小到600,完美解决问题。
PID.c
#include "pid.h"
#include "pwm.h"
#include "usart.h"
#include "motor.h"
#include "ms53l0m.h"
pid_o pid1;
int t=0;
int last_integral = 0;
void PID_Init(int set)
{
pid1.set_position=set;
pid1.Kp=Kp_Value;
pid1.Ki=Ki_Value;
pid1.Kd=Kd_Value;
pid1.error=0;
pid1.error_1 = 0;
pid1.error_2 = 0;
pid1.integral_limit=600;//积分限幅400
pid1.output_Hlimit=1000; //PID上限1000
pid1.output_Llimit=-1000;//PID下限1000
pid1.output=0;
}
void PID_Count(void)
{
float output_kp,output_ki,output_kd;
int dk1;
pid1.actual_position=high; //实际高度
pid1.error=pid1.set_position-pid1.actual_position; //当前误差
pid1.integral=last_integral+pid1.error; //I值累加
// pid1.integral = pid1.error + pid1.error_1 + pid1.error_2;
dk1=pid1.error-pid1.error_1; //d值计算
//积分限幅
if(pid1.integral > pid1.integral_limit)
pid1.integral = pid1.integral_limit;
if(pid1.integral < -pid1.integral_limit)
pid1.integral = -pid1.integral_limit;
// //积分清零
// if(pid1.error<10&&pid1.error>-10){
// t++;
// if(t>20){
// pid1.integral = 0; //到达位置,积分清0
// t=0;
// }
// }else{
// t = 0;
// }
//计算最终PID
output_kp=pid1.Kp*pid1.error;
output_ki=pid1.Ki*pid1.integral;
output_kd=pid1.Kd*dk1;
// if(output_ki > pid1.integral_limit)
// output_ki = pid1.integral_limit;
// if(output_ki < -pid1.integral_limit)
// output_ki = -pid1.integral_limit;
//
pid1.output=output_kp+output_ki+output_kd;
//PID限幅
if(pid1.output>pid1.output_Hlimit)
{
printf("已经上限幅\r\n");
pid1.output=pid1.output_Hlimit;
}else if(pid1.output<pid1.output_Llimit)
{
printf("已经下限幅\r\n");
pid1.output=pid1.output_Llimit;
}
//===============打印数据,调节代码====================
printf("高度:%d\r\n",pid1.actual_position);
printf("误差:%d ",pid1.error);
printf("积分值:%d ",pid1.integral);
printf("P = %f ",output_kp);
printf("I = %f ",output_ki);
printf("D = %f ",output_kd);
printf("PID = %f\r\n",pid1.output);
printf("\r\n");
//===============打印数据,调节代码====================
pid1.output = pid1.output+zero_x;
//记录上次误差
last_integral = pid1.integral;
pid1.error_1=pid1.error;
// pid1.error_2=pid1.error_1;
pid1.Kd=Kd_Value; //防止给予新目标值时kd突变
//输出PWM
Set_Pwm(pid1.output);
}
PID.h
#ifndef __PID_H
#define __PID_H
#include "stm32f10x.h"
#define Kp_Value 3.9//3.9
#define Ki_Value 0.3//+0.3
#define Kd_Value 35//30
#define zero_x 5455
typedef struct
{
int set_position;
int actual_position;
float Kp;
float Ki;
float Kd;
int error;
int error_1;
int error_2;
int integral;
int integral_limit;
int output_Hlimit;
int output_Llimit;
float output;
}pid_o;
extern pid_o pid1;
void PID_Init(int set);
void PID_Count(void);
#endif /* __PID_H */