Muto Agent is the agnostic communication bridge within Eclipse Muto's declarative orchestrator for ROS software stacks on automotive edge devices. As the robot's secure gateway to any cloud backend, it features a pluggable architecture that makes Muto fundamentally agnostic to external systems, enabling interoperability with various cloud orchestrators and communication standards.
The Agent is Eclipse Muto's agnostic communication bridge - the robot's secure gateway to any cloud backend. Its defining characteristic is a pluggable architecture that decouples Muto's core logic from particular cloud orchestrators or communication standards, providing interoperability and flexibility.
The Agent's primary function is to securely and reliably bridge the vehicle and the cloud, delivering the desired "state" from any backend system to the Composer for on-vehicle reconciliation. By default, it comes with Eclipse Ditto and MQTT plugins, but can easily be extended to work with other systems like Eclipse Symphony, Zenoh, or uProtocol through its plugin system.
Eclipse Muto is a declarative orchestrator for managing ROS software stacks on automotive edge devices. Its core function is to ensure the software running on a vehicle continuously matches a predefined "desired state". This declarative model allows operators to define what the final software configuration should be, while Muto's components autonomously handle how to achieve and maintain it.
Muto's architecture is built on two primary modules that work in tandem: the Agent and the Composer.
The Agent is the robot's secure gateway to any cloud backend. Its defining feature is a pluggable architecture. This design makes Muto fundamentally agnostic to the external systems it communicates with. By default, the Agent comes with a (Eclipse Ditto)[] and mqtt plugin, but can easily be made to work with others such as (Eclipse Symphony)[], Zenoh or uProtocol.
- A configured "Plugin" handles the specific details of the transport protocol (like MQTT or gRPC) and the payload schema (like a specific digital twin format).
- This decouples Muto's core logic from praticular cloud orchestrator or communication standard, providing interoperability and flexibility.
The Agent's primary function is to securely and reliably bridge the vehicle and the cloud, delivering the desired "state" to the Composer.
The Composer is the intelligent engine that enforces the desired state on the vehicle. It operates a continuous reconciliation loop which is the heart of the orchestration process:
- Inspects the current state of the live ROS system.
- Compares this live state to the desired state model received from the Agent.
- Acts to close any gap by executing the precise lifecycle using a "pipeline" of commands (start, stop, reconfigure) on the ROS nodes.
This loop ensures the vehicle's software configuration is self-healing and always converging towards the intended state.
In essence, the Agent provides the what (the desired state from any backend), and the Composer handles the how (the on-vehicle actions to achieve that state). This separation of concerns creates a robust and adaptable system for the dynamic environment of a software-defined vehicle.
The platform consists of four main modules:
- Agent: Communication bridge and command execution hub (this package)
- Core: Digital twin synchronization and state management
- Composer: Software deployment and lifecycle orchestration
- Messages: Shared message definitions and communication protocols
This documentation provides an overview about Agent. Even though you could use Agent alone with some little tweaks, it was intended to be used with other parts of Eclipse Muto. They could be found under Core, Composer and Messages. You could refer to Eclipse Muto documentation for a detailed guide on how to set Muto up for your use-case.
- Eclipse Ditto/MQTT Gateway: Secure bidirectional communication with cloud-based digital twin systems
- Command Execution Framework: Plugin-based architecture for executing remote commands
- Message Routing: Intelligent routing of messages between Muto components
- Configuration Management: Dynamic parameter handling integrated with ROS 2 parameter system
- Parsing: Flexible command/topic structure parsing for different "Plugin" deployments
- ROS 2 Integration: Native integration with ROS 2 ecosystem for seamless robotics development
The Agent package consists of the following main components:
- Core Agent
- Muto Agent: Main coordinator node managing component lifecycle and message routing
- Ditto/MQTT Gateway: Handles secure cloud connectivity and message translation
- Message Handlers: Process and route different message types between components
- Command System
- Command Executor: Framework for executing commands through plugins
- Command Registry: Manages available commands and their configurations
- Command Plugins: Extensible plugins for different command types (ROS tools, system commands, etc.)
- Configuration Management
- Config Manager: Handles parameter loading and validation
- Parser: Parses and manages topic structures for multi-vehicle scenarios
The Agent acts as a central communication hub, receiving messages from the cloud via MQTT, routing them to appropriate Muto components (Core, Composer), executing commands locally, and sending responses back to the digital twin.
- ROS 2 Humble or later installed on your system.
- Ditto/MQTT Broker access for cloud connectivity.
-
Clone the Repository
cd $HOME mkdir -p muto/src cd muto/src git clone https://github.com/eclipse-muto/agent.git git clone https://github.com/eclipse-muto/core.git git clone https://github.com/eclipse-muto/composer.git git clone https://github.com/eclipse-muto/messages.git
-
Install Dependencies
Use
rosdepto install dependencies:cd $HOME/muto rosdep update rosdep install --from-paths src --ignore-src -r -y
-
Build the Package
Use
colconto build the package:cd $HOME/muto colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Release
- For a better workflow, you need to create 2 files:
muto.yaml, andmuto.launch.py
cd $HOME/muto
mkdir config/ && cd config/
# Create the below muto.yaml file under this config directorymuto.yaml:
/**:
ros__parameters:
prefix: muto
#You can override this during launch using vehicle_namespace argument
namespace: org.eclipse.muto.sandbox
#You can override this during launch using vehicle_name argument
name: mytest_vehicle_001
stack_topic: "stack"
twin_topic: "twin"
agent_to_gateway_topic: "agent_to_gateway"
gateway_to_agent_topic: "gateway_to_agent"
agent_to_commands_topic: "agent_to_command"
commands_to_agent_topic: "command_to_agent"
thing_messages_topic: "thing_messages"
ignored_packages: ["package1", "package3"] # the packages in this list will be ignored in the build phase
twin_url: "http://ditto:ditto@sandbox.composiv.ai"
host: sandbox.composiv.ai
port: 1883
keep_alive: 60
user: null
password: null
anonymous: false
type: real_car
attributes: '{"brand": "muto", "model": "agent"}'
commands:
command1:
name: ros/topic
service: rostopic_list
plugin: CommandPlugin
command2:
name: ros/topic/info
service: rostopic_info
plugin: CommandPlugin
command3:
name: ros/topic/echo
service: rostopic_echo
plugin: CommandPlugin
command4:
name: ros/node
service: rosnode_list
plugin: CommandPlugin
command5:
name: ros/node/info
service: rosnode_info
plugin: CommandPlugin
command6:
name: ros/param
service: rosparam_list
plugin: CommandPlugin
command7:
name: ros/param/get
service: rosparam_get
plugin: CommandPlugincd $HOME/muto
mkdir launch/ && cd launch/
# Create the below muto.launch.py file under this launch directorymuto.launch.py:
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch.actions.include_launch_description import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
# Arguments
muto_namespace_arg = DeclareLaunchArgument("muto_namespace", default_value="muto")
vehicle_namespace_arg = DeclareLaunchArgument(
"vehicle_namespace",
default_value="org.eclipse.muto.sandbox",
description="Vehicle ID namespace",
)
vehicle_name_arg = DeclareLaunchArgument(
"vehicle_name", description="Vehicle Name"
)
# Files
muto_params = "config/muto.yaml"
# Agent
node_agent = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="agent",
package="agent",
executable="muto_agent",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
node_mqtt_gateway = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="gateway",
package="agent",
executable="mqtt",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
node_commands = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="commands_plugin",
package="agent",
executable="commands",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
# Core
node_twin = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="core_twin",
package="core",
executable="twin",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
# Composer
node_composer = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="muto_composer",
package="composer",
executable="muto_composer",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
node_compose_plugin = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="compose_plugin",
package="composer",
executable="compose_plugin",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
node_provision_plugin = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="provision_plugin",
package="composer",
executable="provision_plugin",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
node_launch_plugin = Node(
namespace=LaunchConfiguration("muto_namespace"),
name="launch_plugin",
package="composer",
executable="launch_plugin",
output="screen",
parameters=[
muto_params,
{"namespace": LaunchConfiguration("vehicle_namespace")},
{"name": LaunchConfiguration("vehicle_name")},
],
)
# Launch Description Object
ld = LaunchDescription()
ld.add_action(muto_namespace_arg)
ld.add_action(vehicle_namespace_arg)
ld.add_action(vehicle_name_arg)
ld.add_action(node_agent)
ld.add_action(node_mqtt_gateway)
ld.add_action(node_commands)
ld.add_action(node_twin)
ld.add_action(node_composer)
ld.add_action(node_compose_plugin)
ld.add_action(node_provision_plugin)
ld.add_action(node_launch_plugin)
return ld- Agent uses the configuration file
muto.yamlto define MQTT connection parameters, topic mappings, and available commands. Ensure that this file is properly configured for your deployment environment.
To start Muto as a whole (including agent):
cd $HOME/muto
source /opt/ros/$ROS_DISTRO/setup.bash && source install/local_setup.bash
ros2 launch launch/muto.launch.py vehicle_namespace:=org.eclipse.muto.test vehicle_name:=test-robot-$(shuf -i 1000-9999 -n 1)Agent listens for command execution requests through ROS 2 services and MQTT messages. Commands can be sent from the digital twin via MQTT or directly through ROS 2 service calls.
# Example: List available ROS topics
ros2 service call /muto/agent/execute_command muto_msgs/srv/CommandPlugin "{method: 'ros/topic', payload: '', meta: {}}"Agent's command execution functionality can be extended through plugins. The default command plugins provide:
- ROS Topic Commands: List, info, and echo operations on ROS topics
- ROS Node Commands: List and info operations on ROS nodes
- ROS Parameter Commands: List and get operations on ROS parameters
To add a new command plugin:
-
Create the Plugin Service
Define your plugin service interface in
muto_msgs/srv. -
Implement the Plugin Node
Create a ROS 2 node that provides the service interface.
-
Update the Configuration
Add your plugin to the
commandssection inmuto.yaml. -
Register the Plugin
Add the plugin executable to the package's setup.py.
For detailed instructions, refer to the Adding a Plugin Guide.
Agent processes several types of messages:
- Gateway Messages: Bidirectional communication with cloud digital twins
- Command Messages: Remote command execution requests and responses
- Composer Messages: Software deployment and lifecycle management
- Twin Messages: Digital twin state synchronization
The message routing system ensures proper delivery to the appropriate Muto components based on message type and content.
Agent provides secure MQTT connectivity for cloud integration:
- Authentication: Supports username/password and anonymous authentication
- Topic Management: Dynamic topic subscription based on vehicle namespace
- Message Translation: Converts between MQTT payloads and ROS 2 messages
- Connection Management: Automatic reconnection and error handling
Refer to the Eclipse Ditto/MQTT Integration Guide for configuration details.
Contributions are welcome! Make sure you follow the coding guidelines that were specified in the project wiki as much as you could.
This project is licensed under the EPL v2.0.
- ROS 2 Community: For providing an excellent framework for robotic software development.
- Eclipse Foundation: For supporting the Eclipse Muto project.
- Contributors: Thanks to all the contributors who have helped improve Agent.