Как эффективно отслеживать большое количество объектов

В этом примере показано, как использовать trackerGNN отслеживать большое количество целей. Подобные методы могут быть применены к trackerJPDA и trackerTOMHT также.

Введение

Во многих приложениях трекеры обязаны отслеживать сотни или тысячи объектов. Увеличение количества треков, поддерживаемых трекером, является проблемой, вызванной вычислительной сложностью алгоритма в основе каждого трекера. В частности, не легко масштабироваться два распространенных этапа на шаге обновления трекера: вычисление стоимости присвоения и выполнение присвоения. Расчет затрат на присвоение является общим для trackerGNN, trackerJPDA, и trackerTOMHTи методы, показанные в этом примере, могут быть применены при использовании любого из этих трекеров. То, как каждый трекер выполняет назначение, является уникальным для каждого трекера и может потребовать индивидуальных решений для улучшения эффективности трекера, которые выходят за возможности этого примера.

Сценарий

В целях этого примера вы задаете сценарий, который содержит 900 платформ, организованных в сетку 15 на 15, с каждой сеткой камеры содержащей 4 платформы. Цель камер сетки состоит в том, чтобы продемонстрировать преимущества вычисления крупных затрат, объясненные ниже в примере.

Следующий код упорядочивает 900 объектов в камере сетки и создает визуализацию. Слева показан весь сценарий. Справа визуализация масштабируется на 4 камерах сетки. Обратите внимание, что каждая камера содержит 4 платформы.

[platforms,tp,zoomedtp] = createPlatforms;

Используйте расчет стоимости присвоения по умолчанию

В этом разделе показаны результаты отслеживания платформ, определенных выше, с помощью trackerGNN с параметрами по умолчанию AssignmentThreshold. The AssignmentThreshold свойство содержит два значения: [C1 C2], где C1 - порог, используемое для присвоения и C2 - порог для грубого расчета, поясненное в следующем разделе.

Когда трекер обновляется новым набором обнаружений, он вычисляет стоимость назначения каждого обнаружения каждому треку. Точный расчет стоимости должен учитывать измерения и неопределенность каждого обнаружения, а также ожидаемые измерения и ожидаемую неопределенность по каждому треку, как показано ниже.

По умолчанию C2 установлено в Inf, что требует вычисления затрат на все комбинации дорожки и обнаружения. Это приводит к более точному назначению, но является более в вычислительном отношении интенсивным. Вы должны начать с настройки по умолчанию, чтобы убедиться, что трекер назначает обнаружения трекам наилучшим образом, и затем рассмотрите снижение значения C2 для сокращения времени, необходимого для вычисления стоимости назначения.

Во время вычисления затрат на присвоение элементы матрицы затрат, значения которых выше C1 заменяются на Inf. Это помогает алгоритму назначения игнорировать невозможные назначения.

Определите трекер, который может отслеживать до 1000 треков. Трекер использует фильтр Калмана с расширенной скоростью по умолчанию, и его состояние определяется как [x;vx;y;vy;z;vz], который используется в positionSelector ниже для получения позиционных компонентов.

tracker = trackerGNN('MaxNumTracks',1000, 'AssignmentThreshold', [30 Inf]);
positionSelector = [1 0 0 0 0 0; 0 0 1 0 0 0; 0 0 0 0 0 0];

При первом вызове шага трекер устанавливает экземпляры всех треков. Чтобы изолировать время, необходимое для создания экземпляров дорожек, от времени вычислений, необходимого для первого шага, можно вызвать setup и reset перед шагом трекера. Смотрите вспомогательную функцию runTracker в конце этого примера для получения дополнительной информации.

[trkSummary,truSummary,info] = runTracker(platforms,tracker,positionSelector,tp,zoomedtp);
Tracker set up time: 8.3108
Step 1 time: 3.7554
Step 2 time: 15.3029
Step 3 time: 14.1099
Step 4 time: 14.3506
Step 5 time: 14.3963

All steps from now are without detections.
Step 6 time: 0.53103
Step 7 time: 0.52582
Step 8 time: 0.50639
Step 9 time: 0.50909
Step 10 time: 0.16034
Scenario done. All tracks are now deleted.

Результаты отслеживания анализируются путем анализа значений метрик присвоения треков. Для идеального отслеживания общее количество треков должно быть равно количеству платформ, и не должно быть ложных, замененных или расходящихся треков. Точно так же не должно быть недостающей истины или пропусков в сводные данные истины.

assignmentMetricsSummary(trkSummary,truSummary)
Track assignment metrics summary:
    TotalNumTracks    NumFalseTracks    MaxSwapCount    MaxDivergenceCount    MaxDivergenceLength
    ______________    ______________    ____________    __________________    ___________________

         900                0                0                  0                      0         

Truth assignment metrics summary:
    TotalNumTruths    NumMissingTruths    MaxEstablishmentLength    MaxBreakCount
    ______________    ________________    ______________________    _____________

         900                 0                      1                     0      

Использование расчета затрат на грубое присвоение

В предыдущем разделе вы видели, что трекер способен отслеживать все платформы, но каждый шаг обновления занимает много времени. Большая часть времени была потрачена на вычисление матрицы затрат на присвоение.

Исследуя матрицу затрат, можно увидеть, что подавляющее большинство ее элементов являются, по сути, Inf.

cm = info.CostMatrix;
disp("Cost matrix has " + numel(cm) + " elements.");
disp("But the number of finite values is " + numel(cm(isfinite(cm))) + newline)
Cost matrix has 810000 elements.
But the number of finite values is 2700

Вышеописанный результат показывает, что расчет стоимости тратит слишком много времени на вычисление стоимости присвоения всех комбинаций дорожки и обнаружения. Однако большинство из этих комбинаций слишком далеко, чтобы быть назначенным, поскольку фактическое измерение слишком далеко от ожидаемого измерения дорожки, основанного на характеристиках датчика. Чтобы избежать отходов при расчете всех затрат, можно использовать расчет крупных затрат.

Грубое вычисление выполняется, чтобы проверить, какие комбинации дорожки и обнаружения могут потребовать точного нормированного вычисления расстояния. Только комбинации, грубые затраты на присвоение которых ниже C2 вычисляются точно. Расчет грубой стоимости показан на изображении ниже. Обнаружение представлено его шумом измерения$z$ и измерения. $R$Две дорожки предсказываются ко времени обнаружения и проецируются в пространство измерений, изображенные точками$z_{e_1}$ и. $z_{e_2}$Обратите внимание, что неопределенность дорожки не проецируется в пространство измерений, что позволяет нам векторизировать грубое вычисление. Это приблизительная оценка, потому что учитывается только неопределенность вокруг обнаружения. В изображенном примере первая дорожка падает за пределы грубой вычислительной шины, в то время как вторая дорожка падает внутрь нее. Таким образом, точное вычисление стоимости выполняется только для комбинации этого обнаружения и второй дорожки.

Чтобы использовать расчет грубых затрат, отпустите трекер и измените его AssignmentThreshold до значения [30 200]. Затем перезапустите трекер.

release(tracker)
tracker.AssignmentThreshold = [30 200];
[trkSummary,truSummary] = runTracker(platforms,tracker,positionSelector,tp,zoomedtp);
Tracker set up time: 6.5846
Step 1 time: 3.5863
Step 2 time: 3.4095
Step 3 time: 2.9347
Step 4 time: 2.8555
Step 5 time: 2.9397

All steps from now are without detections.
Step 6 time: 0.51446
Step 7 time: 0.52277
Step 8 time: 0.54865
Step 9 time: 0.50941
Step 10 time: 0.19085
Scenario done. All tracks are now deleted.

Вы замечаете, что шаги 3-5 теперь требуют значительно меньше времени для выполнения. Шаг 2 также быстрее, чем раньше, но все еще медленнее, чем шаг 3-5.

Чтобы понять, почему шаг 2 медленнее, рассмотрите состояния трека после первого обновления трекера. Состояния содержат информацию о положении, но скорость все еще равна нулю. Когда трекер вычисляет стоимость присвоения, он предсказывает состояния дорожки во время обнаружения, но поскольку дорожки имеют нулевую скорость, они остаются в том же положении. Это приводит к большим расстояниям между измерениями обнаружения и ожидаемыми измерениями от предсказанных состояний пути. Эти относительно большие затраты на назначение затрудняют для алгоритма назначения поиск наилучшего назначения, что заставляет шаг 2 занимать больше времени, чем шаги 3-5.

Важно проверить, что назначение трека с вычислением крупных затрат остается таким же, как и без него. Если метрики назначения дорожки не совпадают, необходимо увеличить размер грубых расчетных ворот. Следующее показывает, что отслеживание все еще идеально, как это было в предыдущем разделе, но каждый шаг обработки занял меньше времени.

assignmentMetricsSummary(trkSummary,truSummary)
Track assignment metrics summary:
    TotalNumTracks    NumFalseTracks    MaxSwapCount    MaxDivergenceCount    MaxDivergenceLength
    ______________    ______________    ____________    __________________    ___________________

         900                0                0                  0                      0         

Truth assignment metrics summary:
    TotalNumTruths    NumMissingTruths    MaxEstablishmentLength    MaxBreakCount
    ______________    ________________    ______________________    _____________

         900                 0                      1                     0      

Использование расчета внешних затрат

Другой способ контролировать время, необходимое для вычисления присвоения затрат, - это использование собственного вычисления затрат на присвоение вместо значения по умолчанию, используемого трекером.

При вычислении внешних затрат могут учитываться атрибуты, которые не являются частью состояния дорожки и ожидаемого измерения. Он также может использовать различные метрики расстояния, для примера евклидовой нормы вместо нормализованного расстояния. Выбор вычисления стоимости для применения зависит от специфики задачи, пространства измерений и от того, как вы задаете состояние и измерение.

Чтобы использовать расчет внешних затрат, вы отпускаете трекер и устанавливаете его HasCostMatrixInput свойство true. Вы должны передать свою собственную матрицу затрат как дополнительный вход с каждым обновлением на трекер. Смотрите вспомогательную функцию runTracker для получения дополнительной информации.

release(tracker);
tracker.HasCostMatrixInput = true;
[trkSummary,truSummary] = runTracker(platforms,tracker,positionSelector,tp,zoomedtp);
assignmentMetricsSummary(trkSummary,truSummary)
Tracker set up time: 6.559
Step 1 time: 3.4394
Step 2 time: 1.7852
Step 3 time: 1.474
Step 4 time: 1.5312
Step 5 time: 1.5152

All steps from now are without detections.
Step 6 time: 0.60809
Step 7 time: 0.61374
Step 8 time: 0.616
Step 9 time: 0.63798
Step 10 time: 0.22762
Scenario done. All tracks are now deleted.

Track assignment metrics summary:
    TotalNumTracks    NumFalseTracks    MaxSwapCount    MaxDivergenceCount    MaxDivergenceLength
    ______________    ______________    ____________    __________________    ___________________

         900                0                0                  0                      0         

Truth assignment metrics summary:
    TotalNumTruths    NumMissingTruths    MaxEstablishmentLength    MaxBreakCount
    ______________    ________________    ______________________    _____________

         900                 0                      1                     0      

Как ожидалось, время вычислений даже меньше при использовании функции вычисления внешних затрат.

Измените алгоритм назначения GNN

Другая опция, чтобы попытаться использовать другой алгоритм назначения GNN, который может быть более эффективным в поиске назначения путем изменения Assignment свойство трекера.

release(tracker)
tracker.Assignment = 'Jonker-Volgenant';
tracker.HasCostMatrixInput = true;
runTracker(platforms,tracker,positionSelector,tp,zoomedtp);
Tracker set up time: 6.494
Step 1 time: 3.5346
Step 2 time: 1.894
Step 3 time: 3.1192
Step 4 time: 3.1212
Step 5 time: 3.1458

All steps from now are without detections.
Step 6 time: 0.61109
Step 7 time: 0.62456
Step 8 time: 0.61849
Step 9 time: 0.60604
Step 10 time: 0.22303
Scenario done. All tracks are now deleted.

Алгоритм Йонкера-Вольгенанта выполняет назначение на втором шаге быстрее относительно алгоритма Мункреса по умолчанию.

Симуляция Монте-Карло

Если вы хотите запустить несколько сценариев, не изменяя настройки трекера, не нужно вызывать release способ. Вместо этого просто вызовите reset метод для удаления предыдущей информации о трекере. Таким образом, вы экономите время, необходимое для создания экземпляров всех треков. Обратите внимание на «Время настройки трекера» ниже относительно предыдущих запусков.

reset(tracker)
runTracker(platforms,tracker,positionSelector,tp,zoomedtp);
Tracker set up time: 0.097531
Step 1 time: 3.4684
Step 2 time: 1.6592
Step 3 time: 3.1429
Step 4 time: 3.1274
Step 5 time: 3.0994

All steps from now are without detections.
Step 6 time: 0.63232
Step 7 time: 0.61857
Step 8 time: 0.61433
Step 9 time: 0.60698
Step 10 time: 0.25301
Scenario done. All tracks are now deleted.

Сводные данные

Этот пример показал, как отслеживать большое количество объектов. При отслеживании многих объектов трекер тратит большую часть времени вычислений на вычисление назначения затрат для каждой комбинации дорожки и обнаружения. Вы увидели, как использовать порог вычисления затрат для улучшения времени, затраченного на вычисление затрат по присвоению. В сложение пример показал, как использовать расчет внешних затрат, который может быть разработан, чтобы быть более вычислительно эффективным для конкретной задачи отслеживания, которая у вас есть.

