Erstellen von und Suchen nach Ankern mit Azure Spatial Anchors in C++/NDK

Azure Spatial Anchors ermöglicht die weltweite gemeinsame Verwendung von Ankern durch verschiedene Geräte. Der Dienst unterstützt verschiedene Entwicklungsumgebungen. In diesem Artikel wird erläutert, wie das Azure Spatial Anchors SDK in C++/NDK für die folgenden Aufgaben verwendet werden kann:

  • Ordnungsgemäßes Einrichten und Verwalten einer Azure Spatial Anchors-Sitzung.
  • Erstellen und Festlegen von Eigenschaften für lokale Anker.
  • Hochladen von Ankern in die Cloud.
  • Suchen nach und Löschen von räumlichen Cloudankern.

Voraussetzungen

Um diese Anleitung durchzuführen, stellen Sie sicher, dass Folgendes erfüllt ist:

Cross Platform

Initialisieren der Sitzung

Der Haupteinstiegspunkt für das SDK ist die Klasse, die die Sitzung darstellt. In der Regel müssen Sie ein Feld in der Klasse deklarieren, die Ihre Ansicht und die native AR-Sitzung verwaltet.

Erfahren Sie mehr über die CloudSpatialAnchorSession-Struktur.

    std::shared_ptr<CloudSpatialAnchorSession> cloudSession_;
    // In your view handler
    cloudSession_ = std::make_shared<CloudSpatialAnchorSession>();

Einrichten der Authentifizierung

Um auf den Dienst zugreifen zu können, müssen Sie einen Kontoschlüssel, ein Zugriffstoken oder ein Microsoft Entra-Authentifizierungstoken angeben. Weitere Informationen hierzu finden Sie auf der Konzeptseite für die Authentifizierung.

Kontoschlüssel

Kontoschlüssel sind eine Anmeldeinformationen, die es Ihrer Anwendung ermöglichen, sich beim Azure Spatial Anchors-Dienst zu authentifizieren. Der Zweck von Kontoschlüsseln besteht darin, Ihnen einen schnellen Einstieg zu ermöglichen. Vor allem während der Entwicklungsphase der Integration Ihrer Anwendung mit Azure Spatial Anchors. Sie können Kontoschlüssel verwenden, indem Sie sie während der Entwicklung in Ihre Clientanwendungen einbetten. Nach Abschluss der Entwicklung wird dringend empfohlen, zu einem Authentifizierungsmechanismus für die Produktionsebene zu wechseln, der von Zugriffstoken oder Microsoft Entra-basierter Benutzerauthentifizierung unterstützt wird. Um einen Kontoschlüssel für die Entwicklung zu erhalten, besuchen Sie Ihr Azure Spatial Anchors-Konto, und navigieren Sie zur Registerkarte „Schlüssel“.

Erfahren Sie mehr über die SessionConfiguration-Struktur.

    auto configuration = cloudSession_->Configuration();
    configuration->AccountKey(R"(MyAccountKey)");

Zugriffstoken

Zugriffstoken sind eine robustere Methode zur Authentifizierung bei Azure Spatial Anchors. Vor allem, wenn Sie Ihre Anwendung für eine Produktionsbereitstellung vorbereiten. Die Zusammenfassung dieses Ansatzes besteht darin, einen Back-End-Dienst einzurichten, bei dem sich Ihre Clientanwendung sicher authentifizieren kann. Ihr Back-End-Dienst kommuniziert zur Runtime mit AAD und mit dem STS-Dienst (Secure Token Service) von Azure Spatial Anchors, um ein Zugriffstoken anzufordern. Dieses Token wird dann der Clientanwendung bereitgestellt und im SDK zur Authentifizierung bei Azure Spatial Anchors verwendet.

    auto configuration = cloudSession_->Configuration();
    configuration->AccessToken(R"(MyAccessToken)");

