Stage 2: agnocast::Node#
In Stage 2, you replace rclcpp::Node with agnocast::Node. This bypasses the rcl layer entirely — no RMW participant is created — reducing launch time and CPU usage.
Prerequisite
Stage 2 can only be applied to a node once all publishers and subscriptions in that node have been migrated to Agnocast APIs (Stage 1 complete).
Migrating a Publisher#
Before (Stage 1)#
```cpp
include "agnocast/agnocast.hpp"#
include "rclcpp/rclcpp.hpp"#
include "std_msgs/msg/string.hpp"#
class MyPublisher : public rclcpp::Node
{
agnocast::Publisher
void timer_callback() { auto msg = pub_->borrow_loaned_message(); msg->data = "Hello, world!"; pub_->publish(std::move(msg)); }
public: MyPublisher() : Node("my_publisher") { auto group = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
pub_ = agnocast::create_publisher<std_msgs::msg::String>(
this, "/topic", 10);
timer_ = create_wall_timer(100ms,
std::bind(&MyPublisher::timer_callback, this), group);
} }; ```
After (Stage 2)#
```cpp
include "agnocast/agnocast.hpp"#
include "std_msgs/msg/string.hpp"#
class MyPublisher : public agnocast::Node // (1)
{
agnocast::Publisher
void timer_callback() { auto msg = pub_->borrow_loaned_message(); msg->data = "Hello, world!"; pub_->publish(std::move(msg)); }
public: MyPublisher() : Node("my_publisher") { auto group = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
pub_ = this->create_publisher<std_msgs::msg::String>( // (3)
"/topic", 10);
timer_ = this->create_wall_timer(100ms,
std::bind(&MyPublisher::timer_callback, this), group);
} }; ```
Key changes:
- Base class changes from
rclcpp::Nodetoagnocast::Node - Timer type changes to
agnocast::TimerBase::SharedPtr - Publisher creation uses member function
this->create_publisher(...)instead of free function
Migrating a Subscription#
Before (Stage 1)#
```cpp
include "agnocast/agnocast.hpp"#
include "rclcpp/rclcpp.hpp"#
include "std_msgs/msg/string.hpp"#
class MySubscriber : public rclcpp::Node
{
agnocast::Subscription
void callback(const agnocast::ipc_shared_ptr
public: MySubscriber() : Node("my_subscriber") { auto group = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive); agnocast::SubscriptionOptions options; options.callback_group = group;
sub_ = agnocast::create_subscription<std_msgs::msg::String>(
this, "/topic", 10,
std::bind(&MySubscriber::callback, this, std::placeholders::_1),
options);
} }; ```
After (Stage 2)#
```cpp
include "agnocast/agnocast.hpp"#
include "std_msgs/msg/string.hpp"#
class MySubscriber : public agnocast::Node // (1)
{
agnocast::Subscription
void callback(const agnocast::ipc_shared_ptr
public: MySubscriber() : Node("my_subscriber") { auto group = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive); agnocast::SubscriptionOptions options; options.callback_group = group;
sub_ = this->create_subscription<std_msgs::msg::String>( // (2)
"/topic", 10,
std::bind(&MySubscriber::callback, this, std::placeholders::_1),
options);
} }; ```
Key changes:
- Base class changes to
agnocast::Node - Subscription creation uses member function instead of free function
Switching the Executor (Nodes with main)#
Replace the initialization and executor.
Before (Stage 1):
cpp
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
agnocast::SingleThreadedAgnocastExecutor executor;
auto node = std::make_shared<MyNode>();
executor.add_node(node);
executor.spin();
rclcpp::shutdown();
}
After (Stage 2):
cpp
int main(int argc, char * argv[])
{
agnocast::init(argc, argv); // (1)
agnocast::AgnocastOnlySingleThreadedExecutor executor; // (2)
auto node = std::make_shared<MyNode>();
executor.add_node(node);
executor.spin();
}
Key changes:
rclcpp::init()→agnocast::init(),rclcpp::shutdown()no longer needed- Executor changes to
AgnocastOnly*variant
Available executors for Stage 2:
| Executor |
|---|
agnocast::AgnocastOnlySingleThreadedExecutor |
agnocast::AgnocastOnlyMultiThreadedExecutor |
agnocast::AgnocastOnlyCallbackIsolatedExecutor |
Note
If a process contains a mix of rclcpp::Node (Stage 1) and agnocast::Node (Stage 2) nodes, use the non-AgnocastOnly executors (e.g., agnocast::CallbackIsolatedAgnocastExecutor), which can handle both.
Switching the Executor (Composable Nodes)#
Replace the EXECUTOR option with an AgnocastOnly* variant.
CMakeLists.txt (before — Stage 1):
cmake
agnocast_components_register_node(
my_component
PLUGIN "MyNode"
EXECUTABLE my_node
)
CMakeLists.txt (after — Stage 2):
cmake
agnocast_components_register_node(
my_component
PLUGIN "MyNode"
EXECUTABLE my_node
EXECUTOR AgnocastOnlySingleThreadedExecutor
)
No launch file changes are needed from Stage 1 — LD_PRELOAD and the container executable remain the same.
Supplementary Information#
agnocast::Node API Compatibility#
agnocast::Node provides an API largely compatible with rclcpp::Node, but some APIs are not yet supported. Before migrating to Stage 2, check the agnocast::Node interface comparison to confirm the APIs your node uses are supported.
Summary of Changes from Stage 1 to Stage 2#
| Aspect | Stage 1 | Stage 2 |
|---|---|---|
| Node class | rclcpp::Node |
agnocast::Node |
| Pub/sub creation | agnocast::create_*(this, ...) |
this->create_*(...) |
| Initialization | rclcpp::init() |
agnocast::init() |
| Executor | agnocast::*AgnocastExecutor |
agnocast::AgnocastOnly*Executor |
| rclcpp dependency | Required | Not required |
| RMW participant | Created | Not created |