Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

Repo symbol

jig repository

jig jig_example

ROS Distro
jazzy

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version jazzy
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

Repo symbol

jig repository

jig jig_example

ROS Distro
kilted

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version kilted
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro rolling showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro ardent showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro bouncy showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro crystal showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro eloquent showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro dashing showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro galactic showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro foxy showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro iron showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro lunar showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro jade showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro indigo showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro hydro showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro kinetic showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro melodic showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file

No version for distro noetic showing humble. Known supported distros are highlighted in the buttons above.
Repo symbol

jig repository

jig jig_example

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/nineyards-robotics/jig.git
VCS Type git
VCS Version humble
Last Updated 2026-03-28
Dev Status MAINTAINED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
jig 0.0.0
jig_example 0.0.0

README

jig

Declarative ROS 2 node scaffolding with built-in best practice

Jig transforms simple YAML interface definitions into strongly-typed C++ and Python ROS 2 lifecycle node scaffolding. Define your publishers, subscribers, services, actions and parameters in one simple file and Jig will handle the rest.

Quick Start

Jig uses a convention-over-configuration approach with automatic build system integration. Here’s how to create a complete ROS 2 package in minutes:

1. Create Package Structure

Your package should follow this structure:

my_package/
├── nodes/
│   └── my_node/
│       ├── interface.yaml    # Interface definition
│       ├── my_node.hpp       # Header (C++ only)
│       └── my_node.cpp       # Implementation (.cpp for C++, .py for Python)
├── CMakeLists.txt
└── package.xml

2. Define Your Node Interface

Create nodes/my_node/interface.yaml:

parameters:
    important_parameter:
        type: string
        default_value: "oh hi mark"
        description: "A very important string."

publishers:
    - topic: some_topic
      type: std_msgs/msg/String
      qos:
        history: 10
        reliability: RELIABLE

subscribers:
    - topic: other_topic
      type: std_msgs/msg/Bool
      qos:
        history: 5
        reliability: BEST_EFFORT

services:
    - name: my_service
      type: example_interfaces/srv/AddTwoInts

3. Implement Your Node

C++ Example

First, create the header (nodes/my_node/my_node.hpp):

Design Pattern: Jig uses a lifecycle Session class rather than subclassing rclcpp_lifecycle::LifecycleNode. This separation makes testing easier (you can test logic without spinning up ROS), keeps state explicit, and allows callbacks to be simple free functions. The Session is created during the on_configure lifecycle transition and destroyed on cleanup/shutdown. To define the Session of your node, you subclass the auto-generated <NodeName>Session struct and add your own variables to it. The auto-generated Session class will contain a reference to the lifecycle node instance, as well as all publishers, subscribers, services, actions and parameters.

#pragma once

#include <memory>
#include <my_package/my_node_interface.hpp>

namespace my_package::my_node {

// Extend the generated session with custom state
struct Session : MyNodeSession<Session> {
    using MyNodeSession::MyNodeSession;
    // Add any custom state here
    int my_counter = 0;
};

// Forward declare on_configure function
CallbackReturn on_configure(std::shared_ptr<Session> sn);

// Define the node class using the generated base
// This must match the pattern: package::node_name::NodeName
using MyNode = MyNodeBase<Session, on_configure>;

} // namespace my_package::my_node

Then implement it (nodes/my_node/my_node.cpp):

Design Pattern: Jig uses a free function on_configure() approach instead of subclassing rclcpp_lifecycle::LifecycleNode. The on_configure() function receives a fully-constructed session with all publishers, subscribers, and parameters ready to use. This functional approach, coupled with the session object, makes nodes easier to reason about, simpler to write and more testable. By storing a reference to the lifecycle node in the session, we create a “has-a” relationship with the Node rather than “is-a”, cleanly separating ROS communication from your implementation logic.

```cpp #include “my_node.hpp”

namespace my_package::my_node {

void msg_callback(std::shared_ptr sn, std_msgs::msg::Bool::ConstSharedPtr msg) { sn->my_counter++; RCLCPP_INFO(sn->node.get_logger(), "Got a bool: %d (count: %d)", msg->data, sn->my_counter); }

File truncated at 100 lines see the full file