Wenn ein Zugriffstoken nicht festgelegt ist, müssen Sie das TokenRequired-Ereignis behandeln oder die tokenRequired-Methode im Delegatenprotokoll implementieren.

Sie können das Ereignis synchron behandeln, indem Sie die Eigenschaft für die Ereignisargumente festlegen.

Erfahren Sie mehr über den TokenRequiredDelegate-Delegaten.

    auto accessTokenRequiredToken = cloudSession_->TokenRequired([](auto&&, auto&& args) {
        args->AccessToken(R"(MyAccessToken)");
    });

Wenn Sie in Ihrem Handler asynchrone Arbeit ausführen müssen, können Sie die Einstellung des Token verzögern, indem Sie ein deferral-Objekt anfordern und es dann wie im folgenden Beispiel abschließen.

    auto accessTokenRequiredToken = cloudSession_->TokenRequired([this](auto&&, auto&& args) {
        std::shared_ptr<CloudSpatialAnchorSessionDeferral> deferral = args->GetDeferral();
        MyGetTokenAsync([&deferral, &args](std::string const& myToken) {
            if (myToken != nullptr) args->AccessToken(myToken);
            deferral->Complete();
        });
    });

Microsoft Entra-Authentifizierung

Bei Azure Spatial Anchors können Anwendungen sich auch mit Microsoft Entra ID-Benutzertoken (Active Directory) authentifizieren. Sie können beispielsweise Microsoft Entra-Token für die Integration in Azure Spatial Anchors verwenden. Wenn ein Unternehmen Benutzer*innen in Microsoft Entra ID verwaltet, können Sie ein Microsoft Entra-Benutzertoken im Azure Spatial Anchors-SDK bereitstellen. Auf diese Weise ermöglichen Sie für Konten, die zum gleichen Microsoft Entra-Mandanten gehören, eine direkte Authentifizierung beim Azure Spatial Anchors-Dienst.

    auto configuration = cloudSession_->Configuration();
    configuration->AuthenticationToken(R"(MyAuthenticationToken)");

Wenn ein Microsoft Entra-Token nicht festgelegt ist, müssen Sie wie bei Zugriffstoken das TokenRequired-Ereignis behandeln oder die tokenRequired-Methode im Delegatenprotokoll implementieren.

Sie können das Ereignis synchron behandeln, indem Sie die Eigenschaft für die Ereignisargumente festlegen.

    auto accessTokenRequiredToken = cloudSession_->AccessTokenRequired([](auto&&, auto&& args) {
        args->AuthenticationToken(R"(MyAuthenticationToken)");
    });

Wenn Sie in Ihrem Handler asynchrone Arbeit ausführen müssen, können Sie die Einstellung des Token verzögern, indem Sie ein deferral-Objekt anfordern und es dann wie im folgenden Beispiel abschließen.

    auto accessTokenRequiredToken = cloudSession_->TokenRequired([this](auto&&, auto&& args) {
        std::shared_ptr<CloudSpatialAnchorSessionDeferral> deferral = args->GetDeferral();
        MyGetTokenAsync([&deferral, &args](std::string const& myToken) {
            if (myToken != nullptr) args->AuthenticationToken(myToken);
            deferral->Complete();
        });
    });

Einrichten der Sitzung

Rufen Sie Start() auf, damit Ihre Sitzung Umgebungsdaten verarbeitet.

Um die von der Sitzung ausgelösten Ereignisse behandeln zu können, fügen Sie einen Ereignishandler an.

Erfahren Sie mehr über die Start-Methode.

    cloudSession_->Session(ar_session_);
    cloudSession_->Start();

Bereitstellen von Rahmen für die Sitzung

