【Python】 SUMO + Python 联合仿真平台

SUMO与python联合仿真(跟驰与换道) 这个对于我来说,是一个非常像解决的问题,比如我想要让其在仿真时间为20s时开始减速,这时应该当step=19时设置这一函数,其次是车辆类型depart参数时,depart表示进入仿真的时间,这个时间如果设置为0时,则出现在仿真场景中的仿真时间是1s。因为我的一个场景是需要均匀减速的,但是我用这个函数时出了问题,他是比如我设置了-2的减速度,他不会立即以这个减速度减速,而是减速度慢慢减至-2,如我设置在仿真20s时减速。要注意的是,他这个代码是关闭自动换道和跟驰的,因此在设置强制换道时,该操作不受影响。 阅读详情

SUMO 与 Python 联合仿真深度剖析

第一部分:SUMO 与 Python 联合仿真基础

1. SUMO (Simulation of Urban MObility) 简介

SUMO 是一款开源、微观、多模态的交通仿真软件。它允许研究者、开发者和交通规划者模拟大规模的交通系统,包括城市路网、公共交通、行人以及不同类型的车辆。SUMO 的核心特点在于其灵活性和可扩展性,使其能够被广泛应用于交通流理论研究、交通管理策略评估、自动驾驶算法测试等多个领域。

1.1 SUMO 的核心概念

理解 SUMO 的核心概念是进行有效仿真的前提。

  • 路网 (Network):

    • 边 (Edge): 代表道路的一段,具有方向性。通常连接两个节点。一条物理道路的双向行驶通常由两条反向的边表示。
    • 节点 (Node/Junction): 代表道路的交叉口或道路的端点。节点定义了不同边之间的连接关系和通行规则(如信号灯、优先权)。
    • 车道 (Lane): 边可以包含一条或多条车道。车辆在车道上行驶,并遵循车道规则(如速度限制、允许的车辆类型)。
    • 连接 (Connection): 定义了在交叉口处,车辆从一条边的某个车道如何行驶到另一条边的某个车道。连接可以有关联的信号灯相位。
    • 交通信号灯 (Traffic Light System, TLS): 控制交叉口交通流的设备。SUMO 允许定义复杂的信号灯逻辑和相位。
    • 类型 (Type): 可以为边和车道定义类型,例如高速公路、城市道路等,这些类型可以关联默认的速度限制、优先级等属性。
  • 交通需求 (Demand/Routes):

    • 车辆 (Vehicle): 仿真的基本动态单元。每辆车都有其类型(如轿车、卡车、公交车)、出发时间、路径以及特定的驾驶行为模型参数。
    • 路径 (Route): 车辆从起点到终点所经过的一系列边的序列。路径可以是预定义的,也可以由 SUMO 的内置路由算法动态计算。
    • 行程 (Trip): 定义车辆的起点边和终点边,以及出发时间。SUMO 可以基于行程自动计算路径。
    • 车流 (Flow): 定义在特定时间段内,从某条边出发,以一定速率(如 veh/h)或数量生成的车辆。常用于模拟背景交通。
    • 车辆类型 (Vehicle Type, vType): 定义车辆的物理属性(长度、最大速度、加速度、减速度等)和行为模型参数(如跟驰模型、换道模型参数)。
  • 仿真控制 (Simulation Control):

    • 仿真步长 (Step Length): 仿真时间推进的最小单位,通常为秒级(如1秒或0.1秒)。较小的步长可以提高仿真精度,但会增加计算时间。
    • 开始与结束时间 (Begin/End Time): 定义仿真的总时长。
    • 随机种子 (Seed): 用于控制仿真中的随机过程(如车辆出发时间、驾驶行为的随机性),确保仿真的可复现性。
  • 输出 (Output):

    • SUMO 可以生成多种类型的输出文件,记录仿真过程中的详细数据,如车辆轨迹、交叉口排队长度、行程时间、排放等。这些数据对于分析仿真结果至关重要。
1.2 SUMO 架构概述

SUMO 本身是一个命令行工具集,包含多个用于不同目的的应用程序:

  • sumo (或 sumo-gui): 核心的仿真程序。sumo 在后台运行仿真,而 sumo-gui 提供了一个图形用户界面,可以实时观察仿真过程。
  • netconvert: 用于从不同格式(如 OpenStreetMap, VISUM, Vissim)导入或生成 SUMO 路网文件 (.net.xml)。也可以用于编辑和检查路网。
  • polyconvert: 用于转换和处理地理形状数据,如建筑物、水域等,以便在 sumo-gui 中显示为多边形。
  • jtrrouter: 基于行程数据和探测器数据(可选)生成车辆路径。
  • duarouter: 基于动态用户分配 (Dynamic User Assignment) 原理生成车辆路径,试图模拟驾驶员的路径选择行为,使路网达到用户均衡状态。
  • od2trips: 将 OD (Origin-Destination) 矩阵转换为单独的行程文件。
  • dfrouter: 基于探测器数据(流量计数)生成路径。
  • 工具 (Tools): SUMO 还包含大量 Python 脚本工具(位于 sumo/tools 目录下),用于数据转换、分析、可视化等辅助任务。

SUMO 的仿真过程可以概括为:

  1. 路网加载: sumo 程序首先加载路网文件 (.net.xml)。
  2. 交通需求加载: 加载车辆路径文件 (.rou.xml) 或其他需求定义文件。
  3. 仿真循环:
    • 时间步进: 仿真时间按照定义的步长向前推进。
    • 车辆行为更新: 在每个时间步,SUMO 根据车辆的当前状态(位置、速度、加速度)、其驾驶行为模型、周围车辆状态以及路网和信号灯信息,更新所有车辆的位置和状态。这包括跟驰、换道、交叉口决策等微观行为。
    • 新车辆生成: 根据需求定义,在合适的出发时间将新车辆引入路网。
    • 数据输出: 根据配置,记录仿真数据。
    • TraCI 交互 (如果启用): 如果有外部程序通过 TraCI 连接,SUMO 会在该步骤处理 TraCI 命令并发送数据。
  4. 仿真结束: 达到预设的结束时间或满足其他终止条件。
1.3 SUMO 的配置文件

SUMO 仿真通常由一个主配置文件 (.sumocfg) 来组织和启动。该文件指定了仿真所需的各种输入文件和参数。

一个简单的 .sumocfg 文件示例:

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/sumoConfiguration.xsd"> <!-- XML 声明和 schema 位置 -->

    <input> <!-- 输入文件配置区域 -->
        <net-file value="example.net.xml"/> <!-- 指定路网文件,value 属性指向文件名 -->
        <route-files value="example.rou.xml"/> <!-- 指定路径文件,可以有多个,用逗号分隔 -->
        <!-- <additional-files value="tls_logic.add.xml, detectors.add.xml"/> --> <!-- 指定附加文件,如信号灯逻辑、探测器等 -->
    </input>

    <time> <!-- 时间相关配置区域 -->
        <begin value="0"/> <!-- 仿真开始时间,单位通常是秒 -->
        <end value="3600"/> <!-- 仿真结束时间,例如3600秒(1小时) -->
        <step-length value="1"/> <!-- 仿真步长,例如1秒 -->
    </time>

    <processing> <!-- 处理相关配置区域 -->
        <!-- <ignore-route-errors value="true"/> --> <!-- 是否忽略路径错误,例如车辆无法到达目的地 -->
        <!-- <max-depart-delay value="60"/> --> <!-- 车辆最大允许的出发延迟时间 -->
    </processing>

    <routing> <!-- 路径选择相关配置 -->
        <!-- <device.rerouting.period value="300"/> --> <!-- 启用车辆动态重新路由,并设置周期为300秒 -->
    </routing>

    <report> <!-- 报告和日志配置区域 -->
        <!-- <verbose value="true"/> --> <!-- 是否输出详细日志信息 -->
        <!-- <duration-log.statistics value="true"/> --> <!-- 是否记录仿真执行时间的统计信息 -->
        <!-- <no-step-log value="true"/> --> <!-- 是否禁止输出每个时间步的日志 (可以减少日志量) -->
    </report>

    <!-- <gui_only> --> <!-- 仅用于 sumo-gui 的配置 -->
        <!-- <start value="true"/> --> <!-- sumo-gui 启动时是否自动开始仿真 -->
        <!-- <tracker-interval value="0.1"/> --> <!-- sumo-gui 中车辆位置更新的视觉间隔 -->
    <!-- </gui_only> -->

