主要内容

如何高效地跟踪大量对象

这个例子展示了如何使用trackerGNN跟踪大量的目标。类似的技术也可以应用于trackerJPDAtrackerTOMHT

介绍

在许多应用程序中,跟踪器需要跟踪数百或数千个对象。由于每个跟踪器的核心算法的计算复杂性,增加跟踪器维护的轨迹数量是一个挑战。特别是,跟踪器更新步骤中的两个常见阶段不容易伸缩:计算分配成本和执行分配。分配成本的计算是常见的trackerGNN,trackerJPDA,trackerTOMHT,本例中显示的技术可以在使用这些跟踪器时应用。每个跟踪器执行分配的方式对每个跟踪器都是独特的,可能需要定制的解决方案来提高跟踪器的性能,这超出了本示例的范围。金宝搏官方网站

场景

对于本例,您将定义一个场景,该场景包含900个平台,组织在一个15 × 15的网格中,每个网格单元格包含4个平台。网格单元的目的是演示粗成本计算的好处,示例中稍后将对此进行说明。

下面的代码在网格单元格中排列900个对象并创建可视化。在左侧显示了整个场景。在右侧,可视化放大了4个网格单元格。注意,每个单元格包含4个平台。

(平台、tp zoomedtp) = createPlatforms;

使用默认分配成本计算

这一节展示了使用trackerGNN用默认AssignmentThreshold.的AssignmentThreshold属性包含两个值:(C1, C2),在那里C1阈值是否用于分配和C2是在下一节中解释的粗计算的阈值。

当跟踪器被更新为一组新的检测时,它计算将每个检测分配到每个轨迹的代价。准确的成本计算必须考虑每个检测的测量和不确定度,以及每个轨道的预期测量和预期不确定度,如下所示。

默认情况下,C2设置为Inf,这需要计算所有轨道和检测组合的成本。这将导致更精确的赋值,但也需要更多的计算。您应该从默认设置开始,以确保跟踪器以最佳方式为跟踪分配检测,然后考虑降低值C2减少计算分配成本所需的时间。

在分配成本计算中,成本矩阵中值大于的元素C1这样做有助于赋值算法忽略不可能的赋值。

定义一个跟踪器,可以跟踪多达1000条轨迹。跟踪器采用默认的恒速扩展卡尔曼滤波器,其状态定义为[x, vx; y; v; z; vz],它被positionSelector下面来获取位置组件。

追踪= trackerGNN (“MaxNumTracks”, 1000,“AssignmentThreshold”[正]30日);positionSelector = [1 0 0 0 0;0 0 1 0 0 0;0 0 0 0 0];

在第一次调用step时,跟踪器实例化所有跟踪。要将实例化轨道所需的时间与第一步所需的处理时间隔离开来,可以调用设置重置在踏上追踪器之前。见支持函数金宝apprunTracker请在本例的最后了解更多细节。

[trkSummary、truSummary信息]= runTracker(平台、跟踪positionSelector、tp zoomedtp);
跟踪器设置时间:8.3108步骤1时间:3.7554步骤2时间:15.3029步骤3时间:14.1099步骤4时间:14.3506步骤5时间:14.3963

从现在开始的所有步骤都不会被发现。步骤6时间:0.53103步骤7时间:0.52582步骤8时间:0.50639步骤9时间:0.50909步骤10时间:0.16034场景完成。所有轨道现在都被删除了。

您可以通过检查跟踪分配指标值来分析跟踪结果。为了实现完美的跟踪,轨道的总数应该等于平台的数量,并且不应该有假的、交换的或发散的轨道。同样,在真理总结中也不应该有遗漏真理或间断。

assignmentMetricsSummary (trkSummary truSummary)
跟踪任务指标简介:TotalNumTracks NumFalseTracks MaxSwapCount MaxDivergenceCount MaxDivergenceLength  ______________ ______________ ____________ __________________ ___________________ 900 0 0 0 0真理的任务指标简介:TotalNumTruths NumMissingTruths MaxEstablishmentLength MaxBreakCount  ______________ ________________ ______________________ _____________ 900 0 1 0

