含PID控制器的巡航控制系统ODE建模及微分项实现问题
带PID控制器的巡航控制系统ODE建模与参数估计问题
我需要把带PID控制器的简易巡航控制系统表示为常微分方程(ODE),并用torchdiffeq库做微分仿真来估计KP、KI、KD参数。目前已经知道可以把误差加入状态量来计算积分项,但在微分项的实现上遇到了困难,想找一种不用全局变量的更优系统表示方法。
当前代码用有限差分近似微分项,之前试过把设定值设为0来构建标准ODE形式,但因为计算误差时的速度截断导致系统不准确,而且也没解决微分项的有限差分近似问题。查了类似问题大多涉及高阶微分方程,我对控制理论和拉普拉斯变换不太熟悉,求帮忙。
当前代码如下:
def forward(self, t, x): y, E = x dydt = torch.zeros_like(x) workaround_int = y + y.trunc().detach() - y.detach() error = self.Setpoint - workaround_int inte = E div = (error - self.Error_previous)/self.Dt pid = self.Params[0]*error + self.Params[1]*inte + self.Params[2]*div F_ego = pid D_ego = self.CdA*(y ** 2) if y > 0: F_net = F_ego - D_ego else: F_net = F_ego + D_ego dydt[0] = F_net/self.M dydt[1] = error self.Error_previous = error return dydt
解决方案:扩展状态量实现无全局变量的PID ODE建模
核心思路是把误差本身也纳入系统状态向量,通过状态导数直接计算微分项,彻底摆脱全局变量依赖,同时消除有限差分的近似误差。
1. 重新定义状态向量
将原状态[速度y, 误差积分E]扩展为[速度y, 误差积分E, 当前误差e],这样所有PID计算需要的量都能从当前状态中获取,无需存储历史值。
2. 完整ODE系统推导
基于巡航系统的物理规律和PID原理,推导各状态的导数:
- 速度导数:
dy/dt = (F_pid - D(y))/M,其中D(y)是空气阻力,F_pid为PID输出控制力 - 误差积分导数:
dE/dt = e(积分项的导数等于当前误差) - 误差导数:
de/dt = d(Setpoint - y)/dt = -dy/dt(设定值为常数,导数为0) - PID控制力:
F_pid = Kp*e + Ki*E + Kd*de/dt
3. 无全局变量的代码实现
def forward(self, t, x): y, E, e = x # 状态向量:速度y、误差积分E、当前误差e dydt = torch.zeros_like(x) # 先计算PID输出(这里先占位速度导数,后续会更新) de_dt = -dydt[0] pid = self.Params[0]*e + self.Params[1]*E + self.Params[2]*de_dt # 计算空气阻力与净力 D_ego = self.CdA*(y ** 2) F_net = pid - D_ego if y > 0 else pid + D_ego # 更新各状态的导数 dydt[0] = F_net / self.M dydt[1] = e dydt[2] = -dydt[0] # 误差导数 = -速度导数 return dydt
4. 初始状态初始化
调用torchdiffeq时,初始状态需包含三个变量:
- 初始速度
y0 - 初始误差积分
E0 = 0(初始时刻无积分积累) - 初始误差
e0 = self.Setpoint - y0
关键优势
- 完全符合ODE定义:状态导数仅依赖当前状态,无全局变量或历史值依赖
- 微分项用精确导数计算,替代有限差分,消除数值近似误差
- 移除了原代码中不必要的速度截断逻辑,保证系统准确性
内容的提问来源于stack exchange,提问作者Tomasz Pleśniak
相关产品推荐
相关产品推荐

