Motive API: Quick Start Guide

SDK/API Support Disclaimer

We provide developer tools to enable OptiTrack customers across a broad set of applications to utilize their systems in the ways that best suit them. Our Motive API through the NatNet SDK and Camera SDK is designed to enable experienced software developers to integrate data transfer and/or system operation with their preferred systems and pipelines. Sample projects are provided alongside each tool, and we strongly recommend the users to reference or use the samples as reliable starting points. The following list specifies the range of support that will be provided for the SDK tools:

  • Using the SDK tools requires background knowledge on software development; therefore, we do not provide support for basic project setup, compiling, and linking when using the SDK/API to create your own applications.

  • Although we ensure the SDK tools and their libraries work as intended, we do not provide support for custom developed applications that have been programmed or modified by users using the SDK tools.

  • Ticketed support will be provided for licensed Motive users using the Motive API and/or the NatNet SDK tools from the included libraries and sample source codes only.

  • The Camera SDK is a free product, and therefore we do not provide free ticketed support for it.

  • For other questions, please check out the NaturalPoint forums. Very often, similar development issues get reported and solved there.

This guide provides detailed instructions on commonly used functions of the Motive API for developing custom applications. For a full list of the functions, refer to the Motive API: Function Reference page. Also, for a sample use case of the API functions, please check out the provided marker project. In this guide, the following topics will be covered:

  • Library files and header files

  • Initialization and shutdown

  • Capture setup (Calibration)

  • Configuring camera settings

  • Updating captured frames

  • 3D marker tracking

  • Rigid body tracking

  • Data streaming

Environment Setup

Library Files

When developing a Motive API project, make sure its linker knows where to find the required library files. This can be done either by specifying its location within the project or by copying these files onto the project folder.

MotiveAPI.h

Motive API libraries (.lib and .dll) are located in the lib folder within the Motive install directory; which is located at C:\Program Files\OptiTrack\Motive\lib by default. In this folder, library files for both 64-bit (MotiveAPI.dll and MotiveAPI.lib) platforms can be found. When using the API library, all of the required DLL files must be located in the executable directory. Copy and paste the MotiveAPI.dll file onto the folder alongside the executable file.

Third-party Libraries

  • Additional third-party libraries are required for Motive API, and most of the DLL files for these libraries can be found in the Motive install directory C:\Program Files\OptiTrack\Motive\. You can simply copy and paste all of the DLL files from the Motive installation directory into the directory of the Motive API project to use them. Highlighted items in the below image are the required DLL files.

  • Lastly, copy the C:\Program Files\OptiTrack\Motive\plugins\platforms folder and its contents into the EXE as well since the libraries contained in this folder will also be used.

Header Files

For function declarations, there are two required header files: MotiveAPI.h and RigidBodySettings.h, and these files are located in the C:\Program Files\OptiTrack\Motive\inc\ folder. Always include the directive syntax for adding the MotiveAPI.h header file for all programs that are developed against the Motive API. The syntax for including RigidBodySetting.h file is already included in the MotiveAPI.h file, so there is no need to include this separately.

#include    "MotiveAPI.h"
  • The MotiveAPI.h file contains the declaration for most of the functions and classes in the API.

  • The RigidBodySettings.h file contains declaration for the cRigidBodySettings class, which is used for configuring Rigid Body asset properties.

Note: You could define these directories by using the MOTIVEAPI_INC, MOTIVEAPI_LIB environment variables. Check the project properties (Visual Studio) of the provided marker project for a sample project configuration.

Motive Files

Motive API, by default, loads the default calibration (CAL) and Application profile (MOTIVE) files from the program data directory unless separately specified. These are the files that Motive also loads at the application startup, and they are located in the following folder:

  • Default System Calibration: C:\ProgramData\OptiTrack\Motive\System Calibration.cal

  • Default Application Profile: C:\ProgramData\OptiTrack\MotiveProfile.motive

If there are specific files that need to be loaded into the project, you can export and import two files from Motive: motive application profile (MOTIVE) and camera calibration (CAL). The application profile is imported in order to obtain software settings and trackable asset definitions. Only after the camera calibration is imported, reliable 3D tracking data can be obtained. Application profiles can be loaded using the TT_LoadProfile function, and the calibration files can be loaded using the TT_LoadCalibration function.

Initialization and Shutdown

When using the API, connected devices and the Motive API library need to be properly initialized at the beginning of a program and closed down at the end. The following section covers Motive API functions for initializing and closing down devices.

Initialization

To initialize all of the connected cameras, call the TT_Initialize function. This function initializes the API library and gets the cameras ready for capturing data, so always call this function at the beginning of a program. If you attempt to use the API functions without the initialization, you will get an error.

// Initializing all connected cameras
TT_Initialize();

Motive Profile Load

Please note that TT_Initialization loads the default Motive profiles (MOTIVE) from the ProgramData directory during the initialization process. To load Motive profile, or settings, from a specified directory, use TT_LoadProfile.

Update

The TT_Update function is primarily used for updating captured frames, which will be covered later, but there is another use. The TT_Update can also be called to update a list of connected devices, and you can call this function after the initialization to make sure all of the newly connected devices are properly initialized in the beginning.

// Initializing all connected cameras
TT_Initialize();

//Update for newly arrive cameras
TT_Update();

Shutdown

When exiting out of a program, make sure to call the TT_Shutdown function to completely release and close down all of the connected devices. Cameras may fail to shut down completely when this function is not called.

// Closing down all of the connected cameras
TT_Shutdown();
return 0;

Setup the Project

Motive Application Profile

The Motive application profile (MOTIVE) stores all the trackable assets involved in a capture and software configurations including application settings and data streaming settings. When using the API, it is strongly recommended to first configure all of the settings and define trackable assets in Motive, export a profile MOTIVE file, and then load the file by calling the TT_LoadProfile function. This way, you can adjust the settings for your need in advance and apply them to your program without worrying about configuring individual settings.

TT_LoadProfile("UserProfile.motive"); 	// Loading application profile, UserProfile.motive

Camera Calibration

Cameras must be calibrated in order to track in 3D space. However, since camera calibration is a complex process, and because of this, it's easier to have the camera system calibrated from Motive, export the camera calibration file (CAL), and the exported file can be loaded into custom applications that are developed against the API. Once the calibration data is loaded, the 3D tracking functions can be used. For detailed instructions on camera calibration in Motive, please read through the Calibration page.

TT_LoadCalibration("CameraCal.cal"); 	// Loading CAL file

Loading Calibration

  1. Open Motive.

  2. [Motive] Calibrate: Calibrate camera system using the Calibration panel. Read through the Calibration page for details.

  3. [Motive] Export: After the system has been calibrated, export the calibration file (CAL) from Motive.

  4. Close out of Motive.

  5. [API] Load: Import calibration onto your custom application by calling the TT_LoadCalibration function to import CAL files.

  6. When successfully loaded, you will be able to obtain 3D tracking data using the API functions.

Note:

  • Calibration Files: When using the exported calibration files, make sure to use only valid calibration. Exported calibration file can be used again as long as the system setup remains unchanged. Note that the file will no longer be valid if any of the system setups have been altered after the calibration. Also, calibration quality may degrade over time due to environmental factors. For these reasons, we recommend re-calibrating the system routinely to guarantee the best tracking quality.

  • Tracking Bars: If you are using a tracking bar, camera calibration is not required for tracking 3D points.

Camera Settings

Connected cameras are accessible by index numbers. The camera indexes are assigned in the order the cameras are initialized. Most of the API functions for controlling cameras require an index value. When processing all of the cameras, use the TT_CameraCount function to obtain the total camera count and process each camera within a loop. For pointing to a specific camera, you can use the TT_CameraID or TT_CameraName functions to check and use the camera with given its index value. This section covers Motive API functions for checking and configuring camera frame rate, camera video type, camera exposure, pixel brightness threshold, and IR illumination intensity.

