Wie Yandex Augmented Reality in Maps für iOS erstellt hat. Erfahrung mit ARKit

Es gibt weniger Menschen, die von Augmented Reality (AR) überrascht werden können. Für einige ist diese Technologie für ein paar Stunden mit einem Spielzeug verbunden. Andere finden es praktischer.


Mein Name ist Dmitry und ich entwickle Yandex.Maps für iOS. Heute werde ich den Lesern von Habr erzählen, wie wir Routing mit Augmented Reality erstellt haben. Sie lernen auch die Funktionen der Verwendung des ARKit-Frameworks kennen, dank dessen die Einführung von Augmented Reality nicht mehr nur Fachleuten auf dem Gebiet der Computer Vision vorbehalten ist.



Im Jahr 2009 war das Esquire-Magazin das erste unter den Medien, das seinem Produkt Augmented-Reality-Unterstützung hinzufügte. Auf dem Cover des Magazins stand ein Code, mit dem man Robert Downey Jr. "live" sehen konnte.



Der Einsatz von AR in der Unterhaltungsindustrie war nicht darauf beschränkt. Ein anschauliches Beispiel war das 2016 veröffentlichte Spiel Pokemon Go. Bis Juli dieses Jahres wurde es über 16 Millionen Mal heruntergeladen. Der Erfolg des Spiels führte zur Entstehung zahlreicher Klone mit AR.


Wichtige Ereignisse in der AR-Branche in den letzten Jahren können als Ankündigungen von Google Glass und Microsoft Hololens angesehen werden. Das Erscheinungsbild solcher Geräte zeigt den Vektor, in dem sich große Unternehmen bewegen.


Apple war keine Ausnahme. 2017 führte das Unternehmen das ARKit-Framework ein, dessen Bedeutung für die Branche kaum zu überschätzen ist. Und wir werden ausführlicher darüber sprechen.


ARKit


Funktionen von ARKit, die die Verwendung von AR vereinfachen:


  • Mangel an speziellen Tags (Markern),
  • Integration in vorhandene Apple 2D / 3D-Grafik-Frameworks - SceneKit, SpriteKit, Metal,
  • hohe Genauigkeit bei der Bestimmung der Position und Ausrichtung des Geräts im Raum,
  • Kamera oder Sensoren müssen nicht kalibriert werden.

Unter der Haube von ARKit befindet sich ein visuelles Trägheits-Kilometerzählersystem, das Daten mit den visuellen (Kamera) und trägen (Beschleunigungsmesser, Gyroskop) Subsystemen des Geräts kombiniert, um Position und Verschiebung auf der Bühne zu bestimmen. Das Verbindungselement dieses Systems ist der Kalman-Filter - ein Algorithmus, der zu jedem Zeitpunkt die besten Messwerte der beiden Subsysteme auswählt und uns in Form unserer Position und Orientierung auf der Bühne zur Verfügung stellt. ARKit hat auch ein „Verständnis“ für die Szene - wir können horizontale und vertikale Flächen sowie die Lichtverhältnisse der Szene definieren. Wenn Sie der Szene ein Objekt hinzufügen, können Sie daher eine Standardbeleuchtung hinzufügen, wodurch das Objekt realistischer aussieht.


Übrigens

In Kürze wird die Framework-Version 2.0 veröffentlicht, in der neue Funktionen hinzugefügt und die Positionierungsgenauigkeit erheblich verbessert werden.


Mit ARKit konnten Entwickler hochwertige Augmented Reality in ihre Anwendungen einbetten und dabei viel weniger Aufwand betreiben. Wir werden dies am Beispiel von Yandex.Maps demonstrieren.


Routing mit AR auf Yandex.Maps


Normalerweise kommen nach der Ankündigung der neuen Version von iOS viele Teams bei Yandex zusammen, um die Möglichkeit der Einführung neuer Funktionen in ihre Anwendungen zu erörtern. Das Yandex.Mart-Team tat dasselbe. Innerhalb eines Monats nach der Ankündigung von ARKit haben wir häufig darüber gesprochen, wie es in Maps implementiert werden kann. Welche Ideen haben wir nicht voneinander gehört! Schnell kamen wir zu dem Schluss, dass eine der nützlichsten und oberflächlichsten Lösungen die Verwendung von Augmented Reality beim Routing ist.


Die Wahl dieser Idee war auf die Tatsache zurückzuführen, dass viele Kartenbenutzer häufig auf eine Situation stoßen, in der Sie sich in einem unbekannten Bereich befinden und schnell entscheiden müssen, wohin Sie gehen möchten. Der Standardansatz für den durchschnittlichen Kartenbenutzer besteht darin, die Anwendung zu öffnen, eine Fußgängerroute zu erstellen und an Ort und Stelle zu bestimmen, wohin er sich bewegen soll. Die Idee, Augmented Reality in das Fußgängerrouting einzuführen, besteht darin, den Benutzer vor unnötigen Aktionen zu bewahren und sofort zu zeigen, wo Sie sich direkt über dem Kamerabild bewegen müssen.


Zunächst möchte ich ein paar Worte zum Routing sagen. Was setze ich in dieses Konzept ein? Unter dem Gesichtspunkt der Implementierung in einer mobilen Anwendung ist dies eine ziemlich standardmäßige Reihe von Schritten, die es dem Benutzer ermöglichen, von Punkt A nach Punkt B zu gelangen:


  • Auswahl der Abfahrts- und Ankunftspunkte,
  • Empfangen einer Route in Form einer Reihe von Punkten in geografischen (Breiten-, Längen-) Koordinaten,
  • Anzeige auf der Karte der Routenlinie,
  • Begleitung des Benutzers mit zusätzlichen Informationen während der Fahrt entlang der Route.

