轨道运行课设--GUI界面设计

任务要求

主要要求是完成GUi界面以及功能的设计,模拟列车的人工控制以及自动控制。

一、人工控制

首先导入需要的包,主要使用tkinter实现

1
2
3
4
5
6
7
8
9
10
import tkinter as tk
from tkinter import messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import pandas as pd
import customtkinter as ctk
import numpy as np
import time
import matplotlib
from tkhtmlview import HTMLLabel

设置使用支持中文的字体

1
2
3
# 设置使用支持中文的字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # 或其他支持中文的字体
matplotlib.rcParams['axes.unicode_minus'] = False # 正常显示负号

创建主类GUi并完成初始化

主要是初始状态的确定(速度、加速度、里程),各种列车状态的初始化,组件的创建,按键事件的创立,

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class TrainSimulationGUI:
def __init__(self, root):
self.root = root
self.root.title("城轨列车仿真运行交互界面")

# 设置深色主题
self.root.configure(bg='#1a1a1a')
ctk.set_appearance_mode("dark")

# 初始化仿真状态和相关属性
self.simulation_running = False
self.current_idx = 0
self.speed = 0 # m/s
self.acceleration = 0 # m/s²
self.distance = 21604.28 # 初始里程 m
self.start_time = None
self.time_records = []
self.distance_records = []

# 状态切换控制
self.last_state_change_time = time.time() # 初始化为当前时间
self.min_coast_time = 0.6 # 最小惰行时间(秒)
self.current_state = 'coast' # 当前状态
self.can_change_state = True # 是否可以改变状态
self.cooldown_active = False # 是否处于冷却状态

self.simulation_speed_multiplier = 1.0

# 紧急制动相关状态
self.emergency_braking = False
self.penalty_start_time = None
self.penalty_duration = 5
self.in_penalty_period = False
self.has_triggered_penalty = False

# 修改实际速度数据存储为列表
self.actual_speed_x = []
self.actual_speed_y = []

# 键盘状态
self.is_increasing = False
self.is_decreasing = False

# 绑定键盘事件
self.root.bind('<KeyPress-Up>', self.on_press_up)
self.root.bind('<KeyPress-Down>', self.on_press_down)
self.root.bind('<KeyRelease-Up>', self.on_release_up)
self.root.bind('<KeyRelease-Down>', self.on_release_down)

# 创建主框架和所有GUI组件
self.main_frame = ctk.CTkFrame(self.root)
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

# 创建图表和信息面板的子框架
self.plot_frame = ctk.CTkFrame(self.main_frame)
self.plot_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10), pady=10)

self.info_frame = ctk.CTkFrame(self.main_frame, width=300)
self.info_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=(10, 0), pady=10)

# 在plot_frame中创建速度-里程图
self._create_speed_position_plot()

# 在info_frame中创建信息面板
self._create_info_panel()

# 创建控制面板
self._create_control_panel()

# 初始化数据
self._initialize_data()

# 初始绘制
self._initial_plot()

# 添加惩罚时间显示
self.penalty_display = ctk.CTkLabel(self.dynamic_info_frame,
text="",
font=("Arial", 14))
self.penalty_display.pack(pady=5)

# 添加冷却时间显示
self.cooldown_display = ctk.CTkLabel(self.dynamic_info_frame,
text="",
font=("Arial", 14))
self.cooldown_display.pack(pady=5)

初始曲线等绘制

限速曲线以及目标函数是初始确定的,GUI启动时即可绘制

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def _initialize_data(self):
"""初始化模拟数据"""
self._load_speed_limits()
self._load_target_speeds()
self.actual_speed = []
self.current_speed_limit = None # 当前里程对应的限速值

def _initial_plot(self):
"""初始绘制限速曲线和目标速度曲线,并准备实际速度曲线"""
if self.mileage and self.speed_limit: # 确保列表不为空
self.speed_limit_line.set_data(self.mileage, self.speed_limit)
self.target_speed_line.set_data(self.target_location, self.target_speed)

self.ax.set_xlim(min(self.mileage), max(self.mileage))
self.ax.set_ylim(0, max(max(self.speed_limit), max(self.target_speed)) * 1.1)
self.actual_speed_line.set_data([], [])
self.canvas.draw()