Fetching Camera Settings

The following functions return integer values for the configured settings of a camera specified by its index number. Camera video type is returned as an integer value that represents a image processing mode, as listed in the NPVIDEOTYPE.

These camera settings are equivalent to the settings that are listed in the Devices pane of Motive. For more information on each of the camera settings, refer to the Devices pane page.

TT_CameraVideoType(int cameraIndex)      // Returns Video Type
TT_CameraExposure (int cameraIndex)      // Returns Camera Exposure
TT_CameraThreshold(int cameraIndex)      // Returns Pixel Threshold
TT_CameraIntensity(int cameraIndex)      // Returns IR Illumination Intensity
TT_CameraFrameRate(int cameraIndex)      // Returns Camera Frame Rate

Configuring Settings

Now that we have covered functions for obtaining configured settings, now let's modify some of them. There are two main functions for adjusting the camera settings: TT_SetCameraSettings and TT_SetCameraFramerate. The TT_SetCameraSettings function configures video type, exposure, threshold, and intensity settings of a camera which is specified by its index number. The TT_SetCameraFrameRate is used for configuring frame rate of a camera. Supported frame rate range may vary for different camera models. Check the device specifications and apply the frame rates only within the supported range.

TT_SetCameraSettings(int cameraIndex, int videoType, int exposure, int threshold, int intensity);
TT_SetCameraFrameRate(int cameraIndex, int framerate);

If you wish to keep part of the current camera settings, you can use the above functions to obtain the configured settings (e.g. TT_CameraVideoType, TT_CameraFrameRate, TT_CameraExposure, etc.) and use them as input arguments for the TT_SetCameraSettings function. The following example demonstrates modifying frame rate and IR illumination intensity for all of the cameras, while keeping the other settings constant.

//== Changing exposure and threshold settings for all of the cameras ==//
int cameraCount = TT_CameraCount();
int intensity = 10;
int framerate = 100;

for (int i = 0; i < cameraCount; i++)
{
	TT_SetCameraSettings(i, TT_CameraVideoType(i), TT_CameraExposure(i),
					TT_CameraThreshold(i), intensity);

	TT_SetCameraFrameRate(i, framerate);

	//== Outputting the Settings ==//
	printf("Camera #%d:\n", i);
	printf("\tFPS: %d\n\tIntensity: %d\n\tExposure: %d\n\tIntensity: %d\n\tVideo Type:%d\n", 
			TT_CameraFrameRate(i), TT_CameraIntensity(i), TT_CameraExposure(i), 
			TT_CameraIntensity(i),TT_CameraVideoType(i));
}

Camera Setting Ranges

Camera Settings

  • Valid frame rate values: Varies depending on camera models, refer to the respective hardware specifications.

  • Valid exposure values: Depends on camera model and frame rate settings.

  • Valid threshold values: 0 - 255

  • Valid intensity values: 0 - 15

Video Types

  • Video Type: see the Data Recording page for more information on image processing modes.

  • Segment Mode: 0

  • Grayscale Mode: 1

  • Object Mode: 2

  • Precision Mode: 4

  • MJPEG Mode: 6

Other Settings

There are other camera settings, such as imager gain, that can be configured using the Motive API. Please refer to the Motive API: Function Reference page for descriptions on other functions.

Updating the frames

In order to process multiple consecutive frames, you must update the camera frames using the following API functions: TT_Update or TT_UpdateSingleFrame. Call one of the two functions repeatedly within a loop to process all of the incoming frames. In the 'marker sample', TT_Update function is called within a while loop as the frameCounter variable is incremented, as shown in the example below.

// marker.cpp sample project

