本教程展示了铁路仿真、GTFS、检测器计数导入、电动汽车、自动充电等内容!
文件#
视频#
文字记录#
简介#
欢迎来到今年的 SUMO 教程。我很高兴每年都能做这个教程,希望我为大家带来了一些有趣的内容。我已经无意中听到观众中有些人对铁路仿真感兴趣,我很确定你会找到符合你口味的内容。 当然,你不需要在电脑上跟着这个教程操作。恐怕我会讲得有点快,但所有的操作——每一次点击、每一次按键——都会被录下来。所以如果你真的想跟进这些内容,你完全可以在稍后进行。 那么,今天的话题。当然,我们将从基于 OSM Web Wizard 工具通过几次点击构建场景开始。然后我们将深入探讨铁路仿真的主题。我们将构建一个柏林市轻轨运输的小型仿真模型。我们将让列车准点运行,根据——嗯,或多或少根据当前官方的时刻表,正如你可能听说的那样,柏林现在发生了一起重大事故——一座大桥倒塌,引发了各种麻烦。 这不仅仅是列车在轨道上行驶——我们还将有乘客乘坐这些列车并换乘。这将是教程的第一部分。在第二部分中,我们再次从 Web Wizard 开始。这次我们将为道路运输构建另一个场景。同样,我们将用真实世界的数据填充它,而不是像通常那样从随机交通开始,因为没有好的数据源。这次我们将查看柏林市发布的数据:本地化的计数数据,我将向你展示如何导入所有这些检测器及其附带的计数数据。 在最后一部分中,我们将把整个场景切换到电动驾驶,看看情况如何,看看电池在哪里电量不足,然后学习如何在仿真中通过自动充电来解决这个问题。所以,请享受。
osmWebWizard#
OSM Web Wizard 工具伴随 SUMO 已有多年。你可以在 SUMO 的 tools 目录中找到它。嗯,无论如何,如果你查看 SUMO 安装目录下的 tools 目录,无论它在哪里,都有一个大的 tools 文件夹,你可以在这里找到它。它叫做 osmWebWizard。当然,如果你使用了安装程序,它就在你的开始菜单里。当你启动它时,它会显示一个类似这样的屏幕,你可以为你构建场景的地图选择一个区域。
在这个例子中,我们使用的是柏林轻轨系统的内环。这里有一个部分,你可以——实际上是在这里——选择你想要导入的道路类型。嗯,实际上,让我们现场演示一下,这样你就知道我在说什么。好吧,显然网络连接不太顺畅,但这没关系。
这就是我想给你看的屏幕。在这个部分,你可以选择要导入的道路类型。请注意,OpenStreetMap 对通过 API 一次可以下载的数据量有限制。所以如果你想要一个更大的场景,取消选择你不需要的内容会很有帮助。所以在这种情况下,我们会取消选择所有内容,只保留轻轨,就在这里,等等。然后当我们这样做时,回顾一下幻灯片,我们可以取消选择一些其他内容。我们不需要任何随机交通,但我们确实想要勾选这个“导入公共交通”复选框。
我们也不需要任何背景多边形、卫星背景,这些都不需要。只需要带有少量公共交通的基本轨道网络。当我们运行这个时,我们会得到一个看起来像这样的场景。
铁路仿真#
所以我们有列车在网络中运行,并做它们的事情。现在这真的很快。我只想指出,我们这里有一些有趣的效果。我们有一些瞬移现象。如果我们查看警告消息,它会说“等待时间过长,已瞬移”,然后可能跳过了一些站点。 那么这里发生了什么?让我们看看这个场景做了什么。这是一个铁路场景。如你所知,有一些非常特殊的规则指导着铁路系统的安全。世界上最常见的情况是闭塞信号系统。因此,网络,即铁路网络,被划分为闭塞区间,由铁路信号分隔,在任何时间一个闭塞区间内只能有一列列车。如果一切顺利,这几乎可以避免碰撞。并且有很多功能确保列车不会闯红灯。所以基本上,铁路运输非常、非常安全。现在,我们刚刚从 OpenStreetMap 的公开数据构建的铁路网络,确实包含一些铁路信号。我通过稍微放大它们来突出显示这些。所以你可以看到所有这些小小的绿线和红线。如果你仔细观察,你可能会注意到这些彩色线条的密度在整个网络中并不均匀。如果你看下面这里,与那边相比,有更大的间隙。 那么,为什么会这样呢?这是公共数据,由志愿者管理,他们输入了已知的轨道位置,并输入了信号,而这些人并不完美。它不是来自德国铁路当局的官方数据。这意味着其中一些信号闭塞区间比现实中的要长。 这是一个建模问题。如果闭塞区间很长,就意味着铁路系统的效率不高,因为列车必须在空间上保持更大的间隔。它们占据更多的空间。它们占据更大的闭塞区间。这当然会对准点率产生负面影响。然后列车就会等待得有点久。根据 SUMO 的默认规则,五分钟后,它们会被瞬移走,这对于铁路仿真来说并不理想,但这只是默认设置。 那么我们要做什么呢?我们将在构建网络时设置一个选项,叫做 --railway.signal.guess.by-stops,它的意思是在每个公共交通站点,我们至少在入口和出口各放置一个铁路信号。 这是一种启发式方法,用于解决我们 OpenStreetMap 数据的局限性。还有另一个警告,我不想详细说明,即“闭塞区间超过最大长度”的警告。这些实际上不是信号闭塞区间,它们是更大的区段,用于检查铁路系统中的死锁并防止死锁。在柏林轻轨系统中,这是一个大圆环,在圆形结构中检查这些死锁有点困难。 但现在这与我们无关,我们只需用这个选项重新构建网络,然后我们就会有一个更好的网络。 所以我准备了另一个配置文件,然后当然,当我们这样做时,我们必须重新构建交通,因为每当有铁路信号时我们都会分割网络,然后列车行驶的所有边的序列就不再完全正确了。但 Web Wizard 场景附带了重新构建所有组件所需的所有工具。所以在重新构建网络之后,我们只需重新构建公共交通交通,现在我们可以再次运行它,这次就不会有任何警告了,列车会运行。 让我们更详细地看看这个,让我们放慢运行速度。所以我们重新加载仿真,如你所见,按 Ctrl + R。 首先,我们给列车上色,我们根据一个与公共交通相关的指标来上色,那就是延误。所以每列列车都有一个它正在接近的站点,通常如此,除非它在一天结束时退出服务,该站点有一些相关的计划时间,计划的到达时间和计划的出发时间。 这里我们只是根据列车相对于时刻表的延误来给它们上色。所以蓝色是好的,蓝色意味着没有延误,随着网络变得拥挤,我们确实有一些延误的列车。实际上,如果我们看得更近一点,我们可以找到一些列车聚集的现象,这在现实中是众所周知的,即几辆车在轨道上紧跟着行驶。实际上,我们会在下一个案例中看到这个,因为现在的交通量还不够。 我们在这里看到了什么?我们看到了来自 OpenStreetMap 的非常粗糙的数据。它只是关于线路运行位置的信息,如果我们幸运的话,还有列车间隔时间,但实际上并没有与之相关的详细时刻表。所以让我们进入下一步。让我们进入获取真实世界数据的部分。
柏林 S-Bahn 仿真 - 时刻表#
确实有这样的数据源。数据格式叫做 GTFS,即通用交通数据规范,最初由谷歌引入,当时叫做谷歌交通数据规范,但现在几乎每个公共交通系统都试图以这种格式提供数据。我们有工具可以导入这种 GTFS 数据。在这个案例中,它是由柏林当局 VBB(Verkehrsverbund Berlin-Brandenburg)提供的。整个城市的文件相当大(我不想把它包含在教程中),所以我实际上把它修剪了一下,只包含轻轨交通。如果你想看看如何修剪 GTFS 数据,我甚至把修剪工具放进了教程里。所以你可以看看这个脚本,了解它是如何工作的。
然后我们有了一个更小的 GTFS 数据文件。我们有一个工具可以导入它。它叫做 gtfs2pt.py,pt 代表公共交通(Public Transport)。这是文件,GTFS 文件。它总是一个 zip 包。我们必须选择一个日期。因为,嗯,我们不想模拟一整年。我们只想模拟一个较小的数据集。我们提供网络和站点。现在这很有趣。如果你有这样的 GTFS 数据,它必须以某种方式映射到你的网络上,通常你只有站点非常粗略的经纬度坐标。但站点由很多东西组成。它包括不同的站台。有时这些站台在不同的层。所以它肯定不止一个坐标,但 GTFS 给你的只是一个坐标。所以这涉及到一些映射工作,实际上有多种解决方案可以做到这一点。但一个解决方案是提供已知的公共交通站点,我们可以从 OpenStreetMap 读取这些站点。
所以我们刚刚从 OSM 导入的东西仍然有用。我们把这些站点传递给导入器,然后它就能更容易地找到这些列车实际应该停靠的位置。所以在铁路仿真中,另一件非常重要的事情是知道轨道的使用方向。当然,一条轨道可以在两个方向使用,但通常不是这样使用的。当我们不谈论中断时,每条轨道都有一个首选方向。这在我们的数据库中通常是未知的。它可以在 OSM 中给出,但通常缺失。所以知道这些列车在哪些轨道上停靠(从 OSM 数据中)真的很有帮助。它暗示了首选的运动方向。所以当我们这样做时,我们就得到了一个仿真。
所以我们现在实际上在下一个文件夹里,这里有一个导入脚本,你可以用它来导入公共交通网络。所以我们来做这个。所以我们这里有一个仿真。它基本上运行这个脚本 gtfs2pt,需要几秒钟。然后我们就会有我们的仿真。
随时都可能完成。所以你看,这是实时的。不是脚本化的。也许我应该把它脚本化以节省几秒钟。但现在完成了。我们有了我们的场景。所以让我们再看看这个。现在我们有了更多的交通量,也有了更多的问题。很多问题。我告诉过你颜色代表延误,所有这些列车都是樱桃红色的。所以这里肯定有什么地方不对劲。现在那是什么?这当然是一个教学机会。所以公共交通当局的数据包括了网络中所有已知的中断,比如一座无法使用的大桥。在这种情况下,列车服务与正常运行不同。列车在双轨线路的错误一侧运行,所以它们去了通常不去的地方。而在 OpenStreetMap 数据中,我们通常没有短期中断。所以当我们决定哪个坐标对应于哪条轨道,以及列车必须在哪个特定站点停靠时,我们使用的这两个数据源之间存在不匹配。我们能做什么?最简单的事情是将网络切换到一种模式,我们说每条轨道,真的是每条轨道,都应该可以在两个方向使用。
有时不这样做会有所帮助,因为约束解集有助于避免糟糕的解决方案。但在这个案例中,我们知道有中断,所以我们必须重建网络,并说我们不知道列车要去哪里,比如,确保每条轨道都可以在两个方向使用。
所以这是我们可以给 netconvert 的一个选项,用来再次构建网络。我们做的另一件事是,嗯,你可以说是作弊,当我们加载这些站点时,说这些是列车可以停靠的潜在位置,我们实际上添加了一个包含两个额外站点的新文件,这些站点不在 OpenStreetMap 数据中,我们说,嗯,如果你去这个位置,实际上这是你可以停靠的轨道。所以这非常精细,但这也是一个特殊情况,因为我们正在处理中断。
所以当我们这样做时,我们回去再次运行 netconvert,使用额外的选项,然后实际上我们必须再次构建。这就是需要几秒钟的事情。所以,趁我们还在等,趁我们等导入器运行的时候,让我们看看铁路系统的一些细节。我们看到,你能用仿真做什么?你当然可以看到列车在哪里停在信号灯前,但你也可以与这些信号灯互动。在彩色栏上右键单击,你会看到一些信息。你看到,实际上,这里是有列车在阻塞通行。所以这列列车必须等待,因为另一列列车在错误的位置或在阻塞位置。在 SUMO 中有一个概念叫做 driveways(德语是 Fahrweg),它实际上是轨道的一个相关部分,是铁路运营的一个相关单元。仿真有一些输出,告诉你这些 driveways 是什么,以及它们彼此之间如何冲突。所有这些结构都是在运行时自动生成的。所以网络被分析,所有通常铁路工程师会做的事情——他们会浪费大量的纸张——充分利用大量的纸张——并弄清楚如何将网络划分为这些区段以及如何避免列车相互碰撞。所有这些都发生在 sumo 内部的运行时,以弄清楚这些铁路信号如何相互依赖以及依赖于系统中列车的位置。
所以回到之前,这部分已经结束了。现在我们可以再次运行我们的铁路仿真。这次它应该运行得很顺利。所以如你所见,没有大的聚集。我们有一些列车彼此紧跟的路段,所以仅仅因为一列列车停在车站,就迫使其他列车在后面等待。
对于公共汽车,这被称为公交车聚集(bus bunching)。我猜这叫做列车聚集(train bunching)。所以这就是我们的柏林轻轨系统。它的一部分,那里被切断了。它实际上要大一些。现在我们要把乘客放进去。我们该怎么做?哦,对了,这里有个小插曲。在铁路系统中,特别是在线路终点,有时列车会使用不同的轨道,因为之后它们会改变方向。所以从 OpenStreetMap 的数据来看,并不总是完全清楚任何一条轨道上的首选行驶方向。因为有些站点只是有时使用,但它们都作为候选存在。所以在这个仿真中可能仍然存在问题,有时列车会在双轨线路的错误一侧运行。当然,这对列车运行的效率有影响。所以这仍然是需要关注的地方,看看是否一切正常运行。但 SUMO 确实提供了工具来指出发生不合理行为的地方。例如,如果列车改变方向(有时是必要的,在线路终点你必须掉头),但你可以在网络中找到所有发生这种情况的位置,方向转换。如果它们不是终点站,那么你可能就知道你的网络模型有问题。
柏林 S-Bahn 仿真 - 乘客#
好了,现在说说乘客。嗯,他们必须,他们应该从一个公共交通站点去到另一个站点。我们没有任何关于这方面的数据,所以我们随机化了乘客需求。但有时当你从城市的一端到另一端时,你无法乘坐直达列车。你必须换乘。这也意味着你有时需要换站台。所以我们的纯铁路网络需要扩展一些,比如说,行人边(pedestrian edges),行人道路,帮助乘客从一个站台换到另一个站台。我们有一个小工具来做这个,它叫做 generateStationEdges.py,基本上,它在每个火车站放置一个边,然后用这个边连接所有站台,就像轨道下面的隧道,行人隧道,类似的东西。
所以我们有这个工具,在这个小小的构建脚本中,我们用这些额外的边修补网络。实际上,我给你看看这个工具,因为它不太复杂。我们现在在下一个文件夹里。这就是这个工具的样子。它生成一些新的边。我不确定你是否能看清。这是 Python 工具。然后它再次调用 netconvert,将这些额外的边合并到网络中。然后我们随机化乘客需求。就这样了。最后,这不是仿真的一部分,这只是分析。我们可以看到乘客实际做了哪些列车乘坐和换乘的序列。所以让我们运行这个。
这只是为了信息。所以这里是生成的乘客行程的静态分析。他们做诸如停止、乘坐公共交通、然后步行的事情。或者他们步行、乘坐公共交通、步行、再次乘坐、再次乘坐,这次是从同一个站台,然后再步行一些。所以所有这些可能的行程类型,还有一些小统计来看它们发生的频率。也许你想知道如何获得这些随机的乘客行程。所以让我们看看随机行程的配置文件。
我们限制时间。所以我们让他们在 7 点到 8 点之间乘车。我们必须定义网络。当然,还有他们可以出发的公共交通站点。为了进行多式联运路径规划,公共交通路径规划,在哪里换乘列车,我们还需要定义列车时刻表,这些仍然取自教程的前一步,GTFS 部分。所以你可以在这里看到这些路径。然后我们将生成就绪的路线(routes)。这些是个人行程(person trips)。trips 这个词实际上是为了激活多式联运路径规划。Trips 意味着有人必须弄清楚如何从 A 到 B。我们在站点开始我们的行程,也在站点结束它们。然后所有的行程都允许使用公共交通。它们也可以使用汽车或只步行。但在我们的轻轨仿真中,显然它们都必须使用公共交通。然后还有这个看似无害的小选项 validate value="true",它确保我们丢弃不可行的行程。
所以如果网络中有任何部分没有相互连接,那么我们就会提前过滤掉这些。实际上,一个小剧透,现在这个选项甚至做得更多。它会弄清楚如何用正确数量的缺失行程来替换所有这些无效行程。所以你现在总是可以从随机行程中得到可预测数量的行程,这是多年来一直被要求的功能。所以让我们看看这个仿真。所以如前所述,它们在 7 点开始。我们让列车提前一点启动,以便它们已经在网络中运行。这里你可以看到所有这些彩色的圆圈,每个圆圈代表一个行人,一个人,不是行人。这里是颜色的含义。蓝色意味着他们正在乘坐列车。黄色意味着他们正在等车。这里,这个浅蓝色意味着他们正在评估一个公交车站。所以他们正在车站的站台之间换乘。然后,嗯,你看到他们在铁路系统中穿梭。
他们每个人都到达了目的地。让我们再看一遍,看看列车和他们一起。所以我们也把列车放大很多。但当然,我们也可以放大并看到他们乘坐列车。也许追踪一列列车。我按住 Shift 键单击了列车来追踪它。在某个时刻,我们可以看到其他乘客上车,第一批乘客下车。当然,列车必须先遵守信号灯。现在他们离开了。我想这可能是 Ostkreuz 车站,他们在那里需要上一层楼。换乘到另一条铁路线。所以这就是铁路仿真,主要基于公共数据,只加入了一点随机性来产生乘客需求。这结束了我们教程的第一部分。嗯,现在有一些关于其局限性的结束语。所以如果你试图构建的不是柏林内环的场景,而是整个柏林市的场景,你会注意到在城市的外围,一些轻轨线路实际上在大型铁路系统的轨道上运行。所以它们是共享轨道。所以如果你只用轻轨构建你的网络,你的网络中会有一些缺口。这不好。而且,如果你获取所有标记为轻轨的东西,那么你会得到太多东西。你会得到所有的维修段和一些工业支线。所以你会有很多轨道在你的公共交通仿真中永远不会被使用。它们可能看起来不错,所有这些带有转盘的维修段,但它们大多数时候并不是特别有用。所以你实际上可以做的是,你可以从 OpenStreetMap 导入一些轨道的使用分类,比如干线或维修段之类的东西。你在导入数据时通过加载另一种类型映射来实现。它叫做 osmNetconvertRailUsage.typ.xml,然后网络中的所有轨道都会有这个额外的标记,说明它们的用途。然后你就可以做更智能的过滤。然后基本上你过滤掉所有轻轨可能行驶的地方。如果某些公共交通路线经过那里,它甚至可能在干线上行驶。
所以如果你对此感兴趣,我们有一个公开的场景。它在这个地址在线,就是 sumo-berlin 场景。它有这个特定的选项来为整个城市构建 S-Bahn,即轻轨场景。所以如果你对此感兴趣,你可以看看这个以获取更多细节。现在这真的结束了教程的第一部分。任何问题,请记下来。我们稍后会有“有问必答”环节。
柏林,计数数据导入#
现在轮到那些对火车不特别感兴趣,想看到一些道路和路上汽车的人了。同样,我们从 Web Wizard 开始。这次我们勾选另一个复选框,叫做“仅汽车网络”,所以我们这次只使用主要道路类型来导入。我们得到了柏林的一个相当大的区域。这是我们在的 Treptow-Köpenick 区。我们大约在这里,如果你想知道的话。
嗯,我们不打算激活任何随机交通。我已经把它划掉了。因为这次我们想用不同的方式做事。我们想使用真实世界的计数数据。我们从哪里得到这个?嗯,这不像 GTFS 那样好,全世界都同意一种数据格式。这或多或少是每个城市自己做。
但是,嗯,如果你称之为标准,通常你会得到逗号分隔值(CSV)文件。所以在这种情况下,柏林市的一个公共数据门户给了我们以下两个文件。它给了我们一个包含城市中所有检测器位置的 Excel 文件。它还给了我们很多 CSV 文件,包含这些检测器的每小时计数数据。
这已经足够好了。所以我们把 Excel 文件保存为逗号分隔值。它看起来是这样的。这只是一个截图,显然。计数数据看起来是这样的。你有计数位置的名称。这可能是一个车道位置,也可能是一个横截面,即道路一个方向的所有车道。然后我们当然有日期。小时,这会是这个。只是一个从 0 到 23 的运行编号。我们有不同类型车辆的计数值。所以主要是个人交通。然后这是 PKV。然后我们有,这是什么?对,卡车。在这里,LKV,卡车。那么,我们用这个做什么?
这次,对于这个教程,我不打算向你展示很多在 netedit 中的点击操作。这次全是,或者主要是工具以及如何使用它们。所以我们有两组工具。第一个是将交通计数数据转换为可用格式。然后第二组工具是实际在该数据上构建交通。因为从计数数据到交通并不完全简单。你们中的一些人可能知道。所以首先,我们导入检测器位置,这本来是 Excel 文件,我们已经转换成了 CSV。我们有这个 mapdDetectors.py 工具,它基本上查看经度、纬度数据并将其放置在道路上。
查找适当的列名。所以,Länge 和 Breite 是经度、纬度的德语单词。嗯,因为是德国,他们必须用变音符号写 Länge,带一个“ä”,如果你在终端中粘贴这样的字母,你会遇到编码问题,因为 Windows 终端对“ä”应该如何显示有一些自己的看法。而用文件写这些的人可能有另一种想法。所以我作弊了。我在 CSV 文件中重命名了它。我只是去掉了变音符号,以免今天处理这个问题。所以我们正在检测这个。输出一个文件,里面有完全可以使用的 SUMO 检测器,这里叫做 det.add.xml,这是我们分配的输出名称。
如果我们看看这个,它真的只是一个带有感应环的 SUMO 文件。注意里面有很多额外的行。这是因为我们设置了这个选项 --write-params。所以数据文件中的每一列,告诉我们这个检测器上次维护的时间或它做什么,我们只是把它放进这个文件里,这样我们就可以在 sumo-gui 中看到它。我们运行的第二个工具叫做 edgeDataFromFlow.py。
这是包含每小时计数的 CSV 文件,我们必须设置各种选项来告诉它列名是什么,以及其中的时间是什么。它可能是五分钟间隔的数据。但在这种情况下,我们知道是每小时的,所以在某个点我们必须说一个数据点对应 3600 秒。所以这是这里的时间尺度。然后我们得到一个名为 countdata.xml 的新文件。
所以我们简要地看一下这个文件。它看起来像这样。我们有间隔。它们都有开始和结束时间。所以你可以很快看出这是每小时的数据。然后我们有一个边 ID。然后我们实际上有所有这些我们已经看到的计数数据列,关于卡车和汽车的计数。好的。
所以下一步是这组工具,我们将这些计数数据转换为在网络中行驶的汽车。SUMO 带有几个试图解决这个问题的工具。有些有它们的优点和缺点。而我向大家推荐使用的功能最全的工具叫做 routeSampler.py,这个工具有用的原因是它让你对如何解决这个问题有更多的控制。基本上,这个问题是约束不足的。有太多不同的方式让汽车行驶,却仍然在检测器处记录相同的计数数据。所以你需要某种方式来约束解空间。一个可能的约束是说,嗯,我知道通过我的网络的典型路线,因为典型的路线不是绕大圈的,而是网络中位置之间更短或更快的路径。
所以我们可以做的是,我们可以在整个网络上构建随机行程。不是绕远路的随机行程,而是通过网络的合理路径。这就是我们在这里做的。我们调用 randomTrips.py,我们只是生成一堆在整个网络中的候选路线,然后是路线采样器工具。所以这些是粉红色的随机路线。路线采样器工具使用这些候选路线作为输入,然后试图找到它们的一个子集以及它们的倍数,以匹配记录的计数。这仍然是一个约束不足的问题,但至少解决方案中不会有任何糟糕的绕远路。所以这是一个好方法。然后我们可以给出更多的选项。我们说汽车在网络中运行时,它们可能应该已经以良好的速度行驶,因为它们不是出发,而是进入场景时就在边上。所以它们在进入场景时已经在行驶了。例如,这是这个选项。我们想把它们放在合理的车道上。所以我们不想让一辆即将左转的汽车停在最右边的车道上,就在一个大路口前面,因为如果汽车必须在短距离内多次变道,那会造成混乱。
所以我们确保所有汽车出发或插入时,都在合理的车道上开始场景,适合它们想去的地方。所有这些事情,所有这四个工具,都在一个名为 build.bat 的脚本中被调用,这是一个批处理文件,然后你就有了一个场景。所以让我们看看带有计数数据的场景。
所以你看,这是一个相当大的区域。这可能是,我不知道,八公里乘五公里。当我们以标准设置运行这个时,我们不会看到太多。所以当我没有预先配置任何东西时,我喜欢做的是,但我会告诉你我做了什么。我勾选这里的车辆选项按钮。叫做“以恒定大小绘制汽车”,所以当我缩小时,我能看到它们更大。然后也许为了获得更多信息,让我们根据它们的速度给它们上色。现在我们可以看到它们在网络中行驶。嗯,我们知道我们在这里看到的所有汽车,它们实际上匹配计数数据。我们怎么知道的?当我们运行工具,导入脚本时,它会告诉我们这些。如果你运行路线采样器,它会给你一些关于加载的计数数据和从路线生成的计数数据之间匹配质量的信息。所以这不是基于时间的匹配。只是,我在这个位置看到了这么多汽车,而我生成的路线,在某个时间间隔内,有相同数量的汽车通过同一个位置,或者至少在某个时间间隔内出发。然后,所以它告诉我们,基本上,我们得到了 100%。
嗯,这可能会让你惊讶,我们在每个间隔都得到了 100% 的匹配。但再次说明,这是因为这个问题约束不足。在这里找到一个解决方案非常容易。如果我们有更多的检测器,就会更难。同样,如果我们有更详细的时间数据,也会困难得多。这是另一个时间的话题。所以在这个案例中,我们确实找到了一个解决方案。只看这个,凭眼睛看,我看到这里可能有问题,因为这是这个场景中的一条主要道路,而它上面的汽车数量就是不够。
不知何故,所有的计数都被那些不去那里的汽车匹配了。所以,是的。请注意,如果你这样做,即使你做的一切都对,也可能你的数据不足以约束问题。在这种情况下,很容易加入一些额外的信息,说我在那条路上期望有一定数量的汽车,这就会解决问题。但你需要多做一步,如果你对你的场景了解足够多,并且你想确保它符合你所有的假设。
电动汽车#
所以,我们确实有一个场景,它确实匹配计数数据。从这里该去哪里?嗯,我们显然走向未来,那里每个人都开电动汽车。在现实中让每个人都转换过来很难,但在 SUMO 中非常容易,我来告诉你怎么做。基本上,你只需要重新配置默认的车辆类型,你只需要说,每辆车现在都有一个电池。这是实现这一点的代码行:has.battery.device value is true。现在,这是简单的部分。每个人都开电动汽车。世界将是完美的。但这太容易了。我们想让世界难一点。所以,在我们的小世界里,我们确保不是每个人都从满电开始。 所以,我们设置这个其他东西,这里,另一个参数,配置电池的初始电量(device.battery.chargeLevel)。注意我在这里给出了一行配置来定义一个值的分布。所以,这是 SUMO 中的一个新东西,一些输入参数,你不仅可以给它们数字,实际上还可以给它们分布。那么,我们在这里看到的是什么?这是 normc,它代表一个带截断的正态分布。所以,如你所知,正态分布,它在两边都趋于无穷大,这不是我们想要的电池电量水平,因为,嗯,它们在物理上受到两端的约束。它们有一个最小和最大电量。这也是我们在这里做的。所以,我们设置分布的均值和标准差,然后我们定义最小电量水平和最大电量水平,其中这个较大的数字实际上是电池的最大容量,当你给它们一个电池设备时,这些车辆的默认容量。所以,当我们这样做时,让我们实际看看仿真。 当你这样做时,我们现在在最后一个文件夹,当你这样做时,你有了你的汽车,这次注意我们根据电量水平给它们上色。绿色的汽车和蓝色的,它们都很好,它们有足够的电量,但那些变成红色甚至灰白色的,它们基本上快没电了,可以这么说。嗯,如果它们是用汽油的话,它们就是在用最后一点油行驶。在这种情况下,它们基本上是电量耗尽了。注意我们甚至在这里得到了这个警告消息。在这一行,它说,某某车辆的电池在某个时间耗尽了。所以,这个可怜的家伙,在柏林的某个地方没电了,在柏林市中心没电可不是好事。所以,是的,我们当然必须解决这个问题,否则我们的未来就不那么光明了,汽车会到处抛锚。但首先,我想给你这个小细节,看看你如何实际根据电池电量给汽车上色,因为你以前可能没见过。 所以,再次说明,在这里的车辆配置选项卡中,我们有各种东西来给汽车上色。我们有速度,之前我们根据站点延误给列车上色。如果我们想看延误,不是它们离开站点的时候,而是它们到达的时候,那将是站点到达延误,但我们可以根据一些数值参数给它们上色。就是这个东西。然后我们会得到另一个需要配置的框。在这里,如果你幸运的话,按这个小箭头,你会得到一堆选择,汽车已经有的参数。但这基本上是秘密知识,嗯,它有文档记录,但在这里不容易访问,要知道一辆有电池设备的汽车总是有一个叫做 device.battery.chargeLevel 的参数。所以如果你把它放进去,你就可以根据这个给它们上色。 如果你想知道所有这些参数是什么,嗯,有一个文档页面我想给你看,但我们离线了。嗯,实际上,我仍然可以给你看,因为,你可能知道,SUMO 总是自带它的文档。所以如果你安装了 SUMO,你总是有 SUMO 的文档。所以让我们展示在哪里找到这个。稍微近一点的版本。所以 docs/userdoc,那里某个地方有一个索引文件。现在你有了你的离线文档。所以我们转到 TraCI,这就是问题所在,我到底该点哪里?不,实际上,可能是我们离线文档中的链接有问题。 所以希望下一个链接能用。是的,这个能用,太好了。但页面内的链接似乎不起作用。但如果你转到车辆的值检索页面,那么你实际上在最底部找到了一个长长的参数列表。那里有电量水平。所以你实际上知道要找什么。出租车设备也有很多迷人的参数。好了。所以总是有时间教学的。现在,我们想改变,我们想解决汽车在路上抛锚的问题。我很高兴地告诉你,SUMO 现在有一个功能可以实现这一点,由我们的同事 Mirko 开发。它就是充电站查找器设备(station finder device)。
电动汽车 - 自动充电#
这是一个让汽车自动找到充电地点然后充电的功能。嗯,在我们做这个之前,我们缺少什么?我们需要网络中有充电的地方。所以让我们回去看看我们实际上如何得到这些。所以回到演示文稿。实际上往回走几步。我们想要另一个构建网络时的配置。所以当我们运行,当我们从 OpenStreetMap 导入数据时,我们实际上想设置这个选项 parking-output value="parking.add.xml",因为当你想充电时,通常你需要离开道路,不能阻碍交通。所以你需要一些,首先是一个停车区,然后才能考虑充电。 幸运的是,OpenStreetMap 提供了这些信息。在当前状态下,使用 SUMO 的最新版本,我们可以可靠地获取路边停车区。所以是路边停车。请注意,在之前的版本中并非如此。简单的原因是 OpenStreetMap 的好心人弃用了一种编码此信息的特定方式。而我们发现得有点太晚了。所以现在我们适应了数据库中捕获这些路边停车的推荐方式,现在它又工作了。所以当你设置这个选项时,大多数时候你确实会为你的区域得到一些东西。所以让我们看看一个带有路边停车的网络,只是为了看看它是什么样子。 不,实际上,让我们像这样看。所以放大,我们有这些停车区。所以这些是蓝色的,蓝色的部分。每个彩色矩形都意味着这是一个可以停车的地方。红色意味着它没有被使用。如果有一辆车停在那里,矩形会变成绿色。也许我们应该换个颜色,实际上。现在想想,这不太直观。好的,所以这些是停车区。但这还不够。我们需要充电的地方。 为此,我们有一个工具。它叫做分发充电站(distribute charging stations)。你可以在教程文件夹中找到调用此工具的脚本。第五个。但我 just 带你过一遍这个工具的配置。所以,当然,它需要一个网络。它也需要停车位。我们刚刚导入了这些。它需要定义充电站放在哪里。而且它可能必须把一些停车区分割成几部分。所以实际上我们也会得到一个关于这些的新输出文件。然后我们 just 定义一个概率。我们说这个城市 3% 的停车区,或者更确切地说是这个城市区域,应该配备具有特定充电功率的充电设施。 都一样,因为我们是一次性大规模建设。这是新的基础设施投资。这里投入了大量资金。所以我们 just 以 3% 的比例建设。然后我们就完成了基础设施部分。所以让我们看看这个。充电的基础设施是什么样子的?现在我们 just 把所有这些地方画得大一点。你可以看到凡是有黄色 C 的地方,就有一个充电站。所以当我们放大时,这只是在这里显示的另一件事。它被放在路上,但它实际上适用于停车区。也许我们应该改进一下这个可视化。有人在做笔记吗?好的。 当然,有些设施可以在路上充电。这也是这些未来场景之一。你 just 开着车。也许你在公交车站停你的公交车,或者你在红绿灯前停下,你被感应充电了一点。这也是可以做到的。但现在我们只想模拟连接到停车区的充电,你在那里插上电缆。所以你看它们在 Treptow-Köpenick 分布得很好,现在轮到充电站查找器设备了,我们告诉汽车它们可以重新规划路线以找到充电的地方。所以这些都是我们可以直接放入 SUMO 配置文件的东西。首先,概率为 1。所以每辆车都被允许四处行驶并充电。因为我们不想考虑谁充谁不充。第二件事,这是一个更美观的事情。我们说它们真的可以把电池耗尽到零。也许出于技术原因你不想这么做。可能你不想把它用到零。但关于抛锚的警告,只有在电池电量为零时才会出现。所以为了保持一致,我们在这里配置它不是 5%,而是真正的 0%。然后我们说,如果你的电量降到 10%,你真的需要考虑再次充电了。 默认值更高,但我们不想让所有车都到处充电,只是一部分。所以我们降低了这个值,这只是一个较小的场景,用于演示目的。所以在这种情况下,我们有意偏离了一些可能合理的东西。还有一个默认值,汽车应该提前多长时间寻找充电地方。默认值设为 3 分钟。不太确定这是从哪里来的。也许是在一个更远的未来,我们到处都有这些站点,我们不需要考虑太多细节。但我真的必须提高这个值。否则,汽车并不总能找到充电站。我们毕竟只设置了 3%。然后还有另一个选项,说当我计划需要多少电量才能到达目的地时,我想设置多少安全裕度。默认是 10%。我发现有些地方,这个预测不够稳健。所以我 just 把这个误差裕度提高到 25%。这可能是我们将来可以改进的地方。这主要发生在汽车必须做估计时,不是在行驶了一段时间之后,它们可以取迄今为止路线的平均值。但如果它们刚刚开始,它们就没有这个平均值。在现实中,司机当然会有,但在仿真中我们没有。然后我们要做的是,查看当前所在的道路,并对未来进行推断。如果我们开始的道路是一条慢速道路,而后来我们在高速公路上行驶,那么我们的估计可能有点偏差。所以这个更大的误差裕度在我们有这两种道路的场景中很有帮助。然后我们在这里配置的最后一件事是,我想在结束前充多少电。 默认是充到 80%,然后继续愉快地上路。但在这种情况下,我们会有很多车堵住充电站。我们没有足够的容量给每个人充电。所以我故意降低了这个值,以减少它们停留在那里充电的时间,给其他车充电的机会。现在所有这些都说了,让我们看看仿真。它已经打开了。所以让我们把充电站变小,汽车变大。让它们行驶。所以你看有些车是红色的,可以这么说。但要么它们的路线很短,所以没关系。它们知道它们能到达目的地,要么它们去绕道充电。现在从这里,很难看出谁在做什么。所以在仿真输出中查看会更容易,看看有多少车实际改变了路线。然后我们还有一点时间。我们可以跟着它转,看看它实际在什么时间改变的。所以让我们把仿真运行到结束。如果你仔细观察,你可能会注意到的另一件事是,仿真现在运行得更慢了。我计时发现,有充电站时,运行时间大约是原来的两倍,因为所有这些额外的路径规划和选择充电地点,确实需要一些计算时间。 所以让我们看看生成的输出。这是一个文件 "tripinfos2" 和另一个叫做 "vehroutes2" 的文件。如果我们看第一个,我们得到一些关于电池的信息,好消息是这里每个人都是耗尽为零。所以没有发生电量耗尽的实例。你得相信我,因为我不会通读整个文件。我们还有另一件事,路线。大多数汽车,嗯,它们 just 遵循路线。它们开始时有足够的电量。它们的分布很幸运,或者它们的路线很短。 但如果我们往下翻,我们会找到一些重新规划路线的实例。我们实际上可以搜索这个。现在我搜索字符串 "attime",这里我们有这些事件。所以在某个特定时间,路线被替换了。我们有一个原因。原因是 "stationfinder:search"。所以充电站查找器设备促使这辆车去别的地方充电,然后再继续,因为初始电量太低了。所以让我们找到第一辆这样做的车。看起来是这辆。然后我们 just 在仿真中找到这辆车。 所以我们需要知道它的 ID。以及它出发的时间。22.00。现在我要做一些对你可能也有用的事情。我想再次运行仿真,但我不想弄乱我的输出文件。如果我现在运行,仿真会再次开始写入,所有文件都会被覆盖。但我设置了一个输出前缀。所以这只是在所有写入的文件名前面加上一些东西。所以当我现在运行仿真时,旧文件将保持不变。我正在写入不同的文件。让我们跳转。正确的时间是什么?所以这是我们的断点。就是它。现在我们可以跳到那辆车。幸运的是,我们有所有这些热键记录。 所以我不用太想它们。我们找到了这辆车。它有一条初始路线,去那里。但我们知道在某个时间,稍后,路线被改变了。准确地说,大约两分半钟后。所以让我们跟着这辆车转转。看看发生了什么。我们看到电池已经在橙色区域了。所以它在路上行驶。然后你看到了。它决定去别的地方。这里。去这个充电站。现在让我们再追踪一下这辆车。所以它离开高速公路。它去充电。你可以看到随着时间的推移,颜色在变化。现在它充到了 30%。所以你看它几乎快到目的地了。所以这里的储备因子非常保守。它肯定能到家。但不是所有的车都能。用较低的储备因子。所以其中一些真的需要充电。为了让你更了解充电情况。我看了充电站的输出。我把它放进我们的一个可视化脚本里。这里你看到不同站点的充电汽车数量。所以我们在网络中总共有 53 个充电站。其中 21 个被使用。有些使用非常频繁。所以你可以做各种分析。一些假设情景。如果我把我的站点放在这里会发生什么。如果我增加或减少站点数量会发生什么。然后你就可以建设我们光明的未来。到处都是电动汽车,它们不会因为电量耗尽而抛锚。 非常感谢。
