first commit
This commit is contained in:
230
README.md
Normal file
230
README.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# Модуль В. Система технического зрения с использованием инструментов ИИ
|
||||
|
||||
## Описание задачи
|
||||
|
||||
На специальной полке на соревновательном поле расположены N > 3 коробок с рисунками инструментов. РМК-1 с манипулятором смотрит на полку сверху (камера на манипуляторе направлена вертикально вниз). Эксперт с помощью генератора случайных чисел выбирает целевую деталь. Алгоритм должен:
|
||||
|
||||
1. Распознать все коробки в кадре камеры
|
||||
2. Определить координаты целевой коробки относительно базы манипулятора (Base_link)
|
||||
3. Направить манипулятор к коробке, схватить её и поднять в стартовое положение
|
||||
4. Опустить коробку на свободное место на полке
|
||||
|
||||
## Классы объектов
|
||||
|
||||
| ID | Название | Описание |
|
||||
|----|----------|----------|
|
||||
| 1 | Молоток (hammer) | Коробка с изображением молотка |
|
||||
| 2 | Гаечный ключ (wrench) | Коробка с изображением гаечного ключа |
|
||||
| 3 | Пассатижи (pliers) | Коробка с изображением пассатижей |
|
||||
|
||||
## Архитектура решения
|
||||
|
||||
- **Модель**: YOLOv8n-cls (nano classification) — легковесная модель для классификации изображений
|
||||
- **Фреймворк**: Ultralytics / PyTorch
|
||||
- **Размер входа**: 224x224 px
|
||||
- **Размер модели**: ~5.3 MB (PyTorch), ~5.5 MB (ONNX)
|
||||
- **Скорость инференса**: ~1.6 мс на CPU
|
||||
|
||||
### Пайплайн обработки кадра
|
||||
|
||||
```
|
||||
Кадр с камеры манипулятора
|
||||
|
|
||||
v
|
||||
Сегментация коробок (OpenCV: пороговая фильтрация + контурный анализ)
|
||||
|
|
||||
v
|
||||
Классификация каждой коробки (YOLOv8n-cls)
|
||||
|
|
||||
v
|
||||
Поиск целевой коробки (по ID класса + порог уверенности > 0.7)
|
||||
|
|
||||
v
|
||||
Пересчёт пиксельных координат в координаты Base_link
|
||||
|
|
||||
v
|
||||
Отправка координат манипулятору (MoveIt2 API)
|
||||
```
|
||||
|
||||
## Структура файлов
|
||||
|
||||
```
|
||||
module_v/
|
||||
├── images/ # Исходные изображения инструментов
|
||||
│ ├── Hammer.jpg
|
||||
│ ├── Wrench.jpg
|
||||
│ └── Pliers.jpg
|
||||
├── dataset/ # Сгенерированный датасет
|
||||
│ ├── train/ # 900 изображений (300 на класс)
|
||||
│ │ ├── hammer/
|
||||
│ │ ├── wrench/
|
||||
│ │ └── pliers/
|
||||
│ └── val/ # 180 изображений (60 на класс)
|
||||
│ ├── hammer/
|
||||
│ ├── wrench/
|
||||
│ └── pliers/
|
||||
├── runs/module_v_cls/weights/ # Результаты обучения
|
||||
│ ├── best.pt
|
||||
│ └── best.onnx
|
||||
├── best.pt # Обученная модель (копия)
|
||||
├── generate_dataset.py # Скрипт генерации датасета
|
||||
├── train_model.py # Скрипт обучения модели
|
||||
├── detect_box.py # Основной скрипт инференса + ROS2
|
||||
├── test_detect.py # Тестовый скрипт с визуализацией
|
||||
├── README.md # Общая инструкция по симулятору
|
||||
└── README_MODULE_V.md # Данный файл
|
||||
```
|
||||
|
||||
## Установка зависимостей
|
||||
|
||||
```bash
|
||||
pip install ultralytics opencv-python-headless Pillow albumentations
|
||||
```
|
||||
|
||||
## Генерация датасета
|
||||
|
||||
Датасет генерируется из 3 исходных изображений с помощью аугментаций:
|
||||
- Композитинг инструментов на различные фоны (имитация полки)
|
||||
- Повороты, отражения, перспективные искажения
|
||||
- Изменение яркости, контраста, насыщенности
|
||||
- Размытие, шум, JPEG-компрессия
|
||||
|
||||
```bash
|
||||
python3 generate_dataset.py
|
||||
```
|
||||
|
||||
Результат: 900 train + 180 val изображений (по 300/60 на класс).
|
||||
|
||||
## Обучение модели
|
||||
|
||||
```bash
|
||||
python3 train_model.py
|
||||
```
|
||||
|
||||
Параметры обучения:
|
||||
- Базовая модель: `yolov8n-cls.pt` (предобученная на ImageNet)
|
||||
- Эпохи: до 80 (с early stopping, patience=15)
|
||||
- Размер изображения: 224x224
|
||||
- Batch size: 32
|
||||
|
||||
Результат: **100% top-1 accuracy** на валидации.
|
||||
|
||||
Модель автоматически экспортируется в ONNX формат.
|
||||
|
||||
## Запуск
|
||||
|
||||
### Тест на отдельном изображении
|
||||
|
||||
```bash
|
||||
python3 detect_box.py --target 1 --image path/to/image.jpg
|
||||
```
|
||||
|
||||
Где `--target` — ID целевой детали (1=молоток, 2=ключ, 3=пассатижи).
|
||||
|
||||
### Тест на всех исходных изображениях
|
||||
|
||||
```bash
|
||||
python3 detect_box.py --target 2
|
||||
```
|
||||
|
||||
### Тест с визуализацией на кадре с камеры
|
||||
|
||||
```bash
|
||||
python3 test_detect.py
|
||||
```
|
||||
|
||||
Результат сохраняется в `test_result.png` с аннотированными рамками и подписями классов.
|
||||
|
||||
### Запуск в режиме ROS2
|
||||
|
||||
Предварительно запустите симуляцию модуля В:
|
||||
|
||||
```bash
|
||||
# Терминал 1: запуск симулятора
|
||||
ros2 launch ar_webots_fms_ros2 module3.launch.py
|
||||
|
||||
# Терминал 2: запуск детектора (указать целевую деталь)
|
||||
python3 detect_box.py --target 1 --ros
|
||||
```
|
||||
|
||||
Скрипт подписывается на топик `/RMC1/arm95/camera_gripper/image_color`, распознаёт коробки и выводит координаты целевой коробки относительно Base_link.
|
||||
|
||||
## Результаты тестирования
|
||||
|
||||
### На исходных изображениях
|
||||
|
||||
| Изображение | Предсказание | Уверенность |
|
||||
|-------------|-------------|-------------|
|
||||
| Hammer.jpg | hammer (молоток) | 99.98% |
|
||||
| Wrench.jpg | wrench (гаечный ключ) | 99.99% |
|
||||
| Pliers.jpg | pliers (пассатижи) | 100.00% |
|
||||
|
||||
### На тестовом кадре с камеры (test.png)
|
||||
|
||||
5 коробок на полке (молоток, ключ, 2 пассатижей + объект на краю):
|
||||
|
||||
| Коробка | Класс | Уверенность |
|
||||
|---------|-------|-------------|
|
||||
| Box 1 | wrench | 99.51% |
|
||||
| Box 2 | pliers | 100.00% |
|
||||
| Box 3 | wrench | 65.81% (край кадра, фильтруется порогом) |
|
||||
| Box 4 | hammer | 79.42% |
|
||||
| Box 5 | pliers | 99.93% |
|
||||
|
||||
Рекомендуемый порог уверенности для фильтрации: **> 0.7**
|
||||
|
||||
## Интеграция с манипулятором
|
||||
|
||||
Пример полного цикла (распознавание + захват):
|
||||
|
||||
```python
|
||||
import rclpy
|
||||
from detect_box import load_model, find_target_box, pixel_to_base_link
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from tf_transformations import quaternion_from_euler
|
||||
from moveit.planning import MoveItPy
|
||||
|
||||
# Инициализация
|
||||
rclpy.init()
|
||||
model = load_model("best.pt")
|
||||
arm95 = MoveItPy(node_name="moveit_py", name_space="/RMC1/arm95")
|
||||
arm95_arm = arm95.get_planning_component("arm95_group")
|
||||
gripper = arm95.get_planning_component("gripper")
|
||||
|
||||
# 1. Получить кадр с камеры (через ROS2 subscriber)
|
||||
# frame = ... (cv2 image from /RMC1/arm95/camera_gripper/image_color)
|
||||
|
||||
# 2. Найти целевую коробку
|
||||
target_id = 1 # молоток
|
||||
result = find_target_box(model, frame, target_id)
|
||||
if result:
|
||||
cx, cy, conf, bbox = result
|
||||
h, w = frame.shape[:2]
|
||||
wx, wy, wz = pixel_to_base_link(cx, cy, w, h)
|
||||
|
||||
# 3. Переместить манипулятор к коробке
|
||||
q = quaternion_from_euler(3.14, 0.0, 1.57)
|
||||
pose_goal = PoseStamped()
|
||||
pose_goal.header.frame_id = "Base_link"
|
||||
pose_goal.pose.orientation.x = q[0]
|
||||
pose_goal.pose.orientation.y = q[1]
|
||||
pose_goal.pose.orientation.z = q[2]
|
||||
pose_goal.pose.orientation.w = q[3]
|
||||
pose_goal.pose.position.x = wx
|
||||
pose_goal.pose.position.y = wy
|
||||
pose_goal.pose.position.z = 0.3 # высота над коробкой
|
||||
|
||||
arm95_arm.set_start_state_to_current_state()
|
||||
arm95_arm.set_goal_state(pose_stamped_msg=pose_goal, pose_link="gripper_base")
|
||||
plan_result = arm95_arm.plan()
|
||||
if plan_result:
|
||||
arm95.execute(plan_result.trajectory, controllers=[])
|
||||
|
||||
# 4. Захватить коробку
|
||||
gripper.set_goal_state(configuration_name="closed")
|
||||
plan_result = gripper.plan()
|
||||
if plan_result:
|
||||
arm95.execute(plan_result.trajectory, controllers=[])
|
||||
```
|
||||
|
||||
**Важно**: координаты `pixel_to_base_link()` требуют калибровки параметров камеры (FOV, высота) под конкретную установку.
|
||||
Reference in New Issue
Block a user