Отслеживание стада птиц

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

Определение сценария

Движение стада моделируется с помощью поведенческой модели, предложенной Рейнольдсом [1]. В этом примере стада состоит из 1000 моделируемых птиц, называемых боидами, чье начальное положение и скорость были ранее сохранены. Они следуют трем правилам стекания: предотвращение столкновения, соответствие скорости и центрирование стада. Каждое правило связано с весом, и общее поведение стада возникает из относительного взвешивания каждого правила. При этом выбираются веса, заставляющие стадо летать вокруг определенной точки и создавать плотный центр. Другие настройки веса могут привести к появлению различных моделей поведения.

Отслеживание такого большого и плотного стада представляет две задачи:

  1. Как эффективно отслеживать 1000 боидов?

  2. Как уметь отслеживать отдельные боиды в таком плотном окружении?

Следующий код моделирует поведение стада в течение 100 шагов по 0,1 секунды. На графике слева показано стадо в целом и рисунок справа масштабирован на самой плотной части в центре стада.

s = rng;   % Keep the current state of the random number generator
rng(2019); % Set the random number generator for repeatable results
load("initialFlock.mat","x","v");
flock = helperFlock("NumBoids",size(x,1),"CollisionAviodanceWeight",0.5,...
    "VelocityMatchingWeight",0.1,"FlockCenteringWeight",0.5,"Velocity",v,...
    "Position",x,"BoidAcceleration",1);
