Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Papyrus/customizations/robotics/ros2"

(Mapping to programming languages)
(Mapping to programming languages)
Line 164: Line 164:
  
 
* CMakeLists.txt/package.xml build and package information
 
* CMakeLists.txt/package.xml build and package information
 +
 +
* launch: folder with launch files for each component and the assembly.
  
 
* src-gen/<package name>/<component name>.cpp|h class with declaration of the lifecycle node, eventual parameters and lifecycle actions. If the component contains a timer, it is started after the configuration of the component.
 
* src-gen/<package name>/<component name>.cpp|h class with declaration of the lifecycle node, eventual parameters and lifecycle actions. If the component contains a timer, it is started after the configuration of the component.
Line 174: Line 176:
  
 
You can find several examples for C++ (File->New example) that include simple examples with two components: a publisher/subscriber pair, the sender/receiver pair and a client-server pair. In addition, the dummy_robot example from the ROS 2 tutorial is available.
 
You can find several examples for C++ (File->New example) that include simple examples with two components: a publisher/subscriber pair, the sender/receiver pair and a client-server pair. In addition, the dummy_robot example from the ROS 2 tutorial is available.
The examples contain pre-filled implementation files.
+
The examples contain pre-filled implementation files. In the sequel, we show some of these files.
 +
 
 +
<source lang="C++">
 +
void PeriodicPublisher_impl::fPublishing() {
 +
  simple_msgs::msg::Map map;
 +
  map.ogm.width = 16;
 +
  map.ogm.height = 16;
 +
  map.ogm.resolution = 0.1;
 +
 
 +
  RCLCPP_INFO(get_logger(), "now publishing");
 +
 
 +
  pMap_pub_->publish(map);
 +
}
 +
</source>
 +
 
 +
 
 +
The constructor of the generated publisher component declares the attributes, calls the superclass (lifecycle-node) and creates the publisher itself. It also creates a callback group for the timer and the activity.
 +
 
 +
<source lang="C++">
 +
PeriodicPublisher::PeriodicPublisher(rclcpp::NodeOptions /*in*/options) :
 +
    rclcpp_lifecycle::LifecycleNode("PeriodicPublisher", options) {
 +
  cbg_Activity1_ = create_callback_group(
 +
      rclcpp::CallbackGroupType::MutuallyExclusive);
 +
  cbg_t_Activity1_ = create_callback_group(
 +
      rclcpp::CallbackGroupType::MutuallyExclusive);
 +
 
 +
  rclcpp::PublisherOptions pMap_options;
 +
  pMap_options.callback_group = cbg_Activity1_;
 +
  pMap_pub_ = create_publisher < simple_msgs::msg::Map
 +
      > ("pMap", 1, pMap_options);
 +
  // directly activate a publisher
 +
  pMap_pub_->on_activate();
 +
 
 +
}
 +
</source>
 +
 
 +
The corresponding counterpart for message reception (subscriber) contains on the one hand the user code:
 +
 
 +
<source lang="C++">
 +
void Subscriber_impl::fListening(const simple_msgs::msg::Map::SharedPtr /*in*/commobj) {
 +
  cout << "got map data" << endl;
 +
  cout << "width: " << commobj->ogm.width << endl;
 +
  cout << "height: " << commobj->ogm.height << endl;
 +
  cout << "resolution: " << commobj->ogm.resolution << endl;
 +
}
 +
</source>
 +
 
 +
 
 +
as well as the generated constructor and main (not shown).
 +
 
 +
<source lang="C++">
 +
Subscriber::Subscriber(rclcpp::NodeOptions /*in*/options) :
 +
    rclcpp_lifecycle::LifecycleNode("Subscriber", options) {
 +
  cbg_HandleMsg_ = create_callback_group(
 +
      rclcpp::CallbackGroupType::MutuallyExclusive);
 +
 
 +
  rclcpp::SubscriptionOptions rMap_options;
 +
  rMap_options.callback_group = cbg_HandleMsg_;
 +
  rMap_sub_ = create_subscription < simple_msgs::msg::Map
 +
      > ("rMap", rclcpp::QoS(rclcpp::KeepLast(100)).best_effort(), std::bind(
 +
          &subscriberCompdef::Subscriber_impl::fListening,
 +
          (Subscriber_impl*) this, std::placeholders::_1), rMap_options);
 +
 
 +
}
 +