</configuration> <!-- 配置结束标签 -->

代码解释:

  • <configuration>: 配置文件的根元素。
  • xmlns:xsi 和 xsi:noNamespaceSchemaLocation: XML 命名空间和 schema 定义,用于验证配置文件结构。
  • <input>: 定义所有输入文件的部分。
    • net-file: 指定路网文件 (.net.xml)。
    • route-files: 指定一个或多个路径文件 (.rou.xml)。
    • additional-files: 指定附加文件,例如用于定义信号灯逻辑、探测器、可变限速标志等 (.add.xml)。
  • <time>: 定义仿真时间参数。
    • begin: 仿真开始的逻辑时间。
    • end: 仿真结束的逻辑时间。如果所有车辆都已离开路网,仿真可能会提前结束。
    • step-length: 仿真时间步的长度,默认是1秒。更小的值如0.1秒可以获得更平滑的车辆运动和更精确的交互,但会显著增加计算量。
  • <processing>: 控制仿真过程中的一些行为。
    • ignore-route-errors: 如果设置为 true,即使车辆的路径存在问题(例如,路径中断或目的地不可达),仿真也会继续,问题车辆会被移除。
  • <routing>: 配置路径选择相关的参数,例如是否启用车辆在仿真过程中的动态路径重计算。
  • <report>: 控制日志输出。
    • verbose: 输出更详细的仿真过程信息。
    • duration-log.statistics: 输出关于仿真各部分执行时间的统计信息。
  • <gui_only> (可选): 仅在通过 sumo-gui 加载此配置文件时生效的参数。
2. Python 在联合仿真中的角色

虽然 SUMO 本身功能强大,但其行为主要由预定义的配置文件和内置模型驱动。当需要更灵活的控制、更复杂的逻辑决策、与外部数据源交互或集成自定义算法时,就需要一种外部控制机制。Python,凭借其简洁的语法、丰富的库生态以及与 C++ (SUMO 的主要开发语言) 良好的交互能力,成为了与 SUMO 进行联合仿真的首选语言。

Python 在 SUMO 联合仿真中的主要角色包括:

  • 动态控制仿真对象:
    • 实时修改车辆属性:如速度、目的地、路径、颜色。
    • 控制交通信号灯:根据自定义算法(如强化学习、模糊逻辑)动态调整信号灯相位和时长。
    • 生成和移除车辆:根据外部事件或算法决策动态增减交通需求。
  • 获取仿真数据:
    • 实时查询车辆状态:位置、速度、加速度、所在车道、油耗、排放等。
    • 获取路网信息:车道长度、限速、占用率、排队长度。
    • 读取探测器数据:流量、速度、密度。
  • 实现复杂算法:
    • 自定义路径规划算法:例如,基于实时交通状况的动态路径诱导。
    • 协同驾驶算法:模拟车联网 (V2X) 环境下的车辆协同行为。
    • 交通需求预测与管理。
  • 与其他系统集成:
    • 连接到数据库、Web 服务获取实时数据或参数。
    • 与机器学习框架 (TensorFlow, PyTorch) 集成,进行智能交通控制模型的训练和部署。
    • 与外部硬件或模拟器(如车辆动力学模拟器、通信模拟器)进行协同仿真。
  • 自动化仿真流程与数据分析:
    • 批量运行多次仿真实验,自动调整参数。
    • 使用 Pandas, NumPy, Matplotlib 等库对 SUMO 输出数据进行后处理、分析和可视化。

这种 Python 与 SUMO 的联合仿真能力,主要是通过 TraCI (Traffic Control Interface) 实现的。

3. TraCI 接口详解

TraCI 是 SUMO 提供的一个应用程序编程接口 (API),它允许外部应用程序在仿真运行时与 SUMO 进行双向通信和控制。TraCI 采用客户端-服务器 (Client-Server) 架构:SUMO 作为服务器端运行仿真,而 Python 脚本(或其他支持 TraCI 的语言编写的程序)作为客户端连接到 SUMO,发送命令并接收数据。

3.1 TraCI 的基本原理
  1. 启动 SUMO 服务器:

    • 在启动 SUMO (或 sumo-gui) 时,需要指定一个端口号,使其作为 TraCI 服务器监听来自客户端的连接。这通常通过命令行参数 --remote-port <PORT_NUMBER> 实现。
    • 例如:sumo -c my_simulation.sumocfg --remote-port 8813
  2. Python 客户端连接:

    • Python 脚本使用 SUMO 提供的 traci Python 库来建立与 SUMO 服务器的连接。
    • import traci
    • traci.connect(port=<PORT_NUMBER>)
  3. 仿真步进与命令执行:

    • 一旦连接建立,Python 脚本可以通过 traci.simulationStep() 命令使 SUMO 仿真向前推进一个时间步。
    • 在每个时间步之前或之后,Python 脚本可以调用 traci 库中的各种函数来:
      • 获取 (Get) 仿真对象(车辆、车道、信号灯等)的状态信息。
      • 设置 (Set/Change) 仿真对象的属性或行为。
      • 订阅 (Subscribe) 特定对象的特定变量,以便在后续时间步高效获取其变化。
  4. 关闭连接:

    • 仿真结束后或不再需要交互时,客户端应关闭连接。
    • traci.close()
3.2 TraCI Python 库

SUMO 官方提供了一个名为 traci 的 Python 库 (通常位于 sumo/tools/traci 或通过 pip install libsumo / pip install traci (后者可能指向不同项目,推荐使用 SUMO 自带或 libsumo)),它封装了与 SUMO 进行 TCP 通信的底层细节,并提供了大量易于使用的 Python 函数,这些函数直接映射到 TraCI 定义的各种命令。

要使用 traci 库,首先需要确保 Python 能够找到它。通常有以下几种方式:

  • 设置 SUMO_HOME 环境变量: 将 SUMO_HOME 指向 SUMO 的安装根目录,然后将 $SUMO_HOME/tools 添加到 Python 的 sys.path 或 PYTHONPATH 环境变量中。
  • 直接修改 sys.path: 在 Python 脚本的开头添加:
    import os, sys # 导入 os 和 sys 模块
    if 'SUMO_HOME' in os.environ: # 检查 SUMO_HOME 环境变量是否存在
        tools = os.path.join(os.environ['SUMO_HOME'], 'tools') # 构建 tools 目录的路径
        sys.path.append(tools) # 将 tools 目录添加到 Python 的搜索路径中
    else:
        sys.exit("please declare environment variable 'SUMO_HOME'") # 如果未设置 SUMO_HOME,则退出程序并提示
    import traci # 导入 traci 库
    
  • 安装 libsumo: libsumo 是 SUMO 的一个库版本,它允许将 SUMO 作为 Python 模块直接导入和运行,traci 命令通过 libsumo 执行,避免了 TCP 通信的开销,性能通常更好。
    # 使用 libsumo 时,启动方式可能略有不同
    # import libsumo as traci # 将 libsumo 导入并别名为 traci
    # traci.start(["sumo", "-c", "my_simulation.sumocfg"]) # 使用 libsumo 启动仿真
    # ... 后续 traci 命令与标准 traci 类似
    
    对于复杂的联合仿真,libsumo 通常是更推荐的方式,因为它避免了进程间通信的开销。
3.3 TraCI 命令类别与常用函数 (概览)

