Kainのアイコン画像
Kain 2024年09月20日作成 (2024年10月31日更新)
製作品 製作品 閲覧数 189
Kain 2024年09月20日作成 (2024年10月31日更新) 製作品 製作品 閲覧数 189

ラズパイで疑似HUDを作る

ラズパイで疑似HUDを作る

概要

車速、エンジンの回転数、そのほかの車両の詳細情報を OBD2 診断機から取得し、それをRaspberry Pi のディスプレイに表示する。表示内容は左右反転させ、フロントガラスへ投影させることで疑似的なヘッドアップディスプレイ(HUD)を表現する。
超音波距離センサを車両後方に取り付け、センサから算出した障害物との距離を表示させ、距離が近づくと、音を再生させて聴覚的に危険を知らせる。
フロントガラスへ投影することで、視界の移動軽減や死角となる後方の障害物との距離の把握が容易になる。

主な動作概要を視覚化するとこの通りとなる。
画像での動作概要
動作概要に関する追記:
画像内では超音波距離センサのデータをBluetoothで送信するような表記になっているが、実際は超音波距離センサは配線を通してRaspberry Piのピンに接続するものとする。

アイデア詳細

今回制作していくシステムの基本機能は以下の通り

  • OBD2 診断機から車両情報を取得 →  ディスプレイに表示
  • 超音波距離センサから距離を算出 →  ディスプレイに表示
  • ディスプレイ表示内容を反転   →  フロントガラスへ投影
  • アラートサウンドを再生      →  車両の AUX 端子に接続、車内スピーカーから再生。

ソナー機能に関しては、ディスプレイ上に障害物との距離を表示して視覚化、障害物に近づくにつれて画面表示やアラートサウンドを再生してドライバーへ接近していることを知らせる。スピーカーに関しては、車両を AUX で接続し、車内スピーカーから音を再生させる。
また、Raspberry Pi の駆動電源に関しては、車の 12V アクセサリーソケットにアダプタを接続してあるので、そこから USB-A to C ケーブルをRaspberry Pi本体に接続する。

使用パーツ

今回の制作にあたり、以下のパーツおよびラズパイを使用する。

  • Raspberry Pi 4
  • 超音波距離センサ
  • 7インチディスプレイ
  • OBD2診断機(ELM327)
  • 各種ケーブル(AUX、USB-A to C、ジャンパ線など)

GUIレイアウト作成

まずは仮ではあるが、デザインツールで仮レイアウトを作成した。
今回はこの仮レイアウトをベースに、GUI面は仕上げていく。
Figmaで作成した仮レイアウト

実際にPythonで再現

先ほど作ったGUIレイアウトをもとに、Pythonで再現した。
Pythonでのレイアウト作成

OBD診断機を接続

Python(Pygame)でのGUIレイアウト制作も終え、OBD2と接続した場合に対するプログラムの準備はできたので、次は実際にELM327とラズパイをBluetoothで接続する。

今回は、以下の車両のOBDコネクタに診断機を接続していく。

メーカー トヨタ
車種 30系プリウス 前期(ZVW30) - 2009年モデル
燃料タイプ ガソリンハイブリッド

OBD2ソケットへELM327を差し込み(30プリウスの場合は運転席の右下当たり)、ラズパイのターミナル上でbluetoothctlコマンドを使用してペアリングとデバイスの信頼を行う。
bluetoothctl上で以下のコマンドを実行する。

bluetoothctlコマンド

power on //Bluetoothの有効化 scan on //付近のデバイスを探す

これを実行後、周辺のデバイスと、デバイスのMACアドレスが表示される。
今回使用するELM327のデバイス名は「OBDII」であるため、該当するデバイス名のMACアドレスが表示されるまで待つ。

デバイス表示例

[NEW] Device AA:BB:CC:11:22:33 OBDII

ここで表示されたOBDIIのMACアドレスを使用してペアリングする。

ペアリング

pair AA:BB:CC:11:22:33

ここで、PINコードの要求がある(場合による)ので、コードを入力してペアリングする。(私の場合は1234)
接続成功後、もう一度pairコマンドを実行しておく。
ペアリング後、デバイスを信頼する。

デバイスの信頼

trust AA:BB:CC:11:22:33

ペアリングと信頼が問題なくでき次第、次は接続したデバイスをrfcommコマンドにてパラレル通信として扱うようにする。
今回は、rfcomm0ポートに割り当てることにする。

rfcommポート割り当て

sudo rfcomm bind /dev/rfcomm0 AA:BB:CC:11:22:33

設定後、sudo rfcomm -i showで確認したところ、実際にrfcomm0に割り当てられていた。
全体の通信準備はこれで整ったので、後は実際にパラレル通信で値を取得できるかどうか検証していく。
今回はscreenコマンドを使用して、通信できるかの検証を行う。(screenコマンドは別途インストールが必要)

screenコマンド

screen /dev/rfcomm0

コマンド実行後ATIと入力して送信(Enter)後、ELM327のバージョン情報が返ってきたら通信成功となる。
ここからは、Pythonで実際に車速などを取得していく。

#Pythonで情報取得
Pythonで、以下のプログラムを実行すると車速やエンジン回転数を取得できる。

OBDIIから情報を取得するPythonプログラム

