汽车功率需求仿真

模型初步搭建与仿真验证

每个驾驶员有自己的驾驶习惯,不同的驾驶习惯对应不同的驾驶模式,不同的驾驶模式对应不同的功率需求。因此,我们需要对不同的驾驶模式进行仿真。

于是我们使用了MATLAB/Simulink进行仿真,很简单粗暴,其公式依据是:$$P_{trac}=\frac{[ma+mgsin\theta+(C_{r1}+C_{r2}v)mgcos\theta+\frac{1}{2}\rho aC_dv^2]v}{\eta_{trans}\eta_{motor}\eta_{invert}}$$

Variants Value meaning
mm ? 车的质量
θ\theta ? arctan(grade)\arctan(grade)道路倾角
Cr1C_{r1} 0.0065 滚动阻力系数1
Cr2C_{r2} 4.92×1054.92\times10^{-5} 滚动阻力系数2
vv ? 车的速度
ρ\rho 1.29kg/m31.29kg/m^3 空气密度
AA 6.5m26.5m^2 车的正前方面积
CdC_{d} 0.8 气动阻力系数
ηtrans\eta_{trans} 暂不考虑 传输效率
ηmotor\eta_{motor} 暂不考虑 发动机效率
ηinvert\eta_{invert} 暂不考虑 反相器效率

可能有些参数设定的不是很准确(比如AA),再说吧。
模型需要参数输入,我个人采用了一种绕弯的思路:python生成h5数据文件,再用matlab转换成mat读取到simulink中(具体为什么,我也不知道我为啥这么干QAQ)

  1. python生成h5数据文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import scipy.io as scio # 可能用不到
import h5py
import numpy as np
import json # 可能也用不到
simin_v = list(np.zeros(5)) + list(range(0,20)) + list(20 * np.ones(20)) + list(np.arange(20, 0, -0.5)) + list(np.zeros(5)) # 速度
simin_mass = list(2000 * np.ones(len(simin_v))) # 质量,由于simulink需要时间戳进行仿真,必须要一串相同的数,考虑相对论效应的另当别论(bushi)
simin_theta = list(np.zeros(len(simin_v))) # 路面倾角

h5file = h5py.File("./dataset/data_v.h5", 'w')
h5file.create_dataset('simin_v', data=simin_v)

h5file = h5py.File("./dataset/data_mass.h5", 'w')
h5file.create_dataset('simin_mass', data=simin_mass)

h5file = h5py.File("./dataset/data_theta.h5", 'w')
h5file.create_dataset('simin_theta', data=simin_theta)

h5file.close
  1. matlab读取h5数据文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fileInfo_v = h5info('./dataset/data_v.h5');
fileInfo_mass = h5info('./dataset/data_mass.h5');
fileInfo_theta = h5info('./dataset/data_theta.h5');

h5disp('./dataset/data_v.h5');
h5disp('./dataset/data_mass.h5');
h5disp('./dataset/data_theta.h5');

v = h5read("./dataset/data_v.h5",'/simin_v');
mass = h5read("./dataset/data_mass.h5",'/simin_mass');
theta = h5read("./dataset/data_theta.h5",'/simin_theta');
% 转换成时间表,不然simulink会报错
v_time_table = array2timetable(v,'TimeStep',seconds(1));
mass_time_table = array2timetable(mass,'TimeStep',seconds(1));
theta_time_table = array2timetable(theta,'TimeStep',seconds(1));

save('data_v.mat','v_time_table',"-v7.3");
save('data_mass.mat','mass_time_table',"-v7.3");
save('data_theta.mat','theta_time_table',"-v7.3");
% 这里是队友写的路面仿真用于导入路面倾角的
% load_system('Road_module');
% road = sim('Road_module');

% 运行模型
load_system('force_model');
out = sim('force_model');
% 画出功率曲线
plot(out.P_trac);

准备好所用的数据之后,就可以开始扔进simulink中进行仿真了。仿真模型如下:
fig01
fig02
fig03

仿真结果:(以python中的数据为例)
fig04

有负值的功率很正常,这涉及到制动过程中的反向充电等机电专业的知识(承认自己也不是很懂这一块)。

用户API的搭建实现个性化输入数据

如前所示,不同的人驾驶习惯不同,主要体现在加速度和速度的变化、车的质量喜好等等,所以我们需要一个用户API,让用户可以自定义输入数据,这样才能更好的模拟真实的驾驶情况。

由于本人之前从没接触过前端开发,队(WANG)友(LAO)曾经用过C++写过串口调试助手,于是也试一试基于C++开发上位机。所用IDE为Visual Studio 2022, 环境搭建参见Microsoft官方文档:https://learn.microsoft.com/zh-cn/cpp/windows/desktop-applications-visual-cpp?view=msvc-170.跑了遍例程,发现报错:

严重性 代码 说明 项目 文件 禁止显示状态
错误 LNK2019 无法解析的外部符号 main,函数 “int __cdecl invoke_main(void)” (?invoke_main@@YAHXZ) 中引用了该符号 DesktopApp D:\PERSONAL FILES\VS2022 WorkSpace\DesktopApp\DesktopApp\DesktopApp\MSVCRTD.lib(exe_main.obj) 1
错误 LNK1120 1 个无法解析的外部命令 DesktopApp D:\PERSONAL FILES\VS2022 WorkSpace\DesktopApp\DesktopApp\x64\Debug\DataCustomize.exe 1