def _load_speed_limits(self):
"""加载速度限制数据"""
try:
df_limits = pd.read_excel('限速要求.xlsx')
self.mileage = []
self.speed_limit = []
for _, row in df_limits.iterrows():
start, end = row['信号里程起点'], row['信号里程终点']
speed = row['土建限速(ATP顶篷速度)']
self.mileage.extend([start, end])
self.speed_limit.extend([speed, speed])
except FileNotFoundError:
messagebox.showerror("文件未找到", "速度限制数据文件 '限速要求.xlsx' 未找到。")
self.mileage = []
self.speed_limit = []

def _load_target_speeds(self):
"""加载目标速度数据"""
try:
df_target = pd.read_excel('目标速度.xlsx')
self.target_location = df_target['列车位置'].values
self.target_speed = df_target['列车速度(km/h)'].values
except FileNotFoundError:
messagebox.showerror("文件未找到", "目标速度数据文件 '目标速度.xlsx' 未找到。")
self.target_location = []
self.target_speed = []

def _create_speed_position_plot(self):
"""创建速度-里程图"""
self.fig, self.ax = plt.subplots(figsize=(8, 4))
self.fig.patch.set_facecolor('#1a1a1a')
self.ax.set_facecolor('#1a1a1a')
self.ax.grid(True, color='#2a2a2a')
self.ax.set_xlabel('里程数 (m)', color='#ffffff')
self.ax.set_ylabel('速度 (km/h)', color='#ffffff')
self.ax.set_title('速度-里程图', color='#ffffff') # 图表标题颜色


self.speed_limit_line, = self.ax.plot([], [], 'r-', label='限速曲线')
self.target_speed_line, = self.ax.plot([], [], 'b--', label='目标速度曲线')
self.actual_speed_line, = self.ax.plot([], [], 'g-', label='实际速度曲线', linewidth=2)
# 设置固定的坐标轴范围
self.ax.set_xlim(21000, 26000) # 根据实际里程范围调整
self.ax.set_ylim(0, 120) # 根据实际速度范围调整
self.ax.tick_params(axis='both', colors='white') # 设置X和Y轴刻度颜色为白色
self.ax.legend()

self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
self.canvas.draw()
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

GUI中各个面板的设计

完成信息面板以及控制面板的设定

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
def _create_info_panel(self):
"""创建信息面板"""
info_frame = self.info_frame

# 列车参数
ctk.CTkLabel(info_frame, text="列车基本参数", font=("Arial", 14, "bold")).pack(pady=10)

params = {
"车长": "23.4m",
"列车编组": "6编组4动2拖",
"列车质量": "194.295×10³ kg",
"当前驾驶模式": "人工控制"
}

for key, value in params.items():
self._create_param_row(info_frame, key, value)

# 速度和加速度显示
self.dynamic_info_frame = ctk.CTkFrame(info_frame)
self.dynamic_info_frame.pack(fill=tk.X, padx=5, pady=10)

# 速度显示
self.speed_display = ctk.CTkLabel(self.dynamic_info_frame,
text="当前速度: 0.0 km/h",
font=("Arial", 14))
self.speed_display.pack(pady=5)

# 加速度显示
self.acceleration_display = ctk.CTkLabel(self.dynamic_info_frame,
text="当前加速度: 0.0 m/s²",
font=("Arial", 14))
self.acceleration_display.pack(pady=5)

# 里程显示
self.distance_display = ctk.CTkLabel(self.dynamic_info_frame,
text=f"当前里程: {self.distance:.2f} m",
font=("Arial", 14))
self.distance_display.pack(pady=5)

# 控制状态显示
self.control_state_display = ctk.CTkLabel(self.dynamic_info_frame,
text="控制状态: 惰行",
font=("Arial", 14))
self.control_state_display.pack(pady=5)

def _create_param_row(self, parent, key, value):
"""创建参数行"""
frame = ctk.CTkFrame(parent)
frame.pack(fill=tk.X, padx=5, pady=2)
ctk.CTkLabel(frame, text=key, anchor="w", width=100, font=("Arial", 12)).pack(side=tk.LEFT)
ctk.CTkLabel(frame, text=value, anchor="w", font=("Arial", 12)).pack(side=tk.RIGHT)

def _create_control_panel(self):
"""创建控制面板,将开始/结束按钮、重置按钮和倍速选择器放在同一行"""
control_frame = ctk.CTkFrame(self.root)
control_frame.pack(fill=tk.X, padx=10, pady=5)

