h_nari @ 熊本市のブログ。電子工作、プログラミング、ゲーム、TV、 政治、インターネットなどに日々の思い付きを、 うだうだ~と書いていきたい。
このブログにはコメント欄を設けておりません。 記事への御意見、ご質問はtwitter @h_nari宛に お願い致します。


アーカイブ


アマゾン・ベストセラー

メタ情報
RSS
Login

ドラレコの軌跡表示

車を買ったものの使う用事があまり無い。 バッテリーが上がるのが嫌なので週1回、半日程度のドライブをしている。 近場の道の駅、観光地などを巡るうち 楽しくなってきて走行コースを地図上で見直したいと 思うようになった。 以前マラソンの練習をしていたころ、 走ったルートを GARMINのランニングウォッチで 見るのが楽しかった。

車載しやすいGPSトラッカーを探すが、 良さげなものがみつからない。 ドライブ・レコーダー(以下ドラレコと記す)が 安いし使いやすい。 どうせ必要なものなので購入することにした。 購入したのは コムテックのZDR036、 アマゾンから 27,055円。 スペーシアに取付け頑張ってケーブルも隠した。 で、ドライブに行ってみた。

ドラレコのSDカードをPCに接続すると コムテック提供の ビューワソフトで映像とともに地図を表示できるが, 地図上の軌跡は30秒刻みで、全体像が見れない。 運行管理ソフト であればコース全体を表示できそうだが、 ZDR036は対象外。 自分でドラレコのファイルからGPS情報を抜き出し、自力で地図上に表示することを 考える。ネットで検索するとやっている人もいるようだ。

AVIファイルからGPS情報を抜き出す

ドラレコのSDカードの中身を調べる。 AVIファイルしかない。 メディアプレイヤー等で開くと動画を再生することができる。 30秒毎にファイルが分かれている。 AVIファイルはコンテナファイルで色んな情報が含まれている のでGPS情報も含まれているのかもしれない。

AVIファイルは RIFFというファイル形式で、これはチャンクと呼ばれるデータの塊で構成されている。チャンクは4バイトの識別子、4バイトの符号なしリトルエンディアンの長さ、可変長データフィールド、パディングから構成される。 長さは可変長データフィールドの長さである。 チャンクの長さが偶数でない場合には1バイトのパディングが追加される。 識別子が'RIFF'と'LIST'のチャンクは可変長データフィールドの中身も 定義されている。先頭は4バイトの識別子で、それ以降は 長さが尽きるまでRIFFのチャンクが続く。 ファイル全体は 識別子RIFFのチャンクとなっている。

これだけわかれば、AVIファイルの構造を表示できる。 ダンプ・プログラムをpythonで作ってみた。

from struct import unpack
from sys import argv
class Riff:
    def __init__(self, name: str, size: int):
        self.name = name
        self.size = size
        self.type = ""
        self.children = []
    def parse(f, pos: int):
        f.seek(pos)
        buf = f.read(8)
        (name, size) = unpack("4sI", buf)
        name = name.decode("UTF-8")
        node = Riff(name, size)
        node.end = pos + 8 + size
        if size % 2:
            node.end += 1
        if name == "RIFF" or name == "LIST":
            f.seek(pos + 8)
            buf = f.read(4)
            node.type = buf.decode("UTF-8")
            pos2 = pos + 12
            while pos2 < node.end:
                child = Riff.parse(f, pos2)
                node.children.append(child)
                pos2 = child.end
        return node
    def dump(self, level: int = 0):
        print(f"{'  ' * level}{self.name} {self.type} size:{self.size}")
        for c in self.children:
            c.dump(level + 1)
if __name__ == "__main__":
    f = open(argv[1], "rb")
    node = Riff.parse(f, 0)
    node.dump()

ドラレコのAVIファイルをダンプさせるとこうなる。

