Main Content

Introduction to Track Logic

This example shows how to define and use confirmation and deletion logic that are based on history or score. It introduces the trackHistoryLogic and trackScoreLogic objects and shows how to use them as stand-alone objects and how to use them as part of the trackerGNN.

Introduction

The tracker maintains a list of tracks, or estimates of the target states in the area of interest. If a detection cannot be assigned to any track already maintained by the tracker, the tracker initiates a new track. In most cases, it is unclear whether the new track represents a true target or a false one. At first, a track is created with tentative status. If enough evidence is obtained, the track becomes confirmed. Similarly, if no detections are assigned to a track, the track is coasted (predicted without correction), but after a few missed updates, the tracker deletes the track.

There are two main ways of confirming and deleting tracks used in the literature:

  1. History-based: the tracker counts the number of detections assigned to a track in several recent updates. If enough detections are assigned, the track is confirmed. If the track is not assigned to any detection for enough updates, it is deleted. This type of logic is often referred to as M-out-of-N or Last-N, meaning that out of N updates, the track must be detected at least _M _ times for it to be confirmed.

  2. Score-based: the tracker calculates the likelihood that a track is of a real target. Instead of likelihood, we use the score, defined as the log of the likelihood. A high positive track score means that the track is very likely to be of a real target. A very negative track score means that the track is likely to be false. As a result, we can set a threshold for confirming a track if the score is high enough. If the score is low, or falls enough from the maximum score, the track is deleted.

In the following sections, you can see how to define and use the two types of objects that implement the history-based and score-based track logic.

Use a History-Based Track Logic to Confirm and Delete a Track

The simplest type of track logic is based on history. This type of logic counts how many times the track is detected (or missed) in the recent N updates. You want to confirm a track after 3 detections in 5 updates (3-out-of-5), and delete it after 6 consecutive misses. First, create the trackHistoryLogic object that maintains the track history by defining the confirmation and deletion thresholds.

historyLogic = trackHistoryLogic('ConfirmationThreshold', [3 5], 'DeletionThreshold', 6)
historyLogic = 
  trackHistoryLogic with properties:

    ConfirmationThreshold: [3 5]
        DeletionThreshold: [6 6]
                  History: [0 0 0 0 0 0]

To illustrate this, in the first 5 updates, the track is detected every other update. The following shows how the track logic gets confirmed after exactly 3 out of 5 hits. Use the init method to initialize the object with the first hit. Then use either the hit or miss methods to indicate whether the track logic is updated by a hit or a miss, respectively.

The checkConfirmation method is used to check if the track can be confirmed based on its history. Use the output method to get the track history, which is a logical array of length Nmax = max(Nconf, Ndel). In this example, Nmax is 6.

wasInitialized = false; % Has the object been initialized yet?
for i = 1:5
    detectedFlag = logical(mod(i,2)); % Only odd updates are true
    if detectedFlag && ~wasInitialized
        init(historyLogic)
        wasInitialized = true;
    elseif detectedFlag && wasInitialized
        hit(historyLogic)
    else
        miss(historyLogic)
    end
    history = output(historyLogic);
    confFlag = checkConfirmation(historyLogic);
    disp(['Track history is: ', num2str(history),'.  Confirmation Flag is: ',num2str(confFlag)])
end
Track history is: 1  0  0  0  0  0.  Confirmation Flag is: 0
Track history is: 0  1  0  0  0  0.  Confirmation Flag is: 0
Track history is: 1  0  1  0  0  0.  Confirmation Flag is: 0
Track history is: 0  1  0  1  0  0.  Confirmation Flag is: 0
Track history is: 1  0  1  0  1  0.  Confirmation Flag is: 1

Now, suppose that the track is not detected for several updates. After the sixth update it should be deleted. Use the checkDeletion method to check if the track was deleted.

for i = 1:6
    miss(historyLogic); % Every update the track is not detected
    history = output(historyLogic);
    deleteFlag = checkDeletion(historyLogic);
    disp(['Track history is: ', num2str(history),'.  Deletion Flag is: ',num2str(deleteFlag)])
end
Track history is: 0  1  0  1  0  1.  Deletion Flag is: 0
Track history is: 0  0  1  0  1  0.  Deletion Flag is: 0
Track history is: 0  0  0  1  0  1.  Deletion Flag is: 0
Track history is: 0  0  0  0  1  0.  Deletion Flag is: 0
Track history is: 0  0  0  0  0  1.  Deletion Flag is: 0
Track history is: 0  0  0  0  0  0.  Deletion Flag is: 1

Use a Score-Based Track Logic to Confirm and Delete a Track

In many cases, it is not enough to know if a track is assigned a detection. You may need to account for the likelihood that the assignment is correct. There is also a need to know how likely the detection is that of a real target, based on its detection probability, or how likely it is to be false, based on its false alarm rate. Additionally, if the track is new, you need to account for the rate, beta , at which new targets are likely to be detected in a unit volume.