使用粗糙分配成本计算

在前一节中,您看到跟踪器能够跟踪所有平台,但每个更新步骤都需要很长时间。大部分时间都花在计算分配成本矩阵上。

检查成本矩阵,您可以看到它的绝大多数元素实际上是Inf。

厘米= info.CostMatrix;disp (“成本矩阵有”+元素个数(cm) +“元素”。);disp ("但有限值的数量是"+ numel(cm(isfinite(cm))) + newline)
成本矩阵有810000个元素。但有限值的数目是2700

以上结果表明,成本计算在计算所有跟踪与检测组合的分配成本上花费了过多的时间。然而,由于实际测量与基于传感器特性的轨道预期测量距离太远,大多数组合都太远而无法分配。为了避免在计算所有成本时的浪费,可以使用粗略的成本计算。

通过粗略计算来验证哪些轨道和探测组合需要精确的归一化距离计算。只有粗分配成本小于的组合C2计算准确。粗略的成本计算如下图所示。探测用它的测量来表示z美元和测量噪声R美元.两条轨迹被预测到检测时间,并投影到测量空间,由点描述美元z_ {e_1} $美元z_ {e_2} $.注意,轨迹不确定性没有投影到测量空间,这允许我们对粗略计算进行矢量化。这是一个粗略的估计,因为只考虑了检测周围的不确定性。在描述的例子中,第一个轨道落在粗计算门的外面,而第二个轨道落在它里面。因此,只有将该检测与第二轨道相结合时才能进行准确的成本计算。

使用粗成本计算,释放跟踪器并对其进行修改AssignmentThreshold为[30 200]。然后,重新运行跟踪器。

发布追踪(追踪)。AssignmentThreshold= [30 200]; [trkSummary,truSummary] = runTracker(platforms,tracker,positionSelector,tp,zoomedtp);
跟踪器设置时间:6.5846步骤1时间:3.5863步骤2时间:3.4095步骤3时间:2.9347步骤4时间:2.8555步骤5时间:2.9397

从现在开始的所有步骤都不会被发现。步骤6时间:0.51446步骤7时间:0.52277步骤8时间:0.54865步骤9时间:0.50941步骤10时间:0.19085场景完成。所有轨道现在都被删除了。

您可以看到步骤3-5现在需要的时间明显更少了。步骤2也比以前快,但仍然比步骤3-5慢。

要理解为什么步骤2比较慢,请考虑第一次跟踪器更新后的轨迹状态。状态包含位置信息,但速度仍然为零。跟踪器在计算分配代价时,将轨迹状态预测到检测次数,但由于轨迹速度为零,所以它们保持在相同的位置。这导致探测测量值与来自预测轨迹状态的预期测量值之间的距离很大。这些较大的分配成本使得分配算法更难找到最优分配,导致步骤2比步骤3-5花费更多的时间。

重要的是要验证粗略成本计算的轨道分配与没有粗略成本计算的轨道分配是一样的。如果跟踪分配指标不相同,则必须增加粗计算门的大小。下面的内容显示了跟踪仍然和上一节一样完美,但是每个处理步骤花费的时间更少。

assignmentMetricsSummary (trkSummary truSummary)
跟踪任务指标简介:TotalNumTracks NumFalseTracks MaxSwapCount MaxDivergenceCount MaxDivergenceLength  ______________ ______________ ____________ __________________ ___________________ 900 0 0 0 0真理的任务指标简介:TotalNumTruths NumMissingTruths MaxEstablishmentLength MaxBreakCount  ______________ ________________ ______________________ _____________ 900 0 1 0

使用外部成本计算

另一种控制计算成本分配所需时间的方法是使用您自己的分配成本计算,而不是跟踪器使用的默认方法。

外部成本计算可以考虑不属于轨道状态和预期度量的属性。它还可以使用不同的距离度量,例如用欧几里得范数代替标准化距离。应用哪种成本计算的选择取决于问题的具体情况、度量空间以及您如何定义状态和度量。