Wir werden nicht auf die ersten beiden Punkte eingehen. Ich kann nur sagen, dass wir die Route über unsere plattformübergreifende Bibliothek Yandex.Mapkit erhalten, die Ihnen auch in Form eines Pods zur Verfügung steht. Wie unterscheidet sich Augmented Reality Routing vom Standard Routing in Karten? Der Hauptunterschied ist zunächst eine fast vollständig versteckte Karte. Der Schwerpunkt liegt auf dem Bildschirmbereich mit dem Bild des Videostreams von der Kamera, dem zusätzliche visuelle Elemente überlagert sind (Zielmarke, Hilfsmarke und Routenlinienbild). Jedes dieser visuellen Elemente hat seine eigene semantische Last und seine eigene Logik (wann und wie es angezeigt werden soll). Wir werden die Rolle jedes dieser Elemente später genauer betrachten, aber jetzt schlage ich vor, die Aufgaben zu betrachten, die wir vor uns hatten:


  • lernen, Objekte in der ARKit-Szene zu positionieren und ihre geografischen Koordinaten zu kennen,
  • Erfahren Sie, wie Sie die erforderliche Benutzeroberfläche in einer 3D-Szene mit ausreichender Leistung zeichnen.

Wir mussten die Koordinaten der Punkte von geografisch in die Koordinaten auf der Bühne konvertieren, auswählen, welche Punkte angezeigt werden sollen, und alle erforderlichen Benutzeroberflächen über dem Kamerabild an der richtigen Position anzeigen. Aber alles stellte sich als etwas komplizierter heraus, als es auf den ersten Blick schien.


Bevor ich mit der direkten Implementierung der Funktionen begann, wurde einem meiner Kollegen die Aufgabe übertragen, einen Prototyp zu erstellen, der die Möglichkeit (oder Unmöglichkeit) zeigt, ähnliche Funktionen mit einem zugänglichen Satz von Tools zu implementieren. Zwei Wochen lang beobachteten wir, wie San Sanych mit einem Telefon in der Hand die Freiflächen des Freiraums und die nahe Umgebung unseres Büros pflügte und die Welt um uns herum durch das Prisma der Kamera betrachtete. Als Ergebnis haben wir einen funktionierenden Prototyp erhalten, der jeden Punkt der Route als Markierung auf der Bühne mit einem Abstand dazu zeigt. Mit Hilfe dieses Prototyps war es unter einer erfolgreichen Kombination von Umständen möglich, von der Arbeit zur U-Bahn zu gelangen und fast nie verloren zu gehen. Im Ernst, er bestätigte die Möglichkeit, die beabsichtigte Funktionalität zu implementieren. Es gab jedoch noch eine Reihe von Aufgaben, die unser Team noch lösen musste.


Alles begann mit dem Studium der Werkzeuge. Zu diesem Zeitpunkt hatte nur eine Person im Team Erfahrung mit 3D-Grafiken. Werfen wir einen kurzen Blick auf die Tools, mit denen sich jeder befassen muss, der über die Implementierung solcher Ideen mit ARKit nachdenkt.


Tools und APIs


Die Hauptaufgabe beim Rendern von Objekten besteht darin, Szenenobjekte für das SceneKit-Framework zu erstellen und zu verwalten. Mit dem Aufkommen von ARKit wurde dem Entwickler die ARSCNView-Klasse (der Nachkomme der SCNView-Klasse - die Basisklasse für die Arbeit mit der Szene in SceneKit) verfügbar, die die meisten zeitaufwändigen Aufgaben der Integration von ARKit und SceneKit löst, nämlich:


  • Synchronisation der Position des Telefons im Raum mit der Position der Kamera auf der Bühne,
  • Das Koordinatensystem der Szene stimmt mit dem Koordinatensystem ARKit überein.
  • Als Hintergrund der Szene wird der Videostream von der Kamera des Geräts verwendet.

Das ARSCNView-Objekt stellt dem Entwickler auch ein Augmented-Reality-Sitzungsobjekt zur Verfügung, das mit der erforderlichen Konfiguration gestartet, gestoppt oder mit dem Delegate-Objekt für verschiedene Ereignisse abonniert werden kann.


Um der Szene Objekte hinzuzufügen, werden Erben oder direkt SCNNode-Objekte verwendet. Diese Klasse repräsentiert eine Position (dreidimensionaler Vektor) im Koordinatensystem ihres übergeordneten Elements. Auf diese Weise erhalten wir einen Baum von Objekten in der Szene mit einer Wurzel in einem speziellen Objekt - dem Wurzelknoten unserer Szene. Alles hier ist der Hierarchie der UIView-Objekte in UIKit sehr ähnlich. SCNNode-Objekte können auf der Bühne angezeigt werden, wenn sie Material und Beleuchtung hinzufügen.


Um einer mobilen Anwendung Augmented Reality hinzuzufügen, müssen Sie auch die Hauptobjekte der ARKit-API kennen. Das wichtigste ist das Objekt der Augmented Reality-Sitzung - ARSession. Dieses Objekt führt die Datenverarbeitung durch und ist für den Lebenszyklus der Augmented Reality-Sitzung verantwortlich. Der Zweck dieses Artikels besteht nicht darin, die Dokumentation von ARKit und SceneKit erneut zu erzählen. Daher werde ich nicht über alle verfügbaren Konfigurationsparameter der Augmented Reality-Sitzung schreiben, sondern mich auf einen der wichtigsten Parameter der Konfiguration der Augmented Reality-Sitzung für Navigationsanwendungen konzentrieren - worldAlignment. Dieser Parameter bestimmt die Richtung der Achsen der Szene zum Zeitpunkt der Initialisierung der Sitzung. Im Allgemeinen erstellt ARKit beim Initialisieren einer Augmented-Reality-Sitzung ein Koordinatensystem mit einem Anfang an einem Punkt, der mit der aktuellen Position des Telefons im Raum übereinstimmt, und lenkt die Achse dieses Systems abhängig vom Wert der Eigenschaft woldAlignment. In unserer Implementierung wird der SchwerkraftAndHeading-Wert verwendet, was bedeutet, dass die Achsen wie folgt ausgerichtet sind: die Y-Achse - entgegen der Schwerkraftrichtung, die Z-Achse - nach Süden und die X-Achse - nach Osten.


Weltausrichtung-Schwerkraft und Überschrift


Bei einer guten Kombination von Umständen werden die X / Z-Achsen zwar mit den Richtungen nach Süd / Ost ausgerichtet, aber aufgrund von Fehlern bei den Kompassablesungen können die Achsen in einem bestimmten Winkel zu der in der Dokumentation beschriebenen Richtung ausgerichtet werden. Dies ist eines der Probleme, mit denen wir uns befassen mussten, aber dazu später mehr.


Nachdem wir die grundlegenden Tools untersucht haben, fassen wir zusammen: Beim Zuordnen einer Route mit SceneKit werden der Szene SCNNode-Objekte an den Positionen hinzugefügt, die durch Konvertieren von geografischen Koordinaten in Szenenkoordinaten erhalten werden. Bevor wir über die Koordinatenkonvertierung und allgemein über das Platzieren von Objekten in der Szene sprechen, wollen wir uns mit den Problemen beim Rendern von UI-Elementen befassen, vorausgesetzt, wir kennen die Position der Objekte auf der Bühne.


Zielmarke


Das visuelle Hauptelement des Fußgängerroutings mit Augmented Reality ist die Zielmarke, die den Endpunkt der Route anzeigt. Ebenfalls über der Markierung zeigen wir dem Benutzer die Entfernung zum Endpunkt der Route.


Ziel-Ortsmarken-Übersicht


Größe


Als uns das Design dieses Tags zum ersten Mal gezeigt wurde, haben wir zuerst die Anforderungen für die Größe dieses Tags berücksichtigt. Sie haben die Regeln der perspektivischen Projektion nicht befolgt. Ich werde erklären, dass in den dreidimensionalen Engines, die zum Erstellen von Computerspielen verwendet werden, der „Look“ mithilfe einer perspektivischen Projektion modelliert wird. Nach den Regeln der perspektivischen Projektion werden entfernte Objekte in kleinerem Maßstab dargestellt, und parallele Linien sind im Allgemeinen nicht parallel. Somit ändert sich die Projektionsgröße des Objekts auf der Bildschirmebene linear (nimmt ab), wenn sich die Kamera vom Objekt in der Szene entfernt. Aus der Beschreibung der Layouts folgt, dass die Größe der Markierung auf dem Bildschirm eine feste (maximale) Größe hat, wenn sie weniger als 50 m entfernt wird, und dann linear von 50 m auf 2 km abnimmt, wonach die minimale Größe unverändert bleibt. Solche Anforderungen sind offensichtlich auf die Benutzerfreundlichkeit zurückzuführen. Sie ermöglichen es dem Benutzer, den Endpunkt der Route niemals aus der Ansicht zu verlieren, sodass der Benutzer immer eine Vorstellung davon hat, wohin er sich bewegen soll.


Finish-ortsmarke-Besitze-anforderungen


Wir mussten verstehen, wie wir uns in den SceneKit-Projektionsmechanismus einklemmen konnten, der nach bestimmten Regeln funktionierte. Ich möchte sofort darauf hinweisen, dass wir ungefähr zwei Wochen Zeit hatten, um alles zu erledigen, sodass einfach keine Zeit blieb, eine eingehende Analyse verschiedener Ansätze zur Lösung der gestellten Probleme durchzuführen. Jetzt ist es viel einfacher, unsere Entscheidungen zu analysieren und zu bewerten, und wir können daraus schließen, dass die meisten getroffenen Entscheidungen richtig waren. Das Erfordernis der Größe war tatsächlich der erste Stolperstein. Alle unten beschriebenen Probleme können sowohl mit SceneKit als auch mit UIKit gelöst werden. Ich habe versucht, detailliert zu erklären, wie jedes der Probleme mit beiden Ansätzen gelöst werden kann. Welchen Ansatz Sie verwenden, liegt bei Ihnen.


Stellen wir uns vor, wir haben beschlossen, ein Finish-Label mit SceneKit zu implementieren. Wenn wir berücksichtigen, dass die Beschriftung gemäß den Layouts wie ein Kreis auf dem Bildschirm aussehen sollte, wird klar, dass das Beschriftungsobjekt in SceneKit eine Kugel sein sollte (da die Projektion der Kugel auf eine beliebige Ebene ein Kreis ist). Damit die Projektion einen bestimmten Radius auf dem Bildschirm hat, der in den Anforderungen der Designer definiert ist, muss der Radius der Kugel zu jedem Zeitpunkt bekannt sein. Wenn Sie also eine Kugel mit einem bestimmten Radius an einem bestimmten Punkt auf der Szene platzieren und ihren Radius ständig aktualisieren, wenn Sie sich nähern oder wegbewegen, erhalten Sie jederzeit eine Projektion auf die Leinwand mit der erforderlichen Größe. Der Algorithmus zum Bestimmen des Radius der Kugel zu einem beliebigen Zeitpunkt lautet wie folgt:


  1. Definieren Sie die Position des Objekts auf der Bühne - den Mittelpunkt der Kugel.
  2. Suchen Sie die Projektion dieses Punkts auf der Bildschirmebene (mithilfe der SceneKit-API).
  3. Um die erforderliche Größe der Markierung auf dem Bildschirm zu bestimmen, ermitteln wir den Abstand von der Kamera zum Mittelpunkt der Kugel auf der Bühne.
  4. Wir bestimmen die erforderliche Größe auf dem Bildschirm anhand des Abstands zum Objekt anhand der im Entwurf beschriebenen Regeln.
  5. Wenn wir die Größe der Markierung auf dem Bildschirm (Durchmesser des Kreises) kennen, wählen wir einen beliebigen Punkt auf diesem Kreis.
  6. Machen Sie die umgekehrte Projektion (unprojectPoint) des ausgewählten Punktes.
  7. Wir finden die Länge des Vektors vom empfangenen Punkt auf der Bühne bis zum Mittelpunkt der Kugel.

Der erhaltene Wert der Länge des Vektors ist der gewünschte Radius der Kugel.


Finish-Ortsmarke-Größe-Lösung-Szenekit


Zum Zeitpunkt der Implementierung war es uns nicht möglich, die Größe des Objekts in der Szene zu bestimmen, und wir haben beschlossen, die Zielmarke mit UIKit zu zeichnen. In diesem Fall wiederholt der Algorithmus die Schritte 1 bis 5, wonach ein Kreis der gewünschten Größe mit den UIKit-Werkzeugen auf dem Bildschirm mit der Mitte an dem in Schritt 2 erhaltenen Punkt gezeichnet wird. Eine beispielhafte Implementierung eines Labels mit UIKit finden Sie hier .


Ein paar Worte zum Code

Am Ende des Artikels habe ich mehrere Links zu nützlichen und einfach interessanten Materialien gegeben, einschließlich Beispielen, in denen Sie den realen Code, der die im Artikel vorgestellten Probleme löst und die vorgestellten Algorithmen implementiert, detailliert betrachten können. Meiner Meinung nach ist das Hauptinteresse der Prototyp der Fußgängerführung , der alle Funktionen zusammenbringt, mit Ausnahme des Achseneinstellmechanismus, der nachstehend ausführlich beschrieben wird.


Der obige Code erhebt keinen Anspruch auf Optimalität, Vollständigkeit und Produktionsqualität =)


Der Unterschied zwischen der Verwendung von SceneKit und UIKit in diesem Fall besteht auch darin, dass bei der Implementierung in SceneKit das SCNNode-Objekt für den Routenendpunkt (Zielmarke) mit Material und Geometrie erstellt wird, da es bei Verwendung von UIKit sichtbar sein muss Wir benötigen das Knotenobjekt ausschließlich, um nach der Projektion auf die Bildschirmebene zu suchen (um die Mitte der Markierung auf dem Bildschirm zu bestimmen). In diesem Fall müssen Geometrie und Material nicht hinzugefügt werden. Beachten Sie, dass der Abstand zwischen der Kamera und dem SCNNode-Objekt des Endpunkts der Route auf zwei Arten ermittelt werden kann: anhand der geografischen Koordinaten der Punkte oder als Länge des Vektors zwischen den Punkten in der Szene. Dies ist möglich, weil das Kameraobjekt eine SCNNode-Eigenschaft ist. Um den Kameraknoten zu erhalten, müssen Sie auf die pointOfView-Eigenschaft unserer Szene verweisen.


Wir haben gelernt, wie der Radius des Zielmarkierungsknotens zu einem beliebigen Zeitpunkt bei der Implementierung in SceneKit und die Position der Ansicht der Zielmarke bei Implementierung in UIKit bestimmt werden. Es bleibt zu verstehen, wann es notwendig ist, diese Werte zu aktualisieren? Dieser Ort ist die SCNSceneRendererDelegate-Objektmethode:


renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) 

Diese Methode wird nach jedem gerenderten Szenenbild aufgerufen. Durch Aktualisieren der Eigenschaftswerte im Hauptteil dieser Methode erhalten wir eine korrekt angezeigte Endbeschriftung.


Animation


Nachdem die Zielmarke in dev angezeigt wurde, fügten wir dieser Marke eine Wellenanimation hinzu. Ich denke, für die meisten iOS-Entwickler ist das Erstellen von Animationen keine große Sache. Beim Nachdenken über die Implementierungsmethode stießen wir jedoch auf das Problem, den Rahmen unserer Ansicht ständig zu aktualisieren. Beachten Sie, dass in den meisten Fällen Animationen zu statischen UIView-Objekten hinzugefügt werden. Ein ähnliches Problem - eine ständige Aktualisierung des Radius der Knotengeometrie tritt bei der Implementierung mit SceneKit auf. Tatsache ist, dass die pulsierende Animation auf die Animation der Größe des Kreises (für UIKit) und des Radius der Kugel (für SceneKit) hinausläuft. Ja, ja, wir wissen, dass diese Art von Animation in UIKit mit CALayer ausgeführt werden kann. Zur Vereinfachung des Geschichtenerzählens habe ich mich jedoch entschlossen, dieses Problem für zwei Frameworks symmetrisch zu betrachten. Betrachten Sie eine Implementierung auf UIKit. Wenn Sie dem vorhandenen Code, der den Ansichtsrahmen aktualisiert, Code hinzufügen, der denselben Frame animiert, wird die Animation durch explizites Festlegen des Frames unterbrochen. Als Lösung für dieses Problem haben wir uns daher entschlossen, die Animation der Eigenschaft transform.scale.xy des UIView-Objekts zu verwenden. Bei der Implementierung mit SceneKit müssen Sie dem SCNNode-Objekt eine Animation der scale-Eigenschaft hinzufügen. Das Schöne an der Verwendung von SceneKit in diesem Fall ist die Tatsache, dass es CoreAnimation vollständig unterstützt, sodass das Erlernen einer neuen API nicht erforderlich ist. Der Code, der eine Animation ähnlich der Beschriftungsanimation in Yandex.Maps implementiert, sieht ungefähr so ​​aus:


 let animationGroup = CAAnimationGroup.init() animationGroup.duration = 1.0 animationGroup.repeatCount = .infinity let opacityAnimation = CABasicAnimation(keyPath: "opacity") opacityAnimation.fromValue = NSNumber(value: 1.0) opacityAnimation.toValue = NSNumber(value: 0.1) let scaleAnimation = CABasicAnimation(keyPath: "scale") scaleAnimation.fromValue = NSValue(scnVector3: SCNVector3(1.0, 1.0, 1.0)) scaleAnimation.toValue = NSValue(scnVector3: SCNVector3(1.2, 1.2, 1.2)) animationGroup.animations = [opacityAnimation, scaleAnimation] finishNode.addAnimation(animationGroup, forKey: "animations") 

Plakatwand


Am Anfang des Artikels erwähnte ich eine Werbetafel mit einem Abstand zum Endpunkt der Route, die im Wesentlichen ein Etikett mit Text ist, der sich immer über der Zielmarke befindet. Aus Tradition werde ich die Probleme skizzieren, die mit Implementierungen auf UIKit und SceneKit verbunden sind, und mögliche Lösungen für jedes der Frameworks erläutern.


Beginnen wir mit UIKit. In diesem Fall handelt es sich bei der Werbetafel um ein reguläres UILabel, bei dem der Text ständig aktualisiert wird und die Entfernung zum Endpunkt der Route anzeigt. Schauen wir uns das Problem an, mit dem wir konfrontiert sind.


Finish-Ortsmarke-Plakat-Problem-Uikit


Wenn Sie einem Rahmen eine Beschriftung zuweisen und dann das Telefon drehen, werden wir feststellen, dass sich der Rahmen nicht ändert (es wäre seltsam, wenn dies nicht der Fall wäre). Gleichzeitig möchten wir, dass das Etikett parallel zur Erdebene bleibt.


Finish-Ortsmarke-Plakat-Wunsch-Uikit


Ich denke, jeder versteht, dass wir beim Ändern der Ausrichtung des Geräts das Etikett drehen müssen, aber in welchem ​​Winkel? Wenn Sie die Imagination einschalten und sich alle Achsen der an diesem Prozess beteiligten Koordinatensysteme und Vektoren vorstellen, können wir schließen, dass der Drehwinkel gleich dem Winkel zwischen der x-Achse des UIKit-Koordinatensystems und der Projektion der X-Achse des SceneKit-Koordinatensystems auf die Bildschirmebene ist.


Finish-Ortsmarke-Plakat-Lösung-Uikit


Eine einfache Aufgabe, die erneut die Nützlichkeit des Schulgeometriekurses unter Beweis stellte.


Wenn Sie die Zielmarke mit SceneKit implementieren, müssen Sie die Werbetafel höchstwahrscheinlich mit SceneKit-Werkzeugen mit Abstand rendern. Dies bedeutet, dass Sie definitiv die Aufgabe haben, das SCNNode-Objekt immer auf die Kamera auszurichten. Ich denke, das Problem wird klarer, wenn Sie sich das Bild ansehen:


Finish-Placemark-Billboard-Problem-Scenekit


Dieses Problem wird mithilfe der SCNBillboardConstraint-API gelöst. Wenn wir der Sammlung von Konstraten unseres Knotens eine Konstante mit einer freien Achse Y hinzufügen, erhalten wir einen Knoten, der sich um die Y-Achse seines Koordinatensystems dreht, um immer zur Kamera ausgerichtet zu sein. Die einzige Aufgabe des Entwicklers besteht darin, diesen Knoten auf der richtigen Höhe zu platzieren, damit die Werbetafel mit dem Abstand für den Benutzer immer sichtbar ist.


 let billboardConstraint = SCNBillboardConstraint() billboardConstraint.freeAxes = SCNBillboardAxis.Y finishNode.constraints = [billboardConstraint] 

Assistent Tag


Als eines der Hauptmerkmale des Fußgängerroutings mit Augmented Reality betrachten wir innerhalb des Teams ein Zusatzetikett - ein spezielles visuelles Element, das auf dem Bildschirm angezeigt wird, wenn der Endpunkt der Route die Sichtbarkeitszone verlässt und dem Benutzer zeigt, wo das Telefon gedreht werden muss, damit das Etikett auf dem Bildschirm angezeigt wird Ziellinie.


Finish-Placemark-Hinweis-Übersicht


Ich bin sicher, dass viele der Leser in einigen Spielen auf ähnliche Funktionen gestoßen sind, meistens auf Schützen. Was für eine Überraschung unser Team war, als wir dieses UI-Element in den Layouts sahen. Ich muss sofort sagen, dass die korrekte Implementierung einer solchen Funktion möglicherweise mehr als eine Stunde Experimentieren erfordert, aber das Endergebnis ist die aufgewendete Zeit wert. Wir haben zunächst Anforderungen definiert, nämlich:


  • Bei jeder Ausrichtung des Geräts bewegt sich das Etikett entlang der Bildschirmränder.
  • Wenn sich der Benutzer um 180 Grad zum Endpunkt der Route gedreht hat, wird die Beschriftung am unteren Bildschirmrand angezeigt.
  • Zu jedem Zeitpunkt sollte das Abbiegen in Richtung der Markierung die kürzeste Abbiegung zum Endpunkt der Route sein.

Nachdem wir die Anforderungen beschrieben hatten, begannen wir mit der Implementierung. Fast sofort kamen wir zu dem Schluss, dass das Rendern mit UIKit erfolgen würde. Das Hauptproblem bei der Implementierung war die Bestimmung des Zentrums dieses Etiketts zu jedem Zeitpunkt. Nach Prüfung der Zielmarke sollte eine solche Aufgabe keine Schwierigkeiten verursachen, daher werde ich nicht näher auf ihre Lösung eingehen. In dem Artikel werde ich nur eine Beschreibung des Algorithmus zur Auswahl der Mitte des Hilfsetiketts geben, und den Quellcode finden Sie hier .


Suchzentrum-Algorithmus Suchalgorithmus:


  1. Erstellen Sie ein SCNNode-Objekt für den Routenendpunkt mit einer Position in der Szene, die sich aus der geografischen Koordinate des Punkts ergibt.
  2. Finden Sie die Projektion eines Punktes auf der Bildschirmebene.
  3. Finden Sie den Schnittpunkt des Segments von der Mitte des Bildschirms bis zum Punkt der gefundenen Projektion mit den Segmenten der Grenzen des Bildschirms im Koordinatensystem des Bildschirms.

Finish-Placemark-Hinweis-Lösung


Der gefundene Schnittpunkt ist das gewünschte Zentrum der Hilfsmarke. In Analogie zu dem Code, der die Endetikettenparameter aktualisiert, haben wir den Code, der das Hilfsetikett rendert, in die oben bereits erwähnte Delegate-Methode eingefügt.


Polylinie weiterleiten


