Skip to content

feat(autoware_mpc_lateral_controller): take the logger and the clock out of the control - #13451

Merged
kazkomiya merged 7 commits into
autowarefoundation:mainfrom
kazkomiya:refact_mpc_macro_rev1_step2b
Oct 13, 2026
Merged

kazkomiya merged 7 commits into
autowarefoundation:mainfrom
kazkomiya:refact_mpc_macro_rev1_step2b

Conversation

@kazkomiya

@kazkomiya kazkomiya commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Description

The aim of this series is to let the lateral controller run and be tested without a node, so that
it can later move to a package of its own. MPC and the classes it uses hold a logger and a
clock of their own, which means rclcpp::init() has to run first.
This PR removes those two dependencies: the package writes logs via a ROS-independent interface, and it takes
the time of the cycle as an argument.

commit1: remove two branches nothing can reach
commit2: publish the error derivatives and the solver time as diagnostics
Two commits above are trying to reduce redundant log messages.

commit3: write the log through an injected interface
LogWriter is the interface, NullLogWriter drops everything, and RosLogWriter writes to a
logger of ROS.
A plain interface would have been enough without throttling. The waiting time of a throttled
line has to live per place in the code, as RCLCPP_*_THROTTLE keeps it, and a function cannot
hold that state unless every caller passes it. That is why the AW_MPC_* macros exist. Since a
macro was needed anyway, they cover the five severities of ROS, so that the logic can keep them
after it moves out of this package. Apart from their namespace and the AW_MPC_ prefix, the
two headers hold nothing that belongs to this package.

commit4: take the ROS clock out of MPC
MPC and SteeringPredictor receive the time of the cycle as an argument. The caller already
passed the same time as the stamp of every message the cycle produces.
test_steering_predictor.cpp is new: the clock the class
held made a test impossible to write before.

Behaviour that changes

The diagnostic array grows from 27 to 32 entries. [0] to [26] keep their meaning. The four
error derivatives are written only by the dynamics vehicle model, as the removed DEBUG lines
were. The README lists the new entries.

Five logger names become one. The four names below built with rclcpp::get_logger() were
never registered with rosout, so their lines reached the terminal but not /rosout, and
~/config_logger could not set their level.This PR sends every line to the child logger of the node, which /rosout publishes and ~/config_logger can set.

logger name before reached /rosout
<node>.lateral_controller yes
mpc_logger no
mpc_steer_predictor no
mpc_utils no
mpc_util no

The steering prediction reads a different clock under simulated time. SteeringPredictor
holds a clock no time source feeds, so it reads the wall clock even with use_sim_time set.
It takes the stamp the node passes instead.

Three DEBUG lines and two ERROR lines are gone, as commit1 and commit2 describe.

Interface changes

DynamicsBicycleModel takes a const LogWriter & in its constructor, and
MPCUtils::calcTrajectoryYawFromXY takes one as its first argument. MPC::setLogger and
MPC::setClock give way to MPC::setLogWriter. MPC::calculateMPC and
SteeringPredictor::calcSteerPrediction take the time of the cycle. No caller of any of these
outside this package was found in this repository.

Related links

How was this PR tested?

process is properly launched by

ros2 run autoware_trajectory_follower_node controller_node_exe --ros-args \
  --params-file <trajectory_follower_node.param.yaml> --params-file <lateral/mpc.param.yaml> \
  --params-file <longitudinal/pid.param.yaml> --params-file <common/nearest_search.param.yaml> \
  --params-file <steer_offset.param.yaml> --params-file <vehicle_info.param.yaml> \
  -p lateral_controller_mode:=mpc -p longitudinal_controller_mode:=pid

The node comes up with the fifteen topics it had before, /diagnostics carries
controller: MPC_solve_checker, and ros2 component load reaches the constructor through the
class loader. With the five inputs published, ~/output/lateral_diagnostic carries 32 entries
and the warning of the solver appears once per second under the logger name
controller.lateral_controller. Setting that logger to DEBUG through ~/config_logger makes
the DEBUG lines of MPC appear and setting it back to INFO stops them.

