Object Fusion Using Data Association in Autoware Perception
The object_merger package fuses detected objects from multiple sources using data association. It relies on the same SSP (Successive Shortest Path) algorithm used in multi_object_tracker for solving the minimum-cost flow data association problem. The cost between objects is computed from their distance, and gates (maximum distance, maximum area, minimum area) reset the cost when violated.
Node Implementation
Enum: MSG_COV_IDX
Defines indices for all covariance entries between state variables.
Class: ObjectFusionNode
The main node class handling merging logic.
Function: IsOverlapWithUnknown
Checks if an Unknown object overlaps with a known object. It takes a known object, computes distance, and then evaluates overlap based on distance, precision, recall, and Generalized IoU thresholds.
Function: BuildClassThresholdMap
Converts a list of distance thresholds (from parameter distance_threshold_list) into a map associating each class label with its threshold. This map is consumed by IsOverlapWithUnknown.
Constructor: ObjectFusionNode::ObjectFusionNode
The node subscribes to two topics, each carrying detection results to be merged:
Parameters loaded:
The constructor sets up a synchronized callback using sync_:
sync_(SyncPolicy(10), sub0_, sub1_);
sync_.registerCallback(std::bind(&ObjectFusionNode::OnObjectsReceived, this, _1, _2));
It also instantiates a DataAssociation object and builds the overlap threshold map:
overlap_params_.distance_threshold_map =
BuildClassThresholdMap(declare_parameter<std::vector<double>>("distance_threshold_list"));
Callback: ObjectFusionNode::OnObjectsReceived
Both object lists are first transformed to the base_link frame. Then data association is performed:
std::unordered_map<int, int> direct_assignment, reverse_assignment;
const auto & objects0 = transformed_objects0.objects;
const auto & objects1 = transformed_objects1.objects;
Eigen::MatrixXd score_matrix =
data_assoc_->ComputeScoreMatrix(transformed_objects1, transformed_objects0);
data_assoc_->Assign(score_matrix, direct_assignment, reverse_assignment);
For each matched pair, the output object is selected based on the configured PriorityMode:
for (size_t i = 0; i < objects0.size(); ++i) {
const auto & obj0 = objects0[i];
if (direct_assignment.contains(i)) {
const auto & obj1 = objects1[direct_assignment[i]];
switch (priority_) {
case PriorityMode::Source0:
output.objects.push_back(obj0);
break;
case PriorityMode::Source1:
output.objects.push_back(obj1);
break;
case PriorityMode::Confidence:
if (obj1.existence_probability <= obj0.existence_probability)
output.objects.push_back(obj0);
else
output.objects.push_back(obj1);
break;
}
} else {
output.objects.push_back(obj0);
}
}
Unmatched objects from the second list are appended:
for (size_t j = 0; j < objects1.size(); ++j) {
if (!reverse_assignment.contains(j))
output.objects.push_back(objects1[j]);
}
Finally, overlapping Unknown objects are filtered out by scanning the merged list with IsOverlapWithUnknown and removing those that overlap with known objects.