PID轨迹跟踪|python实现

PID轨迹跟踪 | python实现

基本概念

关于PID的基本概念网上有很多,简单来说就是用P项(比例项)实现负反馈效果,用I项(积分项)消除稳态误差,用D项(微分项)提供误差信号微分的预测效果。

连续时间下的PID公式如下所示:
u ( t ) = K p ( e ( t ) + 1 T t ∫ 0 t e ( t ) d t + T D d e ( t ) d t ) u(t) = K_p\left(e(t) + \frac{1}{T_t}\int_{0}^{t}e(t)dt + T_D\frac{de(t)}{dt}\right) u(t)=Kp(e(t)+Tt10te(t)dt+TDdtde(t))
式中, u ( t ) u(t) u(t)表示PID控制器的输出,系统的控制输入; e ( t ) = r e f − f e e d b a c k e(t) = ref-feedback e(t)=reffeedback表示误差; K p K_p Kp表示比例增益系数; T t T_t Tt表示积分时间常数, K i = K p T t K_i = \frac{K_p}{T_t} Ki=TtKp表示积分增益系数; T D T_D TD表示微分时间常数, K d = K p T D K_d = K_pT_D Kd=KpTD表示微分增益系数。

离散时间下的PID公式如下所示:
u ( k ) = K p ( e ( k ) + 1 T t ∑ 0 k e ( k ) + T D e ( k ) − e ( k − 1 ) d t ) u(k) = K_p\left(e(k) + \frac{1}{T_t}\sum_{0}^{k}e(k) + T_D\frac{e(k) - e(k-1)}{dt}\right) u(k)=Kp(e(k)+Tt10ke(k)+TDdte(k)e(k1))
式中, d t dt dt表示离散的周期,与连续时间公式中的 d t dt dt表示微分时间单元不同;其它符号的含义与连续时间公式中的含义相同。离散时间的PID用求和代替了积分,用后向差分代替了微分。实际计算机控制时只能是离散的,所以对离散控制进行分析。

PID是一个无模型控制器,没有太多具体的数学上的原理,更多是根据工程中经验而来,因此很难解释公式为什么这么设计、为什么要用这三项,不必在此纠结,只需了解各项作用即可。

比例项 K p e ( k ) K_pe(k) Kpe(k)表示通过比例项增益 K p K_p Kp对误差进行粗调,向着减小误差的方向调整,给出第一部分输出;积分项 K p T t ∑ 0 k e ( k ) \frac{K_p}{T_t}\sum_{0}^{k}e(k) TtKp0ke(k)表示对误差的累计求和,如果存在稳态误差,这一项通过累加放大并向着减小误差的方向调整,给出第二部分输出;微分项 K p T D d t ( e ( k ) − e ( k − 1 ) ) \frac{K_pT_D}{dt}(e(k) - e(k-1)) dtKpTD(e(k)e(k1))表示通过误差信号过去对时间的导数,对误差信号未来的一个预测(这里是假设误差信号一阶时间连续,而在实际情况中也大多如此),提前向着减小误差的方向调整,给出第三部分输出。最后对三部分的输出进行求和,得到最终的PID输出 u ( t ) u(t) u(t)

问题描述

用PID实现轨迹跟踪,就是计算前轮转向角 δ f \delta_f δf,使得车辆与参考轨迹上的参考点 P r = ( x r , y r ) P_r=(x_r, y_r) Pr=(xr,yr)之间的横向误差 e y e_y ey趋近与0。那么这里有两个问题,一是参考点 P r P_r Pr怎么找,二是横向误差 e y e_y ey怎么算。

找参考点

参考点就是人为定义的一个点,可以找当前离自车最近的点为参考点,也可以类似于纯跟踪控制里面找一个预瞄点作为参考点。

实际仿真做下来感觉找离自车最近的点有一个问题,就是车必须在有移动的情况下打方向才会向着参考轨迹移动。但是以参考线上最近点为参考点的话,会导致车还没靠近前一个参考点就要向后一个参考点运动了,就是说留给车的运动空间或者调整距离不够,整个车的行为很滞后。

因此可以考虑找一个预瞄点为参考点,这样就能打一个提前量,车的行为不至于滞后,在轨迹跟踪时能表现好一点。

算横向误差 e y e_y ey


图3 PID控制横向误差

e y = l d sin ⁡ ( θ e ) l d = ( x r − x h ) 2 + ( y r − y h ) 2 θ e = α − ψ α = arctan ⁡ ( y r − y h x r − x h ) \begin{aligned} &e_y = l_d\sin(\theta_e)\\ &l_d = \sqrt{(x_r - x_h)^2 + (y_r - y_h)^2}\\ &\theta_e = \alpha - \psi\\ &\alpha = \arctan{(\frac{y_r - y_h}{x_r - x_h})} \end{aligned} ey=ldsin(θe)ld=(xrxh)2+(yryh)2 θe=αψα=arctan(xrxhyryh)
其中 P h = ( x h , y h ) P_h = (x_h, y_h) Ph=(xh,yh)表示自车的当前坐标点。

PID控制的目标是让某个误差量为0,这里以横向误差 e y e_y ey为误差量,其中 l d l_d ld始终为正,误差量的正负号由 sin ⁡ ( θ e ) \sin{(\theta_e)} sin(θe)提供。就是说参考点在自车左侧时误差为正,参考点在自车右侧时误差为负。需要注意下编程时跟自车方向盘打角的一个正负关系对应,方向盘打角往左为负往右为正时需要加一个负号。

应用demo

PID轨迹跟踪演示的完整代码在github仓库,这里仅给出PID计算部分。图中轨迹跟踪效果比较一般的原因个人理解有几点,一是PID是无模型控制,这里只用了一套参数进行控制,像LQR和MPC好歹模型参数也在实时更新以及在实时求解;二是控制周期给太短了,参考点实时在更新;三是参数没有仔细整定。有兴趣的话可以从这三个角度入手进行改进,以提升轨迹跟踪效果。


图3 PID控制轨迹跟踪
class PID:
    """位置式实现
    """
    def __init__(self, upper, lower, k = [1., 0., 0.]):
        self.kp, self.ki, self.kd = k

        self.e = 0.0      # error
        self.pre_e = 0.0  # previous error
        self.sum_e = 0.0  # sum of error

        self.upper_bound = upper    # upper bound of output
        self.lower_bound = lower    # lower bound of output

    def set_param(self, k, upper, lower):
        self.kp, self.ki, self.kd = k
        self.upper_bound = upper
        self.lower_bound = lower

    def cal_output(self, ref, feedback):   # calculate output
        self.e = ref - feedback

        pid_out = self.e * self.kp + self.sum_e * self.ki + (self.e - self.pre_e) * self.kd
        if pid_out < self.lower_bound:
            pid_out = self.lower_bound
        elif pid_out > self.upper_bound:
            pid_out = self.upper_bound

        self.pre_e = self.e
        self.sum_e += self.e

        return pid_out
  • 29
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值