Main Content

Define and Test Tracking Architectures for System-of-Systems

Since R2021a

This example shows how to define the tracking architecture of a system-of-systems that includes multiple detection-level multi-object trackers and track-level fusion algorithms. You can use the tracking architectures to compare different tracking system designs and find the best solution for your system.

Introduction

The Simulate and Track En-Route Aircraft in Earth-Centered Scenarios example shows how to track aircraft using multiple long-range radar systems and fuse data from Automatic Dependent Surveillance Broadcast (ADS-B) transponders to get an accurate air situation picture. In that example, radar detections are fused centrally by a tracker before these tracks are fused with tracks from the ADS-B reports. You first load scenario data from that example and set up a globe-based visualization.

% Create the scenario and globe viewer
load('recordedScenario','scenarioData');
viewer = createGlobe(scenarioData);

The architecture described above is only one possible architecture and tracking system engineers often consider various other options. There are many factors that determine how a system can be architected, some of them are:

  1. Sensor outputs: Some sensor manufacturers design their sensors to output detection lists while others track objects internally and only provide sensor tracks as an output.

  2. Communication networks: The amount of data that is required to be transmitted can be reduced if sensors report tracks instead of detections. Additionally, latency, physical distance, and other limitations may require that sensors report tracks.

  3. Computational resources: Processing all the detections in a single central tracker usually requires more memory and computations than if processing data distributively between different nodes.

However, there are reasons to prefer central processing over decentralized architectures. First, centralized tracking systems can be optimal, because all the available data is processed in one place and there are no constraints on the data being processed. In addition, the architecture is much simpler as there is only one tracker.

Centralized Air Surveillance Architecture

As a baseline for comparison, you define an architecture that reflects the centralized tracking system described in the Simulate and Track En-Route Aircraft in Earth-Centered Scenarios example. To do that, you first define the same centralized trackerGNN for the radars. Note that the radars update every 12 seconds and the ADS-B receiver updates every second. To accommodate different update rates, you wrap the tracker inside a helperScheduledTracker object.

gnn = trackerGNN(...
    'TrackerIndex',2,...
    'FilterInitializationFcn',@initfilter,...
    'ConfirmationThreshold',[3 5],...
    'DeletionThreshold',[5 5],...
    'AssignmentThreshold',[1000 Inf]);

tracker = helperScheduledTracker(gnn, 12);

You define the ADS-B and tracker as two sources that are fused by a trackFuser. Note that the SourceIndex value for each source configuration must match the SourceIndex value of the ADS-B tracks (1) and the TrackerIndex value of the tracker (2). Additionally, the FuserIndex value must be unique as well. In this case you set it to 3.

sources = {...
    fuserSourceConfiguration("SourceIndex", 1);... % ADS-B
    fuserSourceConfiguration("SourceIndex", 2) ... % Tracker
    };

fuser = trackFuser("FuserIndex",3, ...
    "MaxNumSources", 2,...
    "SourceConfigurations", sources, ...
    "AssignmentThreshold", [1000 Inf],...
    "StateFusion", "Intersection",...
    "StateFusionParameters", "trace",...
    "ProcessNoise", 10*eye(3));

With the tracker and track fuser defined, you define the trackingArchitecture. You add the tracker to it, with sensors 1, 2, and 3 reporting to the tracker. You also add the track fuser to the trackingArchitecture. The trackingArchitecture directs fuser sources to the fuser based on the fuser's SoucreConfigurations property. Finally, you use the show object function to display the architecture.

centralizedArchitecture = trackingArchitecture;
addTracker(centralizedArchitecture,tracker,'Name','Tracker','SensorIndices',[1 2 3],'ToOutput',false);
addTrackFuser(centralizedArchitecture,fuser,'Name','Fuser','ToOutput',true);
show(centralizedArchitecture)

Run the scenario and track using the tracking architecture

% Clear previous viewer data and reset the architecture if previously used
clear(viewer);
plotTruth(scenarioData, viewer);
reset(centralizedArchitecture);

for i = 1:numel(scenarioData)
    % Read the timestamp from the recorded scenario data
    time = scenarioData(i).Time;
    
    % Read detections from all radars in the scenario
    detections = scenarioData(i).Detections;
    
    % Update detections on the globe
    plotDetection(viewer, detections);
    
    % Read ADS-B tracks
    truePose = scenarioData(i).TruePose;
    adsbTracks = scenarioData(i).ADSBTrack;
    
    % Pass the detections and ADS-B tracks to the tracking architecture
    tracks = centralizedArchitecture(detections,adsbTracks,time);
    
    % Update display
    plotTrack(viewer, tracks);
end
% Provide an output image
snap(viewer);

Decentralized Architecture

You want to explore how the system works when each radar reports sensor-level tracks that are fused with the ADS-B tracks by a track-level fusion algorithm. You define the trackingArchitecture that represents this decentralized architecture.

decentralizedArchitecture = trackingArchitecture;

Next, you define a tracker for each radar. There are three radar sensors in the original example, so you define three trackers. For simplicity, assume that these trackers are defined in the same way as the central case. Since the SourceIndex of the ADS-B tracks is 1, you define the trackers with TrackerIndex of 2, 3, and 4. Meanwhile, the radar detections have SensorIndex = 1, 2, and 3.

sensorTracker = trackerGNN(...
    'TrackerIndex',2,...
    'FilterInitializationFcn',@initfilter,...
    'ConfirmationThreshold',[3 5],...
    'DeletionThreshold',[5 5],...
    'AssignmentThreshold',[1000 Inf]);

