电子与信息工程学院
项 目 设 计 报 告
项目名称 双轮自平衡小车设计 学生姓名 戴 磊103621015
廖 崎107221046 李 旭103621045 王思然103522024
专 业 电子信息科学与技术 班 级 103622 指导教师 李东京 万青 赵东
目录
一 自平衡小车的总体方案设计 ................................................................................................... 4 1、自平衡小车的设计方案 ............................................................................................................. 4 2、自平衡小车的总体框图 ............................................................................................................. 4 二 系统的具体设计与实现 ........................................................................................................... 5 1、单片机控制模块......................................................................................................................... 5 2、陀螺仪加速度计模块 ................................................................................................................. 5 3、光码盘测速模块......................................................................................................................... 7 4、稳压模块 .................................................................................................................................... 8 5、电机驱动模块............................................................................................................................. 9 6、LCD1602显示模块 .................................................................................................................. 12 三 软件系统设计......................................................................................................................... 18 1、设计思想 .................................................................................................................................. 18 (1)PID技术 ............................................................................................................................... 18 (2)应用现状 .............................................................................................................................. 18 (3)PID调节规律 ....................................................................................................................... 19 (4)极点配置 .............................................................................................................................. 20 (5)极点配置条件....................................................................................................................... 20 (6)极点配置控制器 ................................................................................................................... 23 2、程序流程图 .............................................................................................................................. 24 3、程序代码 .................................................................................................................................. 25
摘 要
随着科技进步,生活水平的提高,人们追求智能与舒适的愿望也日益强烈。从而催生了许多智能化的产品。如智能电视、智能小车等。如何实现小车的小车的自动快捷驾驶,也成为人们心中的向往与疑问,基于这种趋势与需求,着眼于实际情况。本文介绍了基于STC90C51单片机的自平衡小车系统的设计。系统基于陀螺仪等传感器,利用PID平衡算法,对小车的速度倾斜角度平衡状态来进行检测,并通过单片机来控制电机来实现双轮小车自如平衡地运动。从而实现小车智能自主控制的目的。
关键词: STC90C51 自平衡 PID算法
一 自平衡小车的总体方案设计
1、自平衡小车的设计方案
该自平衡小车,采用STC90C51单片机和各种传感器的组合,构成了自平衡小车系统。其系统主要由以下几个部分组成:单片机控制系统、陀螺仪加速度检测模块、光码盘测速模块、稳压模块、电机驱动模块、LCD1602显示模块组成。本设计的自平衡小车工作原理:给小车通电,平衡放在地上,当小车开始倾斜时,陀螺仪及时地采集的小车倾斜角度数据传给单片机,而加速度计将车子倾斜的瞬时加速度采集后也传给单片机,同时,光码测速仪也将车子的实时速度采集后传给单片机。单片机系统收集到以上三组数据,对数据进行量化处理后,在PID平衡算法的控制下,控制电机及时地做出前进或后退或加速或减速的反应,使车子在一个小角度范围内做平衡地来回摆动,以保持车子的不倒。
2、自平衡小车的总体框图
自平衡小车主要由以下模块组成:单片机控制系统、陀螺仪加速度检测模块、光码盘测速模块、稳压模块、电机驱动模块、LCD1602显示模块,以下是自平衡小车系统方框图。
单片机 光码盘测速模块 控制模 块 稳压模块(电源) 自平衡小车系统框图 陀螺仪加速度模块 电机驱动模块 LCD1602显示模块 二 系统的具体设计与实现
1、单片机控制模块
单片机最小系统原理图如下:
单片机最小系统
单片机最小系统由复位电路以及晶振电路组成,它是保证单片机能正常工作的最基本条件,在此不作过多介绍。
2、陀螺仪加速度计模块
本设计中所采用的陀螺仪加速度设模块为MPU6050,之所以选择这个模块,是因为其有以下优点:
()集角度测量与加速度测量于一体
(2)其那同时测量三轴上的角度与加速度测量 (3)其输出为数字信号,便于处理于存储与传输
(4)测量范围大,反应快。
以下是MPU6050相关资料
MPU-6000为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时之轴间差的问题,减少了大量的包装空间。MPU-6000整合了3轴陀螺仪、3轴加速器,并含可藉由第二个I2C端口连接其他厂牌之加速器、磁力传感器、或其他传感器的数位运动处理(DMP: Digital Motion Processor)硬件加速引擎,由主要I2C端口以单一数据流的形式,向应用端输出完整的9轴融合演算技术
InvenSense的运动处理资料库,可处理运动感测的复杂数据,降低了运动处理运算对操作系统的负荷,并为应用开发提供架构化的API。
MPU-6000的角速度全格感测范围为±250、±500、±1000与±2000°/sec (dps),可准确追緃快速与慢速动作,并且,用户可程式控制的加速器全格感测范围为±2g、±4g±8g与±16g。产品传输可透过最高至400kHz的I2C或最高达20MHz的SPI。
MPU-6000可在不同电压下工作,VDD供电电压介为2.5V±5%、3.0V±5%或3.3V±5%,逻辑接口VVDIO供电为1.8V± 5%。MPU-6000的包装尺寸
4x4x0.9mm(QFN),在业界是性的尺寸。其他的特征包含内建的温度感测器、包含在运作环境中仅有±1%变动的振荡器。
引脚图 实物图
3、光码盘测速模块
此模块有以下优点:
(1)
测速精度高,反应快,因为其一圈有高达60个栅格
(2)能测正反转 此模块详细资料如下: 对管厂家:美国惠普HP原装 供电电压:5V
接线方式:红5V 黑GND
输出信号:5Vp-p 两相信号输出(见下图)。
两根信号线,一根黄色的一根蓝色的(由于生产批次不同,有时候信号线可能为绿色,不影响使用),黄色和蓝色分别是2个TTL电平(可以直接接单片机)信号 (注:测正反转就是靠这两个电平区分是正转还是反转) (设A B 为信号线) A先有脉冲,证明是正转 B先有脉冲,证明是反转
重要说明:长时间挡住或者不挡均没有输出,作为相位检测,只有在对射的上升沿和下降沿才会有波形输出,所以转动码盘或者用不透明物体来回切换状态的时候才会有方波输出,高速状态下,类似正弦波或者锯齿波,详见下面视频演示。万用表测试一直表现为0.1V左右,是无效的!测试请用示波器或者单片机的采集口。
测速原理示意图
测速原理演示图
4、稳压模块
之所以要设计稳压模块,是因为平衡小车不同模块所需电压值不同,比如电机的工作电压在9V,而单片机控制系统、LCD1602显示模块及测速模块工作电压在5V;但同时陀螺仪加速度模块工作电压却在3.3V,因此有必要设计一稳压模块,能够同时稳定地提供这三组电压值。
稳压模块的原理图如下:
稳压模块原理图
其中用到两个稳压芯片LM7805及LM1117,LM7805能够将7V到12V之间的输入电压转化为5V的输出输出电压,同时LM117以LM7805V的输出电压为输入电压,将其转化为3.3V输出以供陀螺仪加速度模块使用。图中,LED为测试电路是否正常,正常时灯亮;反之,灯灭。D1为普通二极管,在LM7805的输入与输出之间起滤波作用。图中电容也均为滤波作用。使用时,两稳压芯片均要用散热片,否则会导致温度过高而烧坏。
相应的PCB布线图如下:
稳压模块PCB图
5、电机驱动模块
由于电机工作电压与单片机的不同,且单片机本身带负载能力不强,电机工作需要较大电流等因素,无法用单片机直接驱动电机运转,因此需要电机驱动模块来驱动电机。在此设计中所选用的电机驱动为L298N模块。
它具有以下优点: (1)负载能力强
(2)能实现高电压强电流的输出 (3)控制简单方便 具体资料如下:
L298N是ST公司生产的一种高电压、大电流电机驱动芯片。该芯片采用15脚封装。主要特点是:工作电压高,最高工作电压可达46V;输出电流大,瞬间峰值电流可达3A,持续工作电流为2A;额定功率25W。内含两个H桥的高电压大电流全桥式驱动器,可以用来驱动直流电动机和步进电动机、继电器线圈等感性负载;采用标准逻辑电平信号控制;具有两个使能控制端,在不受输入信号影响的情况下允许或禁止器件工作有一个逻辑电源输入端,使内部逻辑电路部分在低电压下工作;可以外接检测电阻,将变化量反馈给控制电路。使用L298N芯片驱动电机,该芯片可以驱动一台两相步进电机或四相步进电机,也可以驱动两台直流电机。
简要说明:
一、 尺寸:80mmX45mm
二、 主要芯片:L298N、光电耦合器
三、 工作电压:控制信号直流5V;电机电压直流3V~46V(建议使用36伏以下)
四、 最大工作电流:2.5A 五、 额定功率:25W 特点:1、具有信号指示。 2、转速可调 3、抗干扰能力强
4、具有过电压和过电流保护 5、可单独控制两台直流电机
6、可单独控制一台步进电机 7、PWM脉宽平滑调速 8、可实现正反转 9、采用光电隔离
使用直流/步进两用驱动器可以驱动两台直流电机。分别为M1和M2。引脚A,B可用于输入PWM脉宽调制信号对电机进行调速控制。(如果无须调速可将两引脚接5V,使电机工作在最高速状态,既将短接帽短接)实现电机正反转就更容易了,输入信号端IN1接高电平输入端IN2接低电平,电机M1正转。(如果信号端IN1接低电平, IN2接高电平,电机M1反转。)控制另一台电机是同样的方式,输入信号端IN3接高电平,输入端IN4接低电平,电机M2正转。(反之则反转),PWM信号端A控制M1调速,PWM信号端B控制M2调速。
可参考下图表:
输入PWM信号改变脉宽可调速 电机 旋转方式 控制端IN1 控制端IN2 控制端IN3 控制端IN4 调速端A 正转 反转 停止 M2 正转 反转 停止 其实物图如下:
高 低 低 / / 低 低 高 低 / / 低 / / / 高 低 / / / / 低 高 / 高 高 高 / / / 调速端B / / / 高 高 高 M1
L298N电机驱动模块实物图
6、LCD1602显示模块
此模块主要用来显示自平衡小车的各种参数,如倾斜角、瞬时速度、瞬时加速度等。同时也可以观察可调参数,这些参数可以用按键输入,通过LCD1602显示出来,方便自平衡小车参数的观察与更改,达到高效调速的目的。
其实物图如下:
LCD1602实物图 其与单片机系统组成的原理图如下:
单片机控制系统及1602显示模块
相应的PCB布线图如下:
其详细资料如下:
1602LCD 分为带背光和不带背光两种,基控制器大部分为 HD44780,带背光的
比不带背光
的厚,是否带背光在应用中并无差别 1602LCD 主要技术参数: 显示容量:16×2 个字符 芯片工作电压:4.5—5.5V 工作电流:2.0mA(5.0V) 模块最佳工作电压:5.0V 字符尺寸:2.95×4.35(W×H)mm 引脚功能说明
1602LCD 采用标准的 14脚(无背光)或 16脚(带背光)接口,各引脚接口说明如表 10-13 所示:
编号 符号 引脚说明 编号 符号 引脚说明 1 VSS 电源地 9 D2 数据 2 VDD 电源正极 10 D3 数据 3 VL 液晶显示偏压 11 D4 数据 4 RS 数据/命令选择 12 D5 数据 5 R/W 读/写选择 13 D6 数据 6 E 使能信号 14 D7 数据 7 D0 数据 15 BLA 背光源正极 8 D1 数据 16 BLK 背光源负极 表 10-13:引脚接口说明表 第 1 脚:VSS 为地电源。 第 2 脚:VDD接 5V正电源。
第 3 脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对
比度过高时会产生“鬼影”,使用时可以通过一个 10K 的电位器调整对比度。 第 4 脚:RS 为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第 5 脚:R/W 为读写信号线,高电平时进行读操作,低电平时进行写操作。当 RS和 R/W
共同为低电平时可以写入指令或者显示地址,当 RS 为低电平 R/W 为高电平时可以读忙信号,当 RS 为高电平 R/W为低电平时可以写入数据。
第 6 脚:E端为使能端,当 E 端由高电平跳变成低电平时,液晶模块执行命令。 第 7~14脚:D0~D7为 8 位双向数据线。 第 15脚:背光源正极。 第 16脚:背光源负极。
LCD寄存器的选择 E R/W RS 功能说明 1 0 0 写入命令寄存器 1 0 1 写入数据寄存器 1 1 0 读取忙碌标志及RAM地址 1 1 1 读取RAM数据 0 X 不动作 LCD指令表 控制线 数据线 指令功能 RS R/W D7 D6 D5 D4 D3 D2 D1 D0 0 0 0 0 0 0 0 0 0 1 清除屏幕 清除屏幕,并把光标移至左上角 光标回到原0 0 0 0 0 0 0 0 1 x 点 光标移至左上角,显示内容不变 0 0 0 0 0 0 0 1 I/D S 设定进入模I/D=1:地址递增,I/D=0:地址递减 S=1:开启显示屏,S=0:关闭显式 示屏 0 0 0 0 0 0 1 D C B 显示器开关 D=1:开启显示幕 C=1:开启光标 B=1:光标所在位置的字符闪烁 0 0 0 0 0 1 S/C R/L x x S/C=0、R/L=0:光标左移;S/C=0、R/L=1:光标右移 S/C=1、R/L=0:字符和光标左移;S/C=1、R/L=1:字符和光标右移 0 0 0 0 1 DL N F x x 移位方式 DL=1:数据长度为8位,DL=0:数据长度为4位 N=1:双列字,N=0:功能设定 单列字;F=1:5x10字形,F=0:5x7字形 CG RAM地址0 0 0 1 CG RAM地址 设定 将所要操作的CG RAM地址放入地址计数器 DD RAM地址0 0 1 DD RAM地址 设定 将所要操作的DD RAM地址放入地址计数器 忙碌标志位0 1 BF 地址计数器内容 BF 读取地址计数器,并查询LCM是否忙碌,BF表示LCM忙碌 1
写入数据
1
读取数据
0 1
写入数据
将数据写入CG RAM或DD RAM
读取数据
读取CG RAM或DD RAM的数据
图 10-57 1602LCD 内部显示地址
例如第二行第一个字符的地址是 40H,那么是否直接写入 40H 就可以将光标定位在第二行,第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7恒定为高电平 1 所以实际写入的数据应该是 01000000B(40H)+10000000B(80H)=11000000B(C0H)。 在对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标是自动右移的,无需人工干预。每次输入指令前都要判断液晶模块是否处于忙的状态。
1602 液晶模块内部的字符发生存储器(CGROM)已经存储了 160 个不同的点阵字符图形,如图 10-58 所示,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是 01000001B (41H),显示时模块把地址 41H中的点阵字符图形显示出来,我们就能看到字母“A”
实物图如下:
三 软件系统设计
1、设计思想
其主要是通过PID技术对小车的状态进行实时地跟踪及调整。现对PID算法作些介绍。
(1)PID技术
控制技术是运动控制的核心,各种先进控制技术的研究不断推动着运动控制的发展,比如自适应控制技术和以神经网络和模糊控制为代表的智能控制技术,但在实际生产实践中应用最普遍的还是各种以PID为代表的基本控制技术按照偏差的比例、积分和微分进行控制的调节器,简称为PD调节器,是连续系统中技术成熟且应用广泛的一种调节器。本节将对系统用到的PID控制技术做相应的介绍和研究,传感器将车体的角度和运动速度等信息传递给系统控制器,控制器经分析处理运用PID控制技术,将目标命令传递给电机驱动器来完成系统的闭环控制
(2)应用现状
在电机伺服系统的控制中,经典的PID控制具有其结构简单、鲁棒性强以及现场对Pro的广泛使用积累了丰富的经验等优点,在无刷直流电动机的控制方面一直占有很重要的地位。PID的引入保证了其系统响应的快速性,稳定了闭环控制器,补偿了由逆变器引起的控制误差[25][26]。
PID控制器就是将偏差的比例(Proportional)、积分(Integral)和微分(Differential)通过线性组合构成控制量,用这一控制量对被控对象进行控制。 PID算法是目前工业过程控制中应用最广泛的控制算法。PID算法应用如此广泛,是因为它具有如下优点:
(1)算法较为简单,易于实现;
(2)基于线性控制理论,具备许多成熟的稳定性分析方法,有较高的可靠性; (3)可以在很宽的操作条件内保持较好的鲁棒性,对于控制对象模型参数小
范变化不敏感;
(4)不要求了解控制对象的精确数学模型。利用许多成熟的参数整定方法,可以根据控制对象的实际响应曲线来计算PID控制器的参数;
(5)允许工程技术人员以一种简单直接的方式来调节控制系统,以达到希望得到的控制性能,如上升时间、最大超调量和稳态误差等。
当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,系统 控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。
(3)PID调节规律
PID控制分为两大类,一个是模拟PID控制,一个是数字PM控制。在模拟控制系统中,PID是最常用的控制方法。图4.7所示为模拟PID控制系统原理框图。
r(t) c(t) + 比例系数Kp _ + + u(t) y(t) 被控对象 积分系数 Ki + 微分系数Kd 图4.7模拟PID控制系统原理框图
计算机控制系统中,使用的是数字PID控制器。将连续的模拟量进行离散化处理,则可得
kukk1ekk2eik3ekek1u0i0(4-20)
在数字PID控制中,采样周期相对于系统的时间常数来说一般是很短。因此其参数可按模拟PID控制器中的方法来选择。由于要保持动态的平衡,则小车的倾角在一定的范围内要求可控。故本文选择系统的小车的倾角作为输出量,利用PID技术对其进行分析。
由上述理论分析可以看出:其比例、积分、微分三者是彼此影响;同时要使其系统保持稳定,PID三参数必须满足上述关系;为了让系统能够实现其动态的
平衡,需要通过反复试凑的方法来解决,而且整定的参数多导致反复试凑的次数极大的增加,控制器的参数较难选取[27]。加之系统经过线性化处理,即使所选参数满足上述的关系式,仍需要根据系统的实际模型进行调整,这大大增加了调试的难度。鉴于此我们考虑采用其极点配置的方法来考察分析其系统的稳定性。
(4) 极点配置
所谓极点配置就是指利用状态反馈或输出反馈使闭环系统的极点位于所希望的极点位置。由于系统的性能和它的极点位置密切相关,因此极点配置问题在系统中是很重要的。
控制系统的各种特性及其各种品质指标很大程度上由闭环系统的零点和极点位置决定。零点和极点在复数平面中的分布状况决定了相应表达式中该函数前的系数大小。一组零点和极点的分布就对应了一个系统的响应。极点配置的问题,
就是通过选择反馈增益矩阵,将闭环系统的极点恰好配置在根平面上期望的位置,以获得所希望的动态性能。极点配置决定了控制系统的动态性能和稳定性
[28]
。
对于系统的极点配置,需要解决两个问题:一是建立极点可配置的条件;二
是确定其反馈增益矩阵。
(5) 极点配置条件
状态能控性、状态能观性及其稳定性都是控制系统的重要属性。要设计相应稳定的系统,必须先考察其能控性和能观性。
状态能控性问题只考察系统在u(t)作用下状态的转移情况,与输出量y(t)无
AxBu,关。对于线性连续定常系统x如果存在一个分段连续的输入u(t),能在有限时间区间[t0,tf]内,使系统由某一初始状态x(t0)转移到指定的任一终端状态x(tf),则该状态就是能控的。若系统的所有状态都是能控的,则称此系统是状态完全能控。
本文小车参数参考LEGO公司提供的主要机器人组建参数如下表:
小车的车轮质量(Mw) 小车的车轮半径(r) 车轮的转动惯量(Iw) 车身的质量(Mp) 车轮中心到机器人的质心的距离(l) 车身转动惯量(Ip) 电机惯量(Jm) 电机的电阻(Rm) 反电动势常量(ke) 电机转矩常量(km) 该系统的能控性矩阵可表示为:
TcB0.03kg 0.04m IMr2/22.4105kgm2 0.6kg 0.072m IpMpl2/21.55103kgm2 1105kgm2 6.69Ω 0.468V sec/rad 0.317 Nm/A ABA2BA3B
(4-21)
利用文献[27]中对系统能控性的判定定理,即线性非时变系统为完全能控系统的充分必要条件是能控判别矩阵满秩。即若rank(Tc)=4,则系统能控。
利用MATLAB仿真工具,调用其中函数为Tc=ctrb(A,B)。其代码及结果如下所示。
>> A=[0 1 0 0: 0 -56.5 16 0: 0 0 0 1: 0 -525 239.5 0] >> B=[0: 483.2: 0: 44.9]
>> Ic=ctrb(A,B)% 求系统的能控判别矩阵 Ic =
1.0e+008 *
0 0.0000 -0.0003 0.01 0.0000 -0.0003 0.01 -0.9125 0 0.0000 -0.0025 0.1434 0.0000 0.0025 0.1434 -8.7094
>> rank(Tc)% 求矩阵的值,若此数值是4,则该系统完全可控 ans = 4
由上可以看出,其rank(Tc)=4,这说明两轮自平衡小车系统是完全能控的,只有在此基础才可以设计控制器,实现平衡控制。
状态能观性问题是指对于任意给定的输入u(t)在有限观测时间tf>t0,使得根
据[t0,tf]期间的输出y(t)能唯一的确定系统在初始时刻的状态x(t0),则该状态x(t0)是能观测的。若系统的每一个状态都是能观的,则称此系统是状态完全能观。
该系统状态可观性矩阵为:
ToCCACA2CA3T (4-22)
线性非时变系统为完全能观系统的充分必要条件是能观判别矩阵满秩。即若rank(To)=4,则系统能观。
同样利用MATLAB仿真工具,调用其中函数为To=obsv(A,C);其代码及结果如下所示。
>> A=[0 1 0 0 : 0 -56.5 16 0: 0 0 0 1:0 -525 239.5 0]: >> C=[1 0 0 0 : 0 0 1 0]:
>> To =obsv (A,C)% 求系统能观判别矩阵 To =
1.0e+004 *
0.0001 0 0 0 0
0 0.0001 0
0 0.0001 0 0 0 0 0 0.0001 0 -0.0056 0.0016 0 0 -0.0525 0.0239 0 0 0.3192 -0.0904 0.0016 0 2.9663 -0.8400 0.0239 >> rank (To)% 求矩阵的值,若数值为4,则该系统完全可观 ans = 4
从上中可得出:其rank(To)=4,则该系统能观。在控制系统中,反馈控制信息是由系统的输出或者状态变量组合而成的。但是并非所有系统的状态变量在物理上都能测得,于是能否通过对输出的测量获得全部的状态变量的信息,便是系统的能观测问题。此时系统能观,可以设计控制器对那些不能测量的量进行观测,观测系统变动对它们的影响。
由上述分析可得该系统能控且能观,满足极点配置的条件,因此可以利用状 态反馈或输出反馈来配置该系统的闭环极点。
(6) 极点配置控制器
极点配置的问题,就是通过选择反馈增益矩阵,将闭环系统的极点恰好配置在根平面上期望的位置,以获得所希望的动态性能。极点配置决定了控制系统的动态性能和系统的稳定性。本文通过状态反馈来实现极点配置。
系统采用状态反馈对系统(ABC)任意配置极点的充要条件是系统能控。而第4.5.1节已经证明该系统满足其条件,因此可以利用状态反馈法来任意配置系统的极点。
对于系统(组4-2)引入状态反馈,假定考虑进入受控系统的信号为u=v-kx且D=0的情况,其中v为系统的外部参考输入信号,则可将系统的闭环状态方程写成(组4-3)。
由于稳定是控制系统能够运行的前提,因此选择调节时间和超调量作为考察系统动态性能的指标。为了让系统满足如下的性能要求: 超调量: x(ABK)xBv
yCx
3t1.5ss n 21%e25%调节时间:
取ε=0.6、 ,由 可得该闭环系统的期望主导极点为:s1,2=-3±4i,同时要求另外的两个极点在主导极点左边且远离主导极点,一般取其距离主导极点的4到5倍左右,本设计选取-15和-20。这样高阶系统可近似为二阶系统来处理。
所谓极点配置问题是指通过寻找适当的状态反馈增益矩阵K,使得闭环系统极点(即矩阵A—BK的特征值)位于预先给定位置的状态反馈控制器设计问题。其加入状态反馈后的系统结构图如图4.8所示。
B(4×1) A(4×4) K(1×4) dt C(2×4)
图4.8加入状态反馈后的系统结构图
由线性时不变系统的稳定性分析可知,当v=0时,闭环系统方程 的稳定性由其变化系统矩阵(A-BK)的特征值决定,即其矩阵(A-BK)的所有特征值都具有负实部。而由经典控制理论知道,矩阵(A-BK)的特征值也将影响诸如衰减速度、振荡、超调等过渡过程特性。因此需要找到一个合适的矩阵K,使得矩阵(A-BK)的特征值位于复平面预先给定的特定位置,同时具有所期望的动态响应特性。
2、程序流程图
3、程序代码
#include #include typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; //**************************************** // 定义51单片机端口 //**************************************** #define DataPort P2 //LCD1602数据端口 sbit P30=P3^0; sbit P31=P3^1; sbit P32=P3^2; sbit P33=P3^3; sbit P34=P3^4; sbit P35=P3^5; sbit key0=P1^0; sbit key1=P1^1; sbit key2=P1^2; sbit key3=P1^3; sbit P14=P1^4; sbit P15=P1^5; sbit P16=P1^6; sbit P17=P1^7; sbit P00=P0^0; sbit P01=P0^1; sbit P02=P0^2; sbit P03=P0^3; sbit P04=P0^4; sbit P05=P0^5; sbit P06=P0^6; sbit P07=P0^7; sbit SCL=P3^6; //IIC时钟引脚定义 sbit SDA=P3^7; //IIC数据引脚定义 sbit LCM_RS=P0^5; //LCD1602命令端口 sbit LCM_RW=P0^6; //LCD1602命令端口 sbit LCM_EN=P0^7; //LCD1602命令端口 //**************************************** // 定义MPU6050内部地址 //**************************************** #define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz) #define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz) #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) #define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz) #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C //#define ACCEL_YOUT_H 0x3D //#define ACCEL_YOUT_L 0x3E //#define ACCEL_ZOUT_H 0x3F //#define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 //#define GYRO_YOUT_H 0x45 //#define GYRO_YOUT_L 0x46 //#define GYRO_ZOUT_H 0x47 //#define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用) #define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读) #define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取 //**************************************** //定义类型及变量 //**************************************** uchar dis[4]; //显示数字(-511至512)的字符数组 int dis_data; //变量 //int Temperature,Temp_h,Temp_l; //温度及高低位数据 //**************************************** //函数声明 //**************************************** void delay(unsigned int k); //延时 //LCD相关函数 void InitLcd(); //初始化lcd1602 void lcd_printf(uchar *s,int temp_data); void WriteDataLCM(uchar dataW); //LCD数据 void WriteCommandLCM(uchar CMD,uchar Attribc); //LCD指令 void DisplayOneChar(uchar X,uchar Y,uchar DData); //显示一个字符 void DisplayListChar(uchar X,uchar Y,uchar *DData,L); //显示字符串 //MPU6050操作函数 void InitMPU6050(); //初始化MPU6050 void Delay5us(); void I2C_Start(); void I2C_Stop(); void I2C_SendACK(bit ack); bit I2C_RecvACK(); void I2C_SendByte(uchar dat); uchar I2C_RecvByte(); void I2C_ReadPage(); void I2C_WritePage(); void display_ACCEL_x(); void display_ACCEL_y(); void display_ACCEL_z(); uchar Single_ReadI2C(uchar REG_Address); //读取I2C数据 void Single_WriteI2C(uchar REG_Address,uchar REG_data); //向I2C写入数据 //**************************************** //整数转字符串 //**************************************** void lcd_printf(uchar *s,int temp_data) { if(temp_data<0) { temp_data=-temp_data; *s='-'; } else *s=' '; *++s =temp_data/100+0x30; temp_data=temp_data%100; //取余运算 *++s =temp_data/10+0x30; temp_data=temp_data%10; //取余运算 *++s =temp_data+0x30; } //**************************************** //延时 //**************************************** void delay(unsigned int k) { unsigned int i,j; for(i=0;i //**************************************** void InitLcd() { WriteCommandLCM(0x38,1); WriteCommandLCM(0x08,1); WriteCommandLCM(0x01,1); WriteCommandLCM(0x06,1); WriteCommandLCM(0x0c,1); DisplayOneChar(0,0,'A'); DisplayOneChar(0,1,'G'); } //**************************************** //LCD1602写允许 //**************************************** void WaitForEnable(void) { DataPort=0xff; LCM_RS=0;LCM_RW=1;_nop_(); LCM_EN=1;_nop_();_nop_(); while(DataPort&0x01); LCM_EN=0; } //**************************************** //LCD1602写入命令 //**************************************** void WriteCommandLCM(uchar CMD,uchar Attribc) { CMD = ((CMD&0x01)<<7)|((CMD&0x02)<<5)|((CMD&0x04)<<3)|((CMD&0x08)<<1)|((CMD&0x10)>>1)|((CMD&0x20)>>3)|((CMD&0x40)>>5)|((CMD&0x80)>>7); if(Attribc)WaitForEnable(); LCM_RS=0;LCM_RW=0;_nop_(); DataPort=CMD;_nop_(); LCM_EN=1;_nop_();_nop_();LCM_EN=0; } //**************************************** //LCD1602写入数据 //**************************************** void WriteDataLCM(uchar dataW) { dataW = ((dataW&0x01)<<7)|((dataW&0x02)<<5)|((dataW&0x04)<<3)|((dataW&0x08)<<1)|((dataW&0x10)>>1)|((dataW&0x20)>>3)|((dataW&0x40)>>5)|((dataW&0x80)>>7); WaitForEnable(); LCM_RS=1;LCM_RW=0;_nop_(); DataPort=dataW;_nop_(); LCM_EN=1;_nop_();_nop_();LCM_EN=0; } //**************************************** //LCD1602写入一个字符 //**************************************** void DisplayOneChar(uchar X,uchar Y,uchar DData) { Y&=1; X&=15; if(Y)X|=0x40; X|=0x80; WriteCommandLCM(X,0); WriteDataLCM(DData); } //**************************************** //LCD1602显示字符串 //**************************************** void DisplayListChar(uchar X,uchar Y,uchar *DData,L) { uchar ListLength=0; Y&=0x1; X&=0xF; while(L--) { DisplayOneChar(X,Y,DData[ListLength]); ListLength++; X++; } } //************************************** //延时5微秒(STC90C52RC@12M) //不同的工作环境,需要调整此函数 //当改用1T的MCU时,请调整此延时函数 //************************************** void Delay5us() { _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); } //************************************** //I2C起始信号 //************************************** void I2C_Start() { SDA = 1; //拉高数据线 SCL = 1; //拉高时钟线 Delay5us(); //延时 SDA = 0; //产生下降沿 Delay5us(); //延时 SCL = 0; //拉低时钟线 } //************************************** //I2C停止信号 //************************************** void I2C_Stop() { SDA = 0; //拉低数据线 SCL = 1; //拉高时钟线 Delay5us(); //延时 SDA = 1; //产生上升沿 Delay5us(); //延时 } //************************************** //I2C发送应答信号 //入口参数:ack (0:ACK 1:NAK) //************************************** void I2C_SendACK(bit ack) { SDA = ack; //写应答信号 SCL = 1; //拉高时钟线 Delay5us(); //延时 SCL = 0; //拉低时钟线 Delay5us(); //延时 } //************************************** //I2C接收应答信号 //************************************** bit I2C_RecvACK() { SCL = 1; //拉高时钟线 Delay5us(); //延时 CY = SDA; //读应答信号 SCL = 0; //拉低时钟线 Delay5us(); //延时 return CY; } //************************************** //向I2C总线发送一个字节数据 //************************************** void I2C_SendByte(uchar dat) { uchar i; for (i=0; i<8; i++) //8位计数器 { dat <<= 1; //移出数据的最高位 SDA = CY; //送数据口 SCL = 1; //拉高时钟线 Delay5us(); //延时 SCL = 0; //拉低时钟线 Delay5us(); //延时 } I2C_RecvACK(); } //************************************** //从I2C总线接收一个字节数据 //************************************** uchar I2C_RecvByte() { uchar i; uchar dat = 0; SDA = 1; //使能内部上拉,准备读取数据, for (i=0; i<8; i++) //8位计数器 { dat <<= 1; SCL = 1; //拉高时钟线 Delay5us(); //延时 dat |= SDA; //读数据 SCL = 0; //拉低时钟线 Delay5us(); //延时 } return dat; } //************************************** //向I2C设备写入一个字节数据 //************************************** void Single_WriteI2C(uchar REG_Address,uchar REG_data) { I2C_Start(); //起始信号 I2C_SendByte(SlaveAddress); //发送设备地址+写信号 I2C_SendByte(REG_Address); //内部寄存器地址, I2C_SendByte(REG_data); //内部寄存器数据, I2C_Stop(); //发送停止信号 } //************************************** //从I2C设备读取一个字节数据 //************************************** uchar Single_ReadI2C(uchar REG_Address) { uchar REG_data; I2C_Start(); //起始信号 I2C_SendByte(SlaveAddress); //发送设备地址+写信号 I2C_SendByte(REG_Address); //发送存储单元地址,从0开始 I2C_Start(); //起始信号 I2C_SendByte(SlaveAddress+1); //发送设备地址+读信号 REG_data=I2C_RecvByte(); //读出寄存器数据 I2C_SendACK(1); //接收应答信号 I2C_Stop(); //停止信号 return REG_data; } //************************************** //初始化MPU6050 //************************************** void InitMPU6050() { Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态 Single_WriteI2C(SMPLRT_DIV, 0x07); Single_WriteI2C(CONFIG, 0x06); Single_WriteI2C(GYRO_CONFIG, 0x18); Single_WriteI2C(ACCEL_CONFIG, 0x01); } //************************************** //合成数据 //************************************** int GetData(uchar REG_Address) { char H,L; H=Single_ReadI2C(REG_Address); L=Single_ReadI2C(REG_Address+1); return (H<<8)+L; //合成数据 } //************************************** //在1602上显示10位数据 //************************************** void Display10BitData(int value,uchar x,uchar y) { value/=19; value-=20; //角度值基准校正 //转换为10位数据 lcd_printf(dis, value); //转换数据显示 DisplayListChar(x,y,dis,4); //启始列,行,显示数组,显示长度 } void main() { int i,j,k,m,n,num0=380,num1,num2=380,num3,num4=380,num5,num6=380,num7,cun1,t1,t2; char c='A'; delay(1000); //上电延时 InitLcd(); //液晶初始化 InitMPU6050(); //初始化MPU6050 delay(500); while(1) { for(i=0;i<50000;i++) { if(key0==0) { delay(4000); if(key0==0) { c++; } } if(key1==0) { delay(4000); if(key1==0) { c--; } } if(key2==0&&c=='A') { delay(4000); if(key2==0) { num0+=19; num1++; } } if(key3==0&&c=='A') { delay(4000); if(key3==0) { num0-=19; num1--; } } if(key2==0&&c=='B') { delay(4000); if(key2==0) { num2+=19; num3++; } } if(key3==0&&c=='B') { delay(4000); if(key3==0) { num2-=19; num3--; } } if(key2==0&&c=='C') { delay(4000); if(key2==0) { num4+=19; num5++; } } if(key3==0&&c=='C') { delay(4000); if(key3==0) { num4-=19; num5--; } } if(key2==0&&c=='D') { delay(4000); if(key2==0) { num6+=19; num7++; } } if(key3==0&&c=='D') { delay(4000); if(key3==0) { num6-=19; num7--; } } m=GetData(ACCEL_XOUT_H)/96; //使m的值介于正负180 n=abs(m); j=m/5; k=n*3/5+num1/2+j*num1/3+j*j*num1/4+num3+num3*num3/2; cun1=n*4/5+num5/2+j*num5/2+j*j*num5/3+num7+num7*num7/2; t1=k; t2=cun1; if(m>0&&m<60) //如果车子倾斜在正向30度以内 { P01=0; //控制电机转向 P14=0; P02=1; P15=1; while(t1) { if(k%8==0||k%8==1||k%8==2||k%8==3||k%8==4||k%8==5) { P00=1; //PWM调速 P03=1; } if(k%8==6||k%8==7) { P03=0; P00=1; } t1--; } P00=0; P03=0; delay(4); if(i%30==0) //使显示频率为原来的1/30 { Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度 Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度 DisplayOneChar(7,0,'1'); //如果正倾则显示001 DisplayOneChar(7,1,c); Display10BitData(num0,8,0); Display10BitData(num2,12,0); //按键微调 Display10BitData(num4,8,1); Display10BitData(num6,12,1); } } if(m<0&&m>-60) //如果车子倾斜在负向30度以内 { P01=1; P14=1; P02=0; P15=0; while(t2) { if(cun1%8==0||cun1%8==1||cun1%8==2||cun1%8==3||cun1%8==4||cun1%8==5) { P00=1; P03=1; } if(cun1%8==6||cun1%8==7) { P03=0; P00=1; } t2--; } P00=0; P03=0; delay(4); if(i%30==0) { Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度 Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度 DisplayOneChar(7,0,'0'); //如果反倾则显示000 DisplayOneChar(7,1,c); Display10BitData(num0,8,0); //显示按键值 Display10BitData(num2,12,0); Display10BitData(num4,8,1); Display10BitData(num6,12,1); } /* } if(m>=60||m<=-60) //如果车子倾斜角大于正负30则电机停止转动 { P01=1; P14=1; P02=1; P15=1; } } } } if(i%30==0) { Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度 Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度Display10BitData(num0,8,0); //显示按键值 Display10BitData(num2,12,0); Display10BitData(num4,8,1); Display10BitData(num6,12,1); } */ 结束语:通过这次自平衡小车的设计,我们学到了许多知识,特别是与之相关的硬件,我们通过查找大量资料,熟悉并能正确地使用其中的那些模块。过程中还有其他备选模块,我们也了解了许多。并且能够用Altium Designer 熟悉地绘制原理图与相关的PCB布线图。其中的稳压模块、单片机控制模块及LCD1602显示模块便是应用此软件辅助完成的硬件制作。虽然其中我们付出了很多时间与精力,但是比起我们的所学,还是很值得的。自平衡小车的软件控制部分比较难,需要涉及IIC总线通信,1602的显示,MPU6050的通信协议等,在算法里面涉及卡耳曼滤波、PID算法控制等诸多比较难的编程。这些在我们组成员的共同努力,通过查阅大量参考程序,以及自已积累的单片机编程知识,都完成了相应的工作。遗憾的是,限于时间与精力,自平衡小车的平衡还欠佳,不能平衡太久。以后有空余时间,我们一定会慢慢改进。在此,特别感谢三位指导老师给我们提供的大量真诚与无私的指导与帮助,才让我们有信心坚持下去。同时也教会我们不少产品设计方面的知识,再次深表谢意! 任务分配 戴 磊:硬件的组装与PCB制作 瘳 崎:软件的编程以及后期的调试 李 旭:相关硬件资料的收集及设计报告的书写 王思然:卡耳曼滤波及PID算法的钻研 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- huatuo0.com 版权所有 湘ICP备2023021991号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务