pybind11 Release Preparation
pybind/pybind11
Opens the pybind11 release-preparation pull request: picking the release base, bumping the version in common.h and integrating the changelog, following docs/release.rst.
Comprehensive best practices, design patterns, and common pitfalls for ROS2 (Robot Operating System 2) development.
$ npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install arpitg1304/robotics-agent-skills ros2 --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/arpitg1304/robotics-agent-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/ros2 .claude/skills/ros2 && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "ros2" agent skill from https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2 into .claude/skills/ros2/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ros2", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2Type this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install arpitg1304/robotics-agent-skills ros2 --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/arpitg1304/robotics-agent-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/ros2 .agents/skills/ros2 && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "ros2" agent skill from https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2 into .agents/skills/ros2/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ros2", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install arpitg1304/robotics-agent-skills ros2 --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/arpitg1304/robotics-agent-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/ros2 .cursor/skills/ros2 && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "ros2" agent skill from https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2 into .cursor/skills/ros2/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ros2", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/arpitg1304/robotics-agent-skills.git --path skills/ros2--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install arpitg1304/robotics-agent-skills ros2 --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/arpitg1304/robotics-agent-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/ros2 .gemini/skills/ros2 && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "ros2" agent skill from https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2 into .gemini/skills/ros2/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ros2", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install arpitg1304/robotics-agent-skills ros2Installs for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/arpitg1304/robotics-agent-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/ros2 .github/skills/ros2 && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "ros2" agent skill from https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2 into .github/skills/ros2/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ros2", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install arpitg1304/robotics-agent-skills ros2 --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/arpitg1304/robotics-agent-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/ros2 .opencode/skills/ros2 && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "ros2" agent skill from https://github.com/arpitg1304/robotics-agent-skills/tree/main/skills/ros2 into .opencode/skills/ros2/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "ros2", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
ros2Comprehensive best practices, design patterns, and common pitfalls for ROS2 (Robot Operating System 2) development.
Ros2 is an agent skill from arpitg1304/robotics-agent-skills. 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…
Its SKILL.md is about 7.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Development, covering Design patterns. It works with Python and C++. The repository describes itself as: Agent skills that make AI coding assistants write production-grade robotics software. ROS1, ROS2, design patterns, SOLID principles, and testing — for Claude Code, Cursor… The licence is Apache-2.0.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit f9bc546. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitaptFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
cdds.iogithub.comdownload.ros.orgw3.orgFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Ros2 loads about 7.7k tokens when it runs. Until then it costs about 206 tokens; SKILL.md has 344 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
sudo apt updateAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from arpitg1304/robotics-agent-skills at commit f9bc546, republished under its Apache-2.0 licence (© arpitg1304). 344 words, ~7,697 tokens.
.claude/skills/ros2/SKILL.md (or your agent's skills folder).Basic Node (rclpy):
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
from std_msgs.msg import String
class PerceptionNode(Node):
def __init__(self):
super().__init__('perception_node')
# 1. Declare parameters with types and descriptions
self.declare_parameter('rate_hz', 30.0,
descriptor=ParameterDescriptor(
description='Processing rate in Hz',
floating_point_range=[FloatingPointRange(
from_value=1.0, to_value=120.0, step=0.0
)]
))
self.declare_parameter('confidence_threshold', 0.7)
self.declare_parameter('frame_id', 'camera_link')
# 2. Read parameters
rate_hz = self.get_parameter('rate_hz').value
self.threshold = self.get_parameter('confidence_threshold').value
self.frame_id = self.get_parameter('frame_id').value
# 3. Set up QoS profiles
sensor_qos = QoSProfile(
reliability=ReliabilityPolicy.BEST_EFFORT,
history=HistoryPolicy.KEEP_LAST,
depth=1
)
reliable_qos = QoSProfile(
reliability=ReliabilityPolicy.RELIABLE,
history=HistoryPolicy.KEEP_LAST,
depth=10
)
# 4. Publishers first, then subscribers
self.det_pub = self.create_publisher(
DetectionArray, 'detections', reliable_qos)
self.image_sub = self.create_subscription(
Image, 'camera/image_raw', self.image_callback, sensor_qos)
# 5. Timers for periodic work
self.timer = self.create_timer(1.0 / rate_hz, self.timer_callback)
# 6. Parameter change callback
self.add_on_set_parameters_callback(self.param_callback)
self.get_logger().info(
f'Perception node started at {rate_hz}Hz, '
f'threshold={self.threshold}')
def param_callback(self, params):
"""Handle runtime parameter changes (replaces dynamic_reconfigure)"""
for param in params:
if param.name == 'confidence_threshold':
self.threshold = param.value
self.get_logger().info(f'Threshold updated to {param.value}')
return SetParametersResult(successful=True)
def image_callback(self, msg):
# Process incoming images
pass
def timer_callback(self):
# Periodic work
pass
def main(args=None):
rclpy.init(args=args)
node = PerceptionNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()Basic Node (rclcpp):
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/image.hpp>
#include <vision_msgs/msg/detection2_d.hpp>
#include <memory>
class PerceptionNode : public rclcpp::Node {
public:
PerceptionNode() : Node("perception_node") {
// Declare and get parameters
this->declare_parameter("rate_hz", 30.0);
this->declare_parameter("confidence_threshold", 0.7);
double rate_hz = this->get_parameter("rate_hz").as_double();
// QoS
auto sensor_qos = rclcpp::SensorDataQoS();
auto reliable_qos = rclcpp::QoS(10).reliable();
// Publishers and subscribers
det_pub_ = this->create_publisher<vision_msgs::msg::Detection2D>("detections", reliable_qos);
image_sub_ = this->create_subscription<sensor_msgs::msg::Image>(
"camera/image_raw", sensor_qos, [this](const std::shared_ptr<const sensor_msgs::msg::Image>& msg){
this->image_callback(msg);
});
timer_ = this->create_wall_timer(
std::chrono::milliseconds(static_cast<int>(1000.0 / rate_hz)),
[this](){ this->timer_callback(); });
RCLCPP_INFO(this->get_logger(), "Perception node started at %.1fHz", rate_hz);
}
private:
void image_callback(const std::shared_ptr<const sensor_msgs::msg::Image>& msg) {
// Use shared_ptr for zero-copy potential
}
void timer_callback() {}
rclcpp::Publisher<vision_msgs::msg::Detection2D>::SharedPtr det_pub_;
rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr image_sub_;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<PerceptionNode>());
rclcpp::shutdown();
return 0;
}Use 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.
State Machine: Unconfigured → Inactive → Active → Finalized
from rclpy.lifecycle import Node as LifecycleNode, TransitionCallbackReturn
class ManagedPerception(LifecycleNode):
def __init__(self):
super().__init__('managed_perception')
self.get_logger().info('Node created (unconfigured)')
def on_configure(self, state) -> TransitionCallbackReturn:
"""Load params, allocate memory, set up pubs/subs (but don't activate)"""
self.declare_parameter('model_path', '')
model_path = self.get_parameter('model_path').value
try:
self.model = load_model(model_path)
self.det_pub = self.create_lifecycle_publisher(
DetectionArray, 'detections', 10)
self.get_logger().info(f'Configured with model: {model_path}')
return TransitionCallbackReturn.SUCCESS
except Exception as e:
self.get_logger().error(f'Configuration failed: {e}')
return TransitionCallbackReturn.FAILURE
def on_activate(self, state) -> TransitionCallbackReturn:
"""Start processing — subscriptions go live here"""
self.image_sub = self.create_subscription(
Image, 'camera/image_raw', self.image_callback, 1)
self.get_logger().info('Activated — processing images')
return TransitionCallbackReturn.SUCCESS
def on_deactivate(self, state) -> TransitionCallbackReturn:
"""Pause processing — safe to reconfigure after this"""
self.destroy_subscription(self.image_sub)
self.get_logger().info('Deactivated — stopped processing')
return TransitionCallbackReturn.SUCCESS
def on_cleanup(self, state) -> TransitionCallbackReturn:
"""Release resources, return to unconfigured"""
del self.model
self.get_logger().info('Cleaned up')
return TransitionCallbackReturn.SUCCESS
def on_shutdown(self, state) -> TransitionCallbackReturn:
"""Final cleanup before destruction"""
self.get_logger().info('Shutting down')
return TransitionCallbackReturn.SUCCESS
def on_error(self, state) -> TransitionCallbackReturn:
"""Handle errors — try to recover or fail gracefully"""
self.get_logger().error(f'Error in state {state.label}')
return TransitionCallbackReturn.SUCCESS # Transition to unconfiguredOrchestrating Lifecycle Nodes with a launch file:
from launch import LaunchDescription
from launch_ros.actions import LifecycleNode
from launch_ros.event_handlers import OnStateTransition
from launch.actions import EmitEvent, RegisterEventHandler
from launch_ros.events.lifecycle import ChangeState
from lifecycle_msgs.msg import Transition
def generate_launch_description():
perception = LifecycleNode(
package='my_pkg', executable='managed_perception',
name='perception', output='screen',
parameters=[{'model_path': '/models/yolo.pt'}]
)
# Auto-configure on startup
configure_event = EmitEvent(event=ChangeState(
lifecycle_node_matcher=lambda node: node == perception,
transition_id=Transition.TRANSITION_CONFIGURE
))
# Auto-activate after successful configure
activate_handler = RegisterEventHandler(OnStateTransition(
target_lifecycle_node=perception,
goal_state='inactive',
entities=[EmitEvent(event=ChangeState(
lifecycle_node_matcher=lambda node: node == perception,
transition_id=Transition.TRANSITION_ACTIVATE
))]
))
return LaunchDescription([
perception,
configure_event,
activate_handler,
])QoS mismatches are the most common reason topics silently fail to connect.
QoS Compatibility Matrix:
Publisher Subscriber Compatible?
RELIABLE RELIABLE ✅ Yes
RELIABLE BEST_EFFORT ✅ Yes
BEST_EFFORT BEST_EFFORT ✅ Yes
BEST_EFFORT RELIABLE ❌ NO — SILENT FAILURERecommended QoS Profiles by Use Case:
from rclpy.qos import (
QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy,
QoSDurabilityPolicy, QoSPresetProfiles
)
# Sensor data (cameras, lidars) — tolerate drops, want latest
SENSOR_QOS = QoSProfile(
reliability=QoSReliabilityPolicy.BEST_EFFORT,
history=QoSHistoryPolicy.KEEP_LAST,
depth=1,
durability=QoSDurabilityPolicy.VOLATILE
)
# Commands (velocity, joint) — never miss, small buffer
COMMAND_QOS = QoSProfile(
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=10,
durability=QoSDurabilityPolicy.VOLATILE
)
# Map / static data — reliable, and late joiners get it
MAP_QOS = QoSProfile(
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=1,
durability=QoSDurabilityPolicy.TRANSIENT_LOCAL # Replaces ROS1 latch
)
# Default parameter/state — reliable with some history
STATE_QOS = QoSProfile(
reliability=QoSReliabilityPolicy.RELIABLE,
history=QoSHistoryPolicy.KEEP_LAST,
depth=10
)Debugging QoS Issues:
# Check QoS info for a topic
ros2 topic info /camera/image_raw -v
# Look for "Reliability" and "Durability" fields
# Check for incompatible QoS events
ros2 run rqt_topic rqt_topic # Shows sub counts and QoS
# If 0 subscribers despite nodes running: QoS MISMATCHROS2 launch files are Python, enabling powerful conditional logic:
import os
from launch import LaunchDescription
from launch.actions import (
DeclareLaunchArgument, IncludeLaunchDescription,
GroupAction, OpaqueFunction, TimerAction
)
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import (
LaunchConfiguration, PathJoinSubstitution,
PythonExpression
)
from launch_ros.actions import Node, ComposableNodeContainer, LoadComposableNode
from launch_ros.descriptions import ComposableNode
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
# Arguments
robot_name_arg = DeclareLaunchArgument('robot_name', default_value='ur5')
sim_arg = DeclareLaunchArgument('sim', default_value='false')
use_composition_arg = DeclareLaunchArgument('use_composition', default_value='true')
robot_name = LaunchConfiguration('robot_name')
sim = LaunchConfiguration('sim')
# Load YAML params
config_file = PathJoinSubstitution([
FindPackageShare('my_pkg'), 'config', 'robot_params.yaml'
])
# Standard node
perception_node = Node(
package='my_pkg',
executable='perception_node',
name='perception',
namespace=robot_name,
parameters=[config_file, {'use_sim_time': sim}],
remappings=[
('camera/image_raw', 'realsense/color/image_raw'),
('detections', 'perception/detections'),
],
output='screen',
condition=UnlessCondition(LaunchConfiguration('use_composition')),
)
# Composable nodes (zero-copy, same process)
composable_container = ComposableNodeContainer(
name='perception_container',
namespace=robot_name,
package='rclcpp_components',
executable='component_container_mt', # Multi-threaded
composable_node_descriptions=[
ComposableNode(
package='my_pkg',
plugin='my_pkg::PerceptionComponent',
name='perception',
parameters=[config_file],
remappings=[
('camera/image_raw', 'realsense/color/image_raw'),
],
),
ComposableNode(
package='my_pkg',
plugin='my_pkg::TrackerComponent',
name='tracker',
),
],
condition=IfCondition(LaunchConfiguration('use_composition')),
)
# Delayed start for nodes that need others to initialize first
delayed_planner = TimerAction(
period=3.0,
actions=[
Node(package='my_pkg', executable='planner_node', name='planner')
]
)
return LaunchDescription([
robot_name_arg, sim_arg, use_composition_arg,
perception_node,
composable_container,
delayed_planner,
])#include <rclcpp/rclcpp.hpp>
#include <rclcpp_components/register_node_macro.hpp>
#include <sensor_msgs/msg/image.hpp>
namespace my_pkg {
class PerceptionComponent : public rclcpp::Node {
public:
explicit PerceptionComponent(const rclcpp::NodeOptions& options)
: Node("perception", options)
{
// Use intra-process communication for zero-copy
auto sub_options = rclcpp::SubscriptionOptions();
sub_options.use_intra_process_comm =
rclcpp::IntraProcessSetting::Enable;
sub_ = this->create_subscription<sensor_msgs::msg::Image>(
"camera/image_raw",
rclcpp::SensorDataQoS(),
[this](sensor_msgs::msg::Image::UniquePtr msg) {
this->callback(std::move(msg));
},
sub_options);
}
private:
void callback(sensor_msgs::msg::Image::UniquePtr msg) {
// UniquePtr = zero-copy when:
// - publisher also uses UniquePtr
// - both subscriber and publisher use intra-process
// - this is the only subscriber
// msg is moved, not copied
}
rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr sub_;
};
} // namespace my_pkg
RCLCPP_COMPONENTS_REGISTER_NODE(my_pkg::PerceptionComponent)from rclpy.action import ActionServer, CancelResponse, GoalResponse
from my_interfaces.action import PickPlace
class PickPlaceServer(Node):
def __init__(self):
super().__init__('pick_place_server')
self._action_server = ActionServer(
self, PickPlace, 'pick_place',
execute_callback=self.execute_cb,
goal_callback=self.goal_cb,
cancel_callback=self.cancel_cb,
)
def goal_cb(self, goal_request):
"""Decide whether to accept or reject the goal"""
self.get_logger().info(f'Received goal: {goal_request.target_pose}')
return GoalResponse.ACCEPT
def cancel_cb(self, goal_handle):
"""Decide whether to accept cancel requests"""
self.get_logger().info('Cancel requested')
return CancelResponse.ACCEPT
async def execute_cb(self, goal_handle):
"""Execute the action (runs in an executor thread)"""
feedback_msg = PickPlace.Feedback()
for i, step in enumerate(self.plan(goal_handle.request)):
# Check cancellation
if goal_handle.is_cancel_requested:
goal_handle.canceled()
return PickPlace.Result(success=False)
self.execute_step(step)
feedback_msg.progress = float(i) / len(self.steps)
goal_handle.publish_feedback(feedback_msg)
goal_handle.succeed()
return PickPlace.Result(success=True)# Set DDS middleware (in ~/.bashrc or launch)
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp # Recommended for most cases
# export RMW_IMPLEMENTATION=rmw_fastrtps_cpp # Default, good for multi-machine
# Limit DDS discovery to local machine (reduces network noise)
export ROS_LOCALHOST_ONLY=1
# Use ROS_DOMAIN_ID to isolate robot groups on same network
export ROS_DOMAIN_ID=42 # Range 0-101<?xml version="1.0" encoding="UTF-8"?>
<CycloneDDS xmlns="https://cdds.io/config">
<Domain>
<General>
<NetworkInterfaceAddress>eth0</NetworkInterfaceAddress>
<AllowMulticast>false</AllowMulticast> <!-- Unicast for reliability -->
</General>
<Internal>
<MaxMessageSize>65500</MaxMessageSize>
<SocketReceiveBufferSize>10MB</SocketReceiveBufferSize>
</Internal>
<!-- For large data (images, point clouds) -->
<Sizing>
<ReceiveBufferSize>10MB</ReceiveBufferSize>
</Sizing>
</Domain>
</CycloneDDS>export CYCLONEDDS_URI=file:///path/to/cyclonedds.xml# Create a ROS2 workspace
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
# Clone packages into src/
cd src
git clone https://github.com/org/my_robot_pkg.git
cd ..
# Install dependencies declared in package.xml files
sudo apt update
rosdep update
rosdep install --from-paths src --ignore-src -y
# Build the workspace
source /opt/ros/humble/setup.bash # Source the ROS2 underlay FIRST
colcon build
# Source the workspace overlay
source install/setup.bashEssential colcon flags:
# Build only specific packages (faster iteration)
colcon build --packages-select my_pkg
# Build a package and all its dependencies
colcon build --packages-up-to my_pkg
# Symlink Python files instead of copying (edit without rebuild)
colcon build --symlink-install
# Parallel jobs (default = nproc, lower if running out of RAM)
colcon build --parallel-workers 4
# Pass CMake args to all packages
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release
# Clean build (remove build/ install/ log/ and rebuild)
rm -rf build/ install/ log/
colcon build
# Build with compiler warnings as errors (CI)
colcon build --cmake-args -DCMAKE_CXX_FLAGS="-Wall -Werror"
# Show build output in real-time (useful for debugging build failures)
colcon build --event-handlers console_direct+Choose based on your package language:
ament_cmake — C++ packages, mixed C++/Python packages, packages with custom msgs
ament_python — Pure Python packages (no C++, no custom messages)<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_robot_pkg</name>
<version>0.1.0</version>
<description>My robot perception package</description>
<maintainer email="dev@example.com">Dev Name</maintainer>
<license>Apache-2.0</license>
<!-- Build tool — determines build type -->
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- For pure Python: <buildtool_depend>ament_python</buildtool_depend> -->
<!-- Build-time dependencies (headers, CMake modules) -->
<build_depend>rclcpp</build_depend>
<build_depend>sensor_msgs</build_depend>
<build_depend>OpenCV</build_depend>
<!-- Runtime dependencies -->
<exec_depend>rclcpp</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<exec_depend>rclpy</exec_depend>
<!-- Shortcut: depend = build_depend + exec_depend -->
<depend>rclcpp</depend>
<depend>sensor_msgs</depend>
<depend>geometry_msgs</depend>
<depend>tf2_ros</depend>
<depend>cv_bridge</depend>
<!-- For custom message generation -->
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<!-- Test dependencies -->
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_cmake_pytest</test_depend>
<test_depend>launch_testing_ament_cmake</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>cmake_minimum_required(VERSION 3.8)
project(my_robot_pkg)
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# ── Find dependencies ──────────────────────────────────────────
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(OpenCV REQUIRED)
# ── Custom messages / services / actions ───────────────────────
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Detection.msg"
"srv/GetPose.srv"
"action/PickPlace.action"
DEPENDENCIES geometry_msgs sensor_msgs
)
# ── Standalone executable node ─────────────────────────────────
add_executable(perception_node src/perception_node.cpp)
ament_target_dependencies(perception_node
rclcpp sensor_msgs cv_bridge OpenCV tf2_ros
)
install(TARGETS perception_node
DESTINATION lib/${PROJECT_NAME}
)
# ── Component (composable node) ────────────────────────────────
add_library(perception_component SHARED
src/perception_component.cpp
)
ament_target_dependencies(perception_component
rclcpp rclcpp_components sensor_msgs cv_bridge OpenCV
)
# Register as a composable node
rclcpp_components_register_node(perception_component
PLUGIN "my_robot_pkg::PerceptionComponent"
EXECUTABLE perception_component_node
)
install(TARGETS perception_component
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
# ── Install Python nodes ───────────────────────────────────────
install(PROGRAMS
scripts/planning_node.py
DESTINATION lib/${PROJECT_NAME}
)
# ── Install launch, config, rviz, urdf ─────────────────────────
install(DIRECTORY
launch config rviz urdf
DESTINATION share/${PROJECT_NAME}
)
# ── Install headers ────────────────────────────────────────────
install(DIRECTORY include/
DESTINATION include
)
# ── Tests ──────────────────────────────────────────────────────
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
find_package(ament_cmake_pytest REQUIRED)
ament_add_pytest_test(test_perception test/test_perception.py)
find_package(launch_testing_ament_cmake REQUIRED)
add_launch_test(test/test_integration.py)
endif()
ament_package()# setup.py (for ament_python packages)
from setuptools import find_packages, setup
package_name = 'my_python_pkg'
setup(
name=package_name,
version='0.1.0',
packages=find_packages(exclude=['test']),
data_files=[
# Register with ament index
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
# Package manifest
('share/' + package_name, ['package.xml']),
# Launch files
('share/' + package_name + '/launch',
['launch/robot.launch.py']),
# Config files
('share/' + package_name + '/config',
['config/params.yaml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Dev Name',
maintainer_email='dev@example.com',
description='My Python robot package',
license='Apache-2.0',
entry_points={
'console_scripts': [
# format: 'executable_name = package.module:function'
'perception_node = my_python_pkg.perception_node:main',
'planner_node = my_python_pkg.planner_node:main',
],
},
)# setup.cfg
[develop]
script_dir=$base/lib/my_python_pkg
[install]
install_scripts=$base/lib/my_python_pkg# msg/Detection.msg
std_msgs/Header header
string class_name
float32 confidence
geometry_msgs/Pose pose
float32[4] bbox # [x_min, y_min, x_max, y_max]# srv/GetPose.srv
string object_name
---
bool success
geometry_msgs/PoseStamped pose
string error_message# action/PickPlace.action
# Goal
geometry_msgs/Pose target_pose
string object_class
---
# Result
bool success
string error_message
---
# Feedback
float32 progress
string current_phaseUnderlay (base ROS2) /opt/ros/humble/
↑
Overlay 1 (shared libs) ~/ros2_ws/install/
↑
Overlay 2 (your dev pkg) ~/dev_ws/install/
Source order matters — LAST sourced overlay wins for duplicate packages.# Correct source order
source /opt/ros/humble/setup.bash # Base
source ~/ros2_ws/install/setup.bash # Shared workspace
source ~/dev_ws/install/setup.bash # Your development overlay
# NEVER source setup.bash from build/ — always use install/# "Package not found" during build
# → Missing dependency. Check package.xml and run:
rosdep install --from-paths src --ignore-src -y
# "Could not find a package configuration file provided by X"
# → CMake can't find the package. Did you source the underlay?
source /opt/ros/humble/setup.bash
# Build succeeds but node can't be found at runtime
# → Forgot to source the overlay, or entry_points misconfigured
source install/setup.bash
ros2 pkg list | grep my_pkg # Should appear
ros2 pkg executables my_pkg # List available executables
# Python changes not reflected after rebuild
# → Use --symlink-install, or clean and rebuild
colcon build --packages-select my_pkg --symlink-install
# "Multiple packages with the same name"
# → Duplicate package in workspace. Check with:
colcon list --packages-select my_pkg
# Build runs out of memory (large C++ packages)
colcon build --parallel-workers 2 --executor sequential
# Custom messages not found by Python nodes
# → Missing rosidl_default_runtime in package.xml exec_depend
# → Or forgot to source install/setup.bash after building msgsmy_robot_pkg/
├── CMakeLists.txt # Or setup.py for pure Python
├── package.xml
├── my_robot_pkg/ # Python module (same name as package)
│ ├── __init__.py
│ ├── perception_node.py
│ └── utils/
│ └── transforms.py
├── src/ # C++ source
│ └── perception_component.cpp
├── include/my_robot_pkg/ # C++ headers
│ └── perception_component.hpp
├── config/
│ ├── robot_params.yaml
│ └── cyclonedds.xml
├── launch/
│ ├── robot.launch.py
│ └── perception.launch.py
├── msg/
│ └── Detection.msg
├── srv/
│ └── GetPose.srv
├── action/
│ └── PickPlace.action
├── rviz/
│ └── robot.rviz
├── urdf/
│ └── robot.urdf.xacro
└── test/
├── test_perception.py # pytest
└── test_integration.py # launch_testing# Topic inspection
ros2 topic list
ros2 topic info /camera/image_raw -v # Shows QoS details
ros2 topic hz /camera/image_raw
ros2 topic bw /camera/image_raw
ros2 topic echo /joint_states --once
# Node inspection
ros2 node list
ros2 node info /perception
# Parameter management
ros2 param list /perception
ros2 param get /perception confidence_threshold
ros2 param set /perception confidence_threshold 0.8 # Runtime change!
# Lifecycle management
ros2 lifecycle list /managed_perception
ros2 lifecycle set /managed_perception configure
ros2 lifecycle set /managed_perception activate
# Service calls
ros2 service list
ros2 service call /get_pose my_interfaces/srv/GetPose "{}"
# Action monitoring
ros2 action list
ros2 action info /pick_place
ros2 action send_goal /pick_place my_interfaces/action/PickPlace "{target_pose: {x: 1.0}}"
# Bag recording (ROS2 style)
ros2 bag record -a # All topics
ros2 bag record /camera/image /tf # Specific topics
ros2 bag record -s mcap /camera/image # MCAP format (recommended)
ros2 bag info recording/ # Inspect
ros2 bag play recording/ --clock # Playback
# DDS debugging
ros2 doctor # System diagnostics
ros2 daemon stop && ros2 daemon start # Reset discovery daemonROS_LOCALHOST_ONLY=1 if not communicating across machinesROS_DOMAIN_ID to isolate your robot from others on the networkros2 doctor as part of your health check pipeline© arpitg1304, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/ros2 of arpitg1304/robotics-agent-skills.
Open the folder on GitHubat commit f9bc546
Ros2 next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Ros2 this skillarpitg1304/robotics-agent-skills | 369 | — | ~7.7k | Automated safety check: Notes | Apache-2.0 | |
| pybind11 Release Preparationpybind/pybind11 | 18k | — | ~1.7k | Automated safety check: Pass | Custom licence | |
| Paddle Eager GraphPaddlePaddle/Paddle | 24k | — | ~562 | Automated safety check: Pass | Apache-2.0 | |
| pybind11 Release Publicationpybind/pybind11 | 18k | — | ~2.5k | Automated safety check: Pass | Custom licence | |
| ExecuTorch Build Guidepytorch/executorch | 5.1k | — | ~2.3k | Automated safety check: Notes | Custom licence | |
| Ros2 Engineering Skillsdbwls99706/ros2-engineering-skills | 215 | — | ~3k | Automated safety check: Pass | Apache-2.0 |
pybind/pybind11
Opens the pybind11 release-preparation pull request: picking the release base, bumping the version in common.h and integrating the changelog, following docs/release.rst.
PaddlePaddle/Paddle
A skill your agent uses when navigating Paddle eager-mode (dynamic graph) source code, tracing forward/backward execution, debugging autograd issues, understanding PyLayer, or investigating…
pybind/pybind11
Walks a maintainer through publishing a pybind11 release after the preparation PR merges, with preflight checks, confirmations before each push and a GitHub release.
pytorch/executorch
Builds ExecuTorch from source: the Python package, C++ runtime, model runners, Android and iOS cross-compilation and backend-specific builds, with environment checks.
dbwls99706/ros2-engineering-skills
ROS 2 engineering: rclcpp/rclpy, colcon/ament, launch, QoS/DDS, tf2/URDF, ros2control, Nav2, MoveIt 2, sensors, runtime/artifact provenance, and hardware safety.
mindfold-ai/Trellis
Python design patterns for CLI scripts and utilities — type-first development, deep modules, complexity management, and red flags.
arpitg1304/robotics-agent-skills
Testing strategies, patterns, and tools for robotics software.
arpitg1304/robotics-agent-skills
Best practices, design patterns, and common pitfalls for ROS1 (Robot Operating System 1) development.
arpitg1304/robotics-agent-skills
Best practices for Docker-based ROS2 development including multi-stage Dockerfiles, docker-compose for multi-container robotic systems, DDS discovery across containers, GPU passthrough for…
arpitg1304/robotics-agent-skills
Bringing up a complete ROS2 system on a robot's onboard computer: systemd services, launch file composition, ordered startup, and production monitoring.
arpitg1304/robotics-agent-skills
Comprehensive best practices for robot perception systems covering cameras, LiDARs, depth sensors, IMUs, and multi-sensor setups.
arpitg1304/robotics-agent-skills
Architecture patterns, design principles, and proven recipes for building robust robotics software.
Categories
Comprehensive best practices, design patterns, and common pitfalls for ROS2 (Robot Operating System 2) development. Ros2 is an agent skill from arpitg1304/robotics-agent-skills. Comprehensive best practices, design patterns, and common pitfalls for ROS2 (Robot Operating System 2) development.
Ros2 fits situations like: building ROS2 nodes; debugging ROS2 systems; ever the user mentions ROS2; lifecycle nodes.
Run `npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a claude-code`. Or copy the skill folder (skills/ros2 in arpitg1304/robotics-agent-skills) into .claude/skills/ros2 in your project. Claude Code loads it when a task matches its description.
Run `npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a codex`. Or copy the skill folder (skills/ros2 in arpitg1304/robotics-agent-skills) into .agents/skills/ros2 in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add arpitg1304/robotics-agent-skills --skill ros2 -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ros2, .gemini/skills/ros2, .github/skills/ros2 and .opencode/skills/ros2 in your project.
Going by SKILL.md and its folder, Ros2 needs the command-line tools its instructions call (git and apt). Our summary lists: Python 3.
SKILL.md names 4 domains. In commands or code: cdds.io, github.com, download.ros.org and w3.org; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (runs commands with sudo), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Ros2 is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.7k tokens (SKILL.md is roughly 31k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Ros2: pybind11 Release Preparation (pybind/pybind11, 18k stars), Paddle Eager Graph (PaddlePaddle/Paddle, 24k stars), pybind11 Release Publication (pybind/pybind11, 18k stars) and ExecuTorch Build Guide (pytorch/executorch, 5.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
arpitg1304 (a GitHub user) maintains it in arpitg1304/robotics-agent-skills, which has 369 GitHub stars. The repository holds 10 skills in this directory. The repository was last updated on August 12, 2026.
Source: arpitg1304/robotics-agent-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.