Дневник практики - rangelokk/Practica GitHub Wiki

Задачи на практику

  • Необходимо использовать технологии нейронных сетей для получения координат и положения объекта, для дальнейшего захвата его роботом
    image
  • Пройти технику безопасности!
  • Настроить оборудование для дальнейшей работы
  • Установить необходимые библиотеки
  • Перехватить изображение с камеры
  • Связать Yolo и камеру для детектирования в реальном времени
  • Отснять датасет для обучения сети
  • Разметить полученные изображения
  • Обучить нейросеть на созданном датасете
  • Выполнить сегментацию изображения
  • Получить облако точек с камеры
  • Синхронизировать полученные изображения и облако точек с камеры
  • Сегментировать облако точек
  • Откалибровать камеру

Оборудование и ресурсы

  • Стерео RGBD-камера (Asus Xtion Pro)
  • Нейронная сеть Yolo v3

Организация и должность

  • Организация: ООО "Интеллектуальная робототехника"
  • Должность: Младший ML-инженер

Дневник

17 июня

  • Получили задачи на практику
  • Получили навыки работы с ОС Linux
  • Познакомились с rviz
  • Провели настройку оборудования для дальнейшей работы
    image
  • Установили необходимые библиотеки (darknet, внутри там yolo v3) image image

18 июня

  • Настроили git hub для совместной работы
  • Составили справку о настройке оборудования для начала работы используя git hub
  • Перехватили изображения с rgbd камеры используя код на Python
    https://github.com/rangelokk/Practica/blob/main/code/main.py
    Проблема: неверная кодировка изображения
    image
    Решение:
    image_np = bridge.imgmsg_to_cv2(msg, desired_encoding="bgr8")
    https://github.com/rangelokk/Practica/tree/main/code/BGR
    image

image

19 июня

  • Сохранили настройки для камеры в rviz
  • Подключили обученную модель yolo v3 к камере
    https://github.com/rangelokk/Practica/blob/main/YOLO_code/code.py
    image
    Проблема: код выполняется очень медленно и окошки рисуются не там где надо
    Решение: Координата прямоугольника получается путем умножения относительной координаты объекта на ширину и высоту экрана. Необходимо было задать размеры нашего изображения
width = 640  
height = 480  

Видео работы:
https://github.com/rangelokk/Practica/blob/main/YOLO_code/Screencast%20from%2006-19-2024%2012%3A24%3A11%20PM.webm
image

20 июня

  • Занялись съемкой датасета
    Для этой задачи модифицировали код для сохранения изображений ##(ИДЕЯ СЪЕМКИ ДАТАСЕТА)
if(self.i%self.framerate==0):  
    cv2.imwrite('/home/mlserver/dataset/orange_Sponge/os'+str(self.i/self.framerate)+'.png', image_np)  
self.i += 1  

https://github.com/rangelokk/Practica/blob/main/YOLO_code/20_VideoCapture
Отсняли около 2000 изображений так как это минимальное колличество изображений необходимых для обучения Yolo
image image

  • Начали разметку изображений с помощью Yolo_mark
    image

Yolo_mak позволяет легко размечать изображения image
image

Yolo_mark автоматически создает пути до изображений для обучения
image
А также координаты ограничивающего прямоугольника
image
Успели разметить 1000 изображений image

один!: image

два!: image

21 июня

  • Доделали разметку
    image
    Получили файлики: obj.names, obj.data, train.txt
  • Начали обучение сети
cd darknet  
darknet detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23 -v  
mlserver@mlserver:~/darknet$ darknet detector train data/obj.data yolo-obj.cfg darknet19_448.conv.23 -v  
yolo-obj   

Архитектура сети:
image
Обучение:
image
Проблема: Нейронная сеть долго обучается. Одна итерация занимает около 6 минут, таких итераций необходимо более тысячи

24 июня

Решили проблему скорости обучения:
Что изменили:

  • Теперь нейронная сеть учится на графическом процессоре GPU (У нас это NVIDIA GeForce RTX 3080). Также обновили CUDA и cuDNN
    image

Изменения в файле .cfg:

batch=64  
subdivisions=16  
max_batches = 6000  
steps=4800,5400  
width=416  
height=416  
classes=1  
filters=18  

https://github.com/rangelokk/Practica/blob/main/YOLO_code/cfg/yolo-obj.cfg

image Что получили:

  • Теперь скорость одной итерации около 0,5 сек. Точность прогнозов очень быстро выросла до 80%
    image Объяснение обозначений: image

image image image image

Мы остановили обучение на 6000-ой итерации:
image
image

Получили файлик с весами: yolo-obj_best.weights

25 июня

Написали программный код для решения задачи сегментации с использованием метода Grab в библиотеке cv2.

