Skip to main content
Trackers are input modules that provide 6DOF (degrees of freedom) head position and rotation data. OpenTrack supports a wide variety of tracking technologies.

Tracker Module System

OpenTrack uses a plugin-based tracker system:
struct OTR_API_EXPORT ITracker {
    virtual module_status start_tracker(QFrame* frame) = 0;
    virtual void data(double *data) = 0;
    virtual bool center();
};

Module Selection

Trackers are selected through the module settings:
struct module_settings {
    bundle b { make_bundle("modules") };
    value<QString> tracker_dll { b, "tracker-dll", "pt" };
};

Available Trackers

OpenTrack includes support for many tracking technologies:

PointTracker (pt)

Uses webcam to track LEDs or reflective markers.Best for: DIY LED tracking setups, TrackIR alternativesRequirements:
  • Webcam (higher resolution = better accuracy)
  • 3 LED or reflective points on headset
  • Good lighting conditions

Aruco Markers

Tracks printed ArUco fiducial markers.Best for: Quick testing without special hardwareRequirements:
  • Webcam
  • Printed ArUco marker attached to headset

Easy Tracker

Face tracking using computer vision.Best for: No additional hardware needed

Tracker Initialization

When a tracker starts, it goes through this initialization sequence:
1

Module Loading

The tracker plugin is loaded from the shared library:
std::shared_ptr<ITracker> pTracker;
// Loaded from tracker-{name}.dll/so
2

Initialization

The tracker’s start_tracker method is called:
module_status start_tracker(QFrame* frame) {
    // Initialize hardware/software
    // Optional: Display video feed in frame
    return status_ok(); // or error("message")
}
3

Data Streaming

The data() method is called at ~250Hz:
void data(double *data) {
    // data[0-2]: TX, TY, TZ (cm)
    // data[3-5]: Yaw, Pitch, Roll (degrees)
}

Data Format

All trackers must provide data in OpenTrack’s standard format:
using Pose = Mat<double, 6, 1>;

enum Axis : int {
    TX = 0, TY = 1, TZ = 2,      // Translation (cm)
    Yaw = 3, Pitch = 4, Roll = 5  // Rotation (degrees)
};
Translation units: Centimeters Rotation units: Degrees Coordinate system: Right-handed

Axis Definitions

  • TX: Left (-) / Right (+)
  • TY: Down (-) / Up (+)
  • TZ: Forward (-) / Backward (+)
  • Yaw: Left (-) / Right (+)
  • Pitch: Down (-) / Up (+)
  • Roll: Left (-) / Right (+)

Centering Support

Trackers can optionally handle centering internally:
virtual bool center() {
    // Return true if tracker handles centering internally
    // Return false to use OpenTrack's centering
    return false;
}

Centering Flow

const bool own_center_logic = center_ordered && libs.pTracker->center();

if (own_center_logic) {
    // Tracker resets itself
    center.P  = {};
    center.QC = {};
    center.QR = {};
} else {
    // OpenTrack handles centering
    center.P  = value;
    center.QC = dquat::from_euler(...).conjugated();
    center.QR = dquat::from_euler(...).conjugated();
}

Tracker Dialog

Each tracker can provide a configuration dialog:
struct OTR_API_EXPORT ITrackerDialog : public BaseDialog {
    virtual void register_tracker(ITracker *tracker);
    virtual void unregister_tracker();
};
The dialog receives a pointer to the active tracker instance for live configuration updates.

Common Tracker Settings

Video Feed Display

Optical trackers can display camera feed:
module_status start_tracker(QFrame* frame) {
    if (frame) {
        // Set up video widget in frame
        video_widget = new VideoWidget(frame);
    }
    return status_ok();
}

Exposure and Gain

For optical trackers, proper camera settings are critical:
Low exposure (1-5ms) and high gain provide best results for LED tracking. Automatic exposure often causes tracking issues.

Point Extraction

Optical trackers typically use threshold-based point extraction:
  1. Apply brightness threshold
  2. Find connected components (blobs)
  3. Calculate blob centroids
  4. Match points to 3D model

Tracker Performance

The pipeline runs at ~250Hz (4ms interval):
void pipeline::run() {
    while (!isInterruptionRequested()) {
        logic();
        
        constexpr ms interval{4};
        backlog_time += ms{t.elapsed_ms()} - interval;
        
        const int sleep_ms = (int)std::clamp(
            interval - backlog_time, ms{0}, ms{10}
        ).count();
        
        portable::sleep(sleep_ms);
    }
}
Trackers should not block in the data() method. Use a separate thread for computation and return the latest data.

Error Handling

Trackers can report errors during initialization:
module_status initialize() override {
    if (!hardware_found()) {
        return error(tr("Hardware not found"));
    }
    
    if (!calibration_valid()) {
        return error(tr("Calibration required"));
    }
    
    return status_ok();
}

Creating Custom Trackers

To implement a custom tracker:
1

Implement ITracker interface

class MyTracker : public ITracker {
    module_status start_tracker(QFrame* frame) override;
    void data(double *data) override;
};
2

Implement dialog (optional)

class MyTrackerDialog : public ITrackerDialog {
    void register_tracker(ITracker* t) override;
    void unregister_tracker() override;
};
3

Implement metadata

class MyTrackerMetadata : public Metadata {
    QString name() override { return tr("My Tracker"); }
    QIcon icon() override { return QIcon(":/icon.png"); }
};
4

Register plugin

OPENTRACK_DECLARE_TRACKER(
    MyTracker,
    MyTrackerDialog,
    MyTrackerMetadata
)

Troubleshooting

  1. Check tracker update rate (should be stable)
  2. Verify lighting conditions (for optical trackers)
  3. Enable filtering (see Filters guide)
  4. Check for USB bandwidth issues
  1. Inertial drift is normal for IMU trackers
  2. Enable fusion with optical tracking if possible
  3. Periodically recenter
  4. Check for magnetic interference (IMU trackers)
  1. Verify hardware is connected
  2. Check device drivers are installed
  3. Try different USB ports
  4. Check OpenTrack logs for error messages
Use axis mapping and inversion in Configuration:
axis_opts::src = desired_axis;    // Map input axis
axis_opts::invert_pre = true;     // Invert if needed

Next Steps

Output Setup

Configure game/simulator output protocols

Filters

Add smoothing and noise reduction

Mapping Curves

Fine-tune tracking response