int main()
{
        TT_Initialize();
	int frameCounter = 0; // Frame counter variable
 	while (!_kbhit())
	{
		if(TT_Update() == NPRESULT_SUCCESS)
		{
			// Each time the TT_Update function successfully updates the frame,
			// the frame counter is incremented, and the new frame is processed.
			frameCounter++;

			////// PROCESS NEW FRAME //////
		}
	}
}

TT_Update() vs. TT_UpdateSingleFrame()

There are two functions for updating the camera frames: TT_Update and TT_UpdateSingleFrame. At the most fundamental level, these two functions both update the incoming camera frames. However, they may act differently in certain situations. When a client application stalls momentarily, it could get behind on updating the frames and the unprocessed frames may be accumulated. In this situation, each of these two functions will behave differently.

  • The TT_Update() function will disregard accumulated frames and service only the most recent frame data, but it also means that the client application will not be processing the previously missed frames.

  • The TT_UpdateSingleFrame() function ensures that only one frame is processed each time the function is called. However, when there are significant stalls in the program, using this function may result in accumulated processing latency.

In general, a user should always use TT_Update(). Only in the case where a user wants to ensure their client application has access to every frame of tracking data and they are having problems calling TT_Update() in a timely fashion, should they consider using TT_UpdateSingleFrame(). If it is important for your program to obtain and process every single frame, use the TT_UpdateSingleFrame() function for updating the data.

TT_Update() 		// Process all outstanding frames of data.
TT_UpdateSingleFrame() // Process one outstanding frame of data.

3D Marker Tracking

After loading valid camera calibration, you can use the API functions to track retroreflective markers and get their 3D coordinates. The following section demonstrates using the API functions for obtaining the 3D coordinates. Since marker data is obtained for each frame, always call the TT_Update, or the TT_UpdateSingleFrame, function each time newly captured frames are received.

Marker Index

In a given frame, each reconstructed marker is assigned a marker index number. These marker indexes are used for pointing to a particular reconstruction within a frame. You can also use the TT_FrameMarkerCount function to obtain the total marker count and use this value within a loop to process all of the reconstructed markers. Marker index values may vary between different frames, but unique identifiers will always remain the same. Use the TT_FrameMarkerLabel function to obtain the individual marker labels if you wish to access same reconstructions for multiple frames.

int totalMarker = TT_FrameMarkerCount();
printf("Frame #%d: (Markers: %d)\n", framecounter, totalMarker);

//== Use a loop to access every marker in the frame ==//
for (int i = 0 ; i < totalMarker; i++) {
        printf("\tMarker #%d:\t(%.2f,\t%.2f,\t%.2f)\n\n", 
		i, TT_FrameMarkerX(i), TT_FrameMarkerY(i), TT_FrameMarkerZ(i));
}

Marker Position

For obtaining 3D position of a reconstructed marker, you can use TT_FrameMarkerX, TT_FrameMarkerY, and TT_FrameMarkerZ functions. These functions return 3D coordinates (X/Y/Z) of a marker in respect to the global coordinate system, which was defined during the calibration process. You can further analyze 3D movements directly from the reconstructed 3D marker positions, or you can create a Rigid Body asset from a set of tracked reconstructions for 6 Degrees of Freedom tracking data. Rigid body tracking via the API will be explained in the later section.

int totalMarker = TT_FrameMarkerCount();
printf("Frame #%d: (Markers: %d)\n", framecounter, totalMarker);

//== Use a loop to access every marker in the frame ==//
for (int i = 0 ; i < totalMarker; i++) {
        printf("\tMarker #%d:\t(%.2f,\t%.2f,\t%.2f)\n\n", 
		i, TT_FrameMarkerX(i), TT_FrameMarkerY(i), TT_FrameMarkerZ(i));
}

Rigid Body Tracking

For tracking 6 degrees of freedom (DoF) movement of a Rigid Body, a corresponding Rigid Body (RB) asset must be defined. A RB asset is created from a set of reflective markers attached to a rigid object which is assumed to be undeformable. There are two main approaches for obtaining RB assets when using Motive API; you can either import existing Rigid Body data or you can define new Rigid Bodies using the TT_CreateRigidBody function. Once RB assets are defined in the project, Rigid Body tracking functions can be used to obtain the 6 DoF tracking data. This section covers sample instructions for tracking Rigid Bodies using the Motive API.

