Working with NDI Tracking Systems ================================== The :mod:`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: .. list-table:: :header-rows: 1 :widths: 35 30 35 * - Class - Hardware - Connection * - :class:`~ndi_tracking.NDIPolarisTrackingStream` - Polaris Vega, Polaris Lyra - Ethernet (TCP/IP) * - :class:`~ndi_tracking.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. .. code-block:: python import imfusion import ndi_tracking Optical Tracking with Polaris Vega / Lyra ------------------------------------------ The :class:`~ndi_tracking.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: .. code-block:: python 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 :class:`imfusion.stream.StreamRecorderAlgorithm`: .. code-block:: python 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 :class:`~ndi_tracking.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. .. code-block:: python 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: .. code-block:: python 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 :meth:`~ndi_tracking.NDIAuroraTrackingStream.register_tool` before opening the stream: .. code-block:: python 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 :class:`imfusion.stream.TrackingStream`. Recorded output is a list of :class:`imfusion.TrackingSequence` objects, one per registered tool. Each :class:`imfusion.TrackingSequence` holds a time series of 4×4 transformation matrices representing the tool poses in the tracker's coordinate system. .. code-block:: python 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")