В этом примере показано, как отслеживать большое количество объектов. Генерируется большая стая птиц и глобальный ближайший соседний многообъектный трекер, trackerGNN
, используется для оценки движения каждой птицы в стаде.
Движение стада моделируется с помощью поведенческой модели, предложенной Рейнольдсом [1]. В этом примере стада состоит из 1000 моделируемых птиц, называемых боидами, чье начальное положение и скорость были ранее сохранены. Они следуют трем правилам стекания: предотвращение столкновения, соответствие скорости и центрирование стада. Каждое правило связано с весом, и общее поведение стада возникает из относительного взвешивания каждого правила. При этом выбираются веса, заставляющие стадо летать вокруг определенной точки и создавать плотный центр. Другие настройки веса могут привести к появлению различных моделей поведения.
Отслеживание такого большого и плотного стада представляет две задачи:
Как эффективно отслеживать 1000 боидов?
Как уметь отслеживать отдельные боиды в таком плотном окружении?
Следующий код моделирует поведение стада в течение 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