Использование камеры Fish eye на Raspberry Pi 3 с ROS — часть 1

Добрый день уважаемые читатели Хабра. Несколько лет назад я писал об использовании камеры Raspberry Pi Camera Board на Raspberry Pi в связке с ROS. В этой и следующей статьях я бы хотел рассказать об использовании широкоугольной камеры типа fish eye на Raspberry Pi 3 с установленной Ubuntu 16.04. Кому интересно прошу под кат.
Для начала почему именно fish eye камера? Я видел много статей об использовании широкоугольных камер для визуальной одометрии и SLAM. Благодаря большему углу обзора fish eye камера повышает точность визуальной одометрии. Поэтому мне захотелось попробовать одну такую камеру с Raspberry Pi с поддержкой ROS. Я купил камеру с углом 160 градусов на dx.com за 28$. В комплект с камерой также входят две IR лампы для ночного видения:

image

В первой статье я расскажу об установке необходимых драйверов, OpenCV 3 и пакетов для поддержки камеры Raspberry Pi Camera Board в ROS.

Установка драйвера камеры fish eye на Raspberry Pi 3


Итак начнем. Подключимся к RPi 3 по SSH:

ssh -Y @


Параметр -Y позволяет решить проблему с получением ошибки «Could not connect to display» при запуске некоторых GUI приложений (Qt, окно с изображением из программы OpenCV). Подробнее можно узнать здесь.

Для начала нам нужно включить поддержку драйвера камеры в настройках Raspberry Pi raspi-config. В Raspbian эта служба уже включена, в Ubuntu ее нужно установить:

sudo apt-get install raspi-config


Запустим raspi-config:

sudo raspi-config


Выберем опцию Interfacing, затем Pi Camera и нажмем Yes. И наконец Finish.
Проверим, что поддержка камеры включена с помощью утилиты raspistill:

raspistill -o mypicture.jpg


Если вы получили ошибку «Camera is not detected. Please check carefully the camera module is installed correctly» проверьте, правильно ли вы подключили камеру к Raspberry Pi. Также можно выполнить ребут системы (мне это помогло).

Попробуем записать видео:

raspivid -o myvideo.h264


Я получил изображение на мониторе, подключенном к Raspberry Pi. Мне не удалось получить всплывающее окно на моем компьютере при подключении по ssh.

image

Использование OpenCV 3 с fish eye камерой на Raspberry Pi


Установим библиотеку picamera[array]:

pip install "picamera[array]"


Установим необходимые зависимости для OpenCV. Для начала обновим пакетный менеджер apt и сделаем апгрейд предустановленных пакетов:

sudo apt-get update
sudo apt-get upgrade


Установим некоторые библиотеки:

sudo apt-get install build-essential cmake pkg-config

sudo apt-get install libjpeg8-dev libtiff5-dev libjasper-dev libpng12-dev

sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
sudo apt-get install libxvidcore-dev libx264-dev

sudo apt-get install libgtk-3-dev

sudo apt-get install libatlas-base-dev gfortran

sudo apt-get install python2.7-dev python3.5-dev


Мы установим OpenCV 3 из исходников.

cd ~
wget -O opencv.zip https://github.com/Itseez/opencv/archive/3.1.0.zip
unzip opencv.zip


Нам также необходимо загрузить репозиторий opencv_contrib:

wget -O opencv_contrib.zip https://github.com/Itseez/opencv_contrib/archive/3.1.0.zip
unzip opencv_contrib.zip


Дело в том, что в OpenCV 3 пакеты с дескрипторами признаков (такие как SIFT и SURF) были перенесены в отдельный репозиторий contrib. Теперь для использования дескрипторов признаков нам необходимо отдельно скачивать репозиторий contrib.

Теперь мы наконец-то готовы к установке OpenCV. Мы можем выполнить cmake для компиляции OpenCV с нужными параметрами:

cd ~/opencv-3.1.0/
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D INSTALL_PYTHON_EXAMPLES=ON \
    -D INSTALL_C_EXAMPLES=OFF \
    -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.1.0/modules \
    -D PYTHON_EXECUTABLE=~/.virtualenvs/cv/bin/python \
    -D BUILD_EXAMPLES=ON ..


Если cmake выполнился без ошибок, выполним make:

make -j4


При выполнении компиляции я получил ошибку «Segmentation fault». Если вы получили такую же ошибку, выполните make clean для удаления результатов компиляции и выполните make с одним ядром:

make clean
make


У меня процедура компиляции заняла 3 часа. Наконец выполним установку OpenCV 3:

sudo make install
sudo ldconfig


Здесь есть интересный нюанс, связанный с ROS Kinetic. Если вы устанавливаете ROS Kinetic, то ROS добавляет путь к библиотекам Python (/opt/ros/kinetic/lib/python2.7/dist-packages) в системный путь при выполнении команды source /opt/ros/kinetic/setup.bash. Это ведет к некоторым проблемам при последующей установке OpenCV из исходников (подробнее об этом написано здесь). Чтобы решить проблему, нужно удалить строку «source /opt/ros/kinetic/setup.bash» из файла .bashrc. Не забудем выполнить:

source ~/.bashrc


Давайте проверим, что OpenCV теперь корректно линкуется из Python.
Создадим папку для проекта и простой тестовый скрипт:

