PID控制

一、PID

1. P Controller

PID控制是控制学中一个很广的领域。这里首先以 P Controller 的例子讲起,首先给定一条规划好的参考轨迹线,为x轴,小车一开始在预定线路的上方,如图1所示,我们需要让小车重新行驶回预先规划的线路。


图1. CTE

转向与横切误差CTE是小车与参考轨迹线的垂直距离。因为我们参考的轨迹线是x轴,所以CTE就是小车的当前y位置。之后,通过规划线路的距离CTE获得调整的转向角度 \(\alpha\),即
\begin{equation}
\alpha = -\tau_p \cdot CTE
\end{equation}

这里的\(\tau_p\)是P Controller比例系数。然后我们调用move使得小车根据获得的转向角度开始移动。

def run(robot, tau, n=100, speed=1.0):
   x_trajectory = []
   y_trajectory = []
   # steering = -tau * crosstrack_error
   for i in range(n):
       # move(self, steering, distance, tolerance=0.001, max_steering_angle=np.pi / 4.0)
       crosstrack_error = robot.y - 0
       steering = -tau * crosstrack_error
       robot.move(steering, speed)
       x_trajectory.append(robot.x)
       y_trajectory.append(robot.y)

   return x_trajectory, y_trajectory

robot = Robot()
robot.set(0, 1, 0)  # 初始的坐标(0,1),方向角0
x_trajectory, y_trajectory = run(robot, 0.2)  # tau = 0.2

最后的小车的运动曲线如图2所示。虽然小车会向参考轨迹线靠拢,但也没有预想的那样直接运行到参考线上,而是在参考线附近不停的震荡,且有失控的趋势。


图2. P Controller

2. PD Controller

如果单使用P Controller,会导致轨迹震荡,这是因为P Controller在小车在到达指定轨迹线路时,调整的转向角度的值没有立即变为0。为了使调整的转向角度能随着\(|CTE|\)的减少而减小角度,我们可以增加一个D Controller。微分的作用主要用于克服被控对象的滞后,在温度控制系统中也经常使用。在加入D Controller的功能后, \(\alpha\)的表达式如下

\begin{equation}
\alpha = -\tau_p \cdot CTE - \tau_d \frac{d}{dt} CTE
\end{equation}

这里的$\tau_d $是D Controller比例系数。然后我们调用move使得小车根据获得的转向角度开始移动。

# steering = -tau_p * CTE - tau_d * diff_CTE
# where differential crosstrack error (diff_CTE)
# is given by CTE(t) - CTE(t-1)
def run(robot, tau_p, tau_d, n=100, speed=1.0):
    x_trajectory = []
    y_trajectory = []
    pre_cte = robot.y
    for i in range(n):
        cte = robot.y
        diff_CTE = cte - pre_cte
        pre_cte = robot.y
        steer = -tau_p * cte - tau_d * diff_CTE
        robot.move(steer, speed)
        x_trajectory.append(robot.x)
        y_trajectory.append(robot.y)
    return x_trajectory, y_trajectory
    
robot = Robot()
robot.set(0, 1, 0) # 初始的坐标(0,1),方向角0
x_trajectory, y_trajectory = run(robot, 0.2, 3.0) # tau_p = 0.2 , tau_d = 3.0

最后的小车的运动曲线如图3所示。小车很快调整到规划的线路,且消除了震荡。


图3. PD Controller

3. PID Controller

在现实的系统中,总会有一些意料之外的系统偏差引入,这部分累计误差的消除可以借助 I Controller。积分的作用主要用于消除自动控制系统的剩余误差,在加入 I Controller的功能后, \(\alpha\)的表达式如下

\begin{equation}
\alpha = -\tau_p \cdot CTE - \tau_d \frac{d}{dt} CTE - \tau_i \sum CTE
\end{equation}

这里的\(\tau_i\)是 I Controller比例系数。然后我们调用move使得小车根据获得的转向角度开始移动。