# 左侧子框架:按钮和倍速选择器
left_control_frame = ctk.CTkFrame(control_frame)
left_control_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)

# 开始/结束按钮
self.start_stop_button = ctk.CTkButton(left_control_frame,
text="开始仿真",
command=self._toggle_simulation)
self.start_stop_button.pack(side=tk.LEFT, padx=5)

# 重置按钮
self.reset_button = ctk.CTkButton(left_control_frame,
text="重置仿真",
command=self._reset_simulation,
state="disabled")
self.reset_button.pack(side=tk.LEFT, padx=5)

# 添加倍速选择标签
speed_label = ctk.CTkLabel(left_control_frame, text="选择倍速:", font=("Arial", 12))
speed_label.pack(side=tk.LEFT, padx=(20, 5))

# 添加倍速选择器
self.speed_multiplier_var = tk.DoubleVar(value=1.0)
speed_options = [0.5, 1.0, 1.5, 2.0, 3.0, 5.0] # 倍速选项
self.speed_multiplier_menu = ctk.CTkOptionMenu(left_control_frame,
variable=self.speed_multiplier_var,
values=[str(x) for x in speed_options],
command=self._set_speed_multiplier)
self.speed_multiplier_menu.pack(side=tk.LEFT, padx=5)

# 右侧子框架:时间显示和操作提示
right_control_frame = ctk.CTkFrame(control_frame)
right_control_frame.pack(side=tk.RIGHT, fill=tk.X, expand=True)

# 时间显示
self.time_label = ctk.CTkLabel(right_control_frame, text="仿真时间: 00:00", font=("Arial", 12))
self.time_label.pack(side=tk.RIGHT, padx=10)

# 操作提示
hint_label = ctk.CTkLabel(right_control_frame,
text="操作提示:点击开始仿真并使用上下箭头控制速度",
font=("Arial", 12))
hint_label.pack(side=tk.RIGHT, padx=20)
def _set_speed_multiplier(self, value):
"""设置倍速系数"""
self.simulation_speed_multiplier = float(value)

按钮功能的确定

定义开始、结束、重置按钮的功能

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
43
44
45
46
47
48
49
50
51
def _toggle_simulation(self):
"""切换仿真状态"""
if not self.simulation_running:
# 开始仿真
self.simulation_running = True
self.start_stop_button.configure(text="结束仿真")
self.reset_button.configure(state="normal")
self.start_time = time.time()
self.simulation_start_time = self.start_time
# 启动数据更新
self.root.after(100, self._update_values)
else:
# 结束仿真
self.simulation_running = False
self.start_stop_button.configure(text="开始仿真")
self.reset_button.configure(state="normal")

def _reset_simulation(self):
"""重置仿真(清除除限速曲线和目标速度曲线外的所有状态)"""
self.simulation_running = False
self.current_idx = 0
self.speed = 0
self.acceleration = 0
self.distance = 21604.28
self.time_records = []
self.distance_records = []

# 重置紧急制动相关状态
self.emergency_braking = False
self.penalty_start_time = None
self.in_penalty_period = False
self.has_triggered_penalty = False

# 重置显示
self.speed_display.configure(text="当前速度: 0.0 km/h")
self.acceleration_display.configure(text="当前加速度: 0.0 m/s²")
self.distance_display.configure(text=f"当前里程: {self.distance:.2f} m")
self.control_state_display.configure(text="控制状态: 惰行")
self.time_label.configure(text="仿真时间: 00:00")
self.penalty_display.configure(text="")

self.start_stop_button.configure(text="开始仿真")
self.reset_button.configure(state="disabled")

# 清除实际速度数据
self.actual_speed_x = []
self.actual_speed_y = []
self.actual_speed_line.set_data([], [])

# 重绘画布
self.canvas.draw()

按键功能

编写人工控制下,加速以及减速的案件绑定

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
43
44
45
46
47
48
49
50
51
def on_press_up(self, event):
"""按下上箭头键,尝试进入牵引状态"""
if not self.emergency_braking and not self.in_penalty_period:
current_time = time.time()

# 如果当前是制动状态,必须经过惰行才能牵引
if self.current_state == 'brake':
return

# 检查是否可以切换状态
if self.check_state_transition(current_time):
self.is_increasing = True
self.is_decreasing = False
self.current_state = 'traction'
self.acceleration = self._get_acceleration(self.speed * 3.6)
self.control_state_display.configure(text="控制状态: 牵引")
self.last_state_change_time = current_time

