我最早接触这类系统,是在一次模拟货舱巡检的试飞任务里。飞机沿走廊低速飞行,正前方二十来米横着一排铁丝围网,雷达目标列表里明明有个稳定的反射点,视觉目标检测却始终没把它框出来。眼看飞机越靠越近,最后还是靠那条“任何传感器单独确认的有效目标都按障碍物处理”的保守策略才停下来。那次之后我彻底想明白了一件事:无人机自主避障系统的难点,根本不在某个传感器有多聪明,而在于如何在几十毫秒的时间里,把一堆互不信任、各有缺陷的量测值凑成一份足够可靠的“前方世界模型”。
这篇文章就围绕我背景里反复验证过的一套组合来写:OMAPL138这种DSP+ARM异构处理器做边缘计算核心,搭配FPGA做低延迟前处理,再融合毫米波雷达和单目视觉。内容会讲清楚传感器为什么这么选、三个计算核心怎么分工、雷达和视觉各自的数据边界在哪里、融合仲裁怎么做,以及整个避障决策链路如何落地。适合正在做无人机避障、传感器融合或者嵌入式异构平台的工程师和学生参考,也适合方案选型阶段做技术权衡的人看。
1. 避障系统的实时性账本:为什么传感器组合与计算平台这样选
1.1 先算一笔“检测-决策-执行”的延迟账
避障问题表面上是个感知问题,本质却是个实时控制问题。飞行器在空气中运动几乎没有任何缓冲,感知晚100ms,控制反应慢50ms,加在一起就是十几米的飞行距离。所以任何选型讨论都该先回到实时性上算账。
以中等速度4m/s、距离5m外出现障碍物为例,整个避障链路耗时如果控制在200ms以内,飞机还会靠近障碍物0.8m;如果优化到100ms,这个数字就变成0.4m。对于室内巡检、低空物流这类场景,0.4m的余量可能意味着安全穿越和直接撞上的区别。这还没算上动态障碍物本身运动带来的不确定度。
我习惯把整个链路拆成四段分别评估延迟:传感器采集与传输、前端信号/图像处理、融合决策、飞控执行。摄像头30Hz采集,一帧就是33ms;毫米波雷达典型输出周期50ms;深度学习目标检测在嵌入式端跑一个轻量网络,单帧推理40到80ms;飞控的避障控制环又需要20到50ms。这些数字随意叠加,就能轻松超过300ms。所以在硬件选型时我优先考虑的不是单点性能最强,而是哪套方案能把“采集-处理-融合-决策”的总延迟压到100ms级别。
1.2 为什么不用激光雷达、双目或者纯视觉
可能有人会问,现在激光雷达性能这么好,双目视觉也能直接测距,为什么非要绕一圈去融合毫米波和单目?我在试验里做过横向对比,这里把核心矛盾列出来。
激光雷达的点云质量确实无可挑剔,尤其机械式方案,360度扫描时对稀疏障碍物和细杆的检测特别稳。但问题也很直观:重量、成本和功耗在小型无人机上承受不起,尤其是多旋翼平台,每一克都要算进续航里。再加上纯激光点云没有语义信息,它告诉你有障碍物,但说不清是树枝、电线还是铁丝网,这在后续决策里会很被动。
双目视觉能直接输出深度图,理论上最贴合避障需求。可室外场景下基线长度受限,有效测距范围往往只有十几米;算力开销也大,立体匹配在嵌入式平台上跑30帧要占用大量资源。更麻烦的是,双目在弱纹理区域(白墙、天空、水泥地)和光线剧烈变化时深度图会大片空洞,这种失效方式对安全系统来说是最难处理的。纯单目则完全没有深度,必须靠运动或几何假设来估算距离,单独使用风险很高。
毫米波雷达恰好补上了这些短板:直接输出距离和速度,视线穿透雨雾和尘土,对金属等强反射物极其敏感。但它缺乏语义,角分辨率又低,孤零零一个雷达很难判断“目标是什么、是不是真实障碍物”。所以最合理的方案,就是用雷达提供“可靠的距离与速度”,用视觉提供“丰富的语义与方位”,两者在时间与空间上对齐之后再仲裁。冗余不是锦上添花,而是安全系统的底线要求。
1.3 OMAPL138为什么能扛起边缘计算这杆旗
确定了“雷达+单目”的感知组合,接下来要选计算平台。OMAPL138这类DSP+ARM异构处理器,在工业界用得很广泛,很多老飞控和边缘端设备里都能见到它的身影。它的ARM核跑Linux或者轻量RTOS,负责任务调度、通信和决策;DSP核则专门跑信号处理,比如雷达目标提取、滤波。
有人会觉得ARM9算力不强,跑不动现代视觉模型。这话没错,但得看怎么分工。避障系统里真正吃算力的是两部分:毫米波雷达的中频信号处理和图像的前级流水。雷达算法放在DSP上是天作之合,C674x这类DSP做复数运算、FFT、CFAR检测的效率比ARM高一个数量级。图像端的高并行处理则放给FPGA做,FPGA负责缩放、ROI裁剪、像素格式转换这些脏活累活,ARM反而不需要硬扛高负载。
这个方案的核心逻辑是“让每类计算单元干自己最擅长的事”。ARM做主控和逻辑,DSP做数值密集的雷达算法,FPGA做并行度要求高的图像前处理,三者通过硬件总线连接,延迟可以做到非常低。相比于直接上一块高功耗GPU板卡,这套组合的功耗、可靠性和成本都更适合嵌入到无人机这类受限平台里。
2. 三个计算核心的分工与通信设计
2.1 ARM与DSP的数据通道:不能简单“各干各的”
异构平台的第一道坎是ARM与DSP之间的数据交换。OMAPL138双核之间共享一段物理内存,通过中断或mailbox做握手。表面上看起来很简单,但实际调试中第一个坑就出在“丢帧”和“读写冲突”上。
我用的方案是环形缓冲区加双buffer:DSP侧把处理完的雷达目标列表写入共享内存中的buffer A,同时写一个递增的序列号;写完通过mailbox向ARM发中断。ARM收到中断后,不直接去读buffer A,而是先把指针切到buffer B上,DSP下一次只写buffer B,等ARM处理完A再切换回来。这样读写双方永远不碰同一块区域,彻底绕开锁竞争。
typedef struct { uint32_t seq; // 序列号,用于检测丢帧 uint32_t target_cnt; // 目标个数 uint64_t timestamp_us; // 雷达时间戳 RadarTarget targets[64]; } RadarFrame; // DSP侧:写完数据后 RadarFrame *frame = &shared_mem->buffer[write_idx]; frame->seq = frame_seq++; frame->timestamp_us = get_time_us(); // 写内存屏障,确保数据落盘可见 __builtin_dmb(); // 触发ARM中断 mailbox_send_int(ARM_CPU_ID, frame->seq);ARM侧收到中断后,对应的回调里做的第一件事不是处理数据,而是立刻切换下一次写入索引,然后再慢慢读取当前buffer。这个“读完再切换、写完再切换”的顺序非常重要,如果搞反了,就会出现DSP覆盖尚未处理完的数据,或者ARM读到了半新的脏数据。
共享内存还有个容易被忽略的问题:字节对齐和缓存一致。ARM在Cortex-A系列上有cache,DSP侧写共享内存如果不做cache clean操作,ARM可能读到旧数据。我在早期调试时就遇见过“显示变量变了但值一直不更新”的怪现象,排查了两天才意识到是cache一致性问题,后来干脆把所有共享数据结构都用volatile修饰,并在关键位置加了显式的cache操作。
2.2 FPGA该放什么逻辑,不该放什么
FPGA在系统里的作用,我总结成一句话:做一个确定性的“前端管家”。它要参与的是那些延迟敏感、逻辑简单、并行度高的任务,而不是去硬解复杂算法。
以单目视觉为例,相机输出的原始图像可能是一路MIPI或并行数据,分辨率1280x720。如果直接全图送进ARM,DMA搬运一帧就要几毫秒,再做缩放、格式转换又是几毫秒,累积下来非常可观。FPGA可以在数据流里直接完成:像素级ROI提取(比如只保留前方梯形区域)、双线性插值缩放、YUV到RGB转换、直方图统计,最后通过AXI总线把整理好的图像直接推给ARM侧做检测。整个过程流水线化,延迟只有几十微秒。
雷达侧也一样。毫米波雷达输出的原始点云缓存、坐标极坐标到直角坐标的转换、目标聚集和初筛,这些放在FPGA里做,DSP就能专心跑CFAR和测速算法。加在一起,整个前处理链路的时间几乎不随负载波动,非常符合避障系统对确定性的要求。
但我不建议把深度学习网络也硬塞进FPGA里跑。虽然高端的FPGA上可以做卷积加速,但开发周期长、调试难,而且模型迭代一次就要重新综合一次。更实际的做法是:FPGA只做“看得见的确定性前处理”,真正需要语义理解的检测网络交给ARM侧跑CPU版本的轻量网络,或者在一颗小算力NPU上跑。这样系统边界清晰,任何一层出问题都能快速定位。
2.3 实时任务调度:Linux侧的“软实时”改造
OMAPL138的ARM核跑Linux时有个现实问题:Linux默认调度策略对硬实时任务并不友好。避障决策这种优先级极高的任务,有可能被内核线程或其他进程打断。我的做法是给这类任务使用实时调度策略,把线程优先级提到最高,并配合CPU亲和性绑核。
具体来说,我创建了三个独立线程:毫米波雷达接收线程(周期5ms,优先级最高)、融合线程(周期20ms)、避障决策线程(周期50ms)。雷达接收线程独立占据一个核,不被其他任务干扰;融合线程用高精度定时器驱动,把雷达目标列表与视觉检测结果做关联;决策线程最后输出控制指令给飞控。
struct sched_param param; param.sched_priority = 80; pthread_setschedparam(receiver_thread, SCHED_FIFO, ¶m); // 绑定核 cpu_set_t set; CPU_ZERO(&set); CPU_SET(2, &set); pthread_setaffinity_np(receiver_thread, sizeof(set), &set);这套配置跑下来,雷达接收线程的抖动基本能控制在几百微秒量级。如果你的平台支持内核实时补丁,建议开启,尤其当有多个高频传感器时能明显减少调度延迟。另外,整条链路的调试串口打印一定要限制频率,我曾在debug阶段开着高频printf,结果一个串口输出就能吃掉几毫秒CPU时间,导致避障线程偶发掉帧,这在飞行中是致命的。
3. 毫米波雷达的避障数据:从目标列表到可用的世界模型
3.1 雷达输出的数据形态与坐标系转换
无人机避障场景里用的雷达,绝大多数输出的是“目标列表”而不是原始点云。每个目标通常包含:目标ID、径向距离、径向速度、方位角、俯仰角(如果有)、RCS反射截面积。少数雷达能输出稀疏点云,但无人机平台上的点云密度远不如车载激光雷达,直接依赖点云做配准并不现实。
我手上那颗77GHz雷达,典型配置是最大探测距离150m,距离分辨率0.4m左右,方位角视场±45度,角度分辨率约4度。它输出的目标会包含静止目标和运动目标两类,处理时要注意雷达测的是“径向距离”和“径向速度”,不是三维空间里的笛卡尔坐标。在ARM侧融合之前,必须先做极坐标到机体坐标的转换:
float x = target->range * cos(target->azimuth) * cos(target->elevation); float y = target->range * sin(target->azimuth) * cos(target->elevation); float z = target->range * sin(target->elevation);这里机体坐标系的定义要统一,我用的惯用规则是x向前、y向右、z向下。雷达安装位置相对机体重心有偏移时,还需要做杆臂补偿,否则近距离目标会引入明显误差。这个补偿量很小,但在避障阈值只有一两米时不能忽略。
3.2 哪些障碍物让雷达“撒谎”:误报来源与过滤
雷达的可靠性是相对的,在植被、草地、铁皮建筑、铁丝围网这些场景里,误报比比皆是。第一次在风电场附近试飞时,雷达目标列表里出现了大量静止目标,有的目标距离连续几个周期都稳定存在,看起来非常“真”。后来查了现场才知道,是远处一座铁皮工具棚造成的强反射,雷达波经过铁皮墙多径反射后,在几个不同方位都形成了虚像。
针对这种问题,我总结了一整套过滤规则,基本可按优先级排列:
- 多帧连续性:真实目标通常会在连续5到10帧内保持距离、方位连续变化;单帧闪现的目标直接丢弃。
- RCS阈值与波动:金属建筑、围栏通常RCS很大且稳定,但细树枝、草本植物RCS小且波动剧烈,后者的“可信度”要下降。
- 运动状态一致性:对于无人机自身运动带来的多普勒频移,雷达通常会做运动补偿,但补偿不佳时静止目标可能被标成匀速运动目标。这种情况下,配合惯性导航的速度信息做差值校验会很有效。
尤其要注意“成排的铁丝网柱”这类目标:单根柱子RCS较小可能被过滤,但整排柱子在方位向上会扩展成一个“栅栏”形态。不能简单依靠单目标RCS判断,要看方位向上是否存在连续的小目标串,如果存在,要在决策层把它合并为一面墙处理。
3.3 雷达量测的不确定性如何参与融合权重
雷达的距离测量精度通常很高,量级在0.1m以内,但角度误差明显更大。4度的方位角分辨率,在10m外对应的横向误差大约是0.7m,这已经大于很多小障碍物的尺度。所以在做融合加权时,我从来不给方位角很高的权重,距离和径向速度才是雷达最有价值的信息。
融合中的不确定性模型我会写成协方差矩阵:距离方差取0.05平方米,方位角方差取上一帧角度变化的统计值,目标如果持续被遮挡则方差直接指数增长。这套方法的好处是,当融合滤波器知道雷达的方位量测“不可信”时,会自动降低该维度的增益,不会让一次角度抖动把整个航向修正带偏。
4. 单目视觉的避障能力边界:在缺少深度信息时如何获得距离
4.1 目标检测:语义信息是雷达给不了的
单目视觉最大的贡献是语义。检测网络能在图像里框出人、车辆、树木、建筑、围栏这些障碍物类别,这种能力雷达完全没有。在OMAPL138这类平台上,我不会跑重型检测模型,而是用轻量化网络配合TensorRT或NCNN这类推理库做加速,把YOLO级别的检测任务跑在CPU+少量NEON优化的条件下,尽量做到10到20FPS以上。
检测网络输出的边界框自带类别和置信度,类别的置信度可以作为融合仲裁的权重依据。比如一个置信度0.9的“行人”框,我会把它当作强证据;而一个0.3的“未知物体”框,只作为弱提醒。在很多实际场景中,检测模型的类别质量和稳定性比分辨率更关键。
但这里必须说清楚,图像目标检测给出的是“目标在哪块像素区域”,不是“目标离我多远”。尤其是单目,它天生缺乏深度信息。很多刚接触视觉避障的人会误以为“检测到了障碍物就等于知道距离”,这在技术上完全是两回事。
4.2 基于地面平面假设的距离估计方法
单目要恢复距离,最常用也最稳定的假设是“地面平面模型”。无人机在相对平坦环境下飞行时,可以认为机体下方的地面是水平的,相机光轴相对地面有固定俯仰角。这样一来,图像中障碍物与地面的接触点(通常是检测框底边的中心像素)就可以投影回三维空间。
公式推起来不算复杂。相机内参已知,障碍物底边像素坐标为(u, v),相机高度为H,相机俯仰角为θ,则地面平面上该点的纵向距离近似为:
Z ≈ H * (f_v / (v - v0) ) * (1 / cosθ) / (1 + tanθ * (v - v0)/f_v)
这个式子看起来繁,实际上就是几何投影加小角度近似。底边越靠近地平线(v越接近v0),距离越远;底边越靠近图像底部,距离越近。整个过程中,相机的俯仰角和滚转角标定是否准确,直接决定距离精度。我测试过,俯仰角偏差1度,在10m处就会造成约0.3到0.5m的距离误差。
所以在实际系统里,视觉给出的距离只用来做“辅助确认”,不会直接参与精确近距避障,更不会替代雷达的距离量测。这也意味着视觉模块必须有明确的能力边界:它能帮系统确认“前方那个模糊影子是一棵树”,但无法精确告诉你“树皮离你还有几米”。
4.3 单目会失效的场景:逆光、夜间、低纹理和动态模糊
视觉的失效模式比雷达多得多。逆光时天空和地面过曝,检测框会漂移甚至消失;夜间光线不足,目标对比度低;低纹理墙面会让检测模型“找不到目标”;快速转向时动态模糊让整个画面发糊。这些场景下如果系统只依赖视觉,避障能力会断崖式下降。
我在测试中应对逆光的办法是开启相机HDR模式,并限制自动曝光的变化范围,避免画面在明暗跳变时疯狂调整亮度。夜间则配合红外补光或选用低照度相机,但即便这样,视觉的置信度仍要打折扣。所以融合策略里我始终把“视觉无输出”当成一种正常状态,而不是系统故障,只要雷达还有有效目标,避障就继续工作,只不过自动进入更保守的模式。
5. 融合策略:时间对齐、空间关联,以及冲突时听谁的
5.1 时间对齐:滑窗加插值,别拿“最新帧”硬拼
雷达和相机的采集时刻天然不同步。直接拿最新雷达帧和最新图像帧去融合,相当于拿两杯不同时间接的水兑在一起,结果要么滞后要么超前。我用的方案是给每一帧数据都打上硬件时间戳,在融合节点维护一个长度为几十帧的滑窗,融合时选择一个基准时间(比如当前时刻),把滑窗内离基准时间最近的雷达帧和图像帧分别做线性插值或外推,得到同一时刻的目标状态。
对于飞行速度不高的平台,线性插值已经够用。更重要的是把时间戳对齐后,融合的目标才能和飞控实际的机体姿态同步,否则横滚或俯仰变化时,坐标变换一错,空间关联就全乱了。
5.2 空间关联:雷达目标投影到图像,再找最近邻
空间融合的第一步是把雷达目标从机体坐标投影到相机图像平面。这需要相机相对于机体坐标系的安装矩阵(外参)和相机内参。投影后,每个雷达目标在图像上得到一个像素点,还带一个由雷达角度分辨率决定的像素误差半径。在这个半径范围内寻找检测框,如果能匹配上,就是一个“融合目标”。
反过来也一样:视觉检测框会给出目标在图像上的方位角区间,我可以把它映射回机体极坐标,再看雷达在该方位区间内是否有目标。双向匹配都成功的,融合置信度最高;单向匹配成功的,进入仲裁逻辑。
最近邻匹配的代价函数我用的是归一化加权距离:距离差归一化后乘以权重0.6,方位差归一化后乘以0.4,取最小且小于阈值的作为关联对。阈值不能设得太紧,否则雷达一颗目标对应多帧视觉检测时容易丢关联;也不能太松,否则会把相邻目标和背景关联在一起。
5.3 冲突仲裁:一张表说清到底听谁的
当雷达和视觉输出冲突时,系统必须有个明确策略。我总结的仲裁表可以给大多数场景兜底:
| 雷达目标 | 视觉目标 | 仲裁逻辑 |
|---|---|---|
| 有 | 有 | 融合目标,以雷达距离为准,视觉类别作为语义修正 |
| 有 | 无 | 保留目标,但按“低置信金属/强反射体”处理,距离近时触发减速 |
| 无 | 有 | 保留目标,但按“可能的小目标或低反射物体”处理,距离近且有框时执行绕障 |
| 无 | 无 | 无目标,正常飞行 |
其中“雷达有、视觉无”的常见对象是铁丝网、铁皮房、路灯杆这类金属物;“雷达无、视觉有”的常见对象是行人、深色衣物的人、树木以及部分复合材料障碍物。这两种方向都不该被单方面信任,但当任意一方给出“近距离目标”时,系统必须往保守方向走。我在设计里加了一条硬规则:任何融合目标,如果雷达距离低于阈值且连续三帧稳定,即使视觉没有任何输出,也直接触发避障。
这个“单传感器兜底”规则特别重要。很多融合系统的安全漏洞在于“必须双模确认才动作”,但实际的失效场景里恰恰是某个传感器单独报障的概率最高,真等两边都确认,飞机可能已经撞上去了。
6. 避障决策的落地实现:从感知融合结果到安全飞行路径
6.1 安全阈值的计算:刹车距离不是拍脑袋
避障决策的第一步是计算每个方向上的最大允许速度。我取的反应与执行延迟总和为0.3s,这个值包含了感知融合延迟、决策延迟、飞控响应延迟和电机响应延迟。制动减速度在不同机型上差别很大,多旋翼在低电量时可以做到3m/s²,电量充足时5到6m/s²也很常见。我留了1.5倍的峰值减速作为安全冗余。
根据公式d = v·t + v²/(2a),算几个数值你就能直观感受:
| 速度(m/s) | 总刹车距离(m) | 建议安全阈值(m) |
|---|---|---|
| 3 | 0.9 + 0.45 = 1.35 | 2.0 |
| 5 | 1.5 + 1.25 = 2.75 | 4.0 |
| 8 | 2.4 + 3.2 = 5.6 | 7.5 |
阈值必须大于理论刹车距离,余量至少0.5m以上。在雷达融合目标的距离进入阈值前,系统就应该开始减速,而不是等到距离已经很近再猛拉杆。我在低电量测试时踩过一次坑:阈值按满电3m/s²算的,结果电量不足时制动明显变软,飞机差点怼上去。后来直接把电量对制动能力的影响系数写进了阈值计算。
6.2 反应式绕障:不是简单地“停下来”
单纯“检测到障碍就刹车”不是避障,只是延迟撞上去。真正好用的是反应式水平绕障策略:系统输入前方扇形区域的融合目标,输出一个避障方向和期望横向偏移量。
我把前向180度的视野划分成若干扇区,每个扇区维护一个“最近危险距离”。当正前方目标距离进入安全阈值时,判断目标所在方位:如果目标偏左,则规划向右绕飞;偏右则向左绕飞;正中间则优先选择“目标较少”的一侧。绕飞过程用一个期望航向角闭环控制,横向偏移量至少为“目标自身估计宽度的一半加0.8m安全余量”。绕飞结束后再根据前方扇形区域的障碍物分布情况平滑回到原航向。
这套方法虽然没有全局路径规划那么精美,但胜在计算量小、响应快,很贴合反应式避障的实时性需求。对小障碍物、稀疏障碍物场景非常有效;对大范围遮挡或迷宫式环境,还需要在更高层叠加地图和规划,那已经超出这套边缘避障系统的能力边界。
6.3 决策输出的接口与飞控约束
避障决策结果最终要发给飞控。我在OMAPL138和飞控之间用串口或CAN传输,消息里包含期望航向角、期望速度、是否启用避障指令。飞控侧收到后,通过位置控制环执行,并限制最大倾斜角,防止避障时猛打方向导致失速或电机过载。
另一个容易被忽略的点是“避障指令的滑落保护”。如果避障系统突然掉线或者出现无效帧,飞控必须能自动恢复手动/普通模式,不能一直“钉”在避障指令上。我的方案是让飞控侧对一个看门狗计数器递增,收到避障指令则清零,超时就自动退出避障模式并悬停。这套保险机制在多次试飞中救回过实验飞机。
7. 实测中让我抓狂的几个问题与排查过程
7.1 DSP与ARM共享内存的数据“幽灵错乱”
现象:ARM侧读到的雷达目标数量偶尔跳变,甚至出现距离值明显不合理的情况。排查过程比结果复杂很多。先是怀疑DSP算法本身输出异常,单独抓DSP日志又一切正常;再用逻辑分析仪观察mailbox中断,发现时序也对;最后在ARM侧反复读同一块共享内存,发现每次读到目标列表里某个字段的值都不一样。
真正的原因有两层:一是结构体里字段存在隐式padding,DSP侧按4字节对齐写入,ARM侧按8字节对齐读取,两个视角下结构体内存布局不一致;二是ARM侧cache没有及时失效。解决方法是定义共享结构时全部用明确宽度的字段类型,并显式使用#pragma pack(push, 1)取消padding,同时在读取前调用cache invalidate。改完之后,同样的代码跑了十几个小时再没出现过一次错乱。
7.2 场地边缘的“幽灵目标”差点让飞机触发急刹
测试场旁边有一排铁皮围挡,雷达在某一帧会突然报出正前方30m处有个稳定目标。多帧连续性明明通过了,但它其实是个固定金属物。真正让我警惕的是,当无人机悬停时,这个目标居然出现在“前方”而不是侧方,说明它是通过旁瓣或地面反射进来的。
排查方式:开着地面站实时看所有雷达原始目标点,把目标投影到二维平面上。结果发现该目标对应的方位角和距离完全符合一次地面反射的镜像路径。解决方法是加了一条规则:当目标来源于低仰角区且距离较远、连续多帧位置几乎不变时,降级它的置信度;同时在地面站里划出一个“已知反射区屏蔽范围”。这个教训说明,雷达滤波不是只靠系数,还得理解电磁波在场景里的实际传播路径。
7.3 逆光下视觉漏检,最终靠雷达兜了底
有一次在傍晚测试,太阳正好在航向正前方偏左,逆光环境下视觉检测在10m处漏掉了前方一根铁杆。雷达正常报出该目标,但因为视觉侧完全没匹配上,系统最后只进入了“低置信”状态,没有触发全速制动。
那次之后我把仲裁逻辑改成了“雷达近距稳定目标无条件优先”。无论视觉有没有框到,只要雷达测距连续三帧低于安全阈值的1.2倍,就强制进入避障模式。改完之后逆光场景依然会漏检,但系统不会再因为视觉“看不见”而失去避障能力。
7.4 Linux偶发卡顿与实时任务漂移
某次连续跑了一小时后,避障决策线程的周期从50ms抖动到300ms。一开始以为是内存泄漏,查了一遍没有。后来用perf分析发现,是内核的后台任务(比如网络收发、文件系统日志)频繁抢占CPU,实时优先级线程虽然设置了高优先级,但被中断负载打成碎片。
解决方法是三管齐下:把与避障无关的服务全部禁用,取消文件系统自动挂载任务;实时线程绑在独立CPU核心上,并设置irqaffinity把不相关的中断分散走;最后把时间同步、日志写入等非紧急任务全部移到低优先级线程。改完再跑同样的压力测试,抖动基本消失。嵌入式Linux的实时性从来不是“配个高优先级就完事”,而是要从内核配置、中断隔离、线程模型整体设计。
这套系统从搭建到稳定,前前后后折腾了大半年。现在我很清楚,它不是一个拿去炫耀“检测精度”的演示项目,而是一套能让飞机在真实环境里“不撞上就不撞上”的保障系统。
如果让我用一个词概括这段经验,我会说“取舍”。传感器各有盲区,处理器各有所长,融合算法再精巧也只能把不完美的量测合并成更可靠的判断,不能把错误变成正确。所以在做这套系统时,我最重视的不是某个指标多漂亮,而是每个传感器失效时系统还有没有兜底路径。雷达故障时视觉还能判断类别,视觉瞎了时雷达依然给得出距离,两个都不可靠时避障决策还能按保守逻辑减速悬停——这才是真正值得花时间去打磨的地方。
最后再分享一个小建议:所有阈值和仲裁逻辑,一定要在真实场地反复跑,别在仿真里自我感动。仿真里没有逆光、没有土坡旁的铁皮房、没有无人机螺旋桨搅起来的落叶和尘土,而这些才是现实世界避障系统真正面对的东西。