使用 OpenNI 控制 Kinect 的馬達


雖然應該有很多人是用 Microsoft Kinect for Xbox 360 來進行 OpenNI 程式的開發,但是到目前為止,市面上 OpenNI 真正的相容裝置,其實還是只有 ASUS 的 Xtion Pro 系列,要使用 Kinect 是需要特殊修改過的驅動程式的。而在驅動程式的部分,目前大部分的開發應該都還是使用 avin2 所提供 SensorKinect 吧~

而如果是要使用 Kinect 進行程式開發的話,使用跨平台、開放原始碼的 OpenNI 和使用微軟官方的 Kinect for Windows SDK 相比,一個很大的缺點,就是功能上的不完整;這包括了聲音、以及 Kinect 上的馬達。

聲音的部分,OpenNI 本身有提供 API 來做控制,目前的問題應該是 SensorKinect 還沒有支援(有列在 todo list);而馬達的部分,OpenNI 本來的 API 並沒有支援,不過目前看來也已經是打算透過 xn::GeneralIntCapability 的形式來做支援了,只是現在還是無法使用就是了。

不過,後來才發現,在官方論壇上,已經有強者透過 OpenNI 比較低階的 USB 控制介面(XnUSB.h),來做到馬達的控制了!原文可以參考《Easy way to control Kinect motor through OpenNI》一文;而目前 OpenNI 也已經把他的程式碼放到 github 上的 Samples 裡(連結)了!

他基本上是把相關功能封包成一個 KinectMotors 的類別,把相關的控制寫成它的成員函式、以方便操作;其內容如下:

class KinectMotors
{
public:
  enum { MaxDevs = 16 };
 
public:
  KinectMotors()
  {
    m_isOpen = false;
  }
  
  virtual ~KinectMotors()
  {
    Close();
  }
 
  bool Open()
  {
    const XnUSBConnectionString *paths;
    XnUInt32 count;
    XnStatus res;
 
    // Init OpenNI USB
    res = xnUSBInit();
    if (res != XN_STATUS_OK)
    {
      xnPrintError(res, "xnUSBInit failed");
      return false;
    }
 
    // Open all "Kinect motor" USB devices
    res = xnUSBEnumerateDevices(0x045E /* VendorID */, 0x02B0 /*ProductID*/, &paths, &count);
    if (res != XN_STATUS_OK)
    {
      xnPrintError(res, "xnUSBEnumerateDevices failed");
      return false;
    }
 
    // Open devices
    for (XnUInt32 index = 0; index < count; ++index)
    {
      res = xnUSBOpenDeviceByPath(paths[index], &m_devs[index]);
      if (res != XN_STATUS_OK)
      {
        xnPrintError(res, "xnUSBOpenDeviceByPath failed");
        return false;
      }
    }
 
    m_num = count;
 
    XnUChar buf[1];  // output buffer
 
    // Init motors
    for (XnUInt32 index = 0; index < m_num; ++index)
    {
      res = xnUSBSendControl(m_devs[index], (XnUSBControlType) 0xc0, 0x10, 0x00, 0x00, buf, sizeof(buf), 0);
      if (res != XN_STATUS_OK)
      {
        xnPrintError(res, "xnUSBSendControl failed");
        Close();
        return false;
      }
 
      res = xnUSBSendControl(m_devs[index], XN_USB_CONTROL_TYPE_VENDOR, 0x06, 0x01, 0x00, NULL, 0, 0);
      if (res != XN_STATUS_OK)
      {
        xnPrintError(res, "xnUSBSendControl failed");
        Close();
        return false;
      }
    }
    m_isOpen = true;
    return true;
  }
  
  void Close()
  {
    if (m_isOpen)
    {
      for (XnUInt32 index = 0; index < m_num; ++index)
      {
        xnUSBCloseDevice(m_devs[index]);
      }
      m_isOpen = false;
    }
  }
 
  bool Move(int angle)
  {
    XnStatus res;
    // Send move control requests
    for (XnUInt32 index = 0; index < m_num; ++index)
    {
      res = xnUSBSendControl(m_devs[index], XN_USB_CONTROL_TYPE_VENDOR, 0x31, angle, 0x00, NULL, 0, 0);
      if (res != XN_STATUS_OK)
      {
        xnPrintError(res, "xnUSBSendControl failed");
        return false;
      }
    }
    return true;
  }
 
private:
  XN_USB_DEV_HANDLE m_devs[MaxDevs];
  XnUInt32 m_num;
  bool m_isOpen;
};

