
В нашей лаборатории, с некоторых пор, живут тараканы. Если конкретнее — тараканы «Мёртвая голова» (Blaberus craniifer) (см. справку про данный вид таракана в конце статьи).
И решил я сделать для них экзоскелет.
Идея экзоскелета состоит в том, чтобы при помощи камеры отслеживать движения таракана и в соответствии с его перемещением — выдавать команды управления на моторы мобильной платформы.
За основу экзоскелета, взял шасси из конструктора Makeblock от робота Элвина.
В дело пошёл контроллер CraftDuino с мотор-шилдом, который был изготовлен из прото-шилда с прикрученным мотор-модулем на базе микросхемы L298.
DC-DC преобразователь с кнопкой включения, который используется для преобразования 12В от литий-ионного аккумулятора в 5В для питания одноплатного компьютера Raspberry Pi.
И разумеется, на роботе закрепил Raspberry Pi с модулем камеры и USB-хабом и Wi-Fi-свистком (для удалённого доступа к компьютеру).
Кроме того, на роботе присутствуют 2 ИК-датчика и 1 ультазвуковой дальномер, но в данный момент они не используются.
Получилась вот такая мобильная платформа и она же — экзоскелет для таракана:

На переднюю балку шасси (прямо под камерой), при помощи двухстороннего скотча закрепил пустое ведро от ПКЛ-а.


Это ведро будет рабочим пространством для таракана-водителя.
Чтобы таракан контрастнее выделялся на фоне — на дно ведра вырезал круг из белой бумаги, а чтобы таракан не убежал, края бортов ведра смазал вазелином (почерпнул этот метод у американских исследователей [1]).
Делаем несколько тестовых снимков таракана, чтобы на них подобрать пороги для его детектирования.




Напишем простой скрипт на Питоне, который использует открытую библиотеку компьютерного зрения OpenCV и адаптивное пороговое преобразования (cv2.adaptiveThreshold()) для детектирования таракана.
Подберём пороги при помощи ползунков, а для улучшения быстродействия выделем прямоугольник (зелёная рамка на скриншоте), где вообще нужно искать таракана (ROI).
def find_roach(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blockSize = cv2.getTrackbarPos('blockSize', 'roacha')
C = cv2.getTrackbarPos('C', 'roacha')
if blockSize % 2 == 0:
blockSize = blockSize+1
print("blockSize: {0} C: {1}".format(blockSize, C))
bina = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, blockSize, C)
cv2.imshow('roacha', bina)

тестовая картинка для поиска таракана:

Таракана нет:

Таракан есть:

Таракан обнаружен:

