ElcamyTECH
Articles
🚌

SOARSを使ったエージェントベースシミュレーションでAI交通シミュレータを作ってみた

TechABSSOARSOTP交通シミュレーション2026/04/24

はじめに

弊社(株式会社Elcamy)は、公的研究機関の支援を受けたプロジェクトの一環として、南西諸島のある離島を舞台にした交通シミュレーションを実施しました。

本記事では、シミュレーションの手順と実施結果を紹介します。

エージェントベースシミュレーション(ABS)の基本については、こちらの記事をご覧ください:エージェントベースモデル入門

プロジェクト概要

本シミュレーションは、公的研究プロジェクトの活動報告として実施したものです。対象エリアの地理情報・公共交通データといったオープンデータを活用し、1,000人のエージェントがバスを乗り継いで移動する様子をコンピュータ上で再現しました。

項目内容
対象エリア南西諸島の離島部
エージェント数1,000人
データソースOpenStreetMap(地理情報)・公共交通機関の時刻表(オープンデータ)
シミュレーション内容ランダムな出発地・目的地に基づく交通移動の再現

使用したツール

SOARS(Social Simulation Platform)

エージェントの行動を定義・実行するシミュレーションエンジンです。各エージェントの行動ルール(何時に出発し、どのバスに乗るか)をプログラムし、1,000人分を一斉に動かします。

OpenTripPlanner v2(OTP)

オープンソースの経路探索エンジンです。Googleマップのように「出発地から目的地まで公共交通でどう行くか」を計算でき、APIとして呼び出せるため、大量のエージェントに対して一括で経路計算を行うことができます。

Kepler.gl

地図上にシミュレーション結果を可視化するオープンソースツールです。エージェントがどのルートをたどったかを、時間の経過とともにアニメーションで確認できます。

OpenStreetMap

オープンデータの地理情報プラットフォームです。対象離島の道路・地形データをシミュレーションの地図情報として使用しました。

メモ

本記事で示すコードはプライベートリポジトリの実装をもとにした説明用のサンプルです。実装の詳細については省略しています。

シミュレーションの手順

1. データの準備

シミュレーションには2種類のデータが必要です。

  • 地理情報: OpenStreetMap から対象離島の道路・地形データ(.osm.pbf 形式)を取得します。Geofabrik では日本全体の PBF ファイルを無償でダウンロードでき、BBBike Extract では任意の範囲を切り出して取得することもできます。
  • 交通情報: 対象離島のバス時刻表データを GTFS(General Transit Feed Specification)形式で取得します。地域の公共交通 GTFS データは OTTOP などのオープンデータプラットフォームから入手できます。全国のデータは GTFSデータリポジトリ でも検索できます。

OTPはこの2つのデータを読み込んで起動します。起動後は REST API 経由で経路計算をリクエストできます。

2. ODリストの生成

各エージェントに出発地(Origin)と目的地(Destination)をランダムに割り当て、ODリストをCSVとして生成します。座標は対象エリアの緯度経度の範囲内からランダムにサンプリングします。

java
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
 
public class OdListGenerator {
    private static final double LAT_MIN = 24.30, LAT_MAX = 24.55;
    private static final double LON_MIN = 124.10, LON_MAX = 124.35;
    private static final int NUM_AGENTS = 1000;
 
    public static void main(String[] args) throws IOException {
        Random rng = new Random();
        try (FileWriter fw = new FileWriter("od_list.csv")) {
            fw.write("agent_id,origin_lat,origin_lon,dest_lat,dest_lon,depart_time\n");
            for (int id = 0; id < NUM_AGENTS; id++) {
                double oLat = LAT_MIN + rng.nextDouble() * (LAT_MAX - LAT_MIN);
                double oLon = LON_MIN + rng.nextDouble() * (LON_MAX - LON_MIN);
                double dLat = LAT_MIN + rng.nextDouble() * (LAT_MAX - LAT_MIN);
                double dLon = LON_MIN + rng.nextDouble() * (LON_MAX - LON_MIN);
                int hour = 7 + rng.nextInt(14);
                int min  = rng.nextInt(60);
                fw.write(String.format(
                    "%d,%.6f,%.6f,%.6f,%.6f,%02d:%02d:00%n",
                    id, oLat, oLon, dLat, dLon, hour, min
                ));
            }
        }
    }
}

