Проект «Боевая пчела»

Участники проекта

Автор работы: Петелина Ярослава Андреевна, ученица 6 класса ГБОУ г.Москвы №1391.

Проектный наставник: Петелина Дарья Сергеевна.

Описание и цель проекта

Цель проекта: Собрать карту из фотографий, полученных с дрона, и посчитать с помощью компьютерного зрения количество вражеской техники.

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img27.jpg

Описание проекта:

  • С помощью программы Lobe был обучен классификатор на три вида объектов: пусто, танк, РСЗО

  • Квадрокоптер запрограммирован для облета прямоугольной территории по координатам

  • В параллельном потоке дрон делает фотографии территории

  • После приземления все фото склеиваются в одну карту с помощью библиотеки OpenCV

  • Получившаяся карта делится на прямоугольники такой величины, чтобы на каждом куске было примерно по одному объекту

  • Каждый кусок карты обрабатывается классификатором и подсчитывается количество классифицированных объектов.

С файлами по проекту вы можете ознакомиться на GitHub:

Решаемые задачи

  • Автоматизированная тактическая разведка размещения техники противника с помощью квадрокоптера

  • Составление карты расположения вражеских укреплений и войск

  • Получение оператором с безопасного расстояния информации с воздуха.

Этапы разработки

Обучение и тестирование классификатора

Основной инструкцией для обучения послужил проект Поиск вертолётных площадок

  1. Создание датасета производилось как описано в проекте, но для трёх классов:

    • ничего не обнаружено (noenemy)

    • танк (tank)

    • РСЗО (rszo)

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img01.png
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img02.png
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img03.png
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img04.jpg
  1. В программе Lobe был обучен классификатор на этом датасете и протестирован на реальных объектах, которые попадали в объектив камеры квадрокоптера.

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img05.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img06.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img07.jpg

Пришлось немного дообучать вручную, чтобы добиться на 100% верно предсказанных результатов:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img08.jpg
  1. Полученная модель классификатора была добавлена в проект, а для её тестирования была написана небольшая программа. По ней квадрокоптер в бесполетном режиме выдает видеопоток, а все полученные из него изображения в реальном времени обрабатываются классификатором. Информация о классе обнаруженного на фото объекта выводится красным текстом прямо на фрейме видеопотока:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img09.jpg
 1 import cv2
 2 import numpy as np
 3 from PIL import Image
 4 from lobe import ImageModel
 5
 6 import pioneer_sdk
 7
 8 pioneer = pioneer_sdk.Pioneer()
 9
10 model = ImageModel.load('./zbee_onnx')
11
12 while True:
13     raw = pioneer.get_raw_video_frame()
14     frame = cv2.imdecode(np.frombuffer(raw, dtype=np.uint8), cv2.IMREAD_COLOR)
15
16     frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
17     model_frame = Image.fromarray(frame_rgb)
18
19     predictions = model.predict(model_frame)
20
21     key = cv2.waitKey(1)
22
23     if key == 27:  # esc
24         print('esc pressed')
25         cv2.destroyAllWindows()
26         exit(0)
27
28     cv2.putText(frame, f'Predicted class is {predictions.prediction}', (20, 450), cv2.FONT_HERSHEY_SIMPLEX,
29                 fontScale=0.5, color=(0, 0, 255))
30     cv2.imshow("Frame", frame)
31
32 cv2.destroyAllWindows()
  1. Был получен результат работы скрипта:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img10.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img11.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img12.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img13.jpg

Основная программа

Программа выполняется в 2 потока: один поток отвечает за полёт по координатам, а другой – за фотографирование и сохранение изображений.

  • Программа выполняется в 2 потока: один поток отвечает за полёт по координатам, а другой – за фотографирование и сохранение изображений.

  • Координация между потоками происходит с помощью обмена сообщениями: поток, отвечающий за полёт, прибыв в точку, посылает свои координаты второму потоку. Второй поток сохраняет изображение, полученное с камеры дрона в этот момент, указывая в имени файла координаты.

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img14.png
 1       if __name__ == '__main__':
 2           BaseManager.register('Pioneer', Pioneer)
 3           manager = BaseManager()
 4           manager.start()
 5           pioneer_mini = manager.Pioneer()
 6           pioneer_mini.arm()
 7           pioneer_mini.takeoff()
 8
 9           buffer = mp.Queue(maxsize=1)
10
11           photo_taker = mp.Process(target=take_photo, args=(buffer, pioneer_mini))
12           flight_navigator = mp.Process(target=drone_control, args=(buffer, pioneer_mini))
13
14           photo_taker.start()
15           flight_navigator.start()
16
17           photo_taker.join()
18           flight_navigator.join()
19
20           pioneer_mini.land()
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img15.jpg

Полёт по координатам

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img16.jpg
 1    #i = 0     1    2    3    4   5
 2    x = [0.0, 0.4, 0.4, 0.0, 0.0, 0]
 3    y = [0.5, 0.5, 0.7, 0.7, 0.5, 0]
 4
 5    def drone_control(buff, drone):
 6        new_point = True
 7
 8        i = 0
 9
10        command_x = x[i]
11        command_y = y[i]
12        command_z = float(1)
13        command_yaw = math.radians(float(0))
14
15        if buff.full():
16            buff.get()
17
18        buff.put([i])
19
20        while True:
21            if new_point:
22                print("Летим в точку ", command_x, ", ", command_y, ", ", command_z)
23                drone.go_to_local_point(x=command_x, y=command_y, z=command_z, yaw=command_yaw)
24                new_point = False
25
26            key = cv.waitKey(1)
27            if key == 27:
28                print('esc pressed')
29                pioneer_mini.land()
30
31                if buff.full():
32                    buff.get()
33                buff.put(['end'])
34                break
35
36            time.sleep(5)
37            print("Достигнута точка ", command_x, ", ", command_y, ", ", command_z)
38
39            if buff.full():
40                buff.get()
41            buff.put([i])
42
43            i = i + 1
44
45            if i < len(x):
46                command_x = x[i]
47                command_y = y[i]
48                time.sleep(2)
49                new_point = True
50            else:
51                drone.land()
52                if buff.full():
53                    buff.get()
54                buff.put(['end'])
55                break

