Repo symbol

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

File truncated at 100 lines see the full file

Repo symbol

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
jazzy

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

File truncated at 100 lines see the full file

Repo symbol

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
kilted

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

File truncated at 100 lines see the full file

Repo symbol

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
lyrical

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

File truncated at 100 lines see the full file

Repo symbol

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
rolling

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

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

automatika_ros_sugar repository

automatika_ros_sugar

ROS Distro
humble

Repository Summary

Checkout URI https://github.com/automatika-robotics/ros-sugar.git
VCS Type git
VCS Version main
Last Updated 2026-10-21
Dev Status DEVELOPED
Released RELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Packages

Name Version
automatika_ros_sugar 0.8.2

README

Sugarcoat Logo
**ROS 2, with the sugar on top** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE) [![ROS 2](https://img.shields.io/badge/ROS%202-Humble%20|%20Jazzy%20|%20Kilted%20|%20Lyrical%20|%20Rolling-orange?style=flat-square&logo=ros)](https://emos.automatikarobotics.com/getting-started/installation.html) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/B9ZU6qjzND) [**Documentation**](https://emos.automatikarobotics.com) · [**Developer Docs**](https://sugarcoat.automatikarobotics.com) · [**API Reference**](https://sugarcoat.automatikarobotics.com/apidocs/index.html) · [**Discord**](https://discord.gg/B9ZU6qjzND)

Sugarcoat is the foundation of EMOS, the Embodied Operating System. A ROS 2 application tends to end up as a pile of nodes, launch files and glue, with what the robot actually does scattered across all of them. Sugarcoat gives the robot a nervous system instead. A modern robot drives, watches, listens, speaks and grasps at the same time, in a world that rarely goes to plan, and what it does next has to come from all of it, the moment it happens. A recipe is one Python script in which you declare the components, say which events matter and what happens when they fire, and hand the lot to a launcher that brings the system up, keeps it healthy and takes it down again.

Building Blocks

Block What it gives you
Component A ROS 2 lifecycle node with the plumbing done: declared inputs and outputs, a typed configuration that also loads from YAML, JSON or TOML, a health status, fallbacks, and actions callable by name. Driven by a timer, a trigger topic, a service or an action server.
Topic A named, typed stream. Conversion between ROS messages and Python objects is done for you, spatial inputs arrive in the frame the algorithm needs, and use_plugin serves a topic from the robot’s plugin instead of ROS.
Event A condition on live data in plain Python, Event(battery.msg.data < 20.0). Conditions combine across topics with and, or and not, fire on change, once or debounced, and any function returning bool can be polled as one.
Action Anything callable with its arguments, ready to run when an event fires: a component method, a system action such as restart, reconfigure, publish or log, a plain function, or a plugin action. Arguments can come from topics at run time. A monitored action waits for a success condition, times out, retries and can be cancelled.
Routine A named sequence of monitored steps with a retry and failure policy per step. It can be paused, resumed and aborted, publishes its progress, and is started by an event, from the web UI, or by an agent as a tool.
Fallbacks Recovery a component runs on its own when its health status turns bad. An algorithm failure, a component failure and a system failure each get their own answer, from a retry to a restart.
Launcher Turns the recipe into a running system: configures and activates components as threads or as processes, starts the drivers and external nodes the recipe needs, runs the Monitor that evaluates events and dispatches actions, and supervises until the recipe ends. No launch files.
Robot plugin Adapts one robot or sensor to the interfaces recipes use: transports over ROS, UDP, HTTP or a vendor SDK, feedback decoders and command encoders, the robot’s own actions and events, drivers to start, sensor mounts published to TF, and how the robot maps.
Web UI and API One call serves the recipe’s inputs, outputs, settings, actions and routines to a browser, and the same over a JSON and WebSocket API guarded by HTTPS and API keys.
What a Sugarcoat component is made of

Key Features

  • Recipes, not launch files. A whole system is one Python file. It names the components, attaches the robot’s plugin, and says which events matter and what should happen when they fire. The launcher does the rest. It configures and activates every component, as threads of one process or as processes of their own, keeps an eye on them while the recipe runs, and shuts the lot down cleanly when it is told to.
  • Events in plain Python. Event(battery.msg.data < 20.0) is a complete event. Field paths are checked against the message type when the recipe is imported, so a typo fails on your desk rather than on the robot. A condition can span several topics, fire only when something changes or only once, and a topic can be given a timeout so that an event never fires on a reading that stopped arriving a minute ago.
  • Actions that know whether they worked. Give an action a success condition, a timeout, a retry budget and a cancel method, and the Monitor that every launcher runs will dispatch it, wait for the verdict and try again if it has to. Nothing in a component’s loop blocks for any of this.
  • Routines for behaviours that take steps. Detect, approach, grasp, lift. A routine says a sequence like that in one place, with a policy for each step, and can be paused, resumed and aborted from an event, from the web UI, or by an agent that plans with it as a tool. Its progress is published while it runs.
  • Components that heal. A component reports how it is doing rather than merely that it is alive, and when its status turns bad it runs the fallbacks it was given, whether that means a retry, a simpler algorithm, a reconnect or a restart. Nobody has to be watching.
  • One recipe, any robot. A robot plugin adapts the hardware to the interfaces recipes use, whether the robot speaks ROS, UDP, HTTP or a vendor SDK, and brings the robot’s own actions and events along with it. Large feeds such as images and point clouds travel through shared memory.
  • An interface for free. One call serves the recipe in the browser and over a JSON and WebSocket API, behind HTTPS and API keys, with every asset served from the robot, so it works wherever the robot is, internet or not.

Behaviour trees had their day. A tree is one structure ticked by one node, so everything the robot does has to be squeezed into it, and it notices the world only when the tick reaches a condition node someone remembered to put there. A sugarcoated system is richer on every count. Any topic, any component’s health or any reading from the robot can be an event, written in Python and checked against the message types before anything runs. Any event can trigger any action or routine anywhere in the system, and every event is watched at once, whatever the robot happens to be doing. Sequences live in routines where they belong, with a success test and a retry policy for each step and the means to pause, resume or abort them, while recovery lives with the component that needs it rather than in a branch of a tree. The robot reacts the moment something happens, not when the tick gets round to it.

Quick Start

A component that drives forward, and an event that slows it down when the battery runs low:

from ros_sugar import Launcher
from ros_sugar.core import Action, BaseComponent, Event
from ros_sugar.io import Topic
from ros_sugar.utils import ActionReturnType, component_action

battery = Topic(name="battery", msg_type="Float32")
cmd_vel = Topic(name="cmd_vel", msg_type="Twist")


class Patrol(BaseComponent):
    """Drives forward at a speed the recipe can change at run time."""

    def init_variables(self):
        self.speed = 0.5

    def _execution_step(self):
        self.publishers_dict["cmd_vel"].publish([self.speed, 0.0, 0.0])

    @component_action
    def slow_down(self, factor: float) -> ActionReturnType:
        self.speed *= factor
        return True, f"speed is now {self.speed:.2f} m/s"


patrol = Patrol(component_name="patrol", outputs=[cmd_vel])
low_battery = Event(battery.msg.data < 20.0, on_change=True)

launcher = Launcher()
launcher.add_pkg(components=[patrol])
launcher.on(low_battery, Action(patrol.slow_down, kwargs={"factor": 0.5}))
launcher.bringup()

Actions compose into routines. Here the same event starts a procedure whose steps are the component’s actions, declared like slow_down above, each with its own test for success, and the routine is served in the web UI with its steps checked off as it runs:

```python from ros_sugar.actions import wait

File truncated at 100 lines see the full file