2025-02-27 16:55:58 +03:00
## Мета-модель фреймворка
## Соответствие 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< REPRESENT > :"VALUE"},
"link": \${LINK:Enum< LINK > :"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:Array< number > 3:[0.0,0.0,0.0] }, "rot_xyzw": \${XYZW:Array< number > 4:[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": \${Array< number > 3:[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
< TreeNodesModel >
< SubTree ID = "GraspObject" >
< input_port default = "false" name = "__shared_blackboard" > < / input_port >
< input_port name = "pose_vec" / >
< input_port name = "robot_name" / >
< / SubTree >
< Action ID = "GripperAction" >
< input_port name = "action" > Open or close< / input_port >
< input_port name = "gripper_name" / >
< / Action >
< SubTree ID = "InspectWorkspace" >
< input_port default = "true" name = "__shared_blackboard" >
< input_port name = "pose_vec" / >
< input_port name = "robot_name" / >
< / SubTree >
< Condition ID = "IsObjectInWorkspace" >
< input_port name = "topic_name" / >
< / Condition >
< Condition ID = "IsObjectVisible" / >
< Action ID = "MoveToPose" >
< input_port name = "cancel_timeout" / >
< input_port name = "pose" / >
< input_port name = "robot_name" / >
< input_port name = "server_name" / >
< input_port name = "server_timeout" / >
< / Action >
< Action ID = "MoveToPoseArray" >
< input_port name = "cancel_timeout" / >
< input_port name = "pose_vec_in" / >
< output_port name = "pose_vec_out" / >
< input_port name = "robot_name" / >
< input_port name = "server_name" / >
< input_port name = "server_timeout" / >
< / Action >
< SubTree ID = "PlaceObject" >
< input_port default = "true" name = "__shared_blackboard" > < / input_port >
< input_port name = "pose_vec" / >
< input_port name = "robot_name" / >
< / SubTree >
< Action ID = "PoseEstimationCleanup" / >
< Action ID = "PoseEstimationConfigure" >
< input_port name = "object_name" / >
< output_port name = "topic_name" / >
< input_port name = "weight_file" / >
< / Action >
< Action ID = "RbsBtAction" >
< input_port name = "command" / >
< input_port name = "do" / >
< input_port name = "server_name" / >
< input_port name = "server_timeout" / >
< / Action >
< / TreeNodesModel >
```
## Пример интерфейса в 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"
},
]
}
```
2023-12-21 08:45:25 +00:00
## Пример файла описания сцены из 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": [
"< joint name = \"robotiq_85_right_finger_joint \" type = \"fixed \">< parent link = \"robotiq_85_right_knuckle_link \"/ >< child link = \"robotiq_85_right_finger_link \"/ >< origin rpy = \"0 0 0 \" xyz = \"-0.03152616 0 . 0 -0 . 00376347 \"/></ joint > "
]
},
{
"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
}
}
]
}
```