给大家传福利了。。后续继续共享基于Arduino/安卓Android/树莓派raspberry等平台的两轮车设计
这是我前年暑假做的一个小项目,今年拿出来整理了下当毕设了(当然毕设里添加了许多其他元素,比如Arduino/安卓Android/树莓派raspberry等),现在共享出来给大家,有视频上传在优酷地址为http://v.youku.com/v_show/id_XNzI4MjA0NzQ4.html。
鉴于目前大一大二新生还都在用STC单片机搞基(我大一也是如此),因此特将两轮平衡车的技术移植到STC单片机上,造福新生。。
ps:以后参加校园里面的智能车比赛,就不要做那种光电管检测黑线四轮趴着跑的那种了,照学长的指示设计个基于STC单片机的两轮平衡车跟他们四轮的小车比赛,不管结果怎样,他们瞬间就弱爆了。。
本次设计的最终的目的设计一种基于STC单片机的两轮直立自平衡小车。选用加速度计和陀螺仪为输入和反馈,对输入信号处理加工形成一个单闭环系统,由于PID控制具有结构简单、适用面广、鲁棒性强、可靠性高等优点,控制策略自然采用PID控制。
本次设计研究的主要内容是如何在直立这个自然不稳定系统中设计单闭环调速系统的PID控制器,利用输出不同的PWM控制电机正反转来实现不断调整小车状态使之稳定直立。
2.1 任务分析
此次设计要求小车克服自然不稳定性保持直立状态运行,相比较于两脚着地而言,呵呵看起来还挺复杂的。但是为了将复杂的事情简单化,首先我们将问题分解,一小块一小块地解决貌似会简单点。
从任务分析来看,保持小车直立为结果,采用STC15F2K60S2为要求,通过着地的电机提供动力,因此,将小车看做被控对象,输入量就是姿态解算和PWM输出。小车的角度和速度紧密联系,相互控制,我们既可以通过调整角度来调节速度,也可以通过调节速度来调节角度。而硬件方面STC15F2K60S2这款单片机既有AD模拟口输入又有PWM口输出,因此我们只需选用模拟量输出的姿态传感器,和大电流驱动电路即可完成设计。
点击打开链接2.2 理论分析
图2.1 保持木棒直立的反馈控制
同样的要使小车保持平衡,就需要使重心落在两个轮子上,当然,绝对的落在轮子上是不可能的,因此我们需要不断地调整,通过轮子的前后移动来调整重心,既然有调整那就必须要有负反馈,如图2.2所示小车通过前后加减速调整小车状态。
另外在重力场中我们可以将这个模型改成单摆模型,这样更容易理解和建模计算,如图2.3所示。
图2.3 系统简化单摆模型
对其受力分析如图2.4所示。
图2.4 单摆受力分析
当小球偏离重力的重心后,会受到一个拉回重心位置的力,成为回复力,大小为
但是如果这个角度非常小的话,我们可以忽略掉正玄和正切的差别,近似回复力与偏离角成正比。如上式的公式所示。
下面来讲一下阻尼,我们都知道任何物体的受力分析里面不可或缺的就是摩擦力,在阻扰物体运动的情况下我们称之为阻尼,如下图2.5所示,单摆在空气中受到阻尼力最终回复到重力位置,如图过阻尼和欠阻尼的情况。如果阻尼过大就会导致达到平衡点的时间过长,如果阻尼太小就会来回运动导致时间一样很长,因此,一个合适的阻尼系数很重要。
图2.5 不同阻尼下的运动特性
2.3 可行性分析
上面阐述了车模直立的原理,可以看出要想保证车模直立,我们需要角度值,而单单使用陀螺仪来获取角度值的话存在一个不可跨越的问题,就是陀螺仪的动态性能好但是静态性能差(积分饱和等现象),为了弥补这个差距,我们另外选择加速度计,因为加速度计正好具有静态性能好而动态性能差的性能。形成互补。
(1)加速度传感器
如下图2.6所示为飞思卡尔生产的模拟加速度计MMA7260,他可以精确测量自身加速度,14位高精度。与重力加速度比较即可简单获得目前状态下的角度。
图2.6 MMA系列加速度计
方案一:选用 MMA8451三轴加速度计,利用IIC通信实时读取MMA8451的数据。
方案二:选用MMA模拟系列三轴加速度计,利用MCU片内AD口采集数据并转换。
方案对比:方案一采集到的直接就是数字量可以节省MCU的AD口并且精度高,节省MCU的转换时间及功耗负担。比赛而言建议选MMA8451,但是程序复杂度会较AD复杂一些。
(2)陀螺仪
单轴陀螺仪模拟量输出(前文讲过的村田研究所发明的)如图2.7所示,精确测量自身翻转角速度,以灵敏度著称。其放大电路如图2.8所示。
方案一:电路如图2.9所示,此方案的设计是因为陀螺仪存在一个温漂问题,这样对我们零偏会有影响,因此如图我们在输出端先接一高通电容C3来抑制温漂,然后进行放大输入单片机的MCU的AD口。
方案二:电路在上图的基础上将C3短接,因为加上电容后虽然抑制了温漂,但在车子加减速的时候会出现严重的过冲现象,这样会导致车子激烈抖动无法平稳下来,为了避免此现象我们短接C3,降低放大倍数,达到相对较好的情况,实验证明,此时车子已经相当平稳,对于温漂问题,我们采取软件解决方案,在每次启动时首先执行自动获取零点程序,直接矫零。
3 基于STC单片机的两轮车设计
3.1 STC15F60S2简介
STC15F2K60S2这款单片机是宏晶科技有限公司,也就是生产51的那个公司,该公司一直致力于做国产的芯片,将其做的极致,也是如此国内本科的大学教材统一选用宏晶科技的芯片作为教材案例。支持国产!呵呵。听说该款芯片是1T的单片机,通俗地讲就是更快了,快了几十倍的说。另外这款新片更加注重了加密型,因为以前的该款单片机的二进制文件,居然可以从芯片里面弄出来,这样便可以盗版等等,此款采用的是第八代加密技术,并且在首页悬赏10万元来破解。另外来说说他的可靠性和抗干扰性,在这一方面做的出色的一般都是一些如飞思卡尔等的大公司,稳定性和高的抗干扰能力是芯片性能的衡量标准,笔者在做这次毕业设计的过程中也是深有体会,对于直立车的电机输出可谓是大大检验了芯片的抗干扰能力和稳定性,因为这里输出的PWM变化特别快,而且还经常要改变方向,因此如果输出没有接光电隔离的话,对芯片的干扰势必会非常之大。亲测发现,飞思卡尔和STC15F2K的芯片可以抵抗这样的干扰而稳定工作,而AVR也就是ATM公司生产的328p芯片就无法胜任,必挂的节奏。也就是后面文中将为大家介绍到的ARDUINO 小板,一定要加光电隔离。在指令代码方面,宏晶科技非常人性化的告诉我可以兼容上一代8051的指令代码,在keil中直接包含51头文件就行了(即<reg51.h>)。这次变革,我感觉到的唯一亮点就是,以后韩单片机最小系统再也不用焊接晶振那块的电路了,它内部集成了高精度振荡电路,说是R/C电路,精度在0.003左右,温漂0.01,晶振频率在5MHz到35MHz之间设置,但是并非随意值设定,有5.5296MHz / 11.0592MHz / 22.1184MHz / 33.1776MHz选其中之一。然后值得一提的就是烦人的复位电路官方也帮我们省去了,虽然之前的51我都没焊过复位电路,呵呵。
然后让我们来看看这款芯片的片内资源有多丰富吧,首先就是他有3路独立的CCP/PWM/PCA,这个好像渊源可以满足我么做直立车的输出要求吧。八路可达10位的A/D转换,并且速度非常快,达到三十万次每秒,串口通信也有福了,有两个串口UART1和UART2。资源框架图如图3.1所示。
3.2 软件设计及实现
软件方面我们需要占用两路ADC转换来读取外界陀螺仪加速度计模拟值,然后占用一路串口用于下载程序及输出数据、波形调试参数;还有就是两路PWM输出,这里选用两路PWM而不是四路的用意是,希望后来者做这个两轮车的时候不要用两个电机的两轮车,而选用一个电机的两轮车,这样可以大大降低成本,减少一个电机的成本以及两个驱动芯片的成本,驱动的价格真是贵啊。。。 总体软件框架如图3.2所示。
接下来是一部分小段落的程序,关于程序源码的获取,这里多说几句,因为本文中涉及到的程序太多了,必去安卓那一块,还有飞思卡尔那块代码太长了,不可能在文中附录,因此我会于答辩后近日上传至CSDN的个人博客资源里,供大家下载。也请大家广泛传播关注,不定期更新文中有些只有软件的部分会在学长毕业后继续倒腾各种奇葩的设计,诸如基于RASPBERRY(一种LINUX操作系统)的小型无人侦察机等。贴上个人主页和个人信息(图3.3、3.4所示)。关注成功便可以在我上传的资源里面下载到所有你大学四年要用到的,以及最新的技术动态。附上网址:http://blog.csdn.net/baitianzi534011382
首先初始化AD转换模块,配置函数如下:
void ADC_config(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Px = ADC_P10 | ADC_P11 | ADC_P12;
图3.2 总体软件框图
ADC_InitStructure.ADC_Speed = ADC_360T; ADC_90T,ADC_180T,ADC_360T,ADC_540T
ADC_InitStructure.ADC_Power = ENABLE;
ENABLE,DISABLE
图3.4 博客主页
ADC_InitStructure.ADC_AdjResult = ADC_RES_H8L2; ADC_RES_H2L8,ADC_RES_H8L2
ADC_InitStructure.ADC_Polity = PolityLow;
PolityHigh,PolityLow
ADC_InitStructure.ADC_Interrupt = DISABLE;
ENABLE,DISABLE
ADC_Inilize(&ADC_InitStructure);
ADC_PowerControl(ENABLE);
}
然后初始化PWM输出
void PCA_config(void)
{
PCA_InitTypeDef PCA_InitStructure;
PCA_InitStructure.PCA_Mode = PCA_Mode_HighPulseOutput; //PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
PCA_InitStructure.PCA_PWM_Wide = 0; //PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
PCA_InitStructure.PCA_Interrupt_Mode = ENABLE; //PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
PCA_InitStructure.PCA_Value = 65535;
PCA_Init(PCA0,&PCA_InitStructure);
PCA_InitStructure.PCA_Mode = PCA_Mode_HighPulseOutput; //PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
PCA_InitStructure.PCA_PWM_Wide = 0; //PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
PCA_InitStructure.PCA_Interrupt_Mode = ENABLE;
PCA_Fall_Active, ENABLE, DISABLE
PCA_InitStructure.PCA_Value = 65535;
PCA_Init(PCA1,&PCA_InitStructure);
PCA_InitStructure.PCA_Mode = PCA_Mode_HighPulseOutput; //PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
PCA_InitStructure.PCA_PWM_Wide = 0; //PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
PCA_InitStructure.PCA_Interrupt_Mode = ENABLE; //PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
PCA_InitStructure.PCA_Value = 65535;
PCA_Init(PCA2,&PCA_InitStructure);
PCA_InitStructure.PCA_Clock = PCA_Clock_1T; //PCA_Clock_1T, PCA_Clock_2T, PCA_Clock_4T, PCA_Clock_6T, PCA_Clock_8T, PCA_Clock_12T, PCA_Clock_Timer0_OF, PCA_Clock_ECI
PCA_InitStructure.PCA_IoUse = PCA_P24_P25_P26_P27; //PCA_P12_P11_P10_P37, PCA_P34_P35_P36_P37, PCA_P24_P25_P26_P27
PCA_InitStructure.PCA_Interrupt_Mode = DISABLE; //ENABLE, DISABLE
PCA_InitStructure.PCA_Polity = PolityHigh; PolityHigh,PolityLow
PCA_Init(PCA_Counter,&PCA_InitStructure);
}
读取陀螺仪和加速度计的AD值
gyro_ad = Get_ADC10bitResult(0);
MMA7361_Y = Get_ADC10bitResult(1);
MMA7361_Z = Get_ADC10bitResult(2);
转化为欧拉角
MMA7361_Y-=0X0152;
MMA7361_Z-=0X0152;
roll=(float)(((atan2(MMA7361_Z,MMA7361_Y)*180)/3.14159265)+180);
积分并校正
gyro_angle=gyro_angle+gyro_speed*dt+jiao_du_cha/fGravityTimeConstant*dt;
PID控制器
jiao_PWM=(int)(0-gyro_angle*fAngleControlP-gyro_speed*fAngleControlD);
3.3 调试及实现
整个调试过程是非常重要的环节,因此此处给出调试函数,以下数据及波形均由此函数获得:
void debug( int ch0, int ch1, int ch2){
if(ch0>0)
{ TxSend(ch0%100/10 + '0');
TxSend(ch0%10 + '0');
}else
{ TxSend('-');
ch0 =0 - ch0;
TxSend(ch0%100/10 + '0');
TxSend(ch0%10 + '0');
}
TxSend(',');
if(ch1>0)
{ TxSend(ch1%100/10 + '0');
TxSend(ch1%10 + '0');
}
else
{ TxSend('-');
ch1 = 0 - ch1;
TxSend(ch1%100/10 + '0');
TxSend(ch1%10 + '0');
}
TxSend(',');
if(ch2>0)
{ TxSend(ch2%100/10 + '0');
TxSend(ch2%10 + '0');
}
else
{ TxSend('-');
ch2 = 0 - ch2;
TxSend(ch2%100/10 + '0');
TxSend(ch2%10 + '0');
}
PrintString("\r\n");
}
图3.5 陀螺仪加速计波形
1、初始化调整:对初始化位置的校正,首先手动保持车体处于重心垂直地面状态下读取陀螺仪加速度计的值使之输出为0,即刚好处于临界状态。如图3-5红线和黑线为加速度计X轴和Z轴的纠正后输出,蓝线为陀螺仪输出。
2、随动性调整:调好第一步之后,由debug函数输出融合后的角度值和直接由
加速度计输出的角度值,调整积分时间看两者的跟随情况,使之刚好跟上而不过为最好。如图3.6为调整好的波形输出。
3、PID控制器参数调整:以上两步确定调到最佳状态,然后直接上机,上电看看提起来左右晃动看电机的反应是否正确,一般会有某个电机反转的情况,修改电机接线(推荐)或者修改程序,修改PWM输出引脚等等,然后根据PID调整原则,修改PD参数,使之能够站稳,然后可以考虑注释掉程序中的DEBUG段 这样可以减少控制周期,是直立控制更加稳定,但是这时周期改变了,需要修改积分时间,改小来,这时因为没有了DEBUG函数,只能盲调,但是这时PD的参数已经调好了,就只用调整积分时间这一个变量,因此只会越调越稳,不用担心盲调会调不好或者不好调等问题。
PID参数调节问题,此处不多做讨论。如图3.7为实物照片,及调好的车体照片。