Skip to content

Commit

Permalink
Eclipse Muto ROS2 Port
Browse files Browse the repository at this point in the history
* muto composer ported to ros2

* modified workflow file for ros2

* renamed workflow file and added rosdep

* updated package information

* updated workflow file

* removed rosdep init from workflow

* added python3-pip to workflow run

* added universe repo to workflow file

* updated workflow file

* added -y flag to apt update

* added setuptools to workflow file

* removed newline on description

* changed ros setup script on workflow

* replaced source with . in workflow file

* replaced ros setup scripts from bash to sh

* commented colcon test out on workflow file

* updated README.md for ros2
  • Loading branch information
ibrahimsel authored Nov 17, 2023
1 parent 7574b56 commit cebc72e
Show file tree
Hide file tree
Showing 33 changed files with 1,918 additions and 721 deletions.
24 changes: 0 additions & 24 deletions .github/workflows/catkin-build.yml

This file was deleted.

27 changes: 27 additions & 0 deletions .github/workflows/colcon-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Colcon Build and Test

on: [push]

jobs:
build:
runs-on: ubuntu-latest
container: osrf/ros:humble-desktop
steps:
- name: Checkout
uses: actions/checkout@v3

- run: |
mkdir -p $(pwd)/tmp/muto/src/
ln -s $(pwd) $(pwd)/tmp/muto/src/composer
cd $(pwd)/tmp/muto
apt-get update -y
apt-get install python3-pip -y
pip3 install setuptools==58.2.0
. /opt/ros/humble/setup.sh
rosdep update -y
rosdep install --from-path src --ignore-src -r -y --rosdistro humble
colcon build
. install/setup.sh
# colcon test
# colcon test-result --all --verbose
name: Composer ROS2 Humble container build
99 changes: 31 additions & 68 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,98 +4,58 @@
# Muto Composer

## Build
After you checkou the repository, ource your ROS environment and make sure the additional dependencies such as the eclipse paho mqtt client library is installed.
After you checkout the repository, source your ROS environment and make sure the additional dependencies such as the eclipse paho mqtt client library is installed.

```bash
source /opt/ro/noetic/setup.bash
pip install paho-mqtt celery requests
source /opt/ros/humble/setup.bash
pip3 install paho-mqtt celery requests
```

## Muto Twins

Muto agent requires network connectivity to the Muto Twins (ditto) and MQTT servers. The address for the muto sandbox is:
* The twin servers and API: http://sandbox.muto.ai
* The mqtt server: mqtt://sandbox.muto.ai:1883
* The twin servers and API: https://sandbox.composiv.ai/
* The mqtt server: mqtt://sandbox.composiv.ai:1883

## Running agent

The muto agent must be launched and the target device and the devices should have access to all the packages required to launch the stacks. Fo these purposes you can either choose to run a containerizedf version of muto or launch it direct from the devices after all the packages (such as the muto learning modules) are build and installed on the AV.
The muto agent must be launched and the target device and the devices should have access to all the packages required to launch the stacks. For these purposes you can either choose to run a containerized version of muto or launch it direct from the devices after all the packages (such as the muto learning modules) are build and installed on the AV.

```bash
source devel/setup.bash
roslaunch muto_agent agent.launch
```

where agent.launch is:

```xml
<?xml version="1.0"?>
<launch>
  <node pkg="muto_agent" name="muto_agent" type="muto_agent.py">
      <rosparam  command="load" file="$(find muto_agent)/params.yaml" />
  </node>
</launch>
```

and params.yaml is:

```yaml

muto:
  nav_topic: /nav
  type: simulator
  twin_url: "http://ditto:[email protected]"
  mqtt:
    host: sandbox.composiv.ai
    port: 1883
    keep_alive: 60
    user: none
    password: none
  thing:
    namespace: ai.composiv.sandbox.f1tenth
    anonymous: False  # Use this for automatically generated id (uuid based)
    #   if anonymous is True or anynoymous param is missing, name/id will be auto generated
    name: simulator-monster-01
source install/setup.bash
ros2 launch agent agent.launch
```

## Twins
There are two definition types for thins on the wtin server:
* Stack - The model os the muto adaptive software stack that can be running on an AV
- Thing definition id for stack is: ai.composiv.sandbox.f1tenth:Stack:1.0.0
* Vehicle - The device twin for the AV, where parameters, telemetry data and stack associations are managed
- Thing definition id for vehicle is: ai.composiv.sandbox.f1tenth:TestCar:1.0.0
Examples of these definitions can be sampled by sending http rest requests to the sandbox server
http://sandbox.composiv.ai. Muto Twin Server

