{"id":"a7221fcf-fb00-4099-904b-f7b3cf8d5d46","shortId":"3nne7y","kind":"skill","title":"ros2-development","tagline":"Comprehensive 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","description":"# ROS2 Development Skill\n\n## When to Use This Skill\n- Building ROS2 packages, nodes, or component containers\n- Setting up colcon workspaces, ament_cmake, or ament_python packages\n- Writing CMakeLists.txt, package.xml, or setup.py for ROS2\n- Defining custom messages, services, or actions\n- Writing Python launch files with conditional logic\n- Configuring DDS middleware and QoS profiles\n- Implementing lifecycle (managed) nodes\n- Working with Nav2, MoveIt2, or other ROS2 frameworks\n- Debugging DDS discovery, QoS mismatches, or build failures\n- Deploying ROS2 to production or embedded systems (micro-ROS)\n- Setting up CI/CD for ROS2 packages\n\n## Core Architecture\n\n### 1. Node Design Patterns\n\n**Basic Node (rclpy)**:\n```python\n#!/usr/bin/env python3\nimport rclpy\nfrom rclpy.node import Node\nfrom rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy\nfrom std_msgs.msg import String\n\nclass PerceptionNode(Node):\n    def __init__(self):\n        super().__init__('perception_node')\n\n        # 1. Declare parameters with types and descriptions\n        self.declare_parameter('rate_hz', 30.0,\n            descriptor=ParameterDescriptor(\n                description='Processing rate in Hz',\n                floating_point_range=[FloatingPointRange(\n                    from_value=1.0, to_value=120.0, step=0.0\n                )]\n            ))\n        self.declare_parameter('confidence_threshold', 0.7)\n        self.declare_parameter('frame_id', 'camera_link')\n\n        # 2. Read parameters\n        rate_hz = self.get_parameter('rate_hz').value\n        self.threshold = self.get_parameter('confidence_threshold').value\n        self.frame_id = self.get_parameter('frame_id').value\n\n        # 3. Set up QoS profiles\n        sensor_qos = QoSProfile(\n            reliability=ReliabilityPolicy.BEST_EFFORT,\n            history=HistoryPolicy.KEEP_LAST,\n            depth=1\n        )\n        reliable_qos = QoSProfile(\n            reliability=ReliabilityPolicy.RELIABLE,\n            history=HistoryPolicy.KEEP_LAST,\n            depth=10\n        )\n\n        # 4. Publishers first, then subscribers\n        self.det_pub = self.create_publisher(\n            DetectionArray, 'detections', reliable_qos)\n\n        self.image_sub = self.create_subscription(\n            Image, 'camera/image_raw', self.image_callback, sensor_qos)\n\n        # 5. Timers for periodic work\n        self.timer = self.create_timer(1.0 / rate_hz, self.timer_callback)\n\n        # 6. Parameter change callback\n        self.add_on_set_parameters_callback(self.param_callback)\n\n        self.get_logger().info(\n            f'Perception node started at {rate_hz}Hz, '\n            f'threshold={self.threshold}')\n\n    def param_callback(self, params):\n        \"\"\"Handle runtime parameter changes (replaces dynamic_reconfigure)\"\"\"\n        for param in params:\n            if param.name == 'confidence_threshold':\n                self.threshold = param.value\n                self.get_logger().info(f'Threshold updated to {param.value}')\n        return SetParametersResult(successful=True)\n\n    def image_callback(self, msg):\n        # Process incoming images\n        pass\n\n    def timer_callback(self):\n        # Periodic work\n        pass\n\ndef main(args=None):\n    rclpy.init(args=args)\n    node = PerceptionNode()\n    try:\n        rclpy.spin(node)\n    except KeyboardInterrupt:\n        pass\n    finally:\n        node.destroy_node()\n        rclpy.shutdown()\n\nif __name__ == '__main__':\n    main()\n```\n\n**Basic Node (rclcpp)**:\n```cpp\n#include <rclcpp/rclcpp.hpp>\n#include <sensor_msgs/msg/image.hpp>\n#include <vision_msgs/msg/detection2_d.hpp>\n#include <memory>\n\nclass PerceptionNode : public rclcpp::Node {\npublic:\n    PerceptionNode() : Node(\"perception_node\") {\n        // Declare and get parameters\n        this->declare_parameter(\"rate_hz\", 30.0);\n        this->declare_parameter(\"confidence_threshold\", 0.7);\n        double rate_hz = this->get_parameter(\"rate_hz\").as_double();\n\n        // QoS\n        auto sensor_qos = rclcpp::SensorDataQoS();\n        auto reliable_qos = rclcpp::QoS(10).reliable();\n\n        // Publishers and subscribers\n        det_pub_ = this->create_publisher<vision_msgs::msg::Detection2D>(\"detections\", reliable_qos);\n        image_sub_ = this->create_subscription<sensor_msgs::msg::Image>(\n            \"camera/image_raw\", sensor_qos, [this](const std::shared_ptr<const sensor_msgs::msg::Image>& msg){\n                this->image_callback(msg);\n            });\n\n        timer_ = this->create_wall_timer(\n            std::chrono::milliseconds(static_cast<int>(1000.0 / rate_hz)),\n            [this](){ this->timer_callback(); });\n\n        RCLCPP_INFO(this->get_logger(), \"Perception node started at %.1fHz\", rate_hz);\n    }\n\nprivate:\n    void image_callback(const std::shared_ptr<const sensor_msgs::msg::Image>& msg) {\n        // Use shared_ptr for zero-copy potential\n    }\n    void timer_callback() {}\n\n    rclcpp::Publisher<vision_msgs::msg::Detection2D>::SharedPtr det_pub_;\n    rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr image_sub_;\n    rclcpp::TimerBase::SharedPtr timer_;\n};\n\nint main(int argc, char** argv) {\n    rclcpp::init(argc, argv);\n    rclcpp::spin(std::make_shared<PerceptionNode>());\n    rclcpp::shutdown();\n    return 0;\n}\n```\n\n### 2. Lifecycle (Managed) Nodes\n\nUse lifecycle nodes for production systems where you need deterministic startup, shutdown, and error recovery. This is one of ROS2's most important features over ROS1.\n\n**State Machine**: `Unconfigured → Inactive → Active → Finalized`\n\n```python\nfrom rclpy.lifecycle import Node as LifecycleNode, TransitionCallbackReturn\n\nclass ManagedPerception(LifecycleNode):\n    def __init__(self):\n        super().__init__('managed_perception')\n        self.get_logger().info('Node created (unconfigured)')\n\n    def on_configure(self, state) -> TransitionCallbackReturn:\n        \"\"\"Load params, allocate memory, set up pubs/subs (but don't activate)\"\"\"\n        self.declare_parameter('model_path', '')\n        model_path = self.get_parameter('model_path').value\n\n        try:\n            self.model = load_model(model_path)\n            self.det_pub = self.create_lifecycle_publisher(\n                DetectionArray, 'detections', 10)\n            self.get_logger().info(f'Configured with model: {model_path}')\n            return TransitionCallbackReturn.SUCCESS\n        except Exception as e:\n            self.get_logger().error(f'Configuration failed: {e}')\n            return TransitionCallbackReturn.FAILURE\n\n    def on_activate(self, state) -> TransitionCallbackReturn:\n        \"\"\"Start processing — subscriptions go live here\"\"\"\n        self.image_sub = self.create_subscription(\n            Image, 'camera/image_raw', self.image_callback, 1)\n        self.get_logger().info('Activated — processing images')\n        return TransitionCallbackReturn.SUCCESS\n\n    def on_deactivate(self, state) -> TransitionCallbackReturn:\n        \"\"\"Pause processing — safe to reconfigure after this\"\"\"\n        self.destroy_subscription(self.image_sub)\n        self.get_logger().info('Deactivated — stopped processing')\n        return TransitionCallbackReturn.SUCCESS\n\n    def on_cleanup(self, state) -> TransitionCallbackReturn:\n        \"\"\"Release resources, return to unconfigured\"\"\"\n        del self.model\n        self.get_logger().info('Cleaned up')\n        return TransitionCallbackReturn.SUCCESS\n\n    def on_shutdown(self, state) -> TransitionCallbackReturn:\n        \"\"\"Final cleanup before destruction\"\"\"\n        self.get_logger().info('Shutting down')\n        return TransitionCallbackReturn.SUCCESS\n\n    def on_error(self, state) -> TransitionCallbackReturn:\n        \"\"\"Handle errors — try to recover or fail gracefully\"\"\"\n        self.get_logger().error(f'Error in state {state.label}')\n        return TransitionCallbackReturn.SUCCESS  # Transition to unconfigured\n```\n\n**Orchestrating Lifecycle Nodes** with a launch file:\n```python\nfrom launch import LaunchDescription\nfrom launch_ros.actions import LifecycleNode\nfrom launch_ros.event_handlers import OnStateTransition\nfrom launch.actions import EmitEvent, RegisterEventHandler\nfrom launch_ros.events.lifecycle import ChangeState\nfrom lifecycle_msgs.msg import Transition\n\ndef generate_launch_description():\n    perception = LifecycleNode(\n        package='my_pkg', executable='managed_perception',\n        name='perception', output='screen',\n        parameters=[{'model_path': '/models/yolo.pt'}]\n    )\n\n    # Auto-configure on startup\n    configure_event = EmitEvent(event=ChangeState(\n        lifecycle_node_matcher=lambda node: node == perception,\n        transition_id=Transition.TRANSITION_CONFIGURE\n    ))\n\n    # Auto-activate after successful configure\n    activate_handler = RegisterEventHandler(OnStateTransition(\n        target_lifecycle_node=perception,\n        goal_state='inactive',\n        entities=[EmitEvent(event=ChangeState(\n            lifecycle_node_matcher=lambda node: node == perception,\n            transition_id=Transition.TRANSITION_ACTIVATE\n        ))]\n    ))\n\n    return LaunchDescription([\n        perception,\n        configure_event,\n        activate_handler,\n    ])\n```\n\n### 3. QoS (Quality of Service) — The #1 Source of ROS2 Bugs\n\nQoS mismatches are the most common reason topics silently fail to connect.\n\n**QoS Compatibility Matrix**:\n```\nPublisher     Subscriber    Compatible?\nRELIABLE      RELIABLE      ✅ Yes\nRELIABLE      BEST_EFFORT   ✅ Yes\nBEST_EFFORT   BEST_EFFORT   ✅ Yes\nBEST_EFFORT   RELIABLE      ❌ NO — SILENT FAILURE\n```\n\n**Recommended QoS Profiles by Use Case**:\n```python\nfrom rclpy.qos import (\n    QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy,\n    QoSDurabilityPolicy, QoSPresetProfiles\n)\n\n# Sensor data (cameras, lidars) — tolerate drops, want latest\nSENSOR_QOS = QoSProfile(\n    reliability=QoSReliabilityPolicy.BEST_EFFORT,\n    history=QoSHistoryPolicy.KEEP_LAST,\n    depth=1,\n    durability=QoSDurabilityPolicy.VOLATILE\n)\n\n# Commands (velocity, joint) — never miss, small buffer\nCOMMAND_QOS = QoSProfile(\n    reliability=QoSReliabilityPolicy.RELIABLE,\n    history=QoSHistoryPolicy.KEEP_LAST,\n    depth=10,\n    durability=QoSDurabilityPolicy.VOLATILE\n)\n\n# Map / static data — reliable, and late joiners get it\nMAP_QOS = QoSProfile(\n    reliability=QoSReliabilityPolicy.RELIABLE,\n    history=QoSHistoryPolicy.KEEP_LAST,\n    depth=1,\n    durability=QoSDurabilityPolicy.TRANSIENT_LOCAL  # Replaces ROS1 latch\n)\n\n# Default parameter/state — reliable with some history\nSTATE_QOS = QoSProfile(\n    reliability=QoSReliabilityPolicy.RELIABLE,\n    history=QoSHistoryPolicy.KEEP_LAST,\n    depth=10\n)\n```\n\n**Debugging QoS Issues**:\n```bash\n# Check QoS info for a topic\nros2 topic info /camera/image_raw -v\n# Look for \"Reliability\" and \"Durability\" fields\n\n# Check for incompatible QoS events\nros2 run rqt_topic rqt_topic  # Shows sub counts and QoS\n\n# If 0 subscribers despite nodes running: QoS MISMATCH\n```\n\n### 4. Launch Files (Python-Based)\n\nROS2 launch files are Python, enabling powerful conditional logic:\n\n```python\nimport os\nfrom launch import LaunchDescription\nfrom launch.actions import (\n    DeclareLaunchArgument, IncludeLaunchDescription,\n    GroupAction, OpaqueFunction, TimerAction\n)\nfrom launch.conditions import IfCondition, UnlessCondition\nfrom launch.substitutions import (\n    LaunchConfiguration, PathJoinSubstitution,\n    PythonExpression\n)\nfrom launch_ros.actions import Node, ComposableNodeContainer, LoadComposableNode\nfrom launch_ros.descriptions import ComposableNode\nfrom launch_ros.substitutions import FindPackageShare\n\ndef generate_launch_description():\n\n    # Arguments\n    robot_name_arg = DeclareLaunchArgument('robot_name', default_value='ur5')\n    sim_arg = DeclareLaunchArgument('sim', default_value='false')\n    use_composition_arg = DeclareLaunchArgument('use_composition', default_value='true')\n\n    robot_name = LaunchConfiguration('robot_name')\n    sim = LaunchConfiguration('sim')\n\n    # Load YAML params\n    config_file = PathJoinSubstitution([\n        FindPackageShare('my_pkg'), 'config', 'robot_params.yaml'\n    ])\n\n    # Standard node\n    perception_node = Node(\n        package='my_pkg',\n        executable='perception_node',\n        name='perception',\n        namespace=robot_name,\n        parameters=[config_file, {'use_sim_time': sim}],\n        remappings=[\n            ('camera/image_raw', 'realsense/color/image_raw'),\n            ('detections', 'perception/detections'),\n        ],\n        output='screen',\n        condition=UnlessCondition(LaunchConfiguration('use_composition')),\n    )\n\n    # Composable nodes (zero-copy, same process)\n    composable_container = ComposableNodeContainer(\n        name='perception_container',\n        namespace=robot_name,\n        package='rclcpp_components',\n        executable='component_container_mt',  # Multi-threaded\n        composable_node_descriptions=[\n            ComposableNode(\n                package='my_pkg',\n                plugin='my_pkg::PerceptionComponent',\n                name='perception',\n                parameters=[config_file],\n                remappings=[\n                    ('camera/image_raw', 'realsense/color/image_raw'),\n                ],\n            ),\n            ComposableNode(\n                package='my_pkg',\n                plugin='my_pkg::TrackerComponent',\n                name='tracker',\n            ),\n        ],\n        condition=IfCondition(LaunchConfiguration('use_composition')),\n    )\n\n    # Delayed start for nodes that need others to initialize first\n    delayed_planner = TimerAction(\n        period=3.0,\n        actions=[\n            Node(package='my_pkg', executable='planner_node', name='planner')\n        ]\n    )\n\n    return LaunchDescription([\n        robot_name_arg, sim_arg, use_composition_arg,\n        perception_node,\n        composable_container,\n        delayed_planner,\n    ])\n```\n\n### 5. Components (ROS2's Answer to Nodelets)\n\n```cpp\n#include <rclcpp/rclcpp.hpp>\n#include <rclcpp_components/register_node_macro.hpp>\n#include <sensor_msgs/msg/image.hpp>\n\nnamespace my_pkg {\n\nclass PerceptionComponent : public rclcpp::Node {\npublic:\n    explicit PerceptionComponent(const rclcpp::NodeOptions& options)\n        : Node(\"perception\", options)\n    {\n        // Use intra-process communication for zero-copy\n        auto sub_options = rclcpp::SubscriptionOptions();\n        sub_options.use_intra_process_comm =\n            rclcpp::IntraProcessSetting::Enable;\n\n        sub_ = this->create_subscription<sensor_msgs::msg::Image>(\n            \"camera/image_raw\",\n            rclcpp::SensorDataQoS(),\n            [this](sensor_msgs::msg::Image::UniquePtr msg) {\n                this->callback(std::move(msg));\n            },\n            sub_options);\n    }\n\nprivate:\n    void callback(sensor_msgs::msg::Image::UniquePtr msg) {\n        // UniquePtr = zero-copy when:\n        //   - publisher also uses UniquePtr\n        //   - both subscriber and publisher use intra-process\n        //   - this is the only subscriber\n        // msg is moved, not copied\n    }\n\n    rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr sub_;\n};\n\n}  // namespace my_pkg\n\nRCLCPP_COMPONENTS_REGISTER_NODE(my_pkg::PerceptionComponent)\n```\n\n### 6. Actions (ROS2 Style)\n\n```python\nfrom rclpy.action import ActionServer, CancelResponse, GoalResponse\nfrom my_interfaces.action import PickPlace\n\nclass PickPlaceServer(Node):\n    def __init__(self):\n        super().__init__('pick_place_server')\n        self._action_server = ActionServer(\n            self, PickPlace, 'pick_place',\n            execute_callback=self.execute_cb,\n            goal_callback=self.goal_cb,\n            cancel_callback=self.cancel_cb,\n        )\n\n    def goal_cb(self, goal_request):\n        \"\"\"Decide whether to accept or reject the goal\"\"\"\n        self.get_logger().info(f'Received goal: {goal_request.target_pose}')\n        return GoalResponse.ACCEPT\n\n    def cancel_cb(self, goal_handle):\n        \"\"\"Decide whether to accept cancel requests\"\"\"\n        self.get_logger().info('Cancel requested')\n        return CancelResponse.ACCEPT\n\n    async def execute_cb(self, goal_handle):\n        \"\"\"Execute the action (runs in an executor thread)\"\"\"\n        feedback_msg = PickPlace.Feedback()\n\n        for i, step in enumerate(self.plan(goal_handle.request)):\n            # Check cancellation\n            if goal_handle.is_cancel_requested:\n                goal_handle.canceled()\n                return PickPlace.Result(success=False)\n\n            self.execute_step(step)\n            feedback_msg.progress = float(i) / len(self.steps)\n            goal_handle.publish_feedback(feedback_msg)\n\n        goal_handle.succeed()\n        return PickPlace.Result(success=True)\n```\n\n## DDS Configuration\n\n### Choosing a DDS Implementation\n```bash\n# Set DDS middleware (in ~/.bashrc or launch)\nexport RMW_IMPLEMENTATION=rmw_cyclonedds_cpp    # Recommended for most cases\n# export RMW_IMPLEMENTATION=rmw_fastrtps_cpp    # Default, good for multi-machine\n\n# Limit DDS discovery to local machine (reduces network noise)\nexport ROS_LOCALHOST_ONLY=1\n\n# Use ROS_DOMAIN_ID to isolate robot groups on same network\nexport ROS_DOMAIN_ID=42  # Range 0-101\n```\n\n### CycloneDDS Tuning (cyclonedds.xml)\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<CycloneDDS xmlns=\"https://cdds.io/config\">\n  <Domain>\n    <General>\n      <NetworkInterfaceAddress>eth0</NetworkInterfaceAddress>\n      <AllowMulticast>false</AllowMulticast>  <!-- Unicast for reliability -->\n    </General>\n    <Internal>\n      <MaxMessageSize>65500</MaxMessageSize>\n      <SocketReceiveBufferSize>10MB</SocketReceiveBufferSize>\n    </Internal>\n    <!-- For large data (images, point clouds) -->\n    <Sizing>\n      <ReceiveBufferSize>10MB</ReceiveBufferSize>\n    </Sizing>\n  </Domain>\n</CycloneDDS>\n```\n\n```bash\nexport CYCLONEDDS_URI=file:///path/to/cyclonedds.xml\n```\n\n## Build System\n\n### Workspace Setup and colcon\n\n```bash\n# Create a ROS2 workspace\nmkdir -p ~/ros2_ws/src\ncd ~/ros2_ws\n\n# Clone packages into src/\ncd src\ngit clone https://github.com/org/my_robot_pkg.git\ncd ..\n\n# Install dependencies declared in package.xml files\nsudo apt update\nrosdep update\nrosdep install --from-paths src --ignore-src -y\n\n# Build the workspace\nsource /opt/ros/humble/setup.bash   # Source the ROS2 underlay FIRST\ncolcon build\n\n# Source the workspace overlay\nsource install/setup.bash\n```\n\n**Essential colcon flags**:\n```bash\n# Build only specific packages (faster iteration)\ncolcon build --packages-select my_pkg\n\n# Build a package and all its dependencies\ncolcon build --packages-up-to my_pkg\n\n# Symlink Python files instead of copying (edit without rebuild)\ncolcon build --symlink-install\n\n# Parallel jobs (default = nproc, lower if running out of RAM)\ncolcon build --parallel-workers 4\n\n# Pass CMake args to all packages\ncolcon build --cmake-args -DCMAKE_BUILD_TYPE=Release\n\n# Clean build (remove build/ install/ log/ and rebuild)\nrm -rf build/ install/ log/\ncolcon build\n\n# Build with compiler warnings as errors (CI)\ncolcon build --cmake-args -DCMAKE_CXX_FLAGS=\"-Wall -Werror\"\n\n# Show build output in real-time (useful for debugging build failures)\ncolcon build --event-handlers console_direct+\n```\n\n### Build Types: ament_cmake vs ament_python\n\nChoose based on your package language:\n\n```\nament_cmake     — C++ packages, mixed C++/Python packages, packages with custom msgs\nament_python    — Pure Python packages (no C++, no custom messages)\n```\n\n### package.xml — Declaring Dependencies\n\n```xml\n<?xml version=\"1.0\"?>\n<?xml-model href=\"http://download.ros.org/schema/package_format3.xsd\"\n            schematypens=\"http://www.w3.org/2001/XMLSchema\"?>\n<package format=\"3\">\n  <name>my_robot_pkg</name>\n  <version>0.1.0</version>\n  <description>My robot perception package</description>\n  <maintainer email=\"dev@example.com\">Dev Name</maintainer>\n  <license>Apache-2.0</license>\n\n  <!-- Build tool — determines build type -->\n  <buildtool_depend>ament_cmake</buildtool_depend>\n  <!-- For pure Python: <buildtool_depend>ament_python</buildtool_depend> -->\n\n  <!-- Build-time dependencies (headers, CMake modules) -->\n  <build_depend>rclcpp</build_depend>\n  <build_depend>sensor_msgs</build_depend>\n  <build_depend>OpenCV</build_depend>\n\n  <!-- Runtime dependencies -->\n  <exec_depend>rclcpp</exec_depend>\n  <exec_depend>sensor_msgs</exec_depend>\n  <exec_depend>rclpy</exec_depend>\n\n  <!-- Shortcut: depend = build_depend + exec_depend -->\n  <depend>rclcpp</depend>\n  <depend>sensor_msgs</depend>\n  <depend>geometry_msgs</depend>\n  <depend>tf2_ros</depend>\n  <depend>cv_bridge</depend>\n\n  <!-- For custom message generation -->\n  <build_depend>rosidl_default_generators</build_depend>\n  <exec_depend>rosidl_default_runtime</exec_depend>\n  <member_of_group>rosidl_interface_packages</member_of_group>\n\n  <!-- Test dependencies -->\n  <test_depend>ament_lint_auto</test_depend>\n  <test_depend>ament_cmake_pytest</test_depend>\n  <test_depend>launch_testing_ament_cmake</test_depend>\n\n  <export>\n    <build_type>ament_cmake</build_type>\n  </export>\n</package>\n```\n\n### CMakeLists.txt — ament_cmake Package\n\n```cmake\ncmake_minimum_required(VERSION 3.8)\nproject(my_robot_pkg)\n\n# Default to C++17\nif(NOT CMAKE_CXX_STANDARD)\n  set(CMAKE_CXX_STANDARD 17)\nendif()\n\nif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  add_compile_options(-Wall -Wextra -Wpedantic)\nendif()\n\n# ── Find dependencies ──────────────────────────────────────────\nfind_package(ament_cmake REQUIRED)\nfind_package(rclcpp REQUIRED)\nfind_package(rclcpp_components REQUIRED)\nfind_package(sensor_msgs REQUIRED)\nfind_package(geometry_msgs REQUIRED)\nfind_package(tf2_ros REQUIRED)\nfind_package(cv_bridge REQUIRED)\nfind_package(OpenCV REQUIRED)\n\n# ── Custom messages / services / actions ───────────────────────\nfind_package(rosidl_default_generators REQUIRED)\n\nrosidl_generate_interfaces(${PROJECT_NAME}\n  \"msg/Detection.msg\"\n  \"srv/GetPose.srv\"\n  \"action/PickPlace.action\"\n  DEPENDENCIES geometry_msgs sensor_msgs\n)\n\n# ── Standalone executable node ─────────────────────────────────\nadd_executable(perception_node src/perception_node.cpp)\nament_target_dependencies(perception_node\n  rclcpp sensor_msgs cv_bridge OpenCV tf2_ros\n)\ninstall(TARGETS perception_node\n  DESTINATION lib/${PROJECT_NAME}\n)\n\n# ── Component (composable node) ────────────────────────────────\nadd_library(perception_component SHARED\n  src/perception_component.cpp\n)\nament_target_dependencies(perception_component\n  rclcpp rclcpp_components sensor_msgs cv_bridge OpenCV\n)\n# Register as a composable node\nrclcpp_components_register_node(perception_component\n  PLUGIN \"my_robot_pkg::PerceptionComponent\"\n  EXECUTABLE perception_component_node\n)\ninstall(TARGETS perception_component\n  ARCHIVE DESTINATION lib\n  LIBRARY DESTINATION lib\n  RUNTIME DESTINATION bin\n)\n\n# ── Install Python nodes ───────────────────────────────────────\ninstall(PROGRAMS\n  scripts/planning_node.py\n  DESTINATION lib/${PROJECT_NAME}\n)\n\n# ── Install launch, config, rviz, urdf ─────────────────────────\ninstall(DIRECTORY\n  launch config rviz urdf\n  DESTINATION share/${PROJECT_NAME}\n)\n\n# ── Install headers ────────────────────────────────────────────\ninstall(DIRECTORY include/\n  DESTINATION include\n)\n\n# ── Tests ──────────────────────────────────────────────────────\nif(BUILD_TESTING)\n  find_package(ament_lint_auto REQUIRED)\n  ament_lint_auto_find_test_dependencies()\n\n  find_package(ament_cmake_pytest REQUIRED)\n  ament_add_pytest_test(test_perception test/test_perception.py)\n\n  find_package(launch_testing_ament_cmake REQUIRED)\n  add_launch_test(test/test_integration.py)\nendif()\n\nament_package()\n```\n\n### setup.py / setup.cfg — Pure Python Package\n\n```python\n# setup.py (for ament_python packages)\nfrom setuptools import find_packages, setup\n\npackage_name = 'my_python_pkg'\n\nsetup(\n    name=package_name,\n    version='0.1.0',\n    packages=find_packages(exclude=['test']),\n    data_files=[\n        # Register with ament index\n        ('share/ament_index/resource_index/packages',\n            ['resource/' + package_name]),\n        # Package manifest\n        ('share/' + package_name, ['package.xml']),\n        # Launch files\n        ('share/' + package_name + '/launch',\n            ['launch/robot.launch.py']),\n        # Config files\n        ('share/' + package_name + '/config',\n            ['config/params.yaml']),\n    ],\n    install_requires=['setuptools'],\n    zip_safe=True,\n    maintainer='Dev Name',\n    maintainer_email='dev@example.com',\n    description='My Python robot package',\n    license='Apache-2.0',\n    entry_points={\n        'console_scripts': [\n            # format: 'executable_name = package.module:function'\n            'perception_node = my_python_pkg.perception_node:main',\n            'planner_node = my_python_pkg.planner_node:main',\n        ],\n    },\n)\n```\n\n```cfg\n# setup.cfg\n[develop]\nscript_dir=$base/lib/my_python_pkg\n\n[install]\ninstall_scripts=$base/lib/my_python_pkg\n```\n\n### Custom Message, Service, and Action Definitions\n\n```\n# msg/Detection.msg\nstd_msgs/Header header\nstring class_name\nfloat32 confidence\ngeometry_msgs/Pose pose\nfloat32[4] bbox    # [x_min, y_min, x_max, y_max]\n```\n\n```\n# srv/GetPose.srv\nstring object_name\n---\nbool success\ngeometry_msgs/PoseStamped pose\nstring error_message\n```\n\n```\n# action/PickPlace.action\n# Goal\ngeometry_msgs/Pose target_pose\nstring object_class\n---\n# Result\nbool success\nstring error_message\n---\n# Feedback\nfloat32 progress\nstring current_phase\n```\n\n### Workspace Overlays\n\n```\nUnderlay (base ROS2)         /opt/ros/humble/\n    ↑\nOverlay 1 (shared libs)     ~/ros2_ws/install/\n    ↑\nOverlay 2 (your dev pkg)    ~/dev_ws/install/\n\nSource order matters — LAST sourced overlay wins for duplicate packages.\n```\n\n```bash\n# Correct source order\nsource /opt/ros/humble/setup.bash    # Base\nsource ~/ros2_ws/install/setup.bash  # Shared workspace\nsource ~/dev_ws/install/setup.bash   # Your development overlay\n\n# NEVER source setup.bash from build/ — always use install/\n```\n\n### Build Troubleshooting\n\n```bash\n# \"Package not found\" during build\n# → Missing dependency. Check package.xml and run:\nrosdep install --from-paths src --ignore-src -y\n\n# \"Could not find a package configuration file provided by X\"\n# → CMake can't find the package. Did you source the underlay?\nsource /opt/ros/humble/setup.bash\n\n# Build succeeds but node can't be found at runtime\n# → Forgot to source the overlay, or entry_points misconfigured\nsource install/setup.bash\nros2 pkg list | grep my_pkg      # Should appear\nros2 pkg executables my_pkg      # List available executables\n\n# Python changes not reflected after rebuild\n# → Use --symlink-install, or clean and rebuild\ncolcon build --packages-select my_pkg --symlink-install\n\n# \"Multiple packages with the same name\"\n# → Duplicate package in workspace. Check with:\ncolcon list --packages-select my_pkg\n\n# Build runs out of memory (large C++ packages)\ncolcon build --parallel-workers 2 --executor sequential\n\n# Custom messages not found by Python nodes\n# → Missing rosidl_default_runtime in package.xml exec_depend\n# → Or forgot to source install/setup.bash after building msgs\n```\n\n## Package Structure (ROS2)\n\n```\nmy_robot_pkg/\n├── CMakeLists.txt              # Or setup.py for pure Python\n├── package.xml\n├── my_robot_pkg/               # Python module (same name as package)\n│   ├── __init__.py\n│   ├── perception_node.py\n│   └── utils/\n│       └── transforms.py\n├── src/                        # C++ source\n│   └── perception_component.cpp\n├── include/my_robot_pkg/       # C++ headers\n│   └── perception_component.hpp\n├── config/\n│   ├── robot_params.yaml\n│   └── cyclonedds.xml\n├── launch/\n│   ├── robot.launch.py\n│   └── perception.launch.py\n├── msg/\n│   └── Detection.msg\n├── srv/\n│   └── GetPose.srv\n├── action/\n│   └── PickPlace.action\n├── rviz/\n│   └── robot.rviz\n├── urdf/\n│   └── robot.urdf.xacro\n└── test/\n    ├── test_perception.py      # pytest\n    └── test_integration.py     # launch_testing\n```\n\n## Debugging Toolkit\n\n```bash\n# Topic inspection\nros2 topic list\nros2 topic info /camera/image_raw -v  # Shows QoS details\nros2 topic hz /camera/image_raw\nros2 topic bw /camera/image_raw\nros2 topic echo /joint_states --once\n\n# Node inspection\nros2 node list\nros2 node info /perception\n\n# Parameter management\nros2 param list /perception\nros2 param get /perception confidence_threshold\nros2 param set /perception confidence_threshold 0.8  # Runtime change!\n\n# Lifecycle management\nros2 lifecycle list /managed_perception\nros2 lifecycle set /managed_perception configure\nros2 lifecycle set /managed_perception activate\n\n# Service calls\nros2 service list\nros2 service call /get_pose my_interfaces/srv/GetPose \"{}\"\n\n# Action monitoring\nros2 action list\nros2 action info /pick_place\nros2 action send_goal /pick_place my_interfaces/action/PickPlace \"{target_pose: {x: 1.0}}\"\n\n# Bag recording (ROS2 style)\nros2 bag record -a                              # All topics\nros2 bag record /camera/image /tf               # Specific topics\nros2 bag record -s mcap /camera/image           # MCAP format (recommended)\nros2 bag info recording/                        # Inspect\nros2 bag play recording/ --clock                # Playback\n\n# DDS debugging\nros2 doctor                                     # System diagnostics\nros2 daemon stop && ros2 daemon start           # Reset discovery daemon\n```\n\n## Production Deployment Checklist\n\n1. **Use lifecycle nodes** for all critical components\n2. **Set `ROS_LOCALHOST_ONLY=1`** if not communicating across machines\n3. **Pin your DDS implementation** (CycloneDDS recommended)\n4. **Configure QoS explicitly** — never rely on defaults for production\n5. **Set `ROS_DOMAIN_ID`** to isolate your robot from others on the network\n6. **Enable ROS2 security** (SROS2) for authenticated communication\n7. **Use composition** for nodes that exchange large data\n8. **Record bags in MCAP format** — better tooling, random access, compression\n9. **Set up launch-testing** for integration tests\n10. **Use `ros2 doctor`** as part of your health check pipeline","tags":["ros2","robotics","agent","skills","arpitg1304","agent-skills","ai-coding-assistant","claude-skills"],"capabilities":["skill","source-arpitg1304","skill-ros2","topic-agent-skills","topic-ai-coding-assistant","topic-claude-skills","topic-robotics"],"categories":["robotics-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/arpitg1304/robotics-agent-skills/ros2","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add arpitg1304/robotics-agent-skills","source_repo":"https://github.com/arpitg1304/robotics-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.544","qualityRationale":"deterministic score 0.54 from registry signals: · indexed on github topic:agent-skills · 189 github stars · SKILL.md body (29,917 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T18:54:21.374Z","embedding":null,"createdAt":"2026-04-18T22:05:39.999Z","updatedAt":"2026-05-02T18:54:21.374Z","lastSeenAt":"2026-05-02T18:54:21.374Z","tsv":"'+17':2004 '-101':1686 '-2.0':1946,2339 '/.bashrc':1629 '/camera/image':2824,2833 '/camera/image_raw':1080,2716,2724,2728 '/config':2318 '/dev_ws/install':2447 '/dev_ws/install/setup.bash':2470 '/get_pose':2788 '/joint_states':2732 '/launch':2311 '/managed_perception':2769,2773,2778 '/models/yolo.pt':863 '/opt/ros/humble':2436 '/opt/ros/humble/setup.bash':1754,2463,2528 '/org/my_robot_pkg.git':1727 '/path/to/cyclonedds.xml':1700 '/perception':2742,2748,2752,2758 '/pick_place':2799,2804 '/python':1915 '/ros2_ws':1716 '/ros2_ws/install':2441 '/ros2_ws/install/setup.bash':2466 '/ros2_ws/src':1714 '/tf':2825 '/usr/bin/env':140 '0':565,1105,1685 '0.0':198 '0.1.0':1938,2284 '0.7':203,431 '0.8':2761 '1':132,168,248,712,930,1004,1044,1667,2438,2866,2879 '1.0':193,290,2810 '10':258,453,667,1023,1066,2953 '1000.0':494 '10mb':1694,1695 '120.0':196 '17':2014 '1fhz':510 '2':17,210,566,2443,2622,2874 '3':233,924,2885 '3.0':1325 '3.8':1996 '30.0':179,425 '4':259,1112,1829,2388,2892 '42':1683 '5':282,1352,2902 '6':295,1478,2916 '65500':1693 '7':2924 '8':2933 '9':2944 'accept':1531,1555 'access':2942 'across':2883 'action':80,1326,1479,1574,2078,2373,2693,2791,2794,2797,2801 'action/pickplace.action':2092,2410 'actionserv':1486,1505 'activ':600,642,694,716,887,891,916,922,2779 'add':2028,2101,2130,2237,2250 'alloc':634 'also':1443 'alway':2479 'ament':62,65,1898,1901,1909,1921,1947,1975,1978,1983,1985,1988,2039,2106,2136,2220,2224,2232,2236,2247,2255,2265,2294 'answer':1356 'apach':1945,2338 'appear':2557 'apt':1736 'architectur':131 'archiv':2173 'arg':372,375,376,1174,1182,1190,1340,1342,1345,1832,1840,1871 'argc':550,555 'argument':1171 'argv':552,556 'async':1565 'authent':2922 'auto':443,448,865,886,1395,1977,2222,2226 'auto-activ':885 'auto-configur':864 'avail':2564 'bag':2811,2816,2822,2829,2838,2843,2935 'base':1117,1904,2434,2464 'base/lib/my_python_pkg':2364,2368 'bash':1070,1624,1696,1707,1771,2458,2484,2707 'basic':136,393 'bbox':2389 'best':5,957,960,962,965 'better':2939 'bin':2181 'bool':2402,2420 'bridg':1965,2069,2115,2147 'buffer':1013 'bug':934 'build':23,51,112,1701,1750,1761,1772,1779,1785,1793,1810,1825,1837,1842,1846,1848,1855,1859,1860,1868,1878,1887,1890,1896,2216,2478,2482,2489,2529,2581,2609,2618,2646 'bw':2727 'c':1911,1914,1927,2003,2615,2676,2680 'call':2781,2787 'callback':279,294,298,303,305,322,356,365,482,500,516,532,711,1422,1430,1511,1515,1519 'camera':208,988 'camera/image_raw':277,471,709,1240,1294,1411 'cancel':1518,1547,1556,1561,1591,1594 'cancelrespons':1487 'cancelresponse.accept':1564 'case':976,1641 'cast':493 'cb':1513,1517,1521,1524,1548,1568 'cd':1715,1721,1728 'cfg':2359 'chang':297,328,2567,2763 'changest':839,873,905 'char':551 'check':1071,1088,1590,2492,2600,2962 'checklist':2865 'choos':1620,1903 'chrono':490 'ci':1866 'ci/cd':126 'clang':2027 'class':158,406,610,1371,1493,2380,2418 'clean':762,1845,2577 'cleanup':748,773 'clock':2846 'clone':1717,1724 'cmake':63,1831,1839,1870,1899,1910,1948,1979,1984,1986,1989,1991,1992,2007,2011,2017,2022,2040,2233,2248,2516 'cmake-arg':1838,1869 'cmakelists.txt':69,1987,2654 'colcon':40,60,1706,1760,1769,1778,1792,1809,1824,1836,1858,1867,1889,2580,2602,2617 'comm':1403 'command':1007,1014 'common':10,940 'communic':1390,2882,2923 'compat':948,952 'compil':1862,2018,2024,2029 'compon':29,56,1269,1271,1353,1472,2049,2127,2133,2140,2143,2155,2159,2167,2172,2873 'components/register_node_macro.hpp':1364 'compos':1251,1258,1277,1348,2128,2152 'composablenod':1162,1280,1296 'composablenodecontain':1157,1260 'composit':1189,1193,1250,1310,1344,2926 'comprehens':4 'compress':2943 'condit':86,1125,1246,1306 'confid':201,223,338,429,2383,2753,2759 'config':1208,1214,1233,1291,2194,2200,2313,2683 'config/params.yaml':2319 'configur':88,628,672,687,866,869,884,890,920,1619,2511,2774,2893 'connect':946 'consol':1894,2342 'const':475,517,1379 'contain':57,1259,1263,1272,1349 'copi':528,1255,1394,1440,1463,1805 'core':130 'correct':2459 'could':2506 'count':1101 'cpp':396,1359,1637,1647 'creat':461,469,486,624,1409,1708 'critic':2872 'current':2429 'custom':76,1919,1929,2075,2369,2625 'cv':1964,2068,2114,2146 'cxx':1873,2008,2012,2023 'cyclonedd':1636,1687,1698,2890 'cyclonedds.xml':1689,2685 'daemon':2855,2858,2862 'data':987,1028,2290,2932 'dcmake':1841,1872 'dds':89,107,1618,1622,1626,1655,2848,2888 'deactiv':723,741 'debug':31,106,1067,1886,2705,2849 'decid':1528,1552 'declar':169,416,421,427,1731,1932 'declarelaunchargu':1137,1175,1183,1191 'def':161,320,354,363,370,613,626,692,721,746,766,783,844,1167,1496,1522,1546,1566 'default':1051,1178,1185,1194,1648,1816,1967,1970,2001,2082,2634,2899 'defin':75 'definit':2374 'del':757 'delay':1311,1321,1350 'depend':1730,1791,1933,2036,2093,2108,2138,2229,2491,2639 'deploy':114,2864 'depth':247,257,1003,1022,1043,1065 'descript':174,182,847,1170,1279,2332 'descriptor':180 'design':7,134 'despit':1107 'destin':2123,2174,2177,2180,2188,2203,2212 'destruct':775 'det':458,536 'detail':2720 'detect':269,463,666,1242 'detection.msg':2690 'detectionarray':268,665 'determinist':579 'dev':1943,2327,2445 'dev@example.com':2331 'develop':3,18,44,2361,2472 'diagnost':2853 'dir':2363 'direct':1895 'directori':2198,2210 'discoveri':108,1656,2861 'doctor':2851,2956 'domain':1670,1681,2905 'doubl':432,441 'drop':991 'duplic':2456,2596 'durabl':1005,1024,1045,1086 'dynam':330 'e':682,689 'echo':2731 'edit':1806 'effort':243,958,961,963,966,999 'email':2330 'embed':119 'emitev':834,871,903 'enabl':1123,1406,2917 'endif':2015,2034,2254 'entiti':902 'entri':2340,2545 'enumer':1587 'error':583,685,785,790,799,801,1865,2408,2423 'essenti':1768 'eth0':1691 'event':870,872,904,921,1092,1892 'event-handl':1891 'except':382,679,680 'exchang':2930 'exclud':2288 'exec':2638 'execut':853,1224,1270,1331,1510,1567,1572,2099,2102,2165,2345,2560,2565 'executor':1578,2623 'explicit':1377,2895 'export':1632,1642,1663,1679,1697 'f':309,317,345,671,686,800,1539 'fail':688,795,944 'failur':113,970,1888 'fals':1187,1600,1692 'faster':1776 'fastrtp':1646 'featur':593 'feedback':1580,1610,1611,2425 'feedback_msg.progress':1604 'field':1087 'file':28,84,816,1114,1120,1209,1234,1292,1734,1802,2291,2307,2314,2512 'final':385,601,772 'find':2035,2037,2042,2046,2051,2056,2061,2066,2071,2079,2218,2227,2230,2243,2271,2286,2508,2519 'findpackageshar':1166,1211 'first':261,1320,1759 'flag':1770,1874 'float':187,1605 'float32':2382,2387,2426 'floatingpointrang':190 'forgot':2539,2641 'format':2344,2835,2938 'found':2487,2536,2628 'frame':206,230 'framework':105 'from-path':1742,2498 'function':2348 'generat':845,1168,1968,2083,2086 'geometri':1960,2058,2094,2384,2404,2412 'get':418,436,504,1033,2751 'getpose.srv':2692 'git':1723 'github.com':1726 'github.com/org/my_robot_pkg.git':1725 'gnucxx':2020 'go':701 'goal':899,1514,1523,1526,1535,1541,1550,1570,2411,2803 'goal_handle.canceled':1596 'goal_handle.is':1593 'goal_handle.publish':1609 'goal_handle.request':1589 'goal_handle.succeed':1613 'goal_request.target':1542 'goalrespons':1488 'goalresponse.accept':1545 'good':1649 'grace':796 'grep':2553 'group':1675 'groupact':1139 'handl':325,789,1551,1571 'handler':828,892,923,1893 'header':2208,2378,2681 'health':2961 'histori':244,254,1000,1019,1040,1056,1062 'historypolici':153 'historypolicy.keep':245,255 'hz':178,186,214,218,292,315,316,424,434,439,496,512,2723 'id':207,227,231,882,914,1671,1682,2025,2906 'ifcondit':1145,1307 'ignor':1747,2503 'ignore-src':1746,2502 'imag':276,355,361,466,481,515,541,708,718,1418,1434 'implement':94,1623,1634,1644,2889 'import':142,146,150,156,592,605,820,824,829,833,838,842,980,1128,1132,1136,1144,1149,1155,1161,1165,1485,1491,2270 'inact':599,901 'includ':397,399,402,405,1360,1362,1365,2211,2213 'include/my_robot_pkg':2679 'includelaunchdescript':1138 'incom':360 'incompat':1090 'index':2295 'info':308,344,502,622,670,715,740,761,778,1073,1079,1538,1560,2715,2741,2798,2839 'init':162,165,554,614,617,1497,1500,2670 'initi':1319 'inspect':2709,2735,2841 'instal':1729,1741,1813,1849,1856,2119,2169,2182,2185,2192,2197,2207,2209,2320,2365,2366,2481,2497,2575,2589 'install/setup.bash':1767,2549,2644 'instead':1803 'int':547,549 'integr':2951 'interfac':1973,2087 'interfaces/action/pickplace':2806 'interfaces/srv/getpose':2790 'intra':1388,1401,1452 'intra-process':1387,1451 'intraprocessset':1405 'isol':1673,2908 'issu':1069 'iter':1777 'job':1815 'joiner':1032 'joint':1009 'keyboardinterrupt':383 'lambda':877,909 'languag':1908 'larg':2614,2931 'last':246,256,1002,1021,1042,1064,2451 'latch':1050 'late':1031 'latest':993 'launch':27,83,815,819,846,1113,1119,1131,1169,1631,1981,2193,2199,2245,2251,2306,2686,2703,2948 'launch-test':2947 'launch.actions':832,1135 'launch.conditions':1143 'launch.substitutions':1148 'launch/robot.launch.py':2312 'launch_ros.actions':823,1154 'launch_ros.descriptions':1160 'launch_ros.event':827 'launch_ros.events.lifecycle':837 'launch_ros.substitutions':1164 'launchconfigur':1150,1199,1203,1248,1308 'launchdescript':821,918,1133,1337 'len':1607 'lib':2124,2175,2178,2189,2440 'librari':2131,2176 'licens':2337 'lidar':989 'lifecycl':95,567,571,663,811,874,896,906,2764,2767,2771,2776,2868 'lifecycle_msgs.msg':841 'lifecyclenod':608,612,825,849 'limit':1654 'link':209 'lint':1976,2221,2225 'list':2552,2563,2603,2712,2738,2747,2768,2784,2795 'live':702 'load':632,656,1205 'loadcomposablenod':1158 'local':1047,1658 'localhost':1665,2877 'log':1850,1857 'logger':307,343,505,621,669,684,714,739,760,777,798,1537,1559 'logic':87,1126 'look':1082 'lower':1818 'machin':597,1653,1659,2884 'main':371,391,392,548,2353,2358 'maintain':2326,2329 'make':560 'manag':96,568,618,854,2744,2765 'managedpercept':611 'manifest':2301 'map':1026,1035 'match':2026 'matcher':876,908 'matrix':949 'matter':2450 'max':2395,2397 'mcap':2832,2834,2937 'memori':635,2613 'mention':38 'messag':77,1930,2076,2370,2409,2424,2626 'micro':122 'micro-ro':121 'middlewar':90,1627 'millisecond':491 'min':2391,2393 'minimum':1993 'misconfigur':2547 'mismatch':110,936,1111 'miss':1011,2490,2632 'mix':1913 'mkdir':1712 'model':645,647,651,657,658,674,675,861 'modul':2665 'monitor':2792 'move':1424,1461 'moveit2':101 'msg':358,479,483,521,1417,1420,1425,1433,1436,1459,1581,1612,2689 'msg/detection.msg':2090,2375 'msgs':1416,1432,1920,1951,1955,1959,1961,2054,2059,2095,2097,2113,2145,2647 'msgs/header':2377 'msgs/msg/detection2_d.hpp':404 'msgs/msg/image.hpp':401,1367 'msgs/pose':2385,2413 'msgs/posestamped':2405 'mt':1273 'multi':1275,1652 'multi-machin':1651 'multi-thread':1274 'multipl':2590 'my_interfaces.action':1490 'my_python_pkg.perception':2351 'my_python_pkg.planner':2356 'name':390,856,1173,1177,1198,1201,1227,1231,1261,1266,1288,1304,1334,1339,1944,2089,2126,2191,2206,2275,2280,2282,2299,2304,2310,2317,2328,2346,2381,2401,2595,2667 'namespac':1229,1264,1368,1468 'nav2':100 'need':578,1316 'network':1661,1678,2915 'never':1010,2474,2896 'node':25,54,97,133,137,147,160,167,311,377,381,387,394,410,413,415,507,569,572,606,623,812,875,878,879,897,907,910,911,1108,1156,1217,1219,1220,1226,1252,1278,1314,1327,1333,1347,1375,1383,1474,1495,2100,2104,2110,2122,2129,2153,2157,2168,2184,2350,2352,2355,2357,2532,2631,2734,2737,2740,2869,2928 'node.destroy':386 'nodelet':1358 'nodeopt':1381 'nois':1662 'none':373 'nproc':1817 'object':2400,2417 'one':587 'onstatetransit':830,894 'opaquefunct':1140 'opencv':1952,2073,2116,2148 'oper':15 'option':1382,1385,1397,1427,2030 'orchestr':810 'order':2449,2461 'os':1129 'other':1317,2912 'output':858,1244,1879 'overlay':1765,2432,2437,2442,2453,2473,2543 'p':1713 'packag':26,53,67,129,850,1221,1267,1281,1297,1328,1718,1775,1781,1787,1795,1835,1907,1912,1916,1917,1925,1942,1974,1990,2038,2043,2047,2052,2057,2062,2067,2072,2080,2219,2231,2244,2256,2261,2267,2272,2274,2281,2285,2287,2298,2300,2303,2309,2316,2336,2457,2485,2510,2521,2583,2591,2597,2605,2616,2648,2669 'package.module':2347 'package.xml':70,1733,1931,2305,2493,2637,2660 'packages-select':1780,2582,2604 'packages-up-to':1794 'parallel':1814,1827,2620 'parallel-work':1826,2619 'param':321,324,333,335,633,1207,2746,2750,2756 'param.name':337 'param.value':341,349 'paramet':170,176,200,205,212,216,222,229,296,302,327,419,422,428,437,644,650,860,1232,1290,2743 'parameter/state':1052 'parameterdescriptor':181 'part':2958 'pass':362,369,384,1830 'path':646,648,652,659,676,862,1744,2500 'pathjoinsubstitut':1151,1210 'pattern':8,135 'paus':727 'percept':166,310,414,506,619,848,855,857,880,898,912,919,1218,1225,1228,1262,1289,1346,1384,1941,2103,2109,2121,2132,2139,2158,2166,2171,2241,2349 'perception.launch.py':2688 'perception/detections':1243 'perception_component.cpp':2678 'perception_component.hpp':2682 'perception_node.py':2672 'perceptioncompon':1287,1372,1378,1477,2164 'perceptionnod':159,378,407,412 'period':285,367,1324 'phase':2430 'pick':1501,1508 'pickplac':1492,1507 'pickplace.action':2694 'pickplace.feedback':1582 'pickplace.result':1598,1615 'pickplaceserv':1494 'pin':2886 'pipelin':2963 'pitfal':11 'pkg':852,1213,1223,1283,1286,1299,1302,1330,1370,1470,1476,1784,1799,1937,2000,2163,2278,2446,2551,2555,2559,2562,2586,2608,2653,2663 'place':1502,1509 'planner':1322,1332,1335,1351,2354 'play':2844 'playback':2847 'plugin':1284,1300,2160 'point':188,2341,2546 'pose':1543,2386,2406,2415,2808 'potenti':529 'power':1124 'practic':6 'privat':513,1428 'process':183,359,699,717,728,743,1257,1389,1402,1453 'product':117,574,2863,2901 'profil':93,237,973 'program':2186 'progress':2427 'project':1997,2088,2125,2190,2205 'provid':2513 'ptr':478,520,524 'pub':265,459,537,661 'public':408,411,1373,1376 'publish':260,267,455,462,534,664,950,1442,1449 'pubs/subs':638 'pure':1923,2259,2658 'py':2671 'pytest':1980,2234,2238,2701 'python':66,82,139,602,817,977,1116,1122,1127,1482,1801,1902,1922,1924,2183,2260,2262,2266,2277,2334,2566,2630,2659,2664 'python-bas':1115 'python3':141 'pythonexpress':1152 'qos':92,109,236,239,250,271,281,442,445,450,452,465,473,925,935,947,972,995,1015,1036,1058,1068,1072,1091,1103,1110,2719,2894 'qosdurabilitypolici':984 'qosdurabilitypolicy.transient':1046 'qosdurabilitypolicy.volatile':1006,1025 'qoshistorypolici':983 'qoshistorypolicy.keep':1001,1020,1041,1063 'qospresetprofil':985 'qosprofil':151,240,251,981,996,1016,1037,1059 'qosreliabilitypolici':982 'qosreliabilitypolicy.best':998 'qosreliabilitypolicy.reliable':1018,1039,1061 'qualiti':926 'ram':1823 'random':2941 'rang':189,1684 'rate':177,184,213,217,291,314,423,433,438,495,511 'rclc':42 'rclcpp':395,409,446,451,501,533,538,543,553,557,562,1268,1363,1374,1380,1398,1404,1412,1464,1471,1949,1953,1957,2044,2048,2111,2141,2142,2154 'rclcpp/rclcpp.hpp':398,1361 'rclpi':41,138,143,1956 'rclpy.action':1484 'rclpy.init':374 'rclpy.lifecycle':604 'rclpy.node':145 'rclpy.qos':149,979 'rclpy.shutdown':388 'rclpy.spin':380 'read':211 'real':1882 'real-tim':1881 'realsense/color/image_raw':1241,1295 'reason':941 'rebuild':1808,1852,2571,2579 'receiv':1540 'recommend':971,1638,2836,2891 'reconfigur':331,731 'record':2812,2817,2823,2830,2840,2845,2934 'recov':793 'recoveri':584 'reduc':1660 'reflect':2569 'regist':1473,2149,2156,2292 'registereventhandl':835,893 'reject':1533 'releas':752,1844 'reli':2897 'reliabilitypolici':152 'reliabilitypolicy.best':242 'reliabilitypolicy.reliable':253 'reliabl':241,249,252,270,449,454,464,953,954,956,967,997,1017,1029,1038,1053,1060,1084 'remap':1239,1293 'remov':1847 'replac':329,1048 'request':1527,1557,1562,1595 'requir':1994,2041,2045,2050,2055,2060,2065,2070,2074,2084,2223,2235,2249,2321 'reset':2860 'resourc':753,2297 'result':2419 'return':350,564,677,690,719,744,754,764,781,805,917,1336,1544,1563,1597,1614 'rf':1854 'rm':1853 'rmw':1633,1635,1643,1645 'robot':14,1172,1176,1197,1200,1230,1265,1338,1674,1936,1940,1999,2162,2335,2652,2662,2910 'robot.launch.py':2687 'robot.rviz':2696 'robot.urdf.xacro':2698 'robot_params.yaml':1215,2684 'ros':123,1664,1669,1680,1963,2064,2118,2876,2904 'ros1':595,1049 'ros2':2,13,24,32,39,43,52,74,104,115,128,589,933,1077,1093,1118,1354,1480,1710,1757,2435,2550,2558,2650,2710,2713,2721,2725,2729,2736,2739,2745,2749,2755,2766,2770,2775,2782,2785,2793,2796,2800,2813,2815,2821,2828,2837,2842,2850,2854,2857,2918,2955 'ros2-development':1 'rosdep':1738,1740,2496 'rosidl':1966,1969,1972,2081,2085,2633 'rqt':1095,1097 'run':1094,1109,1575,1820,2495,2610 'runtim':326,1971,2179,2538,2635,2762 'rviz':2195,2201,2695 'safe':729,2324 'screen':859,1245 'script':2343,2362,2367 'scripts/planning_node.py':2187 'secur':2919 'select':1782,2584,2606 'self':163,323,357,366,615,629,695,724,749,769,786,1498,1506,1525,1549,1569 'self._action_server':1504 'self.add':299 'self.cancel':1520 'self.create':266,274,288,662,706 'self.declare':175,199,204,643 'self.destroy':734 'self.det':264,660 'self.execute':1512,1601 'self.frame':226 'self.get':215,221,228,306,342,620,649,668,683,713,738,759,776,797,1536,1558 'self.goal':1516 'self.image':272,278,704,710,736 'self.model':655,758 'self.param':304 'self.plan':1588 'self.steps':1608 'self.threshold':220,319,340 'self.timer':287,293 'send':2802 'sensor':238,280,400,444,472,986,994,1366,1415,1431,1950,1954,1958,2053,2096,2112,2144 'sensordataqo':447,1413 'sequenti':2624 'server':1503 'servic':78,928,2077,2371,2780,2783,2786 'set':58,124,234,301,636,1625,2010,2757,2772,2777,2875,2903,2945 'setparametersresult':351 'setup':1704,2273,2279 'setup.bash':2476 'setup.cfg':2258,2360 'setup.py':72,2257,2263,2656 'setuptool':2269,2322 'share':477,519,523,561,2134,2204,2302,2308,2315,2439,2467 'share/ament_index/resource_index/packages':2296 'sharedptr':535,540,545,1466 'show':1099,1877,2718 'shut':779 'shutdown':563,581,768 'silent':943,969 'sim':1181,1184,1202,1204,1236,1238,1341 'skill':21,45,50 'skill-ros2' 'small':1012 'sourc':931,1753,1755,1762,1766,2448,2452,2460,2462,2465,2469,2475,2524,2527,2541,2548,2643,2677 'source-arpitg1304' 'specif':1774,2826 'spin':558 'src':1720,1722,1745,1748,2501,2504,2675 'src/perception_component.cpp':2135 'src/perception_node.cpp':2105 'sros2':2920 'srv':2691 'srv/getpose.srv':2091,2398 'standalon':2098 'standard':1216,2009,2013 'start':312,508,698,1312,2859 'startup':580,868 'state':596,630,696,725,750,770,787,803,900,1057 'state.label':804 'static':492,1027 'std':476,489,518,559,1423,2376 'std_msgs.msg':155 'step':197,1585,1602,1603 'stop':742,2856 'string':157,2379,2399,2407,2416,2422,2428 'structur':2649 'style':1481,2814 'sub':273,467,542,705,737,1100,1396,1407,1426,1467 'sub_options.use':1400 'subscrib':263,457,951,1106,1447,1458 'subscript':275,470,539,700,707,735,1410,1465 'subscriptionopt':1399 'succeed':2530 'success':352,889,1599,1616,2403,2421 'sudo':1735 'super':164,616,1499 'symlink':1800,1812,2574,2588 'symlink-instal':1811,2573,2587 'system':16,33,120,575,1702,2852 'target':895,2107,2120,2137,2170,2414,2807 'test':1982,2214,2217,2228,2239,2240,2246,2252,2289,2699,2704,2949,2952 'test/test_integration.py':2253 'test/test_perception.py':2242 'test_integration.py':2702 'test_perception.py':2700 'tf2':1962,2063,2117 'thread':1276,1579 'threshold':202,224,318,339,346,430,2754,2760 'time':1237,1883 'timer':283,289,364,484,488,499,531,546 'timeract':1141,1323 'timerbas':544 'toler':990 'tool':2940 'toolkit':2706 'topic':942,1076,1078,1096,1098,2708,2711,2714,2722,2726,2730,2820,2827 'topic-agent-skills' 'topic-ai-coding-assistant' 'topic-claude-skills' 'topic-robotics' 'tracker':1305 'trackercompon':1303 'transforms.py':2674 'transit':807,843,881,913 'transition.transition':883,915 'transitioncallbackreturn':609,631,697,726,751,771,788 'transitioncallbackreturn.failure':691 'transitioncallbackreturn.success':678,720,745,765,782,806 'tri':379,654,791 'trigger':34 'troubleshoot':2483 'true':353,1196,1617,2325 'tune':1688 'type':172,1843,1897 'unconfigur':598,625,756,809 'underlay':1758,2433,2526 'uniqueptr':1419,1435,1437,1445 'unlesscondit':1146,1247 'updat':347,1737,1739 'ur5':1180 'urdf':2196,2202,2697 'uri':1699 'use':19,48,522,570,975,1188,1192,1235,1249,1309,1343,1386,1444,1450,1668,1884,2480,2572,2867,2925,2954 'user':37 'util':2673 'v':1081,2717 'valu':192,195,219,225,232,653,1179,1186,1195 'veloc':1008 'version':1995,2283 'vision':403 'void':514,530,1429 'vs':1900 'wall':487,1875,2031 'want':992 'warn':1863 'werror':1876 'wextra':2032 'whenev':35 'whether':1529,1553 'win':2454 'without':1807 'work':98,286,368 'worker':1828,2621 'workspac':61,1703,1711,1752,1764,2431,2468,2599 'wpedant':2033 'write':68,81 'x':2390,2394,2515,2809 'xml':1690,1934 'y':1749,2392,2396,2505 'yaml':1206 'yes':955,959,964 'zero':527,1254,1393,1439 'zero-copi':526,1253,1392,1438 'zip':2323","prices":[{"id":"54c2c403-c941-4f8c-a65e-c842128bb075","listingId":"a7221fcf-fb00-4099-904b-f7b3cf8d5d46","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"arpitg1304","category":"robotics-agent-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:05:39.999Z"}],"sources":[{"listingId":"a7221fcf-fb00-4099-904b-f7b3cf8d5d46","source":"github","sourceId":"arpitg1304/robotics-agent-skills/ros2","sourceUrl":"https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2","isPrimary":false,"firstSeenAt":"2026-04-18T22:05:39.999Z","lastSeenAt":"2026-05-02T18:54:21.374Z"}],"details":{"listingId":"a7221fcf-fb00-4099-904b-f7b3cf8d5d46","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"arpitg1304","slug":"ros2","github":{"repo":"arpitg1304/robotics-agent-skills","stars":189,"topics":["agent-skills","ai-coding-assistant","claude-skills","robotics"],"license":"apache-2.0","html_url":"https://github.com/arpitg1304/robotics-agent-skills","pushed_at":"2026-03-25T03:44:12Z","description":"Agent skills that make AI coding assistants write production-grade robotics software. ROS1, ROS2, design patterns, SOLID principles, and testing — for Claude Code, Cursor, Copilot, and any SKILL.md-compatible agent.","skill_md_sha":"60faf1b6181325d340d661be08d68dd851b078e2","skill_md_path":"skills/ros2/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2"},"layout":"multi","source":"github","category":"robotics-agent-skills","frontmatter":{"name":"ros2-development","description":"Comprehensive 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, rclcpp, DDS, QoS, lifecycle nodes, managed nodes, ROS2 launch, ROS2 parameters, ROS2 actions, nav2, MoveIt2, micro-ROS, or any ROS2-era robotics middleware. Also trigger for ROS2 workspace setup, DDS tuning, intra-process communication, ROS2 security, or deploying ROS2 in production. Also trigger for colcon build issues, ament_cmake, ament_python, CMakeLists.txt for ROS2, package.xml dependencies, rosdep, workspace overlays, custom message generation, or ROS2 build troubleshooting. Covers Humble, Iron, Jazzy, and Rolling distributions."},"skills_sh_url":"https://skills.sh/arpitg1304/robotics-agent-skills/ros2"},"updatedAt":"2026-05-02T18:54:21.374Z"}}