Digital Twin task logs for Collective Robotic Construction (CRC) — ROB|ARCH 2024
Digital Twin task logs for Collective Robotic Construction (CRC) — ROB|ARCH 2024
Overview
This repository contains a dataset of Digital Twin (DT) task logs from a Collective Robotic Construction (CRC) workshop held at ROB|ARCH 2024. Over three days, 14 participants programmed eight low-cost mobile robots (RADr) to develop and test decentralised construction behaviours, while a Vicon motion tracking system provided global state feedback.
The dataset captures the DT’s task-level interaction with:
- 8 RADr robots (mobile, magnetic gripper, onboard sensors)
- Vicon (external tracking of robot + material poses)
- Digital material modules (passive tracked objects; labelled
DM0…)
Each experimental run is recorded as a JSON array of task records (e.g., Move, Grip, Read) with timestamps, task parameters, and the corresponding responses from the physical actors.
Case study context (CRC)
- Workspace: ~4.6 × 5.8 m floor divided into a 4 × 5 grid.
- Tracking: Vicon motion capture (overhead coverage).
- Robots: 8 × RADr (2-wheel drive, magnetic gripper, onboard proximity/boundary sensors).
- Materials: passive “digital material” modules with retroreflective markers (tracked by Vicon).
- Execution mode: Adaptive multi-actor execution (robots act in parallel; robots can be inserted/removed/reprogrammed during a run; DT maintains shared situational awareness).
The DT architecture instantiated two principal task families:
- RADr tasks:
Move, Grip
- Vicon tasks:
Read (a.k.a. ReadState snapshots)
Repository structure
.
├── Data/
│ ├── Run-1.json
│ ├── Run-2.json
│ ├── ...
│ └── Run-12.json
├── Figures/
│ ├── Tasks and durations per CRC run.png
│ ├── Average task rate per RADr robot.png
│ └── Per RADr control loop times.png
├── Analysis.py
└── Replay_Run.gh
What each top-level item is
Data/Run-*.json: Primary dataset. One JSON file per CRC run (job).
Figures/: Example plots generated from Analysis.py (included for convenience).
Analysis.py: Reference analysis script that loads the runs and reproduces the included plots.
Replay_Run.gh: Grasshopper definition (Rhino/Grasshopper) for replaying/visualising a run (see “Replay” section below).
Runs included
The dataset contains 12 runs (Run-1 … Run-12). Each run is a task log containing a mixture of:
- Vicon
Read snapshots (typically ~1 Hz)
- RADr
Move tasks (motor commands)
- RADr
Grip tasks (motor commands + gripper toggle)
Overall totals (all runs combined):
- Total task records: 22,571
- Vicon
Read: 12,688
- RADr
Move: 8,080
- RADr
Grip: 1,234
- Mean run duration: 18.43 min (range: 8.98–40.72 min)
Data format
Each Data/Run-*.json file is a JSON array:
[
{ "task_id": "...", "task_type": "Read", "main_actor": "Vicon", ... },
{ "task_id": "...", "task_type": "Move", "main_actor": "RADr_3", ... },
...
]
Task record schema (common fields)
Task Data Schema that includes the following fields:
- task_id: A unique identifier for the task record (UUID).
- name: A human-readable task name (e.g.,
"Wander", "Pick", "Vicon_ReadValues").
- task_type: The task category/type (one of
Read, Move, Grip; plus one Capture placeholder).
- main_actor: The physical actor that executed the task (e.g.,
Vicon, RADr_0…RADr_7).
- description: Optional description text for the task.
- message: A log message associated with the task.
- element_id: Optional list of related design element references (empty in these runs).
- job: The run identifier associated with this record (e.g.,
"Run-4").
- level: Process hierarchy level (mostly
0 in these logs).
- task_index: Task order/index value (
null in these logs).
- actors_data: Task input parameters grouped per actor (nested details omitted).
- start_time: Task start timestamp (ISO 8601 string) or
null.
- end_time: Task end timestamp (ISO 8601 string) or
null.
- progress: Task state indicator (observed values:
0, 1, 2).
- response: Task output/response payload for the actor (may be
null; nested details omitted).
- project: Project identifier (e.g.,
"col_robots").
Task types and their data structures
1) Vicon — Read
Purpose: request a global environment snapshot from Vicon.
Typical fields:
main_actor: "Vicon"
task_type: "Read"
actors_data: {"Vicon": {"ReadData": "true"}}
response: {"Vicon": "<JSON string>"}
Vicon response payload structure
After json.loads(record["response"]["Vicon"]), you obtain a dictionary:
{
"RADr_0": [ [ [x,y,z], flag ], [ [qx,qy,qz,qw], flag ] ],
"RADr_1": [ ... ],
...
"DM0": [ [ [x,y,z], flag ], [ [qx,qy,qz,qw], flag ] ],
...
}
Where:
x,y,z are positions in the Vicon world frame (values suggest millimetres, z ~ 60–130 mm for floor objects).
qx,qy,qz,qw are a quaternion orientation.
flag is a Boolean that indicate measurement validity/occlusion (in these logs it is typically false).
Entity keys include:
- Robots:
RADr_0 … RADr_7
- Digital materials:
DM0 … DM24
2) RADr — Move and Grip
RADr tasks control an individual two-wheeled robot (RADr_i). In this dataset there are two closely related task types:
Move: send a wheel-motor command sequence.
Grip: send a wheel-motor command sequence and toggle the magnetic gripper.
Typical fields:
main_actor: "RADr_6" (or any RADr_i)
task_type: "Move" or "Grip"
actors_data["RADr_i"]["move_vectors"]: a string representing a list of 2D wheel-force vectors, one per control step
actors_data["RADr_i"]["gripper"] (only for Grip): "True" / "False" (stored as a string)
Example actors_data snippets:
// Move
"actors_data": {
"RADr_6": {
"move_vectors": "[[-0.1, 0.0], [0.0, 0.65], [0.0, 0.8], [0.0, 1.0]]"
}
}
// Grip
"actors_data": {
"RADr_6": {
"gripper": "True",
"move_vectors": "[[0.0, 0.5], [0.0, 1.0], [0.0, 1.0]]"
}
}
RADr response payload (common to Move and Grip)
response typically contains a per-robot entry whose value is a JSON string that must be parsed with json.loads(...). After parsing, the response provides an updated local state from onboard sensors:
KeyTypeMeaning
Material_SensorboolWhether a material module is detected nearby.
GripperboolCurrent magnetic gripper state.
Robot_SensorintRobot proximity sensor indicator (values observed: -1, 0, …).
Boundary_SensorboolBoundary detection (e.g., leaving the work area).
CounterintA robot-side counter / tick value (useful for debugging timing).
Derived metrics (as used in Analysis.py)
The included analysis script derives two useful “DT performance” proxies from the task logs:
Task rate per robot
For each robot and run:
task_rate = (# Move + Grip tasks) / (robot active time)
Active time is computed from the robot’s first task start to its last task end within a run.
Average task-rate summary (mean ± std across runs):
Overall mean across robots (mean of robot means): 8.44 tasks/min.
Approximate per-robot control-loop time
For each robot, an “ABM-like loop time” is approximated as:
loop_time_s = start_time(next task) - end_time(current task)
This captures the combined effects of:
- participant logic / ABM compute time,
- DT engine scheduling,
- communication latency,
- and any short pauses between tasks.
Overall mean (after outlier removal): 0.66 s.
Usage
1) Load the dataset in Python
import json
import pandas as pd
with open("Data/Run-1.json", "r") as f:
tasks = json.load(f)
df = pd.DataFrame(tasks)
2) Reproduce the included figures
Install dependencies:
pip install numpy pandas matplotlib
Run:
python Analysis.py
The script reads all files in Data/ and writes plots to Figures/.
3) Replay in Grasshopper (Rhino)
Replay_Run.gh is a Grasshopper definition intended to visualise a run by reading Vicon snapshots from the task logs and animating robot/material poses. Open it in Rhino/Grasshopper and ensure the Data/ folder path is correctly set inside the definition.
(Exact inputs/parameters depend on your Grasshopper environment; this repository does not include a *.ghx parameter manifest.)
Notes and known quirks
- Timestamps:
start_time/end_time are ISO 8601 strings; a small fraction of records may contain null.
- Units: Vicon positions are presented in millimetres in a fixed world frame (based on magnitude vs. the physical workspace size).