$npx -y skills add arpitg1304/robotics-agent-skills --skill ros2Comprehensive best practices, design patterns, and common pitfalls for ROS2 (Robot Operating System 2) development. Use this skill when building ROS2 nodes, packages, launch files, components, or debugging ROS2 systems. Trigger whenever the user mentions ROS2, colcon, rclpy, rclc
| 1 | # ROS2 Development Skill |
| 2 | |
| 3 | ## When to Use This Skill |
| 4 | - Building ROS2 packages, nodes, or component containers |
| 5 | - Setting up colcon workspaces, ament_cmake, or ament_python packages |
| 6 | - Writing CMakeLists.txt, package.xml, or setup.py for ROS2 |
| 7 | - Defining custom messages, services, or actions |
| 8 | - Writing Python launch files with conditional logic |
| 9 | - Configuring DDS middleware and QoS profiles |
| 10 | - Implementing lifecycle (managed) nodes |
| 11 | - Working with Nav2, MoveIt2, or other ROS2 frameworks |
| 12 | - Debugging DDS discovery, QoS mismatches, or build failures |
| 13 | - Deploying ROS2 to production or embedded systems (micro-ROS) |
| 14 | - Setting up CI/CD for ROS2 packages |
| 15 | |
| 16 | ## Core Architecture |
| 17 | |
| 18 | ### 1. Node Design Patterns |
| 19 | |
| 20 | **Basic Node (rclpy)**: |
| 21 | ```python |
| 22 | #!/usr/bin/env python3 |
| 23 | import rclpy |
| 24 | from rclpy.node import Node |
| 25 | from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy |
| 26 | from std_msgs.msg import String |
| 27 | |
| 28 | class PerceptionNode(Node): |
| 29 | def __init__(self): |
| 30 | super().__init__('perception_node') |
| 31 | |
| 32 | # 1. Declare parameters with types and descriptions |
| 33 | self.declare_parameter('rate_hz', 30.0, |
| 34 | descriptor=ParameterDescriptor( |
| 35 | description='Processing rate in Hz', |
| 36 | floating_point_range=[FloatingPointRange( |
| 37 | from_value=1.0, to_value=120.0, step=0.0 |
| 38 | )] |
| 39 | )) |
| 40 | self.declare_parameter('confidence_threshold', 0.7) |
| 41 | self.declare_parameter('frame_id', 'camera_link') |
| 42 | |
| 43 | # 2. Read parameters |
| 44 | rate_hz = self.get_parameter('rate_hz').value |
| 45 | self.threshold = self.get_parameter('confidence_threshold').value |
| 46 | self.frame_id = self.get_parameter('frame_id').value |
| 47 | |
| 48 | # 3. Set up QoS profiles |
| 49 | sensor_qos = QoSProfile( |
| 50 | reliability=ReliabilityPolicy.BEST_EFFORT, |
| 51 | history=HistoryPolicy.KEEP_LAST, |
| 52 | depth=1 |
| 53 | ) |
| 54 | reliable_qos = QoSProfile( |
| 55 | reliability=ReliabilityPolicy.RELIABLE, |
| 56 | history=HistoryPolicy.KEEP_LAST, |
| 57 | depth=10 |
| 58 | ) |
| 59 | |
| 60 | # 4. Publishers first, then subscribers |
| 61 | self.det_pub = self.create_publisher( |
| 62 | DetectionArray, 'detections', reliable_qos) |
| 63 | |
| 64 | self.image_sub = self.create_subscription( |
| 65 | Image, 'camera/image_raw', self.image_callback, sensor_qos) |
| 66 | |
| 67 | # 5. Timers for periodic work |
| 68 | self.timer = self.create_timer(1.0 / rate_hz, self.timer_callback) |
| 69 | |
| 70 | # 6. Parameter change callback |
| 71 | self.add_on_set_parameters_callback(self.param_callback) |
| 72 | |
| 73 | self.get_logger().info( |
| 74 | f'Perception node started at {rate_hz}Hz, ' |
| 75 | f'threshold={self.threshold}') |
| 76 | |
| 77 | def param_callback(self, params): |
| 78 | """Handle runtime parameter changes (replaces dynamic_reconfigure)""" |
| 79 | for param in params: |
| 80 | if param.name == 'confidence_threshold': |
| 81 | self.threshold = param.value |
| 82 | self.get_logger().info(f'Threshold updated to {param.value}') |
| 83 | return SetParametersResult(successful=True) |
| 84 | |
| 85 | def image_callback(self, msg): |
| 86 | # Process incoming images |
| 87 | pass |
| 88 | |
| 89 | def timer_callback(self): |
| 90 | # Periodic work |
| 91 | pass |
| 92 | |
| 93 | def main(args=None): |
| 94 | rclpy.init(args=args) |
| 95 | node = PerceptionNode() |
| 96 | try: |
| 97 | rclpy.spin(node) |
| 98 | except KeyboardInterrupt: |
| 99 | pass |
| 100 | finally: |
| 101 | node.destroy_node() |
| 102 | rclpy.shutdown() |
| 103 | |
| 104 | if __name__ == '__main__': |
| 105 | main() |
| 106 | ``` |
| 107 | |
| 108 | **Basic Node (rclcpp)**: |
| 109 | ```cpp |
| 110 | #include <rclcpp/rclcpp.hpp> |
| 111 | #include <sensor_msgs/msg/image.hpp> |
| 112 | #include <vision_msgs/msg/detection2_d.hpp> |
| 113 | #include <memory> |
| 114 | |
| 115 | class PerceptionNode : public rclcpp::Node { |
| 116 | public: |
| 117 | PerceptionNode() : Node("perception_node") { |
| 118 | // Declare and get parameters |
| 119 | this->declare_parameter("rate_hz", 30.0); |
| 120 | this->declare_parameter("confidence_threshold", 0.7); |
| 121 | double rate_hz = this->get_parameter("rate_hz").as_double(); |
| 122 | |
| 123 | // QoS |
| 124 | auto sensor_qos = rclcpp::SensorDataQoS(); |
| 125 | auto reliable_qos = rclcpp::QoS(10).reliable(); |
| 126 | |
| 127 | // Publishers and subscribers |
| 128 | det_pub |