ROS

ROS Executor

Didn’t even know this was a thing, but yea, how does code get executed? Who executes the code?

Resources

The 3 types of executors:

  1. SingleThreadedExecutor
  2. MultiThreadedExecutor (fastest)
  3. StaticSingleThreadedExecutor

When you do rclcpp::spin(node), this runs the executor!

The general syntax (from this tutorial), where they use a SharedPtr (I see this in MIT-PITT-RW codebase, as well as pure pursuit code)

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node_ptr = std::make_shared<PurePursuit>(); // initialise node pointer
    rclcpp::spin(node_ptr);
    rclcpp::shutdown();
    return 0;
}

Second syntax I’ve seen (from this tutorial), where they call make_shared

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}

Both expect a Shared Pointer of some sort, so we should look more into the detail on what is happening.

Is each executor its own process?

In ROS2, executors are not separate processes. They are part of the main application process, so if you define multiple executors in this function, they will still be part of the same process.

rclcpp::spin vs. executor.spin()

The call to spin(node) basically expands to an instantiation and invocation of the Single-Threaded Executor, which is the simplest Executor:

// Expanded!
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin();
 
// Short form!
rclcpp::spin(node);
  • So you can add multiple nodes to the same executor!

Ahh, and you can have a multithreaded executor! I saw this in MIT-PITT-RW code:

def main(args=None):
    rclpy.init(args=args)
    wall_detect_node = WallDetectNode()
 
    executor = MultiThreadedExecutor(num_threads=4)
    executor.add_node(wall_detect_node)
 
    try:
        executor.spin()
    finally:
        executor.shutdown()
        wall_detect_node.destroy_node()
    rclpy.shutdown()
 

For my own code, I didn’t use executors before (ex: rrt code), but under the hood it’s just an executor

def main(args=None):
    rclpy.init(args=args)
    print("RRT Initialized")
    rrt_node = RRT()
    rclpy.spin(rrt_node)
 
    rrt_node.destroy_node()
    rclpy.shutdown()
  • So the rclcpp::spin is basically just an expansion of the SingleThreadedExecutor

How do executors fit into the picture of ROS architecture? Is it like RMW stuff? Same as create_subscription and create_publisher I guess.