使用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 纳入正式支援吧~ ^^"
本文转自http://kheresy.wordpress.com/2012/02/03/control_kinect_motor_via_openni/