diff --git a/articles/150_ros_command_line_arguments.md b/articles/150_ros_command_line_arguments.md new file mode 100644 index 000000000..dc6a5dcda --- /dev/null +++ b/articles/150_ros_command_line_arguments.md @@ -0,0 +1,237 @@ +--- +layout: default +title: ROS Command Line Arguments +permalink: articles/ros_command_line_arguments.html +abstract: + This article describes ROS 2 nodes command line arguments and their syntax. +author: '[Michel Hidalgo](https://github.com/hidmic)' +published: true +--- + +- This will become a table of contents (this text will be scraped). +{:toc} + +# {{ page.title }} + +
+{{ page.abstract }} +
+ +Original Author: {{ page.author }} + +## Overview + +As it was the case in ROS 1, ROS 2 nodes allow configuration via command line arguments to a certain degree. +In ROS 2, this interface had to become more complex to cope with a larger set of configuration options, an ambiguity in remapping rules and parameter assignment syntax (as a result of the leading underscore name convention for hidden resources), a one-to-many relationship between executables and nodes, to name a few. + +Because of this, increasingly precise addressing mechanisms as well as leading double underscores (`__`) in some positional arguments, both natural extensions of existing ROS 1 command line features, are combined with ROS 2 specific command line flags. +Flags, in contrast with other custom syntax alternatives, are: + +- Widely known and used. +- Explicit and less error prone. +- Suitable for tab completion. + +Unfortunately, since these flags coexist with user-defined ones, additional guarding and extraction devices must be put in place -- one of the reasons why these were avoided entirely in ROS 1 command lines. + +## Features + +### Namespacing + +To prevent ROS specific command line flags from colliding with user-defined ones, the former are scoped using the `--ros-args` flag and a trailing double dash token (`--`): + +```sh +ros2 run some_package some_node [...] \ + --ros-args [...] -- [...] +``` + +Note that `--ros-args --` i.e. an empty set is a valid invocation. + +If no user defined arguments are provided after ROS specific arguments are, the double dash token (`--`) may be elided: + +```sh +ros2 run some_package some_node [...] --ros-args [...] +``` + +Note that a sole trailing `--ros-args` remains a valid invocation. + +More than one set of ROS specific flags may appear in the same command line: + +```sh +ros2 run some_package some_node --ros-args [...] -- \ + [...] --ros-args [...] +``` + +This way, multiple sources, potentially unaware of each other, can append flags to the command line with no regard for previous sets. + +### Capabilities + +#### Summary + +As a quick summary of ROS command line capabilities: + +- For name remapping, use either `--remap from:=to` or `-r from:=to`. +- For single parameter assignment, use either `--param name:=value` or `-p name:=value` where value is in YAML format. +- For multiple parameter assignments, use `--params-file path/to/file.yaml` and a parameters YAML file. +- For setting logging (minimum) level, use `--log-level LEVEL_NAME`. +- For external logging configuration, use `--log-config-file path/to/file.config` and a log configuration file. +- For enabling/disabling logging: + - to `rosout`, use `--enable-rosout-logs` or `--disable-rosout-logs` + - to `stdout`, use `--enable-stdout-logs` or `--disable-stdout-logs` + - to a external logging library, use `--enable-external-lib-logs` or `--disable-external-lib-logs` + +For name remapping and parameter assignment, specific nodes can be targeted by prepending the option value with the node name followed by a colon `:`, as in `--remap my_node:from:=to` and `--param my_node:name:=value`. + +#### Name remapping rules + +Remapping rules may be introduced using the `--remap`/`-r` option. +This option takes a single `from:=to` remapping rule. + +As an example, to remap from `foo` to `bar` for `some_ros_executable`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args --remap foo:=bar +``` + +or its shorter equivalent: + +```sh +ros2 run some_package some_ros_executable --ros-args -r foo:=bar +``` + +As is, this remapping rule applies to each and every node that `some_ros_executable` spawns unless explicitly ignored in code. +To limit it to `some_node`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args -r some_node:foo:=bar +``` + +#### Single parameter assignments + +Parameter assignment may be achieved using the `--param`/`-p` option. +This option takes a single `name:=value` assignment statement, where `value` is in [YAML format](https://yaml.org/spec/) and thus YAML type inference rules apply. + +As an example, to assign a string value `test` to a parameter `string_param` for `some_ros_executable`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args --param string_param:=test +``` + +or its shorter equivalent: + +```sh +ros2 run some_package some_ros_executable --ros-args -p string_param:=test +``` + +As is, this parameter assignment applies to each and every node that `some_ros_executable` spawns unless explicitly ignored in code. +To limit it to `some_node`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args -p some_node:string_param:=test +``` + +#### Multiple parameter assignments + +Multiple parameter assignments can be performed at once using the `--params-file` option. +This option takes a [YAML](https://yaml.org/spec/) file with the following structure: + +```yaml +node0_name: + ros__parameters: + param0_name: param0_value + ... + paramN_name: paramN_value +... +nodeM_name: + ros_parameters: + ... +``` + +Multiple nodes in a single executable can be targeted this way. +Note that YAML type inference rules for parameter values apply. + +As an example, to assign a string value `foo` to a parameter `string_param` for `some_node` and a string value `bar` to that same parameter `string_param` but for `another_node` upon running `some_ros_executable` that contains both, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args --params-file params_file.yaml +``` + +where `params_file.yaml` reads: + +```yaml +some_node: + ros__parameters: + string_param: foo +another_node: + ros_parameters: + string_param: bar +``` + +#### Logging level + +Minimum logging level can be externally set using the `--log-level` option. + +As an example, to set logging level to `DEBUG` for `some_ros_executable`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args --log-level DEBUG +``` + +See `rcutils` and `rcl` logging documentation for reference on existing logging levels. + +#### External logging configuration + +External logging may be configured using the `--log-config-file` option. +This option takes a single configuration file, whose format depends on the actual external logging library being used. + +As an example, to pass `some_log.config` configuration file to `some_ros_executable`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args --log-config-file some_log.config +``` + +#### Enabling and disabling logging + +Logging to `rosout`, `stdout` and an external logging library can be independently enabled or disabled. + +As an example, to disable logging to `rosout` and `stdout` but not to an external logging library for `some_ros_executable`, one may execute: + +```sh +ros2 run some_package some_ros_executable --ros-args --disable-rosout-logs --disable-stdout-logs --enable-external-lib-logs +``` + +Logging is fully enabled by default, thus `--enable-*` options are usually redundant unless a `--disable-*` option found earlier in the command line is being overridden. + +## Implementation + +### Extraction + +Command line argument extraction happens within `rcl`. +When an instance of the `--ros-args` flag is found in `argv`, until either a double dash token (`--`) is found or the end of the argument array is reached, all arguments that follow are taken as ROS specific arguments to be parsed as such. +Remaining arguments can still be accessed by the user via `rcl` API. + +### Parsing + +At the time of writing, most ROS specific arguments target and are thus parsed by `rcl`. +This is the case for name remapping rules or parameter assignments flags, to name a few. +However, to support ROS specific arguments that target upper ROS layers e.g. a ROS client library like `rclcpp`, arguments unknown to `rcl` are left unparsed but accessible by these layers, which in turn can continue parsing or eventually warn the user if unknown arguments remain. + +## Alternative designs + +Other, alternative designs were under discussion. + +### Additional operators + +Stop using the same `:=` operator for parameter assignments and name remapping rules and introduce additional operators e.g. `:=` for parameter assignment and `~=` for name remapping. +This keeps the command line verbosity at a minimum and avoids the need for flags, but is error prone. + +### Full name addressing + +Rely on full name addressing to disambiguate operator significance e.g. `rosparam://this:=that` would result in a `that` string value being assigned to parameter `this` while `rosremap://this:=that` would result in name `this` being remapped to name `that`. +Other URL schemes, specific to each interface type e.g. `rostopic` and `rosservice`, may also be used to further scope remapping rules. +This signficantly increases command line verbosity, but still avoids the need for flags. + +### Prefixed option names + +Remove the need for double dash tokens (`--`), conventionally used to signify the end of CLI options for a command, by adding the `--ros-` prefix to all ROS specific command line flags e.g. `--ros-remap`, `--ros-param`, etc. +In exchange, it makes argument extraction slightly more difficult as all options must be known ahead of time, whereas `--ros-args`-based namespacing can achieve the same with a couple rules. +It also increases command line verbosity.