TraCI 提供的命令非常丰富,覆盖了仿真的方方面面。这些命令通常按照它们操作的仿真对象领域 (Domain) 进行组织。以下是一些主要领域及其常用功能的概览 (具体函数名可能略有差异,以 traci.<domain>.<functionName> 的形式调用):

  • simulation (仿真控制域):

    • traci.simulationStep(time_step): 使仿真前进指定的逻辑时间(如果 time_step 未提供或为0,则前进一个仿真配置文件中定义的 step-length)。这是 TraCI 交互的核心循环驱动。
    • traci.simulation.getTime(): 获取当前仿真时间。
    • traci.simulation.getDeltaT(): 获取当前仿真步长。
    • traci.simulation.loadState(filepath) / traci.simulation.saveState(filepath): 加载/保存仿真状态。
    • traci.simulation.getMinExpectedNumber(): 获取当前时间步期望的客户端数量(通常是1,如果使用多客户端则更多)。
  • vehicle (车辆域):

    • traci.vehicle.getIDList(): 获取当前路网中所有车辆的 ID 列表。
    • traci.vehicle.getIDCount(): 获取当前路网中车辆的总数。
    • traci.vehicle.getPosition(vehID): 获取指定车辆的 (x, y) 坐标。
    • traci.vehicle.getAngle(vehID): 获取车辆的行驶角度 (0度为x轴正向,逆时针增加)。
    • traci.vehicle.getSpeed(vehID): 获取车辆的当前速度 (m/s)。
    • traci.vehicle.getLaneID(vehID): 获取车辆当前所在车道的 ID。
    • traci.vehicle.getRoadID(vehID): 获取车辆当前所在边的 ID。
    • traci.vehicle.getRoute(vehID): 获取车辆当前的路径(边的列表)。
    • traci.vehicle.setSpeed(vehID, speed): 设置车辆的目标速度。
    • traci.vehicle.changeTarget(vehID, edgeID): 改变车辆的当前路径的下一个目标边(会触发重新规划路径到新的最终目的地)。
    • traci.vehicle.setRoute(vehID, edgeList): 为车辆设置一条全新的路径。
    • traci.vehicle.moveTo(vehID, laneID, pos): 将车辆移动到指定车道的指定位置 (慎用,可能破坏物理真实性)。
    • traci.vehicle.add(vehID, routeID, typeID="DEFAULT_VEHTYPE", depart=simTime, departLane="first", departPos="base", departSpeed="0"): 在仿真中动态添加一辆新车。
    • traci.vehicle.remove(vehID): 从仿真中移除一辆车。
    • traci.vehicle.setColor(vehID, (R, G, B, A)) : 设置车辆颜色 (A是alpha透明度, 0-255)。
    • traci.vehicle.getLeader(vehID, dist): 获取指定车辆前方一定距离内的前车信息。
    • traci.vehicle.getFollowSpeed(vehID, speed, gap, leaderSpeed, leaderGap): 计算基于跟驰模型的安全跟随速度。
  • lane (车道域):

    • traci.lane.getLength(laneID): 获取车道的长度。
    • traci.lane.getMaxSpeed(laneID): 获取车道的最大允许速度。
    • traci.lane.getLastStepVehicleIDs(laneID): 获取上一个时间步在该车道上的车辆列表。
    • traci.lane.getLastStepMeanSpeed(laneID): 获取上一个时间步该车道的平均车速。
    • traci.lane.getLastStepOccupancy(laneID): 获取上一个时间步该车道的占用率。
    • traci.lane.getWaitingTime(laneID): 获取该车道上车辆的总等待时间。
  • edge (边域):

    • traci.edge.getLastStepVehicleIDs(edgeID): 获取上一个时间步在该边上的车辆列表。
    • traci.edge.getLastStepMeanSpeed(edgeID): 获取上一个时间步该边的平均车速。
    • traci.edge.getTraveltime(edgeID): 获取通过该边的(动态计算的)行程时间。
    • traci.edge.setAllowed(edgeID, allowedVehicleClasses): 设置允许通过该边的车辆类型。
  • junction (交叉口域):

    • traci.junction.getPosition(junctionID): 获取交叉口的 (x, y) 坐标。
  • trafficlight (或 tls, 交通信号灯域):

    • traci.trafficlight.getIDList(): 获取所有交通信号灯系统的 ID 列表。
    • traci.trafficlight.getRedYellowGreenState(tlsID): 获取信号灯当前各进口道信号相位的状态字符串 (如 “rrgGrrgG”)。
    • traci.trafficlight.getPhase(tlsID): 获取当前信号灯的相位索引。
    • traci.trafficlight.setPhase(tlsID, phaseIndex): 设置信号灯到指定的相位索引。
    • traci.trafficlight.setPhaseDuration(tlsID, phaseDuration): 设置当前相位的持续时间。
    • traci.trafficlight.setProgram(tlsID, programID): 切换到指定的信号灯控制程序。
    • traci.trafficlight.getCompleteRedYellowGreenDefinition(tlsID): 获取信号灯所有相位的完整定义。
  • route (路径域):

    • traci.route.add(routeID, edgeList): 在仿真中添加一个新的路径定义。
    • traci.route.getEdges(routeID): 获取指定路径ID包含的边列表。
  • poi (Point of Interest, 兴趣点域) 和 polygon (多边形域):

    • 用于在 sumo-gui 中添加和控制可视化的点或形状。
    • traci.poi.add(poiID, x, y, color, type="", layer=0)
    • traci.polygon.add(polygonID, shape_xy_list, color, fill=True, type="", layer=0)
  • 订阅机制 (Subscription):

    • 为了高效获取大量数据,TraCI 提供了订阅机制。客户端可以预先订阅特定对象的特定变量。之后,在每个 traci.simulationStep() 执行后,SUMO 会自动将这些被订阅变量的最新值发送给客户端,无需客户端为每个变量单独发送 get 请求。
    • traci.vehicle.subscribe(vehID, varIDs=[traci.constants.VAR_SPEED, traci.constants.VAR_POSITION])
    • traci.vehicle.getSubscriptionResults(vehID)
    • traci.constants 模块定义了大量可订阅的变量常量,例如 VAR_SPEED, VAR_POSITION, VAR_LANE_ID, LANE_WAITING_TIME, LAST_STEP_MEAN_SPEED 等。

这只是 TraCI 功能的一个缩影。详细的命令列表和用法可以在 SUMO 官方文档的 TraCI 部分找到。理解这些命令的分类和作用是编写复杂 Python 控制脚本的基础。

4. 环境搭建与第一个联合仿真实例

现在,我们将通过一个简单的实例来演示如何搭建 SUMO 与 Python 的联合仿真环境,并运行一个基本的交互脚本。

