Облачные якоря ARCore с постоянными облачными якорями

1. Обзор

ARCore — это платформа для создания приложений дополненной реальности на мобильных устройствах. API Cloud Anchors позволяет создавать приложения дополненной реальности, использующие общую систему координат, что дает возможность нескольким пользователям размещать виртуальный контент в одном и том же месте реального мира.

Этот практический урок познакомит вас с API Cloud Anchors. Вы возьмете существующее приложение ARCore, модифицируете его для использования Cloud Anchors и создадите общий интерфейс дополненной реальности.

ARCore Anchors и Persistent Cloud Anchors

В основе ARCore лежит концепция «якоря» (Anchor ), описывающего фиксированное положение в реальном мире. ARCore автоматически корректирует значение положения якоря по мере улучшения отслеживания его движения с течением времени.

Облачные якоря — это якоря, размещенные в облаке. Они могут использоваться несколькими пользователями для установления единой системы координат для всех пользователей и их устройств.

Ведение программы "Hosting an Anchor"

Когда размещается якорная ссылка, происходят следующие действия:

  1. Положение опорной точки относительно окружающего мира загружается в облако, и получается идентификатор опорной точки в облаке (Cloud Anchor ID).
    Идентификатор облачного якоря — это строка, которую необходимо отправить любому, кто хочет использовать этот якорь.
  2. На серверы Google загружается набор данных, содержащий визуальные данные для якоря.
    Этот набор данных содержит визуальные данные, недавно полученные устройством. Небольшое перемещение устройства для захвата области вокруг точки привязки с разных ракурсов перед размещением на сервере позволит улучшить локализацию.

Передача идентификаторов облачных якорей

В этом практическом занятии вы будете передавать идентификаторы Cloud Anchor с помощью Firebase . Вы можете свободно делиться идентификаторами Cloud Anchor и другими способами.

Разрешение якоря

Вы можете использовать API Cloud Anchor для определения местоположения якоря по его идентификатору Cloud Anchor ID. Это создаст новый якорь в том же физическом местоположении, что и исходный размещенный якорь. При определении местоположения устройство должно ориентироваться на ту же физическую среду, что и исходный размещенный якорь.

Постоянные облачные якоря

До версии 1.20 облачные якоря могли быть разрешены только в течение 24 часов после их размещения. С помощью API постоянных облачных якорей вы можете создать облачный якорь, который может быть разрешен в течение от 1 до 365 дней после создания.

Что вы построите

В этом практическом занятии вы будете развивать уже существующее приложение на основе ARCore. К концу занятия ваше приложение будет:

  • Необходимо иметь возможность размещать постоянные облачные якоря и получать идентификаторы облачных якорей.
  • Сохраняйте идентификаторы облачных привязок на устройстве для удобного доступа с помощью Android SharedPreferences .
  • Используйте сохраненные идентификаторы облачных якорей для разрешения ранее размещенных якорей. Это позволяет нам легко имитировать работу нескольких пользователей на одном устройстве в рамках данного практического занятия.
  • Передайте идентификаторы Cloud Anchor другому устройству, на котором запущено то же приложение, чтобы несколько пользователей видели статую Android в одном и том же положении.

Статуя андроида отображается в точке, обозначенной облачным якорем:

Что вы узнаете

  • Как размещать якоря с помощью SDK ARCore и получать идентификатор облачного якоря.
  • Как использовать идентификаторы облачных якорей для разрешения якорей.
  • Как хранить и обмениваться идентификаторами Cloud Anchor между различными сеансами дополненной реальности на одном или разных устройствах.

Что вам понадобится

2. Настройте среду разработки.

Настройка машины разработки

Подключите устройство ARCore к компьютеру с помощью USB-кабеля. Убедитесь, что ваше устройство поддерживает отладку по USB .

Откройте терминал и выполните команду adb devices , как показано ниже:

adb devices

List of devices attached
<DEVICE_SERIAL_NUMBER>    device

<DEVICE_SERIAL_NUMBER> — это строка, уникальная для вашего устройства. Убедитесь, что вы видите ровно одно устройство, прежде чем продолжить.

Загрузка и установка кода

Вы можете либо клонировать репозиторий:

git clone https://github.com/googlecodelabs/arcore-cloud-anchors.git

