# MB-001 — Round-trip latency budget and servo speed/torque requirements Researched 2026-11-03 (Asia/Hong_Kong). Companion files: `MB-001-avp-live-video.md`, `MB-001-touchdesigner-avp-stream.md`, pipeline graphic `MB-001-latency-pipeline.svg` / `.png`. **Tags.** **[S]** = number taken from a source (URL given). **[D]** = derived by arithmetic from sourced numbers (shown). **[E]** = my engineering estimate, not measured and not sourced. Treat every [E] as a placeholder until you run the infinite-mirror test (section 7). No vendor publishes camera or servo latency for the specific parts here. ## 0. Topology (as corrected by the user, 2026-11-03) Everything on the rig is **wired to one machine** (Raspberry Pi 5 assumed): * two USB UVC webcams -> USB cable -> machine (WIRED) * servo control -> machine -> serial/I2C/PWM wire -> servos (WIRED; no ESP32-over-Wi-Fi hop) * the **only wireless hop** is machine <-> Apple Vision Pro over Wi-Fi 6, carrying video (machine -> headset) and head pose (headset -> machine) on the same link. Loop: head moves -> pose sent over Wi-Fi -> machine -> wired servo command -> servo travels -> camera captures (USB) -> MJPEG decode + SBS compose -> H.264 encode -> Wi-Fi -> jitter buffer -> decode -> render on the headset display. Scenarios: **(a)** machine on Ethernet to the router, headset on Wi-Fi 6; **(b)** machine acts as the Wi-Fi access point / direct link to the headset; **(c, short note)** remote over the internet / VPN. Design point used for the numbers: 2 x 1280x720 MJPEG @30 fps from the USB cameras -> one 2560x720 side-by-side (SBS) frame -> libx264 `ultrafast`/`zerolatency`, no B-frames -> WebRTC or UDP -> native visionOS app (VideoToolbox decode -> Metal/RealityKit texture). 720p per eye is chosen because 2560x720 is ~1.84 MP per frame, about 89% of one 1080p frame [D: 2560x720 = 1,843,200 px vs 1920x1080 = 2,073,600 px]. ## 1. Reference facts used | Fact | Value | Source | |---|---|---| | Vision Pro display refresh rates | 90, 96, 100, 120 Hz (frame period 8.3-11.1 ms) | https://www.apple.com/apple-vision-pro/specs/ [S] | | Apple R1 photon-to-photon | 12 ms, for the headset's **own passthrough cameras**; it does **not** apply to streamed video | https://www.apple.com/apple-vision-pro/specs/ [S] | | Vision Pro Wi-Fi | Wi-Fi 6 (802.11ax) | https://www.apple.com/apple-vision-pro/specs/ (verified earlier, see ideas.md) | | Raspberry Pi 5 onboard Wi-Fi | **dual-band 802.11ac (Wi-Fi 5)**, Infineon CYW43455 | https://www.raspberrypi.com/products/raspberry-pi-5/ and https://www.raspberrypi.com/news/introducing-raspberry-pi-5/ [S] | | Pi 5 has no H.264 hardware encoder; 1080p30 libx264 low-latency uses ~60-90% of one core | | Raspberry Pi white paper RP-010033-WP-1 (URL in ideas.md) [S] | | USB webcam -> display, 30 fps, USB 3 host: "direct" (no encode) 110 ms glass-to-glass incl. a 60 Hz display (adds 0-17 ms) and 33 ms frame interval; software H.264 enc+dec adds ~10 ms each; local WebRTC ~130 ms total; remote WebRTC via TURN 170 ms (30 ms ping RTT) | measured with infinite mirror on RealSense D435, Logitech C925e, SO101 cam | https://transitiverobotics.com/blog/webrtc-latency-breakdown/ [S] | | WebRTC jitter buffer | ~7 ms local, ~10 ms remote | same Transitive page (chrome://webrtc-internals) [S] | | VisionProTeleop video to Vision Pro, **synthetic frames, no camera**, includes sender encode + network + AVP decode/render + gRPC return, 1,000 trials/res, 30 fps, MacBook Pro M2 -> AVP over Wi-Fi 6 | stereo 3840x1080 wireless: mean 56.6, p50 64, min 16, p95 113, p99 140, max 179 ms. Stereo 2560x720 wireless: mean 59.9, p50 52, min 40, p95 117. Wired (USB-C dev strap) stereo 3840x1080: mean 23.0, p95 40 ms. | `benchmarks/wireless-stereo.json`, `wired-stereo.json` in https://github.com/Improbable-AI/VisionProTeleop (cloned at `research/repos/VisionProTeleop`), method `docs/benchmark.md`; README: wireless <=720p "<100 ms", wired stereo 4K ~50 ms [S] | | Wi-Fi 6 5 GHz ping | ~7 ms average to an internet speedtest server (Ethernet 2.2 ms); ~10 ms idle, 19.7 ms avg and 41 ms max with 10 busy devices | https://mybroadband.co.za/news/wireless/572355-we-tested-wifi-4-5-and-6-here-are-the-results.html , https://mybroadband.co.za/news/wireless/438350-we-tested-wi-fi-5-and-wi-fi-6-to-see-if-there-is-a-big-difference.html [S] | | Wi-Fi jitter | Wi-Fi 6 5 GHz idle avg jitter 4.2 ms, max spike 22 ms; Ethernet 0.3 ms avg, 1.4 ms max; Wi-Fi heavy load avg 47.8 ms | https://jitter.is/blog/ethernet-vs-wifi-jitter/ [S] | | Wi-Fi 6, 80 MHz channel, lab study | 99th-percentile latency below 10 ms in the evaluated scenario | https://doi.org/10.1109/globecom48099.2022.10001475 [S] | | Wi-Fi 6 vs Wi-Fi 5 under moderate load | jitter 17.8 vs 19.1 ms, essentially no gain; 6 GHz (Wi-Fi 6E) 6.9 ms | jitter.is page above [S] | | ARKit predictive tracking in VisionProTeleop | prediction setting 0 / 5 (default) / 33 / 100 ms; "100 ms may cause overshoot on fast movements" | VisionProTeleop README, section on predictive hand tracking [S] | ## 2. Per-stage budget (ms) Scenario (a): machine on Ethernet, headset on Wi-Fi 6. "Best" = everything goes right; "worst" = every stage at its bad tail at once (sum of worsts is a ceiling, not an expected value; real p95 is lower). | # | Stage | Wired / wireless | Best | Typical | Worst | Basis | |---|---|---|---|---|---|---| | S1 | Head pose sample + short prediction + UDP send, headset | on headset | 1 | 6 | 12 | [E] pose polled at display rate, 90 Hz => average wait half period 5.6 ms, max 11.1 ms [D from 90 Hz spec] | | S2 | Wi-Fi 6 uplink, headset -> router -> Ethernet -> machine (tiny UDP packet) | **WIRELESS** (Wi-Fi 6) | 2 | 4 | 25 | [E] about half of the ~7-8 ms Wi-Fi 6 ping, tail from 22 ms idle jitter spike (jitter.is) | | S3 | Machine: UDP receive, clamp/limit, write servo command | WIRED | 1 | 3 | 20 | [E]/[D]: bus servo (STS3215): 10-byte packet at 1 Mbps = 0.1 ms [D]; PWM servos at 50 Hz wait up to 20 ms for the next frame [D from 1/50 Hz]; DS3218 accepts 50-330 Hz [S dsservo datasheet] | | S4 | Servo response (following error while tracking slow turns; see section 4 for fast turns) | WIRED control | 15 | 40 | 90 | [E] internal position loop + inertia; no vendor figure | | S5 | Camera: exposure + sensor readout + ISP/firmware buffering + USB 2.0 MJPEG transfer | **WIRED** USB | 50 | 90 | 110 | typical/worst from Transitive "direct" 110 ms total minus ~0-17 ms display [S/D]; best = 60 fps modes with short exposure [E]; USB 2.0 transfer of one ~300 KB MJPEG frame is ~5-10 ms [E] | | S6 | MJPEG decode x2 + SBS compose on the machine (Pi 5 CPU) | machine | 6 | 14 | 25 | [E] libjpeg-turbo class decode of two 720p frames | | S7 | H.264 software encode of 2560x720 SBS (libx264 ultrafast zerolatency, sliced threads) | machine | 8 | 20 | 35 | [D/E] white paper: ~60-90% of a core per 1080p30 stream => 20-30 ms CPU time per frame single-threaded, x 0.89 for pixel count; threads cut wall time. Transitive measured ~10 ms on a desktop | | S8 | Packetize + Wi-Fi 6 downlink, machine -> Ethernet -> router -> headset (~20 Mbps, ~80 KB/frame average) | **WIRELESS** (Wi-Fi 6) | 3 | 6 | 40 | [E] airtime ~2-4 ms at 200-400 Mbps effective [D]; tail from VisionProTeleop wireless p95 113 / p99 140 ms total | | S9 | Jitter buffer | headset | 2 | 7 | 20 | typical 7 ms [S Transitive local]; best/worst [E] | | S10 | Hardware decode (VideoToolbox) | headset | 2 | 5 | 10 | [E] Transitive: ~10 ms for *software* decode | | S11 | Texture upload + compositor + display scanout (90-120 Hz) | headset | 8 | 15 | 30 | [E] wait for vsync avg half period 4.2-5.6 ms + 1-2 compositor frames at 90-120 Hz [D from refresh spec]; RealityKit path slower than CompositorServices (see avp-live-video.md) | | | **Total (a)** | | **98** | **210** | **417** | sum | **Cross-check against measured data.** S7-S11 (encode -> Wi-Fi -> buffer -> decode -> display) sum to 23 / 53 / 135 ms (best/typ/worst). VisionProTeleop measured the same segment, with a Mac hardware encoder and synthetic frames, at wireless stereo 3840x1080: min 16, mean 57, p50 64, p95 113, p99 140 ms [S]. So the headset-side half of the budget is anchored on real Wi-Fi 6 + Vision Pro data. The camera half (S5) is anchored on Transitive's measurements [S]. The servo and machine stages (S1-S4, S6) are the least certain, and are [E]. **Reading the typical total.** About 210 ms is the time from the start of a head turn until the headset *shows* the result of that turn. Of this, camera+USB is ~43% and servo ~19%, so the network (S2+S8 = 10 ms) is **not** the problem; the camera and servo are. That matches the earlier finding in avp-live-video.md ("the latency floor of the cameras"). ## 3. Scenarios | Scenario | Changes vs (a) | Best | Typical | Worst | Notes | |---|---|---|---|---|---| | **(a)** machine on Ethernet -> router -> headset on Wi-Fi 6 | baseline | **98** | **210** | **417** | one wireless hop; router adds <1 ms [E]; contention from other Wi-Fi clients drives the tail | | **(b)** machine is the Wi-Fi access point / direct link to the headset | S2: 1.5 / 3 / 12; S8: 2 / 4 / 20 (no router, no other clients on your SSID) [E] | **96.5** | **207** | **384** | gain is small on average (~3 ms), mostly in the tail. **Gotcha:** the Pi 5's onboard radio is 802.11ac (Wi-Fi 5) [S], so Pi-as-AP gives the headset a Wi-Fi 5 link unless you add a Wi-Fi 6 adapter (USB or M.2). Wi-Fi 6 gave almost no jitter gain over Wi-Fi 5 under load [S jitter.is], so the isolation (own channel, own SSID, no neighbours' clients) matters more than the Wi-Fi generation. Use a non-DFS 5 GHz channel, 80 MHz. The AP duty also costs CPU on a machine already encoding video | | **(c)** remote over internet / VPN (short note) | adds one WAN round trip R to the loop (pose out on the way in, video on the way back), + ~3-6 ms VPN/NAT and jitter-buffer growth [E]; headset still has its local Wi-Fi 6 hop | R = 20 ms: ~**237**; R = 60: ~**277**; R = 150: ~**367** | | | Transitive: 30 ms ping RTT via TURN cost ~+40 ms on top of local 130 -> 170 [S]. R values are examples, not measurements. Head-pose packets need UDP / unordered unreliable data channel (no TCP head-of-line blocking). Worst-case tail grows with WAN jitter. Above roughly 150 ms total video lag you rely entirely on reprojection (section 6) | (c) values: typical (a) 210 + R + ~7 = 237 / 277 / 367 [D/E]. ## 4. Servo travel time from specs Wired control assumed (bus servo over serial, or PWM through a PCA9685, from the rig machine). Speeds convert as `deg/s = 60 / (s per 60 deg)` [D]. All speeds are **no-load** vendor figures; a camera payload and the 60-degree test angle both flatter them, so a **0.7 derating for load** is applied in the right-hand column [E]. | Servo (spec point) | s / 60 deg [S] | No-load deg/s [D] | Derated x0.7 [E] | 90-degree step, no-load / derated | Stall torque [S] | Weight [S] | Source | |---|---|---|---|---|---|---|---| | MG90S @ 6.0 V | 0.08 | 750 | 525 | 120 / 171 ms | 2.2 kg·cm @6.6 V (1.8 @4.8 V) | 13.4 g | https://www.towerpro.com.tw/product/mg90s-3/ | | MG90S @ 4.8 V | 0.10 | 600 | 420 | 150 / 214 ms | 1.8 kg·cm | 13.4 g | same | | MG996R @ 6.0 V | 0.15 | 400 | 280 | 225 / 321 ms | 11 kg·cm | 55 g | https://www.towerpro.com.tw/product/mg996r/ (the page's table says 0.17 s @4.8 V while the text says 0.19 s; the text values are used) | | MG996R @ 4.8 V | 0.19 | 316 | 221 | 285 / 407 ms | 9.4 kg·cm | 55 g | same | | DS3218 @ 6.8 V | 0.14 | 429 | 300 | 210 / 300 ms | 21.5 kg·cm | 60 g | https://dsservo.com/down.asp?id=22 (datasheet) and https://www.zennixtek.com/products/dsservo-ds3218-mg-servo-motor | | DS3218 @ 5 V | 0.16 | 375 | 262 | 240 / 343 ms | 18 kg·cm | 60 g | same | | Feetech STS3215 (7.4 V version) @ 7.4 V | 0.192 | 312 | 219 | 288 / 411 ms | 19.5 kg·cm | 55 g | https://docs.waveshare.com/ST3215_Servo (Waveshare lists 0.192 s/60 deg at 7.4 V) | | Feetech STS3215 7.4 V version @ 6 V | 0.238 | 252 | 176 | 357 / 510 ms | 19.5 kg·cm (Feetech product page text; its summary table says 16.5) | 55 g | https://www.feetech.cn/en/74v-19-kgcm-plastic-case-metal-tooth-magnetic-code-double-axis-ttl-series-steering-gear | | Feetech STS3215 12 V version @ 12 V | 0.222 | 270 | 189 | 333 / 476 ms | 30 kg·cm | 55 g | https://www.feetech.cn/en/525603.html and datasheet https://cdn.robotshop.com/media/F/Fit/RB-Fit-155/pdf/feetech_12v_30kg_cm_magnetic_encoding_servo_sts321_specification_pdf.pdf | Notes: STS3215 is a serial bus servo (1 Mbps max, 12-bit magnetic encoder, position/speed/load/current feedback, built-in acceleration ramp) [S Feetech/Waveshare pages]. DS3218 accepts 50-330 Hz PWM, 500-2500 us pulse, 3 us dead band [S datasheet]. MG90S/MG996R PWM frame rate is not specified on the Tower Pro pages, so assume the standard 50 Hz [E]. ### 4.1 Human head speed (the requirement) | Motion | Angular velocity | Source | |---|---|---| | Walking/running head motion, group median max | not above 90 deg/s | https://pubmed.ncbi.nlm.nih.gov/3384048/ (Grossman et al.) [S] | | Walking, peak head yaw/pitch | ~17 deg/s | https://doi.org/10.1111/j.1749-6632.2001.tb03741.x [S] | | Deliberate slow look-around (your 30-90 deg/s range) | 30-90 deg/s | user's assumption; consistent with the rows above | | Rapid voluntary yaw in agility tasks / rapid 360-degree turn | ~252 / ~381 deg/s (average peaks) | Utah thesis, https://collections.lib.utah.edu/details?id=2077305 [S, from search-result summary; page itself returned HTTP 418 to my fetcher, so verify before quoting] | | Vigorous voluntary yaw, 20 subjects | group median max **780 deg/s** | https://pubmed.ncbi.nlm.nih.gov/3384048/ [S] | | Fastest voluntary head motion, 9 subjects (any plane) | ~25 rad/s (~1,430 deg/s) | https://pmc.ncbi.nlm.nih.gov/articles/PMC6444911/ [S, search summary; page returned 403 to my fetcher]. Same paper: velocities "usually peaked and returned to zero within 400 ms" | Practical design range: **slow 30-90 deg/s, quick 250-380 deg/s**. Anything near 780 deg/s is a reflex flick you will not track with hobby servos and need not. ### 4.2 Can the servo keep up? (extra lag when speed-limited) For a 60-degree turn at constant head speed w, a servo capped at speed v finishes late by `60 * (1/v - 1/w)` [D]. Derated v used. Extra lag in ms, on top of the S4 following error: | Servo (derated v) | 30 deg/s | 90 deg/s | 250 deg/s | 380 deg/s | 780 deg/s | |---|---|---|---|---|---| | MG90S @6 V (525 deg/s) | keeps up | keeps up | keeps up | keeps up | +37 | | DS3218 @6.8 V (300) | keeps up | keeps up | keeps up | +42 | +123 | | MG996R @6 V (280) | keeps up | keeps up | keeps up | +56 | +137 | | STS3215 @7.4 V (219) | keeps up | keeps up | +34 | +116 | +197 | | STS3215 @6 V (176) | keeps up | keeps up | +100 | +182 | +263 | Slow turns (30-90 deg/s): every candidate keeps up with large margin; servo lag is just S4 (~40 ms typical [E]). Quick turns (250-380 deg/s): DS3218/MG996R at 6-6.8 V are about at the limit; STS3215 at 7.4 V lags 34-116 ms extra, and at 6 V 100-182 ms. For reference, a human quick turn also lasts only ~100-400 ms [S PMC], so a +100 ms lag is a large fraction of the whole motion. ### 4.3 Torque needed Two payload cases, estimated by hand: small = two ~38 mm USB modules + bracket ~0.15 kg, centre of mass 40 mm from the axis; large = two Logitech Brio-class cameras + bracket ~0.30 kg at 60 mm. (Brio weighs 65 g, 102 mm wide per Logitech's spec page [S, via `MB-001-cameras.csv`], so two Brios cannot sit 63 mm apart; large case implies a wider baseline.) Inertia `I = m r^2` [D]; torque `T = I x alpha` with alpha chosen to reach the head speed in 60 ms [D/E]. | Case | Static torque if CoM is 1 axis-radius off-axis | Dynamic torque to reach 90 / 250 / 380 deg/s in 60-150 ms | |---|---|---| | small (0.15 kg, 40 mm) | 0.6 kg·cm | 0.03 / 0.18 / 0.27 kg·cm | | large (0.30 kg, 60 mm) | 1.8 kg·cm | 0.12 / 0.8 / 1.2 kg·cm | Add the tilt axis carrying the weight against gravity (balance the camera platform around the tilt axis and the static term nearly vanishes), plus servo-horn friction and cable drag [E]. With a **3x margin on the stall figure** [E rule of thumb], you need about 2-3 kg·cm for the small case and 6-9 kg·cm for the large case. MG90S (1.8-2.2 kg·cm stall) is therefore marginal even for the small case; MG996R, DS3218, STS3215 all clear both cases. ## 5. Recommendation (servo speed and torque) Target: servo lag (S4 + saturation) should be **well below the rest of the loop**. The loop excluding servo is ~170 ms typical (210 minus S4's 40), so aim for servo lag **<= ~40 ms for slow turns and < ~60 ms extra on quick turns** (about a quarter or less of the rest of the loop). * **Speed:** want at least **~400 deg/s no-load (<= 0.15 s/60 deg at the supply voltage you actually run), ~280+ deg/s after load**, so a 250 deg/s turn is followed without saturating. Above ~380 deg/s head speed, let the servo saturate and rely on headset-side reprojection (section 6) instead of buying faster servos. * **Torque:** pick **>= 10 kg·cm stall** for both axes if you use heavy cameras, >= 5 kg·cm for the small modules; balance the tilt platform around its axis. * **Candidates that fit:** **DS3218 at 6.8 V** (429 deg/s, 21.5 kg·cm, 60 g, PWM up to 330 Hz => S3 ~3 ms) is the best pure speed/torque fit; **MG996R at 6 V** (400 deg/s, 11 kg·cm) is the cheaper fit; **Feetech STS3215 at 7.4 V** (312 deg/s, 19.5 kg·cm) is slightly slower than the target but gives serial control (no PWM frame wait, 0.1 ms per command [D]), feedback of position/load, and a hardware acceleration ramp, which is valuable for the failsafe/park behaviour; run it at the full 7.4 V and keep the speed limit just under 300 deg/s. **MG90S** is fastest (750 deg/s) but only suits the small-module case and has low torque margin. * **Power:** the speed figures only hold at the quoted voltage; sagging a 6.8 V servo to 5 V costs ~13% speed and 16% torque (DS3218: 0.14 -> 0.16 s/60 deg, 21.5 -> 18 kg·cm) [D from datasheet]. Use a separate servo supply with enough current (DS3218 stall 2.2-2.6 A each). * **Control path:** wired PCA9685 or bus-servo board on the rig machine, UDP receiver with a 100-200 Hz command loop. Do not use a 50 Hz-only PWM path if you can avoid it: it adds up to 20 ms of command wait [D]. ## 6. Smoothing vs prediction vs reprojection * **Smoothing adds lag.** A first-order low-pass at cutoff fc adds roughly `1/(2 pi fc)` of delay [D]: 20 Hz => 8 ms, 10 Hz => 16 ms, 5 Hz => 32 ms. Servos twitch from head tremor and 90-120 Hz pose noise, so some filtering is needed; use a velocity-adaptive filter such as the One Euro filter (heavy smoothing when slow, light when fast) and keep the cutoff >= 10 Hz when moving [E]. Budget ~10-15 ms of smoothing lag, already inside S3/S4. * **Prediction removes lag, but only a little.** Extrapolating head pose ahead by `dt` helps if the motion is smooth. VisionProTeleop ships ARKit prediction of 0/5/33/100 ms and warns that 100 ms overshoots on fast movement [S]. A quick head turn lasts only 100-400 ms [S PMC], so predicting 150-200 ms (the full loop) is beyond what a human turn is predictable over, and it will overshoot at reversals. Practical: predict **30-60 ms** on the pose sent to the rig (enough to cancel S1-S3 plus part of S4) [E]; clamp slew rate and acceleration at the servo. * **Prediction cannot hide camera+encode+network lag** (S5-S11 ~ 150 ms typical): those happen after the servo has already moved, and they delay the *picture*, not the command. * **Show the video reprojected by the headset's current pose.** The loop lag creates an angular mismatch of `w x T` [D]: with ~210 ms total, a 30 / 90 / 250 / 380 deg/s head turn leaves the picture 6 / 19 / 53 / 80 degrees behind the head. If the video is pinned to the head (like a screen on your face), the whole world appears to swim; instead, draw the stereo quad **world-locked at the rig's pan/tilt angle at capture time** (send the commanded/measured angles in the frame metadata) and let the Vision Pro's compositor (which does run at 90-120 Hz and reprojects the layer by the current head pose, see avp-live-video.md section on CompositorServices) move it. The lag then shows up as the image edge catching up rather than a laggy world; the Apple 12 ms R1 figure applies to that headset-local part, not to the video content itself [S Apple, D]. * Cheap extra: show a thin local "ghost" frame/horizon at the head's current direction, so the wearer sees how far the rig is behind. ## 7. Levers that cut the budget, ranked [E unless noted] | Lever | Saves (typ) | Cost | |---|---|---| | 60 fps camera mode with short exposure (720p MJPEG, if the module offers it) | ~30-40 ms in S5 (frame interval 33 -> 17 ms [D]) | needs more light; check `v4l2-ctl --list-formats-ext` | | MJPEG passthrough (no decode, no H.264 encode, send both JPEG frames) | S6+S7 = ~34 ms | 10-40 Mbps per eye [S avp-live-video.md]; lost frames; no SBS pairing guarantee | | Hardware-encode board instead of Pi 5 | ~10-15 ms in S7 and less CPU | different board | | Digital servo at 6.8 V or serial bus servo, 200+ Hz command | S3 up to ~15 ms and S4 ~10-20 ms | cost, power supply | | Pi as dedicated AP on a clean 5 GHz channel with Wi-Fi 6 adapter | ~3 ms typical, much less tail | extra adapter | | CompositorServices path instead of RealityKit | maybe 10-20 ms in S11 | harder; no published numbers (avp-live-video.md) | Realistic tuned target: ~150-170 ms typical on the same-LAN setups, i.e. still well above the "comfortable" 20-40 ms figure quoted in `MB-001-touchdesigner-avp-stream.md`, which is why reprojection (section 6) matters more than any one component. ## 8. How to measure it (replace the [E] values) 1. **Camera + USB only:** Transitive's infinite-mirror method (point the camera at a screen showing a timecode), `gst-launch-1.0 v4l2src ... ! image/jpeg ! jpegdec ! timeoverlay ! autovideosink` [S https://transitiverobotics.com/blog/webrtc-latency-breakdown/]. Do this for both the Quanrui USB48MP01-QFZ and a Brio. 2. **Wi-Fi leg:** `ping -i 0.01` and `iperf3 -u` between the machine and a Mac on the same Wi-Fi; log the p99. 3. **Headset display leg:** run VisionProTeleop's `latency_test.py --sweep` [S repo] to reproduce the 57 ms (wireless stereo) anchor with your router. 4. **Servo:** film a red LED on the servo arm and a pose-timestamp LED at 240 fps (phone slow-mo), command a 30-degree step, count frames [E]. 5. **End to end:** wear the headset, film the headset-side display through the lens at 240 fps while moving a target the rig's cameras see. ## 9. Open items / uncertainty * S3, S4, S6, S7, S9-S11 are estimates; S5 is derived from a third-party measurement on different cameras; the stereo pair of two unsynchronised USB cameras adds up to ± one frame interval (33 ms at 30 fps [D]) of left/right mismatch [E] unless frames are paired by timestamp. * The Utah thesis and PMC head-velocity numbers were read from search-result summaries because both pages blocked my fetcher. * MG996R's Tower Pro page contradicts itself on 4.8 V speed (0.17 in the table, 0.19 in the text), and Feetech's page lists 19.5 and 16.5 kg·cm for the same 7.4 V STS3215. Use measured values from your own units.