We strongly recommend reading through the Rigid Body Tracking page for more information on how Rigid Body assets are defined in Motive.

Importing Rigid Body Assets

Let's go through importing RB assets into a client application using the API. In Motive, Rigid Body assets can be created from three or more reconstructed markers, and all of the created assets can be exported out into either application profile (MOTIVE) or a Motive Rigid Body file (TRA). Each Rigid Body asset saves marker arrangements when it was first created. As long as the marker locations remain the same, you can use saved asset definitions for tracking respective objects.

Exporting all RB assets from Motive:

  • Exporting application profile: File → Save Profile

  • Exporting Rigid Body file (TRA): File → Export Rigid Bodies (TRA)

Exporting individual RB asset:

  • Exporting Rigid Body file (TRA): Under the Assets pane, right-click on a RB asset and click Export Rigid Body

When using the API, you can load exported assets by calling the TT_LoadProfile function for application profiles and the TT_LoadRigidBodies or TT_AddRigidBodes function for TRA files. When importing TRA files, the TT_LoadRigidBodies function will entirely replace the existing Rigid Bodies with the list of assets from the loaded TRA file. On the other hand, TT_AddRigidBodes will add the loaded assets onto the existing list while keeping the existing assets. Once Rigid Body assets are imported into the application, the API functions can be used to configure and access the Rigid Body assets.

TT_LoadProfile("UserProfile.motive"); 	// Loading application profile
TT_LoadRigidBodies("rbfile.tra"); 	// Loading TRA file
TT_AddRigidBodies("rbfile.tra"); 

Creating New Rigid Body Assets

Rigid body assets can also be defined directly using the API. The TT_CreateRigidBody function defines a new Rigid Body from given 3D coordinates. This function takes in an array float values which represent x/y/z coordinates or multiple markers in respect to Rigid Body pivot point. The float array for multiple markers should be listed as following: {x1, y1, z1, x2, y2, z2, …, xN, yN, zN}. You can manually enter the coordinate values or use the TT_FrameMarkerX, TT_FrameMarkerY, and TT_FrameMarkerZ functions to input 3D coordinates of tracked markers.

When using the TT_FrameMarkerX/Y/Z functions, you need to keep in mind that these locations are taken in respect to the RB pivot point. To set the pivot point at the center of created Rigid Body, you will need to first compute pivot point location, and subtract its coordinates from the 3D coordinates of the markers obtained by the TT_FrameMarkerX/Y/Z functions. This process is shown in the following example.

NPRESULT		TT_CreateRigidBody(const char* name, int userDataID, int markerCount, float *markerList);

Example: Creating RB Assets

int markerCount = TT_FrameMarkerCount;
vector<float> markerListRelativeToGlobal(3*markerCount);

// add markers to markerListRelativeToGlobal using TT_FrameMarkerX, etc
for (int i = 0; i < markerCount; ++i)
{
    	markerListRelativeToGlobal.push_back(TT_FrameMarkerX(i));
    	markerListRelativeToGlobal.push_back(TT_FrameMarkerY(i));
	markerListRelativeToGlobal.push_back(TT_FrameMarkerZ(i));
}

// then average the locations in x, y and z
for (int i = 0; i < markerCount; ++i)
{
    	float sx += markerListRelativeToGlobal[3*i];
    	float sy += markerListRelativeToGlobal[3*i + 1];
    	float sz += markerListRelativeToGlobal[3*i + 2];
}


float ax = sx/markerCount;
float ay = sy/markerCount;
float az = sz/markerCount;
vector<float> pivotPoint = {ax, ay, az};
vector<float> markerListRelativeToPivotPoint(3*markerCount);

