python
import matplotlib.pyplot as plt
from controller.pid import PID
from system.system_model import ThermalSystem
def run_simulation():
# Time parameters
dt = 0.1
sim_time = 180 # seconds (3 minutes)
steps = int(sim_time / dt)
# System + PID
system = ThermalSystem() # uses gain=4.0, tau=12.0
pid = PID(kp=3.0, ki=1.0, kd=0.2, dt=dt)
# Target temp
setpoint = 50.0
# Logging
times = []
temps = []
outputs = []
setpoints = []
# Start from current system temp
measurement = system.temperature
for i in range(steps):
current_time = i * dt
# PID control
heater_power = pid.compute(setpoint=setpoint, measurement=measurement)
heater_power = max(0.0, min(1.0, heater_power)) # clamp 0--1
# Update plant
measurement = system.update(heater_power=heater_power, dt=dt)
# Log
times.append(current_time)
temps.append(measurement)
outputs.append(heater_power)
setpoints.append(setpoint)
# Plots using a shared figure and axes
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
# Temperature subplot
ax1 = axes[0]
ax1.plot(times, temps, label="Measured Temperature")
ax1.plot(times, setpoints, "--", label="Setpoint")
ax1.set_xlabel("Time (s)")
ax1.set_ylabel("Temperature (°C)")
ax1.set_title("PID Temperature Control Simulation")
ax1.legend()
ax1.grid(True)
# Heater power subplot
ax2 = axes[1]
ax2.plot(times, outputs, label="Heater Power (0--1)")
ax2.set_xlabel("Time (s)")
ax2.set_ylabel("Power")
ax2.set_title("Heater Output Over Time")
ax2.set_ylim(0, 1.05)
ax2.legend()
ax2.grid(True)
# Layout fixes so titles do not jump
fig.tight_layout()
fig.subplots_adjust(top=0.9, hspace=0.35)
plt.show()
if __name__ == "__main__":
run_simulation()
main.py的内容:建一个温度系统 -> 建一个PID控制器 -> 循环控制180秒 -> 记录数据 -> 画图
PART1-导入库
python
import matplotlib.pyplot as plt
from controller.pid import PID
from system.system_model import ThermalSystem
matplotlib.pyplot:拿来画图PID:导入控制器ThermalSystem:导入温度系统
PART2-定义主函数
python
def run_simulation():
把"跑一次完整仿真"这件事打包成一个函数
PART3-时间参数
python
dt = 0.1
sim_time = 180 # seconds (3 minutes)
steps = int(sim_time / dt)
dt = 0.1 表示 每次循环代表 0.1 秒。
sim_time = 180 表示 总共模拟 180 秒,也就是 3 分钟。
steps = int(sim_time / dt) 表示 总步数 = 180 / 0.1 = 1800 步,也就是说,这个程序会循环 1800 次。
PART4-创建系统和控制器
python
system = ThermalSystem() # uses gain=4.0, tau=12.0
pid = PID(kp=3.0, ki=1.0, kd=0.2, dt=dt)
这两行是整个项目最核心的初始化。
system = ThermalSystem() 创建一个"温度对象",这个对象是代码里的温度模型,它内部会记住当前温度,并且根据"加热功率"更新温度。这个模型定义在 system_model.py 里。
PART5-设置目标温度
python
setpoint = 50.0
PART6-准备记录数据
python
times = []
temps = []
outputs = []
setpoints = []
times:每个时刻temps:每个时刻的温度outputs:每个时刻的加热功率setpoints:每个时刻的目标值
这四个列表是拿来存数据的,因为最后要画图。
PART7-初始测量值
python
measurement = system.temperature
一开始先读取一下当前温度,因为一开始还没开始控制,所以当前温度就是系统初始温度。
根据系统模型,这个初始温度默认就是环境温度 20°C。
PART8-主循环
python
for i in range(steps):
接下来开始循环 1800 次,每次代表 0.1 秒
python
current_time = i * dt
记录当前时刻
python
heater_power = pid.compute(setpoint=setpoint, measurement=measurement)
PID控制:把"目标温度"和"当前温度"交给 PID,让它算出应该给多大加热功率。
根据误差,决定控制动作,PID 的具体计算在 pid.py 里,里面核心是 error = setpoint - measurement。
python
heater_power = max(0.0, min(1.0, heater_power)) # clamp 0--1
把加热功率强行限制在 0 到 1 之间,这是饱和限制**。**
python
measurement = system.update(heater_power=heater_power, dt=dt)
把刚刚算出的加热功率送给温度系统,让系统往前走 0.1 秒,然后得到新的温度测量值
python
times.append(current_time)
temps.append(measurement)
outputs.append(heater_power)
setpoints.append(setpoint)
这四句就是把这一时刻的数据存起来,这样最后就可以画出完整曲线。
PART9-画图
python
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
创建两张子图,画一个窗口,里面放上下两张图,上面画温度,下面画功率
第一张图:温度曲线
python
ax1 = axes[0]
ax1.plot(times, temps, label="Measured Temperature")
ax1.plot(times, setpoints, "--", label="Setpoint")
ax1.set_xlabel("Time (s)")
ax1.set_ylabel("Temperature (°C)")
ax1.set_title("PID Temperature Control Simulation")
ax1.legend()
ax1.grid(True)
这段的意思:
- 横轴:时间
- 纵轴:温度
- 蓝线:实际温度
- 虚线:目标温度 50°C
第二张图:功率曲线
python
ax2 = axes[1]
ax2.plot(times, outputs, label="Heater Power (0--1)")
ax2.set_xlabel("Time (s)")
ax2.set_ylabel("Power")
ax2.set_title("Heater Output Over Time")
ax2.set_ylim(0, 1.05)
ax2.legend()
ax2.grid(True)
这段的意思:
- 横轴:时间
- 纵轴:加热功率
- 显示 PID 在每个时刻输出了多大的控制量
最后显示图像
python
fig.tight_layout()
fig.subplots_adjust(top=0.9, hspace=0.35)
plt.show()
这几句主要是为了让图排版更整齐,然后弹出窗口显示。
PART10-程序入口
python
if __name__ == "__main__":
run_simulation()
如果这个文件被直接运行,就执行 run_simulation()