суббота, 20 апреля 2013 г.

Получаем облака точек с помощью Kinect в PCL


Думаю, что многие наслышаны о такой интересной штуке, как Kinect. Сегодня я напишу простейшие рекомендации к использованию сего девайса при помощи OpenNI и Point Cloud Libray на C++.


Какие возможности это даст?

С помощью Kinect+OpenNI+PCL можно получать и обрабатывать облако точек с информацией о их цвете. Самое простое решение показано на видео.



Что нужно установить?


Итак, для использования необходимо установить необходимые пакеты. Для пользователей Windows можно скачать с официального сайта PCL готовый установщик со всем необходимым под MS Visual Studio. Любителям MinGW и других компиляторов придётся собирать пакет вручную, хотя с CMake это не сложно.
Пользователи linux также могут скачать готовый пакет из репозитория своего дистрибутива. Для поддержки Kinect ставим пакеты openni-dev и ps-engine (в разных дистрах названия могут чуть-чуть отличаться).
Тем, кто решился собирать из исходников советую обратиться к этому руководству .

 

 Установка драйверов


  • Windows

Итак, для установки используйте драйвера из папки в которую установлен PrimeSense(в папке /SensorKinect/Driver/). После установки в диспетчере устройств должно появиться три устройства (*Audio, *Motion, *Camera).


  • Linux

Если вы поставили всё верно, то введя lsusb будете наблюдать примерно это:

Bus 002 Device 008: ID 045e:02ad Microsoft Corp. Xbox NUI Audio
Bus 002 Device 007: ID 045e:02b0 Microsoft Corp. Xbox NUI Motor
Bus 002 Device 009: ID 045e:02ae Microsoft Corp. Xbox NUI Camera

Если этого не происходит, то надо проверить пакеты openni-dev и ps-engine и проверить снова.


Hello world from Kinect

На сайте с документацией по PCL есть пример работы с Kinect, но PCL не всегда стабильна и иногда сбоит. Например, те туториалы, что описаны на официальном сайте с документацией, у меня не завелись(тестил под PCL1.6 Windows, PCL1.7 Kubuntu 12.10).
Для начала возьмём пример отсюда.

Иерархия директорий такая
Project_root_dir
|   +  CMakeLists.txt
|--src
|   +  main.cpp
|--build


// -----------------------------------------------------
// Original code can be found here

// http://robotica.unileon.es/mediawiki/

// index.php/ Kinect_tutorial_1:_First_steps

// Based on Geoffrey's Biggs, taken from the PCL tutorial in
// http://pointclouds.org/documentation/tutorials/pcl_visualizer.php
 
// Simple Kinect viewer that also allows to write the current scene to a .pcd
// when pressing SPACE.
 
#include <iostream>
 
#include <pcl/io/openni_grabber.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/console/parse.h>
 
using namespace std;
using namespace pcl; 


// В проекте используются умные указатели BOOST
// Например, PointCloud<PointXYZRGBA>::Ptr означает,
// что мы создаём умный указатель на объект
// класса PointCloud<PointXYZRGBA>
// т.е. boost::shared_ptr< PointCloud<PointXYZRGBA> >

 
PointCloud<PointXYZRGBA>::Ptr cloudptr(new PointCloud<PointXYZRGBA>); 
// Облако с типом точек содержащих координаты и цвет  

 

 PointCloud<PointXYZ>::Ptr fallbackCloud(new PointCloud<PointXYZ>);
// Ооблако с типом точек содержащих только координаты  

 

boost::shared_ptr<visualization::CloudViewer> viewer;
// Объект позволяющий просматривать облако

 

Grabber* kinectGrabber;
// Осуществляет захват данных с Kinect
 

unsigned int filesSaved = 0;
// For the numbering of the clouds saved to disk.
 

bool saveCloud(false), noColour(false);

// Program control.
 
void
printUsage(const char* programName)
{
    cout << "Usage: " << programName << " [options]"
         << endl
         << endl
         << "Options:\n"
         << endl
         << "\t<none>     start capturing from a Kinect device.\n"
         << "\t-v NAME    visualize the given .pcd file.\n"
         << "\t-h         shows this help.\n";
}
 