4.1 环境要求与安装
  1. 安装 SUMO:

    • 从 SUMO 官方网站 (https://sumo.dlr.de/ 或 https://eclipse.dev/sumo/) 下载适合您操作系统的最新稳定版 SUMO。
    • Windows: 通常提供预编译的安装包。
    • Linux: 可以通过包管理器 (如 sudo apt-get install sumo sumo-tools sumo-doc) 或从源码编译安装。
    • macOS: 可以通过 Homebrew (brew install sumo) 或从源码编译。
    • 安装完成后,确保 SUMO 的 bin 目录(包含 sumo, sumo-gui, netconvert 等可执行文件)和 tools 目录(包含 Python 脚本和 traci 库)已正确添加到系统环境变量 PATH 和 PYTHONPATH (或 SUMO_HOME 已设置)。
    • 验证安装:在命令行输入 sumo --version,应能看到版本信息。
  2. 安装 Python:

    • 如果尚未安装,请从 Python 官网 (https://www.python.org/) 下载并安装 Python (推荐 3.7 或更高版本)。
    • 确保 Python 的 Scripts 目录(通常包含 pip)已添加到系统 PATH。
  3. 验证 traci 库:

    • 打开 Python 解释器,尝试 import traci。如果没有错误,说明 traci 库可以被 Python 找到。如果找不到,请检查 SUMO_HOME 和 PYTHONPATH 设置,或者确保 $SUMO_HOME/tools 在 sys.path 中。
4.2 创建一个简单的 SUMO 场景

我们将创建一个包含一条单向道路和一个交通信号灯控制的交叉口的简单场景。

1. 定义路网节点 (example.nod.xml):

<nodes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/nodes_file.xsd"> <!-- 节点文件根元素 -->
    <node id="N1" x="-500.0" y="0.0" type="priority"/> <!-- 定义节点N1,坐标(-500,0),类型为优先通行(非信号灯控制) -->
    <node id="N2" x="0.0"  y="0.0" type="traffic_light"/> <!-- 定义节点N2(交叉口中心),坐标(0,0),类型为信号灯控制 -->
    <node id="N3" x="500.0" y="0.0" type="priority"/> <!-- 定义节点N3,坐标(500,0),类型为优先通行 -->
    <node id="N4" x="0.0"  y="500.0" type="priority"/> <!-- 定义节点N4,坐标(0,500),类型为优先通行 -->
    <node id="N5" x="0.0"  y="-500.0" type="priority"/> <!-- 定义节点N5,坐标(0,-500),类型为优先通行 -->
</nodes> <!-- 节点文件结束 -->

代码解释:

  • <nodes>: 定义路网中所有节点(交叉口或道路端点)。
  • <node>: 定义单个节点。
    • id: 节点的唯一标识符。
    • x, y: 节点的二维坐标。
    • type: 节点的类型。priority 表示由优先规则控制(例如主路优先),traffic_light 表示该交叉口由信号灯控制。

2. 定义路网边 (example.edg.xml):

<edges xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/edges_file.xsd"> <!-- 边文件根元素 -->
    <edge id="E1" from="N1" to="N2" priority="78" numLanes="2" speed="13.89"/> <!-- 定义边E1,从N1到N2,2车道,限速13.89m/s (50km/h) -->
    <edge id="E2" from="N2" to="N3" priority="46" numLanes="2" speed="13.89"/> <!-- 定义边E2,从N2到N3 -->
    <edge id="E3" from="N4" to="N2" priority="78" numLanes="1" speed="11.11"/> <!-- 定义边E3,从N4到N2,1车道,限速11.11m/s (40km/h) -->
    <edge id="E4" from="N2" to="N5" priority="46" numLanes="1" speed="11.11"/> <!-- 定义边E4,从N2到N5 -->
</edges> <!-- 边文件结束 -->

代码解释:

  • <edges>: 定义路网中所有边(道路段)。
  • <edge>: 定义单条边。
    • id: 边的唯一标识符。
    • from: 边的起始节点 ID。
    • to: 边的终止节点 ID。
    • priority: 边的优先级(数值越大优先级越高),用于 netconvert 确定默认的通行权和信号灯相位。
    • numLanes: 该边包含的车道数量。
    • speed: 该边的最大允许速度 (单位: m/s)。

3. 生成 SUMO 路网文件 (.net.xml):
打开命令行,切换到包含上述 .nod.xml 和 .edg.xml 文件的目录,然后运行 netconvert:

netconvert --node-files=example.nod.xml --edge-files=example.edg.xml --output-file=example.net.xml

这将生成 example.net.xml 文件,其中包含了 SUMO 仿真的路网拓扑、连接关系和默认的信号灯逻辑 (因为节点 N2 被定义为 traffic_light 类型,netconvert 会为其生成一个默认的信号灯方案)。

4. 定义车辆路径 (example.rou.xml):

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/routes_file.xsd"> <!-- 路径文件根元素 -->
    <vType id="car_eastbound" accel="2.6" decel="4.5" sigma="0.5" length="5" maxSpeed="13.89" color="1,0,0"/> <!-- 定义东行车辆类型 "car_eastbound",红色 -->
    <vType id="car_northbound" accel="2.6" decel="4.5" sigma="0.5" length="5" maxSpeed="11.11" color="0,0,1"/> <!-- 定义北行车辆类型 "car_northbound",蓝色 -->

    <route id="route_east" edges="E1 E2"/> <!-- 定义路径 "route_east",从 E1 到 E2 -->
    <route id="route_north" edges="E3 E4"/> <!-- 定义路径 "route_north",从 E3 到 E4 -->

    <!-- 每隔一段时间产生车辆 -->
    <flow id="flow_east" type="car_eastbound" route="route_east" begin="0" end="300" period="10" departLane="random" departSpeed="max"/> <!-- 定义东向车流,0-300秒内,每10秒一辆 -->
    <flow id="flow_north" type="car_northbound" route="route_north" begin="0" end="300" period="15" departLane="random" departSpeed="max"/> <!-- 定义北向车流,0-300秒内,每15秒一辆 -->

    <!-- 也可以定义单个车辆 -->
    <!-- <vehicle id="veh0" type="car_eastbound" route="route_east" depart="0" departLane="0" departSpeed="10"/> --> <!-- 定义单个车辆 veh0 -->
</routes> <!-- 路径文件结束 -->

代码解释:

  • <routes>: 定义车辆类型、路径和交通需求。
  • <vType>: 定义车辆类型。
    • id: 车辆类型的唯一标识符。
    • accel: 最大加速度 (m/s^2)。
    • decel: 最大(舒适)减速度 (m/s^2)。
    • sigma: 驾驶员不完美性参数 (0-1,表示驾驶行为的随机性,1为完美驾驶)。
    • length: 车辆长度 (m)。
    • maxSpeed: 车辆最大期望速度 (m/s)。
    • color: 车辆在 sumo-gui 中显示的颜色 (R,G,B 格式,0-1 或 0-255 范围,用逗号分隔)。
  • <route>: 定义一条具体的路径。
    • id: 路径的唯一标识符。
    • edges:构成该路径的边 ID 序列,用空格分隔。
  • <flow>: 定义一个车流生成器。
    • id: 车流的唯一标识符。
    • type: 使用的车辆类型 ID。
    • route: 使用的路径 ID。
    • begin: 车流开始生成的时间 (s)。
    • end: 车流结束生成的时间 (s)。
    • period: 生成车辆的时间间隔 (s)。例如,period="10" 表示大约每10秒生成一辆车。也可以用 vehsPerHour 或 probability。
    • departLane: 车辆出发时选择的车道 (“first”, “last”, “random”, “free”, 或具体索引)。
    • departSpeed: 车辆出发时的速度 (“max”, “random”, 或具体值)。
  • <vehicle> (注释掉的部分): 定义单个车辆。属性与 flow 中的类似,但 depart 指定确切的出发时间。

5. 创建 SUMO 配置文件 (example.sumocfg):

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/sumoConfiguration.xsd"> <!-- XML 声明 -->
    <input> <!-- 输入文件 -->
        <net-file value="example.net.xml"/> <!-- 路网文件 -->
        <route-files value="example.rou.xml"/> <!-- 路径文件 -->
    </input>
    <time> <!-- 时间配置 -->
        <begin value="0"/> <!-- 开始时间 -->
        <end value="360"/> <!-- 结束时间,例如360秒 -->
        <step-length value="1"/> <!-- 步长1秒 -->
    </time>
    <report> <!-- 报告配置 -->
        <verbose value="true"/> <!-- 详细输出 -->
        <duration-log.statistics value="true"/> <!-- 仿真时长统计 -->
    </report>
    <!-- 如果使用 sumo-gui,可以添加以下配置 -->
    <gui_only> <!-- 仅用于 sumo-gui -->
        <delay value="100"/> <!-- sumo-gui 中每个仿真步之间的延迟 (ms),用于减慢可视化 -->
        <start value="true"/> <!-- 启动时自动开始仿真 -->
    </gui_only>
</configuration> <!-- 配置结束 -->

代码解释:

  • 这个配置文件指定了我们刚刚创建的路网和路径文件,并设置了仿真时长为360秒。
  • gui_only 部分中的 delay 可以让 sumo-gui 中的仿真看起来慢一些,便于观察。
4.3 编写 Python TraCI 控制脚本 (runner.py)

现在,我们编写一个 Python 脚本,它将连接到 SUMO,运行仿真,并在每个时间步打印出当前时间和路网中的车辆数量。

import os       # 导入os模块,用于操作系统相关功能,如环境变量
import sys      # 导入sys模块,用于访问Python解释器相关变量和函数
import traci    # 导入traci库,用于与SUMO进行交互
import time     # 导入time模块,用于添加延迟等时间相关操作

# SUMO 启动命令,根据你的系统和SUMO安装位置可能需要调整
# 如果使用 sumo-gui,可以将 "sumo" 替换为 "sumo-gui"
SUMO_BINARY = "sumo"  # 定义SUMO可执行文件的名称(可以是 "sumo" 或 "sumo-gui")
# SUMO_BINARY = "sumo-gui" # 如果想用图形界面,取消这行注释,并注释掉上一行

# 检查 SUMO_HOME 环境变量,并将 tools 目录添加到 Python 路径
if 'SUMO_HOME' in os.environ:                                           # 检查 'SUMO_HOME' 是否在环境变量中定义
    tools_path = os.path.join(os.environ['SUMO_HOME'], 'tools')         # 构建 SUMO tools 目录的路径
    if tools_path not in sys.path:                                      # 如果 tools 目录不在 Python 的模块搜索路径中
        sys.path.append(tools_path)                                     # 将其添加进去
else:                                                                   # 如果 'SUMO_HOME' 未定义
    sys.exit("请设置环境变量 'SUMO_HOME' 指向您的SUMO安装目录。")     # 退出脚本并提示用户设置环境变量

# SUMO 配置文件路径
sumo_config_file = "example.sumocfg"                                    # 定义SUMO配置文件的名称

# TraCI 连接参数
traci_port = 8813                                                       # 定义TraCI服务器将监听的端口号
use_libsumo = False                                                      # 是否使用 libsumo (默认为 False,使用 TCP 连接)

def run_simulation():                                                   # 定义一个函数来运行仿真
    """
    启动SUMO作为服务器,并使用TraCI控制仿真。
    """
    if not use_libsumo:                                                 # 如果不使用 libsumo (即使用标准的 TCP TraCI 连接)
        # 构建 SUMO 启动命令
        sumo_cmd = [
            SUMO_BINARY,                                                # SUMO 可执行文件 (sumo 或 sumo-gui)
            "-c", sumo_config_file,                                     # "-c" 参数后跟配置文件名
            "--remote-port", str(traci_port),                           # "--remote-port" 参数后跟指定的端口号
            "--step-length", "1",                                       # 设置仿真步长为1秒 (也可以在sumocfg中定义)
            "--quit-on-end"                                             # 仿真结束后自动退出SUMO
        ]
        print(f"正在启动 SUMO: {
     
     ' '.join(sumo_cmd)}")                 # 打印将要执行的SUMO启动命令
        
        # 在一个独立的进程中启动 SUMO
        # 注意:对于更健壮的实现,可以使用 subprocess 模块并更好地管理子进程
        # 这里为了简单,我们假设用户会手动先启动SUMO,或者在另一个终端启动
        # 更推荐的方式是使用 subprocess.Popen 来启动和管理 SUMO 进程
        # import subprocess
        # sumo_proc = subprocess.Popen(sumo_cmd)
        # time.sleep(2) # 等待SUMO启动

        # !!! 重要提示: 为了简单起见,此脚本假设SUMO已经由用户在另一个终端手动启动,
        # 或者您将在运行此Python脚本之前先启动SUMO服务器。
        # 例如,手动打开一个终端并运行:
        # sumo -c example.sumocfg --remote-port 8813
        # 或者 sumo-gui -c example.sumocfg --remote-port 8813 --start --delay 100
        #
        # 如果希望脚本自动启动SUMO,请使用 subprocess 模块。
        print(f"\n请确保 SUMO 服务器已在端口 {
     
     traci_port} 上启动并加载了 {
     
     sumo_config_file}") # 提示用户启动SUMO
        print("例如,在另一个终端运行: sumo -c example.sumocfg --remote-port 8813")        # 给出示例命令
        input("按 Enter键 在确认SUMO已启动后继续...")                                  # 等待用户确认

        try:
            # 连接到 SUMO TraCI 服务器
            traci.connect(port=traci_port)                              # 尝试连接到指定端口的TraCI服务器
            print(f"已成功连接到 SUMO (端口: {
     
     traci_port})")            # 打印连接成功信息
        except traci.exceptions.TraCIException as e:                    # 如果连接失败,捕获TraCI异常
            print(f"连接 SUMO 失败: {
     
     e}")                               # 打印错误信息
            print("请确保SUMO实例正在运行,并且 --remote-port 设置正确。")# 提示检查SUMO状态和端口
            return                                                      # 结束函数执行

    else: # 使用 libsumo
        print("正在使用 libsumo 启动仿真...")                           # 打印使用libsumo的信息
        # 注意:如果使用 libsumo,导入时通常用 import libsumo as traci
        # 但如果 SUMO_HOME/tools 在路径中,原生的 traci 模块也可以被 libsumo "劫持"
        # 为了清晰,假设我们已经 import traci (它可能是 libsumo 的接口)
        traci.start([SUMO_BINARY, "-c", sumo_config_file, "--quit-on-end"]) # 使用traci.start(libsumo方式)启动仿真
        print("libsumo 仿真已启动。")                                   # 打印启动成功信息

    # 仿真主循环
    step = 0                                                            # 初始化仿真步数计数器
    max_steps = 360                                                     # 定义最大仿真步数 (与sumocfg中的end时间对应)
    
    try:
        while step < max_steps:                                         # 当仿真步数小于最大步数时循环
            traci.simulationStep()                                      # 执行一个仿真步
            
            current_time = traci.simulation.getTime()                   # 获取当前仿真时间
            vehicle_ids = traci.vehicle.getIDList()                     # 获取当前路网中所有车辆的ID列表
            num_vehicles = len(vehicle_ids)                             # 计算车辆数量
            
            print(f"仿真时间: {
     
     current_time:.2f}s, 当前车辆数: {
     
     num_vehicles}") # 打印当前时间和车辆数

            if num_vehicles > 0 and step % 10 == 0:                     # 如果有车辆并且是每10步
                first_veh_id = vehicle_ids[0]                           # 获取第一辆车的ID
                speed = traci.vehicle.getSpeed(first_veh_id)            # 获取该车的速度
                lane = traci.vehicle.getLaneID(first_veh_id)            # 获取该车所在的车道ID
                print(f"  -> 示例车辆 '{
     
     first_veh_id}': 速度={
     
     speed:.2f} m/s, 车道='{
     
     lane}'") # 打印示例车辆信息

            # 在这里可以添加更多的 TraCI 命令来控制或获取信息
            # 例如,改变交通灯相位,改变车辆速度等
            if current_time > 50 and current_time < 100:                # 如果仿真时间在50到100秒之间
                tls_ids = traci.trafficlight.getIDList()                # 获取所有交通信号灯的ID
                if "N2" in tls_ids:                                     # 如果ID为 "N2" 的信号灯存在
                    # 简单示例:强制将N2信号灯的某个相位(例如索引0)持续时间延长
                    # 注意:直接控制相位可能与SUMO内置逻辑冲突,更复杂控制需禁用默认逻辑
                    # traci.trafficlight.setPhase("N2", 0) # 切换到相位0
                    # traci.trafficlight.setPhaseDuration("N2", 30) # 将当前相位持续时间设为30秒
                    pass # 暂时不执行实际的信号灯控制,避免与默认逻辑冲突

            step += 1                                                   # 仿真步数加1
            # time.sleep(0.05) # 如果希望减慢Python脚本的执行速度(主要用于观察,不影响SUMO仿真速度)

    except traci.exceptions.TraCIException as e:                        # 捕获仿真过程中的TraCI异常
        print(f"仿真过程中发生 TraCI 错误: {
     
     e}")                       # 打印错误信息
    except Exception as e:                                              # 捕获其他可能的Python异常
        print(f"仿真过程中发生未知错误: {
     
     e}")                           # 打印未知错误信息
    finally:                                                            # 无论成功或失败,最后都执行
        print("正在关闭 TraCI 连接...")                                 # 打印关闭连接信息
        traci.close()                                                   # 关闭与SUMO的TraCI连接
        print("TraCI 连接已关闭。")                                     # 打印连接已关闭信息
        # if not use_libsumo and 'sumo_proc' in locals() and sumo_proc.poll() is None:
        #     print("正在终止 SUMO 进程...")
        #     sumo_proc.terminate()
        #     sumo_proc.wait()
        #     print("SUMO 进程已终止。")

