{"id":23168269,"url":"https://github.com/asorbini/hello-perftest","last_synced_at":"2025-04-04T22:22:07.943Z","repository":{"id":146825623,"uuid":"381902434","full_name":"asorbini/hello-perftest","owner":"asorbini","description":"A simple yet not totally trivial ROS 2 example project","archived":false,"fork":false,"pushed_at":"2021-07-01T04:39:50.000Z","size":16,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-10T06:46:06.949Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asorbini.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-01T03:53:12.000Z","updated_at":"2021-07-01T04:39:52.000Z","dependencies_parsed_at":"2023-07-09T09:15:54.796Z","dependency_job_id":null,"html_url":"https://github.com/asorbini/hello-perftest","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asorbini%2Fhello-perftest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asorbini%2Fhello-perftest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asorbini%2Fhello-perftest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asorbini%2Fhello-perftest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asorbini","download_url":"https://codeload.github.com/asorbini/hello-perftest/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247256780,"owners_count":20909357,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-18T02:40:39.486Z","updated_at":"2025-04-04T22:22:07.927Z","avatar_url":"https://github.com/asorbini.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# An example ROS 2 C++ project\n\n## How to create a similar project\n\nThis section list the steps that were taken to generate this repository.\n\nThey provide an outline of the creation process which may be reproduced to\ncreate other similar ROS 2 projects.\n\n- Load ROS 2, e.g. Foxy binary installation:\n\n  ```sh\n  source /opt/ros/foxy/setup.bash\n  ```\n\n- Create a root directory for the new repository, enter it, and\n  initialize a `git` repo:\n\n  ```sh\n  mkdir hello-perftest\n  cd hello-perftest\n  git init\n  ```\n\n- Generate a new empty package which will contain our main\n  application/library. Use the `ament_cmake` build system which\n  simplifies integration with the ROS 2 package system. It is\n  also possible to use \"plain CMake\" for more advanced use cases,\n  while Python packages can use `ament_python`.\n\n  ```sh\n  ros2 pkg create --build-type ament_cmake hello_perftest\n  ```\n\n- The command will generate a empty package stub in the current  \n  directory (NOTE: if you don't already have `tree` installed:\n  `sudo apt install tree`):\n\n  ```sh\n  tree hello_perftest\n  ```\n\n  The command should produce the following output:\n  \n  ```txt\n  hello_perftest\n  ├── CMakeLists.txt\n  ├── include\n  │   └── hello_perftest\n  ├── package.xml\n  └── src\n  ```\n\n- Add a [`README.md`](README.md) file to the root of the repository, e.g.:\n\n  ```sh\n  printf -- \"# An example ROS 2 C++ Project\n\n  Getting started with ROS 2!\n  \" \u003e README.md\n  ```\n\n- Edit/add the metadata fields in [`hello_perftest/package.xml`](hello_perftest/package.xml), for example\n  by updating these fields (some of these fields may also be customized with\n  arguments to `ros2 pkg create`, see `ros2 pkg create -h`):\n\n  - `\u003cversion\u003e`\n  - `\u003cdescription\u003e`\n  - `\u003cmaintainer\u003e`.\n  - `\u003cauthor\u003e` (optional, only needed if different from `\u003cmaintainer\u003e`)\n  - `\u003clicense\u003e`\n\n- Generate an \"interfaces package\", i.e. a ROS 2 package which will only\n  contain definitions for interfaces used by our main ROS 2 package (messages,\n  services, actions). It is a ROS 2 \"best practice\" to separate interfaces in\n  separate packages to make it easier to share them across multiple projects.\n\n  ```sh\n  ros2 pkg create --build-type ament_cmake hello_perftest_interfaces\n  ```\n\n- Edit [`hello_perftest_interfaces/package.xml`](hello_perftest_interfaces/package.xml).\n\n  Beside the fields that were mentioned earlier, let's add a dependency on\n  interface package `std_msgs` so that we may reference those type definitions in our own types.\n  \n  ROS 2 packages which contains interfaces must also be made part of the\n  `rosidl_interface_packages` group.\n  \n  Finally, building the interfaces will require the use of the available code\n  generators, which are provided by package `rosidl_default_generators` (which\n  is only required at build time), while running the generated code will\n  require package `rosidl_default_runtime` to be available in the runtime\n  environment.\n\n  ```xml\n  \u003cpackage format=\"3\"\u003e\n    \u003cname\u003ehello_perftest_interfaces\u003c/name\u003e\n    \u003c!-- ... --\u003e\n    \u003cbuildtool_depend\u003erosidl_default_generators\u003c/buildtool_depend\u003e\n\n    \u003cdepend\u003estd_msgs\u003c/depend\u003e\n\n    \u003cexec_depend\u003erosidl_default_runtime\u003c/exec_depend\u003e\n\n    \u003cmember_of_group\u003erosidl_interface_packages\u003c/member_of_group\u003e\n    \u003c!-- ... --\u003e\n  \u003c/package\u003e\n\n  ```\n\n- Create a directory where to store custom message definitions. Message\n  definitions for a package are typically stored in a `msg/` subdirectory.\n\n  ```sh\n  mkdir hello_perftest_interfaces/msg\n  ```\n\n- Define an example message type using ROS IDL. Create\n  [`hello_perftest_interfaces/msg/TestPayload.msg`](hello_perftest_interfaces/msg/TestPayload.msg) with the following contents:\n\n  ```txt\n  # A simple message used by our example ROS 2 package.\n\n  # Metadata information about this payload\n  std_msgs/Header header\n\n  # An unbounded array of bytes\n  byte[] data\n  ```\n\n- Modify [`hello_perftest_interfaces/CMakeLists.txt`](hello_perftest_interfaces/CMakeLists.txt) to process the custom\n  message definitions, generate the required source code, build them into a\n  library, and install the resulting artifacts so that the types may be used by\n  other packages.\n\n  - Load packages `std_msgs` and `rosidl_default_generators`. Dependencies\n    are loaded usign CMake's `find_package()` function. All required search\n    paths are already set up by `colcon`/`ament` build system.\n\n    ```cmake\n    find_package(std_msgs REQUIRED)\n    find_package(rosidl_default_generators REQUIRED)\n    ```\n\n  - Use function [`rosidl_generate_interfaces()`](https://github.com/ros2/rosidl/blob/master/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake) to\n    generate type supports for our custom messages:\n\n    ```cmake\n    rosidl_generate_interfaces(${PROJECT_NAME}\n      msg/TestPayload.msg\n      DEPENDENCIES std_msgs\n    )\n    ```\n  \n  - Use some of the functions provided by `ament` to export runtime\n    dependencies, and to declare the current source directory as a ROS 2\n    package:\n\n    ```cmake\n    ament_export_dependencies(\n      rosidl_default_runtime\n      std_msgs\n    )\n\n    ament_package()\n    ```\n\n- Delete unused directories in `hello_perftest_interfaces`:\n\n  ```sh\n  rm -r hello_perftest_interfaces/{include,src}\n  ```\n\n- Edit [`hello_perftest/package.xml`](hello_perftest/package.xml) and add a\n  dependency from `hello_perftest_interfaces`. Since we are going to use the\n  ROS 2 C++ API, also add a dependency from `rclcpp`. We will also be taking\n  advantage of the `rclcpp_components` package.\n\n  ```xml\n  \u003cpackage format=\"3\"\u003e\n    \u003cname\u003ehello_perftest\u003c/name\u003e\n    \u003c!-- ... --\u003e\n    \u003cdepend\u003ehello_perftest_interfaces\u003c/depend\u003e\n\n    \u003cdepend\u003erclcpp\u003c/depend\u003e\n\n    \u003cdepend\u003erclcpp_components\u003c/depend\u003e\n    \u003c!-- ... --\u003e\n  \u003c/package\u003e\n\n  ```\n\n- Define `hello_perftest::LatencyNode` as an abstract subclass of \n  `rclcpp::Node` to represent the common structure of a component\n  participating in a latency test.\n\n  Extend this class into two concrete classes, \n  `hello_perftest::LatencyPublisher` and `hello_perftest::LatencySubscriber`.\n\n- Modify `hello_perftest/CMakeLists.txt` to build the example latency  \n  application.\n\n  - Load the package with custom interfaces and other dependencies:\n\n    ```cmake\n    find_package(rclcpp REQUIRED)\n    find_package(rclcpp_components REQUIRED)\n    find_package(${PROJECT_NAME}_interfaces REQUIRED)\n    ```\n  \n  - Generate a library for the node components. We build a single\n    shared library for the whole package.\n\n    ```cmake\n    add_library(${PROJECT_NAME} SHARED\n      src/latency/latency.cpp\n      include/hello_perftest/latency.hpp\n    )\n    ament_target_dependencies(${PROJECT_NAME}\n      rclcpp\n      rclcpp_components\n      ${PROJECT_NAME}_interfaces\n    )\n    target_include_directories(${PROJECT_NAME}\n      PUBLIC\n        \"$\u003cBUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include\u003e\"\n        \"$\u003cINSTALL_INTERFACE:include\u003e\"\n    )\n    install(TARGETS ${PROJECT_NAME}\n      ARCHIVE DESTINATION lib\n      LIBRARY DESTINATION lib\n      RUNTIME DESTINATION bin\n    )\n    install(DIRECTORY include/\n      DESTINATION include\n    )\n    ```\n\n  - Generate two executables from the registered node components:\n\n    ```cmake\n    rclcpp_components_register_node(${PROJECT_NAME}\n      PLUGIN \"hello_perftest::LatencySubscriber\"\n      EXECUTABLE latency_sub\n    )\n\n    rclcpp_components_register_node(${PROJECT_NAME}\n      PLUGIN \"hello_perftest::LatencyPublisher\"\n      EXECUTABLE latency_pub\n    )\n    ```\n  \n  - Alternatively, generate two executables using hand-written `main()` \n    functions:\n\n    ```cmake\n    add_executable(latency_sub_main\n      src/latency/latency_subscriber_main.cpp\n    )\n    target_link_libraries(latency_sub_main ${PROJECT_NAME})\n    install(TARGETS latency_sub_main\n      ARCHIVE DESTINATION lib\n      LIBRARY DESTINATION lib\n      RUNTIME DESTINATION bin\n    )\n\n    add_executable(latency_pub_main\n      src/latency/latency_publisher_main.cpp\n    )\n    target_link_libraries(latency_pub_main ${PROJECT_NAME})\n    install(TARGETS latency_pub_main\n      ARCHIVE DESTINATION lib\n      LIBRARY DESTINATION lib\n      RUNTIME DESTINATION bin\n    )\n    ```\n\n- Commit files to `git`:\n\n  ```sh\n  git add -A\n  git commit -m \"Initial commit\".\n  ```\n\n## Example application\n\nSee [hello_perftest/latency.hpp](hello_perftest/include/hello_perftest/latency.hpp)\nfor the definition of the node classes.\n\nThe base class, `LatencyNode`, creates a publisher and a subcriber on two custom\ntopics. The endpoints use custom Quality of Service. The subscriber is associated\nwith a virtual callback to be implemented by each concrete subclass.\n\nThe nodes declare some \"parameters\" to control some configuration options.\nThis is just to showcase the usage of [ROS 2 parameters](https://docs.ros.org/en/galactic/Concepts/About-ROS-2-Parameters.html) to allow\nthe application to be dynamically reconfigured using YAML files or command line\narguments.\n\nEach concrete class (`LatencyPublisher` and `LatencySubscriber`) is registered\nas an \"rclcpp component\" in [latency.cpp](hello_perftest/src/latency/latency.cpp).\nThis will allow us to build all the nodes into a single shared library, and\nthen automatically generate executables to \"spin them up\" in an independent\nprocess using CMake function `rclcpp_components_register_node()`.\n\nThe nodes can also be used in a custom, hand-written `main()`. In this case\nit is possible to build the node instances passing any number of custom \narguments to their constructors (whereas the node classes must have a \nconstructor which accepts a single argument of type `rclcpp::NodeOptions`\nin order to be used as components).\n\nThe publisher uses the ROS graph events to wait for the discovery of the\nexpected number of subscribers before beginning the test.\n\nAs an extension, the nodes could be turned into \"managed nodes\" by making them\nextend `rclcpp_lifecycle::LifecycleNode` instead of `rclcpp::Node`, and\nby mapping various test actions to \"lifecycle transitions\" which may be\nthen controlled by an external client\n(see this [design document](https://design.ros2.org/articles/node_lifecycle.html)\nand this [example](https://github.com/ros2/demos/tree/master/lifecycle) for more\ninformation on managed nodes).\n\n## Build the repository\n\nCreate a workspace directory and build the packages:\n\n```sh\n# Create a workspace directory\nmkdir ws-hello-perftest\ncd ws-hello-perftest\n\n# Create a symlink to the repository's *root*,\n# not to the `hello_perftest` package.\nln -s ../hello-perftest .\n\n# Build everything with colcon\ncolcon build --symlink-install\n```\n\n## Run example applications\n\n- Load the built workspace:\n\n  ```sh\n  source install/setup.bash\n  ```\n\n- Components can be run using `ros2 run`:\n\n  ```sh\n  # Start a subscriber\n  ros2 run hello_perftest latency_sub\n\n  # Start a publisher\n  ros2 run hello_perftest latency_pub\n  ```\n\n- Hand-written executables can be run by hand:\n\n  ```sh\n  install/hello_perftest/bin/latency_pub_main\n\n  install/hello_perftest/bin/latency_sub_main\n  ```\n\n- Since the nodes use parameters, they can be configured from the\n  command-line. The node name can also be changed when spawning multiple\n  nodes (not necessary, but it allows the nodes to appear with different\n  names in the ROS graph, e.g. if inspected with `rqt`).\n\n  ```sh\n  # Change number of expected subscribers\n  ros2 run hello_perftest latency_pub --ros-args -p subscribers_count:=3\n\n  # Start subscribers with different names\n  ros2 run hello_perftest latency_sub --ros-args -r __node:=latency_sub_1\n\n  ros2 run hello_perftest latency_sub --ros-args -r __node:=latency_sub_2\n\n  ros2 run hello_perftest latency_sub --ros-args -r __node:=latency_sub_3\n\n  ```\n\n- Parameters could also be changed using a YAML file. For example, save\n  the following as `params.yml`:\n\n  ```yaml\n  latency_pub:\n    ros__parameters:\n      subscribers_count: 2\n  ```\n\n  You can then load the parameters file using argument `--params-file`:\n\n  ```sh\n  ros2 run hello_perftest latency_pub --ros-args --params-file params.yml\n  ```\n\n- The example also includes an executable which spawns both nodes in a\n  single process:\n\n  ```sh\n  ./install/hello_perftest/bin/latency_single\n  ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasorbini%2Fhello-perftest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasorbini%2Fhello-perftest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasorbini%2Fhello-perftest/lists"}