HumansXドキュメントセンター 公式サイトへ ↗

主要機能の紹介

概要

インテリジェント顔追跡システムは、先進的なコンピュータビジョン技術と高度なパンチルト制御アルゴリズムを統合し、顔検出から自動追跡までを一貫して実現するシステムです。YOLO11 ディープラーニングモデルと各種のインテリジェントアルゴリズムをベースに、高精度・低遅延なリアルタイム顔追跡機能を提供します。

顔検出システム

YOLO11 検出エンジン

技術的特長:

  • モデルアーキテクチャ:YOLO11n 量子化モデル(RDK X5 プラットフォーム向けに最適化)
  • 検出対象:顔検出に特化し、複数人の顔が映るシーンにも対応
  • 入力サイズ:320 × 320 ピクセル、NV12 形式で入力
  • 検出精度:信頼度しきい値 0.6、IoU しきい値 0.45(変更可能)
  • 処理速度:約 30fps のリアルタイム検出(BPU ハードウェアアクセラレーション)
  • 検出距離:0.5~5 メートルの範囲で顔検出が可能

検出プロセス:

  1. 画像の前処理:BGR 形式から NV12 形式へ変換し、320 × 320 にリサイズ
  2. モデル推論:Horizon BPU 上でハードウェアアクセラレーション推論を実行
  3. 後処理:非最大値抑制(NMS)、座標の逆量子化
  4. 結果の出力:バウンディングボックス座標、信頼度、顔 ID 情報

主要クラスの設計:

class YOLO11_FaceDetect:
    def __init__(self, model_path, conf_thres=0.6, iou_thres=0.45):
        self.dnn_handle = load_model(model_path)
        self.conf_thres = conf_thres
        self.iou_thres = iou_thres
        self.input_shape = (320, 320)

    def detect(self, frame) -> List[Detection]:
        # 1. 画像の前処理
        input_tensor = self.preprocess(frame)

        # 2. BPU 推論
        outputs = self.forward(input_tensor)

        # 3. 後処理
        detections = self.postprocess(outputs, frame.shape)

        return detections

    def preprocess(self, frame):
        """BGR から NV12 形式への前処理"""
        # モデルの入力サイズにリサイズ
        resized = cv2.resize(frame, self.input_shape)

        # BGR を YUV420(NV12)に変換
        yuv = cv2.cvtColor(resized, cv2.COLOR_BGR2YUV_I420)

        return yuv

    def postprocess(self, outputs, original_shape):
        """後処理:NMS + 座標の復元"""
        # モデル出力を解析
        boxes, scores, class_ids = self.decode_output(outputs)

        # 非最大値抑制
        indices = cv2.dnn.NMSBoxes(boxes, scores,
                                   self.conf_thres, self.iou_thres)

        # 座標を元画像のサイズに復元
        detections = []
        if len(indices) > 0:
            for i in indices.flatten():
                box = self.scale_box(boxes[i], original_shape)
                detections.append(Detection(
                    bbox=box,
                    confidence=scores[i],
                    class_id=class_ids[i]
                ))

        return detections

検出性能の最適化

BPU ハードウェアアクセラレーション:

  • Horizon BPU を使用した INT8 量子化推論
  • CPU 推論と比べて 8~10 倍高速
  • 消費電力を 70% 削減し、組み込み機器へのデプロイに最適

メモリの最適化:

  • インプレースの画像処理でメモリコピーを削減
  • リングバッファの再利用により、頻繁なメモリ確保を回避
  • インテリジェントなガベージコレクションでメモリのピークを抑制

マルチスレッドの最適化:

  • 画像取得と推論の並列処理
  • 非同期の後処理パイプライン
  • ロックの最適化によりスレッド競合を低減

インテリジェント追跡システム

マルチターゲット追跡のアーキテクチャ

システム構成:

检测结果 → 目标关联 → 目标评分 → 目标选择 → 追踪输出
    ↓           ↓         ↓         ↓         ↓
 边界框列表 → ID匹配  → 综合评分 → 最优选择 → 追踪坐标

(上図の流れ:検出結果 → ターゲットの対応付け → ターゲットのスコアリング → ターゲットの選択 → 追跡出力。それぞれ、バウンディングボックスのリスト → ID マッチング → 総合スコア → 最適な選択 → 追跡座標 に対応します。)