После порогового преобразования, выполняем поиск контуров (cv2.findContours()) и для найденного контура вычисляем прямоугольник, которым его можно обвести (cv2.boundingRect()).
findroach.py
def find_roach(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blockSize = 109
C = 53
x1 = 55
y1 = 16
x2 = 258
y2 = 219
print("blockSize: {0} C: {1}".format(blockSize, C))
bina = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, blockSize, C)
cv2.imshow('roacha', bina)
roi = bina[y1:y2, x1:x2]
cv2.imshow('roach', roi)
roi2 = img[y1:y2, x1:x2]
contours, hierarchy = cv2.findContours(roi, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# Choose largest contour
best = 0
maxsize = 0
count = 0
for cnt in contours:
if cv2.contourArea(cnt) > maxsize :
maxsize = cv2.contourArea(cnt)
best = count
count = count + 1
x,y,w,h = cv2.boundingRect(contours[best])
cv2.rectangle(roi2, (x,y), (x+w,y+h), (0,255,0), 2)
cv2.circle(roi2, (x+w/2, y+h/2), 2, (0, 255, 0), -1)
cv2.imshow('roi2', roi2)
Результат — наш таракан найден и обведён рамкой:

Теперь напишем скетч для контроллера CraftDuino, который принимает управляющие команды через последовательный порт и выдаёт соответствующие команды на управление моторами (вперёд-назад, вправо-влево, стоп).
Команды соответствуют стандартным для геймеров клавишам упавления движением: ‘w’, ‘s’, ‘a’, ‘d’ и ‘ ‘ — пробел для остановки.
void loop()
{
// read message from serial
int c = 0;
if(Serial.available()) {
c = Serial.read();
if(c == 'w') {
motor_drive(0, SPEED);
motor_drive(1, SPEED);
}
else if(c == 's') {
motor_drive(0, -SPEED);
motor_drive(1, -SPEED);
}
else if(c == 'a') {
motor_drive(0, SPEED);
motor_drive(1, -SPEED);
}
else if(c == 'd') {
motor_drive(0, -SPEED);
motor_drive(1, SPEED);
}
else if(c == ' ') {
motor_drive(0, 0);
motor_drive(1, 0);
}
}
}
Т.о., если скетч считает из последовательного порта символ ‘w’, то на моторы выдаётся команда «вперёд», и так далее.
Осталось адаптировать питоноский скрипт для работы не с картинкой, а с видеокамерой и добавить взаимодействие с CraftDuino через последовательный порт — при помощи модуля serial.
Итоговый скрипт — отслеживает таракана и выдаёт команды в последовательный порт:
roach_follow.py
prev_x = -1
prev_y = -1
while True:
ret, frame = cap.read()
#cv2.imwrite("frame.png", frame)
x,y = find_roach(frame)
print("x: {0} y: {1}".format(x, y))
if x != -1 and y != -1:
if prev_x == -1 and prev_y == -1:
prev_x = x
prev_y = y
if abs(prev_x - x) > 2 or abs(prev_y - y) > 2 :
if x > prev_x :
print("left")
ser.write("a")
elif prev_x > x :
print("right")
ser.write("d")
if y > prev_y :
print("back")
ser.write("s")
elif prev_y > y:
print("forward")
ser.write("w")
else:
print("stop")
ser.write(" ")
prev_x = x
prev_y = y
ch = cv2.waitKey(150)
if ch == 27:
break
Для обнаружения в какую сторону двигается наш таракан-водитель, скрипт запоминает — где он был задетектирован на прошлом кадре и, таким образом, определяет в какую сторону он сместился.
В результате, получился такой вот замечательный экзоскелет для таракана:



Делать и программировать этакую био-механическую кибер-штуковину было очень забавно.
Ни один таракан при изготовлении и испытании экзоскелета не пострадал.
Через некоторое время таракан, вернее, как оказалась — тараканиха — даже разродилась потомством.

Вот такой вот счастливый вышел финал.
Ссылки
проект на гитхабе — roach_driver
Справка о таракане:
Blaberus craniifer — вид южноамериканского таракана сем. Blaberidae рода Blaberus.
Название «Мёртвая голова» получил из-за рисунка находящегося на переднеспинке, который напоминает череп.
Ареал обитания — тропики Центральной и Южной Америки. Живет в лесной подстилке.
В дневное время суток насекомое скрывается в опавшей листве или около корней деревьев.
Активно избегают света. Питаются, в основном, листовым опадом.
Имеет крылья, которые позволяют таракану планировать.
Условия содержания: температура +28…39 градусов Цельсия, влажность воздуха от 60 до 70%.
В домашних условиях живёт от 1 до 2.5 лет.
В основном, приобретается как декоративное и кормовое насекомое (на корм паукам, ящерицам, хамелеонам, игуанам, змеям и т.п.).
Используются на тараканьих бегах.
Один из самых крупных тараканов: длина взрослой особи составляет 60—75 мм (крупные самки иногда до 80 мм).
Самки отличаются от самцов срастающимися последними сегментами брюшка с нижней стороны (характерный признак семейства).
Живородящи (яйцеживорождение), одна самка приносит до 30 нимф. Оотека инкубируется от 60 до 90 дней (в зависимости от температуры).
Личинка широкая, плоская, 6-7 мм в длину. После рождения, зарываются в субстрат и проводят в нём большую часть времени.
Личинки проходят от 9 до 13 стадий в развитии. Линяют, сидя на вертикальной поверхности. Время превращения нимфы во взрослое насекомое занимает 4-5 месяцев.Википедия
Blaberus craniifer
Blaberus
Литература
1. Brian R Tietz (2012) Models of Cockroach Shelter Seeking Implemented on a Robotic Test Platform (PDF)
По теме
OpenCV шаг за шагом. Обработка изображения — пороговое преобразование
OpenCV шаг за шагом. Нахождение контуров и операции с ними
RoboCraft на HackDay #34 в Калининграде
Сборка мощного моторшилда (на базе L298)
OpenCV шаг за шагом. Захват видео с камеры
Питание для Raspberry Pi
Подключение Raspberry Pi к Wi-Fi
Подключение модуля камеры к Raspberry Pi
Аквариум на колёсах на базе Arduino и Beagleboard


0 комментариев на «“Экзоскелет для таракана”»
А видео есть?
Сенсация! По лаборатории RoboCraft разгуливают тараканы-нацисты. Шокирующие эксперименты российских учёных.