Use the trackScoreLogic object to create a score-based track confirmation and deletion logic. Define the ConfirmationThreshold and DeletionThreshold as two scalar values.

When a track is assigned a detection, you update the track logic with a hit and in most cases the track score increases. The ConfirmationThreshold defines the minimum score that is required to confirm a track.

The track score decreases when a track is not assigned a detection. The DeletionThreshold is used to define how much we allow the score to decrease from the maximum score before deleting the track.

scoreLogic = trackScoreLogic('ConfirmationThreshold', 25, 'DeletionThreshold', -5)
scoreLogic = 
  trackScoreLogic with properties:

    ConfirmationThreshold: 25
        DeletionThreshold: -5
                    Score: 0
                 MaxScore: 0

pd = 0.9;   % Probability of detection
pfa = 1e-6; % Probability of false alarm
volume = 1; % The volume of a sensor detection bin
beta = 0.1; % New target rate in a unit volume
wasInitialized = false; % Has the object been initialized yet?

Confirming a track using score-based logic is very similar to confirming a track using history-based logic. The main difference is that now additional parameters are taken into account that include the statistical performance of the sensor as well as the residual distance of the track relative to the detection.

You use the init , hit , and miss methods to initialize on first hit, update the trackScoreLogic with a subsequent hit, or update with a miss, respectively.

You use the checkConfirmation method to check if the track should be confirmed. You use the output method to get the current score and maximum score as a [currentScore, maxScore] array.

r = rng(2018); % Set the random seed for repeatable results
numSteps1 = 6;
scores = zeros(numSteps1,2);
for i = 1:numSteps1
    l = 0.05 + 0.05 * rand; % likelihood of the measurement
    detectedFlag = logical(mod(i,2)); % Only even updates are true in this example
    if detectedFlag && ~wasInitialized
        init(scoreLogic, volume, beta);
        wasInitialized = true;
    elseif detectedFlag && wasInitialized
        hit(scoreLogic, volume, l);
    else
        miss(scoreLogic);
    end
    scores(i,:) = output(scoreLogic);
    confFlag = checkConfirmation(scoreLogic);
    disp(['Score and MaxScore: ', num2str(scores(i,:)),'.  Confirmation Flag is: ',num2str(confFlag)])
end
Score and MaxScore: 11.4076      11.4076.  Confirmation Flag is: 0
Score and MaxScore: 9.10498      11.4076.  Confirmation Flag is: 0
Score and MaxScore: 20.4649      20.4649.  Confirmation Flag is: 0
Score and MaxScore: 18.1624      20.4649.  Confirmation Flag is: 0
Score and MaxScore: 29.2459      29.2459.  Confirmation Flag is: 1
Score and MaxScore: 26.9433      29.2459.  Confirmation Flag is: 1
rng(r); % Return the random seed to its previous setting

Notice how the track score increases with every successful update and decreases with every missed detection. Once the track score is higher than the ConfirmationThreshold, the checkConfirmation function returns true, meaning that the track is confirmed now.

As in the history-based track logic, if the track is not assigned to any detection, it should eventually be deleted. The DeletionThreshold property is used to determine when the track score has decreased enough so that the track should be deleted. Note that the score does not have to fall below the DeletionThreshold, only that the difference between the current score and the maximum score obtained by the track should go below the threshold. The reason is that if a track obtains a high score after many successful updates, and if we used the absolute score, it would take too many missed updates to delete the track. The following shows a track being deleted after three misses.

numSteps2 = 3;
scores(end+1:end+numSteps2,:) = zeros(numSteps2,2);
for i = 1:numSteps2
    miss(scoreLogic);
    deleteFlag = checkDeletion(scoreLogic);
    scores(numSteps1+i,:) = output(scoreLogic);
    disp(['Score and MaxScore: ', num2str(scores(numSteps1+i,:)),'.  Deletion Flag is: ',num2str(deleteFlag)])
end
Score and MaxScore: 24.6407      29.2459.  Deletion Flag is: 0
Score and MaxScore: 22.3381      29.2459.  Deletion Flag is: 1
Score and MaxScore: 20.0355      29.2459.  Deletion Flag is: 1
deletionScore = scores(:,2) + scoreLogic.DeletionThreshold;

stairs(scores)
hold on
plot([1,numSteps1+numSteps2],[scoreLogic.ConfirmationThreshold scoreLogic.ConfirmationThreshold],'--')
stairs(deletionScore,'--')
title('Track Score and Maximum Score')
legend('Score','MaxScore','ConfirmationThreshold','DeletionThreshold','Location','best')

If you want to delay the track deletion until more misses happen, simply change the DeletionThreshold to a more negative value.

