Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions docs/06_Perception/centerpoint_proposal_refinement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# CenterPoint Proposal Refinement Extension

This document describes an optional CenterPoint proposal refinement extension
point for LiDAR detection. The extension is disabled by default and preserves
the original CenterPoint behavior unless explicitly enabled in the detector
model config.

## Scope

The extension point runs after CenterPoint proposal decode and before the
existing object construction and NMS/filtering path.

```text
CenterPoint preprocess / inference / decode
-> optional proposal refinement
-> existing object construction / NMS / filtering
-> existing perception output
```

It does not change Cyber RT input or output channels, perception message
semantics, tracking, fusion, prediction, or planning.

## Configuration

The extension is controlled by `ProposalRefineParam` in
`modules/perception/lidar_detection/detector/center_point_detection/proto/model_param.proto`.

Important fields:

```text
enable_proposal_refine: false
proposal_refine_mode: "mock_zero"
proposal_refine_top_k: -1
proposal_refine_score_threshold: 0.0
proposal_refine_delta_scale: 1.0
proposal_refine_debug_log: false
strict_refine: true
```

When `enable_proposal_refine=false`, the extension does not run and the original
CenterPoint path is preserved.

## Modes

`mock_zero` is a no-op mode. It selects proposals, records statistics, and
leaves boxes, scores, and labels unchanged.

`mock_delta` applies a deterministic small delta for test coverage:

```text
delta_x = 0.1
delta_yaw = 0.05
delta_score = 0.01
```

Both modes are intended for validating the extension point, configuration
gating, delta application, and rollback behavior. They are not intended to claim
perception quality improvement.

## Safety

- Disabled by default.
- No new runtime dependency on quantum libraries.
- No Cyber RT channel changes.
- No public perception message changes.
- No tracking/fusion/prediction/planning changes.
- Box dimensions are clamped positive after delta application.
- Yaw is normalized.
- Score is clamped to `[0, 1]`.

## Rollback

Set:

```text
enable_proposal_refine: false
```

or remove the optional `proposal_refine` config block. With refinement disabled,
the module skips proposal conversion/refinement and continues through the
original CenterPoint detection path.

## Tests

Focused tests:

```bash
bazel test --config=unit_test --cache_test_results=no --test_output=errors \
//modules/perception/lidar_detection:proposal_refine_test
```

Focused build:

```bash
bazel build --config=opt --config=gpu --config=nvidia \
//modules/perception/lidar_detection:apollo_perception_lidar_detection \
//modules/perception/lidar_detection:liblidar_detection_component.so
```

Lint targets:

```bash
bazel build \
//modules/perception/lidar_detection:center_point_proposal_refine_cpplint \
//modules/perception/lidar_detection:proposal_refine_test_cpplint
```

## Follow-Up Work

Optional proposal export, offline teacher experiments, student-model inference,
and experiment reports should be split into later PRs. They are intentionally
outside the conservative first PR scope.
23 changes: 23 additions & 0 deletions modules/perception/lidar_detection/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ filegroup(
]),
)

apollo_cc_library(
name = "center_point_proposal_refine",
srcs = ["detector/center_point_detection/proposal_refine.cc"],
hdrs = ["detector/center_point_detection/proposal_refine.h"],
deps = [
"//cyber",
"//modules/common/math",
"//modules/perception/lidar_detection/detector/center_point_detection/proto:center_point_model_param_cc_proto",
],
)