Или скачайте ZIP-файл и распакуйте его:

Запустите Android Studio. Нажмите « Открыть существующий проект Android Studio» . Затем перейдите в каталог, куда вы распаковали загруженный выше zip-файл, и дважды щелкните по каталогу arcore-cloud-anchors .

Это один проект Gradle с несколькими модулями. Если панель «Проекты» в левом верхнем углу Android Studio еще не отображается, выберите «Проекты» в выпадающем меню. В результате должно получиться примерно следующее:

52282f0415fdbdcb.png

В основном вы будете работать в work модуле. Другие модули включают модуль helpers , содержащий набор полезных классов-оберток, которые вы будете использовать. Также имеются готовые решения для каждой части практического занятия. За исключением модуля helpers , каждый модуль представляет собой собираемое приложение.

Если вы видите диалоговое окно с рекомендацией обновить плагин Android Gradle, нажмите « Больше не напоминать мне об этом проекте» :

31a93c7e9cc58b53.png

Нажмите «Выполнить» > «Выполнить...» > «Работать» . В появившемся диалоговом окне «Выбор целевого устройства развертывания» ваше устройство должно отображаться в списке подключенных устройств . Выберите свое устройство и нажмите «ОК» . Android Studio соберет исходное приложение и запустит его на вашем устройстве.

При первом запуске приложения оно запросит разрешение на CAMERA . Нажмите «РАЗРЕШИТЬ» , чтобы продолжить.

f7ea81f71a4b969e.png

Как пользоваться приложением

  1. Подвигайте устройство, чтобы помочь приложению найти плоскость . Найденная плоскость будет отображена в виде пунктирной поверхности.
  2. Коснитесь любой точки на плоскости, чтобы установить якорь . В месте установки якоря будет нарисована фигурка Android. Это приложение позволяет устанавливать только один якорь за раз.
  3. Перемещайте устройство . Изображение должно оставаться на одном месте, даже если устройство перемещается.
  4. Нажмите кнопку CLEAR, чтобы удалить анкер . Это позволит вам установить другой анкер.

В настоящий момент это приложение использует отслеживание движения, предоставляемое ARCore, только для фиксации точки привязки в течение одного запуска. Если вы решите закрыть, завершить и перезапустить приложение, ранее установленная точка привязки и вся связанная с ней информация, включая её положение, будут потеряны.

В следующих разделах вы познакомитесь с возможностями этого приложения и узнаете, как можно совместно использовать якоря в рамках сессий дополненной реальности.

3. Пригласите ведущего

В этом разделе вы измените work проект, чтобы разместить в нем якорь. Прежде чем писать код, вам потребуется внести несколько изменений в конфигурацию приложения.

Объявить права доступа к Интернету

Поскольку для работы облачных якорей требуется взаимодействие со службой ARCore Cloud Anchor API, вашему приложению необходимо иметь разрешение на доступ к интернету.

В файл AndroidManifest.xml добавьте следующую строку сразу после объявления разрешения android.permission.CAMERA :

<!-- Find this line... -->
<uses-permission android:name="android.permission.CAMERA"/>

<!-- Add the line right below -->
<uses-permission android:name="android.permission.INTERNET"/>

Включите API ARCore

  1. Перейдите на страницу сервиса ARCore API .
  2. В списке проектов выберите проект или создайте новый.
  3. Нажмите «Включить» .

Настройка бесключевой аутентификации

Для использования постоянных облачных якорей вам потребуется использовать бесключевую аутентификацию для аутентификации в API ARCore.

  1. Перейдите в консоль Google Cloud Platform .
  2. Выберите проект из списка.
  3. Если страница «API и сервисы» еще не открыта, откройте меню в левой части консоли и выберите «API и сервисы» .
  4. Слева нажмите «Учетные данные» .
  5. Нажмите «Создать учетные данные» , затем выберите «Идентификатор клиента OAuth» .
  6. Введите следующие значения:
    • Тип приложения : Android
    • Название пакета : com.google.ar.core.codelab.cloudanchor
  7. Получите отпечаток сертификата отладочной подписи:
    1. В проекте Android Studio откройте панель инструментов Gradle .
    2. В разделе cloud-anchors > work > Tasks > android запустите задачу signingReport .
    3. Скопируйте отпечаток SHA-1 в поле " Отпечаток сертификата SHA-1" в Google Cloud.