经过一番搜索,发现是因为项目配置不对。参考下面的博客https://blog.csdn.net/csshuaige/article/details/120313469修改配置后无错运行,证明环境搭建成功。

跑完了,这个界面是真的丑……

果断放弃,转战Qt!
Qt的安装参见官方文档:https://doc.qt.io/qt-5/windows.html#installing-qt-creator,这里不再赘述。

Qt Creator初体验

打开Qt Creator,创建一个新工程。

fig04
fig04
fig04
fig04
fig04
fig04
fig04
fig04

至此,一个简单的Qt工程就创建好了。

该工程的目录结构如下:
fig04

其中供用户操作的文件包括一个头文件,两个cpp,和一个ui文件。头文件中包含了用户API的声明,cpp文件中包含了用户API的实现,ui文件中包含了用户界面的设计。

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTextEdit>
#include <QFile>
#include <QDataStream>
#include <QMessageBox>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget

{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

private slots:
void on_pushButton_clicked();

private:
QTextEdit *textEdit;
Ui::Widget *ui;
};

#endif // WIDGET_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//main.cpp
#include "widget.h"

#include <QApplication>
#include <QLocale>
#include <QTranslator>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = "test01_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
a.installTranslator(&translator);
break;
}
}
Widget w;
w.show();
return a.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//widget.cpp
#include "widget.h"
#include "./ui_widget.h"


Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);

}

Widget::~Widget()
{
delete ui;
}

void Widget::on_pushButton_clicked()
{
QTextEdit *textEdit;
QString text_simulation_time = ui->textEdit->toPlainText();
QString text_car_mass = ui->textEdit_2->toPlainText();
QString text_velocity = ui->textEdit_3->toPlainText();
QString text_angle = ui->textEdit_4->toPlainText();
QFile file("D:/PERSONAL FILES/Qt_workspace/test01/testfile.txt");


if (!file.open(QFile::WriteOnly | QFile::Text)) //检测文件是否打开
{
QMessageBox::information(this, "Error Message", "Please Select a Text File!");
return;
}
QTextStream out(&file);
out << text_simulation_time;
out << "\n";
out << text_car_mass;
out << "\n";
out << text_velocity;
out << "\n";
out << text_angle;
}

ui文件负责设计界面样式,与源代码交互是通过“槽”来实现。比如,如果想在按钮点击的时候触发某些事件,需要在’.ui’文件中右击按钮,点击“转到槽”,然后在弹出的窗口中选择“on_pushButton_clicked”这个槽,这样就将按钮和槽连接起来了。在源代码中,只需要在槽函数中实现相应的功能即可。

下面是我用来实现点击按钮保存的槽函数代码部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Widget::on_pushButton_clicked()
{
QTextEdit *textEdit;
QString text_simulation_time = ui->textEdit->toPlainText();
QString text_car_mass = ui->textEdit_2->toPlainText();
QString text_velocity = ui->textEdit_3->toPlainText();
QString text_angle = ui->textEdit_4->toPlainText();
QFile file("D:/PERSONAL FILES/Qt_workspace/test01/testfile.txt");


if (!file.open(QFile::WriteOnly | QFile::Text)) //检测文件是否打开
{
QMessageBox::information(this, "Error Message", "Please Select a Text File!");
return;
}
QTextStream out(&file);
out << text_simulation_time;
out << "\n";
out << text_car_mass;
out << "\n";
out << text_velocity;
out << "\n";
out << text_angle;
}

下面是初步设计的UI界面
fig04

实现效果:
fig04

至此,初步完成了数据输入保存的操作。

数据有了,接下来的一部就是与之前的python文件结合了。以下为修改之后的用于读取生成数据的txt文件的python脚本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import scipy.io as scio
import h5py
import numpy as np
import json
import os # 用于文件读写操作

file_path = 'D:/PERSONAL FILES/Qt_workspace/test01/testfile.txt'
with open(file_path, encoding='utf-8') as file_obj: # open函数返回一个表示文件的对象,python会将这个对象存储在我们的变量file_obj中
lines = file_obj.readlines()

for line in lines:
print(line.rstrip())


simin_time = int(lines[0])

list_simin_v = lines[2].strip().split(',')
list_simin_v_0 = list(map(lambda x: int(x), list_simin_v))

list_simin_mass = list(int(lines[1]) * np.ones(simin_time))

list_simin_theta = [int(lines[3])]
list_simin_theta_0 = list(map(lambda x: int(x), list_simin_theta))

print(" simin_time:%s;\n car mass:%s;\n car velocity:%s;\n road angle:%s\n" % (simin_time, int(lines[1]), list_simin_v_0, int(lines[3])))
print(list_simin_v_0)
print(list_simin_mass)
print(list_simin_theta_0)
h5file = h5py.File("./dataset/data_v.h5", 'w')
h5file.create_dataset('simin_v', data=list_simin_v_0)

h5file = h5py.File("./dataset/data_mass.h5", 'w')
h5file.create_dataset('simin_mass', data=list_simin_mass)

h5file = h5py.File("./dataset/data_theta.h5", 'w')
h5file.create_dataset('simin_theta', data=list_simin_theta_0)

h5file.close

路面状态仿真

锂电池参数仿真

氢氧燃料电池参数仿真

优化算法