$ python riff2.py data/t240218/NORMAL/Rear/20240218_091403_R_Nor.AVI
RIFF AVI  size:67215100
  LIST hdrl size:440
    avih  size:56
    LIST strl size:116
      strh  size:56
      strf  size:40
    LIST strl size:92
      strh  size:56
      strf  size:16
    LIST strl size:140
      strh  size:56
      strf  size:64
  LIST movi size:67167256
    00dc  size:306740
    01wb  size:4200
    02tx  size:145
    00dc  size:72882
    01wb  size:4200
    02tx  size:145
    01wb  size:4200
    ... 以下略 ...

AVI RIFF File Reference に hdrlやstrh,strfのフォーマットの説明がある。 00dcは圧縮された画像のチャンク、01wbは音声のデータらしい。 ということで 145byteの 02txチャンクが怪しい。 プログラムを修正し、 02txの内容を出力してみる

$ python riff3.py  ../data/t240218/NORMAL/Rear/20240218_091403_R_Nor.AVI
ZDR036:2024-02-18 09:14:03 X: 0.00 Y: 0.00 Z: 0.00 031.0T 12.6V G  32.6847116 N  130.7532066 E 0km/hE:255 M:255 EM:255 DO:255 SA:0 V:100 S:24897k
ZDR036:2024-02-18 09:14:03 X: 0.00 Y: 0.00 Z: 0.00 031.0T 12.6V G  32.6847116 N  130.7532066 E 0km/hE:255 M:255 EM:255 DO:255 SA:0 V:100 S:24897k
ZDR036:2024-02-18 09:14:03 X: 0.00 Y: 0.00 Z:-0.00 031.0T 12.6V G  32.6847116 N  130.7532066 E 0km/hE:255 M:255 EM:255 DO:255 SA:0 V:100 S:24897k
ZDR036:2024-02-18 09:14:03 X: 0.00 Y: 0.00 Z:-0.00 031.0T 12.6V G  32.6847116 N  130.7532066 E 0km/hE:255 M:255 EM:255 DO:255 SA:0 V:100 S:24897k
... 以下略 ...

いろんな情報が出力されている。 緯度経度、加速度、車速、バッテリー電圧等も見える。 これで走行軌跡の緯度、経度が取得できる。

地図への表示

Javascriptのライブラリ leafletを使用すると簡単に地図の表示をすることができる。 地図画像は openstreetmap 国土地理院のものが使える。 GoogleMapも使用できるのだが、 使用して良いかどうかはよくわからない。

地図の表示をWebアプリで作るか、PC上の Electronのアプリで作るか少し悩む。 ドラレコのSDカード上のファイルというかなり大きなファイルを 扱うのでPC上のアプリの方が便利そうだが、 日常使用するにはリンクで起動できるWebアプリの方が使いやすい。 結局、ドラレコのSDカードのファイル操作は別プログラムで処理する ことにして、Webアプリとして作ることにした。

Webアプリのフレームワークには Mojoliciousを使用。 ページのひな型とapiを提供。 実際の処理はtypescriptで行う。 MojoliciousはCGIとしても使用可能なので expressのように常時サーバーを動かしていなくても良い。 低負荷の自分専用のサービスなどに便利だ。 完成した画面を以下に示す。

ドラレコのGPSの情報は画像のフレーム毎に記録されているが 内容は1秒ごと更新されるので間引いて表示している。 それでも1秒ごとに2点で、4時間のドライブだと2×3600×4=28,800点ほどになるが leafletは難なく表示する。遅いと感じることはない。

今後

現状、ドラレコのSDカードからのデータを処理するのに 手作業が多く発生しているが、これを自動化/省力化したい。 また、動画データも地図から指定して再生できるようにしたい。 場所ごとの通過時間はわかっているので、 その時間の動画を再生すればいいのだが、 ひとつの動画ファイルにしてしまうと ダウンロードだけで時間がかかってしまう。 この辺はどうしたらいいのだろうと調べて HLSプロトコルというものを知る。 わりと簡単にできそうなので試してみたい。