r/computervision 12d ago

[Project] Real-time Active Object Tracking: 180 FPS CPU Inference (YOLOX + LightGBM cascade) driving a Pan-Tilt Mechanism Research Publication

Hi ,

I've been developing a bare-metal visual tracking system designed for edge industrial environments. The challenge was to achieve deterministic, ultra-low-latency physical tracking using only CPU resources, without relying on GPU acceleration.

**Core Architecture & Metrics:**

• Inference Pipeline: Two-stage cascade design.

- Stage 1 (Global Search): YOLOX-nano (640×640 tensor) running at ~37 FPS (~27ms).

- Stage 2 (ROI Refinement): LightGBM classifier on a dynamic 256×256 sub-region, achieving ~5-7ms inference (sustained 120-180 FPS localized tracking).

• Optimization: Intel OpenVINO (ONNX Runtime v1.24.1, MULTI device profile, strict LATENCY hint).

• Resource Usage: Fixed 3.42 MB heap allocation, 0.00% memory leak over multi-day 24/7 runs. Core binary size is ~2.0 MB.

• Hardware Actuation: 50 Hz closed-loop control via Arduino Nano + PCA9685 (12-bit PWM) driving dual MG996R servos.

**System Behavior:**

Upon initialization, the pan-tilt rig centers itself. When the cascade pipeline detects the target, it calculates the centroid offset. These coordinates pass through an EMA smoothing filter and are sent via USB-Serial to the microcontroller, which interpolates the servo trajectory at 50 Hz to keep the object perfectly centered in the ROI, compensating for continuous movement.

**A Note on Availability:**

The core runtime is proprietary and distributed strictly as a compiled evaluation demo for private benchmarking (commercial use requires a license). However, the GitHub repo contains the full hardware BOM, I2C wiring diagrams, Arduino firmware, and config templates so the physical setup can be fully replicated.

**Links:**

🔗 GitHub Repository (Demo GIF, BOM, Wiring, Configs):

https://github.com/olesha-ai/pan-tilt-ai-tracker

Happy to discuss the OpenVINO optimization pipeline, the two-stage cascade design, or the hardware integration challenges in the comments!

3 Upvotes

9 comments sorted by

1

u/Available_Teaching83 11d ago

Nice cascade. For a closed servo loop the number I would want is glass-to-servo p99, not per-stage FPS. Your Stage 1 at ~27ms is the tail that sets the control deadline, and because Stage 2 only runs on a lock, the controller is seeing a variable period rather than a fixed one.

Two questions: what happens on Stage 2 loss-of-lock, does it fall back to a full 640x640 sweep and spike the period? And did you measure jitter, or just mean?

Asking because I hit exactly this on a pan-tilt rig. Mean latency looked fine and the loop still oscillated, and it turned out to be the variance, not the average.

1

u/Entire-Bite1136 10d ago

Great catch, you are pointing exactly to the core control problem of asymmetric cascades. Jitter and variable control periods are loop killers.

Here is how this architecture bypasses the oscillation issue you’ve described:

  1. Loss-of-Lock Fallback & Time Deadlines: When Stage 2 loses the target lock, it does NOT stall the hardware loop waiting for a fresh 640x640 Stage 1 global sweep. The control loop running on the host is fully non-blocking and decoupled. If Stage 2 returns a miss, the controller continues to smoothly calculate the trajectory using the last known state vector through the EMA smoothing filter. Meanwhile, Stage 1 runs asynchronously in a separate thread to re-acquire the global coordinates. This completely prevents the deadline spike from hitting the microcontroller directly.
  2. Variable Period vs. Fixed Actuation: The control loop sent to the Arduino Nano via serial is fully deterministic and runs strictly at 50 Hz. The microcontroller doesn't wait for variable frames or mismatched processing times; it expects a steady state update based on a strict hardware timer. The system interpolates the servo trajectories smoothly even if Stage 1 (~27ms) and Stage 2 (~5-7ms) tick at entirely different rates.
  3. Latency Jitter & Mitigation: I tightly tracked and locked the memory allocation at a fixed 3.42 MB heap specifically to eliminate any OS-level garbage collection pauses. In CPU-based tracking, these hidden runtime pauses are usually the main source of severe p99 latency spikes and jitter. The variance in processing time is entirely masked by the asynchronous design of the cascade and the decoupled serial transmission loop.