ターゲット対応付けアルゴリズム

ハンガリアン法によるマッチング:

class TargetAssociator:
    def __init__(self, max_distance=100.0):
        self.max_distance = max_distance
        self.target_history = {}

    def associate_targets(self, current_detections, previous_targets):
        """ハンガリアン法でターゲットを対応付け"""
        if not previous_targets:
            return self.initialize_new_targets(current_detections)

        # 距離行列を計算
        cost_matrix = self.compute_cost_matrix(
            current_detections, previous_targets
        )

        # ハンガリアン法で最適なマッチングを求める
        from scipy.optimize import linear_sum_assignment
        row_indices, col_indices = linear_sum_assignment(cost_matrix)

        # マッチング結果を処理
        matched_targets = []
        unmatched_detections = []

        for det_idx, detection in enumerate(current_detections):
            if det_idx in row_indices:
                # 対応する過去のターゲットを取得
                target_idx = col_indices[row_indices == det_idx][0]
                if cost_matrix[det_idx, target_idx] < self.max_distance:
                    # マッチしたターゲットを更新
                    target = previous_targets[target_idx]
                    target.update(detection)
                    matched_targets.append(target)
                else:
                    # 距離が離れすぎているため、新しいターゲットとして扱う
                    unmatched_detections.append(detection)
            else:
                unmatched_detections.append(detection)

        # マッチしなかった検出結果から新しいターゲットを作成
        new_targets = self.create_new_targets(unmatched_detections)

        return matched_targets + new_targets

    def compute_cost_matrix(self, detections, targets):
        """検出結果とターゲット間のコスト行列を計算"""
        matrix = np.zeros((len(detections), len(targets)))

        for i, detection in enumerate(detections):
            det_center = detection.get_center()
            for j, target in enumerate(targets):
                target_center = target.get_predicted_center()

                # ユークリッド距離を基本コストとする
                distance = np.linalg.norm(det_center - target_center)

                # 信頼度の差を考慮
                conf_diff = abs(detection.confidence - target.confidence)

                # 総合コスト
                matrix[i, j] = distance + conf_diff * 50

        return matrix

ターゲットのスコアリングシステム

多要素スコアリングモデル:

class TargetScorer:
    def __init__(self, weights=None):
        self.weights = weights or {
            'confidence': 0.3,      # 信頼度の重み
            'size': 0.2,           # サイズの重み
            'persistence': 0.25,    # 継続性の重み
            'position': 0.15,      # 位置の連続性の重み
            'current_bonus': 0.1   # 現在のターゲットへの加点
        }

    def calculate_score(self, target, is_current_target=False):
        """ターゲットの総合スコアを計算"""
        # 1. 基本スコア(信頼度 + 正規化サイズ)
        normalized_area = min(target.area / (640 * 480), 1.0)
        base_score = (self.weights['confidence'] * target.confidence +
                     self.weights['size'] * normalized_area)

        # 2. 継続性スコア(連続検出回数)
        max_history = 50
        persistence_score = min(target.detection_count / max_history, 1.0)

        # 3. 位置の連続性スコア
        if hasattr(target, 'position_history') and len(target.position_history) > 1:
            recent_positions = target.position_history[-5:]  # 直近 5 フレーム
            distances = []
            for i in range(1, len(recent_positions)):
                dist = np.linalg.norm(
                    np.array(recent_positions[i]) - np.array(recent_positions[i-1])
                )
                distances.append(dist)

            avg_distance = np.mean(distances) if distances else 0
            max_stable_distance = 20  # ピクセル
            position_score = max(0, 1.0 - avg_distance / max_stable_distance)
        else:
            position_score = 0.5  # 新しいターゲットには中間のスコアを付与

        # 4. 現在のターゲットへの加点(頻繁な切り替えを防止)
        current_bonus = self.weights['current_bonus'] if is_current_target else 0

        # 総合スコア
        final_score = (base_score +
                      self.weights['persistence'] * persistence_score +
                      self.weights['position'] * position_score +
                      current_bonus)

        return final_score

動き予測システム

カルマンフィルタの実装:

class KalmanTracker:
    def __init__(self):
        self.kf = cv2.KalmanFilter(4, 2)  # 状態の次元 4、観測の次元 2
        self.setup_kalman_filter()

    def setup_kalman_filter(self):
        """カルマンフィルタのパラメーターを初期化"""
        dt = 1.0  # 時間ステップ

        # 状態遷移行列 F
        self.kf.transitionMatrix = np.array([
            [1, 0, dt, 0],   # x' = x + vx*dt
            [0, 1, 0, dt],   # y' = y + vy*dt
            [0, 0, 1, 0],    # vx' = vx
            [0, 0, 0, 1]     # vy' = vy
        ], dtype=np.float32)

        # 観測行列 H
        self.kf.measurementMatrix = np.array([
            [1, 0, 0, 0],    # x を観測
            [0, 1, 0, 0]     # y を観測
        ], dtype=np.float32)

        # プロセスノイズの共分散行列 Q
        self.kf.processNoiseCov = np.eye(4, dtype=np.float32) * 0.1

        # 観測ノイズの共分散行列 R
        self.kf.measurementNoiseCov = np.eye(2, dtype=np.float32) * 1.0

        # 初期状態の共分散行列 P
        self.kf.errorCovPost = np.eye(4, dtype=np.float32) * 1000

    def predict(self):
        """次の時刻の状態を予測"""
        prediction = self.kf.predict()
        return prediction[:2]  # 位置の予測値を返す

    def update(self, measurement):
        """観測値で状態を更新"""
        self.kf.correct(np.array(measurement, dtype=np.float32))

    def get_state(self):
        """現在の状態推定値を取得"""
        return self.kf.statePost.flatten()

運動モデルの選択:

  • 等速運動モデル:滑らかに移動するターゲットに適しています
  • 等加速度運動モデル:加速度が変化するターゲットに適しています
  • 旋回モデル:移動方向が変化するターゲットに適しています

ブレ防止の戦略

ターゲット切り替えのしきい値機構:

class TargetSwitchController:
    def __init__(self, switch_threshold=0.15, min_stable_frames=5):
        self.switch_threshold = switch_threshold
        self.min_stable_frames = min_stable_frames
        self.current_target_id = None
        self.stable_frame_count = 0

    def should_switch_target(self, current_target, best_target):
        """追跡ターゲットを切り替えるべきか判定"""
        if current_target is None:
            return True, best_target

        # スコアの差を計算
        score_diff = best_target.score - current_target.score

        # 新しいターゲットのスコアが現在のターゲットより大幅に高い場合のみ切り替える
        if score_diff > self.switch_threshold:
            # 数フレーム連続で安定している場合のみ切り替える
            if best_target.id == getattr(self, 'candidate_target_id', None):
                self.stable_frame_count += 1
                if self.stable_frame_count >= self.min_stable_frames:
                    self.stable_frame_count = 0
                    return True, best_target
            else:
                self.candidate_target_id = best_target.id
                self.stable_frame_count = 1

        return False, current_target

パンチルト制御システム

ハードウェアインターフェース仕様

通信プロトコル:

  • インターフェース種別:RS232 シリアル通信
  • デバイスパス:/dev/ttyS1
  • ボーレート:115200 bps
  • データビット:8 ビット
  • ストップビット:1 ビット
  • パリティ:なし
  • フロー制御:ハードウェアフロー制御なし

サーボの仕様:

  • 種類:PWM デジタルサーボ
  • 制御精度:1 度刻み
  • 角度範囲:0°~270°(水平・垂直)
  • 応答速度:最大 60°/秒
  • 繰り返し精度:±0.5°
  • 動作電圧:6V~7.4V

シリアル通信プロトコル

コマンド形式:

[Header] [Address] [Command] [Data] [Checksum]
  0x55     0xFF      0xXX    N bytes   0xXX

サーボ制御コマンド:

class ServoProtocol:
    def __init__(self, serial_port):
        self.serial = serial_port
        self.header = 0x55
        self.address = 0xFF

    def move_servo(self, servo_id, angle):
        """サーボ移動コマンドを送信"""
        # 角度を PWM 値に変換(500~2500us が 0~180 度に対応)
        pwm_value = int(500 + (angle / 270.0) * 2000)

        # コマンドパケットを構築
        command = 0x03  # サーボ移動コマンド
        data = [
            servo_id,           # サーボ ID
            (pwm_value >> 8) & 0xFF,  # PWM 上位バイト
            pwm_value & 0xFF,   # PWM 下位バイト
            0x00, 0x00          # 移動時間(0 は最速)
        ]

        # チェックサムを計算
        checksum = self.calculate_checksum([command] + data)

        # コマンドを送信
        packet = [self.header, self.address, command] + data + [checksum]
        self.serial.write(bytes(packet))

    def calculate_checksum(self, data):
        """チェックサムを計算"""
        return (~sum(data)) & 0xFF

    def read_response(self, timeout=1.0):
        """サーボの応答を読み取る"""
        start_time = time.time()
        buffer = []

        while time.time() - start_time < timeout:
            if self.serial.in_waiting > 0:
                byte = self.serial.read(1)[0]
                buffer.append(byte)

                # 応答パケットをすべて受信したか確認
                if len(buffer) >= 6:  # 応答パケットの最小長
                    if buffer[0] == 0x55:  # 正しいヘッダー
                        return self.parse_response(buffer)

            time.sleep(0.001)  # 短時間スリープ

        raise TimeoutError("舵机响应超时")

PID 制御システム

PID コントローラーの実装:

class PIDController:
    def __init__(self, kp=0.6, ki=0.1, kd=0.05,
                 max_integral=20.0, min_output=-50, max_output=50):
        self.kp = kp  # 比例ゲイン
        self.ki = ki  # 積分ゲイン
        self.kd = kd  # 微分ゲイン

        # 積分値の制限
        self.max_integral = max_integral
        self.integral = 0.0

        # 出力の制限
        self.min_output = min_output
        self.max_output = max_output

        # 前回値
        self.previous_error = 0.0
        self.previous_time = time.time()

        # 特殊処理用フラグ
        self.is_first_update = True
        self.first_update_factor = 0.3  # 初回応答の減衰係数

    def update(self, error, current_time=None):
        """PID コントローラーを更新"""
        if current_time is None:
            current_time = time.time()

        dt = current_time - self.previous_time
        if dt <= 0:
            dt = 0.01  # ゼロ除算を回避

        # 比例項
        proportional = self.kp * error

        # 積分項
        self.integral += error * dt
        # 積分値の制限(積分ワインドアップを防止)
        self.integral = np.clip(self.integral,
                               -self.max_integral, self.max_integral)
        integral_term = self.ki * self.integral

        # 微分項
        derivative = (error - self.previous_error) / dt
        derivative_term = self.kd * derivative

        # PID 出力
        output = proportional + integral_term + derivative_term

        # 初回更新時の特殊処理(オーバーシュートを抑制)
        if self.is_first_update:
            output *= self.first_update_factor
            self.is_first_update = False

        # 出力の制限
        output = np.clip(output, self.min_output, self.max_output)

        # 前回値を更新
        self.previous_error = error
        self.previous_time = current_time

        return output

    def reset(self):
        """PID コントローラーの状態をリセット"""
        self.integral = 0.0
        self.previous_error = 0.0
        self.previous_time = time.time()
        self.is_first_update = True

    def set_gains(self, kp, ki, kd):
        """PID パラメーターを動的に調整"""
        self.kp = kp
        self.ki = ki
        self.kd = kd

適応型 PID パラメーター調整:

class AdaptivePIDController(PIDController):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.error_history = deque(maxlen=10)
        self.base_kp = self.kp

    def update(self, error, current_time=None):
        self.error_history.append(abs(error))

        # 誤差の大きさに応じてパラメーターを適応的に調整
        avg_error = np.mean(self.error_history)

        if avg_error > 100:  # 誤差が大きい場合:比例ゲインを上げて素早く応答
            self.kp = self.base_kp * 1.5
            self.ki *= 0.8  # 積分を弱めてオーバーシュートを回避
        elif avg_error < 20:  # 誤差が小さい場合:比例ゲインを下げて安定性を向上
            self.kp = self.base_kp * 0.7
            self.ki *= 1.2  # 積分を強めて定常偏差を解消
        else:
            self.kp = self.base_kp  # デフォルトのパラメーターに戻す

        return super().update(error, current_time)

パンチルト台の運動学

座標系の変換:

class GimbalKinematics:
    def __init__(self, image_center=(320, 240), fov_h=60, fov_v=45):
        self.image_center = image_center
        self.fov_h = math.radians(fov_h)  # 水平画角
        self.fov_v = math.radians(fov_v)  # 垂直画角

    def pixel_to_angle(self, pixel_x, pixel_y, image_width=640, image_height=480):
        """ピクセル座標をパンチルト台の角度オフセットに変換"""
        # 画像中心からのピクセルオフセットを計算
        pixel_offset_x = pixel_x - self.image_center[0]
        pixel_offset_y = pixel_y - self.image_center[1]

        # [-1, 1] の範囲に正規化
        norm_x = pixel_offset_x / (image_width / 2)
        norm_y = pixel_offset_y / (image_height / 2)

        # 角度(ラジアン)に変換
        angle_x = norm_x * (self.fov_h / 2)
        angle_y = norm_y * (self.fov_v / 2)

        # 度に変換
        return math.degrees(angle_x), math.degrees(angle_y)

    def angle_to_servo_position(self, angle_offset_x, angle_offset_y,
                               current_pan=135, current_tilt=135):
        """角度オフセットをサーボの絶対位置に変換"""
        # 新しいサーボ位置を計算
        new_pan = current_pan + angle_offset_x
        new_tilt = current_tilt - angle_offset_y  # Y 軸の向きが逆である点に注意

        # 角度の制限
        new_pan = np.clip(new_pan, 0, 270)
        new_tilt = np.clip(new_tilt, 0, 270)

        return new_pan, new_tilt

システム統合とデータフロー

データフローのアーキテクチャ

リアルタイム処理パイプライン:

摄像头捕获 → 图像预处理 → 人脸检测 → 目标追踪 → 运动预测 → PID控制 → 云台执行
     ↓            ↓           ↓         ↓         ↓         ↓         ↓
  30fps BGR   →  NV12格式  → YOLO11  → 匈牙利算法 → 卡尔曼滤波 → PID输出 → 串口通信
    |            |           |         |         |         |         |
    |            |           |         |         |         |         ↓
    |            |           |         |         |         |      舵机响应
    |            |           |         |         |         |
    ↓            ↓           ↓         ↓         ↓         ↓
  Web视频流  ← 结果绘制  ← 检测框绘制 ← 目标标记 ← 轨迹预测 ← 控制反馈

(上図の流れ:カメラ取得(30fps BGR)→ 画像前処理(NV12 形式)→ 顔検出(YOLO11)→ ターゲット追跡(ハンガリアン法)→ 動き予測(カルマンフィルタ)→ PID 制御(PID 出力)→ パンチルト台の実行(シリアル通信 → サーボ応答)。並行して、制御フィードバック → 軌跡予測 → ターゲットのマーキング → 検出枠の描画 → 結果の描画 → Web 映像ストリームの順に画面へ反映されます。)

パフォーマンス最適化の戦略:

  1. 並列処理:検出と制御を非同期に実行
  2. キャッシュの最適化:インテリジェントなキャッシュ戦略で計算量を削減
  3. メモリ管理:オブジェクトプールの再利用により GC を削減
  4. ハードウェアアクセラレーション:BPU と GPU のリソースを最大限に活用

マルチスレッドの協調:

import threading
import queue
from concurrent.futures import ThreadPoolExecutor

class FaceTrackingSystem:
    def __init__(self):
        self.detection_queue = queue.Queue(maxsize=5)
        self.control_queue = queue.Queue(maxsize=10)
        self.running = False
        self.executor = ThreadPoolExecutor(max_workers=3)

    def start(self):
        """マルチスレッド追跡システムを起動"""
        self.running = True

        # 各ワーカースレッドを起動
        self.detection_thread = threading.Thread(
            target=self._detection_worker, daemon=True
        )
        self.tracking_thread = threading.Thread(
            target=self._tracking_worker, daemon=True
        )
        self.control_thread = threading.Thread(
            target=self._control_worker, daemon=True
        )

        self.detection_thread.start()
        self.tracking_thread.start()
        self.control_thread.start()

    def _detection_worker(self):
        """顔検出ワーカースレッド"""
        while self.running:
            frame = self.camera.read()
            if frame is not None:
                # 検出タスクをスレッドプールに投入
                future = self.executor.submit(self.detector.detect, frame)
                try:
                    detections = future.result(timeout=0.1)  # 100ms でタイムアウト
                    self.detection_queue.put((frame, detections), block=False)
                except (queue.Full, TimeoutError):
                    # キューが満杯、または検出がタイムアウトした場合は現在のフレームをスキップ
                    pass

    def _tracking_worker(self):
        """ターゲット追跡ワーカースレッド"""
        while self.running:
            try:
                frame, detections = self.detection_queue.get(timeout=1.0)

                # 追跡アルゴリズムを実行
                tracking_result = self.tracker.update(detections)

                if tracking_result.has_valid_target:
                    # 制御コマンドを計算
                    control_cmd = self.calculate_control_command(
                        tracking_result.target_center
                    )
                    self.control_queue.put(control_cmd, block=False)

                self.detection_queue.task_done()

            except queue.Empty:
                continue
            except queue.Full:
                # 制御キューが満杯の場合は現在の制御コマンドをスキップ
                pass

    def _control_worker(self):
        """パンチルト台制御ワーカースレッド"""
        while self.running:
            try:
                control_cmd = self.control_queue.get(timeout=1.0)

                # パンチルト台の制御を実行
                self.gimbal_controller.execute_command(control_cmd)

                self.control_queue.task_done()

            except queue.Empty:
                continue

    def stop(self):
        """追跡システムを停止"""
        self.running = False

        # すべてのスレッドの終了を待機
        self.detection_thread.join(timeout=2.0)
        self.tracking_thread.join(timeout=2.0)
        self.control_thread.join(timeout=2.0)

        # スレッドプールを終了
        self.executor.shutdown(wait=True)

ユーザーインターフェースと可視化:Web 画面の機能

ライブ映像ストリーミング

映像表示:

  • MJPEG 形式の映像ストリーミングによる低遅延伝送
  • 顔検出枠をリアルタイムに描画
  • 追跡中のターゲットを特別表示(オレンジ色の枠 + ラベル)
  • フレームレートとパフォーマンス指標の表示

インタラクティブなダッシュボード:

  • パンチルト台の手動制御(矢印キー/マウス)
  • 追跡パラメーターのリアルタイム調整
  • システム状態の監視
  • リアルタイムのログ表示

可視化要素:

def draw_tracking_visualization(frame, tracking_result):
    """追跡の可視化情報を描画"""

    # 検出されたすべての顔を描画
    for detection in tracking_result.all_detections:
        color = (0, 255, 0)  # 緑:通常の検出
        thickness = 2

        # 現在の追跡ターゲットは特別な色で表示
        if detection.id == tracking_result.current_target_id:
            color = (0, 165, 255)  # オレンジ:追跡ターゲット
            thickness = 3

            # 追跡ラベルを追加
            label = f"Tracking ID:{detection.id} ({detection.confidence:.2f})"
            cv2.putText(frame, label,
                       (detection.bbox[0], detection.bbox[1] - 10),
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)

        # バウンディングボックスを描画
        cv2.rectangle(frame,
                     (detection.bbox[0], detection.bbox[1]),
                     (detection.bbox[2], detection.bbox[3]),
                     color, thickness)

        # 中心点を描画
        center = detection.get_center()
        cv2.circle(frame, center, 5, color, -1)

    # 予測軌跡を描画
    if hasattr(tracking_result, 'predicted_path'):
        for i, point in enumerate(tracking_result.predicted_path):
            alpha = 1.0 - (i * 0.2)  # 透明度のグラデーション
            color = (int(255 * alpha), int(165 * alpha), 0)
            cv2.circle(frame, point, 3, color, -1)

    # システム情報を描画
    info_lines = [
        f"FPS: {tracking_result.fps:.1f}",
        f"Targets: {len(tracking_result.all_detections)}",
        f"Tracking: {'ON' if tracking_result.is_tracking else 'OFF'}",
        f"Gimbal: {tracking_result.gimbal_position}"
    ]

    for i, line in enumerate(info_lines):
        cv2.putText(frame, line, (10, 30 + i * 25),
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

    return frame

パフォーマンス監視と最適化:主要パフォーマンス指標

検出性能

検出性能:

  • 検出フレームレート:目標 28~30fps
  • 検出遅延:エンドツーエンドで 35ms 未満
  • 検出精度:信頼度 0.6 超において検出精度 95% 超
  • 誤検出率:2% 未満(通常の照明条件下)

追跡性能:

  • 追跡の安定性:ターゲット切り替え頻度 0.1 回/秒未満
  • 追跡精度:中心のずれ 10 ピクセル未満(距離 2m の場合)
  • 応答遅延:検出から制御実行まで 50ms 未満

制御性能:

  • パンチルト台の応答:機械的遅延 100ms 未満
  • 制御精度:角度精度 ±1°
  • 滑らかさ:目立ったブレやオーバーシュートがないこと

パフォーマンス監視システム:

class PerformanceMonitor:
    def __init__(self):
        self.metrics = {
            'detection_fps': deque(maxlen=100),
            'detection_latency': deque(maxlen=100),
            'tracking_latency': deque(maxlen=100),
            'control_latency': deque(maxlen=100),
            'target_switches': 0,
            'total_frames': 0
        }
        self.start_time = time.time()

    def record_detection(self, latency):
        """検出性能を記録"""
        current_time = time.time()
        self.metrics['detection_latency'].append(latency)
        self.metrics['total_frames'] += 1

        # FPS を計算
        if len(self.metrics['detection_fps']) == 0:
            self.last_fps_time = current_time
        elif current_time - self.last_fps_time >= 1.0:
            fps = len(self.metrics['detection_fps'])
            self.metrics['detection_fps'].append(fps)
            self.last_fps_time = current_time

    def get_performance_report(self):
        """パフォーマンスレポートを生成"""
        uptime = time.time() - self.start_time

        return {
            'uptime': uptime,
            'total_frames': self.metrics['total_frames'],
            'average_fps': self.metrics['total_frames'] / uptime,
            'detection_latency': {
                'avg': np.mean(self.metrics['detection_latency']) if self.metrics['detection_latency'] else 0,
                'max': np.max(self.metrics['detection_latency']) if self.metrics['detection_latency'] else 0,
                'min': np.min(self.metrics['detection_latency']) if self.metrics['detection_latency'] else 0
            },
            'tracking_stability': {
                'target_switches': self.metrics['target_switches'],
                'switches_per_minute': self.metrics['target_switches'] / (uptime / 60)
            }
        }

設定とチューニング:システム設定パラメーター

検出の設定

detection:
  model_path: "model/yolo11n_face_detect_bayese_320x320_nv12.bin"
  confidence_threshold: 0.6    # 信頼度しきい値
  iou_threshold: 0.45         # IoU しきい値
  max_detections: 10          # 最大検出数

  # BPU アクセラレーションの設定
  bpu:
    core_id: 0                # BPU コア ID
    priority: "high"          # 推論の優先度
    batch_size: 1            # バッチサイズ

追跡の設定:

tracking:
  max_targets: 5              # 最大追跡ターゲット数
  target_timeout: 3.0         # ターゲットのタイムアウト時間(秒)

  # スコアの重み
  scoring:
    confidence_weight: 0.3
    size_weight: 0.2
    persistence_weight: 0.25
    position_weight: 0.15
    current_bonus: 0.1

  # 切り替え制御
  switch_threshold: 0.15      # 切り替えしきい値
  min_stable_frames: 5        # 最小安定フレーム数

  # カルマンフィルタ
  kalman:
    process_noise: 0.1        # プロセスノイズ
    measurement_noise: 1.0    # 観測ノイズ
    prediction_enabled: true   # 予測を有効化

パンチルト台制御の設定:

gimbal:
  serial_port: "/dev/ttyS1"
  baudrate: 115200
  timeout: 1.0

  # サーボの設定
  pan_servo_id: 3
  tilt_servo_id: 4

  # 角度の制限
  angle_limits:
    pan: [0, 270]
    tilt: [0, 270]

  # PID パラメーター
  pid:
    kp: 0.6
    ki: 0.1
    kd: 0.05
    max_integral: 20.0
    output_limit: [-50, 50]
    first_update_factor: 0.3

  # スムーズ制御
  smooth_control:
    enabled: true
    max_angle_change: 8.0     # 1 回あたりの最大角度変化量
    min_interval: 0.01        # 最小制御間隔

インテリジェント顔追跡システムは、先進的なコンピュータビジョンアルゴリズム、インテリジェントな制御システム、最適化されたハードウェアインターフェースを統合することで、高精度・低遅延かつ安定した信頼性の高い顔追跡機能を実現し、ユーザーに完全なインテリジェント監視ソリューションを提供します。

情報交換の場

資料のダウンロード・技術サポートについては、HumansX株式会社までお問い合わせください。