Настройка ARCore

Далее вам потребуется изменить приложение, чтобы при нажатии пользователем отображалась привязка к определенному элементу, а не обычная. Для этого необходимо настроить сессию ARCore, включив функцию Cloud Anchors.

В файл CloudAnchorFragment.java добавьте следующий код:

// Find this line...
session = new Session(requireActivity());

// Add these lines right below:
// Configure the session.
Config config = new Config(session);
config.setCloudAnchorMode(CloudAnchorMode.ENABLED);
session.configure(config);

Прежде чем продолжить, соберите и запустите приложение. Убедитесь, что вы собрали только work модуль. Ваше приложение должно успешно собраться и работать так же, как и раньше.

Пригласить ведущего

Пришло время разместить якорь, который будет загружен в API ARCore.

Добавьте в класс CloudAnchorFragment следующее новое поле:

// Find this line...
private Anchor currentAnchor = null;

// Add these lines right below.
@Nullable
private Future future = null;

Не забудьте добавить импорт для com.google.ar.core.Future .

Измените метод onClearButtonPressed следующим образом:

private void onClearButtonPressed() {
  // Clear the anchor from the scene.
  if (currentAnchor != null) {
    currentAnchor.detach();
    currentAnchor = null;
  }

  // The next part is the new addition.
  // Cancel any ongoing asynchronous operations.
  if (future != null) {
    future.cancel();
    future = null;
  }
}

Далее добавьте следующий метод в класс CloudAnchorFragment :

private void onHostComplete(String cloudAnchorId, CloudAnchorState cloudState) {
  if (cloudState == CloudAnchorState.SUCCESS) {
    messageSnackbarHelper.showMessage(getActivity(), "Cloud Anchor Hosted. ID: " + cloudAnchorId);
  } else {
    messageSnackbarHelper.showMessage(getActivity(), "Error while hosting: " + cloudState.toString());
  }
}

Найдите метод handleTap в классе CloudAnchorFragment и добавьте следующие строки:

// Find this line...
currentAnchor = hit.createAnchor();

// Add these lines right below:
messageSnackbarHelper.showMessage(getActivity(), "Now hosting anchor...");
future = session.hostCloudAnchorAsync(currentAnchor, 300, this::onHostComplete);

Запустите приложение из Android Studio еще раз. После размещения ссылки вы должны увидеть сообщение « Сейчас размещается якорь... ». После успешного завершения размещения вы должны увидеть другое сообщение. Если вы видите сообщение « Ошибка размещения якоря: ERROR_NOT_AUTHORIZED », убедитесь, что ваш OAuth-клиент настроен правильно.

Любой, кто знает идентификатор точки привязки и находится в том же физическом пространстве, что и точка привязки, может использовать этот идентификатор для создания точки привязки в точно таком же положении (позиции и ориентации) относительно окружающей среды.

Однако идентификатор привязки длинный, и другому пользователю непросто ввести его вручную. В следующих разделах вы научитесь сохранять идентификаторы привязки в облаке таким образом, чтобы их было легко получить, что позволит определять привязку на том же или другом устройстве.

4. Сохранение идентификаторов и разрешение якорей.

В этой части вы присвоите короткие коды длинным идентификаторам Cloud Anchor ID, чтобы упростить их ввод вручную другим пользователям. Вы будете использовать API Shared Preferences для хранения идентификаторов Cloud Anchor ID в виде значений в таблице «ключ-значение». Эта таблица сохранится даже после закрытия и перезапуска приложения.

Вспомогательный класс StorageManager уже предоставлен. Это обертка над API SharedPreferences , содержащая методы для генерации новых уникальных коротких кодов, а также для чтения/записи идентификаторов Cloud Anchor.

Используйте StorageManager

Измените CloudAnchorFragment , чтобы он использовал StorageManager для хранения идентификаторов Cloud Anchor с короткими кодами, что позволит легко их получать.

Создайте следующее новое поле в CloudAnchorFragment :

// Find this line...
private TapHelper tapHelper;

// And add the storageManager.
private final StorageManager storageManager = new StorageManager();

Затем измените метод onHostComplete :

