Skip to main content
ClaudeWave
Skill200 repo starsupdated 4d ago

new_nav2_plugin

Scaffold a new Nav 2 plugin (controller, planner, behavior, smoother, goal-checker, progress-checker, costmap layer, or BT node). Wires pluginlib registration, parameter declaration on the lifecycle node, and a minimal integration test. Trigger when the user asks to write or extend a Nav 2 plugin.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/harunkurtdev/ros2-claude-code-template /tmp/new_nav2_plugin && cp -r /tmp/new_nav2_plugin/.claude/skills/new_nav2_plugin ~/.claude/skills/new_nav2_plugin
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Scaffolding a Nav 2 plugin

Nav 2 exposes every behaviour as a `pluginlib`-loaded class behind a
small set of `nav2_core::*` (or `nav2_costmap_2d::Layer`, or
`BT::ActionNodeBase`) interfaces. The recipe is the same shape for
every plugin kind — only the base class and the host server change.

## Decide first

| Plugin kind         | Base class                                  | Loaded by                  | Plugin description file |
|---------------------|---------------------------------------------|----------------------------|-------------------------|
| Controller          | `nav2_core::Controller`                     | `controller_server`        | `nav2_core_plugins.xml` |
| Global planner      | `nav2_core::GlobalPlanner`                  | `planner_server`           | `nav2_core_plugins.xml` |
| Smoother            | `nav2_core::Smoother`                       | `smoother_server`          | `nav2_core_plugins.xml` |
| Goal checker        | `nav2_core::GoalChecker`                    | `controller_server`        | `nav2_core_plugins.xml` |
| Progress checker    | `nav2_core::ProgressChecker`                | `controller_server`        | `nav2_core_plugins.xml` |
| Behavior            | `nav2_core::Behavior`                       | `behavior_server`          | `nav2_core_plugins.xml` |
| Costmap layer       | `nav2_costmap_2d::Layer` / `CostmapLayer`   | `local_/global_costmap`    | `nav2_costmap_2d_plugins.xml` |
| BT action / cond.   | `BT::SyncActionNode` / `BT::ConditionNode`  | `bt_navigator`             | `nav2_tree_nodes.xml`   |

See `.claude/skills/nav2_core_interfaces/SKILL.md` for the full method
list of each base.

## Step 1 — header

For a controller (other plugin kinds are analogous):

```cpp
// include/<pkg>/<snake_class>.hpp
#ifndef <PKG>__<SNAKE_CLASS>_HPP_
#define <PKG>__<SNAKE_CLASS>_HPP_

#include <memory>
#include <string>

#include <nav2_core/controller.hpp>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_lifecycle/lifecycle_node.hpp>

namespace <pkg>
{

class <ClassName> : public nav2_core::Controller
{
public:
  <ClassName>() = default;
  ~<ClassName>() override = default;

  void configure(
    const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
    std::string name,
    std::shared_ptr<tf2_ros::Buffer> tf,
    std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros) override;

  void cleanup()    override;
  void activate()   override;
  void deactivate() override;

  geometry_msgs::msg::TwistStamped computeVelocityCommands(
    const geometry_msgs::msg::PoseStamped & pose,
    const geometry_msgs::msg::Twist & velocity,
    nav2_core::GoalChecker * goal_checker) override;

  void setPlan(const nav_msgs::msg::Path & path) override;
  void setSpeedLimit(const double & speed_limit, const bool & percentage) override;

private:
  rclcpp_lifecycle::LifecycleNode::WeakPtr node_;
  std::string plugin_name_;
  std::shared_ptr<tf2_ros::Buffer> tf_;
  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros_;

  // Declared params
  double desired_linear_vel_{0.0};
};

}  // namespace <pkg>

#endif
```

## Step 2 — source

```cpp
// src/<snake_class>.cpp
#include "<pkg>/<snake_class>.hpp"

#include <pluginlib/class_list_macros.hpp>

namespace <pkg>
{

void <ClassName>::configure(
  const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
  std::string name,
  std::shared_ptr<tf2_ros::Buffer> tf,
  std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
{
  node_         = parent;
  plugin_name_  = name;
  tf_           = tf;
  costmap_ros_  = costmap_ros;

  auto node = node_.lock();
  nav2_util::declare_parameter_if_not_declared(
    node, plugin_name_ + ".desired_linear_vel",
    rclcpp::ParameterValue(0.5));
  node->get_parameter(plugin_name_ + ".desired_linear_vel",
                      desired_linear_vel_);
}

void <ClassName>::cleanup()    {}
void <ClassName>::activate()   {}
void <ClassName>::deactivate() {}

geometry_msgs::msg::TwistStamped
<ClassName>::computeVelocityCommands(
  const geometry_msgs::msg::PoseStamped & /*pose*/,
  const geometry_msgs::msg::Twist & /*velocity*/,
  nav2_core::GoalChecker * /*goal_checker*/)
{
  geometry_msgs::msg::TwistStamped cmd_vel;
  cmd_vel.header.stamp = node_.lock()->now();
  cmd_vel.twist.linear.x = desired_linear_vel_;
  return cmd_vel;
}

void <ClassName>::setPlan(const nav_msgs::msg::Path & /*path*/) {}
void <ClassName>::setSpeedLimit(const double & /*speed_limit*/,
                                const bool   & /*percentage*/) {}

}  // namespace <pkg>

PLUGINLIB_EXPORT_CLASS(<pkg>::<ClassName>, nav2_core::Controller)
```

