Skip to content

Update costmap on request#3300

Closed
JohnTGZ wants to merge 12 commits intoros-navigation:mainfrom
JohnTGZ:update-costmap-on-request
Closed

Update costmap on request#3300
JohnTGZ wants to merge 12 commits intoros-navigation:mainfrom
JohnTGZ:update-costmap-on-request

Conversation

@JohnTGZ
Copy link

@JohnTGZ JohnTGZ commented Nov 26, 2022


Basic Info

This PR targets #3121, implement the 'update costmap on request' function via the boolean parameter update_on_request which can be specified on the costmap layer.
This PR is also a continuation from #3297 , which is closed as it was supposed to be merged into the main branch.

Info Please fill out this column
Ticket(s) this addresses (#3121)
Primary OS tested on (Ubuntu 22.04)
Robotic platform tested on (turtlebot gazebo simulation, rolling distro)

Description of contribution in a few bullet points

  • Implemented update_on_request parameter for the costmap server
    • If True, the thread running the map update loop is not initialized or is terminated. The planner and controller server will then have to manually call updateAndPublishMap() on each update loop.
    • If False, the thread running the map update loop will be initialized, which is the default behaviour.
    • Param can be dynamically reconfigured during runtime.
  • The planner server and controller server will use the getter function costmap_ros_->isUpdateOnRequest() to determine if it will need to call updateAndPublishMap().
  • Move starting and stopping of thread into separate routines mapUpdateThreadOff() and mapUpdateThreadOn() to make it easier to manage the map update loop thread.

Description of documentation updates required from your changes

  • Add update_on_request parameter to the costmap configuration documentation for navigation.ros.org

Future work that may be required in bullet points

Document the use case and implications of the update_on_request parameter.

  • Possible use-cases: Benchmarking, test cases.
  • Possible implications: As mentioned in Feature Request(galactic): RecoveryServer use it's own costmap for collision check  #3091 .Missing out on sensor information if the planner or controller update frequency is low enough to result in long intervals between requests for costmap updates. Or if the planner/controller update frequency is too high, the costmap update rate will be unnecessarily high, and take up extra CPU resources.

For Maintainers:

  • Check that any new parameters added are updated in navigation.ros.org
  • Check that any significant change is added to the migration guide
  • Check that any new features OR changes to existing behaviors are reflected in the tuning guide
  • Check that any new functions have Doxygen added
  • Check that any new features have test coverage
  • Check that any new plugins is added to the plugins page
  • If BT Node, Additionally: add to BT's XML index of nodes for groot, BT package's readme table, and BT library lists

Instructions for testing

# Launch the turtlebot simulation world 
ros2 launch nav2_bringup tb3_simulation_launch.py

# Modify parameters during run-time
ros2 param set /local_costmap/local_costmap update_on_request <BOOLEAN_VAL>

@mergify
Copy link
Contributor

mergify bot commented Nov 26, 2022

@JohnTGZ, please properly fill in PR template in the future. @SteveMacenski, use this instead.

  • Check that any new parameters added are updated in navigation.ros.org
  • Check that any significant change is added to the migration guide
  • Check that any new features OR changes to existing behaviors are reflected in the tuning guide
  • Check that any new functions have Doxygen added
  • Check that any new features have test coverage
  • Check that any new plugins is added to the plugins page
  • If BT Node, Additionally: add to BT's XML index of nodes for groot, BT package's readme table, and BT library lists

@JohnTGZ
Copy link
Author

JohnTGZ commented Nov 26, 2022

@AlexeyMerzlyakov I just realised that if we are to combine the updating and publishing of map into the updateAndPublishMap() method, then if update_on_request is TRUE, the publish_frequency will not matter.
If it is important for the costmap server to still publish with a fixed frequency, perhaps we can break the publishing out into another thread, what do you think?

@JohnTGZ
Copy link
Author

JohnTGZ commented Nov 26, 2022

please properly fill in PR template in the future

I wonder what I might have missed out here?

Copy link
Collaborator

@AlexeyMerzlyakov AlexeyMerzlyakov left a comment

Choose a reason for hiding this comment

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

Okay, good. Please check the review items for this PR and next step required - is to add a new testcase on this feature.
I think, this should be:

  • New unit test for the update-on-request feature. From brief view, it could be a Costmap2DROS class object with prepared layered_costmap_ inside tuned on one plugin, e.g. on static_layer. Static layer could listen the incoming OccupancyGrid map published by simple OccupandyGrid publisher (also added inside the test). You could publish some occupancy grid once, then run Costmap2DROS until the costmap is not being published (and be equal to initial OccupancyGrid). Then update the incoming map, manually trigger updateAndPublishMap() and check that costmap was updated correctly. If necessary, the test might contain Costmap2DROS class wrapper, inherited from base class that is you will have an access to all protected parent class fields. Also, I think, we need to check that if update_on_request is set to false, costmap is being updated automatically.
    But in overall, it could be something more simpler, if you could find better way and propose instead.
  • Supporting "update_on_request" parameter in dynamic params test test/integration/dyn_params_tests.cpp

Copy link
Member

@SteveMacenski SteveMacenski left a comment

Choose a reason for hiding this comment

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

Generally speaking, this looks good to me, but heed @AlexeyMerzlyakov's comments. I'll take a look once he fully approves, but all looks good to me from a first glance.

You do have some linting issues, which CI should point out to you (or ament_cpplint / ament_uncrustify)

I just realised that if we are to combine the updating and publishing of map into the updateAndPublishMap() method, then if update_on_request is TRUE, the publish_frequency will not matter.

Is that true? Its still being used in the updateAndPublishMap() function to only publish after the duration is expired from the last publication. Isn't the publish_cycle_ still being respected? It may be not queried at the regular looping rate anymore, but should at least enforce approximate publication -- or at least not any more frequently than that.

If it is important for the costmap server to still publish with a fixed frequency, perhaps we can break the publishing out into another thread

I don't think its very important to be exactly at a fixed rate, its more about throttling than it is regularity. If you have updates to the local costmap at 20hz (which is not terribly uncommon), publishing that at 20hz is very expensive just for visualization. 5hz or something is very reasonable, instead.

I wonder what I might have missed out here?

Nothing, the macro is broken and I haven't had a time to fix it yet. Mergify keeps working for a few weeks, then breaks 🙄

On unit test: you could simply create a costmap with this parameter on with a static layer. Send it a random static map (full obstacle, a smiley face, whatever). Check that the costmap is still empty. Then trigger that update method and check that there are new values -- but on that update, not due to a regular background thread. I think that's also what Alexey described, more or less.

@mergify
Copy link
Contributor

mergify bot commented Dec 6, 2022

This pull request is in conflict. Could you fix it @JohnTGZ?

@SteveMacenski
Copy link
Member

@AlexeyMerzlyakov please rereview

@JohnTGZ
Copy link
Author

JohnTGZ commented Dec 7, 2022

Hi all, sorry that I have not had time to add the test yet, I would be able to do it by friday

Copy link
Collaborator

@AlexeyMerzlyakov AlexeyMerzlyakov left a comment

Choose a reason for hiding this comment

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

A few strokes remained:

  1. Please correct the logic of updateAndPublish() call in Costmap2DROS::on_activate()
  2. Rebase onto latest main branch and correct the patch accordingly

And unit test is missing for this feature. You can take nav2_costmap_2d/test/regression/plugin_api_order.cpp as a basis: this testcase creates a new Costmap2DROS object and sets a plugin (in your case it will be static_layer) for it, then executes.

}