You can get stacks from twin server via::
```bash
curl 'http://sandbox.composiv.ai/api/2/search/things?filter=eq(definition,"ai.composiv.sandbox.f1tenth:Stack:1.0.0")'
curl 'link_to_stack_url'
```
returns:
which returns something similar to the below structure:

```json
{
    "name": "Muto Learning Simulator with Gap Follwer",
    "context": "eteration_office",
    "stackId": "ai.composiv.sandbox.f1tenth:composir_simulator_gf.launch",
    "stackId": "org.eclipse.muto.sandbox:f1tenth-multiagent-gym.launch",
    "stack": [
        {
            "thingId": "ai.composiv.sandbox.f1tenth:composiv_simulator.launch"
            "thingId": "org.eclipse.muto.sandbox:racecar1.launch"
        }
    ],
    "node": [
        {
            "name": "cass_gap_follower",
            "pkg": "cass_gap_follower",
            "exec": "cass_gap_follower",
            "name": "reactive_gap_follower",
            "pkg": "reactive_gap_follower",
            "exec": "reactive_gap_follower",
            "param": [
              { "from": "$(find cass_gap_follower)/params.yaml" }
              { "from": "$(find reactive_gap_follower)/params.yaml" }
            ]
        }
    ]
}
```

This is a stack with a single node, "cass_gap_follower". However, it includes another stack (with many other nodes and parameters) that it requires with a stackId reference ai.composiv.sandbox.f1tenth:composiv_simulator.launch. The elements of the stack model resembles a [ROS launch XML](https://wiki.ros.org/roslaunch/XML), so it should be fairly straightforward to understand if you have experience with it.
This is a stack with a single node, "cass_gap_follower". However, it includes another stack (with many other nodes and parameters) that it requires with a stackId reference org.eclipse.muto.sandbox:f1tenth-multiagent-gym.launch. The elements of the stack model resembles a [ROS launch XML](https://wiki.ros.org/roslaunch/XML), so it should be fairly straightforward to understand if you have experience writing XML launch files

## Managing Stacks and Vehicles

Expand All @@ -106,10 +66,10 @@ $ curl -X PUT -H "Content-Type: application/json" -d '
{
"name": "Muto Learning Simulator with Gap Follower",
"context": "eteration_office",
"stackId": "ai.composiv.sandbox.f1tenth:composir_simulator_gf.launch",
"stackId": "org.eclipse.muto.sandbox:composiv_simulator_gf.launch",
"stack": [
{
"thingId": "ai.composiv.sandbox.f1tenth:composiv_simulator.launch"
"thingId": "org.eclipse.muto.sandbox:composiv_simulator.launch"
}
],
"node": [
Expand All @@ -123,24 +83,24 @@ $ curl -X PUT -H "Content-Type: application/json" -d '
}
]
}
' http://sandbox.composiv.ai/api/2/things/ai.composiv.sandbox.f1tenth:composiv_simulator_gf.launch
' http://sandbox.composiv.ai/api/2/things/org.eclipse.muto.sandbox:composiv_simulator_gf.launch

```

## Managing Stacks and Vehicles
We can use the TWINS to directly communicating commands to the vehicle itself. The twin server supports special mqtt channels for these purposes called **twin** and **live** channels. For example the following command can be published to the sandbox MQTT server to activate a stack on a car. Each vehicle has its dedicated **twin** and **live** channels:

```yaml
topic: ai.composiv.sandbox.f1tenth:simulator-monster-01/stack/commands/active
topic: org.eclipse.muto.sandbox::simulator-monster-01/stack/commands/active
```
```yaml
payload: {
    "name": "Muto Learning Simulator with Gap Follwer",
    "context": "eteration_office",
    "stackId": "ai.composiv.sandbox.f1tenth:composiv_simulator_gf.launch",
    "stackId": "org.eclipse.muto.sandbox::composiv_simulator_gf.launch",
    "stack": [
        {
            "thingId": "ai.composiv.sandbox.f1tenth:composiv_simulator.launch"
            "thingId": "org.eclipse.muto.sandbox::composiv_simulator.launch"
        }
    ],
    "node": [
Expand All @@ -159,13 +119,16 @@ payload: {
You can use any open-source mqtt client to issue these commands and monitor various muto twin messages [MQTTX](https://mqttx.app/). Another option is the [mosquitto_pub](https://mosquitto.org/man/mosquitto_pub-1.html), which is a simple MQTT version 5/3.1.1 client that will publish a single message on a topic and exit. You can publish the message described above using the commandline:

```bash
mosquitto_pub -d -h sandbox.composiv.ai -p 1883 -t "ai.composiv.sandbox.f1tenth:simulator-monster-01/stack/commands/active" -m '{"name":"Composiv Learning Simulator with Gap Follwer","context":"eteration_office","stackId":"ai.composiv.sandbox.f1tenth:composiv_simulator_gf.launch","stack":[{"thingId":"ai.composiv.sandbox.f1tenth:composiv_simulator.launch"}],"node":[{"name":"cass_gap_follower","pkg":"cass_gap_follower","exec":"cass_gap_follower","param":[{"from":"$(find cass_gap_follower)/params.yaml"}]}]}'
mosquitto_pub -d -h sandbox.composiv.ai -p 1883 -t "org.eclipse.muto.sandbox::simulator-monster-01/stack/commands/active" -m '{"name":"Composiv Learning Simulator with Gap Follwer","context":"eteration_office","stackId":"org.eclipse.muto.sandbox::composiv_simulator_gf.launch","stack":[{"thingId":"org.eclipse.muto.sandbox::composiv_simulator.launch"}],"node":[{"name":"cass_gap_follower","pkg":"cass_gap_follower","exec":"cass_gap_follower","param":[{"from":"$(find cass_gap_follower)/params.yaml"}]}]}'

```


## Controlling the F1Tenth Car (navigate on/off)

```bash
rostopic pub --once /mux std_msgs/Int32MultiArray "{layout: { dim: [], data_offset: 0}, data: [0, 0, 0, 0, 1 , 0] }"
ros2 topic pub --once /mux std_msgs/Int32MultiArray "{layout: { dim: [], data_offset: 0}, data: [0, 0, 0, 0, 1 , 0] }"
```

## More information
Check out the Eclipse Muto github.io page for further reading: [Eclipse Muto](https://eclipse-muto.github.io/docs/docs/muto)
Empty file added composer/__init__.py
Empty file.
106 changes: 106 additions & 0 deletions composer/compose_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#
# Copyright (c) 2023 Composiv.ai, Eteration A.S. and others
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v2.0
# and Eclipse Distribution License v1.0 which accompany this distribution.
#
# The Eclipse Public License is available at
# http://www.eclipse.org/legal/epl-v10.html
# and the Eclipse Distribution License is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# Contributors:
# Composiv.ai, Eteration A.S. - initial API and implementation
#

import os
import yaml
import json
import rclpy
from rclpy.node import Node

from ament_index_python.packages import get_package_share_directory
from composer.stack import Stack
from composer.twin import Twin
from composer.edge_device import EdgeDevice
from muto_msgs.srv import ComposePlugin
from muto_msgs.msg import PluginResponse, StackManifest, PlanManifest


class MutoDefaultComposePlugin(Node):
def __init__(self):
super().__init__("compose_plugin")

self.declare_parameter("name", "example-01")
self.declare_parameter("namespace", "org.eclipse.muto.sandbox")
self.declare_parameter("stack_topic", "stack")
self.declare_parameter("twin_topic", "twin")
self.declare_parameter("anonymous", False)
self.declare_parameter(
"twin_url", "http://ditto:[email protected]")

self.name = self.get_parameter("name").value
self.namespace = self.get_parameter("namespace").value
self.stack_topic = self.get_parameter("stack_topic").value
self.twin_topic = self.get_parameter("twin_topic").value
self.twin_url = self.get_parameter("twin_url").value
self.anonymous = self.get_parameter("anonymous").value

self.muto = {
"name": self.name,
"namespace": self.namespace,
"stack_topic": self.stack_topic,
"twin_topic": self.twin_topic,
"twin_url": self.twin_url,
"anonymous": self.anonymous
}

self.srv = self.create_service(
ComposePlugin, "muto_compose", self.handle_compose
)

self.twin = Twin(node='muto_compose_plugin',
config=self.muto, publisher=None)
self.edge_device = EdgeDevice(twin=self.twin)

def handle_compose(self, req, res):
plan = req.input

st = json.loads(plan.current.stack)
current_stack = Stack(self.edge_device, st, None)
st = json.loads(plan.next.stack)

if st.get('stackId', None) is not None:
manifest = self.twin.stack(st['stackId'])
nextStack = Stack(self.edge_device, manifest, None)
else:
nextStack = Stack(self.edge_device, st, None)

merged = current_stack.merge(nextStack)

res.output = PlanManifest(
current=plan.current,
next=plan.next,
pipeline=plan.pipeline,
planned=StackManifest(
type="json",
stack=json.dumps(merged.manifest)
),
result=PluginResponse(
result_code=0, error_message="", error_description=""
)
)
return res


def main(args=None):
rclpy.init(args=args)
node = MutoDefaultComposePlugin()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()


if __name__ == "__main__":
main()
Loading

0 comments on commit cebc72e

Please sign in to comment.