要使用外部成本计算,您可以释放跟踪器并设置它HasCostMatrixInput属性为true。您必须将自己的成本矩阵作为每次更新的附加输入传递给跟踪器。见支持函数金宝apprunTracker为更多的细节。

释放(跟踪);跟踪器。HasCostMatrixInput = true;[trkSummary, truSummary] = runTracker(平台、跟踪positionSelector、tp zoomedtp);assignmentMetricsSummary (trkSummary truSummary)
跟踪器设置时间:6.559步骤1时间:3.4394步骤2时间:1.7852步骤3时间:1.474步骤4时间:1.5312步骤5时间:1.5152

从现在开始的所有步骤都不会被发现。步骤6时间:0.60809步骤7时间:0.61374步骤8时间:0.616步骤9时间:0.63798步骤10时间:0.22762场景完成。所有轨道现在都被删除了。跟踪任务指标简介:TotalNumTracks NumFalseTracks MaxSwapCount MaxDivergenceCount MaxDivergenceLength  ______________ ______________ ____________ __________________ ___________________ 900 0 0 0 0真理的任务指标简介:TotalNumTruths NumMissingTruths MaxEstablishmentLength MaxBreakCount  ______________ ________________ ______________________ _____________ 900 0 1 0

正如预期的那样,当使用外部成本计算函数时,处理时间甚至更低。

修改GNN分配算法

另一个选择是尝试使用不同的GNN分配算法,通过修改可能更有效地找到分配赋值跟踪器的属性。

发布追踪(追踪)。赋值=“Jonker-Volgenant”;跟踪器。HasCostMatrixInput = true;runTracker(平台、跟踪positionSelector tp, zoomedtp);
跟踪器设置时间:6.494步骤1时间:3.5346步骤2时间:1.894步骤3时间:3.1192步骤4时间:3.1212步骤5时间:3.1458

从现在开始的所有步骤都不会被发现。步骤6时间:0.61109步骤7时间:0.62456步骤8时间:0.61849步骤9时间:0.60604步骤10时间:0.22303场景完成。所有轨道现在都被删除了。

Jonker-Volgenant算法执行第二步分配的速度比默认Munkres算法快。

蒙特卡罗模拟

如果希望在不修改跟踪器设置的情况下运行多个场景,则不需要调用释放方法。相反,只要打电话给重置方法从跟踪器中清除以前的跟踪信息。这样,您就节省了实例化所有轨道所需的时间。注意下面的“跟踪器设置时间”相对于以前的运行。

重置(跟踪)runTracker(平台、跟踪positionSelector、tp zoomedtp);
跟踪器设置时间:0.097531步骤1时间:3.4684步骤2时间:1.6592步骤3时间:3.1429步骤4时间:3.1274步骤5时间:3.0994

从现在开始的所有步骤都不会被发现。步骤6时间:0.63232步骤7时间:0.61857步骤8时间:0.61433步骤9时间:0.60698步骤10时间:0.25301场景完成。所有轨道现在都被删除了。

总结

这个例子展示了如何跟踪大量的对象。当跟踪多个目标时,跟踪器花费大量的处理时间来计算每次跟踪和检测组合的成本分配。您看到了如何使用成本计算阈值来改进用于计算分配成本的时间。此外,该示例还展示了如何使用外部成本计算,对于特定的跟踪问题,这种计算效率可能更高。

您可以降低成本分配阈值或使用外部成本计算来提高trackerJPDAtrackerTOMHT

金宝app支持功能

createPlatforms

这个函数在一个20x20的网格中创建平台,每个网格单元格有2x2个平台。