Bei einer Sitzung mit Raumankern wird die Umgebung zugeordnet, in der sich der Benutzer gerade befindet. Auf diese Weise lässt sich ermitteln, wo sich Anker befinden. Mobile Plattformen (iOS und Android) erfordern einen nativen Aufruf des Kamerafeeds, um Rahmen aus der AR-Bibliothek Ihrer Plattform abzurufen. Im Gegensatz dazu scannt HoloLens die Umgebung ständig, sodass kein gesonderter Aufruf wie bei mobilen Plattformen erforderlich ist.

Erfahren Sie mehr über die ProcessFrame-Methode.

    cloudSession_->ProcessFrame(ar_frame_);

Bereitstellen von Feedback für Benutzer

Sie können Code schreiben, um das aktualisierte Sitzungsereignis zu verarbeiten. Dieses Ereignis wird jedes Mal ausgelöst, wenn die Sitzung ihr Verständnis für Ihre Umgebung verbessert. Auf diese Weise können Sie Folgendes erreichen:

  • Verwenden Sie die Klasse UserFeedback, um Feedback für Benutzer bereitzustellen, wenn sich das Gerät bewegt und die Sitzung die Informationen zur Umgebung aktualisiert. Gehen Sie dazu folgendermaßen vor:
  • Bestimmen Sie, an welchem Punkt genügend räumliche Daten erfasst wurden, um Raumanker zu erstellen. Verwenden Sie hierzu ReadyForCreateProgress oder RecommendedForCreateProgress. Sobald ReadyForCreateProgress größer 1 ist, liegen genügend Daten zum Speichern eines cloudbasierten Raumankers vor. Es empfiehlt sich jedoch, damit zu warten, bis RecommendedForCreateProgress größer 1 ist.

Erfahren Sie mehr über den SessionUpdatedDelegate-Delegaten.

    auto sessionUpdatedToken = cloudSession_->SessionUpdated([this](auto&&, auto&& args) {
        auto status = args->Status();
        if (status->UserFeedback() == SessionUserFeedback::None) return;
        std::ostringstream str;
        str << std::fixed << std::setw(2) << std::setprecision(0)
            << R"(Feedback: )" << FeedbackToString(status.UserFeedback()) << R"( -)"
            << R"( Recommend Create=)" << (status->RecommendedForCreateProgress() * 100) << R"(%)";
        feedback_ = str.str();
    });

Erstellen eines cloudbasierten Raumankers

Um einen cloudbasierten Raumanker zu erstellen, erstellen Sie zunächst einen Anker im AR-System Ihrer Plattform und dann ein Cloudgegenstück. Sie verwenden die CreateAnchorAsync()-Methode.

Erfahren Sie mehr über die CloudSpatialAnchor-Struktur.

    // Create a local anchor, perhaps by hit-testing and creating an ARAnchor
    ArAnchor* localAnchor;
    ArHitResultList* hit_result_list = nullptr;
    ArHitResultList_create(ar_session_, &hit_result_list);
    CHECK(hit_result_list);
    ArFrame_hitTest(ar_session_, ar_frame_, 0.5, 0.5, hit_result_list);
    int32_t hit_result_list_size = 0;
    ArHitResultList_getSize(ar_session_, hit_result_list, &hit_result_list_size);
    if (hit_result_list_size == 0) {
        ArHitResultList_destroy(hit_result_list);
        return;
    }
    ArHitResult* ar_hit = nullptr;
    ArHitResult_create(ar_session_, &ar_hit);
    // The hitTest method sorts the resulting list by increasing distance from the camera
    // The first hit result will usually be the most relevant when responding to user input
    ArHitResultList_getItem(ar_session_, hit_result_list, 0, ar_hit);
    if (ArHitResult_acquireNewAnchor(ar_session_, ar_hit, &localAnchor) != AR_SUCCESS) return;
    ArTrackingState tracking_state = AR_TRACKING_STATE_STOPPED;
    ArAnchor_getTrackingState(ar_session_, localAnchor, &tracking_state);
    if (tracking_state != AR_TRACKING_STATE_TRACKING) {
        ArAnchor_release(localAnchor);
        ArHitResult_destroy(ar_hit);
        return;
    }
    ArHitResult_destroy(ar_hit);
    ar_hit = nullptr;
    ArHitResultList_destroy(hit_result_list);
    hit_result_list = nullptr;

    // 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.
    std::shared_ptr<CloudSpatialAnchor> cloudAnchor = std::make_shared<CloudSpatialAnchor>();
    cloudAnchor->LocalAnchor(localAnchor);
    cloudSession_->CreateAnchorAsync(cloudAnchor, [this, cloudAnchor](Status status) {
        std::ostringstream str;
        if (status != Status::OK) {
            str << "Save Failed: " << std::to_string(static_cast<uint32_t>(status));
            feedback_ = str.str();
            return;
        }
        str << R"(Created a cloud anchor with ID=)" << cloudAnchor->Identifier();
        feedback_ = str.str();
    });

