コンテンツにスキップ

FX AIベースPRPD分類器の概要

概要

BATCAM FX ファームウェア v1.0.3c より、当社が独自に開発した AI ベースの部分放電分類機能がファームウェアに統合されました。この機能は CUDA および ONNX Runtime を活用し、装置内でリアルタイムに部分放電の種類を分類できるように設計されています。

PRPD(Phase-Resolved Partial Discharge)は、電源位相に対して部分放電信号を解析する部分放電診断手法であり、BATCAM FX の AI 分類器はこの PRPD データを入力として部分放電の種類を判別します。

本ドキュメントでは、本機能の利用者が AI ベースの分類結果をどのように受信し、活用できるかを説明します。 ファームウェアがサポートする分類結果は全 4 種類(Corona、Floating、Surface、Noise)であり、各 LPoint Audio チャンネル(CH0~CH2)ごとに個別の結果が提供されます。

クラス説明
coronaコロナ放電(Corona discharge)タイプ
floating浮遊電極(Floating electrode)放電タイプ
surface表面(沿面、Surface)放電タイプ
noise部分放電ではない背景雑音・信号

分類結果は ROS 2 と WebSocket の 2 つのチャンネルで提供されます。各 LPoint Audio チャンネルについて、サポートする部分放電の種類ごとの logit score を提供し、利用者はこれを基に分類結果を推論できます。

💡 参考: 本ドキュメントは BATCAM FX を基準としています。BATCAM FX2 の AI 機能については デバイスマネージャー — 人工知能 ドキュメントを参照してください。

前提条件

  • ファームウェアバージョン: BATCAM FX ファームウェア v1.0.3c 以上。ROS 2 出力は v1.0.3b で初めて追加され、WebSocket 出力を含む全機能は v1.0.3c より提供されます。詳細な変更履歴は ファームウェアリリースノート を参照してください。

  • バージョンの確認方法: ブラウザで {CAMERA_IP}/setting/status ページにアクセスするか、REST API GET /setting/status のレスポンスの firmware_version 値を確認します。(FX API プレイグラウンド 参照)

  • 受信側の ROS 2 環境: BATCAM FX の ROS 2 機能は ROS 2 Humble Hawksbill をベースとしており、メッセージパッケージ fx-stream-msgs は Humble での使用を前提に、Python 3.10.16、ROS 2 Jazzy(Ubuntu 24.04)でビルド・検証されています。(ROS 概要 参照)

  • ROS_DOMAIN_ID の一致: カメラのデフォルトの ROS_DOMAIN_ID は 123 です。受信側の環境も同じ値を使用しなければトピックが表示されません。(ROS Domain ID 設定 参照)

データ受信

ROS 2 トピック

BATCAM FX は、カメラのハードウェア ID を基準に PRPD 分類結果のトピックを生成します。

項目
トピック名/fx_{hardware_id}/prpd
メッセージタイプfx_stream_msgs/msg/Prpd
メッセージサイズ52 B/メッセージ
平均帯域幅約 220 B/s

Prpd メッセージは、4 つのクラスそれぞれについて、チャンネル別(CH0~CH2)の logit 値を格納した長さ 3 の配列を提供します。

フィールドタイプ説明
noisefloat32[3]チャンネル CH0~CH2 の noise クラスの logit(無次元 score)
coronafloat32[3]チャンネル CH0~CH2 の corona クラスの logit(無次元 score)
floatingfloat32[3]チャンネル CH0~CH2 の floating クラスの logit(無次元 score)
surfacefloat32[3]チャンネル CH0~CH2 の surface クラスの logit(無次元 score)

logit 値は正規化されていない score であり、正・負のいずれも取り得るため、相対的な大きさを基に解釈する必要があります。

ROS 2 トピックの全一覧とメッセージ定義は ROS 概要 のメッセージ定義の項に整理されており、メッセージフォーマットの原本定義は SMI OSS - fx-stream-msgs リポジトリで確認できます。

WebSocket (Protobuf) ストリーム

ファームウェア v1.0.3c より、同じ分類結果が WebSocket ベースの Protobuf ストリームでも提供されます。購読チャンネルで Subscribe ID 3 により Prpd イベントを受信し、BATCAM FX 基準で約 4 Hz で送信されます。

WebSocket の接続・購読・パースの全過程の Python サンプルは Python Protobuf サンプル ドキュメントを参照してください。当該ドキュメントには FX と FX2 の相違点も併せて整理されています。

分類方法

利用者は、各チャンネルについて 4 つのクラス別 logit 値のうち最も高い値を持つ項目(argmax)を選択します。 さらに各結果に対する確率を計算したい場合は、各チャンネルから該当する項目の値を取り出して softmax を適用する必要があります。

チャンネルnoise (logit)corona (logit)floating (logit)surface (logit)分類結果 (argmax)確率 (softmax)
ch0-0.962.98-3.550.30corona0.918 ( 91.8 % )
ch14.485.88-5.45-1.51corona0.802 ( 80.2 % )
ch2-6.54-1.390.730.45floating0.533 ( 53.3 % )

💡 解釈の参考: softmax 確率は 4 つのクラス間の相対的な確信度です。例の ch2 のように最高確率が低く(53.3 %)算出される場合は、floating(0.73)と surface(0.45)の logit の差が小さく、2 つのクラスが競合していることを意味します。分類結果を採用するかどうかの判断基準(しきい値)は応用分野や現場条件によって異なるため、使用するシステムの要件に合わせて定義して適用してください。

Softmax

softmax 関数は logit ベクトルを確率分布に変換します。一般的な数式は次のとおりです:

softmax(xi)=exij=1Kexj\text{softmax}(x_i) = \frac{e^{x_i}}{\sum_{j=1}^{K} e^{x_j}}

ただし、logit 値が過度に大きい、または小さい場合には オーバーフロー(Overflow) または アンダーフロー(Underflow) が発生する可能性があるため、通常は次のような正規化された形で計算します:

softmax(xi)=eximax(x)j=1Kexjmax(x)\text{softmax}(x_i) = \frac{e^{x_i - \max(x)}}{\sum_{j=1}^{K} e^{x_j - \max(x)}}

下記の式を Python に移す場合は、以下の例のようになります:

Python 実装

import numpy as np
def softmax(logits):
logits = np.array(logits)
exps = np.exp(logits - np.max(logits)) # 安定化した softmax
return exps / np.sum(exps) # 確率値を返す

Common Lisp 実装