private void onHostComplete(String cloudAnchorId, CloudAnchorState cloudState) {
  if (cloudState == CloudAnchorState.SUCCESS) {
    int shortCode = storageManager.nextShortCode(getActivity());
    storageManager.storeUsingShortCode(getActivity(), shortCode, anchor.getCloudAnchorId());
    messageSnackbarHelper.showMessage(
        getActivity(), "Cloud Anchor Hosted. Short code: " + shortCode);
  } else {
    messageSnackbarHelper.showMessage(getActivity(), "Error while hosting: " + cloudState.toString());
  }
}

Теперь соберите и запустите приложение из Android Studio. При создании и размещении привязки вместо длинных идентификаторов Cloud Anchor вы должны увидеть короткие коды.

Сразу после установки якоря

После непродолжительного ожидания

Обратите внимание, что в настоящее время короткие коды, генерируемые StorageManager , всегда присваиваются в порядке возрастания.

Далее вы добавите несколько элементов пользовательского интерфейса, которые позволят вам вводить короткие коды и воссоздавать ссылки.

Добавить кнопку «Решить»

Рядом с кнопкой «ОЧИСТИТЬ» вы добавите еще одну кнопку. Это будет кнопка «РАЗРЕШИТЬ» . Нажатие кнопки «РАЗРЕШИТЬ» откроет диалоговое окно, в котором пользователю будет предложено ввести короткий код. Короткий код используется для получения идентификатора облачного якоря из StorageManager и разрешения якоря.

Чтобы добавить кнопку, вам потребуется изменить файл res/layout/cloud_anchor_fragment.xml . В Android Studio дважды щелкните по этому файлу, затем перейдите на вкладку «Текст» внизу, чтобы отобразить исходный XML-код. Внесите следующие изменения:

<!-- Find this element. -->
<Button
    android:text="CLEAR"
    android:id="@+id/clear_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

<!-- Add this element right below. -->
<Button
    android:text="RESOLVE"
    android:id="@+id/resolve_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

Теперь добавьте новое поле в CloudAnchorFragment :

private Button resolveButton;

Добавить новый метод:

private void onResolveButtonPressed() {
  ResolveDialogFragment dialog = new ResolveDialogFragment();
  dialog.show(getFragmentMagetActivity().getSupportFragmentManagernager(), "Resolve");
}

Инициализируйте resolveButton в методе onCreateView следующим образом:

// Find these lines...
Button clearButton = rootView.findViewById(R.id.clear_button);
clearButton.setOnClickListener(v -> onClearButtonPressed());

// Add these lines right below.
resolveButton = rootView.findViewById(R.id.resolve_button);
resolveButton.setOnClickListener(v -> onResolveButtonPressed());

Найдите метод handleTap и внесите в него изменения:

private void handleTap(Frame frame, Camera camera) {
  // ...

  // Find this line.
  currentAnchor = hit.createAnchor();

  // Add this line right below.
  getActivity().runOnUiThread(() -> resolveButton.setEnabled(false)); 
}

Добавьте строку в метод onClearButtonPressed :

private void onClearButtonPressed() {
  // Clear the anchor from the scene.
    if (currentAnchor != null) {
      currentAnchor.detach();
      currentAnchor = null;
    }

    // Cancel any ongoing async operations.
    if (future != null) {
      future.cancel();
      future = null;
    }

  // The next line is the new addition.
  resolveButton.setEnabled(true);
}

Соберите и запустите приложение из Android Studio. Рядом с кнопкой «Очистить» должна появиться кнопка «Решить» . Нажатие кнопки «Решить» должно привести к появлению диалогового окна, как показано ниже.

Кнопка «РЕШИТЬ» теперь видна.

Нажатие кнопки приводит к появлению этого диалогового окна.

Нажатие на самолет и размещение якоря должно отключить кнопку RESOLVE , но нажатие на кнопку CLEAR должно снова ее включить. Это сделано намеренно, чтобы в сцене одновременно находился только один якорь.

Диалоговое окно "Разрешить якорь" ничего не делает, но сейчас вы это измените.

Решите якоря

Добавьте следующие методы в класс CloudAnchorFragment :