if __name__ == "__main__":                                              # 如果该脚本是主程序运行
    # 确保 SUMO 配置文件存在
    if not os.path.exists(sumo_config_file):                            # 检查配置文件是否存在
        print(f"错误: SUMO 配置文件 '{
     
     sumo_config_file}' 未找到。")    # 打印错误信息
        print("请先创建场景文件 (example.nod.xml, example.edg.xml, example.rou.xml) 并用 netconvert 生成 example.net.xml。") # 提示创建文件
    else:
        run_simulation()                                                # 调用运行仿真函数

代码解释 (runner.py):

  1. 导入模块:
    • os, sys: 用于处理环境变量和 Python 路径。
    • traci: SUMO TraCI 客户端库。
    • time: (可选) 用于添加延迟,方便观察。
  2. SUMO_BINARY: 定义了要使用的 SUMO 可执行文件。可以是 sumo (命令行,无 GUI) 或 sumo-gui (带图形界面)。
  3. SUMO_HOME 检查: 这段代码确保 $SUMO_HOME/tools 目录在 Python 的模块搜索路径中,这样 import traci 才能成功。
  4. sumo_config_file: 指定要加载的 SUMO 配置文件。
  5. traci_port: Python 客户端将尝试连接到 SUMO 服务器的 TCP 端口。这个端口号必须与启动 SUMO 时 --remote-port 参数指定的端口号一致。
  6. use_libsumo: 一个布尔标志,用于选择是使用传统的 TCP TraCI 连接还是使用 libsumo。
  7. run_simulation() 函数:
    • 启动 SUMO (非 libsumo 模式):
      • 它构建了一个 sumo_cmd 列表,包含了启动 SUMO 服务器所需的命令和参数。
      • 重要: 在这个简化版本中,脚本提示用户手动启动 SUMO。在实际应用中,通常会使用 subprocess.Popen 在 Python 脚本中自动启动并管理 SUMO 进程。
      • traci.connect(port=traci_port): 尝试连接到正在运行的 SUMO 实例。
    • 启动 SUMO (libsumo 模式):
      • traci.start([...]): 直接通过 libsumo 启动仿真,参数列表与命令行启动 SUMO 类似,但不包括 --remote-port。
    • 仿真循环 (while step < max_steps):
      • traci.simulationStep(): 这是核心命令。它会通知 SUMO 将仿真向前推进一个时间步(由 SUMO 配置文件中的 step-length 或 TraCI 命令指定)。SUMO 完成计算后,控制权返回给 Python 脚本。
      • traci.simulation.getTime(): 获取当前的仿真时间。
      • traci.vehicle.getIDList(): 获取当前时刻所有活动车辆的 ID 列表。
      • 打印当前时间和车辆数。
      • if num_vehicles > 0 and step % 10 == 0: 一个简单示例,每隔10步,如果路网中有车,就获取第一辆车的速度和车道ID并打印。
      • step += 1: 增加 Python 端的步数计数器。
    • 异常处理 (try...except...finally):
      • 捕获 traci.exceptions.TraCIException 以处理与 SUMO 通信或命令执行中可能发生的错误。
      • 捕获通用的 Exception 以处理其他 Python 错误。
      • finally 块确保无论仿真是否成功完成或中途出错,traci.close() 都会被调用以关闭与 SUMO 的连接。这对于释放资源和允许 SUMO 正常退出非常重要。
  8. if __name__ == "__main__":: 这是 Python脚本的常用入口点,确保 run_simulation() 仅在脚本作为主程序直接运行时被调用。