(defun softmax (logits)
"リストまたはベクトル LOGITS を受け取り softmax 確率ベクトルを返す"
(let* ((vec (coerce logits 'vector))
(maxval (reduce #'max vec))
(exps (map 'vector (lambda (x) (exp (- x maxval))) vec))
(sum (reduce #'+ exps)))
(map 'vector (lambda (x) (/ x sum)) exps)))

完全なサンプル: rclpy 購読ノード

以下は、PRPD トピックを購読してチャンネル別の分類結果と確率をログに出力する完結型の Python(rclpy) ノードです。1 つのファイル(prpd_subscriber.py)として保存し、そのまま実行できます。

💡 参考: コード内のトピック名 /fx_276730383020104/prpd は例であり、使用中のカメラのハードウェア ID に合わせて変更する必要があります。実際のトピック名は ros2 topic list コマンドで確認できます。

import numpy as np
import rclpy
from rclpy.node import Node
from fx_stream_msgs.msg import Prpd
# Prpd.msg のフィールド順と同一のラベル定義
LABELS = ['noise', 'corona', 'floating', 'surface']
def softmax(logits):
logits = np.array(logits)
exps = np.exp(logits - np.max(logits)) # 安定化した softmax
return exps / np.sum(exps)
class PrpdSubscriber(Node):
def __init__(self):
super().__init__('prpd_subscriber')
# 使用中のカメラのハードウェア ID に合わせてトピック名を変更してください。
self.subscription = self.create_subscription(
Prpd,
'/fx_276730383020104/prpd',
self.listener_callback,
10)
def listener_callback(self, msg: Prpd):
results = []
# 3 チャンネル (CH0 ~ CH2) 基準
for ch in range(3):
logits = [
msg.noise[ch],
msg.corona[ch],
msg.floating[ch],
msg.surface[ch],
]
probs = softmax(logits)
idx = int(np.argmax(probs))
results.append({
'channel': f'CH{ch}',
'label': LABELS[idx],
'probability': round(float(probs[idx]), 3),
})
# ログ出力
for result in results:
self.get_logger().info(
f"{result['channel']}: {result['label']} "
f"({result['probability'] * 100:.1f}%)"
)
def main(args=None):
rclpy.init(args=args)
node = PrpdSubscriber()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()

実行方法

ROS 2 環境と、fx-stream-msgs メッセージパッケージをビルドしたワークスペースをソースした上で実行します。カメラと同じ ROS_DOMAIN_ID(デフォルト値 123)を使用する必要があります。

Terminal window
source /opt/ros/humble/setup.bash
# fx-stream-msgs メッセージパッケージをビルドしたワークスペースも併せてソースします。
export ROS_DOMAIN_ID=123
python3 prpd_subscriber.py

想定される出力

上記「分類方法」の例の logit 値を受信したと仮定すると、次のようなログが出力されます:

[INFO] [1748324429.123456789] [prpd_subscriber]: CH0: corona (91.8%)
[INFO] [1748324429.123512345] [prpd_subscriber]: CH1: corona (80.2%)
[INFO] [1748324429.123556789] [prpd_subscriber]: CH2: floating (53.3%)

Common Lisp 参考実装

同じ分類ロジック(softmax + argmax)の Common Lisp 参考実装です。メッセージ受信部は使用する環境に合わせて接続する必要があり、以下のコードは msg(noise corona floating surface) それぞれ長さ 3 のベクトルを保持していることを前提としています。

; Define labels for the discharge types
(defparameter *labels* #("noise" "corona" "floating" "surface"))
; Softmax function
(defun softmax (logits)
"リストまたはベクトル LOGITS を受け取り softmax 確率ベクトルを返す"
(let* ((vec (coerce logits 'vector))
(maxval (reduce #'max vec))
(exps (map 'vector (lambda (x) (exp (- x maxval))) vec))
(sum (reduce #'+ exps)))
(map 'vector (lambda (x) (/ x sum)) exps)))
(defun classify-channel (noise corona floating surface)
"チャンネル 1 つのログ値を受け取りラベル/確率を返す"
(let* ((logits (vector noise corona floating surface))
(probs (softmax logits))
(idx (position (reduce #'max probs) probs)))
(list :label (aref *labels* idx)
:probability (aref probs idx))))
(defun listener-callback (msg)
"msg は (noise corona floating surface) それぞれ長さ 3 のベクトルと仮定"
(loop for ch from 0 below 3
for result = (classify-channel
(aref (getf msg :noise) ch)
(aref (getf msg :corona) ch)
(aref (getf msg :floating) ch)
(aref (getf msg :surface) ch))
do (format t "CH~A: ~A (~,1F%)~%"
ch
(getf result :label)
(* (getf result :probability) 100))))

トラブルシューティング (Troubleshooting)

  • prpd トピックが表示されない場合:

    • ファームウェアバージョンが v1.0.3c 以上であるかを確認します。ブラウザで {CAMERA_IP}/setting/status にアクセスするか GET /setting/statusfirmware_version 値を確認し、旧バージョンの場合は Web アップデート または シェルアップデート でファームウェアを更新します。

    • 受信側の ROS_DOMAIN_ID がカメラと一致しているかを確認します。カメラのデフォルト値は 123 であり、ファームウェア 1.0.3 以上では REST API(/ros/domain)で照会・変更できます。(FX API プレイグラウンドROS Domain ID 設定 参照)

    • ros2 topic list/fx_{hardware_id}/prpd トピックが表示されるかを再度確認します。

  • ModuleNotFoundError: No module named 'fx_stream_msgs' エラーが発生する場合:

    • fx-stream-msgs メッセージパッケージをビルドしたワークスペースがソースされていない状態です。ワークスペースの setup.bash をソースした上で再度実行します。
  • softmax の計算で overflow/underflow の警告が発生する場合:

    • logit 値をそのまま指数化せず、上記「Softmax」の項の正規化された形(np.max を引いてから指数化)を使用します。
  • 最高確率が低く算出される場合:

    • エラーではなく、2 つ以上のクラスの logit が近く競合していることを意味します。上記「分類方法」の解釈の参考を確認してください。

関連ドキュメント