DOCS: object detection usecase
This commit is contained in:
parent
649a426c25
commit
dc59211634
9 changed files with 1920 additions and 0 deletions
17
docs/framework_debug_3part.md
Normal file
17
docs/framework_debug_3part.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Фреймворк: отладка.
|
||||||
|
|
||||||
|
После того как был отлажен механизм обнаружения объектов на основе метода YoloV4 и в результате его испытаний, нами было принято решение имплементировать более совершенный и современный метод Yolo 8-ой версии ([YoloV8](https://github.com/ultralytics/ultralytics)).
|
||||||
|
На основе предыдущего опыта был взят за основу lifecycle-узел ROS2, который управлялся бы исполнением действий деревьев поведения (Behavior Tree). В ходе работы над этим модулем и дальнейшей отладки выявились преимущества метода YoloV8 в сравнении с YoloV4: файл весов модели 8-й версии по размеру составил около 6 МБ, в 4-й - около 244 МБ. Также на реальных изображениях распечатанных нами моделей улучшилась их точность распознавания (рис.1). В качестве моделей мы использовали набор шахматных фигур, распечатанных на 3D-принтере.
|
||||||
|
|
||||||
|

|
||||||
|
Рис.1
|
||||||
|
|
||||||
|
Когда отлаживался модуль распознавания объектов (Object Detection), выявилась сложность проектирования и отладки этого навыка. Необходимо было кроме основной логики ROS-узла создавать и отлаживать передачу параметров в дереве поведения. И была предложена схема обобщения интерфейса для любых навыков.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
В ней предположено использовать отдельный интерфейсный узел (Interface Node), который будет реализовывать взаимодействие системы исполнения дерева поведения (BT Engine Node) c библиотекой навыков. А сами навыки предложено упаковывать в отдельные ROS-пакеты с json-файлом описания, который позволит декларативно описывать элементы интерфейса, а также схему запуска навыков.
|
||||||
|
|
||||||
|
Такой интерфейсный узел был реализован и позволил упростить как составление и выполнение дерева поведения, так и облегчить создание самой библиотеки навыков.
|
||||||
|
|
||||||
|
Первым навыком, который использовал интерфейсный узел, стала имплементация метода оценки 6D-позы объекта [DOPE](https://github.com/NVlabs/Deep_Object_Pose).
|
BIN
docs/img/P_curve.png
Normal file
BIN
docs/img/P_curve.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 166 KiB |
BIN
docs/img/scheme1.jpg
Normal file
BIN
docs/img/scheme1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
1262
docs/obj_detection_use_case.drawio
Normal file
1262
docs/obj_detection_use_case.drawio
Normal file
File diff suppressed because one or more lines are too long
123
docs/randomization.md
Normal file
123
docs/randomization.md
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
# Рандомизация
|
||||||
|
|
||||||
|
### При обучении робота (Илья Ураев)
|
||||||
|
|
||||||
|
- Гравитация
|
||||||
|
- Положение камеры
|
||||||
|
- Конфигурацию робота (положение джоинтов так называется)
|
||||||
|
- Положение объекта или точки куда надо дотянутся
|
||||||
|
- Текстуру поверхности размещения
|
||||||
|
- Ну и я думаю чтобы с robot_builder смотрелось, то можно рандомизировать число степеней свободы робота.
|
||||||
|
- Можно рандомизировать позиции спавна робота
|
||||||
|
|
||||||
|
### При создании датасета (Александр Шушпанов)
|
||||||
|
|
||||||
|
- Зона локации (спавна) активных объектов
|
||||||
|
- Позиция камеры: радиус сферы размещения и наклон, центр этой сферы
|
||||||
|
- Источники света: количество, тип, локализация в пространстве, цветность, сила света
|
||||||
|
- Свойства материала по отражению света: зеркальность, шероховатость, металлизация, цвет
|
||||||
|
|
||||||
|
Гиперпараметры.
|
||||||
|
- количество серий (спавна активных объектов)
|
||||||
|
- количество позиций камеры на одну серию
|
||||||
|
- количество сдвигов камеры на 1 позу
|
||||||
|
|
||||||
|
### Общий список параметров рандомизации
|
||||||
|
|
||||||
|
- Положение искомого(активного) объекта(-ов) в рабочей зоне
|
||||||
|
- Позиция камеры: радиус сферы размещения и наклон, центр этой сферы
|
||||||
|
- Текстуры повехностей объектов и/или свойства материала по отражению света: зеркальность, шероховатость, металлизация, цвет
|
||||||
|
- Источники света: количество, тип, локализация в пространстве, цветность, сила света
|
||||||
|
- Конфигурация робота (положение джоинтов), число степеней свободы и его начальное расположение
|
||||||
|
- Гравитация
|
||||||
|
|
||||||
|
## Web-сервис для генерации датасетов
|
||||||
|
|
||||||
|
Для реализации пользовательского интерфейса web-сервиса нами была разработана схема описания параметров рандомизации. Её использование позволяет изменять конфигурацию параметров в зависимости от задачи. Предполагается в дальнейшем использовать модуль ввода параметров в том числе и в задачах обучения с подкреплением.
|
||||||
|
|
||||||
|
Пример такой схемы:
|
||||||
|
```
|
||||||
|
ENUM T = "ObjectDetection","PoseEstimation"
|
||||||
|
ENUM C = "","BOX","SPHERE","CAPSULE","CYLINDER","CONE","CONVEX_HULL","MESH","COMPOUND"
|
||||||
|
ENUM L = "POINT","SUN"
|
||||||
|
ENUM F = "JPEG","PNG"
|
||||||
|
|
||||||
|
MODELS = {
|
||||||
|
"id": ${ID:number:1},
|
||||||
|
"name": ${NAME:string:""},
|
||||||
|
"model": ${MODEL:string:"models/1.fbx"}
|
||||||
|
}
|
||||||
|
OBJECTS_SCENE = {
|
||||||
|
"name": ${NAME:string:""},
|
||||||
|
"collision_shape": ${enum:C:"BOX"},
|
||||||
|
"loc_xyz": [${LOC_XYZ_1:number:0}, ${LOC_XYZ_2:number:0}, ${LOC_XYZ_3:number:0}],
|
||||||
|
"rot_euler": [${ROT_EULER_1:number:0}, ${ROT_EULER_2:number:0}, ${ROT_EULER_3:number:0}],
|
||||||
|
"material_randomization": {
|
||||||
|
"specular": [${SPECULAR_1:number:0}, ${SPECULAR_2:number:1}],
|
||||||
|
"roughness": [${ROUGHNESS_1:number:0}, ${ROUGHNESS_2:number:1}],
|
||||||
|
"metallic": [${METALLIC_1:number:0}, ${METALLIC_2:number:1}],
|
||||||
|
"base_color": [
|
||||||
|
[
|
||||||
|
${BASE_COLOR_1:number:0},
|
||||||
|
${BASE_COLOR_2:number:0},
|
||||||
|
${BASE_COLOR_3:number:0},
|
||||||
|
${BASE_COLOR_4:number:1}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
${BASE_COLOR_5:number:1},
|
||||||
|
${BASE_COLOR_6:number:1},
|
||||||
|
${BASE_COLOR_7:number:1},
|
||||||
|
${BASE_COLOR_8:number:1}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LIGHTS = {
|
||||||
|
"id": ${ID:number:1},
|
||||||
|
"type": ${enum:L:"POINT"},
|
||||||
|
"loc_xyz": [${LOC_XYZ_1:number:5}, ${LOC_XYZ_2:number:5}, ${LOC_XYZ_3:number:5}],
|
||||||
|
"rot_euler": [${ROT_EULER_1:number:-0.06}, ${ROT_EULER_2:number:0.61}, ${ROT_EULER_3:number:-0.19}],
|
||||||
|
"color_range_low": [${COLOR_RANGE_LOW_1:number:0.5}, ${COLOR_RANGE_LOW_2:number:0.5}, ${COLOR_RANGE_LOW_3:number:0.5}],
|
||||||
|
"color_range_high":[${COLOR_RANGE_HIGH_1:number:1}, ${COLOR_RANGE_HIGH_2:number:1}, ${COLOR_RANGE_HIGH_3:number:1}],
|
||||||
|
"energy_range":[${ENERGY_RANGE_1:number:400},${ENERGY_RANGE_2:number:900}]
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
"typedataset": ${enum:T:"ObjectDetection"},
|
||||||
|
"dataset_path": ${DATASET_PATH:string},
|
||||||
|
"models":${ARRAY:MODELS:[]},
|
||||||
|
"models_randomization":{
|
||||||
|
"loc_range_low": [${LOC_RANGE_LOW_1:number:-1}, ${LOC_RANGE_LOW_2:number:-1}, ${LOC_RANGE_LOW_3:number:0}],
|
||||||
|
"loc_range_high": [${LOC_RANGE_HIGH_1:number:1}, ${LOC_RANGE_HIGH_2:number:1}, ${LOC_RANGE_HIGH_3:number:2}]
|
||||||
|
},
|
||||||
|
"scene":{
|
||||||
|
"objects": ${ARRAY:OBJECTS_SCENE:[]},
|
||||||
|
"lights": ${ARRAY:LIGHTS:[]},
|
||||||
|
},
|
||||||
|
"camera_position":{
|
||||||
|
"center_shell": [${CENTER_SHELL_1:number:0}, ${CENTER_SHELL_2:number:0}, ${CENTER_SHELL_3:number:0}],
|
||||||
|
"radius_range": [${RADIUS_RANGE_1:number:0.4}, ${RADIUS_RANGE_2:number:1.4}],
|
||||||
|
"elevation_range": [${ELEVATION_RANGE_1:number:10}, ${ELEVATION_RANGE_2:number:90}]
|
||||||
|
},
|
||||||
|
"generation":{
|
||||||
|
"n_cam_pose": ${N_CAM_POSE:number:5},
|
||||||
|
"n_sample_on_pose": ${N_SAMPLE_ON_POSE:number:3},
|
||||||
|
"n_series": ${N_SERIES:number:100},
|
||||||
|
"image_format": ${enum:F:"jpg"},
|
||||||
|
"image_size_wh": [${IMAGE_SIZE_WH_1:number:640}, ${IMAGE_SIZE_WH_2:number:480}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Вначале описываются перечисления - ENUM, которым присваиваются имена и список возможных значений. Затем описываются составные именованные объекты, а затем основной блок описания параметров. Этот блок представляет из себя JSON-словарь параметров, который будет подготовлен на выходе модуля ввода параметров. Каждый ключ этого словаря дополняется мини-схемой описания вводимого значения.
|
||||||
|
Формат:
|
||||||
|
```
|
||||||
|
${<имя_переменной>:<тип>:<значение_по_умолчанию>}
|
||||||
|
```
|
||||||
|
либо массив объектов
|
||||||
|
```
|
||||||
|
${ARRAY:<имя_объекта>:[]}
|
||||||
|
```
|
||||||
|
|
||||||
|
В нашем алгоритме формирования датасета для задач компьютерного зрения (ObjectDetection, PoseEstimation) выбран формат [BOP: Benchmark for 6D Object Pose Estimation](https://bop.felk.cvut.cz/home/), в его [BOP-Classic](https://bop.felk.cvut.cz/datasets/) версии.
|
||||||
|
Он содержит в своей аннотации все необходимые данные (ground truth) для обучения нейросетевых моделей поиску объектов на изображении, а также распознавания поз искомых объектов.
|
|
@ -1,3 +1,377 @@
|
||||||
|
## Мета-модель фреймворка
|
||||||
|
|
||||||
|
## Соответствие 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
|
## Пример файла описания сцены из Blender
|
||||||
|
|
||||||
TODO: рассписать потребность в основных элементах, что и для чего
|
TODO: рассписать потребность в основных элементах, что и для чего
|
||||||
|
|
|
@ -10,3 +10,7 @@
|
||||||
|
|
||||||
Надстройка для ArgumentParser.
|
Надстройка для ArgumentParser.
|
||||||
Позволяет использовать передачу аргументов командной строки для Bledner и FreeCAD.
|
Позволяет использовать передачу аргументов командной строки для Bledner и FreeCAD.
|
||||||
|
|
||||||
|
### get_interfaces.py
|
||||||
|
|
||||||
|
Получение информации о ROS2-топиках в пакете цифрового двойника, навыка.
|
68
utils/get_interfaces.py
Normal file
68
utils/get_interfaces.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import signal
|
||||||
|
import time
|
||||||
|
|
||||||
|
from ros2cli.node.strategy import NodeStrategy
|
||||||
|
from ros2topic.api import get_topic_names_and_types
|
||||||
|
|
||||||
|
OUTPUT_FILE = "topics.json"
|
||||||
|
TOPICS_FILTER = ["/parameter_events", "/rosout"]
|
||||||
|
|
||||||
|
def get_script_args(cfg):
|
||||||
|
args = cfg["command"].split()
|
||||||
|
args.append(cfg["package"])
|
||||||
|
args.append(cfg["executable"])
|
||||||
|
return args
|
||||||
|
|
||||||
|
def get_topics(filename, path):
|
||||||
|
jsonpath = os.path.join(path, filename)
|
||||||
|
|
||||||
|
with NodeStrategy({}) as node:
|
||||||
|
topic_names_and_types = get_topic_names_and_types(node=node, include_hidden_topics=False)
|
||||||
|
|
||||||
|
topic_info = []
|
||||||
|
for (topic_name, topic_types) in topic_names_and_types:
|
||||||
|
if not topic_name in TOPICS_FILTER:
|
||||||
|
topic_info.append({"name": topic_name, "type": topic_types[0]})
|
||||||
|
|
||||||
|
print(f"---> number of topics: {len(topic_info)}")
|
||||||
|
|
||||||
|
j_data = {"topics": topic_info}
|
||||||
|
with open(jsonpath, "w") as fh:
|
||||||
|
json.dump(j_data, fh, indent=2)
|
||||||
|
|
||||||
|
for topic in topic_info:
|
||||||
|
print(topic["name"])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--package", required=True, help="Json-string/file with package parameters")
|
||||||
|
parser.add_argument("--path", default="", help="Output path")
|
||||||
|
parser.add_argument("--json", default=OUTPUT_FILE, help="Output file name in json-format")
|
||||||
|
parser.add_argument('--delay', default=5, type=int, help="Delay in seconds")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.package[-5:] == ".json":
|
||||||
|
if not os.path.isfile(args.package):
|
||||||
|
print(f"Error: no such file '{args.package}'")
|
||||||
|
exit(-1)
|
||||||
|
with open(args.package, "r") as f:
|
||||||
|
j_data = f.read()
|
||||||
|
else:
|
||||||
|
j_data = args.package
|
||||||
|
try:
|
||||||
|
cfg = json.loads(j_data)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"JSon error: {e}")
|
||||||
|
exit(-2)
|
||||||
|
|
||||||
|
cmd = get_script_args(cfg)
|
||||||
|
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
time.sleep(args.delay)
|
||||||
|
get_topics(args.json, args.path)
|
||||||
|
|
||||||
|
process.send_signal(signal.SIGINT)
|
72
utils/ros2_topic_to_json.py
Normal file
72
utils/ros2_topic_to_json.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
"""
|
||||||
|
ROS2_Topic_to_json
|
||||||
|
ROS 2 program for outputting system objects to json format
|
||||||
|
|
||||||
|
From https://github.com/ros2/ros2cli/blob/humble/ros2topic/ros2topic/verb/list.py
|
||||||
|
|
||||||
|
@shalenikol release 0.1
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
usage: python ros2_topic_to_json.py [-h] [--jsonpath JSONPATH]
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--jsonpath JSONPATH Output file in json-format
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
from ros2cli.node.strategy import NodeStrategy
|
||||||
|
from ros2topic.api import get_topic_names_and_types
|
||||||
|
|
||||||
|
OUTPUT_FILE = "topics.json"
|
||||||
|
|
||||||
|
# def show_topic_info(topic_info, is_publisher):
|
||||||
|
# message = ('Published' if is_publisher else 'Subscribed') + ' topics:\n'
|
||||||
|
# for (topic_name, topic_types, pub_count, sub_count) in topic_info:
|
||||||
|
# count = pub_count if is_publisher else sub_count
|
||||||
|
# if count:
|
||||||
|
# topic_types_formatted = ', '.join(topic_types)
|
||||||
|
# count_str = str(count) + ' ' + ('publisher' if is_publisher else 'subscriber') \
|
||||||
|
# + ('s' if count > 1 else '')
|
||||||
|
# message += f' * {topic_name} [{topic_types_formatted}] {count_str}\n'
|
||||||
|
# return message
|
||||||
|
|
||||||
|
def main(args, jsonpath):
|
||||||
|
topic_info = []
|
||||||
|
with NodeStrategy(args) as node:
|
||||||
|
topic_names_and_types = get_topic_names_and_types(
|
||||||
|
node=node,
|
||||||
|
include_hidden_topics=False)
|
||||||
|
for (topic_name, topic_types) in topic_names_and_types:
|
||||||
|
# if args.verbose:
|
||||||
|
# pub_count = node.count_publishers(topic_name)
|
||||||
|
# sub_count = node.count_subscribers(topic_name)
|
||||||
|
# topic_info.append((topic_name, topic_types, pub_count, sub_count))
|
||||||
|
# else:
|
||||||
|
topic_info.append((topic_name, topic_types))
|
||||||
|
|
||||||
|
# if args.count_topics:
|
||||||
|
print(f"---> number of topics: {len(topic_names_and_types)}")
|
||||||
|
|
||||||
|
j_data = {"topics": topic_info}
|
||||||
|
with open(jsonpath, "w") as fh:
|
||||||
|
json.dump(j_data, fh, indent=2)
|
||||||
|
# elif topic_names_and_types:
|
||||||
|
# if args.verbose:
|
||||||
|
# print(show_topic_info(topic_info, is_publisher=True))
|
||||||
|
# print(show_topic_info(topic_info, is_publisher=False))
|
||||||
|
# else:
|
||||||
|
for (topic_name, topic_types) in topic_info:
|
||||||
|
msg = "{topic_name}"
|
||||||
|
# topic_types_formatted = ', '.join(topic_types)
|
||||||
|
# if args.show_types:
|
||||||
|
# msg += ' [{topic_types_formatted}]'
|
||||||
|
print(msg.format_map(locals()))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--jsonpath", default=OUTPUT_FILE, help="Output file in json-format")
|
||||||
|
m_args = parser.parse_args()
|
||||||
|
|
||||||
|
args = {}
|
||||||
|
main(args, m_args.jsonpath)
|
Loading…
Add table
Add a link
Reference in a new issue