4.4 运行第一个联合仿真
  1. 准备文件: 确保 example.nod.xml, example.edg.xml, example.rou.xml, example.net.xml (由 netconvert 生成), example.sumocfg 和 runner.py 都在同一个目录下。

  2. 选择启动方式:

    • 方式一:手动启动 SUMO,然后运行 Python (推荐初学者理解流程)

      1. 打开一个终端 (我们称之为 终端1)。
      2. 在 终端1 中,导航到包含所有文件的目录。
      3. 启动 SUMO 服务器。如果你想看图形界面:
        sumo-gui -c example.sumocfg --remote-port 8813 --start --delay 100
        
        或者,如果只想在后台运行:
        sumo -c example.sumocfg --remote-port 8813
        
        --start 会让 sumo-gui 自动开始仿真,--delay 100 会让 GUI 中的每一步之间有100毫秒的延迟,方便观察。
      4. 打开另一个终端 (我们称之为 终端2)。
      5. 在 终端2 中,导航到相同的目录。
      6. 运行 Python 脚本中的提示,在 runner.py 脚本中,当看到 “按 Enter键 在确认SUMO已启动后继续…” 时,按 Enter。
      7. 然后运行 Python 脚本:
        python runner.py
        
    • 方式二:使用 libsumo (在 runner.py 中设置 use_libsumo = True)

      1. 修改 runner.py,将 use_libsumo = True。
      2. (可选) 修改 SUMO_BINARY,如果想用 GUI 并且 libsumo 支持 (通常是支持的),可以设为 "sumo-gui"。
      3. 直接运行 Python 脚本:
        python runner.py
        
        此时,Python 脚本会通过 libsumo 直接启动和控制 SUMO。
  3. 观察输出:

    • 如果你使用了 sumo-gui,你会在图形界面中看到车辆在路网上行驶,信号灯在变化。
    • 在运行 Python 脚本的终端(终端2 或直接运行 libsumo 时的终端)中,你会看到类似以下的输出:
      正在启动 SUMO: sumo-gui -c example.sumocfg --remote-port 8813 --step-length 1 --quit-on-end (如果未使用libsumo且未手动启动)
      ...
      请确保 SUMO 服务器已在端口 8813 上启动并加载了 example.sumocfg
      例如,在另一个终端运行: sumo -c example.sumocfg --remote-port 8813
      按 Enter键 在确认SUMO已启动后继续...
      已成功连接到 SUMO (端口: 8813)
      仿真时间: 1.00s, 当前车辆数: 0
      仿真时间: 2.00s, 当前车辆数: 0
      ...
      仿真时间: 10.00s, 当前车辆数: 1
        -> 示例车辆 'flow_east.0': 速度=13.89 m/s, 车道='E1_0'
      仿真时间: 11.00s, 当前车辆数: 1
      ...
      仿真时间: 15.00s, 当前车辆数: 2
      仿真时间: 16.00s, 当前车辆数: 2
      ...
      仿真时间: 20.00s, 当前车辆数: 3
        -> 示例车辆 'flow_east.0': 速度=0.00 m/s, 车道='E1_0' (可能在等红灯)
      ...
      正在关闭 TraCI 连接...
      TraCI 连接已关闭。
      

这个简单的例子展示了 SUMO 与 Python 联合仿真的基本流程:定义 SUMO 场景 -> 编写 Python 控制脚本 -> 启动 SUMO 服务器 -> Python 客户端连接并控制 -> 结束。

第二部分:TraCI 核心控制与数据交互精解

在第一部分的基础上,我们现在将聚焦于 TraCI 的具体应用,通过丰富的代码示例来展示其强大的控制能力和数据获取能力。

2.1 车辆的精细控制与信息获取

车辆是交通仿真的核心动态元素。TraCI 提供了大量函数来实时监控和操纵车辆的行为。

2.1.1 获取车辆基本信息

在仿真过程中,我们经常需要获取车辆的各种状态信息。

# (假设 traci 已经连接,并且仿真正在运行)
# 在仿真循环的某个时间步内:
# current_sim_time = traci.simulation.getTime() # 获取当前仿真时间

vehicle_ids = traci.vehicle.getIDList() # 获取当前路网中所有活动车辆的ID列表 (返回一个字符串元组)
print(f"当前时间: {
     
     traci.simulation.getTime():.2f}s, 车辆ID列表: {
     
     vehicle_ids}") # 打印当前时间和车辆ID列表

if len(vehicle_ids) > 0: # 如果路网中有车辆
    for veh_id in vehicle_ids: # 遍历每一辆车的ID
        try:
            # 获取位置 (x, y 坐标)
            pos_x, pos_y = traci.vehicle.getPosition(veh_id) # 获取车辆veh_id的笛卡尔坐标(x,y)
            # SUMO 内部坐标系,通常原点在路网的某个特定位置

            # 获取地理位置 (经纬度,如果路网有地理参考)
            # lon, lat = traci.vehicle.getPosition3D(veh_id) # 如果路网是地理参考的,这会返回(lon, lat, z)
            # 或者使用 traci.simulation.convertGeo(pos_x, pos_y, fromGeo=False) 转换为经纬度
            # 如果路网本身就是用经纬度定义的,getPosition 可能直接反映一定程度的地理位置
            
            speed = traci.vehicle.getSpeed(veh_id) # 获取车辆veh_id的当前速度 (m/s)
            
            angle = traci.vehicle.getAngle(veh_id) # 获取车辆veh_id的行驶角度 (度),0度沿X轴正向,逆时针增加
                                                 # 这个角度是车辆几何中心的朝向
            
            road_id = traci.vehicle.getRoadID(veh_id) # 获取车辆veh_id当前所在的边的ID
            
            lane_id = traci.vehicle.getLaneID(veh_id) # 获取车辆veh_id当前所在的车道的ID
            
            lane_index = traci.vehicle.getLaneIndex(veh_id) # 获取车辆veh_id当前所在车道在其所在边上的索引 (从0开始,最右侧为0)
            
            lane_position = traci.vehicle.getLanePosition(veh_id) # 获取车辆veh_id在其当前车道上的位置 (从车道起点开始计算的距离,m)
            
            vehicle_type = traci.vehicle.getTypeID(veh_id) # 获取车辆veh_id的类型ID (例如 "car_eastbound")
            
            route_id = traci.vehicle.getRouteID(veh_id) # 获取车辆veh_id当前使用的路径ID
            
            current_route_edges = traci.vehicle.getRoute(veh_id) # 获取车辆veh_id当前剩余路径的边列表 (一个字符串元组)
            
            # 累积等待时间 (车辆速度低于某一阈值时开始累积)
            waiting_time = traci.vehicle.getWaitingTime(veh_id) # 获取车辆veh_id的总累积等待时间 (s)
            
            # 车辆行驶的总距离
            distance_travelled = traci.vehicle.getDistance(veh_id) # 获取车辆veh_id从出发开始已行驶的总距离 (m)

            # 获取车辆的长度
            length = traci.vehicle.getLength(veh_id) # 获取车辆veh_id的长度 (m)

            # 获取车辆颜色 (RGBA格式,0-255)
            color = traci.vehicle.getColor(veh_id) # 获取车辆veh_id的颜色,返回一个 (R, G, B, Alpha) 元组

            print(f"  车辆ID: {
     
     veh_id}") # 打印车辆ID
            print(f"    位置: ({
     
     pos_x:.2f}, {
     
     pos_y:.2f})") # 打印车辆位置,保留两位小数
            print(f"    速度: {
     
     speed:.2f} m/s") # 打印车辆速度,保留两位小数
            print(f"    角度: {
     
     angle:.2f} 度") # 打印车辆角度,保留两位小数
            print(f"    所在边: {
     
     road_id}, 车道: {
     
     lane_id} (索引 {
     
     lane_index})") # 打印车辆所在边、车道和车道索引
            print(f"    车道内位置: {
     
     lane_position:.2f} m") # 打印车辆在车道内的位置,保留两位小数
            print(f"    类型: {
     
     vehicle_type}, 路径ID: {
     
     route_id}") # 打印车辆类型和路径ID
            print(f"    剩余路径: {
     
     current_route_edges}") # 打印车辆剩余路径
            print(f"    累积等待时间: {
     
     waiting_time:.2f} s") # 打印车辆累积等待时间,保留两位小数
            print(f"    已行驶距离: {
     
     distance_travelled:.2f} m") # 打印车辆已行驶距离,保留两位小数
            print(f"    长度: {
     
     length:.2f} m, 颜色: {
     
     color}") # 打印车辆长度和颜色

        except traci.exceptions.TraCIException as e: # 捕获 TraCI 异常 (例如,车辆可能在上一个时间步离开了路网)
            print(f"获取车辆 {
     
     veh_id} 信息时出错: {
     
     e}") # 打印错误信息
            # 这种情况可能发生在该车辆刚好在这个时间步之前离开了仿真区域
            # 或者ID不再有效
            pass # 继续处理下一辆车
