Como criar e localizar âncoras usando as Âncoras Espaciais do Azure em Java

As Âncoras Espaciais do Azure permitem-lhe partilhar âncoras no mundo entre diferentes dispositivos. Ele suporta vários ambientes de desenvolvimento diferentes. Neste artigo, vamos nos aprofundar em como usar o SDK do Azure Spatial Anchors, em Java, para:

  • Configure e gerencie corretamente uma sessão de Âncoras Espaciais do Azure.
  • Crie e defina propriedades em âncoras locais.
  • Carregue-os para a nuvem.
  • Localize e exclua âncoras espaciais da nuvem.

Pré-requisitos

Para completar este guia, certifique-se de:

  • Leia a visão geral das Âncoras Espaciais do Azure.
  • Completou um dos Quickstarts de 5 minutos.
  • Conhecimentos básicos em Java.
  • Conhecimentos básicos em ARCore.

Cross Platform

Inicializar a sessão

O principal ponto de entrada para o SDK é a classe que representa sua sessão. Normalmente, você declarará um campo na classe que gerencia sua exibição e sessão de RA nativa.

Saiba mais sobre a classe CloudSpatialAnchorSession .

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

Configurar a autenticação

Para acessar o serviço, você precisa fornecer uma chave de conta, token de acesso ou token de autenticação do Microsoft Entra. Você também pode ler mais sobre isso na página Conceito de autenticação.

Chaves de conta

As Chaves de Conta são uma credencial que permite que seu aplicativo se autentique com o serviço Âncoras Espaciais do Azure. O objetivo pretendido das Chaves de Conta é ajudá-lo a começar rapidamente. Especialmente durante a fase de desenvolvimento da integração do seu aplicativo com as Âncoras Espaciais do Azure. Como tal, você pode usar chaves de conta incorporando-as em seus aplicativos cliente durante o desenvolvimento. À medida que você avança além do desenvolvimento, é altamente recomendável mudar para um mecanismo de autenticação de nível de produção, suportado por Tokens de Acesso ou autenticação de usuário do Microsoft Entra. Para obter uma Chave de Conta para desenvolvimento, visite sua conta do Azure Spatial Anchors e navegue até a guia "Chaves".

Saiba mais sobre a classe SessionConfiguration .

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

Tokens de Acesso

Os Tokens de Acesso são um método mais robusto para autenticar com as Âncoras Espaciais do Azure. Especialmente enquanto você prepara seu aplicativo para uma implantação de produção. O resumo dessa abordagem é configurar um serviço back-end com o qual seu aplicativo cliente possa autenticar com segurança. Seu serviço back-end faz interface com o AAD em tempo de execução e com o Serviço de Token Seguro de Âncoras Espaciais do Azure para solicitar um Token de Acesso. Esse token é então entregue ao aplicativo cliente e usado no SDK para autenticar com as Âncoras Espaciais do Azure.

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

Se um token de acesso não estiver definido, você deverá manipular o TokenRequired evento ou implementar o tokenRequired método no protocolo delegado.

Você pode manipular o evento de forma síncrona definindo a propriedade nos argumentos do evento.

Saiba mais sobre a interface TokenRequiredListener .

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

Se você precisar executar um trabalho assíncrono em seu manipulador, poderá adiar a configuração do token solicitando um deferral objeto e, em seguida, completando-o, como no exemplo a seguir.

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

Autenticação do Microsoft Entra

As Âncoras Espaciais do Azure também permitem que os aplicativos se autentiquem com tokens de ID do Microsoft Entra (Ative Directory) do usuário. Por exemplo, você pode usar tokens do Microsoft Entra para integrar com as Âncoras Espaciais do Azure. Se uma Empresa mantiver usuários na ID do Microsoft Entra, você poderá fornecer um token do Microsoft Entra de usuário no SDK do Azure Spatial Anchors. Isso permite que você se autentique diretamente no serviço Âncoras Espaciais do Azure para uma conta que faz parte do mesmo locatário do Microsoft Entra.

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

Como acontece com os tokens de acesso, se um token do Microsoft Entra não estiver definido, você deverá manipular o evento TokenRequired ou implementar o método tokenRequired no protocolo delegado.

Você pode manipular o evento de forma síncrona definindo a propriedade nos argumentos do evento.

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

Se você precisar executar um trabalho assíncrono em seu manipulador, poderá adiar a configuração do token solicitando um deferral objeto e, em seguida, completando-o, como no exemplo a seguir.

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

Configurar a sessão

Invoque Start() para permitir que sua sessão processe dados do ambiente.

Para manipular eventos gerados pela sessão, anexe um manipulador de eventos.

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

Fornecer quadros para a sessão

A sessão de âncora espacial funciona mapeando o espaço ao redor do usuário. Isso ajuda a determinar onde as âncoras estão localizadas. As plataformas móveis (iOS ou Android) exigem uma chamada nativa para o feed da câmera para obter quadros da biblioteca de RA da sua plataforma. Em contraste, o HoloLens está constantemente verificando o ambiente, portanto, não há necessidade de uma chamada específica, como em plataformas móveis.

    mCloudSession.processFrame(mSession.update());

Fornecer feedback ao utilizador

Você pode escrever código para manipular o evento atualizado da sessão. Este evento é acionado sempre que a sessão melhora a sua compreensão do que o rodeia. Ao fazê-lo, permite-lhe:

  • Use a classe para fornecer feedback ao usuário à medida que o dispositivo se move e a UserFeedback sessão atualiza sua compreensão do ambiente. Para tal,
  • Determine em que ponto há dados espaciais rastreados suficientes para criar âncoras espaciais. Você determina isso com um ou ReadyForCreateProgressRecommendedForCreateProgress. Uma vez ReadyForCreateProgress acima de 1, temos dados suficientes para salvar uma âncora espacial da nuvem, embora recomendemos que você espere até RecommendedForCreateProgress que esteja acima de 1 para fazê-lo.

Saiba mais sobre a interface 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()));
    });

Criar uma âncora espacial na nuvem

Para criar uma âncora espacial na nuvem, primeiro crie uma âncora no sistema de RA da sua plataforma e, em seguida, crie uma contraparte na nuvem. Você usa o CreateAnchorAsync() método.

Saiba mais sobre a classe 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);
    }

Conforme descrito anteriormente, você precisa de dados de ambiente suficientes capturados antes de tentar criar uma nova âncora espacial na nuvem. Isso significa ReadyForCreateProgress que tem que estar acima de 1, embora recomendemos que você espere até RecommendedForCreateProgress estar acima de 1 para fazê-lo.

    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);
    }

Definir as propriedades

Você pode optar por adicionar algumas propriedades ao salvar suas âncoras espaciais na nuvem. Como o tipo de objeto que está sendo salvo ou propriedades básicas, como se ele deve ser habilitado para interação. Fazer isso pode ser útil após a descoberta: você pode renderizar imediatamente o objeto para o usuário, por exemplo, um quadro de imagem com conteúdo em branco. Em seguida, um download diferente em segundo plano obtém detalhes de estado adicionais, por exemplo, a imagem para exibir no quadro.

    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);
    // ...

Atualizar propriedades

Para atualizar as propriedades em uma âncora, use o UpdateAnchorProperties() método. Se dois ou mais dispositivos tentarem atualizar propriedades para a mesma âncora ao mesmo tempo, usaremos um modelo de simultaneidade otimista. O que significa que a primeira escrita vencerá. Todas as outras gravações receberão um erro de "simultaneidade": uma atualização das propriedades seria necessária antes de tentar novamente.

    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);
    }

Você não pode atualizar o local de uma âncora depois que ela tiver sido criada no serviço - você deve criar uma nova âncora e excluir a antiga para rastrear uma nova posição.

Se você não precisar localizar uma âncora para atualizar suas propriedades, poderá usar o GetAnchorPropertiesAsync() método, que retorna um CloudSpatialAnchor objeto com propriedades.

    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);
    }

Definir expiração

Também é possível configurar sua âncora para expirar automaticamente em uma determinada data no futuro. Quando uma âncora expirar, ela não será mais localizada ou atualizada. A expiração só pode ser definida quando a âncora é criada, antes de salvá-la na nuvem. Não é possível atualizar a expiração posteriormente. Se nenhuma expiração for definida durante a criação da âncora, a âncora só expirará quando excluída manualmente.

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

Localizar uma âncora espacial na nuvem

Ser capaz de localizar uma âncora espacial de nuvem salva anteriormente é um dos principais motivos para usar as Âncoras Espaciais do Azure. Para isso, estamos usando "Watchers". Você só pode usar um Observador de cada vez; vários Observadores não são suportados. Existem várias maneiras diferentes (também conhecidas como Anchor Locate Strategies) de um Observador localizar uma âncora espacial na nuvem. Você pode usar uma estratégia em um observador de cada vez.

  • Localize âncoras por identificador.
  • Localize âncoras conectadas a uma âncora localizada anteriormente. Você pode aprender sobre relacionamentos âncora aqui.
  • Localize a âncora usando a relocalização grosseira.

Nota

Sempre que localizar uma âncora, as Âncoras Espaciais do Azure tentarão utilizar os dados do ambiente recolhidos para aumentar as informações visuais na âncora. Se você estiver tendo problemas para localizar uma âncora, pode ser útil criar uma âncora e, em seguida, localizá-la várias vezes de diferentes ângulos e condições de iluminação.

Se você estiver localizando âncoras espaciais na nuvem por identificador, poderá armazenar o identificador de âncora espacial na nuvem no serviço de back-end do seu aplicativo e torná-lo acessível a todos os dispositivos que possam autenticá-lo corretamente. Para obter um exemplo disso, consulte Tutorial: Compartilhar âncoras espaciais entre dispositivos.

Instancie um AnchorLocateCriteria objeto, defina os identificadores que você está procurando e invoque o método na sessão fornecendo AnchorLocateCriteriaseu CreateWatcher arquivo .

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

Depois que o observador for criado, o evento será acionado para cada âncora AnchorLocated solicitada. Esse evento é acionado quando uma âncora está localizada ou se a âncora não pode ser localizada. Se esta situação acontecer, o motivo será indicado no estado. Depois que todas as âncoras de um observador forem processadas, encontradas ou não encontradas, o LocateAnchorsCompleted evento será acionado. Há um limite de 35 identificadores por observador.

Saiba mais sobre a interface 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;
        }
    });

Excluir âncoras

Excluir âncoras quando não são mais usadas é uma boa prática para incluir no início do processo e das práticas de desenvolvimento, para manter seus recursos do Azure limpos.

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

Pausar, redefinir ou parar a sessão

Para interromper a sessão temporariamente, você pode invocar Stop(). Fazer isso interromperá qualquer observador e processamento de ambiente, mesmo se você invocar ProcessFrame(). Em seguida, você pode invocar Start() para retomar o processamento. Ao retomar, os dados do ambiente já capturados na sessão são mantidos.

    mCloudSession.stop();

Para redefinir os dados do ambiente que foram capturados em sua sessão, você pode invocar Reset().

    mCloudSession.reset();

Para limpar corretamente após uma sessão, invoque close().

    mCloudSession.close();

Próximos passos

Neste guia, você aprendeu sobre como criar e localizar âncoras usando o SDK de Âncoras Espaciais do Azure. Para saber mais sobre relacionamentos âncora, continue para o próximo guia.