apollo_cc_library(
name = "point_pillars_common",
hdrs = ["detector/point_pillars_detection/common.h"],
Expand Down Expand Up @@ -163,6 +174,7 @@ apollo_cc_library(
copts = PERCEPTION_COPTS + if_profiler() + ["-DENABLE_PROFILER=1"],
deps = [
":anchor_mask_cuda",
":center_point_proposal_refine",
":feature_generator_cuda",
":nms_cuda",
":pfe_cuda",
Expand Down Expand Up @@ -220,6 +232,17 @@ apollo_component(
],
)

apollo_cc_test(
name = "proposal_refine_test",
size = "small",
srcs = ["detector/center_point_detection/proposal_refine_test.cc"],
deps = [
":center_point_proposal_refine",
"//modules/common/math",
"@com_google_googletest//:gtest_main",
],
)

apollo_cc_test(
name = "cnn_segmentation_test",
size = "large",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,53 @@ using base::Object;
using base::PointD;
using base::PointF;

namespace {

std::vector<Proposal> BuildProposals(const std::vector<float> &detections,
const std::vector<int64_t> &labels,
const std::vector<float> &scores,
int num_output_box_feature) {
std::vector<Proposal> proposals;
proposals.reserve(scores.size());
for (size_t i = 0; i < scores.size(); ++i) {
const size_t offset = i * num_output_box_feature;
Proposal proposal;
proposal.x = detections[offset + 0];
proposal.y = detections[offset + 1];
proposal.z = detections[offset + 2];
proposal.width = detections[offset + 3];
proposal.length = detections[offset + 4];
proposal.height = detections[offset + 5];
proposal.yaw = detections[offset + 6];
proposal.score = scores[i];
proposal.class_id = static_cast<int>(labels[i]);
proposals.push_back(proposal);
}
return proposals;
}

void WriteProposals(const std::vector<Proposal> &proposals,
int num_output_box_feature,
std::vector<float> *detections,
std::vector<int64_t> *labels,
std::vector<float> *scores) {
for (size_t i = 0; i < proposals.size(); ++i) {
const size_t offset = i * num_output_box_feature;
const Proposal &proposal = proposals[i];
(*detections)[offset + 0] = proposal.x;
(*detections)[offset + 1] = proposal.y;
(*detections)[offset + 2] = proposal.z;
(*detections)[offset + 3] = proposal.width;
(*detections)[offset + 4] = proposal.length;
(*detections)[offset + 5] = proposal.height;
(*detections)[offset + 6] = proposal.yaw;
(*scores)[i] = proposal.score;
(*labels)[i] = proposal.class_id;
}
}

} // namespace

// point cloud range
CenterPointDetection::CenterPointDetection()
: x_min_range_(Params::kMinXRange),
Expand Down Expand Up @@ -98,6 +145,12 @@ bool CenterPointDetection::Init(const LidarDetectorInitOptions &options) {
diff_class_iou_ = model_param_.diff_class_iou();
diff_class_nms_ = model_param_.diff_class_nms();

if (!proposal_refine_module_.Init(model_param_.proposal_refine())) {
AERROR << "Failed to init proposal refine module. mode: "
<< model_param_.proposal_refine().proposal_refine_mode();
return false;
}

cone_score_threshold_ = model_param_.postprocess().cone_score_threshold();
ped_score_threshold_ = model_param_.postprocess().ped_score_threshold();
cyc_score_threshold_ = model_param_.postprocess().cyc_score_threshold();
Expand Down Expand Up @@ -330,6 +383,48 @@ bool CenterPointDetection::Detect(const LidarDetectorOptions &options,
FilterDiffScore(output_bbox_blob, output_label_blob, output_score_blob,
&out_detections, &out_labels, &out_scores);

if (proposal_refine_module_.enabled()) {
const int num_output_box_feature =
model_param_.postprocess().num_output_box_feature();
if (num_output_box_feature < 7) {
AERROR << "Invalid num_output_box_feature: " << num_output_box_feature;
return false;
}
if (out_labels.size() != out_scores.size() ||
out_detections.size() != out_scores.size() * num_output_box_feature) {
AERROR << "Proposal input size mismatch. detections: "
<< out_detections.size() << " labels: " << out_labels.size()
<< " scores: " << out_scores.size()
<< " num_output_box_feature: " << num_output_box_feature;
return false;
}

std::vector<Proposal> proposals = BuildProposals(
out_detections, out_labels, out_scores, num_output_box_feature);
ProposalRefineStats refine_stats;
if (!MaybeRefineProposals(&proposal_refine_module_, &proposals,
&refine_stats)) {
AERROR << "Proposal refine failed.";
if (proposal_refine_module_.strict_refine()) {
return false;
}
} else {
WriteProposals(proposals, num_output_box_feature, &out_detections,
&out_labels, &out_scores);
}
if (proposal_refine_module_.debug_log()) {
AINFO << "Proposal refine enabled: true"
<< " mode: " << proposal_refine_module_.mode()
<< " proposal_count_before_refine: "
<< refine_stats.proposal_count
<< " refined_proposal_count: "
<< refine_stats.refined_proposal_count
<< " delta_min: " << refine_stats.delta_min
<< " delta_max: " << refine_stats.delta_max
<< " latency_ms: " << refine_stats.latency_ms;
}
}

GetObjects(frame->lidar2world_pose, out_detections,
out_labels, out_scores, &frame->segmented_objects);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "modules/perception/common/lidar/common/lidar_frame.h"
#include "modules/perception/lidar_detection/interface/base_lidar_detector.h"
#include "modules/perception/common/interface/base_down_sample.h"
#include "modules/perception/lidar_detection/detector/center_point_detection/proposal_refine.h"
#include "modules/perception/lidar_detection/detector/center_point_detection/proto/model_param.pb.h"

namespace apollo {
Expand Down Expand Up @@ -200,6 +201,8 @@ class CenterPointDetection : public BaseLidarDetector {
std::vector<std::string> output_blob_names_;

std::shared_ptr<BaseDownSample> down_sample_;

ProposalRefineModule proposal_refine_module_;
}; // class CenterPointDetection

} // namespace lidar
Expand Down
Loading