引言#
自 1.5.0 版本起,SUMO 通过出租车设备支持需求响应式交通(DRT)的模拟。这使得一队出租车能够基于可配置的调度算法来响应乘客请求。
Note
由于出租车功能仍在开发中,其状态可以通过 Issue #6418 进行检查。
装备车辆#
可以通过为车辆装备 Taxi 设备,使其成为出租车队列的一部分。要将 Taxi 设备附加到车辆,可以应用标准设备装备程序,使用 <device name>=taxi。
例如,可以将单个车辆配置为出租车,如下列最小示例所示:
<vehicle id="v0" route="route0" depart="0" line="taxi">
<param key="has.taxi.device" value="true"/>
</vehicle>
下表列出了出租车设备的所有可能参数(所有参数名称必须以 "device.taxi" 为前缀):
| 参数 (Parameter) | 类型 (Type) | 范围 (Range) | 默认值 (Default) | 描述 (Description) |
|---|---|---|---|---|
| dispatch-algorithm | enum | {greedy; greedyClosest; greedyShared; routeExtension; traci} | greedy | 调度算法 |
| dispatch-algorithm.output | string | - | 将调度算法的信息写入文件 | |
| dispatch-algorithm.params | string | - | 为 greedyShared 调度算法提供 absLossThreshold 和 relLossThreshold 阈值,格式为 KEY1:VALUE1[,KEY2:VALUE] | |
| dispatch-period | float (s) | 60 | 连续调用调度器之间的间隔时间 | |
| idle-algorithm | enum | {stop; randomCircling; taxistand} | stop | 空闲出租车的行为 |
| idle-algorithm.output | string | - | 将空闲算法的信息写入文件 |
出租车请求#
直接叫车#
可以通过以下定义将人员定义为出租车乘客:
<person id="p0" depart="0.00">
<ride from="B2C2" to="A0B0" lines="taxi"/>
</person>
多模式路径规划#
人员也可以通过在 personTrip 模式中包含出租车来使用出租车:
<person id="p0" depart="0.00">
<personTrip from="B2C2" to="A0B0" modes="taxi"/>
</person>
当人员在多模式路径搜索期间进入出租车时,会应用时间惩罚以解释等待出租车和登机的预期时间损失。默认值为 300 秒,可以通过选项 --persontrip.taxi.waiting-time 进行配置。这可以防止在旅行模式之间快速切换。
人员组#
多人可以作为一个组一起出行,使用属性 group(前提是出租车有足够的容量):
<person id="p0" depart="0.00">
<ride from="B2C2" to="A0B0" lines="taxi" group="g0"/>
</person>
<person id="p1" depart="0.00">
<ride from="B2C2" to="A0B0" lines="taxi" group="g0"/>
</person>
预订#
直接叫车可视为自发预订。如果要在人员到达上车地点之前请求出租车,则需要进行预订。这可以通过在 ride 中指定 earliestPickupTime 和 reservationTime 来实现。reservationTime 指定创建预订并将其提供给调度的时间,它也可以小于人员的出发时间。earliestPickupTime 指定人员准备好上车的时间。根据所使用的算法,这可能允许进行更好的调度。特别是结合通过 TraCi 的自定义调度算法,可以满足不同用例的需求。
应用:#
- 包含人员的文件必须作为附加文件加载。
- 必须至少指定
earliestPickupTime。reservationTime的默认值为模拟的开始时间。 - 必须在
ride中定义from属性。 - 如果之前有定义了
arrivalPos属性的walk,则ride中的fromPos属性也必须定义为相同的值。
示例:#
<person id="p0" depart="100.00">
<ride from="B2C2" to="A0B0" lines="taxi">
<param key="earliestPickupTime" value="100.00"/>
<param key="reservationTime" value="50.00"/>
</ride>
</person>
<person id="p1" depart="0.00">
<walk from="B2C2" to="B1C1"/>
<ride from="B1C1" to="A1B1" lines="taxi">
<param key="earliestPickupTime" value="30.00"/>
</ride>
</person>
行为:#
- 如果人员早于
earliestPickupTime到达出发点,则人员在出租车到达后立即登车。 - 如果出租车准时到达,但人员在
earliestPickupTime之后 3 分钟内未到达出发点,则预订被取消,出租车为下一个预订服务。 - 如果出租车晚点到达,且人员在出租车到达时间之后 3 分钟内未到达出发点,则预订被取消。
- 如果使用 greedy 或 greedyShared 调度算法,则预订按
earliestPickupTime的顺序处理。自发预订和预订的组合可能会导致副作用。建议通过 TraCi 使用自定义调度算法。
注意:#
- 尚不支持合并预订和团体预订的预订。
- 当前状态可在 Ticket 11429 中跟踪。
多个出租车队列#
默认情况下,只有一个使用线路属性 'taxi' 的出租车队列,出租车乘客使用属性 lines="taxi" 进行乘车。
允许使用前缀 'taxi:' 和任意后缀(例如 "taxi:fleetA")定义出租车的线路属性。
同样,允许使用前缀 'taxi:' 和后缀定义乘车的 lines 属性。
完成此操作后,将出租车分配给乘客时应用以下规则:
- 线路为 'taxi:X' 的出租车只能接送具有匹配乘车属性 lines="taxi:X" 的乘客(对于任何 X 值)
- 具有 lines="taxi" 的乘客可以使用任何出租车,无论其队列后缀如何
- 线路为 'taxi' 的出租车可以接送任何乘客,无论其乘车队列后缀如何
调度算法#
调度算法将出租车分配给等待的乘客。算法使用选项 --device.taxi.dispatch-algorithm ALGONAME 进行选择。提供以下算法:
- greedy:按预订顺序将出租车分配给乘客。分配距离最近(以行驶时间计)的出租车。如果预订日期太远,则推迟该乘客。
- greedyClosest:对于每辆可用的出租车,分配距离最近(以行驶时间计)的乘客。如果预订日期太远,则推迟该乘客。
- greedyShared:类似于 'greedy',但在将第一位乘客送达目的地的同时尝试接载另一位乘客。可以使用 --device.taxi.dispatch-algorithm.params KEY1:VALUE1[,KEY2:VALUE] 提供参数 absLossThreshold 和 relLossThreshold 以配置可接受的绕行。
- routeExtension:类似于 greedy,但可以沿路线接载任何乘客,并在 personCapacity 限制内扩展原始路线。
- traci:调度委托给 traci 控制。该算法仅跟踪待处理的预订。
Note
欢迎用户贡献的调度算法。
调度算法运行的周期可以通过选项 --device.taxi.dispatch-period 控制。默认为 60 秒。
算法输出#
可以设置选项 --device.taxi.dispatch-algorithm.output FILE 以接收来自算法的额外输出(例如用于拼车指标)。
出租车行为#
默认情况下,出租车将保留在模拟中,直到所有人员离开。要使它们在更早的时间离开模拟,可以使用其 vType 或 vehicle 定义中的通用参数定义结束时间:
<vType id="taxi" vClass="taxi">
<param key="has.taxi.device" value="true"/>
<param key="device.taxi.end" value="3600"/>
</vType>
空闲行为#
默认情况下,车辆在到达其最终边的末端后将离开模拟。为避免这种情况,出租车具有可配置的空闲行为,使用选项 --device.taxi.idle-algorithm:
- "stop"(默认):在运送完当前服务请求的最后一位乘客后,停在当前位置(路外)。
- "randomCircling":继续行驶到随机的边,直到收到下一个请求。(注意:如果网络中有死胡同,出租车可能会被困住)
- "taxistand":行驶到出租车候客处并在那里等待下一位乘客/调度。定义出租车候客处集合以及从中选择的策略如下所述。
Note
使用 "randomCircling" 时,参数 "device.taxi.end" 的默认值为车辆出发后 8 小时。
定义出租车候客处#
当使用空闲算法 taxistand 时,必须提供以下输入:
- 每个出租车候客处必须定义为一个 parkingArea
- 可用于特定出租车或出租车队列的 parkingArea 列表必须根据停车搜索模拟的描述 定义为
<rerouter>元素。 - 出租车必须定义参数
device.taxi.stands-rerouter,作为<vehicle>或其<vType>的子元素,并声明 rerouter id。
默认情况下,空闲出租车将从替代列表中选择第一个 parkingArea(<rerouter> 中的 <parkingAreaReroute 条目)。
如果在车辆或 vType 中设置了通用参数 <param key="parking.ignoreDest" value="1"/>,则根据重定向策略使用“最佳”候客处(即距离当前车辆位置最近的候客处)。
选择替代出租车候客处的策略遵循停车搜索模拟的描述(即关于剩余容量的先验知识)。
rerouter 和引用它的出租车 vType 的示例声明:
<rerouter id="rr0" edges="B0C0 E2D2" vTypes="taxi">
<interval begin="0" end="1:0:0:0">
<parkingAreaReroute id="pa_0"/>
<parkingAreaReroute id="pa_1"/>
</interval>
</rerouter>
Note
为避免警告,rerouter 的 edges 属性应与包含出租车候客处的边匹配。
<vType id="taxi" vClass="taxi">
<param key="has.taxi.device" value="true"/>
<param key="device.taxi.stands-rerouter" value="rr0"/>
</vType>
乘客停靠点#
出租车将停靠以上下车。停靠点的 'actType' 属性指示目的('pickup' / 'dropOff')以及乘客的 id 和他们的预订 id。可以使用出租车的 <vType> 或 <vehicle> 定义中的通用参数配置停靠点属性:
<vType id="taxi" vClass="taxi">
<param key="has.taxi.device" value="true"/>
<param key="device.taxi.pickUpDuration" value="0"/>
<param key="device.taxi.dropOffDuration" value="60"/>
<param key="device.taxi.parking" value="false"/>
</vType>
- 上车停靠点的持续时间可以使用 vType/vehicle 参数 "device.taxi.pickupDuration" 配置(默认 "0")
- 下车停靠点的持续时间可以使用 vType/vehicle 参数 "device.taxi.dropOffDuration" 配置(默认 "60")
默认情况下,车辆停靠点具有属性 parking="true",这意味着出租车不会阻塞行车道。可以通过将参数 "device.taxi.parking" 设置为 "false" 来更改此行为。
TraCI#
为了将外部调度算法耦合到 SUMO,提供了以下 TraCI 函数:
Note
要使用这些函数,必须设置选项 --device.taxi.dispatch-algorithm traci
- traci.person.getTaxiReservations(reservationState)
- traci.vehicle.getTaxiFleet(taxiState)
- traci.vehicle.dispatchTaxi(vehID, reservations)
这组 API 调用可用于简化编写自定义调度算法,通过让 sumo: - 管理现有预订 - 管理出租车队列 - 通过提供预订 ID 列表来调度出租车以服务一个或多个预订(然后车辆路由和停靠是自动的)。
getTaxiReservations#
返回具有以下属性的 Reservation 对象列表
- id
- persons
- group
- state
- fromEdge
- toEdge
- arrivalPos
- departPos
- depart
- reservationTime
- state(正值,见下文)
调用 traci.person.getTaxiReservations(reservationState) 时,支持以下 reservationState 参数:
- 0:返回所有预订,无论状态如何
- 1:仅返回新预订
- 2:返回已检索的预订
- 4:返回已分配给出租车的预订
- 8:返回已被接载的预订
也支持这些值的组合。例如,发送值 3 (= 1 + 2) 将返回状态为 1 和 2 的所有预订。
getTaxiFleet#
出租车可以处于以下任何状态:
- 0 (空闲):出租车空闲
- 1 (接客途中):出租车正在前往接乘客的路上
- 2 (已载客):出租车上有乘客,正在前往下车点
- 3 (接客途中 + 已载客):出租车上有乘客,但将接载更多乘客
调用 traci.vehicle.getTaxiFleet(taxiState) 时,支持以下 taxiState 参数:
- -1:(返回所有出租车,无论状态如何)
- 0:仅返回空闲出租车
- 1:返回状态为 1 和 3 的出租车
- 2:返回状态为 2 和 3 的出租车
- 3:返回状态为 3 的出租车
dispatchTaxi#
如果出租车是空闲的,则支持以下调度调用:
- dispatchTaxi(vehID, [reservationID]):接载并送走属于给定预订 ID 的人员
- 如果给出多个预订 ID,则每个单独的预订 ID 必须在列表中恰好出现两次,以便完整地上下车。ID 的第一次出现表示上车,第二次出现表示下车。
示例 1:dispatchTaxi(vehID, [a]) 表示:接载并送走 a。 示例 2:dispatchTaxi(vehID, [a, a, b, c, b, c]) 表示:接载并送走 a,然后接载 b 和 c,然后送走 b 和 c。
如果出租车不处于空闲状态,则支持以下重新调度调用:
- 新预订与之前的预订没有重叠:将新预订附加到之前的预订
- 新预订包含所有之前的唯一预订 ID 恰好两次:重置当前路线和停靠点,并视为完整的新调度。如果之前预订的某个人员已被接载,则忽略预订列表中该预订的第一次出现
- 新预订包含所有之前的唯一预订 ID 一次或两次,所有提及一次的客户都已被接载:重置当前路线和停靠点,使用单次出现的 ID 作为下车点
控制空闲出租车#
默认的空闲算法("stop")旨在确保出租车不会退出模拟(如果它曾经驶过其路线的最终边,就会发生这种情况)。为此,它使出租车停在当前位置或使用出租车路线上的现有停靠点,并将其设置为 triggered="person"(这实际上使出租车无限期等待,直到下一次调度)。
为了将空闲出租车发送到另一个目的地,需要按顺序执行以下操作:
- 出租车需要扩展其路线(例如使用
vehicle.changeTarget或vehicle.setRoute) - 必须使用
vehicle.resume中止当前停靠点 - 必须添加新的停靠点(例如使用
vehicle.setStop)。
输出#
出租车设备在 tripinfo 输出文件中生成以下形式的输出:
<tripinfo id="trip_0" ... >
<taxi customers="5" occupiedDistance="6748.77" occupiedTime="595.00"/>
</tripinfo>
参数检索#
可以通过 traci.vehicle.getParameter 检索以下参数,并通过 --fcd-output.params 写入。
也可以通过在 SUMO-GUI 'by param (numerical)' 中设置这些键来为车辆着色。
- device.taxi.state:返回整数值(参见 #gettaxifleet)
- device.taxi.customers:服务的乘客总数
- device.taxi.occupiedDistance:载客行驶的总距离(米)
- device.taxi.occupiedTime:载客行驶的总时间(秒)
- device.taxi.currentCustomers:以空格分隔的待接载或已在车上的人员列表
