Pixels to Motors
DAY 196
mechArm 270 Pi
Log #11
検出パイプライン分割
横カメラを統合する ── 検出パイプラインを上向きと前向きに分割した
FROM / Log#10
物理セットアップ完了
iPhone 11 三脚固定
Iriun 接続
ソフト未着手
→
TO / Log#11
2系統パイプライン確立
カメラ自動識別
TIP検出関数 新規実装
緑2点中点でY整合
💡 Key Takeaway ── 本日の達成
01 / カメラ識別
色パターンで3台から2台を自動判定
OpenCV index は非決定的。青≥2かつ緑=0→上カメラ、青≥2かつ緑≥2→横カメラで起動毎に確実判別。
02 / 関数分割
既存青3点幾何は横カメラに流用不可と確定
wrist 側青シールが爪本体に隠れて2点のみ。実行して (None,None,None) の確証を数字で残した。
03 / TIP定義
緑2点中点がY=959で掴み高さに整合
v1→v2→v3と定義を進化。緑シール位置(爪先端寄り)のみをTIPとすることで物理的な掴み高さと一致。
01
動機
Z方向が収束しない根本原因 ── 上カメラ1台では分解能が足りない
幾何的制約
上カメラ homography の限界 ── 爪が浮いている分だけ視差が乗る
SIDE VIEW
マット面 Z=0
BLK
爪 (TIP)
上cam
視差ズレ
見かけ位置
H
(爪の高さ)
横cam →
Y軸として直接読める
横カメラ追加後
上カメラ (homography制約あり)
横カメラ (Yを直接観測)
上カメラのみ
Z不安定
構造
画像y軸方向 → Z+物理距離
制約
homography は Z=0 平面(マット面)を仮定
問題
爪が浮いていると見かけ位置が奥側にズレる
結果
ループが嘘の差分に向かって動き続ける
横カメラ追加
採用 ★
構造
横から見れば爪の高さYがy座標として直接読める
制約
homography の制約を受けない独立軸
解決
Z方向を独立観測できる欠落軸を補完
前提
Log#10 で iPhone 11 を三脚固定済み
"
上カメラを増やしてもZ方向の収束精度は上がらない。
必要なのは「Zを独立に観測できるカメラ」だ。
Log#11
設計判断
02
識別問題
3台のカメラから「どれが何か」── OpenCV index は非決定的
環境構成
macOS + OpenCV における3カメラ混在問題
MacBook内蔵
FaceTime HD
爪を映していない
iPhone 14 Pro Max
Continuity Camera
→ 上カメラ
iPhone 11
Iriun Webcam (WiFi)
→ 横カメラ
cv2.VideoCapture(0〜2)
起動毎に
index変動
AVFoundation順 ≠ cv2順
対応が公開されていない
試みと結果
ソフト側での識別 ── 全手法が失敗した理由
01
AVFoundation デバイス名取得(ffmpeg)
+
NG
AVFoundation順 ≠ cv2 index順
内部仕様・非公開
起動毎にずれた
02
解像度判定(frame.shape)
+
NG
3台とも1920×1080になり得る
MacBook内蔵もスケーリングされて返る
判定軸として使えない
03
ブライトネス判定(過去実績あり)
+
NG
上カメラ・横カメラ共にiPhone → 明度差なし
Log#7系の3台なら有効だったが今回は不可
04
色パターン識別(物理マーカー追加)← 採用
+
採用 ★
爪上面=青シール / 爪前面=緑シール
映っているものが何かという最高信頼性の情報
ソフト依存ゼロ
識別ロジック
色パターンによるカメラ役割判定フロー
上カメラ視野(真上)
●
青×3 緑×0
横カメラ視野(前面)
wrist隠れる
青×2 緑×2
MacBook内蔵
爪が映っていない
青×0 緑×0
→ 上カメラ
→ 横カメラ
→ 除外
03
実装
auto_detect_cameras.py ── 193行で3台から2台を判定する
確定仕様
auto_detect_cameras.py 設計パラメータ
⏱
Warmup
30 frames
自動露出安定待ち
◼
面積フィルタ
80〜8000 px²
9〜90px四方相当
⟳
モルフォロジー
open→close
ノイズ除去→穴埋め
≥
判定閾値
blue≥2
(3点目欠損許容)
HSV閾値設定
青・緑シール検出の HSV レンジ(OpenCV: H=0〜179)
Hue (0〜179)
0
50
100
150
179
GREEN 35〜85
BLUE 100〜130
Saturation / Value 下限
Blue S≥100
255
Blue V≥50
255
Green S≥80
255
Green V≥60
255
設計判断 01 / Warmup
30フレーム空読み
Iriun はWiFi経由でフレームを送る。開いた直後は黒フレームや低露出が来る。30フレームで概ね安定する。
設計判断 02 / Morphology
open→close の順
収縮→膨張でノイズ消去してから膨張→収縮で穴埋め。逆順にするとノイズが拡大されて誤検出が増える。
設計判断 03 / 判定条件
blue≥2(ジャストでない)
青3点目が手ブレで一瞬隠れても上カメラと識別できるよう、blue≥2という緩い条件にした。個数より「両方同時検出」が本質的弁別。
04
確定
detect_tip_from_frame は横カメラに流用できない ── 数字で確証
物理制約
横カメラ視野での爪シール配置 ── wrist は爪本体に隠れる
上カメラ視野(真上)
cA
cB
wrist
青3点 → 全て検出可能 ✓
横カメラ視野(前面)
cA
cB
wrist
隠れ
緑L
緑R
青2点のみ → detect_tip_from_frame → (None, None, None) ✗
試みた方法
流用
detect_tip_from_frame
期待
青3点→TIP計算
実際
青2点→(None,None,None)
原因
wrist が物理的に隠れる
判定
✗ 構造的に流用不可
→
採用した方法
新規
detect_tip_side()
入力
緑2点(爪前面)
出力
TIP = 緑2点の中点
チェック
青2点を妥当性検証に使用
判定
◎ 独立実装
"
流用できないと判定するために、修正や設定変更を
一度もしていない。関数を呼んで結果を見ただけ。
確証は数字で残す。
P008
測れる手段が
推論より先
05
定義進化
TIP 定義の v1→v2→v3 ── 掴み高さに整合するまで
Y値の変化
TIP定義バージョン別 Y座標(画面下方向が大きい値)
820 px
900 px
930 px
959 px
← 緑Y平均(掴み高さ)
Y≈900
v1
青2点中点
Y≈929
v2
左右爪中点
Y=959 ★
v3
緑2点中点
TIP定義の進化
v1→v2→v3 ── 掴み高さへの収束プロセス
v1
青2点中点
v2
左右爪中点
v3
緑2点中点
↑
バージョンを選択
v1 / 青2点の中点
不採用
TIP Y値
≈ 900 px
爪上面の青シール位置。爪の最も上側。
問題
掴み高さに届かない
ブロックを掴む爪先端(緑シール位置)より約60px上。掴み動作で5〜8mm のオフセット。
爪の上面高さ
ブロック接触なし
v2 / 左右爪それぞれの青+緑縦中点、のさらに中点
不採用
TIP Y値
≈ 929 px
青(≈900)と緑(≈959)の中間。爪の上下中央点。
問題
まだ青寄り
可視化PNGで赤円が爪の上下中央にプロット。掴み高さと約30px(5〜8mm)ズレている。ロジックは正しく動いているが定義が物理位置と合っていない。
ロジックは正しい
定義が物理とズレ
v3 / 緑2点の中点のみ
採用 ★
TIP Y値
= 959 px
緑2点のY平均と完全一致。爪先端寄りの物理的な掴み高さ。
根拠
緑シール = 爪下端寄り
緑シールは爪の前面・下端寄りに貼ってある。掴む高さがそのまま緑シール位置。青は妥当性チェックとして残す。
緑2点中点 = 掴み高さ
青2点は検証に残す
tip_pixel.Y = 緑Y平均
v1 / 青2点中点
≈900
px (Y)
爪の上面
v2 / 左右爪中点
≈929
px (Y)
爪の上下中央
v3 / 緑2点中点 採用
959
px (Y)
掴み高さ ★
06
実機検証
tip_pixel = (950, 959) ── 数値整合と可視化確認
実機出力
side_camera_utils.py 実行結果 ── 検出点の座標整合
1920×1080 フレーム(縮小表示)
爪(横から)
● 青 (999, 902)
○ 緑 (1000, 962)
TIP = (950, 959)
計算整合
緑X: (1000+901)/2
= 950.5 → 950 ✓
緑Y: (962+957)/2
= 959.5 → 959 ✓
tip_pixel = (950, 959)
緑Y平均と完全一致
★ 整合確認
確定値
実機検証出力 ── 横カメラ index=1
◎
TIP pixel
(950, 959)
●
青シール2点
(999,902)
(902,899)
○
緑シール2点
(1000,962)
(901,957)
→
tip_yz_mm
None
(Log#12で変換)
07
課題
連続実行で2/3が落ちる ── 露出問題と照明対策
露出の時系列
Iriun カメラ起動時の自動露出推移 ── warmup 30f の限界
0f
15f
30f
45f
60f
90f
120f
明るい
暗い
成功パターン(室内灯ON)
検出下限
(V≥50/60)
warmup終了
30f
失敗パターン(照明不足)
← 暗いまま30f超過
RuntimeError発生
対策①: warmup→60f
またはsleep 0.5〜1秒
物理: 室内灯ON
!
持ち越し課題
連続実行で約2/3の頻度で RuntimeError 落ち
仮説1
Iriun 露出リセット
仮説2
背景明度に引っ張られ
暫定対策
室内灯ON で安定
→ Log#12 で warmup_frames 増加、スリープ追加、照明プロトコル策定
08
観察 + 設計
誤検出4点目の観察と流用/分割の使い分け原則
!
上カメラ側の観察
青シールが4点検出されるケース(ノイズ1点混入)
想定検出
青3点
実観測
青4点(稀)
原因
キーボード上の青み反射
→ Log#12 で detect_tip_from_frame にマット範囲内フィルタを追加予定(今は観察のみ)
設計原則
流用 vs 分割 ── Log#11 で確定した構造判断
共有:環境依存パラメータ
HSV閾値 / モルフォロジー / 面積フィルタ / bottom-2ピック
import
import
上カメラ検出
detect_tip_from_frame()
青3点幾何 → TIP+向き
戻り値: pixel+Unity+方向
横カメラ検出
detect_tip_side() ← 新規
緑2点中点 → TIP pixel
戻り値: pixel のみ
動的追従ループ(Log#12で統合)
分割
検出ロジック
分割するもの
独立実装
対象
検出ロジック全体
理由1
戻り値の形が異なる(pixel+Unity+向き vs pixel のみ)
理由2
既存ループへの副作用を避けるため
方法
side_camera_utils.py に detect_tip_side() を新規
共有するもの
import 共有
対象
環境依存パラメータ
内容
HSV閾値・面積フィルタ・モルフォロジー
理由
片方だけ調整したとき整合性が壊れるのを防ぐ
方法
auto_detect_cameras から import で流用
09
結語
Log#11 が確立した3つの成果と2つの設計原則
成果 01 / カメラ識別
auto_detect_cameras.py
macOS OpenCV index が非決定的な環境で、色パターンのみを根拠に3台から2台を正しく判定。1台を除外する。
成果 02 / 流用不可確定
detect_tip_from_frame の横カメラ流用不可
修正・設定変更ゼロで実行し (None,None,None) の返却を確認。推論でなく観察で確定。仕様確認スクリプトを残置。
成果 03 / TIP検出
detect_tip_side() / tip_pixel Y=959
緑2点中点がY=緑Y平均と完全一致。実機PNGで爪先端寄りへのプロットを確認。掴み高さに整合した。
設計原則
Log#11 で確立した2原則 ── Log#12以降の実装指針
原則 A
検出ロジックは分割する
環境パラメータは共有する
HSV閾値・モルフォロジー・面積フィルタは
1ヶ所で定義してimport共有する
検出ロジックは視野方向ごとに独立
→ 後の調整で整合性が壊れない
原則 B
流用判定は数字で残す
「見てダメ」より「実行して返値を記録」
関数を呼んで結果を見て
戻り値を文字で記録する
仕様確認スクリプトはコミットして残置
→ 同じ確認を二度やらない
本日の結論 ── 横カメラのパイプラインが立った
BEFORE / Log#10まで
上カメラ1系統のみ
Z方向の独立観測
✗
カメラ自動識別
✗
横カメラ TIP 検出
✗
掴み高さ整合
✗
→
AFTER / Log#11
2系統パイプライン確立
色パターンでカメラ自動判定
◎
detect_tip_side() 新規実装
◎
TIP Y=959 が緑Y平均と完全一致
◎
Log#12 でループ統合へ
→