void
Costmap2DROS::updateAndPublishMap()
Copy link
Collaborator

@AlexeyMerzlyakov AlexeyMerzlyakov Dec 7, 2022

Choose a reason for hiding this comment

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

Please rebase onto to the latest main branch: there is a new indicator variable and behavior used to verificate that Costmap2DROS was started. It ought to be added to updateAndPublishMap(), since this API logic should be called only of Costmap2DROS was already started:

void
Costmap2DROS::updateAndPublishMap()
{
  if (stopped_) {
    // Do not execute if start() did not complete plugins activation
    return;
  }

  nav2_util::ExecutionTimer timer;

  // Measure the execution time of the updateMap method
  timer.start();
  updateMap();
  timer.end();

  ...
}

JohnTGZ and others added 3 commits December 15, 2022 22:12
… least once when update_on_Request parameter is true

Signed-off-by: johntgz <johntan@openrobotics.org>
Signed-off-by: johntgz <johntan@openrobotics.org>
@JohnTGZ
Copy link
Author

JohnTGZ commented Dec 17, 2022

@AlexeyMerzlyakov Thank you for the guidance and suggestions, I have made the suggested changes and added a unit test as well, please take a look when you are free :)
Also, I think I made mistake by merging with main, instead of rebasing it, let me know if you need me to rectify this.


