Use when deciding which existing ROS 2 package already solves a need (control, navigation, manipulation, SLAM, sensor fusion, simulation bridging, recording, visualization, diagnostics, teleoperation, microcontroller bridging) before writing custom code — a curated reference to the 20 most important ROS 2 packages, what each does, when to reach for it, install commands, and pointers to the other skills in this plugin that go deeper.
Scanned 9/19/2026
Install to Claude Code
npx -y skills add enesbirlik/claude-code-robotics --skill ros2-ecosystem-packages --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Ros2 Ecosystem Packages?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/enesbirlik-ros2-ecosystem-packages)More formats (shields.io, HTML) on the badges page.
---
name: ros2-ecosystem-packages
description: Use when deciding which existing ROS 2 package already solves a need (control, navigation, manipulation, SLAM, sensor fusion, simulation bridging, recording, visualization, diagnostics, teleoperation, microcontroller bridging) before writing custom code — a curated reference to the 20 most important ROS 2 packages, what each does, when to reach for it, install commands, and pointers to the other skills in this plugin that go deeper.
---
# ROS 2 Ecosystem: The 20 Packages to Reach For First
Before writing a custom node for a capability listed below, use the maintained upstream
package instead. Reinventing TF broadcasting, SLAM, a diff-drive controller, or a bag recorder
is wasted effort and a common source of subtle bugs (frame timing, QoS mismatches, coordinate
convention errors) that these packages have already had years of real-world use to fix. Install
commands below use the binary APT packages (`ros-<distro>-<package>`, distro = `humble` or
`jazzy`); prefer these over building from source unless you need an unreleased fix.
## Quick reference
| # | Package | Purpose | Reach for it when... |
|---|---|---|---|
| 1 | `rclcpp` / `rclpy` | Core C++/Python client libraries | Writing any ROS 2 node — see [[ros2-workspace]]. |
| 2 | `tf2` (`tf2_ros`, `tf2_geometry_msgs`) | Coordinate frame transforms over time | You need to know where one frame is relative to another, now or in the past. |
| 3 | `robot_state_publisher` | Publishes TF from a URDF + joint states | You have a URDF and need `/tf` for every link, not just the base. |
| 4 | `joint_state_publisher` / `_gui` | Publishes/edits `/joint_states` | Visualizing or manually testing a URDF without real hardware. |
| 5 | `xacro` | Macro-expands `.xacro` into `.urdf` | Any non-trivial robot description — see [[urdf-xacro-builder]]. |
| 6 | `launch` / `launch_ros` | Declarative multi-node bringup | Starting more than one node together with shared arguments. |
| 7 | `ros2_control` + `ros2_controllers` | Real-time hardware abstraction + standard controllers | You need a diff-drive, joint-trajectory, or effort controller instead of hand-rolled PID nodes. |
| 8 | `navigation2` (Nav2) | Autonomous mobile robot navigation stack | Path planning, obstacle avoidance, costmaps — see [[nav2-moveit2-config]]. |
| 9 | `moveit2` | Motion planning for manipulators | Arm/gripper trajectory planning, collision-aware IK — see [[nav2-moveit2-config]]. |
| 10 | `slam_toolbox` | 2D SLAM (mapping + localization) | Building a map from a lidar with no prior map, online or offline. |
| 11 | `robot_localization` | EKF/UKF sensor fusion | Fusing wheel odometry + IMU (+ GPS) into one continuous `odom`/`map` estimate. |
| 12 | `behaviortree.cpp` (`behaviortree_cpp`) | Behavior tree execution engine | Sequencing recoverable, composable robot behaviors — Nav2's BT navigator is built on it. |
| 13 | `rosbag2` | Record/replay topic data | Capturing sensor/log data for offline debugging or dataset collection. |
| 14 | `rviz2` | 3D visualization | Inspecting TF, sensor data, costmaps, or planned trajectories visually. |
| 15 | `rqt` (`rqt_graph`, `rqt_console`, `rqt_plot`) | GUI debugging tool suite | Visualizing the node/topic graph, tailing logs, or plotting a signal live. |
| 16 | `ros_gz` (`ros_gz_bridge`, `ros_gz_sim`, `gz_ros2_control`) | ROS 2 ⟷ Gazebo Sim bridge | Simulating a robot in Gazebo and bridging its topics/clock into ROS 2 — see [[urdf-xacro-builder]]. |
| 17 | `image_transport` + `cv_bridge` | Efficient image topics + OpenCV interop | Publishing/subscribing camera images, especially compressed, or converting to/from `cv::Mat`. |
| 18 | `diagnostic_updater` + `diagnostic_aggregator` | Standardized system health reporting | Reporting node/hardware health in a way `rqt_robot_monitor`/`/diagnostics` consumers expect. |
| 19 | `teleop_twist_keyboard` + `joy` | Manual teleoperation | Driving a robot by keyboard or gamepad for a smoke test, no custom teleop node needed. |
| 20 | micro-ROS (`micro_ros_setup`, `rclc`, micro-ROS Agent) | ROS 2 client stack for microcontrollers | Bridging an STM32/ESP32 into the ROS 2 graph — see [[embedded-ros-bridge]]. |
Also worth knowing, just past the top 20: `rosbridge_suite` / `foxglove_bridge` (WebSocket
bridges for browser-based tooling like Foxglove Studio), `plotjuggler` (standalone bag/live
signal plotting), `ros2_socketcan` (a ready-made SocketCAN ⟷ ROS 2 bridge node, an alternative
to hand-writing the CAN bridge shown in [[embedded-ros-bridge]]).
## Deeper notes on the highest-leverage / most-misused packages
### `tf2` — coordinate transforms
```python
from tf2_ros import Buffer, TransformListener
from rclpy.duration import Duration
self._tf_buffer = Buffer()
self._tf_listener = TransformListener(self._tf_buffer, self)
transform = self._tf_buffer.lookup_transform(
'base_link', 'laser_frame', rclpy.time.Time(), timeout=Duration(seconds=0.2))
```
- Always pass an explicit `timeout` to `lookup_transform` — an un-bounded lookup that never
resolves (a missing publisher, a broken TF tree) hangs the calling thread indefinitely.
- Use `rclpy.time.Time()` (i.e. "latest available") for most control-loop lookups; only request
a specific past stamp when you genuinely need to transform a timestamped sensor reading.
- A `LookupException`/`ExtrapolationException` almost always means a broken or incomplete TF
tree (a missing `robot_state_publisher`, a static transform never published) — fix the tree,
don't catch-and-ignore the exception.
### `ros2_control` — don't write your own hardware loop
Reach for `ros2_control` instead of a hand-rolled node that reads sensors and writes motor
commands in a `while` loop. It gives you: a real-time-safe controller manager, standard
controllers (`diff_drive_controller`, `joint_trajectory_controller`, `forward_command_controller`)
that are already tested against real hardware quirks, and a clean simulation/hardware swap via
the `<ros2_control>` URDF tag shown in [[urdf-xacro-builder]]. Only write a custom
`hardware_interface::SystemInterface` plugin for the actual hardware I/O — never reimplement
the controller logic on top of it.
### `slam_toolbox` vs `robot_localization` — don't confuse the two
- `slam_toolbox` answers "where am I, and what does the map look like?" from lidar scan
matching. It publishes `map -> odom`.
- `robot_localization` answers "what's my best continuous pose estimate from these sensors?"
by fusing odometry/IMU/GPS. It typically publishes `odom -> base_link` (and can publish
`map -> odom` too if configured as the global fusion node, but don't run two nodes both
publishing the same TF edge).
- A common misconfiguration is running both `slam_toolbox` and `robot_localization` each trying
to publish `map -> odom` — pick exactly one authority per TF edge.
### `rosbag2` — record only what you need
```bash
ros2 bag record /scan /tf /tf_static /odom -o my_test_run
ros2 bag play my_test_run
```
- Explicitly list topics rather than `-a` (all) on a real robot — recording high-rate raw
camera/lidar topics unbounded will fill disk and can starve I/O for other processes.
- Always include `/tf_static` alongside `/tf` when recording anything you'll want to visualize
later — static transforms are published once (transient-local) and are easy to miss if the
recorder started after they were published, unless the topic is explicitly subscribed.
### `image_transport` + `cv_bridge` — don't publish raw images if you can help it
```python
from cv_bridge import CvBridge
bridge = CvBridge()
ros_image = bridge.cv2_to_imgmsg(cv_frame, encoding='bgr8')
```
- Publish through `image_transport` (which offers `compressed`, `theora`, etc. transports
automatically) rather than raw `sensor_msgs/Image` on bandwidth-constrained links (Wi-Fi to
a base station, a slow serial-tunneled connection).
- Match the `encoding` string (`bgr8`, `mono8`, `rgb8`, ...) to what the array actually contains
— a mismatched encoding is a common source of a visually "wrong colors" but otherwise
functioning image pipeline.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!