[!NOTE]
2026-09-23 現行GitHub同期メモUPS監視は初期実装から更新されています。現行
mainではups_guard.pyがINA219を直接読み、5秒周期で監視します。通常の外部電源喪失は 電流 <= -0.50 A が20秒継続、非常停止は 7.50 V以下、INA219読取エラーは 30秒継続でshutdown要求を出します。現在の状態は/run/ronv/ups-status.jsonに保持し、Web UIからも参照します。
RON-V 連載リンク
← 前の記事:#19 — スマホから操作するWeb UI
→ 次の記事:#21 — AtomS3RのCAN・IMU・Shutdown制御
RON-Vは車載環境で使用するため、車両側の給電が途切れたときにRaspberry Pi 5を突然電源断させないことが重要です。
現在はWaveshare UPS HAT (B) のINA219を使って、
外部電源あり
↓
通常動作
外部電源喪失
↓
UPS放電を検出
↓
一定時間確認
↓
安全shutdown要求
という構成にしています。
20.1 INA219
UPS HATのINA219はI2Cで読みます。
RON-Vの現行実装では、
I2C bus : 1
address : 0x42
です。
確認例:
ls /dev/i2c-*
sudo i2cdetect -y 1
0x42 が見えればINA219へアクセスできます。
20.2 Python依存関係
INA219アクセスには smbus2 を使います。
現在は requirements.txt に、
smbus2==0.6.1
を固定しています。
新規環境では個別pipではなく、
cd /home/ronv/ronv
/home/ronv/.venvs/ronv/bin/pip install \
-r requirements.txt
で揃えます。
20.3 現行ups_guard.py
監視本体は、
app/power/ups_guard.py
です。
現在は ups_guard.py 自身がINA219を直接読みます。
監視に使う主な定数は、
BUS = 1
ADDR = 0x42
DISCHARGE_A = -0.5
EMERGENCY_V = 7.5
POWER_LOSS_CONFIRM_SEC = 20
UPS_READ_ERROR_CONFIRM_SEC = 30
です。
初期実装ではUPS値をSQLiteへ連続記録する方式も試しましたが、現在の安全停止判定はSQLite記録を前提にしていません。
20.4 5秒周期のsystemd timer
現行timerは、
[Unit]
Description=RON-V UPS Guard Timer
[Timer]
OnBootSec=5s
OnUnitActiveSec=5s
AccuracySec=500ms
Persistent=false
Unit=ronv-ups-guard.service
[Install]
WantedBy=timers.target
です。
serviceはoneshotです。
[Service]
Type=oneshot
User=ronv
RuntimeDirectory=ronv
RuntimeDirectoryPreserve=yes
ExecStart=/home/ronv/.venvs/ronv/bin/python /home/ronv/ronv/app/power/ups_guard.py
約5秒ごとに短時間起動し、UPS状態を判定して終了します。
20.5 通常状態
外部電源があり、放電条件に入っていないときは、
NORMAL
です。
例:
NORMAL 8.188 V 0.120 A UPS state normal
電流値がプラス側なら、今回の実機ではUPSが放電していない状態として観測されます。
20.6 外部電源喪失の判定
車両側電源が切れると、UPSがRaspberry Piと周辺機器へ給電し始め、INA219電流が放電方向へ変化します。
現行条件は、
current <= -0.50 A
です。
1回だけ負になる瞬間ではshutdownしません。
current <= -0.50 A
↓
POWER_LOSS_SUSPECTED
↓
20秒継続確認
↓
SHUTDOWN_PENDING
とします。
開始時刻は、
/run/ronv/ups-power-loss.since
へ保持します。
電流が正常側へ戻ればmarkerを削除し、判定を解除します。
20.7 20秒確認
POWER_LOSS_CONFIRM_SEC は現在20秒です。
実機試験では、例えば、
POWER_LOSS_SUSPECTED ... -1.030 A; 5.5/20s
POWER_LOSS_SUSPECTED ... -0.973 A; 10.5/20s
POWER_LOSS_SUSPECTED ... -1.079 A; 15.8/20s
SHUTDOWN_PENDING ... -1.077 A; 21.3s (confirm 20s)
という流れを確認しました。
つまり瞬間的な負荷変動ではなく、継続した放電を見て外部電源喪失と判定します。
20.8 非常低電圧
通常の20秒確認とは別に、
voltage <= 7.50 V
なら、
SHUTDOWN_PENDING
reason = emergency_low_voltage
とします。
これは通常の外部電源喪失判定が成立しない場合にも深い放電を続けないための保険です。
20.9 INA219読取エラー
電源喪失やI2C異常によってINA219そのものを読めない場合も考慮します。
現行実装では、
UPS read error
↓
/run/ronv/ups-read-error.since
↓
30秒継続
↓
SHUTDOWN_PENDING
です。
1回のI2Cエラーで即shutdownせず、
UPS_READ_ERROR_CONFIRM_SEC = 30
で確認します。
20.10 runtime status
現行実装では最新UPS状態を、
/run/ronv/ups-status.json
へatomic writeします。
内容は概ね、
{
"timestamp_jst": "...",
"status": "NORMAL",
"voltage_v": 8.188,
"current_a": 0.120,
"power_w": 0.983,
"percent": 91.2,
"detail": "UPS state normal"
}
です。
このファイルはWeb UIからも読み込まれます。
RON-V Web
↓
UPS Battery
↓
Voltage / Current / Power / SOC / Guard
として確認できます。
SOCは電圧からの簡易推定であり、クーロンカウンタによる厳密な残容量ではありません。
20.11 shutdown request
ups_guard.py 自身にroot権限は与えません。
shutdown条件を満たすと、
/run/ronv/ups-shutdown.request
を作成します。
systemd path unit:
[Path]
PathChanged=/run/ronv/ups-shutdown.request
Unit=ronv-ups-shutdown.service
が検出し、
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl poweroff
を実行します。
つまり、
ronv user
ups_guard
↓ request file
root/systemd
↓
poweroff
と権限を分離しています。
20.12 テストモード
実際にshutdownさせず、判定ロジックだけ試すために環境変数を用意しています。
RONV_UPS_TEST_VOLTAGE
RONV_UPS_TEST_CURRENT
RONV_UPS_TEST_ELAPSED_SEC
例えば、
RONV_UPS_TEST_VOLTAGE=8.0 \
RONV_UPS_TEST_CURRENT=-0.8 \
RONV_UPS_TEST_ELAPSED_SEC=21 \
/home/ronv/.venvs/ronv/bin/python \
/home/ronv/ronv/app/power/ups_guard.py
で外部電源喪失条件を模擬できます。
テストモードでは実shutdown requestを生成しません。
20.13 現行の停止条件
現在の条件をまとめると、
A. 通常停止
current <= -0.50 A
↓
20秒継続
↓
SHUTDOWN_PENDING
B. 非常停止
voltage <= 7.50 V
↓
SHUTDOWN_PENDING
C. センサー異常
INA219 read error
↓
30秒継続
↓
SHUTDOWN_PENDING
です。
バッテリーを限界まで使うのではなく、
外部電源を失ったことを早めに検出し、ファイルシステムが正常なうちに停止する
ことを優先しています。
20.14 状態確認
現在状態:
cat /run/ronv/ups-status.json
timer:
systemctl status \
ronv-ups-guard.timer \
--no-pager
直近ログ:
journalctl \
-u ronv-ups-guard.service \
-n 100 \
--no-pager
shutdown path:
systemctl status \
ronv-ups-shutdown.path \
--no-pager
20.15 旧UPS loggerについて
repoには開発履歴として、
app/power/ups_logger.py
app/power/ups_report.sh
も残っています。
これらはSQLiteへUPS値を記録した初期試験のコードです。
ただし、現行の安全shutdown制御は ups_guard.py とruntime status fileを正とします。
現在のGitHubには ronv-ups-log.timer のようなlogger timerは存在せず、UPS安全監視は ronv-ups-guard.timer が担当します。
20.16 shutdownログ
安全停止後の解析にはsystemd journalとshutdownログを利用します。
確認例:
journalctl \
-u ronv-ups-guard.service \
-u ronv-ups-shutdown.service \
--no-pager
journalctl --list-boots --no-pager
実機試験では、放電継続判定のあと ronv-ups-shutdown.service が起動し、正常にpoweroffへ移行することを確認しました。
20.17 POWER_OFF_ON_HALT
Raspberry Pi 5 EEPROMの POWER_OFF_ON_HALT は、UPS HATや車載電源の再投入方式と組み合わせて考える必要があります。
「shutdown後に消費を減らす」ことと、
車両電源OFF
↓
shutdown
↓
次回車両電源ON
↓
自動起動
を両立する必要があるためです。
RON-Vではソフトウェア上のsafe shutdownと、実際の電源再投入挙動を分けて評価します。
まとめ
現在のRON-V UPS監視は、
INA219
↓
ups_guard.py
↓ 5秒周期
NORMAL
/ POWER_LOSS_SUSPECTED
/ SHUTDOWN_PENDING
↓
/run/ronv/ups-status.json
↓
Web UI
shutdown条件成立
↓
/run/ronv/ups-shutdown.request
↓
systemd.path
↓
systemctl poweroff
です。
初期の「1分ごとにUPS値をSQLiteへ保存して低電圧を待つ」方式から、
外部電源喪失を放電電流で早期検出し、20秒確認後に安全停止する
方式へ発展しました。
車載ノードでは、UPS容量を最後まで使うことより、SQLite・画像・ファイルシステムを壊さず正常停止することを優先します。