Critical points:

* **Always** use `nav2_util::declare_parameter_if_not_declared` —
  Nav 2 calls `configure` more than once across lifecycle transitions.
* Parameters are keyed under `<plugin_name>.<param>` because users set
  them under the plugin alias they pick in YAML, not under the plugin
  class name.
* `PLUGINLIB_EXPORT_CLASS` last argument must match the loader's
  expected base class (`nav2_core::Controller`,
  `nav2_costmap_2d::Layer`, …).

## Step 3 — pluginlib manifest (`plugins.xml`)

```xml
<library path="<pkg>">
  <class
    type="<pkg>::<ClassName>"
    base_class_type="nav2_core::Controller">
    <description>
      One-line description of what this controller does.
    </description>
  </class>
</library>
```

## Step 4 — `CMakeLists.txt`

```cmake
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)
find_package(pluginlib REQUIRED)
find_package(nav2_core REQUIRED)
find_package(nav2_costmap_2d REQUIRED)
find_package(nav2_util REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(nav_msgs REQUIRED)

add_library(${PROJECT_NAME} SHARED src/<snake_class>.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)

ament_target_dependencies(${PROJECT_NAME}
  rclcpp rclcpp_lifecycle pluginlib nav2_co
behaviortree-reviewerSubagent

Use proactively before opening a PR that adds or changes BehaviorTree.CPP nodes or BehaviorTree.ROS2 wrappers (RosActionNode/RosServiceNode/RosTopicPub/SubNode, TreeExecutionServer). Reviews a diff against BT.CPP v4 conventions — node base-class choice, non-blocking ticks, ports/blackboard typing, factory/plugin registration, XML v4, and the ROS 2 wrapper contract. Returns a punch list with file:line anchors, not a rewrite.

clean-arch-architectSubagent

Use when a design decision touches Clean Architecture boundaries in a ROS 2 project — which layer a new behaviour belongs to, whether a port belongs in domain or application, whether a new node should be lifecycle-managed, whether to compose nodes or split packages. Returns an architectural recommendation with trade-offs, not implementation.

ecs-architectSubagent

Use when a design decision touches the gz-sim ECS — where new state should live, which system phase should write it, how to avoid coupling, whether to add a component vs. a member variable, whether a new system should be split or merged with an existing one. Returns an architectural recommendation with trade-offs, not implementation.

gz-style-reviewerSubagent

Use proactively before opening any gz-sim PR. Reviews a diff against the project's C++17 style, ECS conventions, plugin registration patterns, CMake structure, test placement, Migration.md / Changelog.md expectations, and pre-commit configuration. Returns a punch list, not a rewrite.

ros2-controllers-reviewerSubagent

Use proactively before opening a PR that adds or changes a ros2_control controller, broadcaster, or hardware component (incl. URDF <ros2_control> bringup). Reviews a diff against ros2_controllers / ros2_control_demos conventions — controller & hardware lifecycle, command/state interface configuration, real-time safety of update()/read()/write(), generate_parameter_library usage, pluginlib registration, chainable-controller correctness, URDF wiring, and tests. Returns a punch list with file:line anchors, not a rewrite.

ros2-style-reviewerSubagent

Use proactively before opening any ROS 2 / Nav 2 PR. Reviews a diff against this template's Clean Architecture, ROS 2 communication, lifecycle, testing, and Nav 2 plugin conventions. Returns a punch list with file:line anchors, not a rewrite.

vda5050-reviewerSubagent

Use proactively before opening a PR that touches a VDA 5050 connector / fleet bridge. Reviews a diff against VDA 5050 v3.0.0 protocol compliance (topics, QoS, header rules, base/horizon, action state machine, schema validation) and the template's Clean Architecture for the MQTT↔Nav 2 bridge. Returns a punch list with file:line anchors, not a rewrite.

buildSlash Command

Build the colcon workspace (optionally a single package) and report the outcome.