基于51单片机的电动助力转向系统(EPS)设计与分析(Matlab仿真&Proteus仿真)

1 前言

1.1 EPS系统工作的大致流程

在这里插入图片描述

1.2 本次设计目标

  • 系统正常工作时,LCD1602能实时显示数据
  • 当电流超过额定值时,系统停止工作并报警
  • 扭矩小于1N·m或车速大于90km/h时,系统不工作
  • 当扭矩方向发生改变时,助力电机转动方向改变
  • 同一车速下,扭矩越大,助力电机助力效果好
  • 同一扭矩下,车速越小,助力电机的助力效果好

2 系统总体设计方案:

在这里插入图片描述
各模块对应方案选择:
主控芯片:AT89C52
液晶显示器:LCD1602
稳压芯片:LM7805
助力电机:直流12V电机
电机驱动芯片:L293D
电流传感器:ACS712
扭矩传感器:接触式扭矩传感器
车速传感器:光电式扭矩传感器
AD转换芯片:ADC0832
报警器:有源蜂鸣器+LED灯
稳压模块电气特性:
在这里插入图片描述
电机参数表:
在这里插入图片描述

3 Matlab仿真与分析

3.1 助力特性曲线仿真

使用直线型助力特性曲线:
在这里插入图片描述
直线型特性曲线的特点是在某一特征车速下,车感系数是一个固定的值,所以助力电机的目标电流和方向盘转矩成线性关系。根据直线型助力特性曲线的数学模型如下:
在这里插入图片描述
可以得知助力特性曲线可以分为三个区间:当 时为无助力区间,此时助力电流为0;当 时为助力变化区间,此时助力电流大小取决于车感系数等参数;当 时为助力不变区间,助力电流达到最大值后保持恒定。
拟合数据为:
在这里插入图片描述
Matlab拟合出的图像:
在这里插入图片描述
特性曲线拟合代码为:

%% 1.车感系数曲线拟合
v=[2.5 5 10 15 17.5 20 22.5 25  27.5 30 32.5 35 40 45 50 55 65 75 80 85 90]';%汽车车速数据
k=[3.3 3.2 3.025 2.7 2.525 2.35 2.075 1.825 1.7 1.525 1.41 1.275 1.05 0.895 0.775 0.625 0.475 0.385 0.275 0.155 0.12]';%汽车车速对应的车感系数
K=fit(v,k,'exp1');%指数拟合车感系数,% K(v) = a*exp(b*v), a = 3.9,b = -0.03052,置信区间95%
plot(v,k,'r*', 'linewidth',1,'MarkerSize',7)%画图
hold on
plot(K,'k')
hold off
grid on
legend('原数据','拟合曲线')%显示图标
xlabel('车速km/h');%横坐标
ylabel('车感系数');%纵坐标
title('车感系数曲线拟合')%标题

二维助力特性曲线与Map图:
在这里插入图片描述
在这里插入图片描述

相关代码为:

T=-8:0.01:8;%输入转矩T的变化范围
for n=0:10:90%实现对0-90十个特征车速特性曲线的绘制
 I_n=jsdl(n);