def on_release_up(self, event):
"""释放上箭头键,进入惰行状态"""
if not self.emergency_braking and not self.in_penalty_period:
current_time = time.time()
self.is_increasing = False
self.current_state = 'coast'
self.acceleration = 0
self.control_state_display.configure(text="控制状态: 惰行")
self.last_state_change_time = current_time

def on_press_down(self, event):
"""按下下箭头键,尝试进入制动状态"""
if not self.emergency_braking:
current_time = time.time()

# 检查是否可以切换状态
if self.check_state_transition(current_time):
self.is_decreasing = True
self.is_increasing = False
self.current_state = 'brake'
self.acceleration = -self._get_deceleration(self.speed * 3.6)
self.control_state_display.configure(text="控制状态: 制动")
self.last_state_change_time = current_time

def on_release_down(self, event):
"""释放下箭头键,进入惰行状态"""
if not self.emergency_braking:
current_time = time.time()
self.is_decreasing = False
self.current_state = 'coast'
self.acceleration = 0
self.control_state_display.configure(text="控制状态: 惰行")
self.last_state_change_time = current_time

状态转换转换函数

每次切换状态时,添加一个0.6s的冷却时间,模拟传输时延

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
def check_state_transition(self, current_time):
"""检查是否可以进行状态转换"""
time_since_last_change = current_time - self.last_state_change_time

# 如果处于惰行状态且时间未到,更新显示剩余冷却时间
if self.current_state == 'coast' and time_since_last_change < self.min_coast_time:
remaining_time = self.min_coast_time - time_since_last_change
self.cooldown_display.configure(text=f"状态切换冷却: {remaining_time:.1f}s")
return False

self.cooldown_display.configure(text="")
return True
def check_state_transition(self, current_time):
"""检查是否可以进行状态转换并更新冷却时间显示"""
time_since_last_change = current_time - self.last_state_change_time

if self.current_state == 'coast' and time_since_last_change < self.min_coast_time:
remaining_time = self.min_coast_time - time_since_last_change
self.cooldown_display.configure(
text=f"状态切换冷却: {remaining_time:.1f}s",
text_color="yellow" # 使用黄色显示冷却时间
)
self.cooldown_active = True
return False

if self.cooldown_active:
self.cooldown_display.configure(text="可以切换状态", text_color="green")
self.cooldown_active = False

return True

加速度的确定

通过excel表格确定各个速度区间的加速度

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
def _get_acceleration(self, speed_kmh):
"""根据当前速度获取加速度"""
try:
acceleration_data = pd.read_excel('牵引加速度.xlsx')
# 假设数据表中有 '速度(km/h)' 和 '加速度_AW0(m/s2)' 两列
for i in range(len(acceleration_data)):
if i == 0 and speed_kmh < acceleration_data.loc[i, '速度(km/h)']:
return acceleration_data.loc[i, '加速度_AW0(m/s2)']
if i > 0 and acceleration_data.loc[i - 1, '速度(km/h)'] <= speed_kmh < acceleration_data.loc[i, '速度(km/h)']:
return acceleration_data.loc[i - 1, '加速度_AW0(m/s2)']
return acceleration_data.iloc[-1]['加速度_AW0(m/s2)']
except Exception as e:
messagebox.showerror("错误", f"读取加速度数据时出错: {e}")
return 0

def _get_deceleration(self, speed_kmh):
"""根据当前速度获取减速度"""
try:
# 读取减速度数据
deceleration_data = pd.read_excel('制动加速度.xlsx')

# 遍历数据以找到对应的减速度
for i in range(len(deceleration_data)):
# 当速度高于表中最大速度,返回最大速度对应的加速度
if i == 0 and speed_kmh > deceleration_data.loc[i, '速度(km/h)']:
return deceleration_data.loc[i, '加速度(m/s2)']
# 找到速度区间时,返回区间中较大速度对应的加速度
if i > 0 and deceleration_data.loc[i, '速度(km/h)'] < speed_kmh <= deceleration_data.loc[i - 1, '速度(km/h)']:
return deceleration_data.loc[i - 1, '加速度(m/s2)']

# 当速度小于等于表中最小速度时,返回最小速度对应的加速度
return deceleration_data.iloc[-1]['加速度(m/s2)']