image = image_np.copy()  
mask = np.zeros(image_np.shape[:2], np.uint8)  
bgdModel = np.zeros((1, 65), np.float64)  
fgbModel = np.zeros((1, 65), np.float64)  
rect = boxes[i]  
cv2.grabCut(image, mask, rect, bgdModel, fgbModel, 5, cv2.GC_INIT_WITH_RECT)  
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')  
image = image * mask2[:, :, np.newaxis]  
cv2.imwrite('/home/mlserver/dataset/Segmented/s'+str(count)+'.png', image)  
count += 1  

https://github.com/rangelokk/Practica/blob/main/YOLO_code/Segment/segment.py
Результат:
s0
Проблема: перезаписывает изображения, а не создает новые.
Решение: Было совпадение имен переменных(цикла и счетчика изображений), вынесли счетчик в глобальные переменные и сменили название на count
Проблема: На сегментированных изображениях появлялись рамки
Решение: Во первых поменяли местами сегментирование и добавление рамок. Во вторых, разделили на разные изображения. То есть сегментированные изображения сохраняются в папки, а окне вывода мы видим прямоугольники. Данный подход позволит настраивать камеру для захвата и понять детектировала ли Yolo объект, а уже сегментированные изображения будут соотноситься с показаниями камеры глубины для дальнейшего определения положения и захвата роботом.
image
s17

26 июня

  • Перехватили облако точек с rgbd камеры
    • Импорт библиотек
