openpilot横向控制怎么做到的?期望曲率限速+延迟补偿,把方向盘扭矩误差压进米级车道
openpilot横向控制怎么做到的期望曲率限速延迟补偿把方向盘扭矩误差压进米级车道【免费下载链接】openpilotopenpilot is an operating system for robotics. Currently, it upgrades the driver assistance system on 300 supported cars.项目地址: https://gitcode.com/GitHub_Trending/op/openpilot高速过弯时车身明显偏向车道外侧出弯又猛地拉回来或者方向盘在直线上一阵阵地抖动——这些你大概率体验过的车道保持毛病在 openpilot 的横向控制里对应三个工程问题目标曲率从哪来、模型输出有延迟怎么补偿、300 多台不同转向执行器的车怎么共用一套控制器。下文沿着一条控制环路把这三件事拆开代码全部来自 selfdrive/controls 的真实实现。从神经网络输出到被钳住的期望曲率先回答目标从哪来。controlsd 的 100Hz 主循环里期望曲率直接取自模型服务的输出再经过一道限速处理new_desired_curvature model_v2.action.desiredCurvature if CC.latActive else self.curvature self.desired_curvature, curvature_limited clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)也就是说横向控制器的输入不是车道线位置而是一个标量期望曲率单位 1/m数值越大弯越急。车道线到曲率的换算被上移到了模型层控制器只管跟踪。两级钳位先限jerk再限横向加速度clip_curvature 是整个环路上唯一会修改目标值的地方逻辑就三层max_curvature_rate MAX_LATERAL_JERK / (v_ego ** 2) # EU guidelines new_curvature np.clip(new_curvature, prev_curvature - max_curvature_rate * DT_CTRL, prev_curvature max_curvature_rate * DT_CTRL) roll_compensation roll * ACCELERATION_DUE_TO_GRAVITY max_lat_accel MAX_LATERAL_ACCEL_NO_ROLL roll_compensation new_curvature, limited_accel clamp(new_curvature, min_lat_accel / v_ego ** 2, max_lat_accel / v_ego ** 2)第一层用 5 m/s³ 的横向加加速度jerk即横向加速度变化的快慢限制曲率每帧的最大变化量——曲率变化率上限按 v² 反比缩放车速越快允许的方向变化越缓。第二层用 3 m/s² 的横向加速度上限EU 法规推荐值封顶并叠加车身 roll 角产生的重力分量做补偿坡道上的车本来就有一部分侧倾这部分不该算进控制量里。第三层是 0.2 1/m 的绝对曲率上限约等于大多数车都达不到的小转弯半径。这里的取舍值得说清楚openpilot 没有在控制环内再跑一个曲率优化器而是用三次 O(1) 的 clamp。原因是这段代码每 10ms 执行一次优化器解出来的最优曲率收益有限而硬钳位保证任何时刻输出都合规行为可预测、可调试。舒适性约束在目标端一次解决后面所有控制器继承同样的上限。curvature_limited 信号被钳位本身是信息注意 clip 函数返回的第二个值limited_accel。曲率被加速度上限截断说明目标比车当前能力更急这个布尔量会一路传到控制器的饱和检测里后文会看到用来区分我没跟上和物理上做不到。延迟补偿1秒环形缓冲与摩擦前馈目标曲率到手了为什么不能直接拿它和当前状态做 PID因为从摄像头成像到 CAN 报文发出模型链路有几十毫秒的固有延迟。如果你用现在的期望值去对现在的车况控制器永远在追一个已经过去的目标——这就是出弯猛拉回的元凶。用过去缓冲里的值当 setpointlatcontrol_torque.py 维护了一个 1 秒长的横向加速度请求环形缓冲setpoint 不是最新值而是按实测延迟倒查出来的历史值future_desired_lateral_accel desired_curvature * CS.vEgo ** 2 self.lat_accel_request_buffer.append(future_desired_lateral_accel) delay_frames int(np.clip(lat_delay / self.dt 1, 1, self.lat_accel_request_buffer_len)) expected_lateral_accel self.lat_accel_request_buffer[-delay_frames] setpoint expected_lateral_accel error setpoint - measurementlat_delay由独立的 delay 估计进程给出再叠加模型平滑时间LAT_SMOOTH_SECONDS。倒查delay_frames帧的意思是当前车态是由这个过去时刻的期望造成的用它当参考点误差才是真实误差。这是整个控制器里最容易被低估的一段——它没有增加任何计算量却把开环延迟从误差里拿掉了。误差为什么开在横向加速度空间文件头部的注释写明了设计假设车速超过约 25mph 时同一辆车施加相同转向扭矩获得的横向加速度基本恒定与轮侧打滑和车速都近似无关。因此 PID 的 P 项可以按低速大、高速小插值而不是按角度误差INTERP_SPEEDS [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30] KP_INTERP [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP] # KP 0.81 m/s 时 P 增益是 25030 m/s 时降到 0.8差两个数量级——低速时方向盘摩擦占比极高必须硬掰高速时小角度就够。如果直接在方向盘角度空间做 PID低速摩擦会让增益曲线完全失真这是选加速度空间的核心原因。摩擦、roll 与 jerk 前馈误差项只解决差多少前馈项负责车本来就要花多少力ff gravity_adjusted_future_lateral_accel - self.torque_params.latAccelOffset ff get_friction(error JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)get_friction按方向叠加摩擦估计lateral_accel_deadzone由方向盘死区角steeringAngleDeadzoneDeg换算而来专门吃掉机械间隙latAccelOffset修正设备与车身 roll 不一致带来的固定偏置。另外注意到 jerk 项desired_lateral_jerk是从缓冲里对曲率请求做中心差分再经 1.2Hz 低通滤波得到的乘以 JERK_GAIN0.3 提前加进摩擦阈值判断——弯道里摩擦方向会突变提前 0.19s 看 jerk 趋势可以少抖一下。PID 输出的是目标横向加速度最后才用torque_from_lateral_accel映射回扭矩这样扭矩-加速度的非线性曲线在闭环外侧被消化环路内部始终是线性空间。一套控制器接口四种执行通道按转向控制类型分派300 多台车的执行器接口并不一样有的 CAN 通道下发扭矩有的下发方向盘转角有的特斯拉系直接下发曲率。controlsd 里按 CarParams 一次性分派if self.CP.steerControlType car.CarParams.SteerControlType.angle: self.LaC LatControlAngle(self.CP, self.CI, DT_CTRL) elif self.CP.steerControlType car.CarParams.SteerControlType.curvature: self.LaC LatControlCurvature(self.CP, self.CI, DT_CTRL) elif self.CP.lateralTuning.which() pid: self.LaC LatControlPID(self.CP, self.CI, DT_CTRL) elif self.CP.lateralTuning.which() torque: self.LaC LatControlTorque(self.CP, self.CI, DT_CTRL)四种实现共享 LatControl 基类的update签名和饱和检测。角度型走纯前馈曲率经车辆模型换算成转角不做闭环曲率型在曲率空间做 PID 或纯前馈扭矩型就是上一节的完整实现。取舍很实际与其写一个能兼容所有接口的通用控制器不如让每个控制器贴着自家执行器特性写把公共部分收敛到基类的十几行里。饱和检测什么时候该报警所有控制器共用同一段饱和计数逻辑它决定 UI 是否提示方向盘扭矩受限if (saturated or curvature_limited) and CS.vEgo self.sat_check_min_speed \ and not steer_limited_by_safety and not CS.steeringPressed: self.sat_time self.dt else: self.sat_time - self.dt self.sat_time np.clip(self.sat_time, 0.0, self.sat_limit)三个排除条件各有含义steer_limited_by_safety是安全模块panda主动限幅说明控制器没失控、是策略限制steeringPressed是驾驶员在抢方向盘低速10 m/s不统计因为摩擦会让输出天然顶满。饱和必须持续累计到CP.steerLimitTimer才上报瞬时顶满不算事。在线标定参数热更新扭矩型控制器的三个参数latAccelFactor、latAccelOffset、friction不是静态配置文件而是独立的lateralTorqueParameters服务在行车中持续估计斜跑、转弯工况下回归controlsd 每帧检查并热更新。换车、方向盘老化、摩擦系数漂移环路自己会跟上。参数与权衡集中在 drive_helpers.py 和 latcontrol_torque.py 里的关键可调项参数作用域当前值调整效果MAX_LATERAL_JERK曲率限速5.0 m/s³调小→入弯更柔但出弯滞后更长调大→响应快、侧倾感强MAX_LATERAL_ACCEL_NO_ROLL曲率限速3.0 m/s²舒适/稳定边界接近 EU 推荐值MAX_CURVATURE曲率限速0.2 1/m绝对转弯能力上限基本不动KP_INTERP/KI扭矩 PID 增益250→0.8 / 0.15低速段主导掰得动高速段主导不抖JERK_GAINjerk 前馈0.3弯道摩擦预判强度过大会引起方向微振JERK_LOOKAHEAD_SECONDSjerk 前馈0.19 s提前量与链路延迟匹配LAT_ACCEL_REQUEST_BUFFER_SECONDS延迟补偿1.0 s缓冲长度上限lat_delay 不可超过它LP_FILTER_CUTOFF_HZjerk 滤波1.2 Hz截止频率低→前馈迟钝高→噪声进前馈sat_check_min_speed饱和统计10 m/s低于此速不统计饱和避免摩擦误报STEER_ANGLE_SATURATION_THRESHOLD角度型饱和2.5°指令角与实际角差值超此值判饱和friction/latAccelOffset前馈在线运行时估计由 torqued 回归一般不手调规律是舒适性参数jerk/accel 上限全局一处生效执行参数增益/摩擦按车型分派。动全局参数前先看曲率限速是否成为瓶颈——curvature_limited被置位的频率可以直接从 controlsState 日志里统计。验证与实测量化边界本身就是这套系统的成绩单横向加加速度 ≤5 m/s³、横向加速度 ≤3 m/s²不含 roll、曲率 ≤0.2 1/m全部在 drive_helpers.py 里以常数形式硬约束控制环周期 100Hzcontrolsd.py 中Ratekeeper(100)延迟补偿窗口覆盖 1 秒链路延迟。复现与回归测试在 selfdrive/controls/tests/test_latcontrol.py四种控制器的输出范围与饱和行为test_latcontrol_torque_buffer.py环形缓冲的 setpoint 倒查逻辑test_torqued_lat_accel_offset.pyroll 偏置在线回归的正确性test_following_distance.py横向与纵向耦合下的跟随边界想在自己的车上验证最直接的路径是回放用 tools/replay 跑历史 qlog或用 tools/plotjuggler 打开 controlsState 流desiredCurvature、actualLateralAccel、output、saturated四条曲线对齐看延迟补偿是否生效一眼可见setpoint 与 error 应该在弯道建立期同相而不是滞后一个弯。局限与演进当前方案的两个已知边界一是低速5 m/s时 PID 积分器冻结、且多数车minSteerSpeed直接禁用横向控制蠕行跟弯靠的是原车 ACC 或驾驶员二是延迟补偿完全依赖lateralDelay估计的准确性延迟估计失准时 setpoint 会整体偏帧表现成规律性的 S 形过冲。社区方向上扭矩参数在线回归torqued正在吸收这类缓慢漂移后续把延迟估计也并入同一套在线辨识是控制环路收敛的下一步。一句话收束openpilot 横向控制的工程内核是把舒适性约束钳在目标端、链路延迟查表补偿、执行器非线性关在环外这三件事做到 100Hz 稳定换来曲率、jerk、摩擦三条曲线全部可观测、可回归。想动手从 openpilot/selfdrive/controls/lib/ 四个控制器文件入手再配合 docs/how-to 里的 replay-a-drive 跑一遍回放半天就能把整套环路的波形摸清楚。【免费下载链接】openpilotopenpilot is an operating system for robotics. Currently, it upgrades the driver assistance system on 300 supported cars.项目地址: https://gitcode.com/GitHub_Trending/op/openpilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考