4、交通流生成器:基于泊松分布的车辆到达生成,基于高斯分布的车头时距生成

做交通仿真这么多年,我有个很深的体会:交通流生成器是仿真的起点,也是决定仿真质量的第一道关卡。你想想看,如果连车辆怎么来、车与车之间怎么跟都模拟不准,后面再精细的路网模型、再复杂的信号控制策略,都是空中楼阁。

这一章,我们就来啃下这块硬骨头。我会把两种最经典的随机分布模型——泊松分布和高斯分布——掰开揉碎了讲清楚。说白了,就是解决两个核心问题:车什么时候来?来了之后车距是多少?

4.1 泊松分布:车辆到达的“时间密码”

先聊车辆到达。我刚开始做仿真时,天真地以为车是均匀到达的——比如每小时1200辆车,那就每3秒来一辆。结果呢?仿真跑出来跟实际路况完全对不上。后来才明白,车辆的到达本质上是随机的,而这种随机性,恰好可以用泊松分布来描述。

泊松分布的核心思想是:在单位时间内,事件发生的次数服从泊松分布。用在交通上就是:在给定的时间段内,到达的车辆数服从泊松分布。

数学表达:P(k) = (λ^k * e^(-λ)) / k!

其中 λ 是单位时间内的平均到达率(比如每秒0.5辆车),k 是实际到达的车辆数。

嗯,这里要注意:泊松分布适用于低流量、自由流状态。如果流量大到接近饱和,车辆之间相互影响,泊松分布就不太准了。我在做城市快速路仿真时踩过这个坑,后来改用复合分布才解决问题。

4.1.1 泊松到达的生成算法

实际编程时,我们通常用逆变换法来生成泊松到达时间。核心思路是:先产生一个[0,1]均匀分布的随机数,然后通过指数分布转换得到车头时距。

// 伪代码:生成泊松到达时间序列
function generatePoissonArrivals(lambda, duration):
    arrivals = []
    currentTime = 0.0
    
    while currentTime < duration:
        // 生成指数分布的车头时距
        u = random()  // [0,1)均匀分布
        headway = -log(1 - u) / lambda
        currentTime += headway
        
        if currentTime <= duration:
            arrivals.append(currentTime)
    
    return arrivals

我个人习惯用 -log(1-u) 而不是 -log(u),因为当 u=0 时后者会出问题。虽然概率极低,但仿真跑上百万次时,这种边界情况真的会遇到。

小技巧:lambda 的单位要统一。如果流量是 1800 veh/h,换算成秒就是 0.5 veh/s。我见过有人直接拿小时流量当 lambda 用,结果生成的车头时距小得离谱。

4.2 高斯分布:车头时距的“正态之美”

泊松分布解决了“车什么时候来”的问题,但车来了之后,前后两车之间的时间间隔(车头时距)怎么定?这就轮到高斯分布登场了。

你想想看,实际驾驶中,司机会尽量保持一个“舒适”的跟车距离。这个距离不是固定的,而是围绕某个均值上下波动——正好符合正态分布的特征。

4.2.1 高斯车头时距的生成

生成高斯分布随机数,最经典的方法是Box-Muller变换。我当年第一次看到这个公式时觉得好神奇——两个均匀分布随机数,经过一番变换,居然能生成正态分布。

// 伪代码:生成高斯分布的车头时距
function generateGaussianHeadway(mean, stdDev):
    // Box-Muller变换
    u1 = random()  // [0,1)均匀分布
    u2 = random()  // [0,1)均匀分布
    
    z0 = sqrt(-2 * log(u1)) * cos(2 * PI * u2)
    // z0 是标准正态分布 N(0,1)
    
    headway = mean + z0 * stdDev
    
    // 车头时距不能为负
    return max(headway, 0.5)  // 最小车头时距设为0.5秒

避坑指南:我曾经在生成高斯车头时距时,没有做下限保护,结果跑出负的车头时距——两辆车时间倒流了!所以一定要加 max() 或 clip() 处理。另外,标准差不能太大,否则会出现大量不合理的小值。

4.3 两种分布如何配合使用?

在实际项目中,我通常这样搭配:

  • 低流量场景(λ < 0.3 veh/s):用泊松分布生成到达时间,车头时距自然服从指数分布。此时不需要额外生成高斯车头时距。
  • 中高流量场景(λ ≥ 0.3 veh/s):用泊松分布生成到达时间,但车头时距用高斯分布重新采样。这样可以避免指数分布产生的过小车头时距。
  • 饱和流场景:泊松分布不再适用,改用定周期到达或复合分布。

下面这张图展示了两种分布配合使用的整体流程:

交通流生成器:泊松 + 高斯 配合流程 输入参数 流量 λ, 仿真时长 T λ < 0.3? (低流量判断) 是 否 泊松到达 指数车头时距 泊松到达 + 高斯车头时距 输出车辆到达序列 时间戳列表 ✅ 交通流生成完成 → 送入仿真引擎

4.4 实战:完整的生成器实现

下面是一个完整的 Python 实现,把泊松到达和高斯车头时距整合在一起。这段代码我用了很多项目,基本可以直接拿来用。

import numpy as np

class TrafficFlowGenerator:
    """交通流生成器:支持泊松到达 + 高斯车头时距"""
    
    def __init__(self, flow_rate_vehph, duration_sec):
        """
        flow_rate_vehph: 小时流量 (veh/h)
        duration_sec: 仿真时长 (秒)
        """
        self.lambda_per_sec = flow_rate_vehph / 3600.0
        self.duration = duration_sec
        self.arrivals = []
    
    def generate_poisson_arrivals(self):
        """生成泊松到达时间序列"""
        t = 0.0
        while t < self.duration:
            u = np.random.random()
            # 指数分布车头时距
            headway = -np.log(1 - u) / self.lambda_per_sec
            t += headway
            if t <= self.duration:
                self.arrivals.append(t)
        return np.array(self.arrivals)
    
    def apply_gaussian_headway(self, mean=2.0, std=0.5):
        """用高斯分布重新采样车头时距"""
        if len(self.arrivals) < 2:
            return self.arrivals
        
        new_arrivals = [self.arrivals[0]]
        for i in range(1, len(self.arrivals)):
            # 生成高斯车头时距
            u1 = np.random.random()
            u2 = np.random.random()
            z = np.sqrt(-2 * np.log(u1)) * np.cos(2 * np.pi * u2)
            headway = mean + z * std
            headway = max(headway, 0.5)  # 下限保护
            
            new_time = new_arrivals[-1] + headway
            if new_time <= self.duration:
                new_arrivals.append(new_time)
            else:
                break
        
        return np.array(new_arrivals)
    
    def run(self, use_gaussian=False):
        """运行生成器"""
        arrivals = self.generate_poisson_arrivals()
        if use_gaussian:
            arrivals = self.apply_gaussian_headway()
        return arrivals


# 使用示例
if __name__ == "__main__":
    # 流量 1200 veh/h,仿真 1 小时
    gen = TrafficFlowGenerator(flow_rate_vehph=1200, duration_sec=3600)
    
    # 低流量模式:纯泊松
    arrivals_low = gen.run(use_gaussian=False)
    print(f"纯泊松生成车辆数: {len(arrivals_low)}")
    
    # 高流量模式:泊松 + 高斯
    arrivals_high = gen.run(use_gaussian=True)
    print(f"泊松+高斯生成车辆数: {len(arrivals_high)}")

个人经验:我在做高速公路仿真时,发现纯泊松生成的车头时距方差太大,经常出现几辆车挤在一起的情况。加上高斯分布后,车头时距更均匀,仿真结果跟实测数据吻合度提高了约15%。

4.5 参数标定:别让模型脱离实际

模型再好,参数不对也是白搭。我见过太多人直接拿默认参数跑仿真,结果跟实际差十万八千里。

参数 典型值范围 标定方法 我的建议
λ (到达率) 0.1 ~ 0.8 veh/s 实测流量 / 3600 取高峰小时流量,别用全天平均
μ (高斯均值) 1.5 ~ 3.0 秒 实测车头时距均值 跟车速有关,车速越高均值越大
σ (高斯标准差) 0.3 ~ 0.8 秒 实测车头时距标准差 别超过均值的1/3,否则负值太多

我曾经踩过的坑:有一次做城市道路仿真,直接用了高速公路的参数标定结果。结果跑出来车头时距普遍偏大,路口排队长度严重低估。后来才意识到——城市道路的车头时距比高速公路小得多,因为驾驶员跟车更紧。所以参数一定要根据实际场景重新标定。

4.6 验证:生成的交通流像不像真的?

生成完了,怎么知道对不对?我一般做三个检查:

  1. 总量检查:生成的车辆总数是否接近 λ × 时长?偏差在 ±5% 以内算正常。
  2. 分布检验:用 Kolmogorov-Smirnov 检验,看车头时距是否服从目标分布。
  3. 可视化检查:画车头时距直方图,跟理论分布曲线叠在一起看。

说白了,仿真模型是给实际系统拍一张“照片”,拍得像不像,得拿真照片来对比。我每次生成完交通流,都会先跑一小段仿真,看看排队长度、旅行时间这些宏观指标跟实测是否吻合。如果对不上,回头调参数,别硬着头皮往下跑。

嗯,这一章的内容就到这里。交通流生成是仿真的地基,地基打牢了,后面盖楼才稳当。希望这些实战经验能帮你少走弯路。


可可精品资料库