except Exception as e:
messagebox.showerror("错误", f"读取减速度数据时出错: {e}")
return 0

限速的确定

确定各个里程的限速情况,为紧急制动做基础。

1
2
3
4
5
6
7
8
def _get_current_speed_limit(self, distance):
"""根据当前里程获取限速值"""
for i in range(0, len(self.mileage), 2):
start = self.mileage[i]
end = self.mileage[i+1]
if start <= distance <= end:
return self.speed_limit[i]
return None # 如果里程不在任何限速区间内

惩罚状态显示

紧急制动至0时,惩罚状态的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def _check_penalty_status(self, current_time):
"""检查惩罚状态并更新显示"""
if self.in_penalty_period:
elapsed = current_time - self.penalty_start_time
if elapsed >= self.penalty_duration:
# 惩罚时间结束
self.in_penalty_period = False
self.emergency_braking = False
self.has_triggered_penalty = False
self.penalty_display.configure(text="")
self.control_state_display.configure(text="控制状态: 惰行")
self.is_increasing = False
self.acceleration = 0

else:
# 显示剩余惩罚时间
remaining = self.penalty_duration - elapsed
self.penalty_display.configure(text=f"超速惩罚中: {remaining:.1f}s")

运行数据更新问题

包括速度、加速度、工况等的状态更新,且循环调用不断更新。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def _update_values(self):
"""更新速度、加速度、里程和状态显示"""
if not self.simulation_running:
return

current_time = time.time()
elapsed_time = (current_time - self.simulation_start_time)
elapsed_time *= self.simulation_speed_multiplier

# 显示仿真时间
self.time_label.configure(text=f"仿真时间: {int(elapsed_time // 60):02d}:{int(elapsed_time % 60):02d}")

# 更新状态切换冷却时间显示
if self.current_state == 'coast':
self.check_state_transition(current_time)
elif not self.cooldown_active:
self.cooldown_display.configure(text="")

# 获取当前限速值并检查是否需要触发紧急制动
self.current_speed_limit = self._get_current_speed_limit(self.distance)
if self.current_speed_limit is not None and self.speed * 3.6 > self.current_speed_limit:
if not self.emergency_braking:
self.emergency_braking = True
self.control_state_display.configure(text="控制状态: 紧急制动")

# 检查惩罚状态
self._check_penalty_status(current_time)

# 更新加速度
if self.emergency_braking or self.in_penalty_period:
self.cooldown_display.configure(text="不能切换状态", text_color="red")
self.acceleration = self._get_deceleration(self.speed * 3.6)
else:
if self.is_increasing:
self.acceleration = self._get_acceleration(self.speed * 3.6)
elif self.is_decreasing:
self.acceleration = self._get_deceleration(self.speed * 3.6)
else:
self.acceleration = 0

# 时间间隔 dt 根据倍速进行缩放
dt = 0.1 * self.simulation_speed_multiplier

# 更新速度和距离
prev_speed = self.speed
self.speed += self.acceleration * dt
self.speed = max(0, self.speed)
self.distance += self.speed * dt
# 检查是否需要开始惩罚计时
# 只有在紧急制动状态下,速度降为0时才开始惩罚计时
if self.emergency_braking and prev_speed > 0 and self.speed == 0 and not self.has_triggered_penalty:
self.in_penalty_period = True
self.penalty_start_time = current_time
self.has_triggered_penalty = True

# 更新显示标签
self.speed_display.configure(text=f"当前速度: {self.speed * 3.6:.2f} km/h")
self.acceleration_display.configure(text=f"当前加速度: {self.acceleration:.2f} m/s²")
self.distance_display.configure(text=f"当前里程: {self.distance:.2f} m")

# 记录速度和距离数据并更新图表
self.actual_speed_x.append(self.distance)
self.actual_speed_y.append(self.speed * 3.6)
self.actual_speed_line.set_data(self.actual_speed_x, self.actual_speed_y)
self.canvas.draw()

# 继续更新
if self.simulation_running:
self.root.after(int(100 / self.simulation_speed_multiplier), self._update_values)

定义主函数并运行

1
2
3
4
5
6
7
def main():
root = tk.Tk()
app = TrainSimulationGUI(root)
root.mainloop()

if __name__ == "__main__":
main()

演示视频

自动控制

自动控制主要是更改加速度的获取方式,主要是通过PID算法,通过速度与目标速度之间的差值,获取误差,通过自动调整加速度实现自动控制。界面相似,主要是加速度方面的不同,以下仅展示不同。

获取当前目标速度值

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
def _pid_control(self):
"""根据目标速度和当前速度计算加速度"""
# 根据当前里程设置目标速度
self.target_speed_kmh = self._get_target_speed(self.distance)

# 将当前速度转换为 km/h
current_speed_kmh = self.speed * 3.6

# 计算误差
error = self.target_speed_kmh - current_speed_kmh

# 积分计算
self.integral += error * 0.1 # dt = 0.1

# 微分计算
derivative = (error - self.last_error) / 0.1 # dt = 0.1

# PID公式
self.acceleration = self.Kp * error + self.Ki * self.integral + self.Kd * derivative

# 限制加速度范围(根据实际情况调整)
max_acceleration = 1.0 # m/s²
min_acceleration = -1.0 # m/s²
self.acceleration = max(min(self.acceleration, max_acceleration), min_acceleration)

# 更新上一次误差
self.last_error = error

def _get_target_speed(self, distance):
"""根据当前里程获取目标速度"""
if len(self.target_location) == 0 or len(self.target_speed) == 0:
return 0 # 如果没有目标速度数据,返回0

# 查找当前里程对应的目标速度
for loc, speed in zip(self.target_location, self.target_speed):
if distance <= loc:
return speed
return self.target_speed[-1] # 如果超过所有目标点,返回最后一个速度

加速度补偿

由于目标速度与实际速度都为0时,列车不会启动,因此需要添加加速度补偿。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def _update_values(self):
"""更新速度、加速度、里程和状态显示"""
if not self.simulation_running:
return

current_time = time.time()
elapsed_time = (current_time - self.simulation_start_time)
elapsed_time *= self.simulation_speed_multiplier

# 显示仿真时间
self.time_label.configure(text=f"仿真时间: {int(elapsed_time // 60):02d}:{int(elapsed_time % 60):02d}")

# 获取当前限速值并检查是否需要触发紧急制动
self.current_speed_limit = self._get_current_speed_limit(self.distance)
if self.current_speed_limit is not None and self.speed * 3.6 > self.current_speed_limit:
if not self.emergency_braking:
self.emergency_braking = True
self.control_state_display.configure(text="控制状态: 紧急制动")

# 检查惩罚状态
self._check_penalty_status(current_time)

if not self.initial_accel_set:
# 设置初始加速度,使列车开始加速
self.acceleration = 0.5 # 设置一个合理的初始加速度值 (m/s²),根据需要调整
self.initial_accel_set = True
self.control_state_display.configure(text="控制状态: 初始加速")
else:
# PID控制器计算
if not self.emergency_braking and not self.in_penalty_period:
self._pid_control()
self.control_state_display.configure(text="控制状态: 自动驾驶")
else:
# 如果处于紧急制动或惩罚状态,使用制动减速度
self.acceleration = self._get_deceleration(self.speed * 3.6)

# 新增:当速度为0时,给予加速度补偿
if self.speed == 0 and self.target_speed_kmh == 0 and self.distance <= 24000 :
self.acceleration = 5 # 给予一个加速度补偿

# 时间间隔 dt 根据倍速进行缩放
dt = 0.1 * self.simulation_speed_multiplier

# 更新速度和距离
prev_speed = self.speed
self.speed += self.acceleration * dt
self.speed = max(0, self.speed)
self.distance += self.speed * dt

# 检查是否需要开始惩罚计时
if self.emergency_braking and prev_speed > 0 and self.speed == 0 and not self.has_triggered_penalty:
self.in_penalty_period = True
self.penalty_start_time = current_time
self.has_triggered_penalty = True

# 更新显示标签
self.speed_display.configure(text=f"当前速度: {self.speed * 3.6:.2f} km/h")
self.acceleration_display.configure(text=f"当前加速度: {self.acceleration:.2f} m/s²")
self.distance_display.configure(text=f"当前里程: {self.distance:.2f} m")

# 记录速度和距离数据并更新图表
self.actual_speed_x.append(self.distance)
self.actual_speed_y.append(self.speed * 3.6)
self.actual_speed_line.set_data(self.actual_speed_x, self.actual_speed_y)
self.canvas.draw()

# 继续更新
if self.simulation_running:
self.root.after(int(100 / self.simulation_speed_multiplier), self._update_values)

演示视频