diff --git a/config/config.yaml b/config/config.yaml
index 97af7cc5fff92078f36a216b2c48870529fbb16c..22d7c81f11a593a6d914f71a9882a0309101cf80 100644
--- a/config/config.yaml
+++ b/config/config.yaml
@@ -1,5 +1,3 @@
-connector_node_ros_cgv:
-  cgv_address: "tcp://*:6576"
 max_grasp_approach_velocity: 0.2
 max_grasp_approach_acceleration: 0.2
 max_grasp_transition_velocity: 0.2
diff --git a/launch/cleaning/cleaning_double__dummy_controller_and_dummy_cgv.launch b/launch/cleaning/cleaning_double__dummy_controller_and_dummy_cgv.launch
new file mode 100644
index 0000000000000000000000000000000000000000..757a39d0011b4aa5c2990c86b702803f66c68c1a
--- /dev/null
+++ b/launch/cleaning/cleaning_double__dummy_controller_and_dummy_cgv.launch
@@ -0,0 +1,11 @@
+<launch>
+    <node pkg="ccf" type="dummy_cgv" name="my_dummy_cgv" output="screen" />
+    <node pkg="ccf" type="dummy_cgv_controller" name="my_first_dummy_cgv_controller" output="screen">
+        <param name="connection_address" type="string" value="tcp://*:6576" />
+        <param name="client_controllers" type="yaml" value="['tcp://127.0.0.1:6586']" />
+    </node>
+    <node pkg="ccf" type="dummy_cgv_controller" name="my_second_dummy_cgv_controller" output="screen">
+        <param name="connection_address" type="string" value="tcp://*:6586" />
+        <param name="client_controllers" type="yaml" value="[]" />
+    </node>
+</launch>
diff --git a/launch/cleaning/cleaning_dummy_controller.launch b/launch/cleaning/cleaning_dummy_controller.launch
index 3b28246a90a412e9fd14db9c2054bcfa2e568350..7ce2c9c6d21d609514c386376aad87e93c6b1ebe 100644
--- a/launch/cleaning/cleaning_dummy_controller.launch
+++ b/launch/cleaning/cleaning_dummy_controller.launch
@@ -1,3 +1,11 @@
 <launch>
-    <node pkg="ccf" type="dummy_cgv_controller" name="my_dummy_cgv_controller" output="screen" />
+
+    <arg name="connection_address" default="tcp://*:6576" />
+    <arg name="client_controllers" default="[]" />
+
+    <node pkg="ccf" type="dummy_cgv_controller" name="my_dummy_cgv_controller" output="screen">
+        <param name="connection_address" type="string" value="$(arg connection_address)" />
+        <param name="client_controllers" type="yaml" value="$(arg client_controllers)" />
+    </node>
+
 </launch>
diff --git a/launch/cleaning/cleaning_dummy_controller_and_dummy_cgv.launch b/launch/cleaning/cleaning_dummy_controller_and_dummy_cgv.launch
index ebb269c59dd0bbaafae583b58e66b6b6f094fbc7..0caf4848393fd80c6b1d78b9aad651df2db7de1b 100644
--- a/launch/cleaning/cleaning_dummy_controller_and_dummy_cgv.launch
+++ b/launch/cleaning/cleaning_dummy_controller_and_dummy_cgv.launch
@@ -1,4 +1,4 @@
 <launch>
     <node pkg="ccf" type="dummy_cgv" name="my_dummy_cgv" output="screen" />
-    <node pkg="ccf" type="dummy_cgv_controller" name="my_dummy_cgv_controller" output="screen" />
+    <include file="$(find ccf)/launch/cleaning/cleaning_dummy_controller.launch" />
 </launch>
diff --git a/launch/cleaning/cleaning_robot_controller.launch b/launch/cleaning/cleaning_robot_controller.launch
index a44470f58df938f65dfeca90c1567964050519c2..206780e84ea419d32aa204f1925490fdcce37c5c 100644
--- a/launch/cleaning/cleaning_robot_controller.launch
+++ b/launch/cleaning/cleaning_robot_controller.launch
@@ -2,12 +2,17 @@
 
     <arg name="robot_ip" default="172.31.1.13" />
     <arg name="load_gripper" default="true" />
+    <arg name="connection_address" default="tcp://*:6576" />
+    <arg name="client_controllers" default="[]" />
 
     <include file="$(find ccf)/launch/cleaning/_robot_setup.launch" >
         <arg name="robot_ip" value="$(arg robot_ip)" />
         <arg name="load_gripper" value="$(arg load_gripper)"/>
     </include>
 
