Ros

Gemini(奥比中光深度相机)

  • 在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 相机坐标系

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

图2 一次camera到base_footprint转换过程

图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是轮趣资料包中的。

图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指的是什么。

OBCameraParam

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是不是总是一样的

全部评论: 0

    写评论: