## Мета-модель фреймворка ## Соответствие Digital Twin Definition Language и мета-модели Robossembler Слои абстракции 1. DTDL - BASETYPES, COMPLEXTYPES (Array, Map, ...), DT_TYPES 2. ROBOSSEMBLER_CONTEXT (Represents) 3. USE_CASE_CONTEXT (Entities) 4. USE_CASE_INSTANCE (Product) ## Сравнение мета-моделей Digital Twin Definition Language и ROS 2 | Сущность | DTDL | ROS | Robonomics | | - | - | - | - | | Описывает всё содержимое двойника, включая ниже приведённые сущности | **Interface** | **Interface Spec** | - | | Передаваемые данные и их тип | **Telemetry** | **Topic** | **Datalog Hash** | | Свойство, описывающее какое-то состояние двойника, может быть read-only или read/write; также описывает синхронизацию состояния между компонентами (например, показание датчика записано в облако) | **Property** | **Parameters** | **Launch Param** | | Функция или операция, которая может быть осуществлена над двойником (например, `reboot`) | **Command** | **Service** / **Action** | **Launch** | | Структура из примитивных типов (Array, Enum, Map, Object) данных для сериализации (в JSON, Avro, Protobuf) | **Schema** | **IDL** | - | | Часть интерфейса (отношение part-of с каким-то другим двойником) | **Component** | - | - | | Связь с другим цифровым двойником. Связи могут представлять различные семантические значения. Например, `contains` ("этаж содержит комнату"), `cools` ("hvac cools room"), `isBilledTo` ("счёт выставляется пользователю") | **Relationship** | - | - | ```json # базовые типы значений (из JSON Schema) "string","number", "boolean", "array", "object"; { "type": "object", "properties": { "REPRESENT": { "type": { "enum": [ "OBJECT_LINK", "KEY", "FILEPATH", "VALUE", "TREE", "ARRAY", "SEQUENCE" ] } } } } # представления ENUM REPRESENT = "OBJECT_LINK", # строка-ссылка на объект (ENTITY) "KEY", # уникальный ключ в виде строки (используется как идентификатор записи) "FILEPATH", # строка - путь к файлу "VALUE", # непосредственное значение "TREE", # представление в виде дерева "ARRAY", # массив значений "SEQUENCE"; # массив ссылок на объект определённого типа # сущности ENUM ENTITY = "MESH", "PART", "ASSET", "BTREE", "BTACTION", "SKILL", "DATASET", "INTERFACE", "WEIGHTS", "DEVICE"; ENUM DEVICE = "ROBOT", "SENSOR"; type SCENE = { "objects": [ { ref: "1", type: "PART" }; { ref: "2", type: "PART" }; ] }; type PARAM = { "sid": \${KEY:string:""}, "name": \${NAME:string:""}, "represent": \${REPRESENT:Enum:"VALUE"}, "link": \${LINK:Enum:"topic"} }; ### тип поверхность детали type MESH/SURFACE = { "sid": \${KEY:string:""}; "path": { "stl": "PATH/*.stl", "brep": "PATH/*.brep", "fbx": "PATH/*.fbx", } }; type PART = { "sid": \${NAME:string:""}, "name": \${NAME:string:""}, "pose6d": { "loc_xyz": \${XYZ:Array3:[0.0,0.0,0.0] }, "rot_xyzw": \${XYZW:Array4:[0.0,0.0,0.0,1.0]} }, "attributes": [ "Robossembler_NonSolid": True ], "surface": { "stl": "PATH/*.stl", "brep": "PATH/*.brep", }, "material": "/path/to/robossembler/materials/mmm.FCMat", "unit_scale": \${UNIT_SCALE:number:1.0}, "dimensions": \${Array3:[0.0,0.0,0.0]}, "assets": { "fbx": "PATH/*.fbx", "blend": "PATH/*.blend", } }; type DATASET = { "sid": \${NAME:string:""}, "name": \${NAME:string:""}, "objects": \${SEQUENCE:PART}, "environment": \${SEQUENCE:PART}, "randomisation": \${FILE} }; type WEIGHTS = { "sid": \${NAME:string:""}, "name": \${NAME:string:""}, "file": \${FILE:string:"*.pth"}, "epoch": \${EPOCH:number:""}, "dataset": \${OBJECT_LINK:DATASET} }; type TOPIC = { "sid": ..., "name": "topic_name", "msg": "sensor_msgs/Image", }; DEVICE = { "sid": 1235, "name": "dev", "topics": \${SEQUENCE:TOPIC}, } // DEVICE -> TOPIC LIST -> {DEVICE: {TOPIC LIST}} type POSE_ESTIMATION = { "object_name": \${OBJECT_LINK:PART}, "weights": \${OBJECT_LINK:WEIGHTS}, "topic_name": \${OBJECT_LINK:TOPIC} }; type SKILL = { "sid": \${NAME:string:""}, "name": \${NAME:string:""}, "interface": \${INTERFACE} }; command_LifeCycle = "run", "stop" type ASSEMBLY_SEQUENCE = \{SEQUENCE:TASK}; # task1 = { source_state = []; target_state = [ p1 ] } # task2 = { source_state = [ p1 ]; target_state = [ p1 p2 ] } # task3 = { source_state = [ p1 p2 ]; target_state = [ p1 p2 p3 ] } task = { source_state = \${TREE:PART}; target_state = \${TREE:PART} } type TASK = { "sid": ... "action": \${BT_TREE} "source_state": \${TREE:PART} // PART "target_state": \${TREE:PART} // PRODUCT }; type DOMAIN = { "objects": \{SEQUENCE:PART} "predicates": \{OBJECT_LINK:CONDITION} "actions": \${OBJECT_LINK:BT_ACTION} }; type BTREE = { "sid": \${NAME:string:""}, "name": \${NAME:string:""}, }; ``` ## Device Package ### Camera ```json { "DevicePackage": { "name": "Robossembler", "version": "1.0", "format": "1" }, "Module": { "name": "RealSense Dxx", "description": "ROS Wrapper for Intel(R) RealSense(TM) Cameras" }, "Launch": { "package": "realsense2_camera", "executable": "rs_launch.py" }, "DTwin": [ { "interface": { "input": [ { "name": "camera_namespace", "type": "STRING" }, // -- /robot_drawer/455_1/camera_info { "name": "camera_name", "type": "STRING" }, { "name": "serial_port", "type": "STRING" }, ], "output": [ { "name": "camera_info", "type": "TOPIC", "topic_name": "/${camera_namespace}/${camera_name}/camera_info" }, { "name": "pose", "type": "TOPIC", "msg": "Pose" } ] }, } ], "Settings": [ { "name": "camera_config", "description": "Camera Config", "type":"file", "defaultValue": "{ rgb_camera.profile: 1280x720x15 }" } ] } ``` ### Robot RBS ```json { "DevicePackage": { "name": "Robossembler", "version": "1.0", "format": "1" }, "Module": { "name": "RBS", "description": "Main Robot" }, "Launch": { "package": "rbs_bringup", "executable": "single_robot.launch.py" }, "DTwin": [ { "interface": { "input": [ { "name": "robot_namespace", "type": "STRING", "defaultValue": "rbs_arm" }, { "name": "dof", "type": "INT", "defaultValue": 6 } ] } } ], "Settings": [ { "name": "robot_type", "description": "Type of robot by name", "defaultValue": "rbs_arm" } ] } ``` ### Robot UR5 ```json { "DevicePackage": { "name": "Robossembler", "version": "1.0", "format": "1" }, "Module": { "name": "UR", "description": "..." }, "Launch": { "package": "ur_package", "executable": "ur5_single_arm.py" }, "DTwin": [ { "interface": { "input": [ { "robot_namespace": "robot1" }, ], }, } ], "Settings": [ { "name": "", "description": "Config", "type":"file", "defaultValue": "{}" } ] } ``` ```json { "SkillPackage": { "name": "Robossembler", "version": "1.0", "format": "1" }, "Module": { "name": "PoseEstimation", "description": "Pose Estimation skill with DOPE" }, "Launch": { "package": "rbs_perception", "executable": "pe_dope_lc.py", "name": "lc_dope" }, "BTAction": [ { "name": "peConfigure", "type": "run", "interface": { "input": [ { "name": "image_raw", "type": "TOPIC", "msg": "Image" }, { "name": "camera_info", "type": "TOPIC", "msg": "CameraInfo" }, { "name": "object_name", "type": "PART", "msgs": "Part" }, // string { "name": "weights", "type": "WEIGHTS", "msgs": "" }, ], "output": [ { "name": "pose_estimation", "type": "TOPIC" }, ] }, }, { "name": "peStop", "type": "stop", "interface": { "input": [], "output": [] } } ], "Settings": [ { "name": "publishDelay", "description": "Publish Delay", "type":"float", "defaultValue": 0.5 }, { "name": "tf2_send_pose", "description": "Transform Pose", "type":"int", "defaultValue": 1 }, { "name": "mesh_scale", "description": "Part Mesh Scale", "type":"float", "defaultValue": 0.001 } ] } ``` ## Атомарные навыки ```xml Open or close ``` ## Пример интерфейса в DTDL ```json { "@context": "...;3", "@id": "robossember_assembly;1", "@type": "Interface", "displayName": "Bolt", "contents": [ { "@type": "Property", "name": "Label", "schema": "string" }, { "@type": "Property", "name": "mesh", "writable": true, "schema": "string" }, { "@type": "Telemetry", "name": "Pose6D", "writable": true, "schema": { "@type": "Object", "fields": [ { "name": "x", "schema": "double" }, { "name": "y", "schema": "double" }, { "name": "z", "schema": "double" } ] } }, { "@type": "Relationship", "name": "contains", "target": "dtmi:com:robossember_assembly:Bolt;1" }, { "@type": "Component", "name": "frontCamera", "schema": "dtmi:com:example:Camera;3" }, ] } ``` ## Пример файла описания сцены из Blender TODO: рассписать потребность в основных элементах, что и для чего ```json { "assets": [ { "name": "robo-arm", "id": "629b29d7-fe15-428b-9014-6c3dde045af8", "model_path": "../model.urdf" } ], "instances": [ // измненная URDF модель { "id": "0e29084f-1190-45d0-bd59-8f4ce2591gb1", "name": "robo-arm-1", //assetId указывает на родительский ассет "assetId": "629b29d7-fe15-428b-9014-6c3dde045af8", "pose": { "x": 12.0, "y": 1.0, "z": 4.0, "roll": 1.0, "pitch": 4.0, "yaw": 5.0 }, //если изменено внутренее состояние URDF модели "tags": [ "" ] }, { "id": "0e27984f-8890-4d90-bd59-8f4ce29920f9", "name": "robo-arm-2", //assetId указывает на родительский ассет "assetId": "629b29d7-fe15-428b-9014-6c3dde045af8", //если дефолтные позиции модели "pose": null, //если не изменено внутренее состояние URDF модели "tags": null }, { "type": "LIGHT", "light_type": "SPOT", "power": 10.0, "spot_angle": 45.0, "name": null, "pose": { "x": 0.0, "y": 0.0, "z": 0.0, "roll": 0.0, "pitch": 0.0, "yaw": 0.0 } } ] } ```