Working with NDI Tracking Systems

The ndi_tracking module provides Python bindings for NDI (Northern Digital Inc.) tracking devices, enabling instrument tracking in custom ImFusion applications.

NDI offers two fundamentally different tracking technologies:

  • Optical tracking (Polaris systems): An infrared camera detects reflective or active LED markers attached to instruments and computes their 6-DoF poses. Requires clear line-of-sight between the camera and the tracked tools.

  • Electromagnetic tracking (Aurora): A magnetic field generator creates a working volume in which small EM sensors embedded in instruments are tracked. Works through soft tissue without requiring line-of-sight, but is sensitive to nearby ferromagnetic objects.

Two stream classes are available, corresponding to different NDI hardware families:

Class

Hardware

Connection

NDIPolarisTrackingStream

Polaris Vega, Polaris Lyra

Ethernet (TCP/IP)

NDIAuroraTrackingStream

Aurora

USB / serial

Note

Import the ndi_tracking module before using any of the classes below. The imfusion.stream module is imported automatically as a dependency.

import imfusion
import ndi_tracking

Optical Tracking with Polaris Vega / Lyra

The NDIPolarisTrackingStream connects to a Polaris Vega or Lyra system over Ethernet. Each tracked instrument must be described by a .rom file that encodes the 3-D geometry of its reflective markers. ROM files can be obtained from NDI for standard instruments or created with the NDI Cygna 6D software for custom tools.

Basic setup with two tools:

import imfusion
import ndi_tracking

stream = ndi_tracking.NDIPolarisTrackingStream()
stream.ip_address = "192.168.1.42"  # IP of the Polaris system
stream.port = 8765                   # default NDI port

stream.add_tracking_marker("C:/NDI/tools/pointer.rom", "Pointer")
stream.add_tracking_marker("C:/NDI/tools/reference.rom", "Reference Frame")

stream.open()
stream.start()

Use ip_address = "auto" when the Polaris system is on the same subnet and you want automatic IP detection:

>>> stream = ndi_tracking.NDIPolarisTrackingStream()
>>> stream.ip_address = "auto"
>>> stream.add_tracking_marker("C:/NDI/tools/pointer.rom", "Pointer")
>>> stream.open()
>>> stream.start()

To record the tracking data, pass the stream into a imfusion.stream.StreamRecorderAlgorithm:

import time
import imfusion
import ndi_tracking

stream = ndi_tracking.NDIPolarisTrackingStream()
stream.ip_address = "192.168.1.42"
stream.add_tracking_marker("C:/NDI/tools/pointer.rom", "Pointer")
stream.open()
stream.start()

recorder = imfusion.stream.StreamRecorderAlgorithm([stream])
recorder.start()
time.sleep(5)  # record for 5 seconds
recorder.stop()

stream.stop()
stream.close()

tracking_sequences = recorder.output()
imfusion.save(tracking_sequences, "tracking.imf")

Electromagnetic Tracking with Aurora

The NDIAuroraTrackingStream connects to an NDI Aurora EM tracking system via USB. Unlike the optical systems, a ROM file registration is typically not needed — instruments are auto-detected when the stream is opened.

import imfusion
import ndi_tracking

stream = ndi_tracking.NDIAuroraTrackingStream()
stream.hardware_device = "auto"

stream.open()
stream.start()
# all connected EM sensors are available automatically

The measurement rate can be configured before opening the stream. List the available options and select by index:

stream = ndi_tracking.NDIAuroraTrackingStream()
stream.hardware_device = "auto"

options = stream.measurement_rate_options
print("Available rates:", options)
stream.measurement_rate = next(
    i for i, r in enumerate(options) if r == "115200"
)

stream.open()
stream.start()

Custom ROM files for Aurora probes

Standard Aurora instruments are auto-detected on open(). For custom probes that ship with a ROM file, call register_tool() before opening the stream:

stream = ndi_tracking.NDIAuroraTrackingStream()
stream.hardware_device = "auto"
stream.register_tool("Custom Probe", "C:/NDI/tools/custom_probe.rom", 3) # the tool is plugged into port 3
stream.open()
stream.start()

Note

EM tracking accuracy degrades near ferromagnetic objects (steel tables, surgical tools). Always validate the tracking accuracy in the intended clinical environment.

Working with Tracking Data

Both stream classes derive from imfusion.stream.TrackingStream. Recorded output is a list of imfusion.TrackingSequence objects, one per registered tool. Each imfusion.TrackingSequence holds a time series of 4×4 transformation matrices representing the tool poses in the tracker’s coordinate system.

import time
import imfusion
import ndi_tracking

# Set up whichever stream type matches your hardware
stream = ndi_tracking.NDIPolarisTrackingStream()
stream.ip_address = "192.168.1.42"
stream.add_tracking_marker("C:/NDI/tools/pointer.rom", "Pointer")
stream.add_tracking_marker("C:/NDI/tools/reference.rom", "Reference")
stream.open()
stream.start()

recorder = imfusion.stream.StreamRecorderAlgorithm([stream])
recorder.start()
time.sleep(3)
recorder.stop()

stream.stop()
stream.close()

sequences = recorder.output()
for seq in sequences:
    print(f"Tool '{seq.tracker_id.name}': {seq.size} poses recorded")

# Save to disk for later processing
imfusion.save(sequences, "navigation_recording.imf")