Convert nav2_loopback_sim to C++#6062
Convert nav2_loopback_sim to C++#6062tonynajjar wants to merge 22 commits intoros-navigation:mainfrom
Conversation
- Removed obsolete Python utility functions from utils.py. - Updated package.xml to reflect new dependencies and build system changes. - Deleted unused configuration files (pytest.ini, setup.cfg, setup.py). - Introduced new C++ components: ClockPublisher and LoopbackSimulator for improved simulation capabilities. - Implemented main entry points for the new C++ components. - Removed outdated test files related to copyright and style checks. Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates nav2_loopback_sim from a Python-based loopback simulator to a C++ (ament_cmake) implementation, adding C++ nodes for loopback simulation and simulated /clock publishing.
Changes:
- Replaces the Python loopback simulator implementation with a C++
LoopbackSimulatorlifecycle node (TF + odom + optional LaserScan). - Adds a C++
ClockPublishernode and standalone mains for both nodes. - Converts the package to
ament_cmakeand updates dependencies / removes Python packaging + pytest-based lint tests.
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| nav2_loopback_sim/test/test_pep257.py | Removes Python PEP257 linter test (package no longer Python-based). |
| nav2_loopback_sim/test/test_flake8.py | Removes Python flake8 linter test. |
| nav2_loopback_sim/test/test_copyright.py | Removes Python copyright linter test. |
| nav2_loopback_sim/src/main.cpp | Adds C++ main for spinning the loopback simulator lifecycle node. |
| nav2_loopback_sim/src/loopback_simulator.cpp | Adds C++ loopback simulator implementation (TF, odom, optional scan from map). |
| nav2_loopback_sim/src/clock_publisher.cpp | Adds simulated /clock publisher node with runtime-tunable parameters. |
| nav2_loopback_sim/src/clock_publisher_main.cpp | Adds standalone executable main for the clock publisher node. |
| nav2_loopback_sim/setup.py | Removes Python packaging entrypoint. |
| nav2_loopback_sim/setup.cfg | Removes Python install script configuration. |
| nav2_loopback_sim/pytest.ini | Removes pytest configuration used for Python tests. |
| nav2_loopback_sim/package.xml | Switches build type to ament_cmake and updates dependencies accordingly. |
| nav2_loopback_sim/include/nav2_loopback_sim/loopback_simulator.hpp | Adds C++ public header for the loopback simulator. |
| nav2_loopback_sim/include/nav2_loopback_sim/clock_publisher.hpp | Adds C++ public header for the clock publisher. |
| nav2_loopback_sim/CMakeLists.txt | Adds ament_cmake build, library/executables, component registration, and lint config. |
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Codecov Report❌ Patch coverage is
... and 16 files with indirect coverage changes 🚀 New features to boost your workflow:
|
…move publish_period parameter Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
SteveMacenski
left a comment
There was a problem hiding this comment.
With the ClockPublisher Node question, I only did a medium fidelity review. The next one would be looking at the python vs c++ version implementations feature by feature, but generally looks good on a first look
nav2_loopback_sim/include/nav2_loopback_sim/clock_publisher.hpp
Outdated
Show resolved
Hide resolved
nav2_loopback_sim/include/nav2_loopback_sim/clock_publisher.hpp
Outdated
Show resolved
Hide resolved
I'm not entirely sure I'm following this statement. I don't think a Node with use_sim_time = true is publishing its own clock. I think its always taking a subscription from the I think this feature was something that could be toggled on/off as a param whether to publish the clock or not. If I'm reading "...of a use_sim_time node publishing its own /clock" to mean that you have a separate node publishing clocks in your implementation, then you could just toggle that param off and keep the publishing of the clock in the main simulator code. Personally, I just like this simple simulator to be 1-node since its pretty compact. Though, we could keep that clock publishing as a parameter and keep the Clock Publisher as a separate utility if architecturally you like to keep this separated. In fact, I suppose transform and laser scan handling could also be broken into separate sharper objects if desired (though I'm not asking you / trying to give you more work - only if this is the reason why you did this with the clock. Otherwise, ignore) What's the resource utilization comparison for eq. parameters for odom publishing / laser scan publishing of the python vs C++ version? I think that'll be an imporant metric for the migration guide entry. |
…essage management Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Let me clarify. In end effect I just mean that the timer for publishing the clock should use wall time whereas all other timers in the loopback simulation should use ROS time (/clock or system time). We could do this in the same node though.
I do agree just didn't see a need to separate currently but I think it will come. |
|
Got it - so I think the alignment is to use I agree on the wall time element for the clock publisher. Perhaps worth a comment in the readme about that so its clear to future readers. That should be an easy Claude refactor. Then I'll review line by line for feature parity, though I didn't see anything that stuck out. If you add tests before I do that review there's a good chance we can just merge it immediately upon review. |
… standalone executable Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
|
@SteveMacenski CI's build keeps timing out? |
|
I only had the time for a quick successful functional test today so not ready to merge yet but ready for a review |
What's happening 😅 |
The time it takes to compile the new C++ version of this is throwing off CI times. Remove loopback sim from the 2x entries in this block https://github.com/ros-navigation/navigation2/blob/main/.circleci/config.yml#L573-L584 and it should then build in |
…ishing laser scan Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
Still the same. I won't dig into it until you say you run out of ideas because you know this part better than me |
nav2_loopback_sim/include/nav2_loopback_sim/loopback_simulator.hpp
Outdated
Show resolved
Hide resolved
nav2_loopback_sim/include/nav2_loopback_sim/loopback_simulator.hpp
Outdated
Show resolved
Hide resolved
nav2_loopback_sim/include/nav2_loopback_sim/loopback_simulator.hpp
Outdated
Show resolved
Hide resolved
nav2_loopback_sim/include/nav2_loopback_sim/clock_publisher.hpp
Outdated
Show resolved
Hide resolved
nav2_loopback_sim/include/nav2_loopback_sim/clock_publisher.hpp
Outdated
Show resolved
Hide resolved
| nav2::Publisher<nav_msgs::msg::Odometry>::SharedPtr odom_pub_; | ||
| nav2::Publisher<sensor_msgs::msg::LaserScan>::SharedPtr scan_pub_; | ||
|
|
||
| rclcpp::Client<nav_msgs::srv::GetMap>::SharedPtr map_client_; |
There was a problem hiding this comment.
Want to use our Nav2 version of the service client? That wraps up alot of good boilerplate.
There was a problem hiding this comment.
- we have gotten rid of all other
rclcpp::interfaces to usenav2::now
There was a problem hiding this comment.
Good point, I think it's time we make some instructions for the agents to take into account all the specificities in Nav2
There was a problem hiding this comment.
Might be time we add a claude.md file for nav2 with all that perhaps
There was a problem hiding this comment.
I've always wondered what would be the best way to enforce some custom rules in a codebase. A quick research gave me https://github.com/semgrep/semgrep. We could make such a rule:
rules:
- id: use-nav2-service-client
pattern: rclcpp::Client<$T>
message: "Use nav2::ServiceClient<$T> instead of rclcpp::Client<$T>"
severity: WARNING
languages: [cpp]
paths:
include:
- nav2_loopback_sim/
I think Nav2 has become quite opinionated in many aspects and such rules would help both users without historical context pick the right options and help maintainers focus the review only on functionality (same as code formatters and linters).
That does not replace adding a claude.md, it's on top
There was a problem hiding this comment.
That looks mighty interesting to me. Can you put together a CI job for the ServiceClient and maybe ServiceServer? That can be a recipe I (or some jr developers I recruit) to populate more for subscriptions, publishers, parameter handling, etc. I actually have a ~85% complete list of Nav2 styling requirements that could be used to populate this.
…d z translation Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
nav2_loopback_sim/include/nav2_loopback_sim/clock_publisher.hpp
Outdated
Show resolved
Hide resolved
nav2_loopback_sim/include/nav2_loopback_sim/loopback_simulator.hpp
Outdated
Show resolved
Hide resolved
| if (publish_map_odom_tf_) { | ||
| t_map_to_odom_.header.stamp = this->now(); | ||
| tf_broadcaster_->sendTransform(t_map_to_odom_); | ||
| } |
There was a problem hiding this comment.
The original function does not send the map to odom transformation -- similar to how we don't set it with the gazebo simulator until the user does so
There was a problem hiding this comment.
I actually wish we would set it in gazebo as well. What is the reason for not setting it if in gazebo we know where the real robot spawns? In Loopback simulation, since there is no "real robot" or a counter-truth, then really anywhere we set it is valid theoretically
From a practical aspect, the resulting spam is really counter-intuitive for new users - I think their first thought would be that something is broken, not that they need to set the initial pose
[planner_server-7] [INFO 1775848366.459603721] [global_costmap.global_costmap]: Timed out waiting for transform from base_link to map to become available, tf error: Invalid frame ID "map" passed to canTransform argument target_frame - frame does not exist
[controller_server-6] [ERROR 1775848366.509515471] [local_costmap.local_costmap]: StaticLayer: "map" passed to lookupTransform argument target_frame does not exist.
There was a problem hiding this comment.
For consistency - please remove it :-) We do not know where the robot should be spawned, the user must tell us. The origin could be anywhere and/or nonsensical.
There was a problem hiding this comment.
What if we don't set it by default but make the initial pose a launch argument i.e. node param?
There was a problem hiding this comment.
Sure thing - if we add that for the normal bringup as well with Gazebo. That's a different PR though but totally fine with me
There was a problem hiding this comment.
@tonynajjar if you remove this point, I can merge this right now
|
@tonynajjar can you stop changing things since I've approved everything except for the one open topic. Can you revert the last comment messing with |
I mean I found an opportunity to improve it and I was going to re-request review on that part. Or should I open another PR just for that part? |
|
Another PR please - given I've approved the changes as-is with a pretty time consuming line-by-line python to C++ review. I'd like to not do that again in the context of this large block of code |
672dea5 to
b0426c0
Compare
Signed-off-by: Tony Najjar <tony.najjar@dexory.com>
|
Sorry this is annoying, can you move |
Basic Info
Description of contribution in a few bullet points
nav2_loopback_simfrom Python (ament_python) to C++ (ament_cmake)rclpy.Nodeto anav2::LifecycleNodewith full lifecycle support, bond connection, and composable node registrationClockPublishernode to avoid the circular dependency of ause_sim_timenode publishing its own/clock-> All the timers in the loopback simulation node should use sim time, just not the timer publishing the clock.speed_factorparameter toClockPublisher— allows running sim time faster or slower than real time, dynamically reconfigurable at runtimeodom_publish_durparameter — odometry can now be published at a different rate than the TF update ratescan_noise_stdparameter — configurable Gaussian noise on laser scan measurementsgetLaserScanraycasting — replaced ~150-line inlineLineIterator(6 directional branches with separate slope/intercept tracking) with a simple polar ray-march loop (~15 lines)nav2_util::TwistSubscriberto handle both stamped and unstampedcmd_vel(replaces the manualenable_stamped_cmd_velparameter)nav2::LifecycleNode,nav2::Publisher<T>,declare_or_get_parameter,nav2::qos::SensorDataQoS,nav2_commonCMake macrosDescription of documentation updates required from your changes
ros-navigation/docs.nav2.org#897
Description of how this change was tested
Future work that may be required in bullet points
speed_factorparameter to the launch file for easier configurationFor Maintainers:
backport-*.