Reads rigid body data from a Motive 2.3.0 OptiTrack stream (NatNet 4.0) and forwards position and orientation as OSC messages to any host and port you choose.
Each tracked rigid body produces two OSC messages per frame:
/optitrack/<name>/position float x float y float z (metres)
/optitrack/<name>/rotation float yaw float pitch float roll (degrees, default)
— or —
/optitrack/<name>/rotation float w float x float y float z (quaternion)
The rotation format is selectable at startup (see --rotation-format below). The default is Euler angles using the YXZ Tait-Bryan convention (yaw = rotation around Y, pitch = X, roll = Z), which matches Motive's Y-up coordinate system.
- Anaconda or Miniconda
- Motive 2.3.0 running on the same machine or network, with Broadcast Frame Data enabled and the streaming interface set to the correct network adapter
git clone https://github.com/scheerchristian/optitrack-osc.git
cd optitrack-oscconda create -n optitrack-osc python=3.11
conda activate optitrack-oscpip install -e .This installs python-osc and registers the optitrack-osc command inside the active environment.
- Open Motive 2.3.0 and go to View → Data Streaming Pane.
- Make sure Broadcast Frame Data is checked.
- Set Local Interface to the network adapter that connects to the machine running OptitrackOSC.
- Leave everything else as default.
- Create one or more Rigid Body assets in Motive. Their names will appear in the OSC address path.
Make sure the Conda environment is active first:
conda activate optitrack-oscoptitrack-oscoptitrack-osc --server-ip 192.168.1.10optitrack-osc --server-ip 192.168.1.10 --osc-host 192.168.1.20 --osc-port 8000optitrack-osc --rotation-format quaternionoptitrack-osc --help
--server-ip IP Motive machine IP address (default: 127.0.0.1)
--local-ip IP Local network interface for multicast (default: 0.0.0.0)
--osc-host IP OSC target host (default: 127.0.0.1)
--osc-port PORT OSC target port (default: 9000)
--rotation-format FORMAT euler or quaternion (default: euler)
-v, --verbose Enable debug logging
optitrack-osc --server-ip 192.168.1.10 -v| Address | Arguments | Notes |
|---|---|---|
/optitrack/<name>/position |
x y z (float, metres) |
World-space position |
/optitrack/<name>/rotation |
yaw pitch roll (float, degrees) |
YXZ Tait-Bryan Euler angles (--rotation-format euler, default) |
/optitrack/<name>/rotation |
w x y z (float) |
Unit quaternion (--rotation-format quaternion) |
<name> is the rigid body name set in Motive. Spaces and special characters are replaced with underscores.
- Make sure Edit → Settings → Streaming → Up Axis is set to Y Up.
- Place the passive tracker flat on a table in its intended neutral orientation, fully visible to the cameras.
- In the Assets pane, right-click the rigid body → Reset Pivot. This makes the current pose the zero reference (identity quaternion). Name the rigid body head if you are using benchmark-headtracker.
- Confirm tracking is stable (solid marker colours, low mean error).
conda activate optitrack-osc
optitrack-oscNo further calibration is needed in this app — Motive's Reset Pivot defines the zero.
benchmark-headtracker compares the OptiTrack stream against a webcam-based head tracker across four scenarios: stable grid positions, trajectory following, drift, and latency.
Open head_tracker_benchmark.py and confirm the constants at the top match your setup:
OPTITRACK_PORT = 8001
OPTITRACK_ADDR = "/optitrack/head/rotation" # must end in /rotation, not /quaternion
WEBCAM_PORT = 8000
WEBCAM_ADDR = "/Virtuoso/quat" # address sent by your webcam tracker-
Start optitrack-osc in quaternion mode, targeting the benchmark port:
optitrack-osc --osc-port 8001 --rotation-format quaternion
-
Start your webcam head tracker and make sure it is sending OSC to port
8000. -
Start the benchmark:
conda activate benchmark-headtracker # or whichever env has the dependencies python head_tracker_benchmark.py -
The benchmark waits 2 seconds for both OSC streams to appear, then reports whether each source is live. Calibrate inside the benchmark when prompted — OptiTrack's pivot reset already defines the mechanical zero, so the benchmark calibration only aligns the two trackers' coordinate frames against each other.
-
Follow the on-screen guidance for each measurement mode. Results are saved to
benchmark_data/<timestamp>/.
No data arrives
- Confirm Broadcast Frame Data is enabled in Motive.
- Make sure the Local Interface in Motive is set to the adapter on the same subnet as your machine, not
127.0.0.1or0.0.0.0. - Windows Firewall may block multicast UDP. Add an inbound rule for UDP port
1511if needed.
Rigid body names show as numbers
- The model definition request did not complete before the first frame arrived. Names resolve within a second; if they never appear, check the
--server-ipvalue and firewall rules on port1510.
optitrack-osc command not found
- Make sure the Conda environment is activated:
conda activate optitrack-osc