Skip to content

refactor(autoware_radar_objects_adapter): separate the conversion from the node - #13470

Merged
kentdotn merged 3 commits into
autowarefoundation:mainfrom
kentdotn:refactor/radar-objects-adapter-separate-logic
Oct 19, 2026
Merged

kentdotn merged 3 commits into
autowarefoundation:mainfrom
kentdotn:refactor/radar-objects-adapter-separate-logic

Conversation

@kentdotn

@kentdotn kentdotn commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Description

Recommended: read this PR commit by commit, and the last commit on its own. Three commits: the node class is renamed, its files are renamed, and the conversion moves out of them. The file names swap roles on the way: before this PR radar_objects_adapter.{hpp,cpp} is the node; after it, that name holds the logic and the node lives in radar_objects_adapter_node.{hpp,cpp}. Read as one diff, the PR looks as if node code had left radar_objects_adapter.{hpp,cpp}. Commit 3 shows what happened: the logic left radar_objects_adapter_node.{hpp,cpp}, and the node stayed. Each commit leaves the package building and every test passing.

Purpose

This PR separates the conversion logic of radar_objects_adapter from its ROS node, so that the conversion can be unit-tested without ROS and later reused without the node. It is phase 3 of the plan used for TrafficLightMapVisualizerNode (#12487 → #12511 → #12553 → #12591 / #12596 → #12612): the characterization tests (#13405, #13445) and the preparation (#13465) come before it; the unit tests of the logic come after.

Nothing the node publishes changes. The 25 characterization cases pass unchanged through every commit, and the moved code computes every value as it did.

What goes where

The node keeps everything that talks to ROS: the topics and their QoS, the parameters, the logging, the publishing. RadarObjectsAdapter, in radar_objects_adapter.{hpp,cpp} and free of rclcpp, takes the rest:

  • It is constructed with the parameter values (RadarObjectsAdapterParams), the classification remap as label ids (ClassificationRemap, a map keyed by the message's own label types) and the input topic name, whose hash goes into the tracked objects' UUIDs as before. The remap is built from the parameter names by make_classification_remap_from_string_pair(), which returns it together with the entries it could not read as a name (ClassificationRemapParseResult); the node warns about those.
  • update_radar_info() is fed every radar info message and returns what the adapter decided from it, as a tl::expected: the configuration it will convert with (ConversionConfiguration, so far the optional fields it will fill from a parameter together with the value it will use), or the error InvalidRadarInfo, a std::runtime_error that names the required fields the message does not declare. The node logs that decision; it no longer knows which parameter stands in for which field.
  • convert() answers a RadarObjects message with a tl::expected: the DetectedObjects and TrackedObjects to publish (ConversionOutcome), or the error MissingRadarInfo, a std::runtime_error saying that no valid radar info has arrived. Whether the gate is open is decided inside convert(), because the conversion cannot run without a valid radar info; the node only decides how to log it (severity and throttling stay in the node). tl::expected is what the perception packages use for their logic classes; here each function has its own error type, derived from std::runtime_error so that what() is the log text, and a second kind of error would turn that function's error type into a std::variant of the two.

Inside the adapter, the former node members keep their names and their readers: the field map, valid_radar_info_, the *_available_ flags, topic_hash_code_, required_attributes_ and classification_remap_ are read by the same covariance and populate_* functions as before, now private members of the adapter. The only change in the moved bodies is that the six default_*_ members become locals at the top of populate_common_fields(), copied from params_ (default_size_x for default_size_x_), and that the acceleration covariance helper, which reads no member, is declared static: cppcheck asks for it (functionStatic) now that the class is not derived from Node, which is what kept it quiet about the same function in the node. parse_as_detections / parse_as_tracks become to_detected_objects / to_tracked_objects and return the message instead of publishing it.

The commits

# Commit Kind What to look for
1 name the node class after its role mechanical RadarObjectsAdapter → RadarObjectsAdapterNode, namespace autoware → autoware::radar_objects_adapter, so that the logic can take the plain name. The consequence to decide on: the component plugin name becomes autoware::radar_objects_adapter::RadarObjectsAdapterNode. The launch files reference the executable, whose name does not change, and no launch file in autoware_launch or in TIER IV's launchers names the plugin.
2 name the node files after the node class mechanical git mv of the two source files to radar_objects_adapter_node.{hpp,cpp}, include guard and CMakeLists.txt following. Rename only.
3 separate the conversion from the node design What goes where, as above. git show --color-moved=zebra on this commit tells the moved bodies from the new interface: the interface in radar_objects_adapter.hpp, the error type, the two result builders and the node's two callbacks are new; the rest of radar_objects_adapter.cpp is moved. One warning changes shape without changing what it prints: the six RCLCPP_WARN_ONCE call sites for a defaulted field become one loop over the adapter's list, with the node remembering which fields it has warned about, because RCLCPP_WARN_ONCE fires once per call site, not once per message. One error line changes what it prints: the missing required fields are listed in it (see "Effects on system behavior").

Left alone on purpose

What the moved code shows and this PR does not touch, because none of it belongs to the separation. Each is a later PR under the unit tests:

  • Building the tracked object first and deriving the detected object from it, which replaces the populate_common_fields<T> template and the two loops over the objects.
  • Making the classification remap a class with two layered tables: a fixed one for the labels the perception pipeline does not use, and the configurable, radar-specific one.
  • The fields of successive radar info messages still accumulate, as they did in the node. The maintainers have answered that a radar info describes the radar once, so the first valid one is to fix them.
  • The two 0.0 assignments to the Y_Z covariance entry, which go when the covariances are built by value (the maintainers' answer: the z-related entries are to be set explicitly, all of them), and the float parameters.
  • INVALID and UNKNOWN movement status read as stationary. Kept as it is until there is data to decide otherwise.

Related links

Parent Issue:

How was this PR tested?

From the CI-equivalent dev container (ghcr.io/autowarefoundation/autoware:universe-dependencies-jazzy):

colcon build --packages-select autoware_radar_objects_adapter \
  --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo
colcon test --packages-select autoware_radar_objects_adapter \
  --event-handlers console_direct+
  • The 25 characterization cases pass unchanged after every commit; the package's tests pass with 0 failures.
  • grep -E 'rclcpp|agnocast|RCLCPP' src/radar_objects_adapter.{hpp,cpp} finds nothing.
  • clang-format, cpplint and the rest of the repository's pre-commit hooks pass, and cppcheck with the CI's own settings reports nothing at every commit.

Notes for reviewers

  • Commit 3 is the one to read; commits 1 and 2 are renames. Read the commits, not the combined diff of the PR: the file names swap roles (see the note at the top), and the combined diff is misleading about which way the code moved.
  • The node still logs at the same severities. The _ONCE behavior is kept: once per field for the defaulted fields (now by a set in the node), once in total for the missing required fields, which are now all named in that one line: the loop of RCLCPP_ERROR_ONCE it replaces only ever printed the first one, because _ONCE is per call site.

Interface changes

The component plugin name changes from autoware::RadarObjectsAdapter to autoware::radar_objects_adapter::RadarObjectsAdapterNode. The executable name, the node name, the topics and the parameters do not change.

Effects on system behavior

None on what is published. One log line changes: the error for an invalid radar info lists every missing required field in parentheses, where it used to print the first one on a separate line.

🤖 Generated with Claude Code

kentdotn and others added 2 commits October 8, 2026 14:37
…ts role

RadarObjectsAdapter becomes RadarObjectsAdapterNode in the namespace
autoware::radar_objects_adapter, so that the conversion logic can take
the plain name when it is separated from the node. The component plugin
name changes accordingly; the executable name and the node name stay,
and the launch files only reference the executable.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Kentaro NAGATOMO <kentaro.nagatomo@tier4.jp>
…he node class

radar_objects_adapter.{hpp,cpp} become radar_objects_adapter_node.{hpp,cpp},
so that the conversion logic can take the plain file name when it is
moved out of the node in the next step. Rename only; the include guard
follows the file name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Kentaro NAGATOMO <kentaro.nagatomo@tier4.jp>
@github-actions github-actions Bot added the component:sensing Data acquisition from sensors, drivers, preprocessing. (auto-assigned) label Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Thank you for contributing to the Autoware project!

🚧 If your pull request is in progress, switch it to draft mode.

Please ensure:

@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 37.58%. Comparing base (fe7ac6e) to head (9200205).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #13470      +/-   ##
==========================================
+ Coverage   37.56%   37.58%   +0.02%     
==========================================
  Files        2025     2027       +2     
  Lines      141920   141968      +48     
==========================================
+ Hits        53316    53364      +48     
  Misses      88604    88604              
Flag Coverage Δ *Carryforward flag
daily ?
full-suite 37.52% <ø> (-0.04%) ⬇️ Carriedforward from fe7ac6e

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kentdotn
kentdotn force-pushed the refactor/radar-objects-adapter-separate-logic branch from bdf88d5 to 87e269a Compare October 8, 2026 06:50
@kentdotn
kentdotn marked this pull request as ready for review October 8, 2026 07:05

@sasakisasaki sasakisasaki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. I approve this PR while put quick win comments 👍

Comment on lines +246 to +251
auto default_size_x = params_.default_size_x;
auto default_size_y = params_.default_size_y;
auto default_size_z = params_.default_size_z;
auto default_position_z = params_.default_position_z;
auto default_velocity_z = params_.default_velocity_z;
auto default_acceleration_z = params_.default_acceleration_z;

@sasakisasaki sasakisasaki Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we do not need to define these variables for simplicity. Because we can directly use this value as my proposal below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that they are not needed. They are there so that the moved lines read as they did in the node (default_size_xz_ becomes default_size_x) and this commit stays a move. The function is replaced as a whole in the next refactoring step, which builds the tracked object first (kentdotn#15), and the locals disappear with it. I would like to leave them as they are in this PR.

Comment on lines +257 to +259
output_shape.dimensions.x = size_x_available_ ? input_object.size.x : default_size_x;
output_shape.dimensions.y = size_y_available_ ? input_object.size.y : default_size_y;
output_shape.dimensions.z = size_z_available_ ? input_object.size.z : default_size_z;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my proposal is applied (same for other places):

Suggested change
output_shape.dimensions.x = size_x_available_ ? input_object.size.x : default_size_x;
output_shape.dimensions.y = size_y_available_ ? input_object.size.y : default_size_y;
output_shape.dimensions.z = size_z_available_ ? input_object.size.z : default_size_z;
output_shape.dimensions.x = size_x_available_ ? input_object.size.x : params_.default_size_x;
output_shape.dimensions.y = size_y_available_ ? input_object.size.y : params_.default_size_y;
output_shape.dimensions.z = size_z_available_ ? input_object.size.z : params_.default_size_z;

@kentdotn
kentdotn force-pushed the refactor/radar-objects-adapter-separate-logic branch from 87e269a to 60ecbcb Compare October 8, 2026 09:24

@technolojin technolojin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked runtime and worked. LGTM

@interimadd interimadd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR!
I have left comments on the design of core logic functions. Could you take a look?

…m the node

The conversion of radar objects into detected and tracked objects moves
out of the node into RadarObjectsAdapter, a class without rclcpp, in
radar_objects_adapter.{hpp,cpp}. The node keeps the topics, the
parameters and the logging; it hands the adapter the parameter values,
the classification remap and the input topic name, feeds it the radar
info messages, and publishes what convert() returns.

The conversion code is the former node code with its members: the field
map, the validity and the *_available_ flags, the remap table and the
hash of the topic name keep their names and are read by the same
functions as before, now private members of the adapter. What is new is
the interface. update_radar_info() returns what the adapter decided from
a radar info: the optional fields it will fill from a parameter and the
value it will use, or, as the error of a tl::expected, the required
fields the message does not declare, so that the node logs the decision
without knowing which parameter stands in for which field. convert()
returns the messages to publish, or why the objects were dropped. The
errors derive from std::runtime_error and carry their text; the node
keeps the severity and the throttling. The remap built from the
parameter names comes back together with the entries that could not be
read, instead of through an output argument. No value is computed
differently. The acceleration covariance helper, which reads no member,
is declared static: cppcheck asks for it now that the class is not
derived from Node.

One log line changes: the missing required fields are listed in the
error itself, where the node printed them in a loop of
RCLCPP_ERROR_ONCE that only ever showed the first one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Kentaro NAGATOMO <kentaro.nagatomo@tier4.jp>
@kentdotn
kentdotn force-pushed the refactor/radar-objects-adapter-separate-logic branch from 60ecbcb to 9200205 Compare October 9, 2026 09:48

@interimadd interimadd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for addressing comments! LGTM

@YoshiRi YoshiRi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:sensing Data acquisition from sensors, drivers, preprocessing. (auto-assigned) run:build-and-test-differential Mark to enable build-and-test-differential workflow. (used-by-ci)

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

5 participants