-    <node pkg="ccf" type="moveit_cgv_controller" name="moveit_cgv_controller_instance" output="screen" />
+    <node pkg="ccf" type="moveit_cgv_controller" name="moveit_cgv_controller_instance" output="screen" >
+        <param name="connection_address" type="string" value="$(arg connection_address)" />
+        <param name="client_controllers" type="yaml" value="$(arg client_controllers)" />
+    </node>
 
 </launch>
\ No newline at end of file
diff --git a/launch/cleaning/cleaning_robot_controller_and_dummy_cgv.launch b/launch/cleaning/cleaning_robot_controller_and_dummy_cgv.launch
index d5e84b2b4f7df7173590eb8080f29c902dd4431f..190cb8536bccc44f0ae5cdae86319f72f29a829b 100644
--- a/launch/cleaning/cleaning_robot_controller_and_dummy_cgv.launch
+++ b/launch/cleaning/cleaning_robot_controller_and_dummy_cgv.launch
@@ -1,14 +1,4 @@
 <launch>
-
-    <arg name="robot_ip" default="172.31.1.13" />
-    <arg name="load_gripper" default="true" />
-
-    <include file="$(find ccf)/launch/cleaning/_robot_setup.launch" >
-        <arg name="robot_ip" value="$(arg robot_ip)" />
-        <arg name="load_gripper" value="$(arg load_gripper)"/>
-    </include>
-
-    <node pkg="ccf" type="moveit_cgv_controller" name="moveit_cgv_controller_instance" output="screen" />
     <node pkg="ccf" type="dummy_cgv" name="my_dummy_cgv" output="screen" />
-
-</launch>
\ No newline at end of file
+    <include file="$(find ccf)/launch/cleaning/cleaning_robot_controller.launch" />
+</launch>
diff --git a/launch/cleaning/cleaning_simulation.launch b/launch/cleaning/cleaning_simulation.launch
index 60f3f7ce950292a70e50ad1fddbcf07f0c265489..f9b5a6cd9961d5da418ce24fc28302166b21ae92 100644
--- a/launch/cleaning/cleaning_simulation.launch
+++ b/launch/cleaning/cleaning_simulation.launch
@@ -1,7 +1,13 @@
 <launch>
 
+    <arg name="connection_address" default="tcp://*:6576" />
+    <arg name="client_controllers" default="[]" />
+
     <include file="$(find ccf)/launch/cleaning/_simulation_setup.launch"/>
 
-    <node pkg="ccf" type="moveit_cgv_controller" name="moveit_cgv_controller_instance" output="screen" />
+    <node pkg="ccf" type="moveit_cgv_controller" name="moveit_cgv_controller_instance" output="screen" >
+        <param name="connection_address" type="string" value="$(arg connection_address)" />
+        <param name="client_controllers" type="yaml" value="$(arg client_controllers)" />
+    </node>
 
 </launch>
\ No newline at end of file
diff --git a/launch/cleaning/cleaning_simulation_and_dummy_cgv.launch b/launch/cleaning/cleaning_simulation_and_dummy_cgv.launch
index 218804860ea27a429a200b2f6f0cc5f2e4179cff..96d7379a184345a50379497e10b5cb737fc78e72 100644
--- a/launch/cleaning/cleaning_simulation_and_dummy_cgv.launch
+++ b/launch/cleaning/cleaning_simulation_and_dummy_cgv.launch
@@ -1,8 +1,4 @@
 <launch>