而在程式裡建立一個 KinectMotors 的物件後,就可以透過他的 Move() 這個函式,來進行 Kinect 仰角的調整了!簡單的操作範例,大致上如下:

KinectMotors motors;
if (!motors.Open())
  return 1;
  
motors.Move(31);

不過基本上,這個方法應該只算是一個雛形,和 OpenNI 提供的一般操作介面不完全一致;而且在這個類別裡,他基本上會根據 Vender ID 和 Product ID 把系統裡的 Kinect 馬達裝置都找出來(透過 xnUSBEnumerateDevices()),然後都開啟來控制;所以如果有使用多台 Kinect 的話,他會同時對所有的 Kinect 來進行操作!如果要個別控制的話,則是需要改寫這個類別,並針對得到的 XnUSBConnectionString 來和 xn::Device 的資訊(應該是「Create Info」、參考)做比對、以找出要控制的是哪一個 Kinect 。

另外要注意的是,由於 Kinect 內部其實還有一個加速度感應器,可以判斷自身的傾角,所以實際上這邊透過 Move() 設定的角度,會是空間中絕對的角度!也就是說,0 度的時候,Kinect 會是水平的,正值會往上抬、負值則會往下傾,直到到達目標角度為止;這是在使用時可能要注意的地方。

總之,如果有必要的話,透過這個方法應該是可以用來操作 Kinect 的馬達了~不過,還是希望 OpenNI 能趕快把 Kinect 納入正式支援吧~ ^^"


OpenNI / Kinect 相關文章目錄

對「使用 OpenNI 控制 Kinect 的馬達」的想法

  1. 您好
    我將github的程式在vs2010上執行
    但是卻出現fatal error C1083: 無法開啟包含檔案: ‘windows.h’: No such file or directory

  2. 這東西真厲害 直接套上很好用 終於能動角度了orz 不然實驗很累 還要自己抓角度= =
    Heresy您的文章的真的非常有參考跟價值性
    也很感謝您~

  3. 請問一下我將https://github.com/manctl/openni/blob/manctl/Samples/Kinect/kinect-motors.cpp
    裡面的c++程式碼丟到vs 2010執行,code沒有錯,但卻有六個不知道怎麼解掉的bug

    1>isocpp.obj : error LNK2019: 無法解析的外部符號 __imp__xnUSBSendControl 在函式 “public: bool __thiscall KinectMotors::Open(void)" (?Open@KinectMotors@@QAE_NXZ) 中被參考
    1>isocpp.obj : error LNK2019: 無法解析的外部符號 __imp__xnUSBOpenDeviceByPath 在函式 “public: bool __thiscall KinectMotors::Open(void)" (?Open@KinectMotors@@QAE_NXZ) 中被參考
    1>isocpp.obj : error LNK2019: 無法解析的外部符號 __imp__xnUSBEnumerateDevices 在函式 “public: bool __thiscall KinectMotors::Open(void)" (?Open@KinectMotors@@QAE_NXZ) 中被參考
    1>isocpp.obj : error LNK2019: 無法解析的外部符號 __imp__xnPrintError 在函式 “public: bool __thiscall KinectMotors::Open(void)" (?Open@KinectMotors@@QAE_NXZ) 中被參考
    1>isocpp.obj : error LNK2019: 無法解析的外部符號 __imp__xnUSBInit 在函式 “public: bool __thiscall KinectMotors::Open(void)" (?Open@KinectMotors@@QAE_NXZ) 中被參考
    1>isocpp.obj : error LNK2019: 無法解析的外部符號 __imp__xnUSBCloseDevice 在函式 “public: void __thiscall KinectMotors::Close(void)" (?Close@KinectMotors@@QAEXXZ) 中被參考
    1>E:\Users\Nathaniel.Think\Documents\Visual Studio 2010\Projects\isocpp\Debug\isocpp.exe : fatal error LNK1120: 6 個無法解析的外部符號

    懇請指教,謝謝

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料