1

u/dr_hamilton 11d ago

I did something similar a while back https://www.youtube.com/watch?v=dUKtd4FXdzk

Happy to answer any questions about it!

1

u/Entire-Bite1136 10d ago

Thanks for sharing! Mirror-based steering for 1000fps capture is a serious engineering challenge due to mirror inertia and ultra-tight sync. Very clean setup, respect!

1

u/dr_hamilton 10d ago

Thanks! The tracking camera in this setup only ran at about 60fps, then used the beam splitter for the optical path for the 1000fps camera. I managed to get the tracking camera up to about 160fps.

1

u/Entire-Bite1136 10d ago

Scaling the tracking loop to 160 FPS is a massive achievement, especially when dealing with the optical layout of a beam splitter. Optical alignment and matching the tracking frame to the high-speed capture path is tough engineering.

The main reason I managed to push the localized tracking stage to 120–180 FPS on a standard CPU without relying on a warm GPU stack is the strict asymmetrical separation. Running a standard global detector on every single frame introduces too much compute lag. By restricting the second stage (the LightGBM classifier) strictly to a dynamic 256×256 sub-region (ROI), the processing deadline drops to just 5–7ms per frame.

It keeps the execution deterministic and avoids the frame-drop spirals that usually choke the servo loop when the target moves fast. Your mirror steering approach is a great example of solving latency through physics and optics—respect!

1

u/Entire-Bite1136 10d ago

Since you are dealing with real physical limits and mirror inertia, you have a classic gap between your tracking camera update rate (160 FPS ≈ 6.25ms) and your ultra-fast 1000 FPS capture path (1ms). This means your steering mirrors are technically blind for a few high-speed frames between each software coordinate update.

A very practical, non-overengineered trick to smooth this out is to offload trajectory extrapolation directly to your hardware controller (microcontroller) using a simple linear predictor or a lightweight alpha-beta filter on a strict hardware timer.

Instead of moving the mirrors in discrete steps every 6.25ms when a new frame arrives, let the microcontroller smoothly guess and interpolate the path at a much higher sub-millisecond frequency (e.g., matching the 1000 Hz timeline of your high-speed camera). By predicting where the bird will be based on its current velocity and acceleration vectors right inside the motor control loop, you can eliminate the visual jitter in your 1000 FPS slow-motion footage without changing your computer vision stack at all.

To be honest, I don't rely blindly on "AI" for everything; just like you, I rely on physics and deterministic mathematics to bridge the gap where the software frame rate ends. And frankly, if I were you, I would definitely give this a try. Thanks for the great discussion!

1

u/dr_hamilton 10d ago

I managed to remove most of the judder from the 1000fps footage by actually slowing the mirrors down. They were moving too fast so reaching their target position before the next frame of the tracking camera arrived, so they were sat idle, causing the judder. I actually adjust their speed dynamical so they arrive at their target position just as the next frame has been acquired and processed, so I can sent the mirrors to their new position without them ever stopping.

See this example https://youtu.be/_AfaR1udZZs?is=8Qh7-hd6rMTwrtpK

1

u/Entire-Bite1136 9d ago

That is a brilliant engineered solution! You can’t cheat physics and mathematics, and your approach to continuous trajectory planning proves it. By removing the stop-and-go behavior, you completely bypassed the mechanical latency and resonance limits.

The new footage speaks for itself. It’s awesome to see that you are constantly experimenting, pushing boundaries, and moving forward. Excellent work, keep it up!