Фотографирование по координатам

Точка B:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img17.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img18.jpg

Точка C:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img19.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img20.jpg

Точка D:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img21.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img22.jpg

Точка E:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img23.jpg
https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img24.jpg
 1       def take_photo(buff, drone):
 2           new_message = False
 3           while True:
 4               try:
 5                   frame = cv.imdecode(np.frombuffer(drone.get_raw_video_frame(), dtype=np.uint8),
 6                                          cv.IMREAD_COLOR)
 7
 8                   if not buff.empty():
 9                       message = buff.get()
10                       if len(message) == 1 and message[0] == 'end':
11                           break
12                       i = message[0]
13                       new_message = True
14
15                   if new_message:
16                       name = "frame" + str(i) + "_" + str(x[i]) + "_" + str(y[i]) + ".jpg"
17                       cv.imwrite(name, frame)
18
19                       new_message = False
20
21               except cv.error:
22                   continue
23
24               cv.imshow('pioneer_camera_stream', frame)
25
26               key = cv.waitKey(1)
27               if key == 27:
28                   print('esc pressed')
29                   drone.land()
30                   break

Постобработка фотографий

После полёта получается четыре изображения, которые склеиваются с помощью библиотеки OpenCV cv.Stitcher:

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img25.jpg
 1       def take_photo(buff, drone):
 2           new_message = False
 3           while True:
 4               try:
 5                   frame = cv.imdecode(np.frombuffer(drone.get_raw_video_frame(), dtype=np.uint8),
 6                                          cv.IMREAD_COLOR)
 7
 8                   if not buff.empty():
 9                       message = buff.get()
10                       if len(message) == 1 and message[0] == 'end':
11                           break
12                       i = message[0]
13                       new_message = True
14
15                   if new_message:
16                       name = "frame" + str(i) + "_" + str(x[i]) + "_" + str(y[i]) + ".jpg"
17                       cv.imwrite(name, frame)
18
19                       new_message = False
20
21               except cv.error:
22                   continue
23
24               cv.imshow('pioneer_camera_stream', frame)
25
26               key = cv.waitKey(1)
27               if key == 27:
28                   print('esc pressed')
29                   drone.land()
30                   break

Обработка карты по секторам с помощью классификатора

Склеенную карту разрезаем с помощью той же OpenCV на сектора и вызываем для каждого вырезанного фото классификатор.

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img26.jpg
 1 def cropping_and_predict():
 2   tank_width = 60
 3   tank_height = 60
 4
 5   cell_width = 3*tank_width
 6   cell_height = 3*tank_height
 7
 8   image = cv.imread(cv.samples.findFile('map.jpg'))
 9   height, width = image.shape[:2]
10
11   x = 0
12   y = 0
13
14   x_count = int(width / cell_width)
15   cell_width = width // x_count
16
17   y_count = int(height / cell_height)
18   cell_height = height // y_count
19
20   print(cell_width, ", ", cell_height)
21
22   crop_imgs = []
23   for i in range(1, width//cell_width + 1):
24       print("X:", x, ":", (x + cell_width))
25       y = 0
26       for j in range(1, height//cell_height + 1):
27           print("Y: ", y,":",(y + cell_height))
28           crop_img = image[y:y+cell_height, x:x+cell_width]
29
30           cv.imwrite("part" + str(i) + "_" + str(j) + ".jpg", crop_img)
31           crop_imgs.append(crop_img)
32           y = y + cell_height
33       x = x + cell_width
34
35   model = ImageModel.load('./zbee_onnx')
36
37   i = 0
38   tank_count = 0
39   rszo_count = 0
40   for crop_img in crop_imgs:
41       frame_rgb = cv.cvtColor(crop_img, cv.COLOR_BGR2RGB)
42       model_frame = Image.fromarray(frame_rgb)
43
44       predictions = model.predict(model_frame)
45
46       if predictions.prediction == 'Class_tank':
47           tank_count = tank_count + 1
48       if predictions.prediction == 'Class_rszo':
49           rszo_count = rszo_count + 1
50
51       cv.putText(crop_img, f'{predictions.prediction}', (0, 40), cv.FONT_HERSHEY_SIMPLEX,
52                   fontScale=1, color=(0, 0, 255))
53       cv.imshow(f'{predictions.prediction}.jpg', crop_img)
54
55       cv.imwrite("Frame"+str(i)+".jpg", crop_img)
56       i=i+1
57
58       cv.waitKey(-1)
59
60   print("Результат работы")
61   print("Количество танков: ", tank_count)
62   print("Количество ракетных установок: ", rszo_count)

Результат

В результате работы программы осуществлён подсчёт и расположение вражеской техники. Количество танков: 3. Количество РСЗО: 2.

https://storage.yandexcloud.net/pioneer-doc.geoscan.ru-static/images/learning_cases/user_projects/project07/img27.jpg

При развитии проекта в дальнейшем планируется:

  • Заменить разрезание карты на сектора на детектор объектов YOLOv3
  • Увеличить точность подсчета объектов на карте
  • Усовершенствовать передвижение квадрокоптера по координатам. Сейчас столкнулись с неправильной работой функции point_reached blocking=False. При её использовании некоторые координатные точки пропускались, поэтому она была заменена на неэффективный time.sleep().

Материалы проекта