void waitForMap()
{
while (!this->isCurrent()) {
Copy link
Author

Choose a reason for hiding this comment

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

One thing that I can't quite figure out is if we still need to check if the layered costmap is current? From the static layer plugin, it is set to true after receiving a new map. I can remove this check if that is the case and just do a normal spin.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not think this is a problem: another integration tests are also uses the same approach. As indicator current_ is also OK: this means that we could use the map (for static layer in this case - it was received).

Comment on lines +537 to +539
// Execute after start() will complete plugins activation
if (!stopped_) {
// Measure the execution time of the updateMap method
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since it have already begun to do, why not optimize some: instead of do all this stuff inside if-statement, it is better to add something like mentioned in previous comment item:

 if (stopped_) {
    // Do not execute if start() did not complete plugins activation
    return;
  }

  nav2_util::ExecutionTimer timer;

  // Measure the execution time of the updateMap method
  timer.start();
  updateMap();
  timer.end();
  ...

Which is more readable instead of having one more indent in the code. What do you think?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, this is definitely more readable, I will make the changes.

@@ -0,0 +1,257 @@
// Copyright (c) 2022 Samsung R&D Institute Russia
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please, correct the copyright message to one related to you.

Comment on lines +21 to +25
#include <rclcpp/rclcpp.hpp>
#include <nav_msgs/msg/occupancy_grid.hpp>
#include <nav2_costmap_2d/cost_values.hpp>
#include <nav2_costmap_2d/costmap_2d_ros.hpp>
#include <nav2_util/occ_grid_values.hpp>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use " to address these headers as relative to the ROS/project rather than system ones


void waitForMap()
{
while (!this->isCurrent()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I do not think this is a problem: another integration tests are also uses the same approach. As indicator current_ is also OK: this means that we could use the map (for static layer in this case - it was received).


// Spin to check that background thread does not update the costmap
waitSome(200ms);
// rclcpp::spin_some(costmap_ros_wrapper_->get_node_base_interface());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please, remove unnecessary commented line

publishOccupancyGrid();

// Spin until the background thread updates the map
waitSome(200ms);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since there is the waitForMap() call on next line, do we really need this wait?

Copy link
Author

@JohnTGZ JohnTGZ Dec 22, 2022

Choose a reason for hiding this comment

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

The issue is that without waiting for the costmap update thread to spin for at least 1/map_update_frequency, the map will not be updated by the costmap node.

void
Costmap2DROS::mapUpdateLoop(double frequency)
{
  // --snip--
  
  rclcpp::WallRate r(frequency);    // 200ms by default

  while (rclcpp::ok() && !map_update_thread_shutdown_) {
    updateAndPublishMap();
    // Make sure to sleep for the remainder of our cycle time
    r.sleep();
  
  // --snip--
}

@AlexeyMerzlyakov
Copy link
Collaborator

Also, I think I made mistake by merging with main, instead of rebasing it, let me know if you need me to rectify this.

I am not sure, how GitHub will handle correctly non-linear merges. So, it is better to force-push your branch made over latest main.

If I did this, I would like to rename my_branch to something else, like my_branch_old. Then check out to the main, update it to the latest vanilla state, then create my_branch again (it actually will be a new branch), and apply all your changes (e.g. using git cherry-pick or manually making the patch) from my_branch_old to my_branch. Force-push should lead your local GitHub repository to "forget" about old non-linear history.

You also could verify the results by clonning https://github.com/JohnTGZ/navigation2.git in a /tmp directory after it, and then using git lg1 (you could search this alias through the Internet). Hope this will help

@JohnTGZ JohnTGZ closed this Dec 22, 2022
@JohnTGZ JohnTGZ deleted the update-costmap-on-request branch December 22, 2022 05:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants