主要機能の紹介
概要
インテリジェント顔追跡システムは、先進的なコンピュータビジョン技術と高度なパンチルト制御アルゴリズムを統合し、顔検出から自動追跡までを一貫して実現するシステムです。YOLO11 ディープラーニングモデルと各種のインテリジェントアルゴリズムをベースに、高精度・低遅延なリアルタイム顔追跡機能を提供します。
顔検出システム
YOLO11 検出エンジン
技術的特長:
- モデルアーキテクチャ:YOLO11n 量子化モデル(RDK X5 プラットフォーム向けに最適化)
- 検出対象:顔検出に特化し、複数人の顔が映るシーンにも対応
- 入力サイズ:320 × 320 ピクセル、NV12 形式で入力
- 検出精度:信頼度しきい値 0.6、IoU しきい値 0.45(変更可能)
- 処理速度:約 30fps のリアルタイム検出(BPU ハードウェアアクセラレーション)
- 検出距離:0.5~5 メートルの範囲で顔検出が可能
検出プロセス:
- 画像の前処理:BGR 形式から NV12 形式へ変換し、320 × 320 にリサイズ
- モデル推論:Horizon BPU 上でハードウェアアクセラレーション推論を実行
- 後処理:非最大値抑制(NMS)、座標の逆量子化
- 結果の出力:バウンディングボックス座標、信頼度、顔 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 映像ストリームの順に画面へ反映されます。)
パフォーマンス最適化の戦略:
- 並列処理:検出と制御を非同期に実行
- キャッシュの最適化:インテリジェントなキャッシュ戦略で計算量を削減
- メモリ管理:オブジェクトプールの再利用により GC を削減
- ハードウェアアクセラレーション: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株式会社までお問い合わせください。
ドキュメント