生成されたCSVの各行が1エージェントの移動計画の入力になります。

3. OTPへのクエリリクエスト

ODリストを読み込み、各エージェントの経路をOTPのREST APIに問い合わせます。OTPは地図データと時刻表をもとに、最適な公共交通経路(乗り換えを含む)を返します。

java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
 
public class OtpClient {
    private static final String OTP_BASE_URL = "http://localhost:8080";
    private final HttpClient http = HttpClient.newHttpClient();
 
    public String queryRoute(
            double originLat, double originLon,
            double destLat,   double destLon,
            String date,      String time) throws Exception {
 
        String url = String.format(
            "%s/otp/routers/default/plan"
                + "?fromPlace=%f,%f&toPlace=%f,%f"
                + "&date=%s&time=%s&mode=BUS,WALK&numItineraries=1",
            OTP_BASE_URL,
            originLat, originLon,
            destLat,   destLon,
            date, time
        );
 
        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .GET()
            .build();
 
        // レスポンスはJSON文字列。各エージェントの legs(移動区間)を取り出して処理する
        HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
        return res.body();
    }
}

レスポンスの legs には、各移動区間のモード(BUS/WALK)・出発時刻・到着時刻・バス停名・座標が含まれます。これをもとにエージェントの行動スケジュールを組み立てます。

4. SOARSでのエージェント実装

OTPから取得した経路情報をもとに、SOARSでエージェントの行動を定義します。SOARSでは「スポット(場所)」と「エージェント」をルールで結びつけ、各時刻にエージェントを移動させます。

java
// エージェントクラス: OTPの経路情報を保持する
public class TransportAgent extends TAgent {
    private List<Leg> legs;   // OTPから取得した移動区間リスト
    private int currentLeg = 0;
 
    public TransportAgent(long id, List<Leg> legs) {
        super(id);
        this.legs = legs;
    }
 
    public Leg getNextLeg() {
        return legs.get(currentLeg);
    }
}
 
// ルール: 各ステップで次の区間に移動するかを判断する
public class MoveToNextStopRule extends TAgentRule {
    @Override
    public void doIt(TTime currentTime, TAgent agent, ...) {
        TransportAgent ta = (TransportAgent) agent;
        Leg next = ta.getNextLeg();
 
        // 次の区間の出発時刻になったらスポットを移動
        if (currentTime.isAfterOrEqual(next.departureTime)) {
            agent.moveTo(next.toSpot);
            ta.currentLeg++;
        }
    }
}

この仕組みにより、1,000人のエージェントが各自の時刻表に従って独立に動作します。

5. 結果の出力と可視化

各エージェントの移動ログはCSVとして出力されます。各行はエージェントがある時刻にどの座標にいたかを記録しています。

csv
agent_id, timestamp,          lat,      lon,      mode
0,        2024-01-15T08:05:00, 24.3421, 124.1567, WALK
0,        2024-01-15T08:12:00, 24.3398, 124.1623, BUS
0,        2024-01-15T08:45:00, 24.4607, 124.2133, WALK
...

このCSVをKepler.glに読み込むと、時間軸に沿ったアニメーションとして可視化できます。

シミュレーション結果

以下の動画は、1,000人のエージェントが対象エリア内を公共交通機関で移動している様子をKepler.glで可視化したものです。

各エージェントの動きを地図上で追うことで、交通の流れや混雑ポイントを直感的に把握できます。

まとめ

今回のシミュレーションでは、対象エリアのオープンデータと既存のオープンソースツールを組み合わせることで、実際に現地調査をしなくても公共交通の利用状況をコンピュータ上で再現することができました。

このような技術は、以下のような場面で活用できます:

  • 交通インフラの計画立案 — 新路線やバス停の設置効果を事前に検証
  • 観光地の混雑対策 — 観光客の動線をシミュレーションし、分散策を検討
  • 防災計画 — 避難経路の有効性を大規模に確認
  • 物流・配送の最適化 — 配送ルートや拠点配置の改善

株式会社Elcamyでは、こうしたシミュレーション技術を活用した課題解決のお手伝いをしています。「こんなことがやりたい」というご相談があれば、ぜひお気軽にお問い合わせください。

Elcamyについて

AI・データエンジニアリング・クラウドを中心に、技術で事業課題を解決しています。 お気軽にご相談ください。