truLabels = string(num2str((1:flock.NumBoids)'));
bound = 20;
flockCenter = mean(x,1);
[tp1,tp2] = helperCreateDisplay(x,bound);

% Simulate 100 steps of flocking
numSteps = 100;
allx = repmat(x,[1 1 numSteps]);
dt = 0.1;
for i = 1:numSteps
    [x,v] = move(flock,dt);
    allx(:,:,i) = x;
    plotTrack(tp1.Plotters(1),x)
    inView = findInView(x,-bound+flockCenter,bound+flockCenter);
    plotTrack(tp2.Plotters(1),x(inView,:),truLabels(inView))
    drawnow
end

Определение трекера

Трекер определяется как показано в примере Как эффективно отслеживать большое количество объектов.

Вы наблюдаете, что боиды следуют изогнутым путем и выбираете модель постоянного поворота, заданную initctekf.

Чтобы ограничить время, необходимое для вычисления стоимости, вы уменьшаете порог вычисления крупных затрат в AssignmentThreshold к низкому значению.

Кроме того, вы выбираете более эффективную Jonker-Volgenant в качестве алгоритма назначения вместо стандартного Munkres алгоритм.

Вы хотите, чтобы треки были быстро подтверждены и удалены, и установите пороги подтверждения и удаления [2 3] и [2 2], соответственно.

Наконец, вы знаете, что датчик сканирует только часть стада при любом заданном скане, и поэтому вы устанавливаете HasDetectableTrackIDsInput на true чтобы иметь возможность передать обнаруживаемые идентификаторы дорожек трекеру.

В следующей линии показано, как трекер сконфигурирован с указанными выше свойствами. Можно увидеть, как сгенерировать код для трекера в How to Generate C Code for a Tracker, и трекер для этого примера сохранен в функции flockTracker_kernel.m

% tracker = trackerGNN("FilterInitializationFcn",@initctekf,"MaxNumTracks",1500,...
%         "AssignmentThreshold",[50 800],"Assignment","Jonker-Volgenant",...
%         "ConfirmationThreshold",[2 3],"DeletionThreshold",[2 2],...
%         "HasDetectableTrackIDsInput",true);

Отследите стадо

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

Упрощенная модель датчика моделируется с помощью detectFlock вспомогательная функция. Он имитирует датчик, который сканирует стадо слева направо и захватывает пятую часть диапазона стада в оси X в каждом скане. Датчик имеет 0,98 вероятность обнаружения, и шум моделируется с помощью нормального распределения со стандартным отклонением 0,1 метра вокруг каждого компонента положения.

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

clear flockTracker_kernel
positionSelector = [1 0 0 0 0 0 0; 0 0 1 0 0 0 0; 0 0 0 0 0 1 0];
trackIDs = zeros(0,1,'uint32');
trax = zeros(0,3);
bounds = inf(3,2);
alltrax = zeros(size(allx));
allIDs = repmat({},1,numSteps);
trup2 = tp2.Plotters(1);
trap2 = tp2.Plotters(2);
trup2.HistoryDepth = 2*trap2.HistoryDepth;
clearPlotterData(tp1)
clearPlotterData(tp2)
for i = 1:numSteps
    t = i*dt;
    [detections, currentScan] = detectFlock(allx(:,:,i),t);
    bounds(1,:) = currentScan;
    tracksInScan = findInView(trax,bounds(:,1),bounds(:,2));
    [tracks,info] = flockTracker_kernel(detections,t,trackIDs(tracksInScan,1));
    trax = getTrackPositions(tracks,positionSelector);
    if ~isempty(tracks)
        trackIDs = uint32([tracks.TrackID]');
    else
        trackIDs = zeros(0,1,'uint32');
    end
    alltrax(1:size(trax,1),1:3,i) = trax;
    allIDs{i} = string(trackIDs);
    helperVisualizeDisplay(tp1,tp2,truLabels,allx,allIDs,alltrax,i)
end
rng(s); % Reset the random number generator to its previous state

Результат трекера в сгенерированном коде

Следующий GIF показывает эффективность трекера в файл MEX.

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

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

Ссылки

[1] Craig W. Reynolds, «Flocks, Hards, and Schools: A Behavioral Model», Computer Graphics, Vol. 21, Number 4, July 1987.

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

helperCreateDisplay

Функция создает отображение примера и возвращает указатель на оба театральных графика.

function [tp1,tp2] = helperCreateDisplay(x,bound)
f  = figure("Visible", "off");
set(f,"Position",[1 1 1425 700]);
movegui(f,"center")
h1 = uipanel(f,"FontSize",12,"Position",[.01 .01 .48 .98],"Title","Flock View");
h2 = uipanel(f,"FontSize",12,"Position",[.51 .01 .48 .98],"Title","Flock Center");
flockCenter = mean(x,1);

a1 = axes(h1,'Position',[0.05 0.05 0.9 0.9]);
grid(a1,'on')
tp1 = theaterPlot("Parent",a1);

% Flock View (Truncated)
halfspan = 250;
tp1.XLimits = 100*round([-halfspan+flockCenter(1) halfspan+flockCenter(1)]/100);
tp1.YLimits = 100*round([-halfspan+flockCenter(2) halfspan+flockCenter(2)]/100);
tp1.ZLimits = 100*round([-halfspan+flockCenter(3) halfspan+flockCenter(3)]/100);
trackPlotter(tp1,"DisplayName","Truth","HistoryDepth",0,"Marker","^","MarkerSize",4,"ConnectHistory","off");
set(findall(a1,"Type","line","Tag","tpTrackHistory_Truth"),"Color","k");
view(a1,3)
legend('Location','NorthEast')

% Flock center
a2 = axes(h2,'Position',[0.05 0.05 0.9 0.9]);
grid(a2,'on')
tp2 = theaterPlot("Parent",a2);
tp2.XLimits = 10*round([-bound+flockCenter(1) bound+flockCenter(1)]/10);
tp2.YLimits = 10*round([-bound+flockCenter(2) bound+flockCenter(2)]/10);
tp2.ZLimits = 10*round([-bound+flockCenter(3) bound+flockCenter(3)]/10);
trackPlotter(tp2,"DisplayName","Truth","HistoryDepth",0,...
    "Marker","^","MarkerSize",6,"ConnectHistory","off","FontSize",1);
set(findall(a2,"Type","line","Tag","tpTrackHistory_Truth"),"Color","k");

% Track plotters
TrackColor = [0 0.4470 0.7410]; % Blue
TrackLength = 50;
trackPlotter(tp1,"DisplayName","Tracks","HistoryDepth",TrackLength,"ConnectHistory","off",...
    "Marker",".","MarkerSize",3,"MarkerEdgeColor",TrackColor,"MarkerFaceColor",TrackColor);
set(findall(tp1.Parent,"Type","line","Tag","tpTrackHistory_Tracks"),...
    "Color",TrackColor,"MarkerSize",3,"MarkerEdgeColor",TrackColor);
trackPlotter(tp2,"DisplayName","Tracks","HistoryDepth",TrackLength,"ConnectHistory","on",...
    "Marker","s","MarkerSize",8,"MarkerEdgeColor",TrackColor,"MarkerFaceColor","none","FontSize",1);
set (findall(tp2.Parent,"Type","line","Tag","tpTrackPositions_Tracks"),"LineWidth",2);
set(findall(tp2.Parent,"Type","line","Tag","tpTrackHistory_Tracks"),"Color",TrackColor,"LineWidth",1);

view(a2,3)
legend('Location','NorthEast')
set(f,'Visible','on')
end

detectFlock

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

function [detections,scanLimits] = detectFlock(x,t)
persistent sigma allDetections currentScan numScans
numBoids = size(x,1);
pd = 0.98;
if isempty(sigma)
    sigma = 0.1;
    oneDet = objectDetection(0,[0;0;0],"MeasurementNoise",sigma,'ObjectAttributes',struct);
    allDetections = repmat(oneDet,numBoids,1);
    currentScan = 1;
    numScans = 5;
end

% Vectorized calculation of all the detections
x = x + sigma*randn(size(x));
[allDetections.Time] = deal(t);
y = mat2cell(x',3,ones(1,size(x,1)));
[allDetections.Measurement] = deal(y{:});

% Limit the coverage area based on the number of scans
flockXSpan = [min(x(:,1),[],1),max(x(:,1),[],1)];
spanPerScan = (flockXSpan(2)-flockXSpan(1))/numScans;
scanLimits = flockXSpan(1) + spanPerScan * [(currentScan-1) currentScan];
inds = and(x(:,1)>=scanLimits(1), x(:,1)<=scanLimits(2));

% Add Pd
draw = rand(size(inds));
inds = inds & (draw<pd);
dets = allDetections(inds);
detections = num2cell(dets);

% Promote the scan count
currentScan = currentScan+1;
if currentScan > numScans
    currentScan = 1;
end
end

findInView

Функция возвращает логический массив для позиций, которые попадают в пределы minBound и maxBound.

function inView = findInView(x,minBound,maxBound)
inView = false(size(x,1),1);
inView(:) = (x(:,1)>minBound(1) & x(:,1)<maxBound(1)) & ...
         (x(:,2)>minBound(2) & x(:,2)<maxBound(2)) & ...
         (x(:,3)>minBound(3) & x(:,3)<maxBound(3));
end

helperVisualizeDisplay

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

function helperVisualizeDisplay(tp1,tp2,truLabels,allx,allIDs,alltrax,i)
trup1 = tp1.Plotters(1);
trap1 = tp1.Plotters(2);
trup2 = tp2.Plotters(1);
trap2 = tp2.Plotters(2);
plotTrack(trup1,allx(:,:,i))
n = numel(allIDs{i});
plotTrack(trap1,alltrax(1:n,:,i))

bounds = [tp2.XLimits;tp2.YLimits;tp2.ZLimits];

inView = findInView(allx(:,:,i),bounds(:,1),bounds(:,2));
plotTrack(trup2,allx(inView,:,i),truLabels(inView))
inView = findInView(alltrax(1:n,:,i),bounds(:,1),bounds(:,2));
plotTrack(trap2,alltrax(inView,:,i),allIDs{i}(inView))
drawnow
end
Для просмотра документации необходимо авторизоваться на сайте