-
-    <include file="$(find ccf)/launch/cleaning/_simulation_setup.launch"/>
-
-    <node pkg="ccf" type="dummy_cgv" name="dummy_cgv_instance" output="screen" />
-    <node pkg="ccf" type="moveit_cgv_controller" name="moveit_cgv_controller_instance" output="screen" />
-
-</launch>
\ No newline at end of file
+    <node pkg="ccf" type="dummy_cgv" name="my_dummy_cgv" output="screen" />
+    <include file="$(find ccf)/launch/cleaning/cleaning_simulation.launch" />
+</launch>
diff --git a/src/ccf/controller/RobotArmController.cpp b/src/ccf/controller/RobotArmController.cpp
index 9ea39c59b97a8d89ed1d63c4403025dc7cc91d1b..9b39591609db4cc91cf15a112e7e5b602ce1255d 100644
--- a/src/ccf/controller/RobotArmController.cpp
+++ b/src/ccf/controller/RobotArmController.cpp
@@ -44,8 +44,6 @@ Object *RobotArmController::resolveObject(const std::string &id) {
 void RobotArmController::sendScene() {
     if (scene) { // meaning if the (smart) pointer is not a nullptr
 
-        scene->SerializeAsString();
-
         ROS_INFO_STREAM("[ros2cgv] Sending scene with " << scene->objects().size() << " objects.");
         sendToAll(sendSceneTopic, scene->SerializeAsString());
         sceneUpdateAction();
diff --git a/src/dummy_cgv_controller.cpp b/src/dummy_cgv_controller.cpp
index 191078a3c69c06df4293600bcae37c63fbfb7c3c..5714c2f3f7d1784db5e53b22dd05ba6dda478bac 100644
--- a/src/dummy_cgv_controller.cpp
+++ b/src/dummy_cgv_controller.cpp
@@ -19,19 +19,21 @@
 #include "ccf/connection/NngConnection.h"
 #include "ccf/util/NodeUtil.h"
 
-const std::string NODE_NAME = "ros2cgv_dummy";
+std::string NODE_NAME;
 
 using CetiRosToolbox::getParameter;
+using CetiRosToolbox::getPrivateParameter;
 
 int main(int argc, char **argv) {
 
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 
+    NODE_NAME = ros::this_node::getName();
     ros::init(argc, argv, NODE_NAME);
 
     ros::NodeHandle n("connector_node_ros_cgv"); // namespace where the connection_address is
 
-    std::string cgv_address = getParameter<std::string>(n, "connection_address", "tcp://*:6576");
+    auto cgv_address = getPrivateParameter<std::string>("connection_address", "tcp://*:6576");
 
     DummyRobotArmController connector{n, NODE_NAME};
 
@@ -41,6 +43,16 @@ int main(int argc, char **argv) {
     connection->setSendTopic(getParameter(n, "topics/scene", NODE_NAME + "/scene/update"));
     connector.addConnection(std::move(connection));
 
+    auto clientControllers = getPrivateParameter<std::vector<std::string>>("client_controllers");
+
+    ROS_INFO_STREAM("[" << NODE_NAME << "] Connecting to " << clientControllers.size() << " client controllers.");
+    for (const auto &client : clientControllers) {
+        ROS_INFO_STREAM("[" << NODE_NAME << "] Connecting to client at " << client << ".");
+        std::unique_ptr<NngConnection> client_connection = std::make_unique<NngConnection>(client, false);
+        client_connection->setSendTopic(getParameter<std::string>(n, "topics/selection", "selection"));
+        connector.addConnection(std::move(client_connection));
+    }
+
     connector.loadScene(ros::package::getPath("ccf") + "/config/config_scene.yaml");
 
     Object *robot = nullptr;
@@ -53,9 +65,13 @@ int main(int argc, char **argv) {
     ros::Timer timer = n.createTimer(ros::Duration(10), [&connector](
             const ros::TimerEvent &event) { connector.sendScene(); }); // send a scene every ten seconds
 
-    auto selectionMessageCallback = [&currentlyPickedBox, &connector, &robot, &selectedBox, &selectedBin](
+    auto selectionMessageCallback = [&currentlyPickedBox, &connector, &robot, &selectedBox, &selectedBin, &n](
             const Selection &selection) {
 
+        // forward the selection to the clients
+        connector.sendToAll(getParameter<std::string>(n, "topics/selection", "selection"),
+                            selection.SerializeAsString());
+
         if (currentlyPickedBox.has_value()) {
             ROS_WARN_STREAM("[" << NODE_NAME << "] Unable to accept selections while picking is in progress.");
             return;
diff --git a/src/dummy_rag_controller_a.cpp b/src/dummy_rag_controller_a.cpp
index f1ee30c697f28ef5c183553221b0865ae6aaa5ad..2d44325f68f9b9e3c43e835c208bdd2ded372c37 100644
--- a/src/dummy_rag_controller_a.cpp
+++ b/src/dummy_rag_controller_a.cpp
@@ -28,6 +28,7 @@ const std::string CELL_NAME = "place-a";
 const std::string NODE_NAME = "rag_connector_dummy_a";
 
 using CetiRosToolbox::getParameter;
+using CetiRosToolbox::getPrivateParameter;
 
 int main(int argc, char **argv) {
 
@@ -37,7 +38,7 @@ int main(int argc, char **argv) {
 
     ros::NodeHandle n("ceti_connector"); // namespace
 
-    std::string cgv_address = getParameter<std::string>(n, "cgv_address", "tcp://*:6576");
+    auto cgv_address = getPrivateParameter<std::string>("connection_address", "tcp://*:6576");
 
     DummyRobotArmController connector{n, CELL_NAME};
 
diff --git a/src/dummy_rag_controller_b.cpp b/src/dummy_rag_controller_b.cpp
index 0004ef290fa7d42533c9f87b2190daa729f8bea0..464a69c28919feadc976156c2e40eb1c6e1c7d24 100644
--- a/src/dummy_rag_controller_b.cpp
+++ b/src/dummy_rag_controller_b.cpp
@@ -27,6 +27,7 @@ const std::string CELL_NAME = "place-b";
 const std::string NODE_NAME = "rag_connector_dummy_b";
 
 using CetiRosToolbox::getParameter;
+using CetiRosToolbox::getPrivateParameter;
 
 int main(int argc, char **argv) {
 
@@ -36,7 +37,7 @@ int main(int argc, char **argv) {
 
     ros::NodeHandle n("ceti_connector"); // namespace
 
-    std::string cgv_address = getParameter<std::string>(n, "cgv_address", "tcp://*:6576");
+    auto cgv_address = getPrivateParameter<std::string>("connection_address", "tcp://*:6576");
 
     std::string cell_prefix = getParameter(n, "place_b_prefix", CELL_NAME);
 
diff --git a/src/moveit_cgv_controller.cpp b/src/moveit_cgv_controller.cpp
index 58cb6597bad21dfd0c0f1555a9f6d30e4186f358..d38f637b50df1580e83bf013b676ff9c9c53b322 100644
--- a/src/moveit_cgv_controller.cpp
+++ b/src/moveit_cgv_controller.cpp
@@ -18,19 +18,21 @@
 #include "ccf/connection/NngConnection.h"
 #include "ccf/util/NodeUtil.h"
 
-const std::string NODE_NAME = "ros2cgv_moveit";
+std::string NODE_NAME;
 
 using CetiRosToolbox::getParameter;
+using CetiRosToolbox::getPrivateParameter;
 
 int main(int argc, char **argv) {
 
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 
+    NODE_NAME = ros::this_node::getName();
     ros::init(argc, argv, NODE_NAME);
 
     ros::NodeHandle n("connector_node_ros_cgv"); // namespace where the connection_address is
 
-    std::string cgv_address = getParameter<std::string>(n, "connection_address", "tcp://*:6576");
+    auto cgv_address = getPrivateParameter<std::string>("connection_address", "tcp://*:6576");
 
     MoveItRobotArmController connector{n, NODE_NAME};
 
@@ -40,6 +42,16 @@ int main(int argc, char **argv) {
     connection->setSendTopic(getParameter(n, "topics/scene", NODE_NAME + "/scene/update"));
     connector.addConnection(std::move(connection));
 
+    auto clientControllers = getPrivateParameter<std::vector<std::string>>("client_controllers");
+
+    ROS_INFO_STREAM("[" << NODE_NAME << "] Connecting to " << clientControllers.size() << " client controllers.");
+    for (const auto &client : clientControllers) {
+        ROS_INFO_STREAM("[" << NODE_NAME << "] Connecting to client at " << client << ".");
+        std::unique_ptr<NngConnection> client_connection = std::make_unique<NngConnection>(client, false);
+        client_connection->setSendTopic(getParameter<std::string>(n, "topics/selection", "selection"));
+        connector.addConnection(std::move(client_connection));
+    }
+
     // scene loading is not required, the scene is updated by moveit
 
     Object *robot = nullptr;
@@ -52,9 +64,13 @@ int main(int argc, char **argv) {
     ros::Timer timer = n.createTimer(ros::Duration(10), [&connector](
             const ros::TimerEvent &event) { connector.sendScene(); }); // send a scene every ten seconds
 
-    auto selectionMessageCallback = [&currentlyPickedBox, &connector, &robot, &selectedBox, &selectedBin](
+    auto selectionMessageCallback = [&currentlyPickedBox, &connector, &robot, &selectedBox, &selectedBin, &n](
             const Selection &selection) {
 
+        // forward the selection to the clients
+        connector.sendToAll(getParameter<std::string>(n, "topics/selection", "selection"),
+                            selection.SerializeAsString());
+
         if (currentlyPickedBox.has_value()) {
             ROS_WARN_STREAM("[" << NODE_NAME << "] Unable to accept selections while picking is in progress.");
             return;