Проект «Боевая пчел໶
Участники проекта¶
Автор работы: Петелина Ярослава Андреевна, ученица 6 класса ГБОУ г.Москвы №1391.
Проектный наставник: Петелина Дарья Сергеевна.
Описание и цель проекта¶
Цель проекта: Собрать карту из фотографий, полученных с дрона, и посчитать с помощью компьютерного зрения количество вражеской техники.
Описание проекта:
С помощью программы Lobe был обучен классификатор на три вида объектов: пусто, танк, РСЗО
Квадрокоптер запрограммирован для облета прямоугольной территории по координатам
В параллельном потоке дрон делает фотографии территории
После приземления все фото склеиваются в одну карту с помощью библиотеки OpenCV
Получившаяся карта делится на прямоугольники такой величины, чтобы на каждом куске было примерно по одному объекту
Каждый кусок карты обрабатывается классификатором и подсчитывается количество классифицированных объектов.
С файлами по проекту вы можете ознакомиться на GitHub:
Решаемые задачи¶
Автоматизированная тактическая разведка размещения техники противника с помощью квадрокоптера
Составление карты расположения вражеских укреплений и войск
Получение оператором с безопасного расстояния информации с воздуха.
Этапы разработки¶
Обучение и тестирование классификатора¶
Основной инструкцией для обучения послужил проект Поиск вертолётных площадок
Создание датасета производилось как описано в проекте, но для трёх классов:
ничего не обнаружено (noenemy)
танк (tank)
РСЗО (rszo)
В программе Lobe был обучен классификатор на этом датасете и протестирован на реальных объектах, которые попадали в объектив камеры квадрокоптера.
Пришлось немного дообучать вручную, чтобы добиться на 100% верно предсказанных результатов:
Полученная модель классификатора была добавлена в проект, а для её тестирования была написана небольшая программа. По ней квадрокоптер в бесполетном режиме выдает видеопоток, а все полученные из него изображения в реальном времени обрабатываются классификатором. Информация о классе обнаруженного на фото объекта выводится красным текстом прямо на фрейме видеопотока:
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()
Был получен результат работы скрипта:
Основная программа¶
Программа выполняется в 2 потока: один поток отвечает за полёт по координатам, а другой – за фотографирование и сохранение изображений.
Программа выполняется в 2 потока: один поток отвечает за полёт по координатам, а другой – за фотографирование и сохранение изображений.
Координация между потоками происходит с помощью обмена сообщениями: поток, отвечающий за полёт, прибыв в точку, посылает свои координаты второму потоку. Второй поток сохраняет изображение, полученное с камеры дрона в этот момент, указывая в имени файла координаты.
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()
Полёт по координатам¶
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:
Точка C:
Точка D:
Точка E:
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:
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 на сектора и вызываем для каждого вырезанного фото классификатор.
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.
При развитии проекта в дальнейшем планируется:
Заменить разрезание карты на сектора на детектор объектов YOLOv3 Увеличить точность подсчета объектов на карте Усовершенствовать передвижение квадрокоптера по координатам. Сейчас столкнулись с неправильной работой функции point_reached blocking=False. При её использовании некоторые координатные точки пропускались, поэтому она была заменена на неэффективный time.sleep().
Материалы проекта¶
Презентация: Проект «Боевая пчела»
Проект на GitHub