Можно уменьшить порог присвоения затрат или использовать расчет внешних затрат для улучшения скорости trackerJPDA и trackerTOMHT также.

Вспомогательные функции

createPlatforms

Эта функция создает платформы в сетке 20x20 с 2x2 платформами на сетку камеры.

function [platforms,tp,zoomedtp] = createPlatforms
% This is a helper function to run the tracker and display the results. It
% may be removed in the future.
nh  = 15; % Number of horizontal grid cells
nv  = 15; % Number of vertical grid cells
nsq = 2;  % 2x2 platforms in a grid cell
nPl = nh*nv*nsq^2; % Overall number of platforms
xgv = sort(-50 + repmat(100 * (1:nh), [1 nsq]));
ygv = sort(-50 + repmat(100 * (1:nv), [1 nsq]));
[X,Y] = meshgrid(xgv,ygv);

npts = nsq/2;
xshift = 10*((-npts+1):npts) -5;
yshift = xshift;

xadd = repmat(xshift, [1 nh]);
yadd = repmat(yshift, [1 nv]);

[Xx, Yy] = meshgrid(xadd,yadd);

X = X + Xx;
Y = Y + Yy;
pos = [X(:),Y(:),zeros(numel(X),1)];

% The following creates an array of struct for the platforms, which are
% used later for track assignment metrics.
vel = [3 1 0]; % Platform velocity
platforms = repmat(struct('PlatformID', 1, 'Position', [0 0 0], 'Velocity', vel),nPl,1);
for i = 1:nPl
    platforms(i).PlatformID  = i;
    platforms(i).Position(:) = pos(i,:);
end

% Visualization
f = figure('Position',[1 1 1425 700]);
movegui center;
h1 = uipanel(f,'FontSize',12,'Position',[.01 .01 .48 .98],"Title","Scene View");
a1 = axes(h1,'Position',[0.05 0.05 0.9 0.9]);
tp = theaterPlot('Parent', a1, 'XLimits',[0 nh*100], 'YLimits',[0 nv*100]);
set(a1,'XTick',0:100:nh*100)
set(a1,'YTick',0:100:nv*100)
grid on
pp = trackPlotter(tp,'Tag','Truth','Marker','^','MarkerEdgeColor','k','MarkerSize',4,'HistoryDepth',10);
plotTrack(pp,reshape([platforms.Position],3,[])');
trackPlotter(tp, 'Tag','Tracks','MarkerEdgeColor','b','MarkerSize',6,'HistoryDepth',10);
c = get(a1.Parent,'Children');
for i = 1:numel(c)
    if isa(c(i),'matlab.graphics.illustration.Legend')
        set(c(i),'Visible','off')
    end
end

h2 = uipanel(f,'FontSize',12,'Position',[.51 .01 .48 .98],'Title','Zoomed View');
a2 = axes(h2,'Position',[0.05 0.05 0.9 0.9]);
zoomedtp = theaterPlot('Parent', a2, 'XLimits',[400 500], 'YLimits',[400 500]);
set(a2,'XTick',400:100:500)
set(a2,'YTick',400:100:500)
grid on
zoomedpp = trackPlotter(zoomedtp,'DisplayName','Truth','Marker','^','MarkerEdgeColor','k','MarkerSize',6,'HistoryDepth',10);
plotTrack(zoomedpp,reshape([platforms.Position],3,[])');
trackPlotter(zoomedtp, 'DisplayName','Tracks','MarkerEdgeColor','b','MarkerSize',8,'HistoryDepth',10,'ConnectHistory','on','FontSize',1);
end

runTracker

Эта функция запускает трекер, обновляет плоттеры и собирает метрики назначения треков.

function [trkSummary,truSummary,info] = runTracker(platforms,tracker,positionSelector,tp,zoomedtp)
% This is a helper function to run the tracker and display the results. It
% may be removed in the future.

pp = findPlotter(tp,'Tag','Truth');
trp = findPlotter(tp, 'Tag','Tracks');
zoomedpp = findPlotter(zoomedtp,'DisplayName','Truth');
zoomedtrp = findPlotter(zoomedtp, 'DisplayName','Tracks');

% To save time, pre-allocate all the detections and assign them on the fly.
nPl = numel(platforms);
det = objectDetection(0,[0;0;0]);
dets = repmat({det},[nPl,1]);

% Define a track assignment metrics object.
tam = trackAssignmentMetrics;

% Bring the visualization back.
set(tp.Parent.Parent.Parent,'Visible','on')

hasExternalCostFunction = tracker.HasCostMatrixInput;

% Measure the time it takes to set the tracker up.
tic
if ~isLocked(tracker)
    if hasExternalCostFunction
        setup(tracker,dets,0,0);
    else
        setup(tracker,dets,0);
    end
end
reset(tracker)
disp("Tracker set up time: " + toc);

% Run 5 steps with detections for all the platforms.
for t = 1:5
    for i = 1:nPl
        dets{i}.Time = t;
        dets{i}.Measurement = platforms(i).Position(:);
    end

    tic
    if hasExternalCostFunction
        if isLocked(tracker)
            % Use predictTracksToTime to get all the predicted tracks.
            allTracks = predictTracksToTime(tracker,'all',t);
        else
            allTracks = [];
        end
        costMatrix = predictedEuclidean(allTracks,dets,positionSelector);
        [tracks,~,~,info] = tracker(dets,t,costMatrix);
    else
        [tracks,~,~,info] = tracker(dets,t);
    end
    trPos = getTrackPositions(tracks, positionSelector);
    trIDs = string([tracks.TrackID]');
    disp("Step " + t + " time: " + toc)

    % Update the plot.
    plotTrack(pp,reshape([platforms.Position],3,[])');
    plotTrack(trp,trPos);
    plotTrack(zoomedpp,reshape([platforms.Position],3,[])');
    plotTrack(zoomedtrp,trPos,trIDs);
    drawnow

    % Update the track assignment metrics object.
    if nargout
        [trkSummary, truSummary] = tam(tracks,platforms);
    end

    % Update the platform positions.
    for i = 1:nPl
        platforms(i).Position = platforms(i).Position + platforms(i).Velocity;
    end
end
snapnow

% Run steps with no detections until the tracker deletes all the tracks.
disp("All steps from now are without detections.")
while ~isempty(tracks)
    t = t+1;
    tic
    if hasExternalCostFunction
        allTracks = predictTracksToTime(tracker,'all',t);
        costMatrix = predictedEuclidean(allTracks,{},positionSelector);
        tracks = tracker({},t,costMatrix);
    else
        tracks = tracker({},t);
    end
    disp("Step " + t + " time: " + toc)

    % Update the position of the tracks to plot.
    trPos = getTrackPositions(tracks,positionSelector);
    trIDs = string([tracks.TrackID]');

    % Update the plot.
    plotTrack(pp,reshape([platforms.Position],3,[])');
    plotTrack(trp,trPos);
    plotTrack(zoomedpp,reshape([platforms.Position],3,[])');
    plotTrack(zoomedtrp,trPos,trIDs);
    drawnow

    % Update the platform positions.
    for i = 1:nPl
        platforms(i).Position = platforms(i).Position + platforms(i).Velocity;
    end
end
disp("Scenario done. All tracks are now deleted." + newline)
clearData(pp)
clearData(trp)
clearData(zoomedpp)
clearData(zoomedtrp)
set(tp.Parent.Parent.Parent,'Visible','off') % Prevent excessive snapshots
drawnow
end

predictedEuclidean

Функция вычисляет евклидово расстояние между измеренными положениями от обнаружений и предсказанными положениями от дорожек.

function euclidDist = predictedEuclidean(tracks,detections,positionSelector)
% This is a helper function to run the tracker and display the results. It
% may be removed in the future.

if isempty(tracks) || isempty(detections)
    euclidDist = zeros(numel(tracks),numel(detections));
    return
end

predictedStates = [tracks.State];
predictedPositions = positionSelector * predictedStates;
dets = [detections{:}];
measuredPositions = [dets.Measurement];
euclidDist = zeros(numel(tracks),numel(detections));
for i = 1:numel(detections)
    diffs = bsxfun(@minus, predictedPositions',measuredPositions(:,i)');
    euclidDist(:,i) = sqrt(sum((diffs .* diffs),2));
end
end

assignmentMetricsSummary

Функция отображает метрики назначения ключей в таблицу форме.

function assignmentMetricsSummary(trkSummary,truSummary)
trkSummary = rmfield(trkSummary, {'TotalSwapCount','TotalDivergenceCount',...
    'TotalDivergenceLength','MaxRedundancyCount','TotalRedundancyCount',...
    'MaxRedundancyLength','TotalRedundancyLength'});
truSummary = rmfield(truSummary, {'TotalEstablishmentLength','TotalBreakCount',...
    'MaxBreakLength','TotalBreakLength'});
trkTable = struct2table(trkSummary);
truTable = struct2table(truSummary);
disp("Track assignment metrics summary:")
disp(trkTable)
disp("Truth assignment metrics summary:")
disp(truTable)
end