cover

PID控制

一、PID

1. P Controller

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

图1. CTE

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

<mtable><mtr><mtd>α=τpCTE</mtd></mtr></mtable>\begin{aligned} \alpha = -\tau_p \cdot CTE \end{aligned}

这里的τp\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|CTE|的减少而减小角度,我们可以增加一个D Controller。微分的作用主要用于克服被控对象的滞后,在温度控制系统中也经常使用。在加入D Controller的功能后, α\alpha的表达式如下

<mtable><mtr><mtd>α=τpCTEτd<mfrac>ddt</mfrac>CTE</mtd></mtr></mtable>\begin{aligned} \alpha = -\tau_p \cdot CTE - \tau_d \frac{d}{dt} CTE \end{aligned}

这里的$\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的表达式如下

<mtable><mtr><mtd>α=τpCTEτd<mfrac>ddt</mfrac>CTEτiCTE</mtd></mtr></mtable>\begin{aligned} \alpha = -\tau_p \cdot CTE - \tau_d \frac{d}{dt} CTE - \tau_i \sum CTE \end{aligned}

这里的τi\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算法是一种局部爬山算法,其通过遍历τp\tau_pτi\tau_iτd\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 实例

经验凑试法在实践中最为实用。在调节参数时,必须认真观察系统响应情况,根据系统的响应情况决定调整那些参数。 以小车为例,首先调节τp\tau_p,因为τp\tau_p与问题建立线性关系,所以τp\tau_p越大,响应越快。然后考虑怎么用积分项消除系统的偏差。最后再调节τd\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

This article was posted on Instant Matataki,

Disclaimer: This article was uploaded and posted by user "一二三", and the content is the author's independent opinion. It does not represent Instant Matataki's position, does not constitute investment advice, please treat it with caution.

0/500