Добрый день уважаемые читатели Хабра. Несколько лет назад я писал об использовании камеры 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 <user>@<ip>

Параметр -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. В следующий раз мы попробуем детекцию объектов. Всем удачи в экспериментах и до новых встреч!

Комментарии (7)


  1. MaxAlekseev
    17.11.2018 21:56
    +1

    На процессоре малины необходимо иметь радиатор, иначе при сборке opencv и при работе перегрев и вылет,

    make -j4
    это явный перебор, можно не пытться, -j2 максимум.


  1. Codiko
    18.11.2018 20:34

    Странно, по железу прогресс идет полным ходом, а ОС так и осталась в мезазойской эре. Неужели нельзя сделать что бы подключение камеры было как в винде, поставил и все заработало сразу без танцев с бубном?


    1. ntfs1984
      18.11.2018 21:10

      Подобные устройства даже в Винде подключаются с танцем с бубном. Это ведь не USB-вебкамера, а совершенно отдельное устройство со своей шиной.

      Конечно можно это все автоматизировать. Но не нужно. Эта автоматизация выльется в лишнее потребление ресурсов, которых на Малинке не так много, а на выхлопе получим упрощение единоразовой задачи одного процента пользователей подобных камер.


      1. Codiko
        18.11.2018 22:08

        Есть вполне понятная камера, достаточно сделать один файл скрипт в котором все само установится именно под эту камеру. И ничего не нужно руками вводить. Запустил и все подключилось само. Это что, какая то нереальная задача?
        У меня недавно была задача на PI — при запуске что бы открывался браузер с развернутой на весь экран страницей, которая обновлялась бы каждые 15 минут, ночью гас экран а утром включался.
        Я несколько дней потратил что бы этот элементарный алгоритм заработал!!!
        Это ппц просто!
        Не должен человек который в первый раз видит PI столько е… ся что бы решить задачу, которая решается в несколько кликов и без лазания по инету в поисках решения.
        Неправильно это, всего лишь таков мой посыл в сообщении


        1. ntfs1984
          19.11.2018 02:00
          +1

          Не не не. Если вы перечитаете статью, то увидите что камера устанавливается всего лишь активацией драйвера утилитой.

          Запустим raspi-config:
          sudo raspi-config
          Выберем опцию Interfacing, затем Pi Camera и нажмем Yes. И наконец Finish


          Все остальное устанавливается для работы проекта, а не для работы камеры.
          Повторюсь, это везде так. Ну можете попробовать подключить, не знаю, SPI-TFT в Windows, и потом на него что-то вывести. Это примерно то же самое.

          У меня недавно была задача на PI — при запуске что бы открывался браузер с развернутой на весь экран страницей, которая обновлялась бы каждые 15 минут, ночью гас экран а утром включался

          Хм, я с таким не сталкивался, но что вам помешало echo 'chromium-browser --kiosk «www.google.com» ' >> ~/.config/lxsession/LXDE/autostart
          А потом crontab -e и
          0 23 * * * echo 0 > /sys/class/backlight/power
          0 7 * * * echo 1 > /sys/class/backlight/power
          Или как там ваш экран включается\выключается?

          Я ничего не гуглил, это первое что на ум пришло. Впрочем Raspberry Pi тут ни при чем. Во-первых, на десктопных Линуксах будет то же самое, а во-вторых, в Винде было бы еще хуже, и хрен бы вы что-то изменили на лету скриптом.


          1. Codiko
            19.11.2018 15:52

            вот как раз живой пример — так он не выключается :)

            0 23 * * * echo 0 > /sys/class/backlight/power
            0 7 * * * echo 1 > /sys/class/backlight/power
            Или как там ваш экран включается\выключается?

            10 08 * * 0-6 /usr/bin/vcgencmd display_power 1
            10 01 * * 0-6 /usr/bin/vcgencmd display_power 0


  1. Zel
    19.11.2018 10:18

    А тут специально все изображения перевёрнуты?
    Я не спец, извините