Wie bereits erwähnt, benötigen Sie ausreichende Umgebungsdaten, um einen neuen cloudbasierten Raumanker erstellen zu können. Das bedeutet, dass ReadyForCreateProgress größer 1 sein muss. Es empfiehlt sich jedoch, zu warten, bis RecommendedForCreateProgress größer 1 ist.

Erfahren Sie mehr über GetSessionStatusAsync-Methode.

    cloudSession_->GetSessionStatusAsync([this](Status status, const std::shared_ptr<SessionStatus>& value) {
        if (status != Status::OK) {
            std::ostringstream str;
            str << "Session status error: " << std::to_string(static_cast<uint32_t>(status));
            feedback_ = str.str();
            return;
        }
        if (value->RecommendedForCreateProgress() < 1.0f) return;
        // Issue the creation request ...
    });

Eigenschaften festlegen

Beim Speichern Ihrer cloudbasierten Raumanker können Sie einige Eigenschaften hinzufügen. Beispiele: die Art des gespeicherten Objekts oder grundlegende Eigenschaften wie etwa das Ermöglichen von Interaktion. Solche Eigenschaften können beim Ermitteln nützlich sein: Sie können ein Objekt sofort für den Benutzer rendern – z.B. als Bildrahmen mit leerem Inhalt. Ein anderer Download im Hintergrund stellt dann zusätzliche Statusdetails bereit, beispielsweise das Bild, das im Rahmen angezeigt werden soll.

Erfahren Sie mehr über die AppProperties-Methode.

    std::shared_ptr<CloudSpatialAnchor> cloudAnchor = std::make_shared<CloudSpatialAnchor>();
    cloudAnchor->LocalAnchor(localAnchor);
    auto properties = cloudAnchor->AppProperties();
    properties->Insert(R"(model-type)", R"(frame)");
    properties->Insert(R"(label)", R"(my latest picture)");
    cloudSession_->CreateAnchorAsync(cloudAnchor, [this, cloudAnchor](Status status) {
        // ...
    });

Aktualisieren von Eigenschaften

Um die Eigenschaften eines Ankers zu aktualisieren, verwenden Sie die UpdateAnchorProperties()-Methode. Wenn zwei oder mehr Geräte gleichzeitig versuchen, die Eigenschaften für den gleichen Anker zu aktualisieren, verwenden wir ein Modell der optimistischen Nebenläufigkeit. Das bedeutet, dass der erste Schreibvorgang gewinnt. Bei allen anderen Schreibvorgängen tritt ein Parallelitätsfehler auf: Vor einem erneuten Versuch müssen die Eigenschaften aktualisiert werden.

Erfahren Sie mehr über die UpdateAnchorPropertiesAsync-Methode.

    std::shared_ptr<CloudSpatialAnchor> anchor = /* locate your anchor */;
    auto properties = anchor->AppProperties();
    properties->Insert(R"(last-user-access)", R"(just now)");
    cloudSession_->UpdateAnchorPropertiesAsync(anchor, [this](Status status) {
        if (status != Status::OK) {
            std::ostringstream str;
            str << "Updating Properties Failed: " << std::to_string(static_cast<uint32_t>(status));
            feedback_ = str.str();
        }
    });

Sie können den Speicherort eines Ankers nicht aktualisieren, nachdem er für den Dienst erstellt wurde – Sie müssen einen neuen Anker erstellen und den alten löschen, um eine neue Position zu verfolgen.

Wenn Sie einen Anker nicht suchen müssen, um seine Eigenschaften zu aktualisieren, können Sie die GetAnchorPropertiesAsync()-Methode verwenden, die ein CloudSpatialAnchor-Objekt mit Eigenschaften zurückgibt.

Erfahren Sie mehr über die GetAnchorPropertiesAsync-Methode.

    cloudSession_->GetAnchorPropertiesAsync(R"(anchorId)", [this](Status status, const std::shared_ptr<CloudSpatialAnchor>& anchor) {
        if (status != Status::OK) {
            std::ostringstream str;
            str << "Getting Properties Failed: " << std::to_string(static_cast<uint32_t>(status));
            feedback_ = str.str();
            return;
        }
        if (anchor != nullptr) {
            auto properties = anchor->AppProperties();
            properties->Lookup(R"(last-user-access)") = R"(just now)";
            cloudSession_->UpdateAnchorPropertiesAsync(anchor, [this](Status status) {
                // ...
            });
        }
    });

Ablaufdatum festlegen

Sie können Ihren Anker auch so konfigurieren, dass er an einem bestimmten Datum automatisch abläuft. Wenn ein Anker abläuft, wird er nicht mehr lokalisiert oder aktualisiert. Ein Ablauf kann nur beim Erstellen des Ankers festgelegt werden, bevor er in der Cloud gespeichert wird. Eine nachträgliche Aktualisierung des Ablaufdatums ist nicht möglich. Wenn während der Erstellung des Ankers kein Ablauf festgelegt wird, läuft der Anker nur ab, wenn er manuell gelöscht wird.

Erfahren Sie mehr über die Expiration-Methode.

    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    std::chrono::system_clock::time_point oneWeekFromNow = now + std::chrono::hours(7 * 24);
    const int64_t oneWeekFromNowUnixEpochTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(oneWeekFromNow.time_since_epoch()).count();
    cloudAnchor->Expiration(oneWeekFromNowUnixEpochTimeMs);

Lokalisieren eines cloudbasierten Raumankers

Die Möglichkeit, einen zuvor gespeicherten cloudbasierten Raumanker zu lokalisieren, ist einer der Hauptgründe für die Nutzung von Azure Spatial Anchors. Dazu verwenden wir "Watchers". Sie können jeweils nur einen Watcher verwenden. Mehrere Watchers werden nicht unterstützt. Es gibt verschiedene Möglichkeiten (auch bekannt als Anchor Locate Strategies) ein Watcher kann einen Cloudraumanker finden. Sie können jeweils eine Strategie pro Watcher verwenden.

  • Lokalisieren von Ankern anhand des Bezeichners
  • Lokalisieren von Ankern, die mit einem zuvor lokalisierten Anker verbunden sind. Informationen zu Ankerbeziehungen finden Sie hier.
  • Lokalisieren eines Ankers unter Verwendung der ungefähren Standortbestimmung

Hinweis

Bei jeder Lokalisierung eines Ankers wird von Azure Spatial Anchors versucht, die erfassten Umgebungsdaten zu verwenden, um die visuellen Informationen des Ankers zu erweitern. Falls Sie beim Lokalisieren eines Ankers Probleme haben, kann es hilfreich sein, einen Anker zu erstellen und diesen dann mehrfach aus unterschiedlichen Winkeln und bei unterschiedlichen Lichtverhältnissen zu lokalisieren.

Wenn Sie cloudbasierte Raumanker anhand des Bezeichners lokalisieren, sollten Sie den Bezeichner des cloudbasierten Raumankers im Back-End-Dienst Ihrer Anwendung speichern und für alle Geräte verfügbar machen, die sich ordnungsgemäß authentifizieren können. Ein entsprechendes Beispiel finden Sie unter Tutorial: Freigeben von Raumankern für Geräte.

Instanziieren Sie ein AnchorLocateCriteria-Objekt, legen Sie die zu suchenden IDs fest, und rufen Sie die CreateWatcher-Methode für die Sitzung auf, indem Sie Ihr AnchorLocateCriteria-Objekt angeben.

Erfahren Sie mehr über die CreateWatcher-Methode.

    auto criteria = std::make_shared<AnchorLocateCriteria>();
    criteria->Identifiers({ R"(id1)", R"(id2)", R"(id3)" });
    auto cloudSpatialAnchorWatcher = cloudSession_->CreateWatcher(criteria);

Nachdem Ihr Watcher erstellt wurde, wird das AnchorLocated-Ereignis für jeden angeforderten Anker ausgelöst. Dieses Ereignis wird sogar dann ausgelöst, wenn ein Anker gefunden wird, oder der Anker nicht gefunden werden kann. Wenn dies geschieht, wird die Ursache im Status angegeben. Nachdem alle Anker für einen Watcher – ob gefunden oder nicht gefunden – verarbeitet wurden, wird das LocateAnchorsCompleted-Ereignis ausgelöst. Es besteht eine Beschränkung von 35 Bezeichner pro Watcher.

Erfahren Sie mehr über den AnchorLocated-Delegaten.

    auto anchorLocatedToken = cloudSession_->AnchorLocated([this](auto&&, auto&& args) {
        switch (args->Status()) {
            case LocateAnchorStatus::Located: {
                std::shared_ptr<CloudSpatialAnchor> foundAnchor = args->Anchor();
                // Go add your anchor to the scene...
            }
                break;
            case LocateAnchorStatus::AlreadyTracked:
                // This anchor has already been reported and is being tracked
                break;
            case LocateAnchorStatus::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 LocateAnchorStatus::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;
        }
    });

Löschen von Ankern

Das Löschen von Ankern, die nicht mehr verwendet werden, ist eine bewährte Methode, die Sie frühzeitig in Ihren Entwicklungsprozess und Ihre Praktiken einbeziehen sollten, damit Ihre Azure-Ressourcen bereinigt werden.

Erfahren Sie mehr über die DeleteAnchorAsync-Methode.

    cloudSession_->DeleteAnchorAsync(cloudAnchor, [this](Status status) {
        // Perform any processing you may want when delete finishes
    });

Anhalten, Zurücksetzen oder Beenden einer Sitzung

Um eine Sitzung vorübergehend anzuhalten, können Sie Stop() aufrufen. Damit werden alle Überwachungskomponenten und sämtliche Prozesse der Umgebungsverarbeitung angehalten, auch wenn Sie ProcessFrame() aufrufen. Danach können Sie Start() aufrufen, um die Verarbeitung fortzusetzen. Beim Fortsetzen werden alle in der Sitzung bereits erfassten Umgebungsdaten beibehalten.

Erfahren Sie mehr über die Stop-Methode.

    cloudSession_->Stop();

Um die Umgebungsdaten zurückzusetzen, die in der Sitzung erfasst wurden, können Sie Reset() aufrufen.

Erfahren Sie mehr über die Reset-Methode.

    cloudSession_->Reset();

Zur ordnungsgemäßen Bereinigung nach einer Sitzung geben Sie alle Verweise frei.

    cloudSession_ = nullptr;

Nächste Schritte

In dieser Anleitung haben Sie gelernt, wie Sie mit dem Azure Spatial Anchors SDK Anker erstellen und lokalisieren. Weitere Informationen zu Ankerbeziehungen finden Sie in der nächsten Anleitung.