4、行人感知与决策:让虚拟行人“看见”并“思考”

好,我们进入这一章的核心。行人行为模拟里,最难的不是走路动画,而是让行人感知环境做出决策。说白了,就是给虚拟人装上眼睛和大脑。

我刚开始做这个模块时,犯过一个低级错误——让行人“全知全能”。他能看到360度所有车辆,能预判5秒后的轨迹。结果仿真跑起来,行人个个像开了上帝视角,完全不真实。后来我才明白:真实行人的感知是有局限的,决策是有偏好的

这一章,我们就来拆解这三个核心模块:感知模型决策模型意图预测

4.1 行人感知模型:视野范围与注意力

行人怎么“看”世界?不是像雷达那样扫描,而是靠眼睛和大脑的协同。我习惯把感知模型拆成两层:物理视野注意力筛选

4.1.1 视野范围(Field of View, FoV)

人的视野不是均匀的。中心区域清晰,边缘模糊。在仿真里,我们通常用一个扇形区域来近似:

  • 水平视野:约120°(有效视区),中心60°为高分辨率区
  • 垂直视野:约60°(上下各30°)
  • 最大感知距离:城市环境中约50-100米(受遮挡影响)

举个例子,我在项目中这样实现视野检测:

// 伪代码:行人视野检测
bool IsInFieldOfView(Vehicle vehicle, Pedestrian ped) {
    Vector3 dirToVehicle = vehicle.position - ped.position;
    float distance = dirToVehicle.magnitude;
    
    // 1. 距离过滤
    if (distance > ped.maxPerceptionRange) return false;
    
    // 2. 角度过滤(水平120°)
    float angle = Vector3.Angle(ped.forwardDir, dirToVehicle);
    if (angle > ped.horizontalFov / 2.0f) return false;
    
    // 3. 遮挡检测(射线投射)
    if (Physics.Raycast(ped.eyePos, dirToVehicle, out hit, distance)) {
        if (hit.collider.tag != "Vehicle") return false; // 被其他物体挡住
    }
    
    return true;
}
注意:别忽略遮挡检测!我曾经有个项目,行人能“看穿”建筑物看到后面的车,结果行人站在楼后突然开始避让,完全不合理。加个射线检测,成本不高,效果天差地别。

4.1.2 注意力模型(Attention Model)

视野里能看到的东西,不代表行人真的注意到了。人的注意力是有限的。你想想看,过马路时你会同时盯着左边来的车、右边来的自行车、脚下的坑、手机上的消息吗?不会的。

我常用的注意力模型分三个层次:

  • 焦点注意力:当前最关注的目标(比如最近的车)
  • 周边注意力:视野内但非焦点的目标(比如远处的车)
  • 忽略目标:视野外或被认为不危险的目标

具体实现时,我会给每个目标算一个注意力权重

影响因素 权重系数 说明
距离 0.4 越近越关注
相对速度 0.3 速度越快越危险
运动方向 0.2 朝向行人的更危险
目标大小 0.1 大物体更容易被注意

嗯,这里要注意:注意力是会转移的。行人不会一直盯着同一个方向。我习惯加一个扫视机制——每隔2-3秒,行人会随机扫视左右,更新注意力焦点。

经验之谈:注意力模型的参数最好做成可配置的。我在测试中发现,不同年龄的行人注意力差异很大——年轻人扫视频率高但持续时间短,老年人扫视慢但更专注。调参时多试试极端值。

4.2 行人决策模型:过马路与避让

感知完了,接下来就是决策。行人决策模型,说白了就是回答两个问题:“现在过马路安全吗?”“如果危险,我该往哪躲?”

4.2.1 过马路决策(Crossing Decision)

过马路决策的核心是间隙接受理论(Gap Acceptance Theory)。行人会判断:下一个车到达我当前位置的时间(Time to Arrival, TTA)是否足够我通过。

我常用的决策流程是这样的:

  1. 计算安全间隙:行人需要的最小时间 = 道路宽度 / 行人步行速度 + 安全余量
  2. 评估当前间隙:最近车辆到达的时间 TTA = 距离 / 车速
  3. 决策规则:如果 TTA > 安全间隙,则过马路;否则等待

代码实现大概这样:

// 过马路决策
bool ShouldCross(Pedestrian ped, Road road, List<Vehicle> vehicles) {
    float roadWidth = road.width;
    float walkSpeed = ped.walkSpeed; // 约1.2-1.5 m/s
    float safetyMargin = 1.5f; // 秒,个人安全偏好
    
    float minTimeNeeded = roadWidth / walkSpeed + safetyMargin;
    
    // 找到最近车辆的TTA
    float minTTA = float.MaxValue;
    foreach (var v in vehicles) {
        float tta = CalculateTTA(v, ped);
        if (tta < minTTA) minTTA = tta;
    }
    
    // 决策
    return minTTA > minTimeNeeded;
}
关键点:安全余量(safetyMargin)因人而异。我见过一个项目把余量设成固定值,结果所有行人行为一模一样。后来改成正态分布采样(均值1.5s,标准差0.3s),行为多样性立刻出来了。

4.2.2 避让决策(Avoidance Decision)

过马路过程中,如果突然有车冲过来怎么办?这时候就需要避让决策。我把它分成三种策略:

  • 加速通过:如果已经走了一半以上,加速跑过去
  • 后退返回:如果刚起步,退回路边
  • 原地等待:站在路中间的安全岛等待

选择哪种策略,取决于风险等级当前位置。我习惯用一个简单的状态机:

enum AvoidanceStrategy { Accelerate, Retreat, Wait };

AvoidanceStrategy DecideAvoidance(Pedestrian ped, Vehicle threat) {
    float progress = ped.distanceCrossed / roadWidth; // 过马路进度
    float ttc = CalculateTTC(threat, ped); // 碰撞时间
    
    if (ttc < 1.0f) {
        // 极度危险
        if (progress > 0.6f) return Accelerate;
        else return Retreat;
    } else if (ttc < 2.0f) {
        // 中度危险
        if (progress > 0.4f) return Accelerate;
        else return Wait;
    } else {
        // 低风险,继续正常行走
        return Wait; // 实际上是不需要避让
    }
}
避坑指南:我曾经把加速策略的触发阈值设得太低,结果行人动不动就冲刺,看起来像在拍动作片。后来加了最大加速度限制(正常人冲刺加速度不超过2m/s²),行为才正常。

4.3 行人意图预测:预判行人的下一步

这一节其实是为自动驾驶车辆服务的。车辆需要预测:这个行人接下来要干嘛? 是继续等,还是突然冲出来?

我常用的意图预测方法有三种:

4.3.1 基于轨迹的预测

最简单的方法。根据行人过去几帧的位置,用卡尔曼滤波线性外推预测未来轨迹。

// 简单线性外推预测位置
Vector3 PredictPosition(Pedestrian ped, float deltaTime) {
    Vector3 velocity = (ped.position - ped.lastPosition) / Time.deltaTime;
    return ped.position + velocity * deltaTime;
}

但这个方法有个问题——它假设行人会保持当前运动状态。如果行人突然改变主意(比如本来在等,突然决定过马路),预测就失效了。

4.3.2 基于意图的预测

更高级的方法。我们直接预测行人的决策意图。比如:

  • 过马路意图:行人是否在观察车流?身体是否朝向马路?
  • 等待意图:行人是否在看手机?是否站在路边不动?
  • 犹豫意图:行人是否在左右张望?脚步是否有小幅度移动?

我习惯用隐马尔可夫模型(HMM)来做意图识别。把行人的状态(观察、等待、行走、冲刺)作为隐藏状态,把观测到的行为(头部朝向、脚步移动、身体倾斜)作为观测序列。

个人经验:HMM的转移矩阵需要大量真实数据来训练。我早期用手调参数,效果惨不忍睹。后来用路测数据训练,准确率从60%提升到了85%以上。数据才是王道。

4.3.3 混合预测方法

实际项目中,我推荐轨迹预测 + 意图预测结合。先用意图预测判断行人的决策模式,再用轨迹预测计算具体路径

举个例子:

  1. 意图预测输出:行人“很可能要过马路”(概率85%)
  2. 轨迹预测:根据过马路意图,预测行人会沿直线穿过马路,速度1.3m/s
  3. 最终输出:未来3秒的预测轨迹点

这样既考虑了行人的长期意图,又保留了短期运动细节

总结一下:感知模型让行人“看见”世界,决策模型让行人“思考”行动,意图预测让车辆“读懂”行人。这三个模块环环相扣,缺一不可。我见过太多仿真项目只做决策不做感知,结果行人行为像提线木偶——看着在动,但毫无灵魂。

好,这一章就到这里。下一章我们会讲行人群体行为模拟——当一群行人在一起时,他们怎么互相影响?怎么形成人流?那又是另一个有意思的话题了。