plot(T,I_n)
hold on
grid on
end
line([-7 -7],[0 -23.4005],'Color','b','LineStyle','--')%画出两条最大电流对应的虚线
line([7 7],[0 23.4005],'Color','b','LineStyle','--')
legend('特征车速0','特征车速10','特征车速20','特征车速30','特征车速40','特征车速50','特征车速60','特征车速70','特征车速80','特征车速90')
xlabel('转矩/(N/m)');
ylabel('助力电流/A');
title('二维助力特性曲线')
axis([-8 8 -30 30])%限制坐标轴范围
%% 3.绘制助力特性曲线Map图
t1=-8:0.01:8; %输入转矩T的变化范围
Z=[jsdl(0);jsdl(10);jsdl(20);jsdl(30);jsdl(40); jsdl(50);jsdl(60);jsdl(70);jsdl(80);jsdl(90)]';%计算助力电流
x=0:10:90;%特征车速
[X,Y]=meshgrid(x,t1);
meshc(X,Y,Z,'FaceColor','interp')%三维map绘制
ylabel('输入扭矩/N·m')
xlabel('车速/(Km/h)')
zlabel('电动机助力电流/A')
title('助力特性曲线Map图')
————————————————————————————————————————————————————————
function [i] = jsdl(vv)
%根据输入车速计算对应电流
v=[2.5 5 10 15 17.5 20 22.5 25  27.5 30 32.5 35 40 45 50 55 65 75 80 85 90]';
k=[3.3 3.2 3.025 2.7 2.525 2.35 2.075 1.825 1.7 1.525 1.41 1.275 1.05 0.895 0.775 0.625 0.475 0.385 0.275 0.155 0.12]';
K=fit(v,k,'exp1');
Td0=1; % 初始扭矩
Tdmax=7;  %方向盘最大助力电流对应的最大扭矩
T=-8:0.01:8;%扭矩考虑范围
i=  K(vv)*(T-Td0).*(T>=1 &T<7)+K(vv)*(Tdmax-Td0).*(T>=7 & T<=8)+K(vv)*(T+Td0).*(T<=-1& T>-7)+K(vv)*(-Tdmax+Td0).*(T<=-7& T>=-8)+0.*(T<=1& T>=-1);%由直线型助力特性曲线计算助力电流
end



以助力电流的大小作为评价助力效果好坏的标准。
在低车速时,助力效果好,满足转向轻便性要求。
在高车速时,助力效果差,满足高速助力不足的要求。
最终拟合助力效果图:
在这里插入图片描述

3.2二自由度整车模型建立

在这里插入图片描述
对应公式:
在这里插入图片描述

3.3 Simulink仿真

仿真参数表:
在这里插入图片描述
仿真模型:
在这里插入图片描述
在这里插入图片描述
仿真结果:
(1)正弦信号输入
在这里插入图片描述
(2)阶跃信号输入响应
在这里插入图片描述

4 硬件电路设计

在这里插入图片描述

5 程序代码