</source>
  
  
Line 207: Line 273:
 
The constructor of the generated publisher component declares the attributes, calls the superclass (lifecycle-node) and creates the publisher itself.  
 
The constructor of the generated publisher component declares the attributes, calls the superclass (lifecycle-node) and creates the publisher itself.  
  
def __init__(self, options):
+
<source lang="python">
self.pMap_pub_: Publisher = None
+
  def __init__(self, options):
self.indoor_: bool = False
+
    self.pMap_pub_: Publisher = None
self.noOfScans_: int = 0
+
    self.indoor_: bool = False
super().__init__('PeriodicPublisher')
+
    self.noOfScans_: int = 0
self.pMap_pub_ = self.create_publisher(Map, "pMap", 1)
+
    super().__init__('PeriodicPublisher')
 +
    self.pMap_pub_ = self.create_publisher(Map, "pMap", 1)
 +
</source>
  
  

Revision as of 04:56, 8 March 2023

ROS 2

Acknowledgements

The work on Papyrus for Robotics in general is supported by the RobMoSys project. The ROS 2 support is supported by the project ROSIN FTP (focused-technical project) ROS MDD

Papyrus-customizations-robotics-rosin ack logo wide.png

Supported by ROSIN - ROS-Industrial Quality-Assured Robot Software Components. More information: rosin-project.eu

Papyrus-customizations-robotics-rosin eu flag.jpg This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement No. 732287.

Mapping RobMoSys concepts to ROS 2

Communication patterns

One of the primary issues in mapping RobMoSys concepts to ROS 2 is how to map RobMoSys interaction patterns to ROS 2 . In the sequel, we outline this for each [communication pattern] in RobMoSys.

QUERY pattern

The QUERY pattern is a classical client/server pattern with one server and n clients. The clients execute a request and expect a response. There are two ways, how a client can handle responses: either with a blocking wait or by receiving a callback with the response (this option primarily affects the activity definition within a component). Both options are supported by ROS 2 services. On the server side, a service request is executed within a handler. In case of ROS 2, requests are always executed by a ROS 2 multi-threaded executor that calls the request handler.

Papyrus-customizations-robotics-commpatternQueryAsync.png
Asynchronous variant of the query pattern

Papyrus-customizations-robotics-commpatternQuerySync.png
Synchronous variant of the query pattern

SEND pattern

The SEND pattern in RobMoSys is an asynchronous service request without expecting a response. One or more (n) clients can send a communication object to a server. This is mapped to ROS 2 publish/subscribe with messages, as these support n:m relation between publishers and subscribers.

Papyrus-customizations-robotics-commpatternSend.png
Asynchronous variant of the query pattern

PUSH pattern

The PUSH pattern in RobMoSys is an asynchronous message publication with one publisher and n subscribers. This is directly mapped to ROS 2 publish/subscribe with messages. A publisher can directly push messages without queuing to subscribed consumers. Compared to the Send pattern, the role of client and servers are inverted: The Publisher is the server (port providing a service), the subscriber is a client (port requiring a service).

Papyrus-customizations-robotics-commpatternPush.png
Asynchronous variant of the query pattern

EVENT pattern

The EVENT patterns enables asynchronous, filtered event handling. It is currently not supported by the ROS 2 mapping.

Component lifecycle