unit test

  • commands:
source /opt/ros/jazzy/setup.bash && source install/setup.bash && PKG=autoware_mpc_lateral_controller && colcon build --symlink-install --packages-select $PKG --cmake-args -DBUILD_TESTING=true -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS='--coverage -O0 -g' -DCMAKE_C_FLAGS='--coverage -O0 -g' && colcon lcov-result --zero-counters --packages-select $PKG && colcon lcov-result --initial --packages-select $PKG --lcov-args=--ignore-errors=mismatch,gcov && colcon test --event-handlers console_cohesion+ --packages-select $PKG && colcon lcov-result --packages-select $PKG --lcov-args=--ignore-errors=mismatch,gcov,unused --filter "*/test/*" "/usr/*" "/opt/*" "*/install*/"
  • results:
100% tests passed, 0 tests failed out of 5

Summary coverage rate:
  lines......: 88.2% (1978 of 2242 lines)
  functions..: 89.7% (200 of 223 functions)
  branches...: 47.0% (1898 of 4042 branches)

Evaluator (TIER IV internal)

Because Evaluator cannot be executed momentarily for some technical issues, I tried to locally execute scenario tests to be fulfilled, but failed due to administration to some datasets.
We agreed with the reviewers that evaluator shall be done after the logic is extracted in later PR and then this PR can be open and merged.
->
Confirmed success in the test scnearios below in evaluator
https://evaluation.ci.tier4.jp/evaluation/reports/de0e7bd1-50c9-5ade-8ce7-665e5d5d994d?project_id=autoware_dev
https://evaluation.ci.tier4.jp/evaluation/reports/e6be184a-a794-58e3-96bb-6dd0404a63e3?project_id=autoware_dev
https://evaluation.ci.tier4.jp/evaluation/reports/92aa3150-a3df-5bcd-89f8-53791537b57e?project_id=autoware_dev

@github-actions github-actions Bot added type:documentation Creating or refining documentation. (auto-assigned) component:control Vehicle control algorithms and mechanisms. (auto-assigned) labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 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:

…g can reach

MPC::setReferenceTrajectory checks the size of the smoothed trajectory right
after it pushed a point onto it, so the size is never zero. calcNearestPoseInterp
checks the converted trajectory for points after it already returned on an empty
input, and the conversion writes one point per input element.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
…nd the solver time as diagnostics

The two DEBUG lines named "(before lpf)" and "(after lpf)" both printed the value
after the filter, because the filter had already overwritten the variable. The
value before the filter was never reachable. The solver time was a DEBUG line as
well, so reading it needed the log.

The five values now go to ~/output/lateral_diagnostic, which lengthens the array
from 27 to 32 entries. Entries [0] to [26] keep their meaning. The four error
derivatives are written only by the dynamics vehicle model, as the removed lines
were, so the other models leave them at zero.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
…njected interface

MPC wrote to a logger of its own, which needed rclcpp to be initialised before the
control could run. It now writes through LogWriter, an interface with no ROS type in
it. RosLogWriter is the one implementation that writes to a logger, and the node
builds it, so only the node layer reaches rclcpp for the log.

log_writer.hpp and ros_log_writer.hpp hold nothing that belongs to this package
apart from their namespace and the AW_MPC_ prefix of the macros, so another package
can take them as they are.