函数(平台、tp zoomedtp) = createPlatforms这是一个helper函数,用于运行跟踪器并显示结果。它%将来可能会被移除。nh = 15;%水平网格单元格数nv = 15;%垂直网格单元格数nsq = 2;网格单元格中的% 2x2平台不良贷款= nh * nv * nsq ^ 2;%平台总数XGV = sort(-50 + repmat(100 * (1:nh), [1 nsq]));Ygv = sort(-50 + repmat(100 * (1:nv), [1 nsq])));(X, Y) = meshgrid (xgv ygv);《不扩散核武器条约》= 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)];%下面的代码为平台创建了一个结构体数组%稍后用于跟踪分配指标。Vel = [3 1 0];%平台速度平台= repmat(结构体(“PlatformID”, 1“位置”, [0 0 0],“速度”韦尔),不良贷款,1);i = 1:nPl平台(i)。PlatformID =我;平台(我).Position (,) = pos(我:);结束%的可视化图(f =“位置”,[1 1 1425 700]);movegui中心;h1 = uipanel (f,“字形大小”12“位置”,(。01.01.48.98],“标题”,“场景视图”);a1 =轴(h1,“位置”,[0.05 0.05 0.9 0.9]);tp = theaterPlot (“父”a1,“XLimits”[0 nh * 100],“YLimits”[0 nv * 100]);集(a1,“XTick”0:100: nh * 100)集(a1,“YTick”0:100: nv * 100)网格页= trackPlotter (tp,“标签”,“真相”,“标记”,“^”,“MarkerEdgeColor”,“k”,“MarkerSize”4“HistoryDepth”10);plotTrack (pp、重塑([platforms.Position] 3 []) ');trackPlotter (tp,“标签”,“跟踪”,“MarkerEdgeColor”,“b”,“MarkerSize”6“HistoryDepth”10);c = (a1。父母,“孩子”);i = 1:元素个数(c)如果isa (c(我),“matlab.graphics.illustration.Legend”)组(c(我),“可见”,“关闭”结束结束h2 = uipanel (f,“字形大小”12“位置”,(。50.01 . 48.98],“标题”,“放大视图”);a2 =轴(h2,“位置”,[0.05 0.05 0.9 0.9]);zoomedtp = theaterPlot (“父”a2“XLimits”(400 500),“YLimits”500年[400]);集(a2,“XTick”400:100:500)组(a2,“YTick”400:100:500电网)zoomedpp = trackPlotter (zoomedtp,“DisplayName的”,“真相”,“标记”,“^”,“MarkerEdgeColor”,“k”,“MarkerSize”6“HistoryDepth”10);plotTrack (zoomedpp重塑([platforms.Position] 3 []) ');trackPlotter (zoomedtp“DisplayName的”,“跟踪”,“MarkerEdgeColor”,“b”,“MarkerSize”8“HistoryDepth”10“ConnectHistory”,“上”,“字形大小”1);结束

runTracker

该函数运行跟踪器,更新绘图仪,并收集跟踪分配指标。