// subtract the pivot point location from the marker location
for (int i = 0; i < markerCount; ++i)
{
    markerListRelativeToPivotPoint.push_back(markerListRelativeToGlobal[3*i] - ax);
    markerListRelativeToPivotPoint.push_back(markerListRelativeToGlobal[3*i + 1] - ay);
    markerListRelativeToPivotPoint.push_back(markerListRelativeToGlobal[3*i + 2] - az);
}

TT_CreateRigidBody("Rigid Body New", 1, markerCount, markerListRelativeToPivotPoint);

Rigid Body 6 DoF Tracking Data

6 DoF Rigid Body tracking data can be obtained using the TT_RigidBodyLocation function. Using this function, you can save 3D position and orientation of a Rigid Body into declared variables. The saved position values indicate location of the Rigid Body pivot point, and they are represented in respect to the global coordinate axis. The Orientation is saved in both Euler and Quaternion orientation representations.

void	TT_RigidBodyLocation(int rbIndex, 				//== RigidBody Index
			float *x, float *y, float *z, 			//== Position
			float *qx, float *qy, float *qz, float *qw, 	//== Quaternion
			float *yaw, float *pitch, float *roll);   	//== Euler

Example: RB Tracking Data

//== Declared variables ==//
float	x, y, z;
float 	qx, qy, qz, qw;
float	yaw, pitch, roll;
int rbcount = TT_RigidBodyCount();

for(int i = 0; i < rbcount; i++)
{
	//== Obtaining/Saving the Rigid Body position and orientation ==//
	TT_RigidBodyLocation( i, &x, &y, &z, &qx, & qy, &qz, &qw, &yaw, &pitch, &roll );
	
	if( TT_IsRigidBodyTracked( i ) )
	{
		printf( "%s: Pos (%.3f, %.3f, %.3f) Orient (%.1f, %.1f, %.1f)\n", 
					TT_RigidBodyName( i ), x, y, z, yaw, pitch, roll );
	}
}

Rigid Body Properties

In Motive, Rigid Body assets have Rigid Body properties assigned to each of them. Depending on how these properties are configured, display and tracking behavior of corresponding Rigid Bodies may vary. When using the API, Rigid Body properties are configured and applied using the cRigidBodySettings class which is declared within the RigidBodySetting.h header file.

Within your program, create an instance of cRigidBodySettings class and call the API functions to obtain and adjust Rigid Body properties. Once desired changes are made, use the TT_SetRigidBodySettings function to assign the properties back onto a Rigid Body asset.

For detailed information on individual Rigid Body settings, read through the Properties: Rigid Body page.

NPRESULT	TT_RigidBodySettings(int rbIndex, RigidBodySolver::cRigidBodySettings &settings);
NPRESULT	TT_SetRigidBodySettings(int rbIndex, RigidBodySolver::cRigidBodySettings &settings);

Data Streaming

Once the API has been successfully initialized, data streaming can be enabled, or disabled, by calling either the TT_StreamNP, TT_StreamTrackd, or TT_StreamVRPN function. The TT_StreamNP function enables/disables data streaming via the NatNet. The NatNet SDK is a client/server networking SDK designed for sending and receiving NaturalPoint data across networks, and tracking data from the API can be streamed to client applications from various platforms via the NatNet protocol. Once the data streaming is enabled, connect the NatNet client application to the server IP address to start receiving the data.

TT_StreamNP(true);	//Enabling NatNet Streaming.

The TT_StreamNP function is equivalent to Broadcast Frame Data from the Data Streaming pane in Motive.

Data Streaming Settings

The Motive API does not currently support configuring data streaming settings directly from the API. To configure the streaming server IP address and the data streaming settings, you will need to use Motive and save an application profile MOTIVE file that contains the desired configuration. Then, the exported profile can be loaded when using the API. Through this way, you will be able to set the interface IP address and decide which data to be streamed over the network.

For more information on data streaming settings, read through the Data Streaming page.

Last updated