【Python】交通流シミュレータUXsimのチュートリアル日本語版

# UXsimチュートリアル

「Pythonユーザーが手軽に使えるシンプルな交通シミュレータ」がコンセプトの自作シミュレータUXsim（[GitHubレポ](https://github.com/toruseo/UXsim)）のチュートリアルの日本語版です．GitHubレポに[Jupyter Notebook版](https://github.com/toruseo/UXsim/blob/main/demos_and_examples/demo_notebook_01jp.ipynb)がありますが，アクセス性のよいこちらにも抜粋して掲載します．直接実行したい方はNotebookをダウンロードしてください．

シミュレータの概要を知りたい方は[Qiitaの別記事](https://qiita.com/toruseo/items/c40ec8db868ac95867f6)をご覧ください．

# チュートリアル和訳

これはUXsimを使った交通シミュレーションとその結果分析の簡単なチュートリアルです．

## インストール

インストールがまだの場合はpipで入れます．
```
pip install uxsim
```

## 簡単なシナリオの例

以下，Pythonコーディングでシミュレーションシナリオを作成，実行，結果分析を一貫してやります．Jupyter Notebookで順番にセルに打ち込んでいく形がお勧めです．

最初にモジュールをインポートします．


```python
from uxsim import *
```

### シナリオ定義

まず，シミュレーション世界`W`を作成します．これは[World](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.World.html)オブジェクトを定義して変数に保存することで行われます．オブジェクトの定義の際は主に以下のパラメータを使用します．なお，時間の単位は秒，距離の単位はメートルで統一しています．

- `name`: シミュレーションの名前．出力データ保存先のフォルダ名にも使われる．空文字列`""`でも良い．
- `deltan`: シミュレーション内で何台の車両をひとまとめにして扱うかを指定する整数（一車群を構成する車両数）．
- `tmax`: シミュレーション時間長．
- `print_mode`: シミュレーションの途中経過をprintするかの0/1変数．通常は1が推奨されるが，大量のシミュレーションを自動で実行するときは0が良い．
- `save_mode`: シミュレーション可視化結果を保存するかどうかの0/1変数．
- `show_mode`: シミュレーション可視化結果を表示するかどうかの0/1変数．Jupyter Notebookで実行するときは1，それ以外は0を推奨．
- `random_seed`: ランダムシード．毎回ランダムな結果が欲しい時は`None`，結果を固定したい場合は適当な値．


```python
W = World(
    name="simple_demo",    # シナリオ名
    deltan=5,   # 車両集計単位
    tmax=1200,  # シミュレーション時間長
    print_mode=1, save_mode=1, show_mode=1,    # オプション
    random_seed=0    # ランダムシード．
)
```

次に，ネットワークのノード（交差点）とリンク（道路）を定義します．
これは，Worldオブジェクトの[addNode](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.World.html#uxsim.World.addNode)メソッド（[Node](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.Node.html#uxsim.Node)オブジェクトを追加）と[addLink](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.World.html#uxsim.World.addLink)メソッド（[Link](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.Link.html#uxsim.Link)オブジェクトを追加）を使用して行います．
先にノードを定義し，二つのノードを繋ぐ形でリンクを定義していきます．

addNodeメソッドには主に以下のパラメータが必要です：

- `name`: ノードの名前を指定する文字列．
- `x`: 可視化のためのノードのx座標を指定する実数．
- `y`: 可視化のためのノードのy座標を指定する実数．

addLinkメソッドには主に以下のパラメータが必要です（一部オプショナル）：

- `name`: リンクの名前を指定する文字列．
- `start_node`: 開始ノードの名前を指定する文字列．Nodeオブジェクトを直接指定することも可能．
- `end_node`: 終了ノードの名前を指定する文字列．Nodeオブジェクトを直接指定することも可能．
- `length`: リンクの長さを指定する実数．
- `free_flow_speed`: リンクの最大速度を指定する実数．
- `number_of_lanes`: リンクの車線数を指定する整数．
- `merge_priority`: 合流時のリンクの優先度を指定する実数．


```python
W.addNode("orig1", 0, 0)
W.addNode("orig2", 0, 2)
W.addNode("merge", 1, 1)
W.addNode("dest", 2, 1)

W.addLink("link1", "orig1", "merge", length=1000, free_flow_speed=20, number_of_lanes=1, merge_priority=0.5)
W.addLink("link2", "orig2", "merge", length=1000, free_flow_speed=20, number_of_lanes=1, merge_priority=2)
W.addLink("link3", "merge", "dest", length=1000, free_flow_speed=20, number_of_lanes=1)
```

最後に，出発ノードから到着ノードに流れる交通需要を[adddemand](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.World.html#uxsim.World.adddemand)メソッドを使用して指定します．これにより，適切な数の[Vehicle](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.Vehicle.html)オブジェクトが追加されます．このメソッドには主に以下のパラメータが必要です：

- `orig`: 起点ノードの名前を指定する文字列．Nodeオブジェクトを直接指定することも可能．
- `dest`: 終点ノードの名前を指定する文字列．Nodeオブジェクトを直接指定することも可能．
- `t_start`: 需要の開始時刻を秒単位で指定する実数．
- `t_end`: 需要の終了時刻を秒単位で指定する実数．
- `flow`: 需要の流量を秒当たりの車両数で指定する実数．


```python
W.adddemand("orig1", "dest", 0, 1000, 0.4)
W.adddemand("orig2", "dest", 500, 1000, 0.6)
```

作成したネットワークの形状は，`W.show_network()`で確認できます．このシナリオは単純なY字型のネットワークで，左側から右側に向かって交通が流れます．途中の合流によって交通渋滞が発生する可能性があります．


```python
W.show_network()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/93ac7b10-1fe7-0fe3-7145-21ffb69f66ba.png" width="300"/>

### シミュレーションの実行

シナリオを定義したら，`World`オブジェクトの`exec_simulation`メソッドを呼び出すことでシミュレーションを実行できます．
まず，シミュレーション設定の概要が表示され，その後，進捗を確認するために以下の情報がシミュレーション中に表示されます：

- 現在のシミュレーション時間
- ネットワーク内の車両数
- 車両の平均速度
- これまでのシミュレーションステップに要した計算時間


```python
W.exec_simulation()
```
出力：

    simulation setting:
     scenario name: simple_demo
     simulation duration:	 1200 s
     number of vehicles:	 700 veh
     total road length:	 3000 m
     time discret. width:	 5 s
     platoon size:		 5 veh
     number of timesteps:	 240
     number of platoons:	 140
     number of links:	 3
     number of nodes:	 4
     setup time:		 3.86 s
    simulating...
          time| # of vehicles| ave speed| computation time
           0 s|        0 vehs|   0.0 m/s|     0.00 s
         600 s|      100 vehs|  17.5 m/s|     0.04 s
        1195 s|       25 vehs|  20.0 m/s|     0.09 s
     simulation finished
    


### 結果の分析

シミュレーション結果の分析は，`W.analyzer`としてアクセス可能な[Analyzer](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.analyzer.Analyzer.html)オブジェクトが担当します．

結果の概要は以下のように出力できます．遅延率は総移動時間に対する遅延時間の比率で，0に近い値はスムーズな交通（最短経路が渋滞なしで走行可能な状態）を示し，大きな値は渋滞（最短経路が迂回されたり渋滞したりしている状態）を示します．


```python
W.analyzer.print_simple_stats()
```

出力：

    results:
     average speed:	 13.8 m/s
     number of completed trips:	 675 / 700
     average travel time of trips:	 142.7 s
     average delay of trips:	 42.7 s
     delay ratio:			 0.299
    

シミュレーション結果は，以下のように`pandas.DataFrame`オブジェクトとして出力できます．なお，`-1`という値は基本的に未定義を意味します（例えば，前方に車両がない場合の車間距離など）．


```python
#全体結果
df = W.analyzer.basic_to_pandas()
display(df)

#OD別分析
df = W.analyzer.od_to_pandas()
display(df)

#MFD
df = W.analyzer.mfd_to_pandas()
display(df)

#リンクレベル
df = W.analyzer.link_to_pandas()
display(df)

#リンク内レベル
df = W.analyzer.link_traffic_state_to_pandas()
display(df)

#車両レベル
df = W.analyzer.vehicles_to_pandas()
display(df)
```

出力（一部）：

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>total_trips</th>
      <th>completed_trips</th>
      <th>total_travel_time</th>
      <th>average_travel_time</th>
      <th>total_delay</th>
      <th>average_delay</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>700</td>
      <td>675</td>
      <td>96325.0</td>
      <td>142.703704</td>
      <td>28825.0</td>
      <td>42.703704</td>
    </tr>
  </tbody>
</table>
</div>




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>name</th>
      <th>dn</th>
      <th>orig</th>
      <th>dest</th>
      <th>t</th>
      <th>link</th>
      <th>x</th>
      <th>s</th>
      <th>v</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
      <td>5</td>
      <td>orig1</td>
      <td>dest</td>
      <td>15</td>
      <td>link1</td>
      <td>0.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0</td>
      <td>5</td>
      <td>orig1</td>
      <td>dest</td>
      <td>20</td>
      <td>link1</td>
      <td>100.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>0</td>
      <td>5</td>
      <td>orig1</td>
      <td>dest</td>
      <td>25</td>
      <td>link1</td>
      <td>200.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0</td>
      <td>5</td>
      <td>orig1</td>
      <td>dest</td>
      <td>30</td>
      <td>link1</td>
      <td>300.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>4</th>
      <td>0</td>
      <td>5</td>
      <td>orig1</td>
      <td>dest</td>
      <td>35</td>
      <td>link1</td>
      <td>400.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>...</th>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th>4207</th>
      <td>139</td>
      <td>5</td>
      <td>orig2</td>
      <td>dest</td>
      <td>1090</td>
      <td>link3</td>
      <td>600.0</td>
      <td>225.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>4208</th>
      <td>139</td>
      <td>5</td>
      <td>orig2</td>
      <td>dest</td>
      <td>1095</td>
      <td>link3</td>
      <td>700.0</td>
      <td>225.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>4209</th>
      <td>139</td>
      <td>5</td>
      <td>orig2</td>
      <td>dest</td>
      <td>1100</td>
      <td>link3</td>
      <td>800.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>4210</th>
      <td>139</td>
      <td>5</td>
      <td>orig2</td>
      <td>dest</td>
      <td>1105</td>
      <td>link3</td>
      <td>900.0</td>
      <td>-1.0</td>
      <td>20.0</td>
    </tr>
    <tr>
      <th>4211</th>
      <td>139</td>
      <td>5</td>
      <td>orig2</td>
      <td>dest</td>
      <td>1105</td>
      <td>trip_end</td>
      <td>-1.0</td>
      <td>-1.0</td>
      <td>-1.0</td>
    </tr>
  </tbody>
</table>
<p>4212 rows × 9 columns</p>
</div>

### 結果の可視化

様々な可視化手法も組み込まれています．

#### リンク内レベル

リンク内の車両の移動や渋滞の伝播を可視化するには，時空間図というものが有用です．


```python
W.analyzer.time_space_diagram_density("link1")
W.analyzer.time_space_diagram_traj("link1")

W.analyzer.time_space_diagram_traj_links([["link1", "link3"]])
```

出力（一部）：
![demo_notebook_01jp_22_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/6c89cfaf-a6ed-c331-03c2-59c4900864b0.png)
![demo_notebook_01jp_22_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/ab6af33f-b4cf-1c8a-80bf-4b494e260c98.png)
![demo_notebook_01jp_22_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/c19bd905-6382-1c60-aed3-434a79aeefbf.png)

待ち行列の分析に用いられる累積図です．


```python
W.analyzer.cumulative_curves(["link1"])
```
出力：
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/2b4895db-b6eb-d474-1067-2d1b0a05e66b.png" width="300"/>

#### ネットワーク/エリアレベル

ネットワークの交通状態は複数の方法で可視化できます：

- `W.analyzer.network(t, detailed=0)`: リンク平均の交通状態のスナップショット
- `W.analyzer.network(t, detailed=1)`: セグメントレベルの交通状態のスナップショット
- `W.analyzer.network_anim`: ネットワーク交通状態のgifアニメーション
- `W.analyzer.network_fancy`: ネットワーク内の車両軌跡のgifアニメーション

デフォルトでは，リンクごとの交通状況，リンク内の区間ごとの交通状況（ネットワーク形状によっては不明確な場合があります），そして一部の車両の移動軌跡を可視化できます．リンクの幅が太いほど車両の数と密度が大きく，色が濃いほど速度が低いことを示します．大規模なシナリオの場合，アニメーション生成速度に時間がかかる場合があります．

また，この例ではリンクの向きを表現するのに左側通行（日本や英国など）を使用しています．右側通行を使用したい場合は，オプション`left_handed=0`を指定してください．


```python
for t in list(range(0,W.TMAX,int(W.TMAX/3))):
    W.analyzer.network(t, detailed=0, network_font_size=0, figsize=(3,3))
for t in list(range(0,W.TMAX,int(W.TMAX/3))):
    W.analyzer.network(t, detailed=1, network_font_size=0, figsize=(3,3))

W.analyzer.network_anim(animation_speed_inverse=15, timestep_skip=30, detailed=0, figsize=(6,6), network_font_size=0)    
```

出力（一部）：
![demo_notebook_01jp_26_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/d3ee0cce-df39-5ada-8db4-7286294ea3e0.png)
![demo_notebook_01jp_26_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/799b57f0-e832-b806-11e4-ae9e4e7ec3e1.png)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/4a3bc79f-94f9-956f-9d8e-c4cd15dc56ed.png" width="300"/>
### インタラクティブな可視化と分析のためのGUI

可視化と分析のためのGUIビューアを起動することもできます．このGUIでは，ネットワーク交通のアニメーションの再生と一時停止，選択したリンクの時空間ダイアグラムの表示，特定の車両の追跡，いくつかの統計情報の確認，そしてこれらの結果をCSVファイルにエクスポートすることが可能です．


```python
from uxsim.ResultGUIViewer import ResultGUIViewer
ResultGUIViewer.launch_World_viewer(W)
```

[GUIの動いている様子はこんな感じです（シナリオは異なります）](https://github.com/toruseo/UXsim?tab=readme-ov-file#interactive-gui-for-exploring-a-simulation-result)

## Sioux Fallsネットワーク

最後に，もう少し大規模なシミュレーション例を示します．これは交通工学の研究でベンチマークとしてよく用いられるSioux Falls（スーフォールズ）ネットワークと呼ばれるデータセットです．

シミュレーション分析を行う上で最も頻繁に使用される関数は，[World](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.World.html)（シミュレーションシナリオを定義するため）と[Analyzer](https://toruseo.jp/UXsim/docs/_autosummary/uxsim.analyzer.Analyzer.html)（シミュレーション結果を分析するため）のものになると思います．それらの詳細については，[技術リファレンス](https://toruseo.jp/UXsim/docs/tech_ref.html)をご参照ください．


```python
# Simulation main
W = World(
    name="simple_demo",
    deltan=5,
    tmax=7200,
    print_mode=1, save_mode=1, show_mode=0,
    random_seed=0
)

# Scenario definition
W.load_scenario("dat/sfnetwork.uxsim_scenario")

# Simulation execution
W.exec_simulation()

# Results analysis
W.analyzer.print_simple_stats()
```

出力：

    loading scenario from 'dat/sfnetwork.uxsim_scenario'
     DATA SOURCE AND LICENCE : Sioux Falls network. This is based on https://github.com/bstabler/TransportationNetworks/tree/master/SiouxFalls by Transportation Networks for Research Core Team. Users need to follow their licence. Especially, this data is for academic research purposes only, and users must indicate the source of any dataset they are using in any publication that relies on any of the datasets provided in this web site.
     Number of loaded nodes: 24
     Number of loaded links: 76
     Number of loaded `adddemand`s: 1056
    simulation setting:
     scenario name: simple_demo
     simulation duration:	 7200 s
     number of vehicles:	 34690 veh
     total road length:	 314000.0 m
     time discret. width:	 5 s
     platoon size:		 5 veh
     number of timesteps:	 1440
     number of platoons:	 6938
     number of links:	 76
     number of nodes:	 24
     setup time:		 0.52 s
    simulating...
          time| # of vehicles| ave speed| computation time
           0 s|        0 vehs|   0.0 m/s|     0.00 s
         600 s|     2550 vehs|   7.2 m/s|     1.72 s
        1200 s|     5815 vehs|   6.9 m/s|     3.99 s
        1800 s|     8700 vehs|   6.5 m/s|     6.59 s
        2400 s|    10335 vehs|   5.8 m/s|     9.16 s
        3000 s|    10955 vehs|   5.3 m/s|    11.69 s
        3600 s|    13390 vehs|   4.9 m/s|    14.26 s
        4200 s|    14265 vehs|   4.5 m/s|    16.70 s
        4800 s|    15175 vehs|   4.4 m/s|    19.30 s
        5400 s|    11190 vehs|   5.4 m/s|    21.47 s
        6000 s|     6745 vehs|   6.0 m/s|    22.99 s
        6600 s|     3460 vehs|   6.1 m/s|    23.73 s
        7195 s|     1525 vehs|   6.2 m/s|    24.07 s
     simulation finished
    results:
     average speed:	 5.4 m/s
     number of completed trips:	 33165 / 34690
     average travel time of trips:	 1751.1 s
     average delay of trips:	 383.5 s
     delay ratio:			 0.219
    



可視化
```python
W.analyzer.network_anim(animation_speed_inverse=15, timestep_skip=8, detailed=0, network_font_size=0)
with open("outsimple_demo/anim_network0.gif", "rb") as f:
    display(Image(data=f.read(), format='png'))
```

出力：
![demo_notebook_01jp_35_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3849254/351ca020-efdb-faed-3f1d-08e13db884c2.png)