//---------------------------头文件--------------------------//
#include <REGX51.H>
#include <INTRINS.h>
#include <LCD1602.h>//显示模块头文件
#include <Delay.h>//延时函数头文件
#include <ADC0832.h>//AD转换模块头文件
#include <ADFilter.h>//滤波模块头文件
//-------------------------引脚配置---------------------------//
sbit	m1=P3^0;//电机驱动芯片输入端
sbit	m2=P3^1;//电机驱动芯片输入端
sbit	EN=P3^2;//电机驱动芯片使能端
sbit  no=P2^6;//报警电路端口
sbit  yes=P2^7;//EPS正常工作电路
//--------------------------函数声明区--------------------------//
void Delay(unsigned int xms);//延时函数
void LCD_Init();//LCD1602初始化函数
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);//LCD显示字符串函数
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//LCD显示数字函数
unsigned int ADC0832_ReadAD(unsigned char CH);//车速扭矩传感器AD转换函数
unsigned int ADC0832_ReadADI(unsigned char CH);//电流信号AD转换函数
unsigned int AD_Average(unsigned int buffer[6]);//求大小为6的数组均值函数
unsigned int AD_Filter(CH);//AD数据滤波函数
//----------------------------主函数------------------------------//
void main()
{		
		LCD_Init();//LCD1602初始化
		LCD_ShowString(1,1,"I:  A");//LCD显示文本
		LCD_ShowString(2,1,"T:      V:");
		LCD_ShowString(2,13,"Km/h");
		LCD_ShowString(2,5,"Nm");
		yes=1;//系统工作绿LED亮
		no=0;//报警电路不工作
		LCD_ShowString(1,8,"EPS is Ok");//LCD显示文本
		EN=1;//电机驱动使能端置1
		m1=1;//电机输入两端都给1不工作
		m2=1;
         while(1)//死循环
         {
			float I;//定义电流变量
			unsigned char T,K;//定义扭矩和车感系数变量
			unsigned char AD_T =	AD_Filter(0);//定义滤波后的扭矩变量
			unsigned char V =AD_Filter(1)/2.57;//定义滤波后的车速变量
			if(AD_T<127.5){T=(127.5-AD_T)/12.75;}//以127.5数字量为扭矩为0
			if(AD_T>127.5){T=(AD_T-127.5)/12.75;}//大于127.5则扭矩方向为正
			I=(ADC0832_ReadADI(0)-125)/0.1;//电流数字量的计算0.1=采样电阻
			if(I>50)//当电流过大时
			{
				m1=0;//电机停止工作
				m2=0;
				no=1;
				yes=0;//绿灯电路停止工作
				LCD_ShowString(1,8,"EPS Error");//LCD显示系统错误
				Delay(1000);//延时1s
				LCD_ShowString(1,8,"EPS is OK");
			}
			else
			{
				no=0;
				yes=1;//EPS系统正常工作
				LCD_ShowNum(2,3,T,2);//显示扭矩大小
				LCD_ShowNum(2,11,V,2);?/显示车速
				LCD_ShowNum(1,3,I,2);//显示电流
				if(0<V<10){K=39;}//根据车速的不同选择车感系数增益
				if(10<V<20){K=29;}//参考Matlab仿真的Map
				if(20<V<30){K=22;}//对应的车感系数10倍用于调节PWM值
				if(30<V<40){K=16;}
				if(40<V<50){K=12;}
				if(50<V<60){K=9;}
				if(60<V<70){K=7;}
				if(70<V<80){K=5;}
				if(80<V<90){K=3;}
				if(AD_T<140&&AD_T>114&&V>90){yes=0;}
				//扭矩值小于1或者车速大于90时EPS系统正常但不工作
				//根据扭矩和车速不同改变PWM
				if(AD_T<114&&V<90)//扭矩为负值且车速小于90
				{
					m1=0;//电机反转
					m2=1;
					Delay((K*(100-V)*(127-AD_T))/100);//反转持续时间
					m1=0;//电机停止工作
					m2=0;
					Delay(100);//停止工作时间
				}

				if(AD_T>140&&V<90)//车速和扭矩满足要求
				{

					m1=1;//电机正转
					m2=0;
					Delay((K*(100-V)*(AD_T-127)/100));
					//根据K车速V和扭矩大小工作时间
					m1=0;//电机停止工作
					m2=0;
					Delay(100);//延时100ms			
				}
			 }
         }
//----------------------------延时模块c程序------------------------------//
void Delay(unsigned int xms) //延时函数
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}
//----------------------------滤波模块c程序------------------------------//
#include <REGX51.H>
#include <INTRINS.h>
#include <Delay.h>
#include <ADC0832.h>
/******************************************************************
函数功能:对长度为6的数组取均值
入口参数:长度为6的数组buffer[6]
出口参数:数组出去极值后的均值
  ******************************************************************/
unsigned int AD_Average(unsigned int buffer[6])
{
		unsigned int max,min,temp,i;
        max = buffer[0];//将第一个值定义为最大值
        min = max;//将第一个值定义为最小值
        temp = 0;
        for(i=0;i<6;i++)
        {
            temp += buffer[i];//temp=temp+buffer[i],求数组元素和           
            if(buffer[i]>max)//若有值大于现在的最大值则取代之
            {
                max = buffer[i];//最终得到max为数组中最大值
            }
            if(buffer[i]<min)//若有值小于现在的最小值则取代之
            {
                min = buffer[i];//最终确定min为数组中最小值
            }
        }
        temp= temp-max-min; //去掉数组中连两个极值 
        return temp/4;//返回剩下四个数均值     
}
/******************************************************************
函数功能:采集AD值然后进行滤波处理
入口参数:传感器采集通道口CH
出口参数:均值滤波后的值temp
  ******************************************************************/