import obd import time connection = obd.OBD("/dev/rfcomm0") # Bluetooth接続のポート cmd_speed = obd.commands.SPEED # 車速 cmd_rpm = obd.commands.RPM # エンジン回転数 while True: # 速度を取得して表示 response_speed = connection.query(cmd_speed) print("車速: {} km/h".format(response_speed)) # エンジン回転数を取得して表示 response_rpm = connection.query(cmd_rpm) o = response_rpm.value.magnitude print(o) print("エンジン回転数: {} RPM".format(response_rpm.value)) time.sleep(1) # 1秒ごとに更新

このプログラムで取得に成功したことが確認できたので、先ほど作ったGUIプログラムでこの数値を表示させるようプログラムを修正した。

ELM327との相性問題

ELM327には、メーカーなどによって当たり外れの個体差が激しく、相性の問題もよくあるようだ。
私が最初に購入、使用したELM327でもラズパイとの通信で問題が起きた。AndroidのOBDアプリでは通信できているが、もしかするとラズパイとの相性の問題があるのかもしれない。(記事内で動作したとの記載のあったものを購入したが、ラズパイとの相性にも個体差が存在する可能性がある)
OBD診断機とRaspberryPiではBluetoothのペアリングとパラレル通信の設定まではできているようだが、コマンドを送信してもELM327からの応答がなくエラーとなり、正しく通信できていない。
Bluetoothで接続する方法は他にもあり、試してみたがそれでも同様の症状であった。

別メーカー品で検証

ELM327の相性問題も可能性としてあるため、ほかの記事で動作が確認されていた他メーカーのELM327を急遽購入した。
以下、今回購入したELM327のリンク↓
https://www.amazon.co.jp/dp/B07RNHZS44?ref=cm_sw_r_apin_dp_ABRRMEHQ499K0EYKZ57Z&ref_=cm_sw_r_apin_dp_ABRRMEHQ499K0EYKZ57Z&social_share=cm_sw_r_apin_dp_ABRRMEHQ499K0EYKZ57Z&starsLeft=1&skipTwisterOG=1
結果として、Bluetoothペアリング、パラレル通信の設定から、実際の通信まで行うことができた。前回のものと異なり、コマンドを実行後に応答があり、車速やエンジン回転数を取得することが可能となった。
注意:
今回私の方ではこの製品で通信することができたが、先述の通りELM327は個体差が激しいようなので、あくまで参考程度で。

距離測定

主にリバースモード時に、超音波距離センサを使用して障害物との距離を測るようにした。
使用した部品はHC-SR04を2つ、それぞれ異なるブレッドボードに接続した。
超音波距離センサを利用した距離測定は、以下のプログラム制御で行える。
(VCC → ラズパイの5V、GND → GND、TRIG → GPIO23、ECHO → GPIO24へ接続したとする)

超音波距離センサでの距離測定

import RPi.GPIO as GPIO import time # ピンの設定 TRIG = 23 # TRIGピンに接続したGPIOピン番号 ECHO = 24 # ECHOピンに接続したGPIOピン番号 # GPIOの初期設定 GPIO.setmode(GPIO.BCM) GPIO.setup(TRIG, GPIO.OUT) GPIO.setup(ECHO, GPIO.IN) def measure_distance(): # TRIGをLowにして安定させる GPIO.output(TRIG, False) time.sleep(0.5) # TRIGを0.01msだけHighにする GPIO.output(TRIG, True) time.sleep(0.00001) GPIO.output(TRIG, False) # ECHOがHighになるまでの時間を計測 while GPIO.input(ECHO) == 0: pulse_start = time.time() # ECHOがLowになるまでの時間を計測 while GPIO.input(ECHO) == 1: pulse_end = time.time() # 距離を計算 pulse_duration = pulse_end - pulse_start distance = pulse_duration * 17150 # 音速の1/2 distance = round(distance, 2) # 小数第2位までにする return distance try: while True: dist = measure_distance() print(f"距離: {dist} cm") time.sleep(1) # 1秒ごとに測定 except KeyboardInterrupt: print("測定を終わります") GPIO.cleanup()

実際の動作画面

全ての工程を終え、実際にプログラムを動作させた。
車速とエンジン回転数表示

リバース時の距離表示
距離が近すぎる場合の表示
※シフト情報を取得して画面切り替えをしたかったが、シフト情報はCAN通信が必要となり、シフト情報に関するアドレスがわからなかったため、今回はトグルスイッチを押して切り替えするようにした。

また、実際に走行テストを行い、車速と回転数が更新されることを確認した。
走行中の車速と回転数
※スマホを固定したうえで自動撮影

まとめ

今回、最終的にプログラムの動作や車両情報の取得には成功したものの、OBDとの通信問題で時間を大幅に消費してしまった。
そのため、実際に車のフロントガラスへ投影することと、サウンドを実装する作業が間に合わず、やむを得ず省略することとなった。しかし、動作画面のスクリーンショットをスマートフォンで表示し、それを試しに投影した際は想定以上には奇麗に投影できた。ただ、今回試験的に投影した際、表示内容が2重になってしまった。これは、HUDで投影する際の課題の一つである二重像というものであり、読み取ることはできるものの鮮明さには欠けると感じた。この課題には、特殊ガラスで対応するのが一般的とされるが、近年では特殊ガラス不要で解決させる策も提案されているようだ。今後機会があれば、私もこの課題解決に挑戦してみたい。

参考文献

Kainのアイコン画像
うぁ~
ログインしてコメントを投稿する