% Add one tracker to each radar
for i = 2:4
    tracker = helperScheduledTracker(clone(sensorTracker),12);
    tracker.TrackerIndex = i; % Specify each radar tracker with a different index
    addTracker(decentralizedArchitecture, tracker, 'Name', strcat('Radar',num2str(i-1)), 'SensorIndices', i-1, 'ToOutput', false); % Connect the tracker to the correct radar index 
end

You add a trackFuser that fuses tracks from four sources, the ADS-B and the three radar trackers.

% Define the sources
sources = {...
    fuserSourceConfiguration(1);... % ADS-B
    fuserSourceConfiguration(2);... % Tracker for radar 1
    fuserSourceConfiguration(3);... % Tracker for radar 2
    fuserSourceConfiguration(4);... % Tracker for radar 3
    };

% Add the fuser
fuser = trackFuser("FuserIndex", 5, ...
    "MaxNumSources", 4,...
    "SourceConfigurations", sources, ...
    "AssignmentThreshold", [1000 Inf],...
    "StateFusion", "Intersection", ...
    "StateFusionParameters", "trace", ...
    "ProcessNoise", 10*eye(3));
addTrackFuser(decentralizedArchitecture, fuser, 'Name', 'Fuser');

% Show the tracking architecture in a figure
show(decentralizedArchitecture)

The replayScenario function reads the recorded scenario data, steps the new architecture, and displays the results.

% Rerun the scenario and observe tracking results
replayScenario(decentralizedArchitecture,scenarioData,viewer)

Regional Air Surveillance Architecture

Another possible configuration can fall between the centralized and decentralized architectures above. In many cases, there is no need for an air traffic control center to track aircraft that are too far away from it. In that case, regional air traffic control centers can be used, where one or more radars report to each region.

regionalArchitecture = trackingArchitecture;
for i = 2:4
    tracker = helperScheduledTracker(clone(sensorTracker),12);
    tracker.TrackerIndex = i; % Specify each radar with a different index
    addTracker(regionalArchitecture, tracker, 'Name', strcat('Radar',num2str(i-1)), 'SensorIndices', i-1, 'ToOutput', false);
end

You define two regions and attach two radar sensors to each. Additionally, each region only fuses ADS-B tracks if the reporting aircraft is close enough to the region.

region1Fuser = trackFuser('FuserIndex',5, 'MaxNumSources',3, ...
    'SourceConfigurations',sources([1,2,3]),... % Two radars and ADS-B
    "AssignmentThreshold",[1000 Inf],... 
    "StateFusion",'Intersection',...
    'StateFusionParameters','trace',...
    'ProcessNoise',10*eye(3)); 
region2Fuser = trackFuser('FuserIndex',6, 'MaxNumSources',3, ...
    'SourceConfigurations',sources([1,3,4]),... % Two radars and ADS-B
    "AssignmentThreshold",[1000 Inf],... 
    "StateFusion",'Intersection',...
    'StateFusionParameters','trace',...
    'ProcessNoise',10*eye(3));
addTrackFuser(regionalArchitecture, region1Fuser, 'Name', 'Region1');
addTrackFuser(regionalArchitecture, region2Fuser, 'Name', 'Region2');
show(regionalArchitecture)

% Rerun the scenario and observe tracking results
replayScenario(regionalArchitecture,scenarioData,viewer)

Summary

In this example, you learned how to define tracking architectures for system-of-systems that use multiple trackers and track-to-track fusion algorithms. You saw how to pass detections and tracks as inputs to the tracking architecture and how to output tracks from it.

Supporting Functions

createGlobe Creates the globe visualization

function gl = createGlobe(scenarioData)
% You use the helperGlobeViewer object attached in this example to display
% platforms, trajectories, detections, and tracks on the Earth.
gl = helperGlobeViewer;
setCamera(gl,[28.9176  -95.3388  5.8e5],[0 -30 10]);

% Show flight route
plotTruth(scenarioData, gl);
end

plotTruth plots the ground truth position

function plotTruth(scenarioData, viewer)
poses = [scenarioData.TruePose];
positions = vertcat(poses.Position);
plotLines(viewer, positions(:,1), positions(:,2), positions(:,3))
end

initfilter defines the extended Kalman filter used by the tracker

Airplane motion is well approximated by a constant velocity motion model. Therefore a rather small process noise will give more weight to the dynamics compared to the measurements which are expected to be quite noisy at long ranges.

function filter = initfilter(detection)
filter = initcvekf(detection);
filter.StateCovariance = 10*filter.StateCovariance; % initcvekf uses measurement noise as the default
filter.ProcessNoise = eye(3);
end

replayScenario Replays the recorded scenario and generates results

function replayScenario(arch,scenarioData,viewer)
clear(viewer);
plotTruth(scenarioData, viewer);
reset(arch);
numFusers = numel(arch.TrackFusers);
tracks = cell(1,numFusers);
for i = 1:numel(scenarioData)
    time = scenarioData(i).Time;
    
    % Create detections from all radars in the scenario
    detections = scenarioData(i).Detections;
    
    % Update detections on the globe
    plotDetection(viewer, detections);
    
    % Generate ADS-B tracks
    adsbTracks = scenarioData(i).ADSBTrack;
    
    % Pass the detections and ADS-B tracks to the tracking architecture
    [tracks{1:numFusers}] = arch(detections,adsbTracks,time);
    
    % Update display
    plotTrack(viewer, vertcat(tracks{:}));
end
snap(viewer);
end

Related Topics