You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何基于PID控制的球杆状态空间系统始终不稳定?

球杆系统PID控制发散问题排查与解决思路

问题概述

无法找到合适的控制器增益稳定球杆系统,系统目标是通过控制电压(V)将小球移动至梁上指定位置(r)。系统始终不稳定,控制器无法及时响应自身引发的振荡;调试发现,A矩阵完成首次r更新前,qdot值已过大,无法通过增益抵消,最终导致系统发散。其中A为线性化6×6矩阵,B为[0,0,0,0,0,1]的6×1矩阵。

已尝试的方案:添加Ziegler-Nichols整定器、缩短current_time步长、限制pid_output,但系统仍发散。

相关代码

#Code to determine the A and B matrix

import numpy as np
import matplotlib.pyplot as plt

#Define constants, state space variables, and calculate_system_matrices function...

class PIDController:
    def __init__(self, Kc, Ti, Td, setpoint, A, B, C, D):

        #initializing, for example:

        self.max_output = 24
    
    def compute(self, current_value, current_time):
        error = self.setpoint - current_value[0]  
        
        delta_time = current_time - self.prev_time
        self.prev_time = current_time
        
        # Proportional term
        proportional = self.Kc * error
        self.proportional_values.append(proportional)  
        
        # Integral term
        if self.Ti != 0:
            self.integral += error * delta_time
            self.integral = np.clip(self.integral, - 
            self.max_output/self.Ti, self.max_output/self.Ti)  
        else:
            self.integral = 0.0 
        self.integral_values.append(self.integral)  # Store integral term
        
        
        # Derivative term
        if delta_time != 0:
            self.derivative = (error - self.prev_error) / delta_time
        else:
            # Handle division by zero error
            self.derivative = 0.0  
        self.derivative_values.append(self.derivative)  
    
        self.prev_error = error
        
        # PID control law with output limitation
        if self.Ti != 0 and self.Td !=0:
            pid_output = proportional + self.Kc/self.Ti * self.integral + 
                         self.Kc * self.Td * self.derivative
        elif self.Td !=0:
            pid_output = proportional + self.Kc * self.Td * self.derivative
        
        else:
            pid_output = proportional
         
        # Apply output saturation
        pid_output = np.clip(pid_output, -self.max_output, self.max_output)
       
        # State-space model simulation
        x_next = np.dot(self.A, current_value) + self.B * pid_output
        output = np.dot(self.C, current_value) + self.D * pid_output
        
        self.r_values.append(current_value[0])
        self.error_values.append(error)
    
        return output, x_next


if __name__ == "__main__":
     # Calculate system matrices A and B from the provided system dynamics
      A, B = calculate_system_matrices() 
      C = np.array([[1., 0., 0., 0., 0., 0.]])

     # Define output matrix C and feedforward matrix D based on your 
                         requirements
     C = np.array([[1., 0., 0., 0., 0., 0.]])
     D = np.array([[0.]])

    # Setpoint is the target value
    setpoint = 0.2
    
    # Define initial state
    initial_state = np.array([[0.1], [0], [0], [0], [0], [0]])
    
    Kc = 10
    Ti = 0
    Td = 1
    
    # Initialize PID controller with tuned parameters
    pid = PIDController(Kc, Ti, Td, setpoint, A, B, C, D)
    
    # Simulation parameters
    num_steps = 100
    current_time = 0.0  # Initialize current time
    
    # Lists to store results
    outputs = []
    r_values = []
    error_values = []
    
    current_value = initial_state
    # Simulating the system
    for _ in range(num_steps):
        # Compute control signal and update state
        output, current_value = pid.compute(current_value, current_time)
        
        # Store results
        outputs.append(output)
        r_values.append(current_value[0])  # Assuming position is the first element of current_value
        error_values.append(pid.setpoint - current_value[0])  # Calculate error
        
        # Increment time
        current_time += 0.1  # Increment current_time by 1 (assuming unit time steps)
        print("current_time:",current_time)
        #print("Output:", output, "State:", current_value)

关键问题排查与解决建议

1. PID控制器初始化缺失

你的PIDController.__init__方法缺少多个关键变量的初始化,首次调用compute时会直接报错或计算异常:

  • 必须初始化self.prev_time = 0.0、self.prev_error = 0.0、self.integral = 0.0
  • 存储数据的列表(proportional_values、integral_values、derivative_values、r_values、error_values)也需要在__init__中创建,否则会出现AttributeError

修改后的__init__示例:

def __init__(self, Kc, Ti, Td, setpoint, A, B, C, D):
    self.Kc = Kc
    self.Ti = Ti
    self.Td = Td
    self.setpoint = setpoint
    self.A = A
    self.B = B
    self.C = C
    self.D = D
    self.max_output = 24
    # 初始化状态变量
    self.prev_time = 0.0
    self.prev_error = 0.0
    self.integral = 0.0
    # 初始化存储列表
    self.proportional_values = []
    self.integral_values = []
    self.derivative_values = []
    self.r_values = []
    self.error_values = []

2. 导数项计算逻辑错误

当前导数项计算的是误差的变化率,这会放大初始误差突变带来的控制输出波动,对于球杆这种不稳定系统,极易引发qdot值瞬间过大。标准PID的导数项应基于测量值的变化率,而非误差变化率:

# 修改导数项计算逻辑
if delta_time != 0:
    # 存储上一次的位置测量值
    if not hasattr(self, 'prev_r'):
        self.prev_r = current_value[0]
    # 导数项为测量值的负变化率
    self.derivative = -(current_value[0] - self.prev_r) / delta_time
    self.prev_r = current_value[0]
else:
    self.derivative = 0.0

3. 线性化模型的适用范围问题

球杆系统是非线性系统,线性化仅在平衡点附近有效。如果calculate_system_matrices是在r=0处线性化,但你的设定点是0.2,偏离平衡点过远,线性化模型会严重失配,导致PID控制失效。需要在目标设定点r=0.2处重新线性化A和B矩阵。

4. 抗积分饱和机制缺失

虽然你对积分项做了钳位,但逻辑不够完善。当输出达到饱和时,积分项仍可能持续累积,导致系统退出饱和后出现超调甚至发散。可以添加积分分离机制:

# 积分项添加积分分离逻辑
if self.Ti != 0:
    # 仅当误差在阈值内时才累积积分
    if abs(error) < 0.05:  # 阈值根据系统调整
        self.integral += error * delta_time
        self.integral = np.clip(self.integral, -self.max_output/self.Ti, self.max_output/self.Ti)
    else:
        self.integral = 0.0

5. 尝试更适合的控制策略

球杆系统是典型的欠驱动不稳定多变量系统,仅用位置反馈的PID很难稳定。建议尝试:

  • LQR状态反馈控制:利用全部6个状态量(位置r、球速度、杆角度、杆角速度等)设计控制器,通过求解Riccati方程得到最优反馈增益,比PID更适合这类系统。
  • 串级PID控制:分别设计小球位置的外环PID和杆角度的内环PID,利用内环快速稳定杆的姿态,再通过外环调整小球位置。

内容的提问来源于stack exchange,提问作者Daan Vissers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:04:53