mkdir PiCamera && cd PiCamera
vim test_cam.py


Добавим следующий код в файл:

from picamera.array import PiRGBArray                                                                         
from picamera import PiCamera                                                                                 
import time                                                                                                   
import cv2                                                                                                    
                                                                                                              
# initialize the camera and reference the raw camera capture                                                  
camera = PiCamera()                                                                                           
rawCapture = PiRGBArray(camera)                                                                               
                                                                                                              
# allow camera to warmup                                                                                      
time.sleep(0.1)                                                                                               
                                                                                                              
# grab an image                                                                                               
camera.capture(rawCapture, format="bgr")                                                                      
image = rawCapture.array                                                                                      
                                                                                                              
cv2.imshow("Capture", image)                                                                                  
cv2.waitKey(0)


Запустим скрипт:

python test_cam.py


В случае успеха мы получим примерно такую картинку:

image

Давайте теперь попробуем записать видео с камеры.

vim test_videom.py


Добавьте в файл вот такой код:

# import the necessary packages                                                                               
from picamera.array import PiRGBArray                                                                         
from picamera import PiCamera                                                                                 
import time                                                                                                   
import cv2                                                                                                    
                                                                                                              
# initialize the camera and grab a reference to the raw camera capture                                        
camera = PiCamera()                                                                                           
camera.resolution = (640, 480)                                                                                
camera.framerate = 32                                                                                         
rawCapture = PiRGBArray(camera, size=(640, 480))                                                              
                                                                                                              
# allow the camera to warmup                                                                                  
time.sleep(0.1)                                                                                               
                                                                                                              
# capture frames from the camera                                                                              
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):                        
    # grab the raw NumPy array representing the image, then initialize the timestamp                          
    # and occupied/unoccupied text                                                                            
    image = frame.array                                                                                       
                                                                                                              
    # show the frame                                                                                          
    cv2.imshow("Frame", image)                                                                                
    key = cv2.waitKey(1) & 0xFF                                                                               
                                                                                                              
    # clear the stream in preparation for the next frame                                                      
    rawCapture.truncate(0)                                                                                    
                                                                                                              
    # if the `q` key was pressed, break from the loop                                                         
    if key == ord("q"):                                                                                       
        break


Давайте попробуем что-нибудь поинтереснее, например, добавим детекцию краев. Я здесь использую детектор Кенни (код взят отсюда):

from picamera.array import PiRGBArray                                                                         
from picamera import PiCamera                                                                                 
import time                                                                                                   
import cv2                                                                                                    
import numpy as np                                                                                            
                                                                                                              
# initialize the camera and grab a reference to the raw camera capture                                        
camera = PiCamera()                                                                                           
camera.resolution = (640, 480)                                                                                
camera.framerate = 32                                                                                         
rawCapture = PiRGBArray(camera, size=(640, 480))                                                              
                                                                                                              
                                                                                                              
# allow the camera to warmup                                                                                  
time.sleep(0.1)                                                                                               
                                                                                                              
# capture frames from the camera                                                                              
for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):                        
    # grab the raw NumPy array representing the image, then initialize the timestamp                          
    # and occupied/unoccupied text                                                                            
    image = frame.array                                                                                       
                                                                                                              
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)                                                              
                                                                                                              
    lower_red = np.array([30,150,50])                                                                         
    upper_red = np.array([255,255,180])                                                                       
                                                                                                              
    mask = cv2.inRange(hsv, lower_red, upper_red)                                                             
    res = cv2.bitwise_and(image,image, mask= mask)
    edges = cv2.Canny(res,100,200)                                                                            
    # show the frame                                                                                          
    cv2.imshow("Frame", image)                                                                                
    cv2.imshow("Edges", edges)                                                                                
    key = cv2.waitKey(1) & 0xFF                                                                               
                                                                                                              
    # clear the stream in preparation for the next frame                                                      
    rawCapture.truncate(0)                                                                                    
                                                                                                              
    # if the `q` key was pressed, break from the loop                                                         
    if key == ord("q"):                                                                                       
        break


Вот результат запуска программы:

image

Добавление поддержки камеры Raspberry Pi Camera в ROS


Теперь добавим возможность работать с fish eye камерой для Raspberry Pi из ROS. Для начала установим необходимый драйвер V4L2 для Raspberry Pi камеры (можно подробнее прочитать здесь). Выполним команду:

sudo rpi-update


и сделаем ребут системы. Теперь добавим драйвер:

sudo modprobe bcm2835-v4l2


Проверим, что устройство /dev/video0 доступно:

ll /dev/video0


Вывод будет таким:

crw-rw----+ 1 root video 81, 0 Mar 17 15:47 /dev/video0


Скачаем пакет usb_cam:

sudo apt-get install ros-kinetic-usb-cam
source /opt/ros/kinetic/setup.bash


Запустим ROS мастер и rqt_image_view:

roscore
roslaunch usb_cam usb_cam-test.launch
rosrun rqt_image_view rqt_image_view


Выберем топик /usb_cam/image_raw. Мы получим такую картинку:

image

Теперь fish eye камеру можно использовать с любыми пакетами компьютерного зрения в ROS. В следующий раз мы попробуем детекцию объектов. Всем удачи в экспериментах и до новых встреч!

© Habrahabr.ru