函数[trkSummary、truSummary信息]= runTracker(平台、跟踪positionSelector、tp zoomedtp)这是一个helper函数,用于运行跟踪器并显示结果。它%将来可能会被移除。页= findPlotter (tp,“标签”,“真相”);trp = findPlotter (tp,“标签”,“跟踪”);zoomedpp = findPlotter (zoomedtp,“DisplayName的”,“真相”);zoomedtrp = findPlotter (zoomedtp,“DisplayName的”,“跟踪”);%为了节省时间,预先分配所有的检测并动态分配它们。不良贷款=元素个数(平台);侦破= objectDetection (0, 0, 0, 0));依据= repmat({侦破},[不良贷款,1]);定义一个轨道分配指标对象。tam = trackAssignmentMetrics;把视觉化带回来。集(tp.Parent.Parent.Parent,“可见”,“上”) hasexternalstfunction = tracker.HasCostMatrixInput;%测量设置跟踪器所需的时间。抽搐如果~ isLocked(跟踪)如果hasExternalCostFunction设置(追踪,侦破,0,0);其他的设置(追踪,侦破,0);结束结束重置(跟踪)disp (“跟踪器设置时间:”+ toc);%对所有平台运行5步检测。t = 1:5i = 1:nPl det {i}。时间= t;依据我{}。测量=平台(i) .Position (:);结束抽搐如果hasExternalCostFunction如果isLocked(跟踪)%使用predictTracksToTime获取所有预测轨道。allTracks = predictTracksToTime(追踪,“所有”t);其他的allTracks = [];结束costMatrix = predictedEuclidean (allTracks侦破,positionSelector);[痕迹,~,~,信息]=追踪(侦破t costMatrix);其他的[痕迹,~,~,信息]=追踪(侦破,t);结束trPos = getTrackPositions(轨道,位置选择器);trIDs =字符串([tracks.TrackID]”);disp (“步骤”+ t +“:”+ toc)%更新图表。plotTrack (pp、重塑([platforms.Position] 3 []) ');trPos plotTrack (trp);plotTrack (zoomedpp重塑([platforms.Position] 3 []) ');plotTrack (zoomedtrp trPos trIDs);drawnow%更新轨道分配指标对象。如果nargout [trkSummary, trussummary] = tam(轨道,平台);结束%更新平台位置。i = 1:nPl平台(i)。位置=平台(i)。位置+平台(我).Velocity;结束结束snapnow%在没有检测的情况下运行步骤,直到跟踪器删除所有轨道。disp (“从现在开始的所有步骤都不会被发现。”~isempty(tracks) t = t+1;抽搐如果hasexternalstfunction allTracks = predictTracksToTime(跟踪器,“所有”t);{}, costMatrix = predictedEuclidean (allTracks positionSelector);跟踪=追踪({}t costMatrix);其他的跟踪=追踪({},t);结束disp (“步骤”+ t +“:”+ toc)%更新要绘制的轨道位置。trPos = getTrackPositions(跟踪、positionSelector);trIDs =字符串([tracks.TrackID]”);%更新图表。plotTrack (pp、重塑([platforms.Position] 3 []) ');trPos plotTrack (trp);plotTrack (zoomedpp重塑([platforms.Position] 3 []) ');plotTrack (zoomedtrp trPos trIDs);drawnow%更新平台位置。i = 1:nPl平台(i)。位置=平台(i)。位置+平台(我).Velocity;结束结束disp (”场景。所有的音轨现在都被删除了。”+ newline) clearData(pp) clearData(trp) clearData(zoomedpp) clearData(zoomedtrp) set(tp.Parent.Parent.Parent,“可见”,“关闭”%防止快照过多drawnow结束

predictedEuclidean

该函数计算从探测到的测量位置和从轨道到预测位置之间的欧氏距离。

函数euclidDist = predictedEuclidean(跟踪、检测、positionSelector)这是一个helper函数,用于运行跟踪器并显示结果。它%将来可能会被移除。如果isempty(tracks) || isempty(detections) euclidDist = 0 (numel(tracks),numel(detections));返回结束predictedStates = [tracks.State];predictedPositions = positionSelector * predictedStates;依据=(检测{:});measuredPositions = [dets.Measurement];euclidDist = 0(元素个数(跟踪),元素个数(检测);i = 1:numel(检测)diffs = bsxfun(@minus, predictedPositions',measuredPositions(:,i)');euclidDist(:,i) =√(sum((diff .* diff),2));结束结束

assignmentMetricsSummary

该函数以表格形式显示键分配指标。

函数trkSummary = rmfield(trkSummary, {)“TotalSwapCount”,“TotalDivergenceCount”,...“TotalDivergenceLength”,“MaxRedundancyCount”,“TotalRedundancyCount”,...“MaxRedundancyLength”,“TotalRedundancyLength”});truSummary = rmfield(truSummary, {“TotalEstablishmentLength”,“TotalBreakCount”,...“MaxBreakLength”,“TotalBreakLength”});trkTable = struct2table (trkSummary);truTable = struct2table (truSummary);disp (“跟踪分配指标总结:”) disp trkTable disp (“真理分配指标总结:”) disp (truTable)结束