The macros build the message only when it would be read, in the way the logging
macros of rclcpp do. Write them in a source file: the waiting time of a throttled
line lives in a static of the line that writes it, and a static inside a function
defined in a header is shared between shared libraries.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
MPC and SteeringPredictor receive the time of the control cycle as an argument
and no longer hold a clock. The caller already passed the same time as the stamp
of every message the cycle produces, so the computation now reads the instant
the cycle started instead of an instant a few microseconds later.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
@kazkomiya
kazkomiya force-pushed the refact_mpc_macro_rev1_step2b branch from a49be15 to 411cf91 Compare October 1, 2026 04:14
@kazkomiya kazkomiya added the run:build-and-test-differential Mark to enable build-and-test-differential workflow. (used-by-ci) label Oct 1, 2026
@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.66667% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.44%. Comparing base (e0c3bec) to head (bf35e2f).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...ontrol/autoware_mpc_lateral_controller/src/mpc.cpp 62.50% 6 Missing ⚠️
..._lateral_controller/src/mpc_lateral_controller.cpp 66.66% 6 Missing ⚠️
...ude/autoware/mpc_lateral_controller/log_writer.hpp 66.66% 1 Missing ⚠️
.../autoware_mpc_lateral_controller/src/mpc_utils.cpp 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #13451      +/-   ##
==========================================
- Coverage   37.56%   37.44%   -0.12%     
==========================================
  Files        2025     2033       +8     
  Lines      141946   142080     +134     
==========================================
- Hits        53319    53199     -120     
- Misses      88627    88881     +254     
Flag Coverage Δ *Carryforward flag
full-suite 37.42% <ø> (-0.14%) ⬇️ Carriedforward from eb0e8d0

*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.

@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.

Thanks for PR. I have approved with minor comment.

@kazkomiya
kazkomiya force-pushed the refact_mpc_macro_rev1_step2b branch from a6b950b to 4396e47 Compare October 1, 2026 07:05
Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
@kazkomiya
kazkomiya force-pushed the refact_mpc_macro_rev1_step2b branch from 4396e47 to 5864547 Compare October 1, 2026 07:12
return true;
}

void RosLogWriter::write(const Level level, const std::string_view line) const

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.

The rclcpp logger can record the calling function and line number along with the message string, but I believe that with RosLogWriter, all of these will end up pointing to the write function. Could you fix this if possible?

… log comes from

The writer received only the level and the text, so RCUTILS_CONSOLE_OUTPUT_FORMAT
reported ros_log_writer.cpp for every line. The macros now fill a Site with the
function, the file and the line of the caller, and RosLogWriter passes it to
rcutils_log.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
@kazkomiya
kazkomiya enabled auto-merge (squash) October 13, 2026 11:33
@kazkomiya
kazkomiya merged commit 1258c0c into autowarefoundation:main Oct 13, 2026
29 of 30 checks passed
@github-project-automation github-project-automation Bot moved this from To Triage to Done in Software Working Group Oct 13, 2026
interimadd pushed a commit to tier4/autoware_universe that referenced this pull request Oct 22, 2026
…out of the control (autowarefoundation#13451)

feat(autoware_mpc_lateral_controller): take the logger and the clock out of the control (autowarefoundation#13451)

MPC and the classes it uses no longer hold a logger or a clock of ROS, so they can run and be
tested without rclcpp::init(). They write the log through an injected LogWriter and take the time
of the control cycle as an argument. All log lines go to the child logger of the node and reach
/rosout. ~/output/lateral_diagnostic grows from 27 to 32 entries: the error derivatives and the
solver time, which only DEBUG lines printed before.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
interimadd pushed a commit to tier4/autoware_universe that referenced this pull request Oct 22, 2026
…out of the control (autowarefoundation#13451)

feat(autoware_mpc_lateral_controller): take the logger and the clock out of the control (autowarefoundation#13451)

MPC and the classes it uses no longer hold a logger or a clock of ROS, so they can run and be
tested without rclcpp::init(). They write the log through an injected LogWriter and take the time
of the control cycle as an argument. All log lines go to the child logger of the node and reach
/rosout. ~/output/lateral_diagnostic grows from 27 to 32 entries: the error derivatives and the
solver time, which only DEBUG lines printed before.

Signed-off-by: Kazuki Komiya <290287332+kazkomiya@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:control Vehicle control algorithms and mechanisms. (auto-assigned) run:build-and-test-differential Mark to enable build-and-test-differential workflow. (used-by-ci) type:documentation Creating or refining documentation. (auto-assigned)

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants