-
Notifications
You must be signed in to change notification settings - Fork 874
Description
Feature request
This ticket is meant to track the implementation of static remapping.
Feature description
Static remapping is the ability to remap one name (topic, service, parameter) to another. See the design doc for more info.
Implementation considerations
ROS 2 implementation proposal (work in progress, Feedback is welcome)
TODO
- PR to ros2/design to update grammar to support URL scheme
Unresolved questions:
- Can parameters be remapped? If so, what is needed to be compatible with the parameter design? Parameters use
.as a separator?
Resolved questions:
Does dynamic composition by aroslaunchequivalent need a way to pass rules to RCL afterrcl_init(...)has been called?- Yes
- A tool like roslaunch launching dynamically composed nodes needs to have them ignore global arguments
Topic name design doc Mentions URL form of names, but the remapping grammar does not consider them.- PR to ros2/design updating grammar to support URL form of names
Does remapping need to be able to remap a topic but not a service of the same name?- Yes
- Use URL form for this case. If url form not given then assume rule applies to both.
How should the ros1 bridge deal with remapping?- It should bypass remap rules.
Library Responsibilities
rclresponsible for command line parsing and name remappingrmwlibraries are completely ignorant of remapping- clients like
rclcppandrclpyrely onrclto do remapping transparently, but have options to bypass it for special cases (ros1 bridge)
High level rcl implementation
rcloffers an API for parsing command line argumentsrcl_init()parses command line arguments and stores the results globally- A node can be given its own arguments and told to either override or ignore the global rules completely
- Remapping is applied to names transparently in most
rcl_*functions - Names used in
graph.hfunctions are not remapped, since those functions are used for introspection of the system.
Low level rcl implementation
rcl type changes
rcl_node_options_t- Add field
bool use_global_arguments;. True means global remapping rules (parsed byrcl_init()are applied. False means only FQN expansion/substitution and node specific remap rules are applied.
- Add field
- Add
rcl_arguments_t- Output of parsing CLI arguments
- Includes a list of remap rules
New rcl functions
rcl_arguments_t * rcl_parse_arguments(int argc, char ** argv, rcl_allocator_t * alloc)- Parses arguments and returns a structure that includes a list of remap rules
char * rcl_remap_node_name(rcl_node_t * node, char * name, rcl_allocator_t * alloc)- Remaps from one node name to another (
__node:=new_nodename)
- Remaps from one node name to another (
char * rcl_remap_namespace(rcl_node_t * node, char * namespace, rcl_allocator_t * alloc)- Remaps from one namespace to another (
__ns:=/new/namespace)
- Remaps from one namespace to another (
char * rcl_remap_name(rcl_node_t * node, char * name, rcl_allocator_t * alloc)- Given an expanded name (
rcl_expand_topic_name(...)must be called first), return what it would be remapped to - node argument necessary because rules can be node specific.
- Remapping rules are tested in the order they were given on the command line, and stops at the first matching rule.
- Given an expanded name (
Behavior of existing rcl functions
rcl_init()- Parses remapping rules from command line arguments
- stores the result of parsing in a global instance of
rcl_arguments_t
rcl_node_get_default_options()- returns options with
use_global_arguments = true.
- returns options with
rcl_expand_topic_name(...)- Unchanged. This function is responsible for substitutions and FQN expansion. Remapping takes place afterwards.
rcl_node_init(...)- Implementation uses
rcl_get_remap_node_name()andrcl_get_remap_namespace()on supplied node name and namespace
- Implementation uses
rcl_client_init(...),rcl_publisher_init(...),rcl_service_init(...),rcl_subscription_init(...)- Implementation uses
rcl_remap_name(...)on the supplied service/topic name
- Implementation uses
rcl_client_get_service_name(...),rcl_publisher_get_topic_name(...),rcl_service_get_service_name(...),rcl_subscription_get_topic_name(...)- Returns the remapped name. The documentation on these functions already says as much
rcl_get_topic_names_and_types(...)- This is a graph function used for introspection. The returned names are not remapped before being returned.
rcl_count_publishers(...),rcl_count_subscribers(...)- This is a graph function used for introspection. The supplied name is not remapped internally.
Low level rclcpp implementation
rclcpp::expand_topic_or_service_name()- Logic unchanged. Add to documentation that returned name does not include any remapping.
rclcpp::Node::Node(...)andrclcpp::NodeBase::NodeBase(...)- Add another new constructor with
rclcpp::Arguments.Nodepasses this through toNodeBasewhich setsoptions.use_global_argumentsandrcl_arguments_twhen callingrcl_node_init().
- Add another new constructor with
rclcpp::NodeGraph::get_topic_names_and_types(),rclcpp::NodeGraph::get_topic_names_and_types()- Document that these are the actual names used, and attempts to create pubs/subs using these names may not work depending on whether those names match any remapping rules.
rclcpp::NodeGraph::count_publishers(...),rclcpp::NodeGraph::count_publishers(...)- Document that the topic name passed in is not automatically remapped, and users must remap them prior to passing them in if that is what they want info on.
rclcpp::NodeBase::remap_name(const std::string & name)- Remaps a name, doing expansion internally
Low level rclpy implementation
rclpy_create_node(...)- Add argument for
cli_args,use_global_arguments
- Add argument for
Node.__init__(...)- Add keyword arugments
cli_args=None,use_global_arguments=True
- Add keyword arugments
Additional Info
Ros 1 bridge handling of remapping
Currently dynamic_bridge passes all command line arguments to both ros::init and rclcpp::init. Say the bridge is supplied a command line argument /foo/bar:=/fiz/buz. There is a ros1 node with a publisher on /foo/bar, and a ros2 subscriber on /foo/bar. The bridge will attempt to create a ros1 subscriber and a ros2 publisher, but the names will be remapped so the bridge is subscribed to the ros1 topic /fiz/buz and publishing to the ros2 topic /fiz/buz.
To solve this the dynamic_bridge executable will explicitly asks for names to not be remapped
- Disables remapping of names when creating publishers, subscribers, clients and services
- ROS 1 by using
NodeHandlespecific remappings by creating a node handle with a remap rule to remap a topic to itself. This will prevent any rules passed on the command line from applying to it - ROS2 by setting
remap = falseinrcl_*_options_tto disable automatic name remapping
- ROS 1 by using
How was remapping implemented in ROS 1?
roscpp stores global remapping rules (from command line arguments or passed via ros::init()) in two global variables. These variables are written to only once; it happens during library initialization. One variable stores the remappings before expanding relative names to fully qualified names (unresolved), while the other stores the same remappings after expansion (resolved).
The unresolved remappings are only used to implement searchParam. Since it is not known what namespace the param should be found in, relative name remapping will change the relative parameter name being searched for (TODO if there is a rule from a relative name to a fully qualified name that matches the parameter being searched for, what will the master do when told to search with a fully qualified parameter)?
Remapping rules can also be passed into a ros::NodeHandle via its constructor. Any name created via this node handle is first checked against the remappings given to the constructor. If none match then the global rules are checked.
The name remappings are evaluated when:
- asked to wait for a service (global remappings only, does not consider
NodeHandlespecific remappings, TODO why) - determining a namespace of a NodeHandle during construction
- advertising a topic
- subscribing to a topic
- advertising a service
- creating a service client
- Calling a service
- getting, setting, or searching for parameters (too many places to link)
rospy also stores global remappings into two global variables with the same meaning as in roscpp. All remappings in rospy are global. It does not have an equivalent to NodeHandle. Remappings either must come from the command line, or they must be given as if they were command line arguments to the argv argument of rospy.init_node. (TODO verify name remappings being evaluated at places equivalently to roscpp)