Compartir a través de


Creación y localización de anclajes mediante Azure Spatial Anchors en Java

Azure Spatial Anchors permite compartir delimitadores entre diferentes dispositivos de todo el mundo. Admite varios entornos de desarrollo diferentes. En este artículo, se examinará en profundidad cómo usar el SDK de Azure Spatial Anchors, en Java, para:

  • Configurar y administrar correctamente una sesión de Azure Spatial Anchors.
  • Crear y establecer propiedades en los anclajes locales.
  • Cargarlos en la nube.
  • Localizar y eliminar los anclajes espaciales en la nube.

Requisitos previos

Para completar esta guía, asegúrese de tener:

Cross Platform

Inicialización de la sesión

El punto de entrada principal del SDK es la clase que representa la sesión. Normalmente declarará un campo en la clase que administra la vista y la sesión de AR nativa.

Obtenga más información sobre la clase CloudSpatialAnchorSession.

    private CloudSpatialAnchorSession mCloudSession;
    // In your view handler
    mCloudSession = new CloudSpatialAnchorSession();

Configuración de la autenticación

Para acceder al servicio, deberá proporcionar una clave de cuenta, un token de acceso o un token de autenticación de Microsoft Entra. También puede leer más información al respecto en la página Concepto de autenticación.

Claves de cuenta

Las claves de cuenta son una credencial que permite que la aplicación se autentique con el servicio Azure Spatial Anchors. Las claves de cuenta están pensadas para ayudarle a empezar a trabajar rápidamente, sobre todo durante la fase de desarrollo de integración de la aplicación con Azure Spatial Anchors. Para usar las claves de cuenta, debe insertarlas en las aplicaciones cliente durante el desarrollo. A medida que avance en el desarrollo, se recomienda encarecidamente que cambie a un mecanismo de autenticación que sea de nivel de producción, compatible con tokens de acceso, o a la autenticación de usuario de Microsoft Entra. Para obtener una clave de cuenta para el desarrollo, visite su cuenta de Azure Spatial Anchors y vaya a la pestaña "Claves".

Obtenga más información sobre la clase SessionConfiguration.

    mCloudSession.getConfiguration().setAccountKey("MyAccountKey");

Tokens de acceso

Los tokens de acceso son un método más sólido para autenticarse con Azure Spatial Anchors, sobre todo al preparar la aplicación para una implementación de producción. En resumen, este enfoque consiste en configurar un servicio back-end con el que la aplicación cliente se pueda autenticar de forma segura. El servicio de back-end se comunica con AAD en tiempo de ejecución y con el servicio de token seguro de Azure Spatial Anchors para solicitar un token de acceso. Después, este token se entrega a la aplicación cliente y se usa en el SDK para autenticarse con Azure Spatial Anchors.

    mCloudSession.getConfiguration().setAccessToken("MyAccessToken");

Si no se ha establecido un token de acceso, debe controlar el evento TokenRequired o implementar el método tokenRequired en el protocolo de delegado.

Puede controlar el evento de forma sincrónica mediante el establecimiento de la propiedad en los argumentos del evento.

Obtenga más información sobre la interfaz TokenRequiredListener.

    mCloudSession.addTokenRequiredListener(args -> {
        args.setAccessToken("MyAccessToken");
    });

Si tiene que ejecutar un trabajo asincrónico en el controlador, puede aplazar el establecimiento del token. Para ello, solicite un objeto deferral y complételo, como se indica en el ejemplo siguiente.

    mCloudSession.addTokenRequiredListener(args -> {
        CloudSpatialAnchorSessionDeferral deferral = args.getDeferral();
        MyGetTokenAsync(myToken -> {
            if (myToken != null) args.setAccessToken(myToken);
            deferral.complete();
        });
    });

Autenticación de Microsoft Entra

Azure Spatial Anchors también permite que las aplicaciones se autentiquen con tokens de usuario de Microsoft Entra ID (Active Directory). Por ejemplo, puede usar tokens de Microsoft Entra para realizar la integración con Azure Spatial Anchors. Si una empresa mantiene usuarios en Microsoft Entra ID, puede proporcionar un token de usuario de Microsoft Entra en el SDK de Azure Spatial Anchors. De este modo, podrá autenticarse directamente en el servicio de Azure Spatial Anchors para una cuenta que forme parte del mismo inquilino de Microsoft Entra.

    mCloudSession.getConfiguration().setAuthenticationToken("MyAuthenticationToken");

Al igual que sucede con los tokens de acceso, si no se ha establecido un token de Microsoft Entra, debe controlar el evento TokenRequired o implementar el método tokenRequired en el protocolo de delegado.

Puede controlar el evento de forma sincrónica mediante el establecimiento de la propiedad en los argumentos del evento.

    mCloudSession.addTokenRequiredListener(args -> {
        args.setAuthenticationToken("MyAuthenticationToken");
    });

Si tiene que ejecutar un trabajo asincrónico en el controlador, puede aplazar el establecimiento del token. Para ello, solicite un objeto deferral y complételo, como se indica en el ejemplo siguiente.

    mCloudSession.addTokenRequiredListener(args -> {
        CloudSpatialAnchorSessionDeferral deferral = args.getDeferral();
        MyGetTokenAsync(myToken -> {
            if (myToken != null) args.setAuthenticationToken(myToken);
            deferral.complete();
        });
    });

Configuración de la sesión

Invoque Start() para permitir que la sesión procese datos del entorno.

Para controlar los eventos que genere la sesión, asocie un controlador de eventos.

    mCloudSession.setSession(mSession);
    mCloudSession.start();

Suministro de fotogramas a la sesión

La sesión de anclaje espacial funciona mediante la asignación del espacio en torno al usuario. Esto ayuda a determinar dónde se encuentran los delimitadores. Las plataformas móviles (iOS y Android) requieren una llamada a la fuente de la cámara para obtener fotogramas de la biblioteca de AR de la plataforma nativa. En cambio, HoloLens examina constantemente el entorno, por lo que no hay ninguna necesidad de realizar una llamada concreta, como en las plataformas móviles.

    mCloudSession.processFrame(mSession.update());

Envío de comentarios al usuario

Puede escribir código para controlar el evento actualizado de la sesión. Este evento se desencadena cada vez que la sesión mejora su capacidad de reconocimiento del entorno. Esto le permite hacer lo siguiente:

  • Usar la clase UserFeedback para proporcionar comentarios al usuario cuando el dispositivo se mueve y la sesión actualiza su capacidad de reconocimiento del entorno. Para ello:
  • Determine en qué momento hay suficientes datos espaciales de seguimiento para crear anclajes espaciales, lo que se puede determinar con ReadyForCreateProgress o RecommendedForCreateProgress. Una vez que ReadyForCreateProgress sea superior a 1, tendremos suficientes datos para guardar un anclaje espacial de la nube, aunque se recomienda esperar hasta que RecommendedForCreateProgress sea superior a 1 para hacerlo.

Obtenga más información sobre la interfaz SessionUpdatedListener.

    mCloudSession.addSessionUpdatedListener(args -> {
        auto status = args->Status();
        if (status->UserFeedback() == SessionUserFeedback::None) return;
        NumberFormat percentFormat = NumberFormat.getPercentInstance();
        percentFormat.setMaximumFractionDigits(1);
        mFeedback = String.format("Feedback: %s - Recommend Create=%s",
            FeedbackToString(status.getUserFeedback()),
            percentFormat.format(status.getRecommendedForCreateProgress()));
    });

Creación de un anclaje espacial de la nube

Para crear un anclaje espacial de la nube, primero es preciso crear un anclaje en el sistema de realidad aumentada de la plataforma y, luego, crear otro equivalente en la nube. Para ello, use el método CreateAnchorAsync().

Obtenga más información sobre la clase CloudSpatialAnchor.

    // Create a local anchor, perhaps by hit-testing and creating an ARAnchor
    Anchor localAnchor = null;
    List<HitResult> hitResults = mSession.update().hitTest(0.5f, 0.5f);
    for (HitResult hit : hitResults) {
        Trackable trackable = hit.getTrackable();
        if (trackable instanceof Plane) {
            if (((Plane) trackable).isPoseInPolygon(hit.getHitPose())) {
                localAnchor = hit.createAnchor();
                break;
            }
        }
    }

    // If the user is placing some application content in their environment,
    // you might show content at this anchor for a while, then save when
    // the user confirms placement.
    CloudSpatialAnchor cloudAnchor = new CloudSpatialAnchor();
    cloudAnchor.setLocalAnchor(localAnchor);
    Future createAnchorFuture = mCloudSession.createAnchorAsync(cloudAnchor);
    CheckForCompletion(createAnchorFuture, cloudAnchor);

    // ...

    private void CheckForCompletion(Future createAnchorFuture, CloudSpatialAnchor cloudAnchor) {
        new android.os.Handler().postDelayed(() -> {
            if (createAnchorFuture.isDone()) {
                try {
                    createAnchorFuture.get();
                    mFeedback = String.format("Created a cloud anchor with ID=%s", cloudAnchor.getIdentifier());
                }
                catch(InterruptedException e) {
                    mFeedback = String.format("Save Failed:%s", e.getMessage());
                }
                catch(ExecutionException e) {
                    mFeedback = String.format("Save Failed:%s", e.getMessage());
                }
            }
            else {
                CheckForCompletion(createAnchorFuture, cloudAnchor);
            }
        }, 500);
    }

Como ya se ha indicado, para intentar crear un anclaje espacial de la nube es preciso tener suficientes datos del entorno capturados, lo que significa que el valor de ReadyForCreateProgress debe ser superior a 1, aunque se recomienda esperar hasta que el valor de RecommendedForCreateProgress esté por encima de 1 para hacerlo.

    Future<SessionStatus> sessionStatusFuture = mCloudSession.getSessionStatusAsync();
    CheckForCompletion(sessionStatusFuture);

    // ...

    private void CheckForCompletion(Future<SessionStatus> sessionStatusFuture) {
        new android.os.Handler().postDelayed(() -> {
            if (sessionStatusFuture.isDone()) {
                try {
                    SessionStatus value = sessionStatusFuture.get();
                    if (value.getRecommendedForCreateProgress() < 1.0f) return;
                    // Issue the creation request...
                }
                catch(InterruptedException e) {
                    mFeedback = String.format("Session status error:%s", e.getMessage());
                }
                catch(ExecutionException e) {
                    mFeedback = String.format("Session status error:%s", e.getMessage());
                }
            }
            else {
                CheckForCompletion(sessionStatusFuture);
            }
        }, 500);
    }

Establecimiento de las propiedades

Puede optar por agregar algunas propiedades al guardar los anclajes espaciales de la nube, como el tipo de objeto que se está guardando o propiedades básicas, por ejemplo, si debe habilitarse para la interacción. Esto puede ser útil tras la detección, ya que le permite representar el objeto de inmediato para el usuario, por ejemplo, un marco de imagen con contenido en blanco. Después, una descarga diferente en segundo plano obtiene otros detalles de estado, como la imagen que se va a mostrar en el marco.

    CloudSpatialAnchor cloudAnchor = new CloudSpatialAnchor();
    cloudAnchor.setLocalAnchor(localAnchor);
    Map<String,String> properties = cloudAnchor.getAppProperties();
    properties.put("model-type", "frame");
    properties.put("label", "my latest picture");
    Future createAnchorFuture = mCloudSession.createAnchorAsync(cloudAnchor);
    // ...

Actualización de propiedades

Para actualizar las propiedades de un delimitador, se usa el método UpdateAnchorProperties(). Si dos o más dispositivos intentan actualizar las propiedades del mismo delimitador al mismo tiempo, se usa un modelo de simultaneidad optimista, lo que significa que la primera escritura tendrá prioridad. En todas las demás se producirá un error de "simultaneidad", es decir, será necesario actualizar las propiedades antes de intentarlo de nuevo.

    CloudSpatialAnchor anchor = /* locate your anchor */;
    anchor.getAppProperties().put("last-user-access", "just now");
    Future updateAnchorPropertiesFuture = mCloudSession.updateAnchorPropertiesAsync(anchor);
    CheckForCompletion(updateAnchorPropertiesFuture);

    // ...

    private void CheckForCompletion(Future updateAnchorPropertiesFuture) {
        new android.os.Handler().postDelayed(() -> {
            if (updateAnchorPropertiesFuture.isDone()) {
                try {
                    updateAnchorPropertiesFuture.get();
                }
                catch(InterruptedException e) {
                    mFeedback = String.format("Updating Properties Failed:%s", e.getMessage());
                }
                catch(ExecutionException e) {
                    mFeedback = String.format("Updating Properties Failed:%s", e.getMessage());
                }
            }
            else {
                CheckForCompletion1(updateAnchorPropertiesFuture);
            }
        }, 500);
    }

No se puede actualizar la ubicación de un delimitador una vez que se ha creado en el servicio. Debe crear otro delimitador y eliminar el antiguo para realizar un seguimiento de una nueva posición.

Si no necesita localizar un delimitador para actualizar sus propiedades, puede usar el método GetAnchorPropertiesAsync(), que devuelve un objeto CloudSpatialAnchor con propiedades.

    Future<CloudSpatialAnchor> getAnchorPropertiesFuture = mCloudSession.getAnchorPropertiesAsync("anchorId");
    CheckForCompletion(getAnchorPropertiesFuture);

    // ...

    private void CheckForCompletion(Future<CloudSpatialAnchor> getAnchorPropertiesFuture) {
        new android.os.Handler().postDelayed(() -> {
            if (getAnchorPropertiesFuture.isDone()) {
                try {
                    CloudSpatialAnchor anchor = getAnchorPropertiesFuture.get();
                    if (anchor != null) {
                        anchor.getAppProperties().put("last-user-access", "just now");
                        Future updateAnchorPropertiesFuture = mCloudSession.updateAnchorPropertiesAsync(anchor);
                        // ...
                    }
                } catch (InterruptedException e) {
                    mFeedback = String.format("Getting Properties Failed:%s", e.getMessage());
                } catch (ExecutionException e) {
                    mFeedback = String.format("Getting Properties Failed:%s", e.getMessage());
                }
            } else {
                CheckForCompletion(getAnchorPropertiesFuture);
            }
        }, 500);
    }

Configurar expiración

También es posible configurar el delimitador para que expire automáticamente en el futuro en una fecha determinada. Cuando expira un delimitador, ya no se encontrará ni se actualizará. La expiración solo se puede establecer cuando se crea el anclaje, antes de guardarlo en la nube. No es posible actualizarla más adelante, Si no se establece ninguna expiración durante la creación del anclaje, este solo expirará cuando se elimine manualmente.

    Date now = new Date();
    Calendar cal = Calendar.getInstance();
    cal.setTime(now);
    cal.add(Calendar.DATE, 7);
    Date oneWeekFromNow = cal.getTime();
    cloudAnchor.setExpiration(oneWeekFromNow);

Búsqueda de un anclaje espacial de la nube

Una de las principales razones para usar Azure Spatial Anchors es poder localizar un anclaje espacial en la nube previamente guardado. Para ello, usamos "Monitores". Solo se puede usar un monitor a la vez; no se admiten varios monitores. Hay varias maneras diferentes (también conocidas como Estrategias de búsqueda de anclajes) que un monitor puede localizar un delimitador espacial en la nube. Una estrategia no se puede usar en varios monitores a la vez.

  • Busque delimitadores por identificador.
  • Busque los delimitadores conectados a un delimitador ubicado previamente. Aquí puede obtener información sobre las relaciones de los delimitadores.
  • Busque un delimitador mediante la relocalización general.

Nota

Cada vez que se encuentra un delimitador, Azure Spatial Anchors intenta usar los datos de entorno recopilados para aumentar la información visual del delimitador. Si tiene problemas para encontrar un delimitador, puede ser útil crear uno y, a continuación, colocarlo varias veces en diferentes ángulos y condiciones de iluminación.

Si va a buscar anclajes espaciales en la nube por identificador, podrá almacenar el identificador de anclaje espacial de la nube en el servicio back-end de la aplicación y hacer que todos los dispositivos que puedan autenticarse correctamente en él puedan acceder a él. For ver un ejemplo de esto, consulte Tutorial: Uso compartido de Spatial Anchors entre dispositivos.

Crear una instancia de un objeto AnchorLocateCriteria, establezca los identificadores que busca e invoque el método CreateWatcher en la sesión mediante su AnchorLocateCriteria.

    AnchorLocateCriteria criteria = new AnchorLocateCriteria();
    criteria.setIdentifiers(new String[] { "id1", "id2", "id3" });
    mCloudSession.createWatcher(criteria);

Una vez creado el monitor, se desencadenará el evento AnchorLocated para cada delimitador solicitado. Este evento se desencadena cuando se encuentra un delimitador o si no puede encontrarse. Si se produce esta situación, el motivo se incluirá en el estado. Una vez procesados todos los delimitadores de un monitor, tanto si se encuentran como si no, se desencadenará el evento LocateAnchorsCompleted. Hay un límite de 35 identificadores por monitor.

Obtenga más información sobre la interfaz AnchorLocatedListener.

    mCloudSession.addAnchorLocatedListener(args -> {
        switch (args.getStatus()) {
            case Located:
                CloudSpatialAnchor foundAnchor = args.getAnchor();
                // Go add your anchor to the scene...
                break;
            case AlreadyTracked:
                // This anchor has already been reported and is being tracked
                break;
            case NotLocatedAnchorDoesNotExist:
                // The anchor was deleted or never existed in the first place
                // Drop it, or show UI to ask user to anchor the content anew
                break;
            case NotLocated:
                // The anchor hasn't been found given the location data
                // The user might in the wrong location, or maybe more data will help
                // Show UI to tell user to keep looking around
                break;
        }
    });

Eliminación de delimitadores

La eliminación de los anclajes que ya no se usan es una práctica recomendada que debe incluirse en las primeras fases del proceso de desarrollo para mantener limpios los recursos de Azure.

    Future deleteAnchorFuture = mCloudSession.deleteAnchorAsync(cloudAnchor);
    // Perform any processing you may want when delete finishes (deleteAnchorFuture is done)

Pausa, restablecimiento o detención de la sesión

Para detener la sesión temporalmente, puede invocar Stop(). Si lo hace, se detendrán los monitores y el procesamiento de entorno, incluso si invoca ProcessFrame(). Puede invocar Start() para reanudar el procesamiento. Cuando se reanude, se conservarán los datos del entorno que ya se hayan capturado en la sesión.

    mCloudSession.stop();

Para restablecer los datos del entorno que se han capturado en la sesión, puede invocar Reset().

    mCloudSession.reset();

Para realizar correctamente una limpieza después de una sesión, invoque close().

    mCloudSession.close();

Pasos siguientes

En esta guía, ha aprendido a crear y localizar delimitadores mediante el SDK de Azure Spatial Anchors. Para obtener más información sobre las relaciones de los delimitadores, vaya a la guía siguiente.