from sensor_msgs.msg import PointCloud2  
from sensor_msgs_py import point_cloud2  
    • Чтобы получить облако точек с камеры нам было необходимо сменить Qos profile
      image
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy    
Qos_profile = QoSProfile(`    ``
    reliability=QoSReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,  
    history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,  
    depth=5  
)  
self.subscription = self.create_subscription(  
    PointCloud2,  
    '/camera/depth_registered/points',  
    self.depth_callback,  
    qos_profile=Qos_profile)

image
image

27 июня

class Multiply_Subscriber(Node):
    def __init__(self):
        super().__init__('multiply_subscriber')
        self.bridge = CvBridge()
        qos_profile_i = QoSProfile(
            reliability=QoSReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_RELIABLE,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=5
        )
        self.image_sub = Subscriber(self, Image, '/camera/rgb/image_raw', qos_profile=qos_profile_i)

        qos_profile_d = QoSProfile(
            reliability=QoSReliabilityPolicy.RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
            history=QoSHistoryPolicy.RMW_QOS_POLICY_HISTORY_KEEP_LAST,
            depth=5
        )
        self.depth_sub = Subscriber(self, PointCloud2, '/camera/depth_registered/points',  qos_profile=qos_profile_d)

        self.ts = ApproximateTimeSynchronizer([self.image_sub, self.depth_sub], 10, 0.1)
        self.ts.registerCallback(self.multi_callback)

    def multi_callback(self, img_msg, dep_msg):
        self.get_logger().info('Multi msg')

    def destroy_node(self):
        self.image_sub.destroy()
        self.depth_sub.destroy()
        super().destroy_node()

image image

  • Настроили отображение 3d в реальном времени image
  • Добавили цвета на облако точек модель чтобы было легче ориентироваться, а также оптимизировали код получения облака точек
convert_rgbUint32_to_tuple = lambda rgb_uint32: (
    (rgb_uint32 & 0x00ff0000)>>16, (rgb_uint32 & 0x0000ff00)>>8, (rgb_uint32 & 0x000000ff)
)
convert_rgbFloat_to_tuple = lambda rgb_float: convert_rgbUint32_to_tuple(
    int(cast(pointer(c_float(rgb_float)), POINTER(c_uint32)).contents.value)
)
def pointcloud2_to_open3d(data):
    field_names=[field.name for field in data.fields]
    cloud_data = list(point_cloud2.read_points(data, skip_nans=True, field_names = field_names))
    xyz = [(x,y,z) for x,y,z,rgb in cloud_data ]
    
    point_cloud = o3d.geometry.PointCloud()
    point_cloud.points = o3d.utility.Vector3dVector(np.array(xyz))
    if "rgb" in field_names:
        rgb = [convert_rgbFloat_to_tuple(rgb) for x,y,z,rgb in cloud_data ]
    point_cloud.colors = o3d.utility.Vector3dVector(np.asarray(rgb)/255.0)
    
    return point_cloud

image

  • Трансформировали облако точек, чтобы оно соответствовало изображению
pcd = pointcloud2_to_open3d(dep_msg)
pcd.transform([1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1](/rangelokk/Practica/wiki/1,-0,-0,-0],-[0,--1,-0,-0],-[0,-0,--1,-0],-[0,-0,-0,-1))

image

28 июня

  • Работаем над сегментацией облака точек.
  • Сформировали идею решения задачи сегментации облака точек на основе преобразования RGBD-изображения в RGB.

1 июля

  • Решили задачу 28 июня, но нужны доработки с цветом точек, а также добавить bounding box
  • Чтобы отобрать необходимые точки из облака точек, проецируем из на плоскость изображения. Далее идет сравнение если текущая точка соответствует не черной точке изображения, мы включаем ее в сегментированное облако. Вдохновение на решение и формулы взяты отсюда:
    https://waksoft.susu.ru/2020/02/23/geometriya-formirovaniya-izobrazhenij/

Формулы для рассчета:
image
https://github.com/rangelokk/Practica/tree/main/main_segment

for point in np.asarray(pc.points):
    u = round(f * point[0] * 1.0 / point[2])
    v = round(f * point[1] * 1.0 / point[2])
    if (u>0 and u<480) and (v>0 and v<640):           
        if np.any(im[u,v]) != 0:
            filtered_points.append(point)
  • Написали publisher чтобы посмотреть результат нарезки в rviz
def point_cloud(points, parent_frame):
    ros_dtype = sensor_msgs.PointField.FLOAT32
    dtype = np.float32
    itemsize = np.dtype(dtype).itemsize # A 32-bit float takes 4 bytes.

    data = points.astype(dtype).tobytes() 
    fields = [sensor_msgs.PointField(
        name=n, offset=i*itemsize, datatype=ros_dtype, count=1)
        for i, n in enumerate('xyz')]

    header = std_msgs.Header(frame_id=parent_frame)

    return sensor_msgs.PointCloud2(
        header=header,
        height=1, 
        width=points.shape[0],
        is_dense=False,
        is_bigendian=False,
        fields=fields,
        point_step=(itemsize * 3), 
        row_step=(itemsize * 3 * points.shape[0]),
        data=data
    )

image

2 июля

  • Так как система координат камеры находится в центре, добавили смещение к координатам
u = round(f * np.asarray(pc.points)[i][0] * 1.0 / np.asarray(pc.points)[i][2]) + 320
v = round(f * np.asarray(pc.points)[i][1] * 1.0 / np.asarray(pc.points)[i][2]) + 240

image

  • Добавили цвета чтобы понимать что конкретно обрезает программа и выяснили, что из-за неправильного фокусного растояния она обрезает неправильно image

  • Поняли что необходимо откалибровать камеру
    https://docs.ros.org/en/rolling/p/camera_calibration/tutorial_mono.html

  • Для калибровки камеры мы решили воспользоваться пакетом ros2 camera_calibration

Строчка кода для запуска:

ros2 run camera_calibration cameracalibrator --size=7x5 --square=0.02 --no-service-check --ros-args -r image:=/camera/rgb/image_raw

Дисплей программы для калибровки:
image

Процесс калибровки:
image

Результат: image

Вторая калиброка: image ('Wrote calibration data to', '/tmp/calibrationdata.tar.gz')
https://github.com/rangelokk/Practica/blob/main/main_segment/new_becap.py

Проблема: надо исправить окраску вырезанного объекта image

3 июля

  • Получив параметры матрицы камеры мы смогли задать нужные параметры в формулах и получили правильное сегментирование
v = round((fx * np.asarray(pc.points)[i][0])/ np.asarray(pc.points)[i][2]) + 326
u = round((fy * np.asarray(pc.points)[i][1])/ np.asarray(pc.points)[i][2]) + 227

https://github.com/rangelokk/Practica/blob/main/Segmentation_Pointcloud2/backup.py
image image

  • Чтобы определить положение прямоугольника попробовали применить Bounding_Box
bounding_box = indices_to_select.get_oriented_bounding_box()
print("Bounding_Box: " + str(bounding_box.get_center()))
obb = indices_to_select.get_oriented_bounding_box()
obb.color = [1.0, 0.0, 0.0]
o3d.visualization.draw_geometries([indices_to_select, obb])

https://github.com/rangelokk/Practica/blob/main/Segmentation_Pointcloud2/Bounding_Box.py
image
Как видно, пока он криво рисует прямоугольник

  • Поменяли Oriented_Box на Minimal_Oriented_Box,прямоугольник стал точнее
obb = indices_to_select.get_minimal_oriented_bounding_box()

image Как выглядит детектированный прямоугольник в общем облаке точек: image image image

4 июля - 5 июля

  • Распределили задачи между собой и оформляли отчет, дневник и индивидуальное задание по практике

8 июля

image
Решили оптимизировать код с помощью pep8
image

Запуск:
image

mlserver@mlserver:~/ros2_ws/src/py_pubsub/py_pubsub$ pep8 main.py 

сделать requirements.txt добавить конфиги рвиз2, и обученные весы конфиги уже yolo

9 июля

Продолжение написание отчёта по практике.

10 июля