# ผลประเมิน VIO อิสระ: Basalt กับ TUMVI room4

วันที่ประเมิน: 30 กันยายน 2569 — **คำนวณผลแล้ว ไม่ใช่การรับรองความพร้อมบิน**

นำ trajectory เดิม 600 ตำแหน่งจาก Basalt 0.1.7 มาจับคู่กับ ground truth ของ TUMVI room4 ที่เก็บไว้แล้ว ครอบคลุมประมาณ29.95วินาที ไม่รัน estimator ซ้ำ ไม่ฝึกหรือปรับพารามิเตอร์ให้เข้ากับคำตอบ

## ผลที่วัดได้

| ตัวชี้วัด | RMSE | มัธยฐาน | ค่าสูงสุด | จำนวน |
|---|---:|---:|---:|---:|
| APE ตำแหน่ง หลัง SE(3) alignment | 0.021963 m | 0.018474 m | 0.036953 m | 600 poses |
| APE มุมหมุน หลัง alignment เดียวกัน | 0.503762° | 0.458636° | 1.433144° | 600 poses |
| RPE ตำแหน่ง ช่วงห่าง20เฟรม | 0.010047 m | 0.007091 m | 0.025622 m | 580 คู่ |
| RPE มุมหมุน ช่วงห่าง20เฟรม | 0.438633° | 0.348529° | 1.465564° | 580 คู่ |

APE ตำแหน่ง RMSE ประมาณ2.20ซม. หมายถึงขนาดความคลาดเคลื่อนเฉลี่ยแบบกำลังสองหลังย้ายและหมุนเส้นทางทั้งชุดให้ตรงกัน ไม่ได้หมายความว่าตำแหน่งในพิกัดโลกจริงแม่นยำ2.20ซม. RPE ตำแหน่ง RMSE ประมาณ1.00ซม. อธิบายความผิดพลาดของการเคลื่อนที่ระหว่างคู่ตำแหน่ง ช่วงเวลาจริงของแต่ละคู่คือ0.998821–1.001040วินาที จึงเรียกว่า “ประมาณ1วินาที”

## ทำอย่างไรให้เปรียบเทียบอย่างเป็นธรรม

```mermaid
flowchart LR
  A[Trajectory เดิม 600 poses] --> C[คร่อมเวลาและตรวจ gap]
  B[Ground truth ในกรอบ IMU] --> C
  C --> D[Linear position + SLERP]
  D --> E[SE3 alignment: scale 1]
  E --> F[APE: ต่างกันที่แต่ละเวลา]
  D --> G[RPE: ต่างกันระหว่างคู่ห่าง 20 เฟรม]
  F --> H[RMSE / median / max พร้อมข้อจำกัด]
  G --> H
```

1. ตรวจ SHA256 ของ trajectory และ archive เดิม แล้วเทียบ mocap CSV กับสมาชิกใน archive แบบ byte-for-byte
2. ใช้ ground truth ที่ผ่าน calibration แล้วในกรอบ IMU ไม่ใส่ extrinsic หรือ time offset ของข้อมูลดิบซ้ำ
3. เก็บเวลาเป็น integer nanoseconds เพื่อเลี่ยงความละเอียดที่หายไปจากเลข epoch ขนาดใหญ่ จับคู่ด้วยการคร่อมเวลาโดยไม่ extrapolate
4. แทรกตำแหน่งแบบเส้นตรงและทิศทางแบบ shortest-path SLERP ระหว่าง ground truth สองตัวอย่าง Gap มากที่สุดที่พบ8.334ms ต่ำกว่าเกณฑ์20msที่กำหนดก่อนรัน ครอบคลุมครบ600ตำแหน่ง
5. จัดแนว SE(3) ด้วยตำแหน่งครบ600จุด: อนุญาตหมุนและเลื่อน แต่ตรึง scale=1 ไม่ใช้ Sim(3) ที่อาจซ่อนความผิดพลาดของสเกล
6. คำนวณ APE และ RPE จาก pose โดยตรง แยกจากค่า diagnostic ที่ Basalt พิมพ์เอง

ค่า singular values ของ covariance คือ0.355076,0.144402,0.007460m² ผ่านเงื่อนไขไม่เสื่อมสภาพของการจัดแนวที่กำหนดไว้ ไม่ได้ปรับเกณฑ์หลังเห็นผล

## สูตรพร้อมคำอธิบาย

ให้ \(T_i\) เป็น pose อ้างอิง และ \(\hat T_i\) เป็น pose ประมาณ ทั้งคู่แปลงจากกรอบ IMU สู่กรอบโลกของตนเอง ให้ \(S\in SE(3)\) เป็นการหมุนและเลื่อนที่จัดแนวเส้นทางประมาณเข้าหาอ้างอิง

\[
E_i^{APE}=T_i^{-1}S\hat T_i,\qquad e_{p,i}=\|\operatorname{trans}(E_i^{APE})\|_2
\]

ส่วน translation คือเวกเตอร์ตำแหน่งผิดพลาด นำความยาวเวกเตอร์เป็นความผิดพลาดหน่วยเมตร สำหรับ RPE ใช้การเคลื่อนที่ระหว่างเฟรม แทนการเทียบตำแหน่งในโลก:

\[
E_{i,j}^{RPE}=(T_i^{-1}T_j)^{-1}(\hat T_i^{-1}\hat T_j),\qquad j=i+20
\]

การหมุนและเลื่อนกรอบโลกคงที่หักล้างกันใน relative pose จึงไม่ต้องนำ alignment มา “แก้” RPE อีกครั้ง คู่580คู่ซ้อนทับกัน จึงไม่ใช่การทดลองอิสระ580ครั้ง

\[
\mathrm{RMSE}=\sqrt{\frac{1}{N}\sum_{i=1}^{N}e_i^2},\qquad
\theta=\cos^{-1}\left(\frac{\operatorname{tr}(R_E)-1}{2}\right)
\]

สูตรมุมหมุนใช้ rotation matrix ของ error และ clamp อินพุตให้อยู่ใน[-1,1] เพื่อกัน roundoff ก่อนแปลง radian เป็นองศา ตัวอย่างความผิดพลาด1,2,3ซม. มี RMSE=√((1²+2²+3²)/3)≈2.16ซม.; ตัวอย่างนี้ใช้สอนสูตร ไม่ใช่ข้อมูลทดลองเพิ่มเติม

## วิธีทำซ้ำและหลักฐาน

- วิธีที่กำหนดก่อนรัน: `research/vio-accuracy-2026-09-30-protocol.md`
- ตัวคำนวณ: `tools/basalt-readiness/evaluate_accuracy.py` (Python + NumPy; stdin JSON มี estimate แบบ TUM และ ground_truth แบบ Euroc CSV)
- ตัวรัน sandbox/ตรวจ provenance: `tools/basalt-readiness/check_accuracy.py`
- ผลตัวเลขเต็มและคำสั่ง: `tools/basalt-readiness/accuracy-result-2026-09-30.json`

การประมวลผล1.034วินาที exit0 ใน cached container ที่ไม่มี network, nonroot/read-only, CPU0.5/RAM512MiB/watchdog30s และลบ container ตาม ID สำเร็จ ตรวจ inventory ว่างแล้ว ผ่าน synthetic checks8ประเด็น เช่น rigid-transform invariance, drift ที่ทราบคำตอบ, quaternion เครื่องหมายตรงข้าม, interpolation, missing coverage และข้อมูลผิดรูปแบบ ตัวอย่างสังเคราะห์ใช้ทดสอบตัวคำนวณเท่านั้น

การตรวจทานอิสระของโค้ด/สูตร/แฮชผ่าน โดยผู้ตรวจไม่รันข้อมูลซ้ำ ข้อปรับปรุงที่ยังแยกไว้: เพิ่ม oracle สำหรับมุมหมุนที่ไม่เป็นศูนย์ และปรับตัวเก็บหลักฐานให้บันทึก exact container ID/เขียนรายงานได้แม้คำสั่ง cleanup โยน exception หลักฐานรอบสำเร็จนี้เก็บ exit0 และ inventory ว่าง แต่ไม่มี ID ในไฟล์รายงาน

## ข้อจำกัดที่ต้องอ่านพร้อมตัวเลข

- เป็นข้อมูล benchmark ถือด้วยมือในห้อง ไม่ใช่โดรน ไม่ครอบคลุมแรงสั่นสะเทือน ใบพัด หรือแสงกลางแจ้ง
- ใช้ช่วงสั้นส่วนต้น ไม่ใช่ทั้ง room4 และไม่ใช่ทุกสภาพแวดล้อม
- Alignment fit ด้วยชุดเดียวกับที่รายงาน error จึงเป็น best-fit trajectory comparison ไม่ใช่การสอบเทียบอิสระ
- มีความผิดพลาดจาก ground truth และ interpolation; รอบนี้ไม่ได้ประเมิน uncertainty รวม หรือช่วงความเชื่อมั่น
- การคำนวณอิสระไม่ได้เท่ากับการทวนสอบด้วยซอฟต์แวร์ตัวที่สองทุกค่า ยังไม่อ้างผลตรงกับ evo แบบตัวต่อตัว
- ไม่มีเกณฑ์ผ่าน/ตกด้านการบินที่กำหนดไว้ จึงไม่เรียกผลนี้ว่า “พร้อมบิน”

## แหล่งอ้างอิง

1. [TUM Visual-Inertial Dataset](https://cvg.cit.tum.de/data/datasets/visual-inertial-dataset) — Schubert และคณะ; ข้อมูล CC BY4.0; กรอบพิกัด เวลา และขอบเขต mocap ของข้อมูล calibrated
2. [Basalt 0.1.7 source: vio.cpp](https://gitlab.com/VladyslavUsenko/basalt/-/raw/0.1.7/src/vio.cpp) — ตรวจ T_w_i และลำดับ quaternion ของไฟล์ trajectory; ไม่ใช้ optional groundtruth export ซึ่งใส่ quaternion เอกลักษณ์
3. [evo: Metrics and alignment](https://github.com/MichaelGrupp/evo/wiki/Metrics) — ความหมาย APE/RPE และการแยก SE(3) ออกจาก Sim(3)

เข้าถึงแหล่งทางการวันที่30กันยายน2569
