SUMOを用いた沿道駐車場への入庫シミュレーション

## はじめに
道路上から沿道の路外施設へ入庫する際には，その車両は道路上で減速または，停止することになります．
このような減速や停止はその道路上の交通流の円滑姓を低減させかねません．

さて，道路上で起きる事象の影響を分析する方法の一つに交通流シミュレーションが挙げられます．
マクロな交通の流れを観察するシミュレータには，例えば，[uxsim](https://toruseo.jp/uxsim/)があります．
一方で，ミクロな交通の流れを観察するシミュレータには，例えば，[SUMO](https://eclipse.dev/sumo/)があります．
本記事では，SUMOを用いて，店舗入出庫を想定した交通流をシミュレートし，その結果の可視化を行いたいと思います．

## 参考サイト

https://qiita.com/h-masano/items/a137b5c7d4e3e70e0919

https://sumo.dlr.de/docs/

https://www.docswell.com/s/niyalist/KYWY6L-2024-07-02-101523#p6

https://note.com/morimaru68/n/n20c945915132

## 編集ソフトの立ち上げと初期設定
1. タスクバーの検索ボックスで，"netedit"と検索して，Neteditを立ち上げます．
    
    ![netedit_standup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/2f74146c-f076-45e1-a05d-0bb29a07692e.png)

1. Ctrl + Nで白紙のネットワークを立ち上げます．

    ![netedit_BlankNetwork.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/d4941a2c-9d46-4057-873b-1897b75cf465.png)

1. Processingタブから，Optionsを選択し，Netedit optionsウィンドウを立ち上げます．

    ![NeteditOptions_standup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/0a877497-0794-448c-b921-60a3637fc685.png)

1. 立ち上げたNetedit optionsウィンドウで，Processing > lefthandにチェックを入れて，ウィンドウ下部の`OK`ボタンを押します．

    ![NeteditOptions_lefthand.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/d3c8c534-6924-451d-b1ae-d7385f140407.png)

## ネットワーク作成
1. Neteditウィンドウ上部の`Network`が選択された状態で，`Edge mode`ボタンを押し，編集モードに入ります．

    ![netedit_EdgeMode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/89bccfea-8394-4bad-bbd0-366aa9dd9994.png)

1. 同じく，Neteditウィンドウ上部の`Network`が選択された状態で，`Show grid`ボタンを押し，グリッドを表示します．デフォルトは100mグリッドです．

    ![netedit_ShowGrid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/feff212d-157a-42f4-81dd-7aa7e8eb508d.png)

1. 任意の2点を順にクリックすることで，2つのノードが配置され，その間に有向のエッジが張られます．対向車線も作成したい場合は，同じ2点間を逆の順にクリックします．今回は500mの直線道路を作成しました．

    ![netedit_CreateEdges.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/00f3e272-7e76-454a-b67f-115d38873d6d.png)

1. 店舗駐車場に見立てて，直線道路から左右に分岐する細街路を作ります．まずは，グリッドのサイズを変えましょう．`Edit Coloring Schemes`ボタンを押して，View Settingウィンドウを立ち上げます．BackgroundのToggle gridのx-spacingとy-spacingの値をそれぞれ`10`に変更して，ウィンドウ下部の`OK`ボタンを押して，グリッドのサイズを10m四方に変更します．

    ![netedit_EditColoringSchemes.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/e2beac8b-aaf3-4113-8c8d-4f8c1f540f87.png)
    ![ViewSetting_GridSize.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/127937f3-9515-4fc3-b904-d828b11987dc.png)

1. 任意の地点で直線道路のエッジを右クリックして，`Edge operations` > `Split edge here`を選択します．**対向車線も併せて切っておかないと，平面交差していることにならないので，気を付けてください．**
    
    ![netedit_EdgeSplit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/ec4adc21-f2be-46c2-ad89-d057dd38af86.png)

1. エッジを分割した地点から入庫用の細街路を伸ばします．
    
    ![netedit_AddEdges.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/d56b5115-eba0-404e-9eaa-db2e9b9369c6.png)

1. エッジの属性を変更します．`Inspec Mode`ボタンを押して，対象のエッジを選択し，エッジの属性情報を確認します．

    ![netedit_InspectMode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/1fa25008-0364-45b4-887c-ce1b667f294d.png)

1. speed属性は対象エッジの最高速度[m/s]です．今回は細街路の最高速度を20km/h = `5.56`[m/s]にしておきます．
    
    ![Inspect_speed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/056a06d3-d2e1-487d-bac4-7f2923c13519.png)

1. これで，500mの直線道路とその途中4箇所に左右各2方向の駐車場入口(に見立てた細街路)を設置しました．Ctrl + Sで，作成したネットワークをネットワークファイル(.net.xml)として保存しましょう．

## 需要の設定
### neteditで編集
1. 上部タブバーで`Demand`を選択し，`route mode`ボタンを押します．
    
    ![netedit_RouteMode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/774122a2-f666-4a17-a6f6-171c468987e9.png)

1. 各エッジを順に選択して，各車の移動経路を作成します．全てのエッジをクリックした後に，`finish route creation`ボタンを押すと，経路が作成されます．

    ![routes_RouteCreation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/4b91b557-5325-4294-93f5-3329cefcb3d3.png)

1. 上部バーの`Vehicle Mode`ボタンを押します．

    ![netedit_VehicleMode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/7e6f42be-fd4f-4cd4-a5b7-71c8538cc806.png)

1. 立ち上がったVehiclesウィンドウのVehiclesプルダウンには，[多くの種類](https://sumo.dlr.de/docs/Netedit/elementsDemand.html)がありますが，ここでは，`flow(over route)`を選択します．

   ![vehicles_vehicles.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/c343fa00-4615-431b-a2fd-94224669abca.png)

1. vehsPerHourに任意の交通需要[台/時]を設定して，作成した経路をクリックします．

    ![Vehicles_CreateFlow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/aab0bd08-62ff-4145-b2fa-7662ae593122.png)

1. 以上で，移動経路とその経路上の交通需要の設定が完了しました．上部バーで，`Save individual files` > `Save demand elements`ボタンを押して，ルートファイル(.rou.xml)として保存します．

    ![netedit_SaveRoute.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/88f2fcda-9e7b-474c-b1da-bfb066b12609.png)

### テキストエディタで編集する
ルートファイル(.rou.xml)はテキストエディタで編集することもできます．
先ほど保存したルートファイルを適当なエディタで立ち上げてください．
``` demand.rou.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- generated on 2025-05-27 17:33:31 by Eclipse SUMO netedit Version 1.23.1
-->

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/routes_file.xsd">
    <!-- Routes -->
    <route id="r_0" edges="E0 E12"/>
    <!-- Vehicles, persons and containers (sorted by depart) -->
    <flow id="f_0" begin="0.00" route="r_0" number="1800" vehsPerHour="10"/>
</routes>
```
    
`<route id="r_0" edges="E0 E12"/>`は，`E0`と`E12`を順に通る`r_0`というIDの経路を定義する構文です．
`<flow id="f_0" begin="0.00" route="r_0" number="1800" vehsPerHour="10"/>`は，シミュレーション開始後`0.00`秒から，経路`r_0`上を`10`[台/時]の流率で，計`1800`[台]流す`f_0`というIDのフローを定義する構文です．

これらを応用して，以下のような変更・追記を行います．
``` demand.rou.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- generated on 2025-05-27 17:33:31 by Eclipse SUMO netedit Version 1.23.1
-->

<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/routes_file.xsd">
    <!-- Routes -->
    <route id="r_right_1" edges="E0 E12" color="1,0,0"/>
    <route id="r_right_2" edges="E0 E0.200 E0.300 E16" color="1,0,0"/>
    <route id="r_left" edges="E0 E0.200 E15" color="0,1,0"/>
    <route id="r_strate_1" edges="E0 E0.200 E0.300 E0.400" color="0,0,1"/>
    <route id="r_strate_2" edges="-E0" color="0,0,1"/>
    <!-- Vehicles, persons and containers (sorted by depart) -->
    <flow id="f_1" begin="0.00" route="r_right_1" number="10" vehsPerHour="10"/>
    <flow id="f_2" begin="0.00" route="r_right_2" number="10" vehsPerHour="10"/>
    <flow id="f_3" begin="0.00" route="r_left" number="10" vehsPerHour="10"/>
    <flow id="f_4" begin="0.00" route="r_strate_1" number="350" vehsPerHour="350"/>
    <flow id="f_5" begin="0.00" route="r_strate_2" number="350" vehsPerHour="350"/>
</routes>
```
上記のスクリプトでは，
- 右折入庫2本，左折入庫1本，直進方向，対向直進方向の計5本のルートを設定しています．
- 経路の判別が付きやすいように，各ルートに`color`属性を与えています．
- 各経路に個別の需要(`flow`)を設定しています．
- 直線道路上は350[台/時]の流率で350台，入庫経路には10[台/時]の流率で10台が流れるように設定しています．

これらを変更することで，様々な経路，需要におけるシミュレーションを行えます．

## シミュレーションの実行
適当なディレクトリにおいて，(任意の名称).sumocfgというファイルを作成します．
sumoのtutorialにあるファイルを参考にシミュレーションの設定ファイルを作成します．
```ParkingLotEntrance.sumocfg
<?xml version="1.0" encoding="UTF-8"?>

    <!-- generated on 2024-06-12 18:31:58 by Eclipse SUMO sumo Version v1_20_0+0443-233e48e5c88
    -->
    
    <sumoConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/sumoConfiguration.xsd">
    
        <input>
            <net-file value="network.net.xml"/>
            <route-files value="demand.rou.xml"/>
        </input>
    
        <time>
            <begin value="0"/>
            <end value="3600"/>
            <step-length value="0.01"/>
        </time>
    
        <output>
            <fcd-output value="probe.xml"/>
        </output>
    
    </sumoConfiguration>
```

上記のスクリプトに関して，
- `<input> ~~ </input>`では，シミュレーションに必要なファイルを指定しています．
    - `<net-file value="quickstart.net.xml"/>`はネットワークのデータを設定したファイルを指定しています．
    - `<route-files value="quickstart.rou.xml"/>`は交通需要のデータを設定したファイルを指定しています．
- `<time>~~</time>`では，シミュレーション時間に関する設定を行っています．
    - `<begin value="54000"/>`はシミュレーションの開始時刻です．
    - `<end value="54900"/>`はシミュレーションの終了時刻です．
    - `end`を設定しない場合，全ての車両のシミュレーションが完了するまでシミュレーションが継続します．
    - `<step-length value="0.01"/>`で，デフォルトでは1秒である，シミュレーションのタイムステップを10ミリ秒に変更しています．
- `<output>~~</output>`は結果の出力に関する設定です．
    - `<fcd-output value="probe.xml"/>`で各車のプローブ情報を`probe.xml`に保存します．

タスクバーの検索ボックスで，"sumo-gui"と検索して，sumo-guiを立ち上げます．
    
![sumo-gui_standup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/0b17246b-d86b-41da-9481-b04a7d4152d9.png)

ウィンドウ上部のタブバーから，`ファイル` > `シミュレーションを開く`を選択して，先ほど作成した.sumocfgファイルを開きます．

![sumo-gui_OpenSimulation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/34e5b203-6843-49ec-9810-a06009eda732.png)

ウィンドウ上部のDelayの数値を調整して，シミュレーションの経過を観察しやすくします．[公式](https://sumo.dlr.de/docs/sumo-gui.html)によると，この値はシミュレーションが次のフレームに移行する際までの待機時間です．

![sumo-gui_delay.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/12a2ec52-bffc-416a-9d0d-871a76e8d64e.png)

走行する車両のデザインを変更します．`Edit Coloring Schemes`ボタンを押して，View Settingウィンドウを立ち上げます．`Vehicles`を選択して，Show Asのプルダウンから`raster images`を選択します．

![sumo-gui_ViewSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/03b11afe-2429-4518-8390-cc1e383f0552.png)

![ViewSetting_RasterImages.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/9e058278-15e5-4699-b007-46733258a3bc.png)

ウィンドウ上部のシミュレーション開始ボタンを押すと，シミュレーションが開始されます．

![capture_crop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/0da30881-e4b8-431e-90b1-8f3706ad938e.gif)


## 結果の可視化
結果の可視化の一例として，交通工学分野でよく用いられるタイムスペース図を作成してみようと思います．
これは，横軸に時刻，縦軸に位置を取ったグラフです．
まずは，シミュレーション結果が記録されたxmlファイルを表敬式に変換します．
Excelで直接展開できるという記事も散見されますが，著者はうまく開けなかったので，以下のコードでPandasのDataFrame形式にしました．
```xml_to_csv.py
import pandas as pd
import xml.etree.ElementTree as ET

# XMLファイルを読み込み
tree = ET.parse("probe.xml")  # 出力されたxmlファイルを指定する
root = tree.getroot()

# データ抽出
data = []
for timestep in root.findall("timestep"):
    time = timestep.attrib['time']
    for vehicle in timestep.findall("vehicle"):
        entry = {
            "time": float(time),
            "id": vehicle.attrib["id"],
            "x": float(vehicle.attrib["x"]),
            "y": float(vehicle.attrib["y"]),
            "angle": float(vehicle.attrib["angle"]),
            "type": vehicle.attrib["type"],
            "speed": float(vehicle.attrib["speed"]),
            "pos": float(vehicle.attrib["pos"]),
            "lane": vehicle.attrib["lane"],
            "slope": float(vehicle.attrib["slope"])
        }
        data.append(entry)

# DataFrame作成
df = pd.DataFrame(data)

display(df)
```
続けて，特定のレーンについてタイムスペース図を作成します．
```probe_analyze.py
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib

df = pd.read_csv("probe.csv", encoding='shift_jis')
display(df)

print(df["lane"].unique().tolist())  # 特定の車線のみを抽出する前に，車線名のリストを取得する．

df_filtered_lanes = df[df['lane'].isin(["E0_0", "E0.200_0", "E0.300_0", "E0.400_0", ":J20_2_0", ":J20_1_0", ":J21_1_0", ":J21_0_0", ":J22_2_0", ":J22_1_0"])]  # 描画対象の車線のみにする．

plt.figure(figsize=(16, 6))  # 適当なサイズに設定する
plt.scatter(df_filtered_lanes["time"], df_filtered_lanes["y"], s=0.0001)  # 時刻とy座標についてプロットする．マーカーのサイズは適当な値に設定する．
plt.xlabel("時刻 (s)")
plt.ylabel("Y座標")
plt.title("タイムスペース図")
plt.grid(True)
plt.show()
```
描画したタイムスペース図がこちらです．
意外と停滞していませんね...．もう少し設定をいじって詰まりやすくすると面白いかもしれません．

![Time_Space.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017920/1be72c3f-7629-40f6-9b08-51e03d13bf36.png)

## おわりに
今回はミクロ交通流シミュレータSUMOを用いて，直線道路から遠藤駐車場への入庫を想定したシミュレーションを行いました．
今回のシミュレーション設定では，あまり詰まっている様子は見られませんでしたが，都市内では，店舗の入出庫による短時間の閉塞でも多くの車両を停滞させてしまうこともあるでしょう．
色々なネットワーク，色々な需要で試してみるのも面白いと思います．