- 在mOBContext.close()前,必须确保此次得到的mDevice都被close()过。mDevice.close()会在两处,如果正处在orbbecCreateContext(),由orbbecCreateContext负责close()。否则在onDeviceAttach,立即就mDevice.close()。
- 检测到插入一个相机时,会调用onDeviceAttach。每次构造mOBContext时,是否也肯定会调用onDeviceAttach?——我猜是的,但没官方确认。
深度相机,需要支持两个平台,一是windows,主要是开发、调试阶段,二是android,是机器人最终运行的平台。控制机器人用Ros,当然,Ros已移植到支持Android,在那里编程语言可说全是C++,要融入深度相机,自然希望还是C++,不要有Java。
奥比中光只提供sdk,没提供源码。针对Android,给出的app示例代码全是java。如何改为用更多C++,第一想到的自然是参考windows示例,毕竟那里全是C++。
比较下来,在windows,一开始创建ob::Pipeline,就会找到相机,并成功。换到android,一开始创建ob::Pipeline,会因为找不到相机,创建失败。针对这个原因,在android在额外要做的,是创建ob::Pipeline前,先出来个能让发现相机的环境。这个操作是通过Android SDK提供的initOBContext函数。
基于的是OrbbecSDK_Android_v1.6.3,这里改出一种方法,大致分三个阶段。
一、(java)创建OBContext mOBContext
新增java函数:orbbecCreateContext。app调用它,它调用initOBContext。确认得到一个mDevice后,此函数就结束。认为已准备好能让发现相机的环境,后面C++调用“new ob::Pipeline”应该能找到相机,并成功创建Pipeline。
1.1 orbbecCreateContext
public static boolean orbbecCreateContext() { mIsWaitOrbbecCreateContext = true; boolean ret = false; initOBContext();
(1/4)构造OBContext。然后就等着检测到一个奥比中光相机(mDevice),或3秒检测不到的话,认为此时没有相机,orbbecCreateContext返回false。
int maxTimes = 60; // 3 seconds while (maxTimes > 0) { try { Thread.sleep(50); if (mDevice != null) { if (null != mDevice) { mDevice.close(); mDevice = null;
(3/4)已得到一个mDevice, 认为OBContext就绪了。
必须调用这个mDevice.close()。否则紧接的“new ob::Pipeline”时会报下面警告,导致失败。
W/OrbbecSDK: [2023-11-26 14:13:24.368076][warning][4495][ObException.cpp:5] Trying to create a device that you've already created! SN/ID: , uid: 1004
} // 释放SDK if (null != mOBContext) { // mOBContext.close(); // mOBContext = null;
不能调用上面的释放SDK。否则紧接的“new ob::Pipeline”时会报下面警告,导致失败。
W/OrbbecSDK: [2023-11-26 14:25:30.376564][warning][4930][ObException.cpp:5] No device found, fail to create pipeline!
} ret = true; break; } maxTimes --; } catch (InterruptedException e) { } } mIsWaitOrbbecCreateContext = false; return ret; }
1.2 initOBContext
initOBContext是SDK中的同名函数简化版。由于创建pipeline、start等都要改放到C++,java部分得到mDevice就结束onDeviceAttach。
private static void initOBContext() { // 1.初始化SDK, 并监听设备变化 mOBContext = new OBContext(mSingleton, new DeviceChangedCallback() { @Override // 每次构造OBContext时,是否都会有onDeviceAttach? ---应该是吧 public void onDeviceAttach(DeviceList deviceList) { try { if (null == mDevice) { if (!mIsWaitOrbbecCreateContext) { mDevice.close(); mDevice = null;
要进此个入口,意味着C++已创建pipeline,并已经start, 还有可能在处理图像。此时拔出相机、又插入。
对此时的mDevice,由这里销毁。如果不销毁此个的mDevice,下一次构造OBContext会出异常。
} mDevice = deviceList.getDevice(0);
(2/4)已得到一个mDevice,这个mDevice会在orbbecCreateContext销毁。见上面的(3/4)。
} } catch (Exception e) { e.printStackTrace(); } finally { // 12.释放设备列表资源 deviceList.close(); } } @Override public void onDeviceDetach(DeviceList deviceList) { try { deviceList.close(); } catch (Exception e) { e.printStackTrace(); } } }); }
二、(C++)创建Pipeline,并使用
可以用无参数版本的ob::Pipeline()创建Pipeline,然后加入流,start,并处理深度帧、彩色帧。结束了调用stop。
整个过程和Windows下C++ SDK可说没啥两样,全是C++代码。
三、(java)销毁之前创建mOBContext
app调用orbbecDestroyContext()销毁mOBContext,不论之前有没有得到过mDevice,都要销毁。
public static void orbbecDestroyContext() { assert(mDevice == null); assert(mOBContext != null); // (4/4)释放SDK if (null != mOBContext) { mOBContext.close(); mOBContext = null; } }
要严格的话,需对mDevice访问加上同步锁。orbbecCreateContext、orbbecDestroyContext都发生在主线程,onDeviceAttach是在另一个线程,这两个线程都会对mDevice执行close()、置null操作。要发生多线程安全问题,具本会是在拔出相机,然后插入,在插入时,同时调用了orbbecDestroyContext。
四、相机坐标系

图1是相机坐标系。要把相机坐标系下坐标转换到base_footprint坐标下坐标,可以先绕着x轴逆时针旋转90度,再绕着z轴逆时针旋转90度。用RPY表示就是(-M_PI / 2, 0.0, -M_PI / 2)。

图2显示了一次转换过程。变量transform是相机坐标系到base_footprint坐标系的tf,当中平移部分设置了(-0.000567, 0.00992, 0.0000368875)。camera_post是一个camera坐标系下位姿(向量),经过转换后,在base_footprint位姿是t_out。接下让直接理解平移部分前、后值。
- position.x。base_footprint的x轴是camera的z轴,而且方向一样。旋转后,x将等于原z=3,加上平移-0.000567后,最终x值是2.9999432。
- position.y。base_footprint的y轴是camera的x轴,但方向相反。旋转后,y将等于原x=-1,加上平移0.00992后,最终y值是-0.99008。
- position.z。base_footprint的z轴是camera的y轴,但方向相反。旋转后,y将等于原y=-2,加上平移0.0000368875后,最终z值是-1.999963。
代码中trans[2]要除以1000后再赋值给translation.x,是因为trans[2]单位是毫米,translation.x单位是米。
[OpenNIDeviceInfo.cpp:180] New openni device matched. [DeviceManager.cpp:15] Current found device(s): (1) [DeviceManager.cpp:24] - Name: SV1301S_U3, PID: 0x0614, SN/ID: , Connection: USB2.0 [OpenNIHostProtocol.cpp:546] Hardware versions: FW=5.8.23 (14), HW=0, Chip=7, Sensor=0, SYS=12 [OpenNIHostProtocol.cpp:563] Get usb core type failed! [OpenNISensorFirmware.cpp:1022] Sensor serial number:AY2B43100AL [OpenNISensorFirmware.cpp:1050] Firmware version RD3013 [OpenNISensorFirmware.cpp:1056] Device frequency 50 [OpenNIHostProtocol.cpp:936] Host Protocol sub cmd not supported! [OpenNISensorFirmware.cpp:148] OpenNI2 camera don't support Watchdog function! [OpenNIDevice.cpp:28] OpenNI device created! PID: 0x0614, SN: AY2B43100AL [DeviceManager.cpp:154] Device created successfully! Name: SV1301S_U3, PID: 0x0614, SN/ID: [Pipeline.cpp:44] Pipeline created with device: {name: SV1301S_U3, sn: AY2B43100AL}, @0x2435B3028E0 FAILED!!, Device not support depth work mode
五、一些疑问
(有些可能只是需要确认下,详细等见面时聊)
1、https://developer.orbbec.com.cn/v/forum_detail/14817。3楼
2、造成盲区条件。前方、上方、下方。
3、相机坐标系。图1是我认为的,图3是轮趣资料包中的。

——正确的是图1中坐标系。
4、深度不是该像素到相机坐标系原点(左侧红外相机中心)距离,而是到平面距离,这平面指的是两个IR相机组成的平面。
5、RGB图像中心点是对应RGB相机的中心点吗?可在rgb的x=0时,对应x大概是-20mm,似乎又是红外投影仪中心。——是。
6、深度图像中心点是对应左侧红外相机的中心点吗?——是。
7、ALIGN_D2C_SW_MODE、ALIGN_D2C_HW_MODE区别。是否都可以用ALIGN_D2C_HW_MODE。——目前都用ALIGN_D2C_HW_MODE。
8、depth_work_mode指什么?手头这个Gemini似乎不支持。下面这条语句返回false。
ob_device_is_property_supported(dev, OB_STRUCT_CURRENT_DEPTH_ALG_MODE, OB_PERMISSION_READ_WRITE, &error);
9、CameraParamList中的getCameraParam(index)可以得到OBCameraParam,ob::Pipeline中的getCameraParam()出可以得到OBCameraParam。对同一个相机,它们得到的OBCameraParam会能什么不一样吗?
建议在什么时候调用getCameraParam()?——在只深度或深度彩色同步时,pipe.start(config)后就立即调用camera_param_ = pipe.getCameraParam(),就一定会得到有效的camera_param_吗?在只彩色图像时,遇到过得到的camera_param_中字段全0,而且之后pipe.getCameraParam()一直是全0,但出来和图像是正常的。——目前用pipe.start(config)后,再调用camera_param_ = pipe.getCameraParam()。
OBCameraParam中的transform中的rot、trans指的是什么。

10、enableDeviceClockSync是做啥用的
if (!isOpenNIDevice(device_info_->pid())) { ctx_->enableDeviceClockSync(5000); }
——目前就按SDK给的代码来。
11、OrbbecSDKConfig_v1.0.xml作用。现在不提供,似乎也没啥问题。
12、手头这Gemini的y差了近2cm。
12、libOrbbecSDK.so是否含有libyuv,实现了MJPGToARGB?
[arm64-v8a] Compile++ : main <= convert_jpeg.cc [arm64-v8a] SharedLibrary : libmain.so ld: error: undefined symbol: MJPGToARGB1 >>> referenced by ColorViewer.cpp:578 (jni/../../../../launcher/orbbec\ColorViewer.cpp:578) >>> ./obj/local/arm64-v8a/objs/main/launcher/orbbec/ColorViewer.o:(ColorViewer_slice(SDL_Renderer*, gui2::ttrack&)) >>> did you mean: MJPGToARGB >>> defined in: jni/../../../../../linker/android/lib/arm64-v8a\libOrbbecSDK.so clang++: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [C:/Users/ancientcc/AppData/Local/Android/Sdk/ndk-bundle/build//../build/core/build-binary.mk:657: obj/local/arm64-v8a/libmain.so] Error 1
从这里看libOrbbecSDK.so可能含有libyuv,像实现了MJPGToARGB。
A/DEBUG: #00 pc 000000000004facc /apex/com.android.runtime/lib64/bionic/libc.so (abort+164) (BuildId: 79b24c1030e6eef06bc5ed359f9fabba) A/DEBUG: #01 pc 000000000004b8b0 /apex/com.android.runtime/lib64/bionic/libc.so (__bionic_setjmp_checksum_mismatch+20) (BuildId: 79b24c1030e6eef06bc5ed359f9fabba) A/DEBUG: #02 pc 0000000000575cc4 /data/app/~~L7PMXemmb6x0Jiu4uHhUqg==/com.kos.launcher-7u3phMSP7iT_VCFnxXv3YQ==/base.apk!libOrbbecSDK.so (libyuv::ErrorHandler(jpeg_common_struct*)+20) (BuildId: 71a1c29bdfbd0fd13af7017550d164b44565a5c6) A/DEBUG: #03 pc 00000000017355bc /data/app/~~L7PMXemmb6x0Jiu4uHhUqg==/com.kos.launcher-7u3phMSP7iT_VCFnxXv3YQ==/base.apk!libmain.so (jpeg_CreateDecompress+72) (BuildId: eff61fc7b92c3c0f3c69104344480d9b62bc0a5c) A/DEBUG: #04 pc 0000000000575c8c /data/app/~~L7PMXemmb6x0Jiu4uHhUqg==/com.kos.launcher-7u3phMSP7iT_VCFnxXv3YQ==/base.apk!libOrbbecSDK.so (libyuv::MJpegDecoder::MJpegDecoder()+180) (BuildId: 71a1c29bdfbd0fd13af7017550d164b44565a5c6) A/DEBUG: #05 pc 000000000057461c /data/app/~~L7PMXemmb6x0Jiu4uHhUqg==/com.kos.launcher-7u3phMSP7iT_VCFnxXv3YQ==/base.apk!libOrbbecSDK.so (MJPGToARGB+92) (BuildId: 71a1c29bdfbd0fd13af7017550d164b44565a5c6) A/DEBUG: #06 pc 000000000130bab4 /data/app/~~L7PMXemmb6x0Jiu4uHhUqg==/com.kos.launcher-7u3phMSP7iT_VCFnxXv3YQ==/base.apk!libmain.so (ColorViewer_slice(SDL_Renderer*, gui2::ttrack&)+2236) (BuildId: eff61fc7b92c3c0f3c69104344480d9b62bc0a5c)
launcher调用libyuv::MJPGToARGB时,总是调用到libOrbbecSDK.so中的,导致app崩溃。
13、(android)OBCameraNode内,depth时,videoFrame = depthFrame->as<ob::VideoFrame>(),得到的videoFrame是nullptr。但在ColorViewer时,得到的videoFrame不是nullptr。
14、是不是距离越近,精度越高?能出来深度值也越多?
15、只是深度时,原点取左侧IR中心,计算x、y时用深度内参、外参。是深度+RGB时,原点取RGB相机中心,计算x、y时用RGB内参、外参。
1)两突兀物体交界地方错。2)噪声。3)随机会收不到帧,给变换后,能收到。4)d2c时,两种帧width、height是不是总是一样的