else:
    print("  路网中当前没有车辆。") # 如果路网中没有车辆,打印提示信息

代码解释与知识点:

  • traci.vehicle.getIDList(): 返回一个包含当前仿真中所有车辆 ID 的元组。如果路网中没有车辆,则返回空元组。
  • traci.vehicle.getPosition(vehID): 返回车辆 vehID 的中心点在 SUMO 内部笛卡尔坐标系中的 (x, y) 坐标。这个坐标系的原点和方向取决于路网是如何创建的。
  • traci.vehicle.getAngle(vehID): 返回车辆的朝向角度,单位是度。0 度代表车辆沿 X 轴正方向行驶,角度按逆时针方向增加。
  • traci.vehicle.getRoadID(vehID) 和 traci.vehicle.getLaneID(vehID): 分别返回车辆当前所在的边 (road) 和车道 (lane) 的 ID。
  • traci.vehicle.getLaneIndex(vehID): 对于多车道的边,返回车辆当前所在车道的索引。索引从0开始,通常0是最右侧车道(或根据路网定义)。
  • traci.vehicle.getLanePosition(vehID): 返回车辆在当前车道上的纵向位置,即从车道起点开始计算的行驶距离。
  • traci.vehicle.getRoute(vehID): 返回车辆当前计划要行驶的剩余路径,是一个由边 ID 组成的元组。
  • traci.vehicle.getWaitingTime(vehID): 返回车辆自进入路网以来,因为拥堵或红灯等原因,速度低于某个阈值 (默认为 0.1 m/s) 的总累积时间。这个值对于评估交通拥堵很有用。
  • traci.vehicle.getDistance(vehID): 返回车辆从出发点开始算起的总行驶距离。
  • 异常处理: 当尝试获取一个刚刚离开仿真或由于某种原因不再有效的车辆信息时,TraCI 可能会抛出 traci.exceptions.TraCIException (通常是 FatalTraCIError,子类为 TraCIException,或者特定错误如 veículo 'ID' não é conhecido (vehicle ‘ID’ is not known))。因此,在循环处理多个车辆时,对每个车辆的操作使用 try-except 块是一个好习惯。
2.1.2 修改车辆状态与行为

TraCI 不仅可以获取信息,更强大的是它可以动态修改车辆的状态和行为。

# (假设 traci 已经连接,并且仿真正在运行)
# 在仿真循环的某个时间步内:
vehicle_ids = traci.vehicle.getIDList() # 获取当前路网中所有活动车辆的ID列表

if "flow_east.0" in vehicle_ids: # 假设我们知道一个特定的车辆ID,例如 "flow_east.0"
    veh_id_to_control = "flow_east.0" # 定义要控制的车辆ID

    try:
        # 1. 修改车辆速度 (影响期望速度,实际速度受跟驰模型等限制)
        target_speed = 10.0  # 设置目标速度为 10 m/s (约 36 km/h)
        traci.vehicle.setSpeed(veh_id_to_control, target_speed) # 为车辆 veh_id_to_control 设置新的期望速度
        print(f"  车辆 {
     
     veh_id_to_control}: 已设置期望速度为 {
     
     target_speed} m/s") # 打印设置速度的信息

        # setSpeedMode: 控制车辆如何遵守速度限制和安全距离等规则
        # mode=0: 忽略所有安全检查 (非常危险,车辆可能碰撞)
        # mode=31 (默认): 遵守所有检查 (限速、安全距离、红灯等)
        # traci.vehicle.setSpeedMode(veh_id_to_control, 0) # 例如,让车辆忽略安全检查 (不推荐)
        # traci.vehicle.setSpeedMode(veh_id_to_control, 31) # 恢复默认安全模式

        # 2. 修改车辆目标边 (会触发到该新目标边的路径重规划)
        # 注意:这会改变车辆当前的 "下一个主要目标边",并可能导致其最终目的地改变
        # 如果只是想改变路径中的下一条边,而最终目的地不变,使用 rerouteTraveltime 或 setRoute
        current_route = traci.vehicle.getRoute(veh_id_to_control) # 获取车辆当前路径
        if len(current_route) > 1: # 如果路径中至少还有两条边
            # 假设我们想让它跳过下一条边,直接去下下条边 (如果合法)
            # 这是一个简化的例子,实际应用中需要更复杂的逻辑
            # new_target_edge = current_route[1] # 获取当前路径的第二条边作为新目标
            # traci.vehicle.changeTarget(veh_id_to_control, new_target_edge) # 改变车辆的目标边
            # print(f"  车辆
SUMO(二)——与Python的基本联合 简单的实现SUMO和Python联合运用 阅读详情

相关推荐

【Python】SUMO + Python 联合仿真平台

强化学习的核心思想是让智能体 (Agent) 通过与环境 (Environment) 的交互来学习如何做出最优决策,以最大化累积奖励 (Cumulative Reward)。在交通场景中,智能体可以是交通信号控制器、自动驾驶车辆或交通管理中心,环境则是 SUMO 模拟的交通路网,奖励则与交通效率、安全、环保等目标相关。交通系统本质上是动态的、随机的、高度复杂的系统,传统的基于规则或模型的优化方法在面对不断变化的需求和突发事件时,往往显得力不从心。强化学习的优势在于:在交通领域,强化学习已经被广泛应用于:为了

宅男很神经 1973

自动驾驶变道博弈实战:用Python+SUMO模拟混合交通中的纳什均衡

本文探讨了自动驾驶变道博弈的实战应用,通过Python+SUMO模拟混合交通中的纳什均衡。文章详细介绍了博弈论在自动驾驶变道决策中的应用,包括安全收益、效率收益和舒适度成本的计算,以及如何构建博弈矩阵并求解纳什均衡。通过SUMO-Python联合仿真平台,展示了自动驾驶车辆与人类驾驶车辆的交互决策过程,并提供了优化模型和效果验证的方法。

w3x4y的博客 648

【仿真】CARLA实战避坑指南:从SUMO联调到Docker部署的典型问题解析

本文详细解析了CARLA仿真平台在SUMO联调、Traffic Manager使用和Docker部署中的典型问题及解决方案。从车辆ID映射错误到同步机制配置,再到Docker环境下的日志录制失效和显卡驱动兼容性问题,提供了实战避坑指南,帮助开发者高效搭建自动驾驶仿真环境。