// Callback-функция
//(вызывается когда появляются новые данных с устройства)
void
grabberCallback(const PointCloud<PointXYZRGBA>::ConstPtr& cloud)
{
    if (! viewer->wasStopped())
        viewer->showCloud(cloud);
 
    if (saveCloud)
    {
        stringstream stream;

 stream << "inputCloud" << filesSaved << ".pcd";
        string filename = stream.str();
        if (io::savePCDFile(filename, *cloud, true) == 0)
        {
            filesSaved++;
            cout << "Saved " << filename << "." << endl;
        }
        else PCL_ERROR("Problem saving %s.\n", filename.c_str());
 
        saveCloud = false;
    }
}
 
// For detecting when SPACE is pressed.
void
keyboardEventOccurred(const visualization::KeyboardEvent& event,
    void* nothing)
{
    if (event.getKeySym() == "space" && event.keyDown())
        saveCloud = true;
}
 
// Creates, initializes and returns a new viewer.
boost::shared_ptr<visualization::CloudViewer>
createViewer()
{
    boost::shared_ptr<visualization::CloudViewer> v
        (new visualization::CloudViewer("3D Viewer"));
    v->registerKeyboardCallback(keyboardEventOccurred);
 
    return(v);
}
 
int
main(int argc, char** argv)
{
    if (console::find_argument(argc, argv, "-h") >= 0)
    {
        printUsage(argv[0]);
        return 0;
    }
 
    bool justVisualize(false);
    string filename;
    if (console::find_argument(argc, argv, "-v") >= 0)
    {
        if (argc != 3)
        {
            printUsage(argv[0]);
            return 0;
        }
 
        filename = argv[2];
        justVisualize = true;
    }
    else if (argc != 1)
    {
        printUsage(argv[0]);
        return 0;
    }
 
    // First mode, open and show a cloud from disk.
    if (justVisualize)
    {
        // Try with colour information...
        try
        {
            io::loadPCDFile<PointXYZRGBA>(filename.c_str(), *cloudptr);
        }
        catch (PCLException e1)
        {
            try
            {
               // ...and if it fails, fall back to just depth.
               io::loadPCDFile<PointXYZ>(filename.c_str(), *fallbackCloud);
            }
            catch (PCLException e2)
            {
                return -1;
            }
 
            noColour = true;
        }
 
        cout << "Loaded " << filename << "." << endl;
     if (noColour)
      cout << "This file has no RGBA colour information present." << endl;
    }
    // Second mode, start fetching and displaying frames from Kinect.
    else
    {
      kinectGrabber = new OpenNIGrabber();
      if (kinectGrabber == 0)
            return false;
      boost::function<void (const PointCloud<PointXYZRGBA>::ConstPtr&)> f =
        boost::bind(&grabberCallback, _1);
      kinectGrabber->registerCallback(f);
    }
 
    viewer = createViewer();
 
    if (justVisualize)
    {
        if (noColour)
            viewer->showCloud(fallbackCloud);
        else viewer->showCloud(cloudptr);
    }
    else kinectGrabber->start();
 
    // Main loop.
    while (! viewer->wasStopped())
        boost::this_thread::sleep(boost::posix_time::seconds(1));
 
    if (! justVisualize)
        kinectGrabber->stop();
}
// -----------------------------------------------------

Будем собирать проект CMake, для этого создадим в папке проекьта файл CMakeLists.txt, содержащий следующее:

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
 
project(kinect_PCL_viewer)
 
find_package(PCL 1.6 REQUIRED)
 
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
 
set(PCL_BUILD_TYPE Release)
 
file(GLOB kinectpclviewer_SRC
    "src/*.h"
    "src/*.cpp"
)
add_executable(kinectPCLviewer ${kinectpclviewer_SRC})
 
target_link_libraries (kinectPCLviewer ${PCL_LIBRARIES})


Собираем

cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make

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

./kinectPCLviewer -v inputCloud0.pcd

Каверзные вопросы

Итак, предположим, что Вы научились получать данные с Kinect и хотите
их обработать PCL, но все ваши попытки это сделать приводят к ошибкам сегментации и т.д. Самые распространённые по опыту причины:

  • Nan в данных. Специфика Kinect такова, что при невозможности получить данные о глубине(положении), координаты точки выставляются в Nan(not a number). Многие алгоритмы PCL при встрече с такими точками работают некорректно или падают. Поэтому перед тем, как начать работу с облаком, удалите из него точки со значениями Nan.
  • Ошибка при работе с указателями. Пусть это не отностися напрямую к Kinect, но следите за инициализацией, операциями с указателями. Да, Boost и так облегчает это, но не избавляет от невниматльности.


Таким образом, мы научились настраивать всё необходимое и рассмотрели простой пример использования Kinect с помощью PCL на C++.

Комментариев нет:

Отправить комментарий