unsigned int AD_Filter(CH)			
{
	unsigned int Data_Buffer[6]={0},temp;
	unsigned char i;
	for(i=0;i<6;i++)
	{
		Data_Buffer[i]=ADC0832_ReadAD(CH);	//采集AD数据放到数组buffer中
		Delay(1);//延时1毫秒采集一次
	}
	temp=AD_Average(Data_Buffer);	//对采集到的信号数据进行滤波处理
	return temp;//返回滤波后的均值
}
//----------------------------ADC0832模块c程序------------------------------//
#include <REGX52.H>
#include <INTRINS.H>
sbit AD_CS=P1^0;//片选
sbit AD_CLK=P1^1;//时钟
sbit AD_DI=P1^2;//数据输入
sbit AD_DO=P1^2;//数据输出
sbit I_CS=P1^4;//电流片选
sbit I_CLK=P1^5;//电流时钟
sbit I_DI=P1^6;//电流数据输入
sbit I_DO=P1^6;//电流数据输出
/******************************************************************
函数功能:采集扭矩和车速的模拟信号
入口参数:采样通道CH
出口参数:nop
  ******************************************************************/
unsigned int ADC0832_ReadAD(unsigned char CH)//获取AD采样值返回8位数据
{
	unsigned char i, AD_value1= 0x00, AD_value2= 0x00,AD_value=0;
	if (CH==0){CH=0x02;}//读取通道选择参数
	if (CH==1){CH=0x03;}//CH0:0x02   CH1:0x03
	AD_DI=1;	//时钟信号来之前为高电平
	AD_CS=0;//芯片开始工作
	AD_CLK=1;//第一个时钟
	AD_CLK=0;
	//第二三个时钟上升沿输入选择通道10 为单端通道0 11为单端通道1
	AD_CLK=1;//第二个时钟
	AD_DI=CH&0x1;
	AD_CLK=0;//下降沿读取第一个数据
	AD_CLK=1;//第三个时钟
	AD_DI=(CH>>1)&0x1;
	AD_CLK=0;//下降沿读取第二个数据
	//AD转换通道选择完成
	AD_DI=1;//控制命令结束
	AD_DO=1;
	for( i = 0;i < 8;i++ )      
	{		
		AD_CLK=1;//时钟下降沿读取数据
        AD_CLK=0;
        if(AD_DO){AD_value1=AD_value1 |(0x80>>i);}
		//优先读取高8位数据
    }
	for (i = 0; i < 8; i++)      
	{
		if(AD_DO){AD_value2=AD_value2 |(0x01<<i);}
		//读取低八位数据
        AD_CLK=1;//下降沿读取数据
        AD_CLK=0;
	}
	AD_DO=1;
    AD_CS=1;//芯片停止工作
    return (AD_value1==AD_value2)? AD_value1:0;
}
/******************************************************************
函数功能:采集电流的模拟数据
入口参数:采样通道口CH
出口参数:nop
  ******************************************************************/
unsigned int ADC0832_ReadADI(unsigned char CH)
{
	unsigned char i, I_value1= 0x00, I_value2= 0x00,I_value=0;
	if (CH==0){CH=0x02;}//CH0:0x02   CH1:0x03
	if (CH==1){CH=0x03;}
	I_DI=1;	//芯片开始
	I_CS=0;//第二三个时钟上升沿输入选择通道10 为单端通道0 11为单端通道1
	I_CLK=1;//第一个时钟
	I_CLK=0;
	I_CLK=1;
	I_DI=CH&0x1;
	I_CLK=0;//下降沿读取第一个数据
	I_CLK=1;
	I_DI=(CH>>1)&0x1;
	I_CLK=0;//下降沿读取第二个数据
	//AD转换通道选择完成
	I_DI=1;//控制命令结
	I_DO=1;
	for( i = 0;i < 8;i++ )      
	{		
		I_CLK=1;//时钟下降沿读取数据
        I_CLK=0;
        if(I_DO){I_value1=I_value1 |(0x80>>i);}
		//读取高八位数据
    }
	for (i = 0; i < 8; i++)      
	{
		if(I_DO){I_value2=I_value2 |(0x01<<i);}
		//读取低八位数据
        I_CLK=1;//时钟下降沿读取数据
        I_CLK=0;
	}
	I_DO=1;
    I_CS=1;//芯片停止工作
    return (I_value1==I_value2)? I_value1:0;
}
//----------------------------LCD1602显示模块c程序------------------------------//
#include <REGX52.h>
#include <INTRINS.h>
//引脚配置:
sbit LCD_RW=P2^0;
sbit LCD_RS=P2^1;
sbit LCD_EN=P2^2;
#define LCD_DataPort P0
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

公子易平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值