As RobMoSys component have a lifecycle (see RobMoSys wiki, a RobMoSys component is mapped to a ROS 2 lifecycle node. By default, a component cycles from an initial state to a configured and then to an activate state. Periodic tasks are started when the component enters the "active" state.

ROS 2 code generation

Message package generation

A RobMoSys ServiceDefinition model is mapped a set of ROS 2 packages containing messages and services. As for the other code generation artifacts, a message package will be generated in its own Eclipse CDT project. This CDT project is configured to use colcon as build command, options can be configured in the preferences (type "ROS 2" in the filter to see the associated preference page).

It is only necessary to create a new ROS 2 message/service package, if that does not already exist. Service generation is not invoked directly from a service definition model, but will be invoked automatically during component generation, if that component references non standard messages or services. The generator uses a preference dialog below (see below) in order to detemine what a standard message is.

Papyrus-customizations-robotics-ros2prefs.png
ROS 2 preference dialog

The following text-box outlines the structure of the generated artefacts for a message package:

 folder (corresponds to a message package, i.e. a sub-packages within a RobMoSys service definition module)
   CMakeLists.txt - Makefile
   package.xml - Meta-data/build info
   msg - folder with message definitions
   srv - folder with service definitions

Component code generation

Papyrus for robotics supports the generation of (C++) code for ROS 2. The code generation can be triggered via the "Robotics" context menu when a component definition or a system (component assembly) is selected.

Let's look at an example, the dummy_joint_states component that is part of the standard ROS 2 demos. The following figure shows the RobMoSys view of this component. As the source code creates a publisher with the topic "joint_states" and the message type "JointState", the RobMoSys model of this components contains a provided PUSH port with the respective name and type.

Papyrus-customizations-robotics-dummy joint states.compdef.png
dummy_joint_states component, modeled with Papyrus for Robotics

The original source code performs an initialization of a message and then enters a loop. In this loop, it modifies an entry of the message and performs a wait. The mapping to RobMoSys implies splitting this code into two parts, the initialization of the message and the periodic publication of the modified message. The result is an Activity with two functions that take over the respective part. The period length is modeled via a periodic timer.

Compared to the monolithic source code that contained the creation of a publisher port, the initialization of a message and then a periodic loop with a hard-coded period, the corresponding model separates these aspects into a modeled port and two reusable (composable) functions.

If a component is selected, a ROS 2 package can be generated for this component, as shown in the following dialog.

Papyrus-customizations-robotics-menu-gencode.png
Context menu for generating code

Currently, each RobMoSys component is mapped to a dedicated ROS 2 package containing the code for a node. The generated package contains generated build files (package.xml and CMakeLists.txt), as well as a src-gen folder with the C++ code that instantiates a ROS 2 node when executed. On the RobMoSys level, the behavior of a component is described by activities which in turn are broken down into functions. The code of these functions can be embedded into the model and will be copied into the generated code.

The ROS 2 demos contain examples that establish the connection between two nodes by using the same topic name. While this is fine for an example, a component must not "know" to which component it will be connected in a system. This assures its re-usability on source and binary level. The code generator uses the port names as topics. Topic re-mapping (see below) enables the connection on the system level.

The following text-box outlines the structure of the generated artefacts for a component definition:

 folder (corresponds to RobMoSys component-definition-module)
   src-gen - generated source and include files
   CMakeLists.txt - Makefile
   package.xml - Meta-data/build info

System code generation

If a system is selected, code is generated for all components within the system. In addition, a python launch script that brings-up the robotic system is generated. It references a yaml file that contains port/topic remappings and references a yaml file taking into account parameter values that override the default ones for the component. The topic remappings establish the connection between the component instances (see topics above).

Papyrus-customizations-robotics-dummy robot.system.png
dummy_robot system, modeled with Papyrus for Robotics

The script below shows the generated launch file for the dummy_robot system

 def generate_launch_description():
   ld = launch.LaunchDescription()
   ld.add_entity(LifecycleNode(
     node_name='ComponentInstance1',
     package='dummy_joint_statescompdef', node_executable='Dummy_joint_states',
     remappings=[
       ('joint_states', 'ComponentInstance1/JointState/joint_states')
     ],
     parameters=['cfg/param.yaml']
   ))
   ... idem for Dummy_laser, Dummy_map_server, Robot_state_publisher and rviz

The following text-box outlines the structure of the generated artefacts for a component definition. The folder contains a launch.py file that starts all component-instances of a system, as well as an additional file for each instance that starts only the specific instance. In addition, there is an activate.py file that launches the component(s) and in the sequel configures and activates them.

 folder (this corresponds RobMoSys system-component-architecture)
   launch.py
   activate.py
   for each instance: launch.<instance-name>.py
   for each instance: activate.<instance-name>.py
   cfg - subfolder for parameters
      params.yaml - generated parameter file containing the (non-default) parameter values defined at system-level

ROS 2 test models

The following page describes shipped test models, in particular how to generate code, compile and run these models: ROS 2 test models for code generation

Reverse engineering

see ROS 2 reverse

Mapping to programming languages

ROS 2 has a mapping to C++ and Python, supported via ROS client libraries (RCLs) for C++ and Python. The code generator supports these two libraries and generates either code for C++ or Python. The location of the files is slightly different for the two languages and described in the sequel

Code generation for C++

Code generation for C++ handles parameter declaration, initialization and life-cycle events. The following files are generated, either in a src or src-gen folder:

  • CMakeLists.txt/package.xml build and package information
  • launch: folder with launch files for each component and the assembly.
  • src-gen/<package name>/<component name>.cpp|h class with declaration of the lifecycle node, eventual parameters and lifecycle actions. If the component contains a timer, it is started after the configuration of the component.
  • src-gen/<package name>/<component name>_main.cpp|h startup code (main). It registers the component with a multi-threaded executor and a callback group.
  • src-skel/<package name>/<component name>_impl.cpp|h A skeleton of an implementation (if at least one function is not specified in a code fragment within the model). It serves as a blueprint for code that is enriched with behaviors after generation by the developer. This file is not included in the CMakeLists.txt, i.e. it is ignored during the build. It is overwritten in each code generation pass.
  • src/<package name>/<component name>_impl The implementation file that contains added code from the developer, if needed. The file is only generated, if it does not exist yet to avoid overwriting existing code. If the model changes, the changes are reflected in the _impl file in the src-skel folder above and can be manually merged.

You can find several examples for C++ (File->New example) that include simple examples with two components: a publisher/subscriber pair, the sender/receiver pair and a client-server pair. In addition, the dummy_robot example from the ROS 2 tutorial is available. The examples contain pre-filled implementation files. In the sequel, we show some of these files.

Invalid language.

You need to specify a language like this: <source lang="html4strict">...</source>

Supported languages for syntax highlighting:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, otj, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


void PeriodicPublisher_impl::fPublishing() {
  simple_msgs::msg::Map map;
  map.ogm.width = 16;
  map.ogm.height = 16;
  map.ogm.resolution = 0.1;

  RCLCPP_INFO(get_logger(), "now publishing");

  pMap_pub_->publish(map);
}


The constructor of the generated publisher component declares the attributes, calls the superclass (lifecycle-node) and creates the publisher itself. It also creates a callback group for the timer and the activity.

Invalid language.

You need to specify a language like this: <source lang="html4strict">...</source>

Supported languages for syntax highlighting:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, otj, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


PeriodicPublisher::PeriodicPublisher(rclcpp::NodeOptions /*in*/options) :
    rclcpp_lifecycle::LifecycleNode("PeriodicPublisher", options) {
  cbg_Activity1_ = create_callback_group(
      rclcpp::CallbackGroupType::MutuallyExclusive);
  cbg_t_Activity1_ = create_callback_group(
      rclcpp::CallbackGroupType::MutuallyExclusive);

  rclcpp::PublisherOptions pMap_options;
  pMap_options.callback_group = cbg_Activity1_;
  pMap_pub_ = create_publisher < simple_msgs::msg::Map
      > ("pMap", 1, pMap_options);
  // directly activate a publisher
  pMap_pub_->on_activate();

}

The corresponding counterpart for message reception (subscriber) contains on the one hand the user code:

Invalid language.

You need to specify a language like this: <source lang="html4strict">...</source>

Supported languages for syntax highlighting:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, otj, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


void Subscriber_impl::fListening(const simple_msgs::msg::Map::SharedPtr /*in*/commobj) {
  cout << "got map data" << endl;
  cout << "width: " << commobj->ogm.width << endl;
  cout << "height: " << commobj->ogm.height << endl;
  cout << "resolution: " << commobj->ogm.resolution << endl;
}


as well as the generated constructor and main (not shown).

Invalid language.

You need to specify a language like this: <source lang="html4strict">...</source>

Supported languages for syntax highlighting:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, otj, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


Subscriber::Subscriber(rclcpp::NodeOptions /*in*/options) :
    rclcpp_lifecycle::LifecycleNode("Subscriber", options) {
  cbg_HandleMsg_ = create_callback_group(
      rclcpp::CallbackGroupType::MutuallyExclusive);

  rclcpp::SubscriptionOptions rMap_options;
  rMap_options.callback_group = cbg_HandleMsg_;
  rMap_sub_ = create_subscription < simple_msgs::msg::Map
      > ("rMap", rclcpp::QoS(rclcpp::KeepLast(100)).best_effort(), std::bind(
          &subscriberCompdef::Subscriber_impl::fListening,
          (Subscriber_impl*) this, std::placeholders::_1), rMap_options);

}


Code generation for Python

Code generation is also available for Python. As for C++, it handles parameter declaration, initialization and life-cycle events. Three files are generated

  • <component name> class with declaration of the lifecycle node, eventual parameters and lifecycle actions. If the component contains a timer, it is started after the configuration of the component.
  • <component name>_main.py startup node. It registers the component with a multi-threaded executor.
  • <component name>_impl_skel A skeleton of an implementation (if at least one function is not specified in a code fragment within the model, i.e. must be added later by the developer)
  • <component name>_impl The implementation file. If refinement from the developer is needed, the file is only generated, if it does not exist yet to avoid overwriting existing code. If the model changes, the changes are reflected in the _impl_skel above and can be manually merged.

You can find an example (File->New example) that consists of the simple publisher/subscriber pair for Python. This example contains a pre-filled implementation for publishing

  def fPublishing(self):
    transMap = Map()
    transMap.ogm.width = 16
    transMap.ogm.height = 16
    transMap.ogm.resolution = 0.1
 
    self.get_logger().info('Publishing map...')
    self.pMap_pub_.publish(transMap)


The constructor of the generated publisher component declares the attributes, calls the superclass (lifecycle-node) and creates the publisher itself.

  def __init__(self, options):
    self.pMap_pub_: Publisher = None
    self.indoor_: bool = False
    self.noOfScans_: int = 0
    super().__init__('PeriodicPublisher')
    self.pMap_pub_ = self.create_publisher(Map, "pMap", 1)


The corresponding counterpart for message reception (subscriber) contains on the one hand the user code:

  def fListening(self, commobj):
    print("Map data received")
    print("width: " + str(commobj.ogm.width))
    print("height: " + str(commobj.ogm.height))
    print("resolution: " + str(commobj.ogm.resolution))


as well as the generated constructor and main (not shown).

  def __init__(self, options):
    self.rMap_sub_: Subscription = None
    super().__init__('Subscriber')
    defaultQoSProfile = rclpy.qos.QoSProfile(
        reliability=ReliabilityPolicy.BEST_EFFORT,
        history=HistoryPolicy.KEEP_LAST, depth = 100)
    self.rMap_sub_ = self.create_subscription(Map, "rMap", self.fListening, qos_profile = defaultQoSProfile)


A difference compared to C++ is that the code generation and editing is not integrated into a specific Python environment for Eclipse. The colcon build can be invoked either from the command line or from Eclipse (code generation adds a build specification). The output of the build process is available in the Eclipse console (select a console with the name ROS 2).


Once built, Python components can be started (or lifecycle changes applied) in the same way as C++ components.

Back to the top