当前位置:   article > 正文

自动驾驶控制算法---横向控制(MPC/LQR)和纵向控制(PID算法及基于CARLA的Python代码实现)_carla的mpc

carla的mpc

1、给控制的规划轨迹

  给出一条轨迹,然后去找匹配点(与真实位置距离最短的点),再由匹配点去算出投影点,然后让车按照真实位置与投影点的误差去算出u得到控制量使得误差err为0。但实际上无人驾驶环境是动态的,事先设计的轨迹未必是合理的轨迹,而且每次寻找匹配点都要遍历一遍耗费算力。

  因此,规划的轨迹一般是带有时间的。假设初始条件x(0)x(0)˙x(0)¨y(0)y(0)y(0)和终止条件x(T)x(T)˙x(T)¨y(xend)y(xend)y(xend),通过这些边界条件可以算出五次多项式(六个未知数)x(t)y(x)y(x)代表对曲线的斜率有要求)。

  通过中间变量求导得出:

y(t)=y(x(t))

y˙(t)=y(x(t))x(t)˙

y¨(t)=y(x(t))x(t)˙2+y(x(t))x(t)¨

  这就有了y(t)

 1.1、横向控制接口

  t时刻轨迹给出此时刻xryr的值,则:

xr(t)=x(t)

yr(t)=y(t)

θr(t)=arctan{y[x(t)]}

kr(t)=y[x(t)](1+y[x(t)]2)1.5

  得到这些横向控制接口,然后就可以用反馈控制LQR+前馈控制δf进行横向控制。

 1.2、纵向控制接口

  如上图,纵向位置误差es=(xxr)τm

  速度误差vps˙=xr(t)˙2+yr(t)˙2vxcos(φθr(t))vysin(φθr(t))1kr(t)ed

  加速度ap=xr(t)¨2+yr(t)¨2

  纵向控制去用双PID去控制速度,即用es作为位置PID的输入,速度差和位置PID的输出作为速度PID的输入,速度PID的输出和加速度一起作为加速度给汽车。

2、PID纵向控制

  速度和加速度匹配:规划的速度是10,因此希望车以10的速度在跑。因为加速度一开始是0,必然要有一个很大的加速度让车加速起来,然后在加速过程加速度逐渐的减小,速度刚好是10的时候加速度刚好是0,速度没有到10的时候,一直有加速度,一直不断在加速,这叫匹配的加速度。

  用PID控制纵向速度,可以自动的匹配加速度,期望的速度去减当前的速度,差值作为加速度信号输进去。速度-速度=加速度(忽略量纲),看成一个数减一个数,只要满足在刚开始的时候离目标很远加速度很大,在接近目标的时候加速度逐渐减缓,达到目标时候加速度变为0就可以了。

u(t)=Kp(e(t)+1Ti0te(t)dt+Tdde(t)dt)

  PID:比例P(proportional)、积分I(integral)、微分D(derivative)。

  (1)、P

  P就是个比例项,即对误差信号乘个数值。

  比例项P的作用:可以加快信号到达目标的速度,将误差信号(期望速度和实际速度之差)乘个比例项P能够讲误差信号扩大几倍,然后加快达到期望速度的速度,缩小稳态误差。

  比例项P的问题:

  ①、而且不能消除稳态误差,因为不管放大10倍、100倍,总有时刻因为误差信号乘完比例项P后依然太小了不起作用。

  ②、比例项P值较小的情况下,响应速度变慢了,而且最终离目标的误差较大;比例项P值较大的情况下,虽然响应速度比较快,但是振荡很严重,可能一点点微小的误差加速度就会放得很大,就会很敏感、波动。

  (2)、I

  I就是对信号积分,然后输出积分的值(有滞后的作用)。  

  积分项I的作用:为了消除稳态误差。只要实际速度和期望速度时间的差值(即误差信号)不是0,那么积分项I就会一直积分,信号一直有,就会消除稳态误差。积分会随着时间的增长而越来越大,积分控制器会明显的消除稳态误差。

  积分项I的问题:会带来超调。比如本来目标是10,是从50降到10的,但是降到了0,再从0上升到10(虽然收敛了,但是产生了超调),我们理想的控制是从50逐渐的降到10而不带有超调。

  (3)、D

  D就是对信号微分,然后输出微分的值(这里的微分D不是数学意义上的微分,严格来说是一个数值微分,因为信号离散了没有办法做数学意义上的解析的微分,所以要进行相关滤波操作,因为数值微分很容易震荡) 。

  微分D的作用:抑制超调(微分就是可以提前控制,达到抑制超调的目的)。

  在实际应用中比例项也会引起超调,因为在实际应用中信号是有延迟的,无论你踩到刹车还是油门,发动机还是电机开始转进行工作都有一定的延迟,一旦有延迟的话比例项就会产生超调,微分D就起作用了。

总结:比例项P可以加快信号到达目标的速度,p过大在实际应用中会带来超调(因为延迟),就会用微分D来消掉实际应用中比例项P产生的超调。积分项I一般不常用(微分D降比例P的超调还是比较容易的,但是降积分I的超调就比较难 ),虽然积分项I能够消除稳态误差,但是会带来很大的超调,而且消起来比较麻烦(除非稳态误差特别大),而且一般积分带来的超调只能通过降低比例项(比例P降低一点)来减小超调,但是比例P减小导致响应变慢了。一般来说在控制的时候用比例项P、微分D就够了,保证误差在一定容忍范围内。

一般来说经验是先调比例项P、再调微分D、最后再调积分项I。只要控制的精度达到一定范围就可以了,不要求完全的精确,因为要达到特别精确一定要增大积分项I过渡的消除稳态误差,必然会导致超调,然后再去调增大微分D去抑制超调,必然会导致噪声增大(伯德图的对数幅频曲线往上翘 )。

P+D:又快又满足精度要求,I:给个非常小的积分I起一点作用,减小稳态误差。

3、结合自动驾驶问题的纵向速度控制PID代码实现

  1、导入库文件

  1. import numpy as np
  2. import math
  3. import carla
  4. import cvxopt
  5. from collections import deque

  2、初始化信息(比例系数k_p、积分系数k_i、微分系数k_d等)

  1. class Longitudinal_PID_controller(object): #纵向控制
  2. def __init__(self, ego_vehicle, k_p=1.15, k_i=0, k_d=0, d_t=0.01): # 手动调节参数k_p、k_i、k_d
  3. """
  4. 采用PID进行纵向速度控制,包括比例项P、积分项I、微分项D
  5. ego_vehicle: 是carla中的主车
  6. k_p: 比例项系数
  7. k_i: 积分项系数
  8. k_d: 微分项系数
  9. d_t: 控制间隔
  10. """
  11. self.vehicle = ego_vehicle # ego_vehicle是carla中的主车
  12. self.k_p = k_p # 比例系数
  13. self.k_i = k_i # 积分系数
  14. self.k_d = k_d # 微分系数
  15. self.d_t = d_t # 控制间隔
  16. self.target_speed = None # 目标速度
  17. self.error_buffer = deque(maxlen=60) # 设置一个误差缓存区,用于积分项和差分项的计算
  18. self.error_threshold = 1 # 设定一个阈值

  3、PID的实现(输出速度控制量u)

  1. def PID_control(self, target_speed):
  2. """
  3. 函数:计算PID控制的输出
  4. return: u
  5. """
  6. v = self.vehicle.get_velocity() # self.vehicle.get_velocity()的格式:Vector3D(x=0.000000, y=0.000000, z=-0.194462)
  7. v_length = 3.6 * math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) # 速度大小,m/s转为km/h,1m/s = 3.6km/h
  8. self.target_speed = target_speed # 目标速度
  9. error = self.target_speed - v_length # 速度误差
  10. self.error_buffer.append(error) # 将新的速度误差放入缓存区,如果缓存区满了,最左边的溢出,整体数据左移一位,新的数据加在最右边
  11. if len(self.error_buffer) >= 2:
  12. integral_error = sum(self.error_buffer) * self.d_t # 积分误差,为了解决稳态误差
  13. derivative_error = (self.error_buffer[-1] - self.error_buffer[-2]) / self.d_t # 微分误差,为了缓解超调
  14. else:
  15. integral_error = 0.0
  16. derivative_error = 0.0
  17. if abs(error) > self.error_threshold: # 一旦出现误差大于阈值的情况,只有比例项发挥作用
  18. integral_error = 0.0
  19. self.error_buffer.clear()
  20. u = self.k_p * error + self.k_i * integral_error + self.k_d * derivative_error
  21. return u

  4、整个控制算法(结合MPC/LQR的横向控制和PID的纵向控制)

  1. class Vehicle_control(object):
  2. def __init__(self, ego_vehicle, vehicle_para, pathway, controller_type="MPC_controller"):
  3. """
  4. 初始化车辆的油门范围、转角范围、刹车范围
  5. """
  6. self.vehicle = ego_vehicle # ego_vehicle是carla中的主车
  7. self.max_throttle = 1 # 最大油门 = 1,最小油门 = 0
  8. self.max_brake = 1 # 最大刹车 = 1,最小刹车 = 0
  9. self.max_steer = 1 # 最大转角 = 1
  10. self.min_steer = -1 # 最小转角 = 1
  11. self.Lateral = None
  12. if controller_type == "MPC_controller": # 采用MPC横向控制
  13. self.Lateral = "MPC_controller"
  14. self.Lateral_control = Lateral_MPC_controller(ego_vehicle, vehicle_para, pathway)
  15. elif controller_type == "LQR_controller": # 采用LQR横向控制
  16. self.Lateral = "LQR_controller"
  17. self.Lateral_control = Lateral_LQR_controller(ego_vehicle, vehicle_para, pathway)
  18. self.Longitudinal_control = Longitudinal_PID_controller(ego_vehicle)
  19. def run_step(self, target_speed):
  20. """
  21. 函数:计算横向控制与纵向控制的流程
  22. return: control
  23. """
  24. control = carla.VehicleControl() # 驾驶控制来管理车辆的基本运动,包括throttle、steer、brake、hand_brake、reverse、Manual_gear_shift、gear
  25. control.hand_brake = False # 不使用手刹
  26. control.reverse = False # 车辆不向后移动
  27. control.manual_gear_shift = False # 不通过手动换档来控制车辆
  28. control.gear = 1 # 车辆行驶的档位 = 1
  29. if self.Lateral == "MPC_controller":
  30. steer_r = self.Lateral_control.MPC_control() # 返回横向控制的转角
  31. if self.Lateral == "LQR_controller":
  32. steer_r = self.Lateral_control.LQR_control() # 返回横向控制的转角
  33. acceleration_r = self.Longitudinal_control.PID_control(target_speed) # 返回纵向控制的加速度
  34. # 横向控制限定范围
  35. if steer_r >= 0:
  36. control.steer = min(self.max_steer, steer_r)
  37. else:
  38. control.steer = max(self.min_steer, steer_r)
  39. # 纵向控制限定范围
  40. if acceleration_r >= 0:
  41. control.throttle = min(self.max_throttle, acceleration_r)
  42. control.brake = 0
  43. else:
  44. control.throttle = 0
  45. control.brake = max(self.max_brake, acceleration_r)
  46. v = self.vehicle.get_velocity() # self.vehicle.get_velocity()的格式:Vector3D(x=0.000000, y=0.000000, z=-0.194462)
  47. v_length = math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) # 速度大小,m/s
  48. return control
  49. control_object = Vehicle_control(ego_vehicle, vehicle_para, pathway, controller_type="MPC_controller")
  50. control_object.run_step(target_speed)
  51. ego_vehicle.apply_control(control_object) # apply_control:将横向控制以及纵向控制输出的信息交给主车ego_vehicle去执行命令

到此完成了整个控制算法的讲解以及代码编写。感谢!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/429151
推荐阅读
相关标签
  

闽ICP备14008679号