Nachdem der Benutzer eine Route erstellt und die Zielmarke auf dem Bildschirm gesehen hat, kann er sie erreichen, indem er sich nur in Richtung der Markierung orientiert. Das Routing wird jedoch so genannt, weil es dem Benutzer die Route anzeigt. Wir dachten, dass es sehr seltsam wäre, die Funktionalität des Fußgängerroutings zu reduzieren, ohne die Routenanzeige von der AR-Version auszuschließen. Um die Routenlinie zu visualisieren, wurde beschlossen, eine Reihe von Pfeilen anzuzeigen, die sich entlang der Route bewegen. In diesem Fall waren die Designer davon überzeugt, dass die Pfeile beim Wegbewegen praktisch verschwinden würden (die Größe würde durch die Regeln der perspektivischen Projektion bestimmt), und es wurde beschlossen, SceneKit für die Implementierung zu verwenden.


Bevor Sie mit der Beschreibung der Implementierung fortfahren, ist zu beachten, dass die Pfeile konstruktionsbedingt einen Abstand von 3 m voneinander haben sollten. Wenn Sie die Anzahl der Objekte (Pfeile) schätzen, die mit einer ungefähr 1 km langen Route gerendert werden müssen, sind es ungefähr 330 Teile. Gleichzeitig wird jedem Objekt eine Bewegungsanimation entlang seines Streckenabschnitts hinzugefügt. Beachten Sie, dass die Pfeile, die in einem Abstand von etwa 100 bis 150 Metern von der Position der Kamera auf der Bühne entfernt sind, aufgrund ihrer geringen Größe praktisch unsichtbar sind. Unter Berücksichtigung dieser Faktoren wurde beschlossen, nicht alle Objekte anzuzeigen, sondern nur diejenigen, die nicht mehr als 100 Meter entlang der Routenlinie vom Benutzer entfernt wurden, und den angezeigten Satz von Objekten regelmäßig zu aktualisieren. Wir zeigen eine ausreichende Menge an visuellen Informationen an, wodurch unnötige SceneKit-Berechnungen vermieden und der Akku des Benutzers geschont werden.


Route-Polylinien-Übersicht


Schauen wir uns die wichtigsten Schritte an, die wir ausführen mussten, um das Endergebnis zu erhalten:


  • Auswahl des Routenabschnitts, für den die Grundelemente angezeigt werden sollen,
  • Erstellung von 3D-Modellen,
  • Animationserstellung
  • Update beim Fahren entlang einer Route.

Auswählen eines anzuzeigenden Diagramms


Wie oben erwähnt, werden keine Pfeile für die gesamte Route angezeigt, sondern der optimale Abschnitt für die Anzeige ausgewählt. Die Auswahl eines Segments zu einem beliebigen Zeitpunkt besteht darin, das nächstgelegene Routensegment (die Route ist eine Folge von Segmenten / Segmenten) zur aktuellen Position des Benutzers zu finden und Segmente vom nächstgelegenen zum Endpunkt der Route auszuwählen, bis ihre Gesamtlänge 100 Meter überschreitet.


route-polyline-route-part-auswahl


3D-Modellerstellung