private void onShortCodeEntered(int shortCode) {
  String cloudAnchorId = storageManager.getCloudAnchorId(getActivity(), shortCode);
  if (cloudAnchorId == null || cloudAnchorId.isEmpty()) {
    messageSnackbarHelper.showMessage(
        getActivity(),
        "A Cloud Anchor ID for the short code " + shortCode + " was not found.");
    return;
  }
  resolveButton.setEnabled(false);
  future = session.resolveCloudAnchorAsync(
      cloudAnchorId, (anchor, cloudState) -> onResolveComplete(anchor, cloudState, shortCode));
}

private void onResolveComplete(Anchor anchor, CloudAnchorState cloudState, int shortCode) {
  if (cloudState == CloudAnchorState.SUCCESS) {
    messageSnackbarHelper.showMessage(getActivity(), "Cloud Anchor Resolved. Short code: " + shortCode);
    currentAnchor = anchor;
  } else {
    messageSnackbarHelper.showMessage(
        getActivity(),
        "Error while resolving anchor with short code "
            + shortCode
            + ". Error: "
            + cloudState.toString());
    resolveButton.setEnabled(true);
  }
}

Затем измените метод onResolveButtonPressed :

private void onResolveButtonPressed() {
  ResolveDialogFragment dialog = ResolveDialogFragment.createWithOkListener(
      this::onShortCodeEntered);
  dialog.show(getActivity().getSupportFragmentManager(), "Resolve");
}

Соберите и запустите приложение из Android Studio, выполнив следующие шаги:

  1. Создайте точку привязки на плоскости и дождитесь, пока эта точка привязки будет размещена.
    Запомните короткий код.
  2. Нажмите кнопку ОЧИСТИТЬ , чтобы удалить привязку.
  3. Нажмите кнопку «РЕШИТЬ» . Введите короткий код из шага 1.
  4. Вы должны увидеть якорь в том же положении относительно окружающей среды, в котором вы его изначально разместили.
  5. Закройте и завершите работу приложения, а затем откройте его снова.
  6. Повторите шаги (3) и (4). Вы должны увидеть новый якорь, снова в том же положении.

Ввод короткого кода

Якорь успешно решен.

5. Совместное использование между устройствами

Вы уже видели, как можно сохранить идентификатор облачного якоря (Cloud Anchor ID) в локальном хранилище вашего устройства и позже извлечь его для создания аналогичного якоря. Но весь потенциал облачных якорей раскрывается только тогда, когда вы можете обмениваться идентификаторами облачных якорей между разными устройствами.

Способ обмена идентификаторами Cloud Anchor ID в вашем приложении зависит от вас. Для передачи строки с одного устройства на другое можно использовать что угодно. В этом практическом задании вы будете использовать базу данных Firebase Realtime Database для передачи идентификаторов Cloud Anchor ID между экземплярами приложения.

Настройка Firebase

Для использования этого приложения вам необходимо настроить базу данных Firebase Realtime Database с помощью вашей учетной записи Google. Это легко сделать с помощью Firebase Assistant в Android Studio.

В Android Studio нажмите Инструменты > Firebase . В появившейся панели помощника нажмите База данных реального времени , затем нажмите Сохранить и получить данные :

68e927cbf324a3b2.png

Нажмите кнопку «Подключиться к Firebase» , чтобы подключить свой проект Android Studio к новому или существующему проекту Firebase.

63f3b1ffd6bd263e.png

Вам будет предложено выбрать модуль. Выберите work модуль:

be737c689ad6dd78.png

Отобразится диалоговое окно «Начало подключения». Это может занять некоторое время.

b48626f8672551ee.png

Войдите в систему с помощью своей учетной записи Google и пройдите веб-процесс создания проекта Firebase для вашего приложения, пока не вернетесь в Android Studio.

Далее в панели «Помощник» нажмите «Добавить базу данных реального времени в ваше приложение» :

68e0843fa2531c4c.png

В появившемся диалоговом окне выберите пункт «Работа» в раскрывающемся списке «Целевой модуль» , затем нажмите «Принять изменения».

155fd89533c02671.png

Это позволит:

  1. Добавьте файл google-services.json в свою work директорию.
  2. Добавьте пару строк в файл build.gradle , расположенный в той же директории.
  3. Соберите и запустите приложение (возможно, вы увидите ошибку разрешения, связанную с номером версии базы данных Firebase).

В файле build.gradle work модуля найдите и удалите следующую строку ( xxxx — это заполнитель для номера последней версии):

dependencies {
  ...
  implementation 'com.google.firebase:firebase-database:xxxx'

Далее ознакомьтесь (но пока не следуйте) инструкциям, ссылка на которые приведена на странице настройки правил публичного доступа , чтобы настроить вашу базу данных Firebase Realtime Database так, чтобы она была доступна для записи всем пользователям. Это поможет упростить тестирование в этом практическом задании:

666ebefd39019c05.png

В консоли Firebase выберите проект, к которому вы подключили свой проект Android Studio, затем выберите BUILD > Realtime Database .

Расположение базы данных Firebase Realtime Database

Нажмите «Создать базу данных» , чтобы настроить и подготовить базу данных реального времени :

Создать базу данных

Выберите любое местоположение базы данных.

На следующем шаге выберите правила безопасности тестового режима и нажмите «Включить» :

Безопасность баз данных

Ваше приложение теперь настроено на использование базы данных Firebase.

Использование FirebaseManager

Теперь замените StorageManager на FirebaseManager .

В Android Studio найдите класс CloudAnchorFragment в work директории. Замените StorageManager на FirebaseManager :

// Find this line.
private final StorageManager storageManager = new StorageManager();

// And replace it with this line.
private FirebaseManager firebaseManager;

Инициализируйте firebaseManager в методе onAttach :

public void onAttach(@NonNull Context context) {
  super.onAttach(context);
  tapHelper = new TapHelper(context);
  trackingStateHelper = new TrackingStateHelper(requireActivity());

  // The next line is the new addition.
  firebaseManager = new FirebaseManager(context);
}

Измените метод onShortCodeEntered следующим образом:

private void onShortCodeEntered(int shortCode) {
  firebaseManager.getCloudAnchorId(shortCode, cloudAnchorId -> {
    if (cloudAnchorId == null || cloudAnchorId.isEmpty()) {
      messageSnackbarHelper.showMessage(
          getActivity(),
          "A Cloud Anchor ID for the short code " + shortCode + " was not found.");
      return;
    }
    resolveButton.setEnabled(false);
    future = session.resolveCloudAnchorAsync(
        cloudAnchorId, (anchor, cloudState) -> onResolveComplete(anchor, cloudState, shortCode));
  });
}

Затем измените метод onHostComplete следующим образом:

private void onHostComplete(String cloudAnchorId, CloudAnchorState cloudState) {
  if (cloudState == CloudAnchorState.SUCCESS) {
    firebaseManager.nextShortCode(shortCode -> {
      if (shortCode != null) {
        firebaseManager.storeUsingShortCode(shortCode, cloudAnchorId);
        messageSnackbarHelper.showMessage(getActivity(), "Cloud Anchor Hosted. Short code: " + shortCode);
      } else {
        // Firebase could not provide a short code.
        messageSnackbarHelper.showMessage(getActivity(), "Cloud Anchor Hosted, but could not "
            + "get a short code from Firebase.");
      }
    });
  } else {
    messageSnackbarHelper.showMessage(getActivity(), "Error while hosting: " + cloudState.toString());
  }
}

Соберите и запустите приложение . Вы должны увидеть тот же пользовательский интерфейс, что и в предыдущем разделе, за исключением того, что теперь для хранения идентификаторов Cloud Anchor и коротких кодов используется онлайн-база данных Firebase вместо локального хранилища устройства.

Многопользовательское тестирование

Для проверки работы в многопользовательском режиме используйте два разных телефона:

  1. Установите приложение на два устройства.
  2. Используйте одно устройство для размещения якорной ссылки и генерации короткого кода.
  3. Используйте другое устройство для определения точки привязки с помощью этого короткого кода.

Вы сможете размещать ссылки на одном устройстве, получать короткий код и использовать этот короткий код на другом устройстве, чтобы увидеть ссылку в том же месте!

6. Подведение итогов

Поздравляем! Вы дошли до конца этого практического занятия!

Что мы рассмотрели

  • Как размещать якоря с помощью SDK ARCore и получать идентификатор облачного якоря.
  • Как использовать идентификаторы облачных якорей для разрешения якорей.
  • Как хранить и совместно использовать идентификаторы облачных якорей между различными сеансами дополненной реальности на одном или разных устройствах.

Узнать больше