App Note: Multi Camera Synchronization using PTP and Scheduled Action Commands


In machine vision applications, cameras often need to be synchronized with other cameras or devices such as lighting and controllers to capture images precisely.

What is the Precision Time Protocol (PTP)?

Precision Time Protocol (PTP or IEEE1588) is an IEEE standard that has been integrated into GigE Vision 2.0. It is a method of synchronizing the clock of multiple devices on an Ethernet network. This is achieved by setting one device as the master clock and have all other devices synchronize and adjust to the master clock periodically. The synchronization is handled automatically by the devices once the master and slave devices have been set.

PTP can be used with LUCID cameras in several ways.


Figure 1. A PTP-supported network switch or a camera can be used as the master to synchronize all the other devices

What is Scheduled Action Command?

Scheduled Action Command is a method that allows the user to set certain actions to occur at a pre-determined time in the future. If the devices on the network are synchronized via PTP, this scheduled action will also be synchronized. This enables triggering over Ethernet which simplifies multi-camera setups. Instead of wiring up multiple cameras for hardware triggering, these cameras can now be triggered simultaneously over Ethernet.

The following examples provide more information on how to use PTP and Scheduled Action Command in Arena SDK.

  • Arena SDK or newer
  • LUCID Vision Labs Phoenix GigE camera with firmware version 1.12 or newer
  • LUCID Vision Labs Triton GigE camera with firmware version 1.4 or newer
  • Microsoft Visual Studio 2015
Equipment Used

The following example was successfully tested using 7 cameras connected via a switch to a network card. The same setup can be scaled with a larger switch and more cameras. Test setup included:

  • 7 PHX016S-C cameras
  • Netgear MS510TXPP switch
  • Intel I350 Gigabit 1000BaseT Network Ethernet Adapter
  • PC with specifications
    • ASUS PRIME Z370-A
    • Intel Core i7-8700 3.2Ghz
    • 16 GB DDR4 RAM
Example Setup

This section goes over the steps required in order to enable PTP and grab images using Scheduled Action Command.

Step 1: Enable PTP
In order to enable PTP, set the “PtpEnable” node to true.

Arena::SetNodeValue(pDevice->GetNodeMap(), "PtpEnable", true);

Step 2: Set one camera as the master
At least two cameras are required for time synchronization over PTP. One camera acts as the master, while the other cameras act as slaves. To set a camera as master, we set “PtpSlaveOnly” node to false and confirm the status by reading “PtpStatus” node.

Arena::SetNodeValue(pDevice->GetNodeMap(), "PtpSlaveOnly", false);
std::string currPtpStatus = Arena::GetNodeValue(pDevice->GetNodeMap(), "PtpStatus");
	while (currPtpStatus != "Master");

Step 3: Set the remaining cameras as slaves
Once a camera is set as the master, the other connected cameras need to be set as slaves. For this, we set “PtpSlaveOnly” node to true and same as before, confirm the status by reading “PtpStatus” node.

Arena::SetNodeValue(pDevice->GetNodeMap(), "PtpSlaveOnly", true);	
std::string currPtpStatus = Arena::GetNodeValue(pDevice->GetNodeMap(), "PtpStatus");
while (currPtpStatus != "Slave");

Step 4: Set TransferControlMode to UserControlled
“TransferControlMode” is set to “UserControlled” so that images can be transferred to the host in a controlled manner. If all the connected cameras attempt to transfer images to the host at the same time, the result could be loss of packets during transfer or torn images received by the host. This will become clear in Step 8.

Executing “TransferStop” will stop images from being transferred to the host.

Arena::SetNodeValue(pDevice->GetNodeMap(), "TransferControlMode", "UserControlled");
Arena::SetNodeValue(pDevice->GetNodeMap(), "TransferOperationMode", "Continuous");
Arena::ExecuteNode(pDevice->GetNodeMap(), "TransferStop");

Step 5: Enable Trigger Mode
To fire Scheduled Action Command, “TriggerMode” must be set to “On” and “TriggerSource” must be set to “Action0”. Setting “TriggerSelector” to “FrameStart” will ensure images are acquired only when a Scheduled Action Command is fired, thus keeping the timestamps of acquired images synchronized.

Executing “TransferStop” will stop images from being transferred to the host.

Arena::SetNodeValue(pDevice->GetNodeMap(), "TriggerMode", "On");
Arena::SetNodeValue(pDevice->GetNodeMap(), "TriggerSelector", "FrameStart");
Arena::SetNodeValue(pDevice->GetNodeMap(), "TriggerSource", "Action0");

Step 6: Settings for Scheduled Action Command
Enabling the unconditional action command mode allows action commands to be processed on the camera even when connection to the host is closed. “ActionSelector” is set to the default value of “0”.

  • “ActionDeviceKey” node sets the Device Key, which is a 32-bit unique identifier for each camera. Action Command uses the Device Key to find the correct camera for trigger.
  • “ActionGroupKey” node sets the Group Key, which is a 32-bit value. Several cameras can be assigned the same Group Key. Action Command uses the Group Key to find the correct group of cameras for trigger.
  • “ActionGroupMask” node sets the Group Mask. All cameras are assigned a Device Key and Group Key. The Group Mask is used to mask the Group Key so only a subgroup of cameras can be triggered.
Arena::SetNodeValue(pDevice->GetNodeMap(), "ActionUnconditionalMode", "On");
Arena::SetNodeValue(pDevice->GetNodeMap(), "ActionSelector", 0);
Arena::SetNodeValue(pDevice->GetNodeMap(), "ActionDeviceKey", g_action_device_key);
Arena::SetNodeValue(pDevice->GetNodeMap(), "ActionGroupKey", g_action_group_key);
Arena::SetNodeValue(pDevice->GetNodeMap(), "ActionGroupMask", g_action_device_key);

Step 7: Firing Scheduled Action Command
Executing the “PtpDataSetLatch” node latches the current value from the camera’s PTP clock and “PtpDataSetLatchValue” node reads that value.

The following code example rounds the read value to the nearest second and schedules an Action Command at this time. Executing “ActionCommandFireCommand” fires the action command.

// Get the curent PTP timestamp from the Master camera
	Arena::ExecuteNode(g_camEntries[0]->pCam->GetNodeMap(), "PtpDataSetLatch");
int64_t curr_ptp = Arena::GetNodeValue(g_camEntries[0]->pCam->GetNodeMap(), "PtpDataSetLatchValue");
// Round up to the nearest second
	if (g_round_up_action_time)
		curr_ptp /= 1000000000;
		curr_ptp += static_cast(g_action_delta_time) + 1;
		curr_ptp *= 1000000000;
		curr_ptp += static_cast(g_action_delta_time) * 1000000000;
// Fire an Action Command g_action_delta_time seconds from now
std::cout << TAB2 << "Scheduled Action Command set for time: " << curr_ptp << " ns" << std::endl;

Arena::SetNodeValue(pSystem->GetTLSystemNodeMap(), "ActionCommandExecuteTime", curr_ptp);

	Arena::ExecuteNode(pSystem->GetTLSystemNodeMap(), "ActionCommandFireCommand");

Step 8: Transfer grabbed images to the host
After ActionCommandFireCommand has been fired, acquired images are stored in the queue ready to be transferred to the host.

Transferring images in a controlled manner will make sure no frames are dropped in the process. This could be achieved by executing “TransferStart” and “TransferStop”.

// Transfer images to host
	Arena::ExecuteNode(g_camEntries[x]->pCam->GetNodeMap(), "TransferStart");

	// Wait for 2 * g_action_delta_time in seconds
	pImage = g_camEntries[x]->pCam->GetImage(g_action_delta_time * 1000 * 2);

	Arena::ExecuteNode(g_camEntries[x]->pCam->GetNodeMap(), "TransferStop");


The output of this example code can be found below:

Open System
7 cameras found
Waiting for Camera_0 (183900005) to become Master
Waiting for Camera_1 (183900013) to become Slave
Waiting for Camera_2 (183900004) to become Slave
Waiting for Camera_3 (183900012) to become Slave
Waiting for Camera_4 (183900003) to become Slave
Waiting for Camera_5 (183900001) to become Slave
Waiting for Camera_6 (183900014) to become Slave
  Applied the following settings to cameras:
  StreamAutoNegotiatePacketSize = true
  StreamPacketResendEnable = true
  TransferControlMode = UserControlled
  TransferOperationMode = Continuous
  AcquisitionFrameRateEnable = false
  TriggerMode = On
  TriggerSelector = FrameStart
  TriggerSource = Action0
  ExposureAuto = Off
  ExposureTime = 500
  ActionUnconditionalMode = On
  ActionSelector = 0
  ActionDeviceKey = 1
  ActionGroupKey = 1
  ActionGroupMask = 1

  Start streaming on Camera_0 (183900005) (Master)
  Start streaming on Camera_1 (183900013) (Slave)
  Start streaming on Camera_2 (183900004) (Slave)
  Start streaming on Camera_3 (183900012) (Slave)
  Start streaming on Camera_4 (183900003) (Slave)
  Start streaming on Camera_5 (183900001) (Slave)
  Start streaming on Camera_6 (183900014) (Slave)

    Read PtpDataSetLatchValue on Camera_0 (183900005) (Master): 2072140242512 ns
    Scheduled Action Command set for time: 2074000000000 ns
      Grabbing images...
      Got FrameID 1 from Camera_0 (183900005) (Master) with timestamp: 2074000033912 ns
      Got FrameID 1 from Camera_1 (183900013) (Slave) with timestamp: 2074000024760 ns
      Got FrameID 1 from Camera_2 (183900004) (Slave) with timestamp: 2074000031128 ns
      Got FrameID 1 from Camera_3 (183900012) (Slave) with timestamp: 2074000024808 ns
      Got FrameID 1 from Camera_4 (183900003) (Slave) with timestamp: 2074000030904 ns
      Got FrameID 1 from Camera_5 (183900001) (Slave) with timestamp: 2074000028856 ns
      Got FrameID 1 from Camera_6 (183900014) (Slave) with timestamp: 2074000025968 ns 

  Stop streaming on Camera_0 (183900005) (Master)
  Stop streaming on Camera_1 (183900013) (Slave)
  Stop streaming on Camera_2 (183900004) (Slave)
  Stop streaming on Camera_3 (183900012) (Slave)
  Stop streaming on Camera_4 (183900003) (Slave)
  Stop streaming on Camera_5 (183900001) (Slave)
  Stop streaming on Camera_6 (183900014) (Slave)
Close System
Press Enter to exit.