Use a Track Logic in a Tracker

Typically, track logic is used inside a tracker. Configure the trackerGNN to use history-based track logic with confirmation after 3 successes in 5 updates and deletion after 5 misses in 6 updates.

tracker = trackerGNN('Assignment', 'Auction', 'ConfirmationThreshold', [3 5], 'DeletionThreshold', [5 6], 'TrackLogic', 'History')
tracker = 
  trackerGNN with properties:

                  TrackerIndex: 0
       FilterInitializationFcn: 'initcvekf'
                  MaxNumTracks: 100
              MaxNumDetections: Inf
                 MaxNumSensors: 20

                    Assignment: 'Auction'
           AssignmentThreshold: [30 Inf]
          AssignmentClustering: 'off'

                  OOSMHandling: 'Terminate'

                    TrackLogic: 'History'
         ConfirmationThreshold: [3 5]
             DeletionThreshold: [5 6]

            HasCostMatrixInput: false
    HasDetectableTrackIDsInput: false
               StateParameters: [1x1 struct]

             ClassFusionMethod: 'None'

                     NumTracks: 0
            NumConfirmedTracks: 0

        EnableMemoryManagement: false

The following loop updates the tracker with a detection at every odd numbered update. After 5 updates, the tracker confirms the track. You can look at the track history and the track confirmation flag after each update by examining the TrackLogicState and IsConfirmed fields of the track output.

for i = 1:5
    detectedFlag = logical(mod(i,2)); % Only odd updates are true
    if detectedFlag
        detection = {objectDetection(i,[1;2;3])};
    else
        detection = {};
    end
    [~,~,allTracks] = tracker(detection, i)
end
allTracks = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 1
                         Age: 1
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [1 0 0 0 0 0]
                 IsConfirmed: 0
                   IsCoasted: 0
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

allTracks = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 2
                         Age: 2
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [0 1 0 0 0 0]
                 IsConfirmed: 0
                   IsCoasted: 1
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

allTracks = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 3
                         Age: 3
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [1 0 1 0 0 0]
                 IsConfirmed: 0
                   IsCoasted: 0
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

allTracks = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 4
                         Age: 4
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [0 1 0 1 0 0]
                 IsConfirmed: 0
                   IsCoasted: 1
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

allTracks = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 5
                         Age: 5
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [1 0 1 0 1 0]
                 IsConfirmed: 1
                   IsCoasted: 0
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

The following loop updates the tracker 5 more times with 5 misses. The track is deleted after 4 additional updates (the 9th update overall) because it has missed detections in the 4th, 6th, 7th, 8th and 9th updates (5 out of the last 6 updates).

for i = 1:5
    detection = {};
    confirmedTrack = tracker(detection, i+5)
end
confirmedTrack = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 6
                         Age: 6
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [0 1 0 1 0 1]
                 IsConfirmed: 1
                   IsCoasted: 1
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

confirmedTrack = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 7
                         Age: 7
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [0 0 1 0 1 0]
                 IsConfirmed: 1
                   IsCoasted: 1
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

confirmedTrack = 
  objectTrack with properties:

                     TrackID: 1
                    BranchID: 0
                 SourceIndex: 0
                  UpdateTime: 8
                         Age: 8
                       State: [6x1 double]
             StateCovariance: [6x6 double]
             StateParameters: [1x1 struct]
               ObjectClassID: 0
    ObjectClassProbabilities: 1
                  TrackLogic: 'History'
             TrackLogicState: [0 0 0 1 0 1]
                 IsConfirmed: 1
                   IsCoasted: 1
              IsSelfReported: 1
            ObjectAttributes: [1x1 struct]

confirmedTrack = 

  0x1 objectTrack array with properties:

    TrackID
    BranchID
    SourceIndex
    UpdateTime
    Age
    State
    StateCovariance
    StateParameters
    ObjectClassID
    ObjectClassProbabilities
    TrackLogic
    TrackLogicState
    IsConfirmed
    IsCoasted
    IsSelfReported
    ObjectAttributes


confirmedTrack = 

  0x1 objectTrack array with properties:

    TrackID
    BranchID
    SourceIndex
    UpdateTime
    Age
    State
    StateCovariance
    StateParameters
    ObjectClassID
    ObjectClassProbabilities
    TrackLogic
    TrackLogicState
    IsConfirmed
    IsCoasted
    IsSelfReported
    ObjectAttributes

Summary

Trackers require a way to confirm tracks that are considered to be of true targets and delete tracks after they are not assigned any detections after a while. Two types of track confirmation and deletion logic were presented: a history-based track logic and a score-based track logic. The history-based track logic only considers whether a track is assigned detections in the recent past updates. The score-based track logic provides a statistical measure of how likely a track is to represent a real target. Both types of track logic objects can be used as stand-alone objects, but are typically used inside a tracker object.