# steering = -tau_p * CTE - tau_d * diff_CTE - tau_i * int_CTE
def run(robot, tau_p, tau_d, tau_i, n=100, speed=1.0):
    x_trajectory = []
    y_trajectory = []
    # TODO: your code here
    pre_cte =robot.y
    int_CTE = 0
    for i in range(n):
        CTE = robot.y
        diff_CTE = CTE - pre_cte
        pre_cte = CTE
        int_CTE += CTE
        steering = -tau_p * CTE - tau_d * diff_CTE - tau_i * int_CTE
        robot.move(steering, speed)

        x_trajectory.append(robot.x)
        y_trajectory.append(robot.y)
    return x_trajectory, y_trajectory

robot = Robot()
robot.set(0, 1, 0)  # 初始的坐标(0,1),方向角0
robot.set_steering_drift(10.0/180.0*np.pi) # 设定系统偏差
x_trajectory, y_trajectory = run(robot, 0.2, 3.0, 0.008)   # tau_p = 0.2 , tau_d = 3.0 , tau_d = 0.008

最后的小车的运动曲线如图4所示。小车虽然一开始因为系统偏差导致越界,但是随着时间的累积,积分项会纠正系统误差。而积分项的加入会增加系统控制的鲁棒性。


图4. PID Controller

二、Twiddle算法

Twiddle算法是一种局部爬山算法,其通过遍历\(\tau_p\)\(\tau_i\)\(\tau_d\)所有可能值,以获得较优的结果,这降低了PID调参的难度。

def twiddle(tol=0.2):
    # Don't forget to call `make_robot` before every call of `run`!
    p = [0, 0, 0]
    dp = [1, 1, 1]
    robot = make_robot()
    x_trajectory, y_trajectory, best_err = run(robot, p)
    #  twiddle loop here
    it = 0
    while(sum(dp) > tol):
        print("Iteration {}, best error = {}".format(it, best_err))
        for i in range(len(p)):
            p[i] += dp[i]
            robot = make_robot()
            x_trajectory, y_trajectory, err = run(robot, p)

            if err < best_err:
                best_err = err
                dp[i] *= 1.1
            else:
                p[i] -= 2 * dp[i]
                robot = make_robot()
                x_trajectory, y_trajectory, err = run(robot, p)

                if err < best_err:
                    best_err = err
                    dp[i] *= 1.1
                else:
                    p[i] += dp[i]
                    dp[i] *= 0.9
        it += 1

    return p, best_err

params, err = twiddle()

twiddle算法与其他控制器的区别比较,如图5所示。


图5. 控制器比较图

三、调参体验

3.1 口诀

业界有个整定口诀:参数整定找最佳, 从小到大顺序查。先是比例后积分, 最后再把微分加。曲线振荡很频繁, 比例度盘要放大。曲线漂浮绕大弯, 比例度盘往小扳。曲线偏离回复慢, 积分时间往下降。曲线波动周期长, 积分时间再加长。曲线振荡频率快, 先把微分降下来。动差大来波动慢, 微分时间应加长。理想曲线两个波, 前高后低四比一。一看二调多分析, 调节质量不会低。

整定步骤:先比例,后积分,再微分。

3.2 实例

经验凑试法在实践中最为实用。在调节参数时,必须认真观察系统响应情况,根据系统的响应情况决定调整那些参数。 以小车为例,首先调节\(\tau_p\),因为\(\tau_p\)与问题建立线性关系,所以\(\tau_p\)越大,响应越快。然后考虑怎么用积分项消除系统的偏差。最后再调节\(\tau_d\),以此来减小系统震荡。

  1. 起初,我将所有PID系数设置为0.01。
  2. 首先,我保持I和D的系数不变,只调整了P的参数,发现当P=1.5时,相对较好。
  3. 之后,我调整了D的参数,发现当D =6时,它是比较好的,我可以跑很长的距离。
  4. 最后,保持D的参数不变,调整P的参数,当P=0.25时,顺利跑完整个路程。
P = 0.25, I = 0.01, D = 6

posted @ 2021-03-01 13:57  iamwasabi  阅读(868)  评论(0编辑  收藏  举报