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

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

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

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

Отслеживание такого многочисленного и плотного скопления представляет собой две проблемы:

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

  2. Как смочь отследить отдельный boids в такой плотной среде?

Следующий код симулирует поведение скопления для 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

Определение средства отслеживания

Вы задаете средство отслеживания как показано в примере, Как Эффективно Отследить Большие количества Объектов.

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

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

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

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

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

Следующая линия показывает, как средство отслеживания сконфигурировано с вышеупомянутыми свойствами. Вы видите, как сгенерировать код для средства отслеживания в том, Как Сгенерировать код С для Средства отслеживания, и средство отслеживания для этого примера сохранено в функции 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.

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

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

Ссылки

[1] Крэйг В. Рейнольдс, "Скопления, стада и школы: поведенческая модель", компьютерная графика, издание 21, номер 4, июль 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