Lassen Sie uns den Prozess der Erstellung eines 3D-Modells genauer betrachten. In den meisten Fällen müssen Sie zum Erstellen eines einfachen 3D-Modells (wie unserem Pfeil) lediglich einen beliebigen 3D-Editor öffnen, einige Zeit damit verbringen, ihn zu beherrschen und dieses Modell darin zu erstellen. Wenn die Jungs aus Ihrem Team Erfahrung in der 3D-Modellierung haben oder Zeit haben, um beispielsweise 3DMax zu lernen (und es muss gekauft werden), dann haben Sie unglaublich viel Glück. Leider hatte zum Zeitpunkt der Implementierung dieser Funktion keiner von uns besondere Erfahrungen, es gab keine freie Zeit für Schulungen, so dass wir sozusagen mit improvisierten Mitteln ein Modell erstellen mussten. Ich meine die Beschreibung des Modells im Code. Alles begann mit der Präsentation eines 3D-Modells in Form von Dreiecken. Dann mussten wir die Koordinaten der Eckpunkte dieser Dreiecke manuell im Koordinatensystem des Modells finden und dann ein Array von Indizes der Eckpunkte der Dreiecke erstellen. Mit diesen Daten können wir die erforderliche Geometrie direkt in SceneKit erstellen. Sie können beispielsweise ein ähnliches Modell wie das unsere erstellen:


 class ARSCNArrowGeometry: SCNGeometry { convenience init(material: SCNMaterial) { let vertices: [SCNVector3] = [ SCNVector3Make(-0.02, 0.00, 0.00), // 0 SCNVector3Make(-0.02, 0.50, -0.33), // 1 SCNVector3Make(-0.10, 0.44, -0.50), // 2 SCNVector3Make(-0.22, 0.00, -0.39), // 3 SCNVector3Make(-0.10, -0.44, -0.50), // 4 SCNVector3Make(-0.02, -0.50, -0.33), // 5 SCNVector3Make( 0.02, 0.00, 0.00), // 6 SCNVector3Make( 0.02, 0.50, -0.33), // 7 SCNVector3Make( 0.10, 0.44, -0.50), // 8 SCNVector3Make( 0.22, 0.00, -0.39), // 9 SCNVector3Make( 0.10, -0.44, -0.50), // 10 SCNVector3Make( 0.02, -0.50, -0.33), // 11 ] let sources: [SCNGeometrySource] = [SCNGeometrySource(vertices: vertices)] let indices: [Int32] = [0,3,5, 3,4,5, 1,2,3, 0,1,3, 10,9,11, 6,11,9, 6,9,7, 9,8,7, 6,5,11, 6,0,5, 6,1,0, 6,7,1, 11,5,4, 11,4,10, 9,4,3, 9,10,4, 9,3,2, 9,2,8, 8,2,1, 8,1,7] let geometryElements = [SCNGeometryElement(indices: indices, primitiveType: .triangles)] self.init(sources: sources, elements: geometryElements) self.materials = [material] } } static func arrowBlue() -> SCNGeometry { let material = SCNMaterial() material.diffuse.contents = UIColor.blue material.lightingModel = .constant return ARSCNArrowGeometry(material: material) } 

Das Endergebnis sieht folgendermaßen aus:


Route-Polylinien-Pfeil-Modell


Routenlinienanimation


Der nächste Schritt auf dem Weg zur Anzeige einer animierten Linie der Route war die Erstellung der Animation selbst. Aber wie kann die Animation realisiert werden, die in der endgültigen Form so aussieht, als ob der Pfeil seine Bewegung am Startpunkt des ausgewählten Abschnitts der Route beginnt und entlang der Route bis zum Ende dieses Abschnitts „schwebt“?



Ich werde nicht alle möglichen Wege beschreiben, um eine solche Animation zu erstellen, sondern ich werde näher auf die von uns gewählte Methode eingehen. Nachdem ein Abschnitt der Route ausgewählt wurde, teilen wir ihn in Abschnitte gleicher Länge auf - Abschnitte der Animation eines Pfeils. Jeder dieser Abschnitte ist farblich hervorgehoben und hat eine Länge, die dem Abstand zwischen den Pfeilen entspricht.


route-polyline-route-part-partitioning


Zu Beginn jedes Abschnitts erstellen wir das SCNNode-Objekt des Pfeils, dessen Animation darin besteht, sich entlang seines Abschnitts zu bewegen.


Route-Polylinien-Pfeile-Anfangsposition


Wie Sie sehen können, besteht der Animationsabschnitt manchmal aus einem Segment, manchmal aus zwei oder mehr. Es hängt alles von der Stufe (in unserem Fall - 3 Meter) zwischen den Pfeilen und den Koordinaten der Punkte ab, aus denen die Route besteht.


Eine Pfeilanimation besteht aus zwei Schritten:


  • Aussehen in der Ausgangsposition mit dem Anfangsdrehwinkel,
  • eine Folge von Offsets entlang von Segmenten mit Rotationen an den Verbindungspunkten der Segmente.

Schematisch sieht es so aus:


Route-Polylinien-Pfeil-Anitaion-Schritte


Es schien uns der einfachste Weg zu sein, eine solche Animation mithilfe der SCNAction-API zu implementieren - einer deklarativen API, mit der Sie bequem sequentielle, gruppierte und sich wiederholende Animationen erstellen können. Sie können sich die Implementierung hier genauer ansehen. Aufgrund der Tatsache, dass jeder Pfeil seine Animation am Startpunkt des Animationsabschnitts des nächsten Pfeils beendet, wird der Eindruck einer kontinuierlichen Bewegung des Pfeils entlang des gesamten ausgewählten Abschnitts der Route erzeugt.


In diesem Zusammenhang schlage ich vor, die Betrachtung verschiedener Aspekte des Renderns abzuschließen und zum Hauptteil überzugehen - der Bestimmung der Positionen von Objekten auf der Bühne anhand der geografischen Koordinaten der Objekte.


Bestimmen der Position eines Objekts in der Szene


Wir beginnen das Gespräch über die Bestimmung der Position eines Objekts in der Szene unter Berücksichtigung von Koordinatensystemen, deren Konvertierung erfolgen muss. Es gibt nur 2 davon:


  • geodätische (oder der Einfachheit halber geografische) Koordinaten - die Position von Objekten (Routenpunkten) in der realen Welt,
  • Kartesische Koordinaten - die Position von Objekten in der Szene (in ARKit). Denken Sie daran, dass das Koordinatensystem der Szene mit dem Koordinatensystem ARKit übereinstimmt (bei Verwendung von ARSCNView).

Die Übersetzung von einem Koordinatensystem in ein anderes und umgekehrt ist möglich, da die Koordinaten in ARKit in Metern gemessen werden und der Versatz zwischen zwei geodätischen Koordinaten mit großer Genauigkeit in den Versatz in Metern entlang der X- und Z-Achse des ARKit-Koordinatensystems bei kleinen Versätzen übersetzt werden kann. Ich möchte Sie daran erinnern, dass geodätische Koordinaten Punkte mit einem bestimmten Längen- und Breitengrad sind.


Erinnern wir uns an so wichtige Konzepte aus dem Verlauf der Geographie wie Parallelen und Meridiane und ihre grundlegenden Eigenschaften:


  • Parallel ist eine Linie mit einem Breitengrad. Die Längen der verschiedenen Parallelen sind unterschiedlich.
  • Meridian - eine Linie mit einem Längengrad. Die Längen aller Meridiane sind gleich.

Nun wollen wir sehen, wie Sie den Versatz in Metern zwischen zwei geodätischen Koordinaten mit Koordinaten berechnen können \ inline (lat_1, lon_1) und \ inline (lat_2, lon_2) ::


\ Delta x = \ Delta lon \ times meterInLonDegree (lat_ {0}) , \ Delta z = \ Delta lat \ times meterInLatDegree


meterInLonDegree (\ alpha) = \ frac {2 \ pi R_ \ text {landet} \ cos \ left (\ alpha \ right)} {360 ^ {°}} , meterInLatDegree = \ frac {2 \ pi R_ \ text {Lands}} {360 ^ {°}}


Erklärung

Die Verschiebung in geodätischen Koordinaten wird nur bei kleinen Verschiebungen linear auf Meter abgebildet. Bei großen Verschiebungen ist es notwendig, das Integral ehrlich zu nehmen.


Nachdem wir den Versatz von einem Koordinatensystem in ein anderes verschieben können, müssen wir uns für einen Referenzpunkt entscheiden - einen Punkt, für den die geografische Koordinate und die Koordinate in ARKit (Koordinate auf der Bühne) gleichzeitig bekannt sind. Nachdem wir einen solchen Punkt gefunden haben, können wir die Koordinate jedes Objekts auf der Bühne bestimmen, seine geografische Koordinate kennen und die obigen Formeln verwenden.


Betrachten Sie zur Verdeutlichung ein Beispiel:
Zu Beginn der Augmented Reality-Sitzung haben wir CoreLocation nach unserer geografischen Koordinate gefragt und diese sofort erhalten - \ inline (lat_0, lon_0) . Unter Hinweis darauf, dass der Ursprung des ARKit-Koordinatensystems zu Beginn der Sitzung an dem Punkt liegt, an dem sich das Gerät befindet, haben wir den Referenzpunkt erhalten, da wir die geografische Koordinate und die Koordinate in der Szene kennen \ inline (x_0, y_0, z_0) = (0,0,0) . Lassen Sie uns die Koordinate auf der Szene des Objekts mit einer geografischen Koordinate finden \ inline (lat_1, lon_1) . Suchen Sie dazu den Versatz in Metern zwischen der geografischen Koordinate des Objekts und der geografischen Koordinate unseres Referenzpunkts und fügen Sie den gefundenen Versatz zur Koordinate in der Szene des Referenzpunkts hinzu. Die resultierende Koordinate in der Szene ist die gewünschte.


Koordinaten-Konvertierungs-Objekt-Position-auf-Szene


Ich stelle fest, dass die auf diese Weise gefundene Position auf der Szene nur dann der Position des Objekts in der realen Welt entspricht, wenn die X / Z-Achse des Szenenkoordinatensystems mit den Richtungen nach Süd / Ost ausgerichtet ist. Die Achsenausrichtung sollte theoretisch erreicht werden, indem das Flag worldAlignment auf gravitiAndHeading gesetzt wird. Aber wie ich zu Beginn des Beitrags sagte, ist dies bei weitem nicht immer der Fall.


Betrachten wir die Methode zur Bestimmung des Referenzpunkts genauer. Zu diesem Zweck führen wir das Konzept der Schätzung ein - eine Reihe von geografischen Koordinaten und Koordinaten auf der Bühne.


Koordinaten-Umrechnung-Schätzung-Definition


Das oben vorgeschlagene Verfahren zur Bestimmung des Referenzpunktes wird möglicherweise nicht immer verwendet. Zum Zeitpunkt des Starts einer Augmented-Reality-Sitzung kann eine Anforderung zur CLLocation eines Benutzers nicht sofort ausgeführt werden, außerdem kann die Genauigkeit der erhaltenen Koordinate einen großen Fehler aufweisen. Es wäre richtiger, SceneKit nach einer Position auf der Bühne zu fragen, wenn wir den Wert von CoreLocation erhalten. In diesem Fall werden die Komponenten der resultierenden Schätzung tatsächlich gleichzeitig erhalten, und wir haben die Möglichkeit, eine der Schätzungen als Referenzpunkt zu verwenden. Bei der Arbeit mit ARKit steigt der Offset-Fehler mit der Zeit an. Apple empfiehlt daher nicht, ARKit als Navigationswerkzeug zu verwenden.


Als wir beschlossen, Fußgängerrouting mit Augmented Reality zu implementieren, haben wir ein wenig nach den damals existierenden Lösungen gesucht und ARKit für ähnliche Aufgaben verwendet. Dabei sind wir auf das ARKit + CoreLocation-Framework gestoßen. Die Idee dieses Frameworks war, dass wir dank ARKit den Standort des Benutzers genauer bestimmen können als wenn wir ausschließlich CoreLocation verwenden.


ARKit + CoreLocation-Konzept:


  • beim Empfang von CLLocation von CLLocationManager
    • Fordern Sie mit scene.pointOfView.worldPosition eine Position in der Szene an
    • Speichern Sie dieses Koordinatenpaar (Schätzung) im Puffer
  • Holen Sie sich bei Bedarf den genauen Standort
    • Wählen Sie die beste Schätzung
    • Berechnen Sie den Versatz zwischen der aktuellen Position auf der Bühne und der Position auf der Bühne der besten Schätzung

, , CoreLocation, .


, « ». , .


(, ):


  • ( horizontalAccuracy),
  • ,
  • 100 .

CoreLocation . , , CoreLocation , 100 .


, . , , ( 100 ).



, X/Z ARKit / . ARKit , , .


Warum?

, (, IKEA, ), Y ARKit – , . gravity worldAlignment.


, . , , , . . AR . , , , , . AR.



, . , \inline t_1 CLLocationManager \inline (lat_1,lon_1)\inline (x_1,z_1) . \inline t_2 CLLocationManager — \inline (lat_2,lon_2) \inline (x_2,z_2) entsprechend.


ARKit — \inline (\Delta x,\Delta z) 2 CoreLocation \inline t_2 . \inline (lat_2,lon_2) \inline (lat_{2calc},lon_{2calc}) . , CoreLocation . . ARKit /.


coordinates-conversion-correction-angle-problem


ARKit Y? . :


  1. ,
  2. ,
  3. ,
  4. ,
  5. .

. . CLLocationManager' , ( ), ( ).


?

. , , . , , GPS .


1, 2 : \inline initialBearing(1,2) und \inline initialBearing(1,2_{calc}) wo \inline 2_{calc} – 2, ARKit. \inline initialBearing(a,b) ( Bearing).


coordinates-conversion-correction-angle-calculation-for-pair


. , ? , , , , . , , , horizontalAccuracy. , , , . :


coordinates-conversion-correction-angle-calculation-error


, , .


. , . Zum Beispiel:


  • N ,
  • ,
  • M ( ?).

, , , , (), . , . , , . , , ( ). .


, . , , ( , , ).


Testen


, . , , , . 2 :


  • ,
  • .

- , , , , .


. , , 100 CLLocation, . , , , 10 ( 10 ). ? , "". , . , , , . , , . , CoreLocation. , . , .


. , . , (, ), , 0 . , , .


" ". . , , , , CLLocation, , . ( ) .


, ARKit.


correction-angle-calculation-alg-testing-street-before-correction


, .


correction-angle-calculation-alg-testing-street-after-correction


( 3-4 ) , .


correction-angle-calculation-alg-testing-street-after-last-correction


JS, AR CoreLocation.


correction-angle-calculation-alg-testing-tracks


— gravity worldAlignment . , . .


Anstelle einer Schlussfolgerung


Slack, , , , . AR. . AR AppStore 2017 . , .


Nützliche Links



, . .

Source: https://habr.com/ru/post/de421957/


All Articles