Использовать Daydream Controller на HoloLens или вне Daydream?
Контроллер daydream великолепен, и мы хотим использовать его в моем приложении AR. Он подключается через Bluetooth к HoloLens просто отлично, но не уверен, смогу ли я просмотреть его в Unity.
И HoloLens, и Daydream требуют собственных технических предварительных обзоров Unity. Код контроллера gvr находится в сети, но, кажется, говорит напрямую с GVR C api.
Есть мысли о том, возможен ли доступ к контроллеру daydream в Unity за пределами предварительного просмотра daydream tech?
1 ответ
Очень возможно получить доступ к контроллеру мечтаний без услуг GVR. Я сам над этим работаю и могу поделиться тем, что знаю.
Получение данных
Используя bluetooth gatt, вы можете просмотреть все доступные данные и подписаться на желаемый идентификатор. Я не знаю, как бы вы сделали это в рамках Hololens/Unity. В основном вы хотите:
- Подключиться к устройству
- Выберите услугу (
0000fe55-0000-1000-8000-00805f9b34fb
) - Выберите характеристику (
00000001-1000-1000-8000-00805f9b34fb
) - Запросить уведомления об этом (
00002902-0000-1000-8000-00805f9b34fb
)
Пример Android:
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);
Я предлагаю поискать Bluetooth Gatt, чтобы понять больше об услугах и характеристиках. Я также использовал приложение BLE Scanner в PlayStore, чтобы просмотреть большую часть этой информации, прежде чем начинать с кода.
Разбор данных
Устройство выдает 20 байтов данных для работы. Он состоит из времени, ориентации, ускорения, сырого гироскопа, положения касания и флагов кнопок.
Пример (лежа на столе):
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
Благодаря этому у меня есть сенсорная панель и кнопки, способные работать с любым устройством Bluetooth, для которого я могу создавать приложения. Кроме того, вам нужно будет добавить обратно функциональность для сброса положения устройства, управления аудио и т. Д.
Используя это определение на 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 -битного со знаком int.
Я надеюсь, что это поможет и с нетерпением жду дополнительных ответов.
- Обновление 2 -
Посмотрев код gvr, я обнаружил, что у меня возникли некоторые проблемы с моими предыдущими предположениями. Это на самом деле ориентация / ускорение / гироскоп. Также был еще 1 бит для последовательности и 1 меньше для времени. Я обновил определение байта и пример Android.
Кроме того, значения X,Y,Z необходимо масштабировать до значений с плавающей точкой. Для Unity вы можете поместить их в Vector3s, а затем использовать следующее. Я также отрицал x и y в oriVector, для Unity.
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);
}
Это все, что связано с работой мечты с обычными устройствами Bluetooth. Я также использовал вышеупомянутый код C# в Unity3D.
- Обновление 1 -
Добавлено более полное определение байта. Значения, отсутствовавшие ранее, были данными гироскопа, магнитометра и ускорения. Каждый из них имеет три 13-битных подписанных целых. Также, кажется, есть порядковый номер, связанный с битами времени.
Идти вперед
Чтобы использовать данные устройства с другими платформами, вам необходимо поместить данные с помощью аналогичных уравнений, используемых для устройств 9DoF/IMU. Я не знаю точно, как решить эту проблему.
Последний байт
Это, вероятно, зарезервировано для флагов, и я не уверен в значении, но у меня есть некоторые выводы, чтобы перечислить. Номер версии - версия прошивки контроллера.
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