435 lines
14 KiB
Markdown
435 lines
14 KiB
Markdown
## Мета-модель фреймворка
|
||
|
||
## Соответствие 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"
|
||
},
|
||
]
|
||
}
|
||
```
|
||
|
||
## Пример файла описания сцены из 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
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|