unity3d - 在 HoloLens 上还是在 Daydream 之外使用 Daydream Controller?

标签 unity3d google-vr hololens daydream

daydream Controller 很棒,我们希望能够在我的 AR 应用中使用它。它通过蓝牙与 HoloLens 配对就好了,但不确定我是否可以在 Unity 中查看它。

HoloLens 和 daydream 都需要自己的 Unity 技术预览。 gvr Controller code在线,但似乎直接与 GVR C api 对话。

关于是否可以在 daydream 技术预览之外访问 Unity 中的 daydream Controller 有任何想法吗?

最佳答案

在没有 GVR 服务的情况下访问 daydream Controller 是非常可能的。事实上,我自己也在做这方面的工作,可以分享我所知道的。

获取数据

使用蓝牙 gatt,您可以查看所有可用数据并订阅您想要的 ID。我不知道您将如何在 Hololens/Unity 中具体执行此操作。基本上你想:

  • 连接设备
  • 选择服务 ( 0000fe55-0000-1000-8000-00805f9b34fb )
  • 选择特征 ( 00000001-1000-1000-8000-00805f9b34fb )
  • 请求通知 ( 00002902-0000-1000-8000-00805f9b34fb )

  • 安卓示例:

    static final UUID DAYDREAM_CUSTOM_SERVICE = UUID.fromString("0000fe55-0000-1000-8000-00805f9b34fb");
    static final UUID DAYDREAM_CHARACTERISTIC = UUID.fromString("00000001-1000-1000-8000-00805f9b34fb");
    static final UUID CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
    ...
    BluetoothGattService service = gatt.getService(DAYDREAM_CUSTOM_SERVICE);
    BluetoothGattCharacteristic characteristic = service.getCharacteristic(DAYDREAM_CHARACTERISTIC);
    gatt.setCharacteristicNotification(characteristic, true);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CHARACTERISTIC_UPDATE_NOTIFICATION_DESCRIPTOR_UUID);
    descriptor.setValue( BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    gatt.writeDescriptor(descriptor);
    

    我建议查看蓝牙 Gatt 以了解有关服务和特性的更多信息。在开始编写代码之前,我还使用 Playstore 上的 BLE Scanner 应用程序查看了大量此类信息。

    解析数据

    该设备提供 20 个字节的数据以供使用。它由时间、方向、加速度、原始陀螺仪、触摸位置和按钮标志组成。

    示例(平放在 table 上):
    5BEBFFB825FDB000041000B00000000000000000
    63EFFFB825FDB000041000B00000000000000008
    6C73FFB825FDB000041000B00000000000000038
    

    示例(使用触摸板):
    480BFE87EB00E801841000B00000000191FBA008
    4F8FFE47EB00E800441000B0000003FEB1FBA038
    5893FE27EB00EFFF041000B0000003FF51FBA000
    

    字节定义如下:
    Bytes:
    
      - 1: TTTT TTTT * T for time, loops
      - 2: TNNN NNKK * N is sequence number
      - 3: KKKK KKKK * IJK is orientation
      - 4: KKKI IIII
      - 5: IIII IIII
      - 6: JJJJ JJJJ
      - 7: JJJJ JOOO * MNO is acceleration
      - 8: OOOO OOOO
      - 9: OONN NNNN
      -10: NNNN NNNM
      -11: MMMM MMMM
      -12: MMMM CCCC * CDE for raw gyro
      -13: CCCC CCCC
      -14: CDDD DDDD
      -15: DDDD DDEE
      -16: EEEE EEEE
      -17: EEEX XXXX * All the X is the X touch position (8 bits)
      -18: XXXY YYYY * Y the Y touch position (8 bits)
      -19: YYYB BBBB * B the buttons (5 bits | [+][-][App][Home][Click])
      -20: Values vary
    

    有了这个,我的触摸板和按钮可以与我可以为其构建应用程序的任何蓝牙设备一起使用。此外,您需要添加返回功能以重置设备位置、控制音频等。

    在 Android 上使用此定义:

    static final int CLICK_BTN = 0x1;
    static final int HOME_BTN = 0x2;
    static final int APP_BTN = 0x4;
    static final int VOL_DOWN_BTN = 0x8;
    static final int VOL_UP_BTN = 0x10;
    float xTouch=0, yTouch=0;
    ...
    final boolean isClickDown = (data[18] & CLICK_BTN) > 0;
    final boolean isHomeDown = (data[18] & HOME_BTN) > 0;
    final boolean isAppDown = (data[18] & APP_BTN) > 0;
    final boolean isVolMinusDown = (data[18] & VOL_DOWN_BTN) > 0;
    final boolean isVolPlusDown = (data[18] & VOL_UP_BTN) > 0;
    
    final int time = ((data[0] & 0xFF) << 1 | (data[1] & 0x80) >> 7 );
    
    final int seq = (data[1] & 0x7C) >> 2;
    
    int xOri = (data[1] & 0x03) << 11 | (data[2] & 0xFF) << 3 | (data[3] & 0xE0) >> 5;
    xOri = (xOri << 19) >> 19;
    
    int yOri = (data[3] & 0x1F) << 8 | (data[4] & 0xFF);
    yOri = (yOri << 19) >> 19;
    
    int zOri = (data[5] & 0xFF) << 5 | (data[6] & 0xF8) >> 3;
    zOri = (zOri << 19) >> 19;
    
    int xAcc = (data[6] & 0x07) << 10 | (data[7] & 0xFF) << 2 | (data[8] & 0xC0) >> 6;
    xAcc = (xAcc << 19) >> 19;
    
    int yAcc = (data[8] & 0x3F) << 7 | (data[9] & 0xFE) >>> 1;
    yAcc = (yAcc << 19) >> 19;
    
    int zAcc = (data[9] & 0x01) << 12 | (data[10] & 0xFF) << 4 | (data[11] & 0xF0) >> 4;
    zAcc = (zAcc << 19) >> 19;
    
    int xGyro = ((data[11] & 0x0F) << 9 | (data[12] & 0xFF) << 1 | (data[13] & 0x80) >> 7);
    xGyro = (xGyro << 19) >> 19;
    
    int yGyro = ((data[13] & 0x7F) << 6 | (data[14] & 0xFC) >> 2 );
    yGyro = (yGyro << 19) >> 19;
    
    int zGyro = ((data[14] & 0x03) << 11 | (data[15] & 0xFF) << 3 | (data[16] & 0xE0) >> 5);
    zGyro = (zGyro << 19) >> 19;
    
    xTouch = ((data[16] & 0x1F) << 3 | (data[17] & 0xE0) >> 5) / 255.0f;
    yTouch = ((data[17] & 0x1F) << 3 | (data[18] & 0xE0) >> 5) / 255.0f;
    

    这可以优化,但它分配除最后一个字节之外的所有位。代码value = (value << 19) >> 19也可以value = (value >> 12) == 0 ? value : ~0x1FFF | value .它只是将有符号位扩展为 32 位有符号整数。

    我希望这会有所帮助,并期待更多的答案。

    -- 更新 2 --

    查看 gvr 代码后,我发现我之前的假设存在一些问题。它实际上是方向/加速度/陀螺仪。此外,序列多 1 位,时间少 1 位。我已经更新了字节定义和 android 示例。

    此外,X、Y、Z 值需要缩放为浮点数。对于 Unity,您可以将整数放入 Vector3s 中,然后使用以下内容。对于 Unity,我还否定了 oriVector 中的 x 和 y。

    Vector3 oriVector = new Vector3 (-xOri, -yOri, zOri);
    ...
    oriVector *= (2 * Mathf.PI / 4095.0);
    accVector *= (8 * 9.8 / 4095.0);
    gyroVector *= (2048 / 180 * Mathf.PI / 4095.0);
    

    然后要获得旋转,您只需要 oriVector。这实际上是一个轴角存储为:单位向量 * 角度。

    public Quaternion orientation = Quaternion.identity;
    private Quaternion controllerPoseInSensorSpace = Quaternion.identity;
    private Quaternion startFromSensorTransformation = Quaternion.identity;
    ...
    // do this bit after getting the data and scaling it
    float sqrMagnitude = oriVector.sqrMagnitude;
    if (sqrMagnitude > 0) {
        // extract radian angle
        float w = Mathf.Sqrt (sqrMagnitude);
        // normalize vector
        oriVector /= w;
        // set orientation space
        setOrientationInSensorSpace (w,oriVector);
    }
    ...
    // then assign to a Transform
    controller.localRotation = this.orientation;
    ...
    // sets orientation with rotation offset
    void setOrientationInSensorSpace(float angle, Vector3 axis) {
        // set orientation space
        this.controllerPoseInSensorSpace = Quaternion.AngleAxis(angle*Mathf.Rad2Deg,axis);
        // rotate based on centered offset
        this.orientation = this.startFromSensorTransformation * this.controllerPoseInSensorSpace;
    }
    ...
    // after holding home for 600 milliseconds
    private void setStartFromSensorTransformation() {
        Vector3 angles = this.controllerPoseInSensorSpace.eulerAngles;
        // reset rotation on Y
        this.startFromSensorTransformation.Set(0,Mathf.Sin(-angles.y * Mathf.Deg2Rad / 2f), 0, Mathf.Cos(angles.y * Mathf.Deg2Rad / 2f));
        // could also reset all, easier to work with
        //this.startFromSensorTransformation = Quaternion.Inverse (this.controllerPoseInSensorSpace);
    }
    

    这就是与使用普通蓝牙设备实现白日梦相关的一切。我还在 Unity3D 中使用了上述 C# 代码。

    -- 更新 1 --

    添加了更完整的字节定义。之前缺失的值是陀螺仪、磁力计和加速度数据。它们每个都有三个 13 位有符号整数。时间位中似乎还有一个序列号。

    往前走

    为了在其他平台上使用设备数据,您需要将数据放入用于 9DoF/IMU 设备的类似方程中。我不知道如何解决这个问题。

    最后一个字节

    这可能是为标志保留的,我不确定其含义,但我有一些发现要列出。版本号是 Controller 的固件版本。
    1.0.10 (out of the box): 0xF0/0xF8
    1.0.10 (previously used with gvr): 0x00/0x08/0x38/0x51
    1.0.15: 0x00/0x70
    

    关于unity3d - 在 HoloLens 上还是在 Daydream 之外使用 Daydream Controller?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40730809/

    相关文章:

    c# - 引用 Visual Studio 2015 UNITY 项目中的 System.Data

    ios - Unity和Facebook 6.0:解决主机超时:Integrated-plugin-canvas-rsrc.fbsbx.com

    c# - 调用 ReadPixels 以从系统帧缓冲区读取像素,而不是在绘图帧内。 UnityEngine.Texture2D :ReadPixels

    android - 如何在 VrPanoramaView 中添加如图所示的导航 float 按钮?

    ios - ARKit 图像检测 卡住相机

    oculus - 适用于三星 Gear VR 的 Google VR SDK?

    c# - 如何使对象/层忽略附近的裁剪平面?

    hololens - 如何在hololens中为光标添加点击 Action

    Azure 空间 anchor - Hololens - 不起作用

    ios - Unity 中的 Azure 空间 anchor 子对象