Pythonによる交通シミュレーション

「Pythonユーザーが手軽に使えるシンプルな交通シミュレータ」がコンセプトの自作シミュレータUXsimの紹介です．
GitHubでオープンソースで公開しています：https://github.com/toruseo/UXsim

<div class="repo-preview">
	<a href="https://github.com/toruseo/UXsim">
		<img src="https://opengraph.githubassets.com/1/toruseo/UXsim" alt="GitHub Repository Preview" width=400>
	</a>
</div>

↓UXsimによる10km x 10kmの道路網での2時間に渡る6万台の車両のシミュレーション．計算時間は30秒ほど
<p float="left">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/c952187b-8a13-7a5e-dc01-12aec28c4aa3.gif" width="300"/>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/92ade615-c28d-864f-b6c2-1271ae2f8ba4.gif" width="300"/>
</p>

## はじめに

交通シミュレーションは様々な目的に使われます．例えば，渋滞の様子を再現したり，街づくりのための分析をしたり，最近では自動運転やライドシェア，機械学習による交通制御などの先端的なトピックの実験ツールとしてよく用いられます．

既存のオープンソース交通シミュレータとしては[SUMO](https://eclipse.dev/sumo/)が非常に有名です．これは車両一台一台の動きを精密に再現する詳細な交通流シミュレータで，各種APIも整備されていて大変強力なシミュレータです．

これに対し，今回ご紹介するUXsimはもう少し大局的な交通流シミュレータで，車両一台一台に注目するのではなく，渋滞がどこでどれくらい生じるかなどの大規模な交通現象を再現するものです．そのため，細かい条件設定（車の加速度とか交差点の構造とか）を気にすることなく，手軽にシミュレーションが実行可能です．さらに，純Pythonとすることでコーディングにより柔軟な操作が可能なようにデザインされています．

## 簡単な使い方

非常に簡単に使えるので，早速使い方を説明します．インストールはpipで一発です：
```
pip install uxsim
```

シミュレーションは全てPythonコードで行います．以下のコードは，Y字型ネットワークで合流による渋滞が生じる状況をシミュレーションするものです：
```python
from uxsim import *

# シミュレーション本体の定義
# 単位は全て秒とメートル
W = World(
    name="",    # シナリオ名
    deltan=5,   # 車両集計単位
    tmax=1200,  # シミュレーション時間
    print_mode=1, save_mode=1, show_mode=0,    # オプション
    random_seed=0    # ランダムシード
)

# シナリオ定義
## ノードの作成
W.addNode(name="orig1", x=0, y=0)
W.addNode("orig2", 0, 2)
W.addNode("merge", 1, 1)
W.addNode("dest", 2, 1)
## ノード間のリンクの作成
W.addLink(name="link1", start_node="orig1", end_node="merge",
          length=1000, free_flow_speed=20, number_of_lanes=1)
W.addLink("link2", "orig2", "merge", length=1000, free_flow_speed=20, number_of_lanes=1)
W.addLink("link3", "merge", "dest", length=1000, free_flow_speed=20, number_of_lanes=1)
## ノード間のOD交通需要の作成
W.adddemand(orig="orig1", dest="dest", t_start=0, t_end=1000, flow=0.45)
W.adddemand("orig2", "dest", 400, 1000, 0.6)

# シミュレーション実行
W.exec_simulation()

# 結果表示
W.analyzer.print_simple_stats()

# ネットワーク交通状態のスナップショット可視化
W.analyzer.network(100, detailed=1, network_font_size=12)
W.analyzer.network(600, detailed=1, network_font_size=12)
W.analyzer.network(800, detailed=1, network_font_size=12)
```

まずはシミュレーションシナリオをまとめる`World`を定義し，そこに`addNode`でノード（交差点）を作り，`addLink`でノードを繋ぐリンク（道路）を定義します．そして，`adddemand`でノードから別のノードに向かう交通需要を定義します．コメントにもそれぞれの意味が書いてありますが，Pythonに慣れた方であれば直感的に意味がつかめると思います．絵にすると下のような感じです：
![y-network.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/0817176c-49ef-7f06-91f7-ba9a3c8dc6c1.png)


そして，`W.exec_simulation()`でシミュレーションを実行し，その後にいくつか簡単な分析・可視化を行っています．

これを実行すると以下のような結果が出力されます．
```
simulation setting:
 scenario name:
 simulation duration:    1200 s
 number of vehicles:     810 veh
 total road length:      3000 m
 time discret. width:    5 s
 platoon size:           5 veh
 number of timesteps:    240
 number of platoons:     162
 number of links:        3
 number of nodes:        4
 setup time:             0.00 s
simulating...
      time| # of vehicles| ave speed| computation time
       0 s|        0 vehs|   0.0 m/s|     0.00 s
     600 s|      130 vehs|  13.7 m/s|     0.03 s
    1195 s|       75 vehs|  12.3 m/s|     0.06 s
 simulation finished
results:
 average speed:  11.6 m/s
 number of completed trips:      735 / 810
 average travel time of trips:   162.6 s
 average delay of trips:         62.6 s
 delay ratio:                    0.385
```
![sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/43417e70-9d01-9b45-5e07-cd3850ce09ea.png)


810台の車両のシミュレーションが0.06秒で計算できました．Y字型の合流部で，上側のリンクで渋滞が発生している様子が可視化されています（濃い色の部分が渋滞を意味します）．車両一台の平均旅行時間は160秒程で，内60秒が渋滞による遅れという結果となりました．

こんな感じで，複雑なシナリオを定義したり，シミュレーション途中に内部状態に介入して交通制御をおこなったり，結果をmatplotlibやpandas.dataframeに出力して自動分析したりといったことが気軽にできるように設計されています．

## 高度な応用例

以下に高度な応用例の概要を紹介します．細かい説明は省きますので，詳細に興味のある方は是非[GitHubリポジトリ](https://github.com/toruseo/UXsim)や[リファレンスサイト](https://toruseo.jp/UXsim/docs/)をご覧ください．様々な実例とともに詳細な説明をしています．

### 大都市圏の100万台シミュレーション

Pythonは遅いことに定評がありますが，UXsimはNumPyやSciPyの機能を活用することで高速な計算を実現しています．下の図は，シカゴ都市圏の100万台の車両が通勤ラッシュ時に交通する様子をシミュレーションし，その一部の車両の様子を表示したものです．計算時間は普通のデスクトップPCで40秒足らずです．このように，都市規模の大交通流を手軽にシミュレーションできるのがマクロシミュレータの長所です．

↓クリックでgifアニメに飛びます
<a href="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/0c40d2ff-158e-1ade-302f-5765a0de77a1.gif">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/b705ae11-8a0e-975c-ac31-3f44aee15982.png">
</a>

### 深層強化学習（AI）による信号制御

UXsimには信号交差点の機能も付いています．赤信号・青信号の表示をPyTorchの深層強化学習（DQN）により最適化する例を示します．Python製ですので，PyTorchと簡単に連携できます．

<p float="left">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/a8280709-e991-7785-0ea0-2215796fa6aa.gif" width="300"/>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/cebee7ee-96ce-684f-781b-ed831d9c010c.gif" width="300"/>
</p>
一個目の図は特に制御を行っていない場合で，すぐに渋滞してしまいました．二個目の図はAI制御の場合で，交通状況に合わせて信号を賢く変化させることで円滑な交通が実現しています．

### 自動運転タクシーの最適配車

最近ではライドシェアや自動運転タクシーのような新たなモビリティが注目されています．そのようなシステムでは，乗客に合わせた最適な配車と配送が必要になります．UXsimにはそのような問題を扱う機能も実装されていて，例えば自作のライドシェアマッチングアルゴリズムの性能を検証するようなこともできます．

下の図は「都市にタクシーは何台必要か？」という問題をUXsimのシミュレーションにより分析したものです．

![taxi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/2d237af1-17db-015d-89df-ce3059a74c97.png)
横軸が都市のタクシー台数，緑線が乗客の平均待ち時間，青線が乗客の平均旅行時間，赤線が都市交通の平均速度です．タクシーが少なすぎると待ち時間が長くて不便ですが，逆にタクシーが多すぎても渋滞によって旅行時間が延びてしまうので，タクシー台数はちょうどよい値＝2000台にするとよいことがわかります．
このような分析も，UXsimのシナリオをfor文で自動で大量生産することで簡単に実行できます．

## シミュレーションの仕組み

参考までに，シミュレーションの仕組みについても簡単に説明します．UXsimでは，最も細かいレベルでは車両の動きを計算します．走行中は，前方の車両と近づきすぎたら減速したり，交差点が赤信号だったり詰まっていたら停止します．この加減速はミクロシミュレーションほどは精密ではないですが，基本的な渋滞は再現できる程度にはちゃんとモデル化されています．
（同じ車両挙動モデルを用いた[単一道路上の交通シミュレーションのWebブラウザ上デモ](http://seo.cv.ens.titech.ac.jp/traffic-flow-demo/bottleneck_jp.html)もあるので良ければ遊んでみてください）

経路選択も考慮しています．各車両は各時刻の交通状況を見て，その時点での最小旅行時間経路を通って目的地に向かおうとします．時刻の経過とともに交通状況が変化したら，それに合わせて経路を変更することもあります．

UXsimの特徴は，車両の動きを計算する際に，一台一台を個別に動かすのではなく，複数台をまとめて一個と扱い効率的に計算できる点です．この「まとめる台数」を大きくすれば計算速度が速くなり，小さくすれば精度が高くなる仕組みになっています．そのため，細かい分析をしたいときは「まとめる台数」を1台としたり，都市規模の分析をしたいときは「まとめる台数」を大きくする（前述の100万台の例では30台をまとめています）というように，スケールに応じた柔軟な計算が可能です．

## おわりに

Python製交通シミュレータUXsimのご紹介でした．大学の教員が開発したということもあり，卒論や修論のような教育・研究目的に特に便利なつくりになっているかと思いますが，基礎となっているモデルは最先端の研究成果に基づく真面目なものですので，ユーザーの創意工夫次第で色々な目的に使えると思います．是非お気軽にお試しください！

GitHubでのフィードバックやコントリビューションも歓迎いたします！

追記：[日本語版チュートリアル](https://qiita.com/toruseo/items/7736af7c2602cb1669ea)もQiitaに投稿しました

## 関連サイトのリンク
- [GitHubレポジトリ](https://github.com/toruseo/UXsim)
- [テクニカルリファレンス](https://toruseo.jp/UXsim/docs/)
- [arXivプレプリント](https://arxiv.org/abs/2309.17114)
- [日本語版チュートリアル](https://qiita.com/toruseo/items/7736af7c2602cb1669ea)