weixin_26782929的博客 381

SUMO与Python联合仿真实战:从环境配置到交通流优化

本文详细介绍了SUMO与Python联合仿真的实战指南,从环境配置到交通流优化。通过SUMO-Python联合仿真平台,用户可以灵活控制交通仿真过程,集成机器学习算法,实现高效的交通网络模拟。文章涵盖了环境配置、交通场景构建、Python控制仿真及进阶优化技巧,适合交通工程和自动驾驶领域的研究人员。

weixin_29051193的博客 449

基于SUMO与Python的微观交通仿真平台搭建与实战

本文详细介绍了如何基于SUMO与Python搭建微观交通仿真平台,并通过Traci库实现深度交互。从环境配置、场景构建到智能控制,提供实战代码与避坑指南,帮助开发者快速掌握联合仿真技术,优化交通信号控制与数据分析。

weixin_30443075的博客 491

从零搭建CARLA-SUMO联合仿真环境:详细配置与避坑指南

本文详细介绍了如何从零搭建CARLA-SUMO联合仿真环境,涵盖Windows和Linux双平台的基础配置、CARLA与SUMO的安装与配置、联合仿真实战步骤以及常见问题解决方案。通过具体命令和调试技巧,帮助开发者快速掌握联合仿真技术,避免常见错误,提升开发效率。

weixin_29094539的博客 595

SUMO与CARLA联合仿真:构建宏观可控、微观可感的车路协同验证体系

交通仿真系统是智能网联汽车研发的核心基础设施,其本质是在数字空间中复现真实道路环境下的车辆交互与动态演化。基于离散事件驱动的SUMO擅长建模百万级交通流、信号配时与路网级拥堵传播,而基于物理引擎的CARLA则提供高保真感知数据、车辆动力学响应与决策执行能力。二者联合并非简单接口对接,而是通过时间步长协商、进程级解耦与数据契约设计,实现宏观交通状态到微观车辆行为的可信映射。该技术已广泛应用于L4自动驾驶算法回归测试、车路协同场景生成及数字孪生交通推演等工程实践,显著提升测试复现性与场景覆盖度。

davy57345的博客 366

交通仿真软件:SUMO_(12).交互仿真

交互仿真在交通管理中发挥着重要作用,通过实时控制、数据收集、路径规划和事件触发等技术,可以实现对交通系统的动态管理和优化。尽管交互仿真面临一些挑战,但通过优化数据传输、提高仿真效率和简化算法等方法,可以有效应对这些挑战。希望本文的内容能帮助读者更好地理解和应用 SUMO 中的交互仿真技术,从而提高交通系统的性能和安全性。

kkchenjj的博客 1094

【SUMO仿真】都需要什么文件 | sumo官网/官方文档查看 | SUMO仿真小案例 | TraCI接口 | Trace接口?

Value Retrieval 属性值查找,可以分为 Lane Value Retrieval----车道的属性、Vehicle Value Retrieval----车辆的属性、Person Value Retrieval----行人的属性、Edge Value Retrieval----道路属性的查找 等。官网的解析是这样:“通过为sumo(或 sumo-gui )提供这样的路线定义,sumo将构建一个类型为“type1”、名为“0”的红色(颜色=1,0,0)车辆,该车辆从时间 0 开始。

追光者♂:记录、分享、总结、提升,现象级专栏《Python从入门到人工智能》作者,无惧黑暗,坚信曙光 1017

投入产出表之直接消耗系数和完全消耗系数计算--基于Excel

介绍 上篇博客已介绍投入产出表的结构类型及意义解读,在此就不重复写了。 投入产出法中相对稳定的因素是一些系数,根据投入产出表可以计算很多系数: 案例 案例数据即流量矩阵,采用的是中国2012年的部分行业数据,若有不对,请批评指正哈。 直接消耗系数的计算 根据上表和直接消耗系数的计算公式,直接消耗系数矩阵通常用A表示。农林牧渔产品和服务部门对其他部门产品的直接消耗系数分别为: 这些消耗系数的经济意义是,农、林、牧、渔产品和服务部门每生产单位(1亿元)总产出,要直接消耗0.1378亿元本部门产品,0.26

m0_46271335的博客 7万+

Windows系统下com0com虚拟串口安装与驱动签名禁用全攻略

本文详细介绍了在Windows系统下安装com0com虚拟串口工具的全过程,包括驱动签名问题的解决方法。通过禁用驱动程序强制签名,用户可以顺利安装并使用这款开源工具创建虚拟串口对,适用于软件开发测试、硬件调试等场景。文章还提供了验证虚拟串口功能的实用技巧和常见问题解决方案。

weixin_28720573的博客 371

MIT Cheetah Learning (一):State Estimate 从理论到实践:EKF融合IMU与编码器的状态估计全解析

本文深入解析了MIT Cheetah机器人中基于Extended Kalman Filter的状态估计技术,详细探讨了如何融合IMU与编码器数据以实现精准的State Estimate。通过四元数建模和传感器误差分析,揭示了EKF在复杂地形中的工程实践价值,为四足机器人的实时状态估计提供了完整解决方案。

weixin_30292843的博客 302

1998年高教社杯全国大学生数学建模竞赛 B 题:《灾情巡视路线》真题解析与 MATLAB 解决方案

🏆 本文已收录于专栏:《滚雪球学数学建模(含历年真题)》,持续更新最新及历年数学建模竞赛资料,每篇内容力求做到“问题讲透、模型讲清、代码可用、论文可参考”,帮助你从零开始逐步建立完整的数学建模知识体系。

**My Coding Family** 874

BME688传感器原理图设计,已量产(环境传感器)

在便携式空气质量监测、可穿戴健康设备这类低功耗场景中,BME688 这类集成了 VOC、温湿度、气压的四合一 MEMS 传感器越来越受欢迎 —— 它体积小、功耗低,刚好匹配便携设备的需求。但这类小信号传感器对电源噪声、电平匹配的要求很细,今天结合实际项目里的一款 BME688 接口电路,聊聊硬件设计的关键细节。

不脱发的程序猿 504

深入解析雪球网股票行情数据爬取:基于WebSocket与API的实时数据获取技术实践

本文详细介绍了如何使用Python爬虫技术获取雪球网的股票行情数据,涵盖了WebSocket实时数据连接和RESTful API接口调用两种主要技术方案。完整的WebSocket客户端,支持实时行情订阅和数据处理功能丰富的API客户端,支持多种数据类型的获取数据存储和管理系统,使用SQLite进行数据持久化实时监控和预警系统,支持自定义预警规则技术指标计算,提供基本的数据分析功能。

2201_76125261的博客 1251

【AI黑话日日新】什么是算力调度?

算力调度 = 在有限资源下,把任务智能分配到最合适的计算节点,实现效率、成本、稳定性最优。更完整的定义:算力调度是对 CPU、GPU、内存、网络、存储等异构资源进行统一抽象、感知、决策、分配、监控与自愈的全生命周期管理,是分布式系统的“智能管家”。算力调度不是玄学,它是资源管理 + 算法 + 工程实践的结合。先懂概念 → 再玩工具 → 再写策略 → 再优化算法从单机到集群,从CPU到GPU,从离线到在线,循序渐进未来AI与算力网络时代,算力调度是性价比最高、最稀缺、最长期吃香的技术方向之一。

专注:太空算力、AI infra、异构调度、大模型成本工程 260

vivo X200 Ultra前瞻系列(1):重塑影像旗舰标准

在色彩风格化、视频专业度上实现了安卓阵营的突破性进步。其2亿像素长焦、双LYT-818传感器及5轴防抖技术,对追求个性化创作的用户具有强吸引力。若真机表现能兑现硬件承诺,X200 Ultra有望重新定义手机影像的“专业”标准。然而,其厚重设计、价格门槛及生态适配问题,仍需市场检验。vivo X200 Ultra作为2025年上半年影像旗舰,以“全焦段夜视仪+电影级视频”为核心卖点,主打。,对标小米15 Ultra、OPPO Find X8 Ultra等竞品。,顶配版(24GB+2TB)逼近万元。

专注:太空算力、AI infra、异构调度、大模型成本工程 959

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

上一篇: 【Python】wave库的使用
下一篇: 【Python】SUMO + Python 联合仿真平台
宅男很神经
博客等级 码龄5年 7949粉丝 1249原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宅男很神经

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值