Wie nutze ich den iSense oder Structure Sensor?

Der iSense Struktursensor ist der erste 3D Sensor für mobile Geräte. Er kann über den Lightning connector mit dem iPad oder iPhone, bzw. über einen Standard-USB Port mit dem PC, Mac oder Android Geräten verbunden werden. Der Sensor ermöglicht es Mobilgeräten, ihre Umgebung in 3D festzuhalten. Das Gerät an sich ist kein 3D Scanner, aber er bietet Entwicklern die Möglichkeit, 3D Tiefendaten in ihren Anwendungen zu nutzen.

Raum scannen mit dem iSense

Das öffnet natürlich die Tür für einige interessante und neue Applikationen:

+ 3D scannen von Räumen für Echtzeitvermessungen oder virtuelle Raumausstattung
+ erweiterte Augmented Reality (AR), bei der virtuelle Objekte mit echten Objekten interagieren können (z.B. durch Schatten, Verdeckungen etc.)
+ 3D Scan von Menschen (z.B. für virtuelle Anprobe von Kleidung)
+ Virtual Reality Spiele in realen 3D Umgebungen (d.h. man bewegt sich durch die virtuelle Welt indem man, ein iPad mit Struktursensor in den Händen haltend, durch die echte Welt geht)
+ 3D Scan von Objekten zur Nutzung für AR, Web etc.

Der iSense Struktursensor nutzt einen Infrarot-Laserprojektor, der ein unsichtbares Muster auf die zu scannenden Objekte projiziert. Dann wird mit einer Infrarot Kamera festgestellt, wie sich dieses Muster verändert. Daraus kann der Struktursensor einen Stream mit 30 Frames pro Sekunde und VGA (640×480) Auflösung generieren, in dem jedes Pixel einen exakten Abstand zu einem realen Punkt darstellt. Die Tiefenreichweite des Sensors beträgt in etwa 40 Zentimeter bis 3,5 Meter.

Aus der Entwicklersicht, ist der Struktursensor eine wahnsinnig tolle Erfindung. Das SDK (software development kit) unterstützt nicht nur iOS, sondern auch das 3D Programm Unity und eröffnet somit vielfältige und mächtige Entwicklungsmöglichkeiten für Spiele und Virtual Reality.

iSense_scan_object_iPad

Damit auch du gleich mit der Entwicklung für den iSense Struktursensor beginnen kannst, gehe ich jetzt die nötigen Schritte durch um ein Projekt aufzusetzen und die Sensordaten auszulesen.

Los geht’s

1. Lade dir das SDK herunter (den Download Link kannst du hier anfordern)

2. Lege ein neues iOS Projekt an

3. Kopiere das Structure.framework in das Hauptverzeichnis deines Projektes (oder eben dahin wo du es haben möchtest) und füge es zum Projekt hinzu

4. Die folgenden libraries solltest du auch verlinken:
ExternalAccessory.framework
ImageIO.framework
Accelerate.framework
libz.tbd

5. Erstelle in deiner Info.plist eine neue Reihe namens „Supported external accessory protocols“ und füge die folgenden String Values hinzu:
io.structure.control
io.structure.depth
io.structure.infrared

6. In den Build Settings deines Targets:
1. Enable Bitcode -> No
2. Other Linker Flags: -lc++
3. Header Search Paths:
$(inherited)
$(PROJECT_DIR)/Structure.framework/Headers

7. a. Wenn du Objective-C nutzt
In deinem view controller, solltest du unbedingt HAS_LIBCXX definieren, befor du die Structure Headers importierst:
#define HAS_LIBCXX
#import "Structure.h"
#import "StructureSLAM.h"

b. Wenn du Swift nutzt
Erstelle einen Bridging Header und füge die folgenden Zeilen hinzu:
#define HAS_LIBCXX
#import "Structure.h"
#import "StructureSLAM.h"

8. Stelle deinen View Controller als STSensorControllerDelegate ein:
[STSensorController sharedController].delegate = self;

9. Implementiere die folgenden Methoden:

/// Benachrichtigt den Delegate, dass der Controller erfolgreich eine Verbindung zum Sensor hergestellt hat.
(void)sensorDidConnect;

/// Benachrichtigt den Delegate, dass der Sensor vom Controller getrennt wurde.
(void)sensorDidDisconnect;

/// Benachrichtigt den Delegate, dass der Sensor den Stream zum Controller unterbrochen hat.
(void)sensorDidStopStreaming:(STSensorControllerDidStopStreamingReason)reason;

/// Benachrichtigt den Delegate, dass der Sensor den Lw Power Modus verlassen hat.
(void)sensorDidLeaveLowPowerMode;

/// Benachrichtigt den Delegate, dass der Sensor aufgeladen werden muss.
(void)sensorBatteryNeedsCharging;

10. Um den Sensor zu initialisieren, musst du die initializeSensorConnection Funktion aufrufen:
STSensorControllerInitStatus result = [[STSensorController sharedController] initializeSensorConnection];
BOOL didSucceed = (result == STSensorControllerInitStatusSuccess || result == STSensorControllerInitStatusAlreadyInitialized);

11. Um vom Gerät Daten auszulesen, musst du den Stream starten. Beispiel für Tiefendaten:
NSError *error = nil;
[[STSensorController sharedController] startStreamingWithOptions:@{kSTStreamConfigKey: @(STStreamConfigDepth640x480), kSTHoleFilterConfigKey: @YES} error:&error];

12. Du solltest das Projekt jetzt builden können, aber du kannst damit noch nichts anfangen. Um die vom Sensor ausgelesenen Daten zu nutzen, musst du eine oder mehrere der folgenden Funktionen implementieren.
/// Benachrichtigt den Delegate, dass der Sensor dem Controller einen neuen Tiefen-Frame zur Verfügung gestellt hat. Wenn die Daten über den Umfang dieser Methode hinaus genutzt werden sollen, muss der Tiefen-Frame oder die dazugehörigen Daten vom Empfänger z.B. durch [STDepthFrame copy] kopiert werden.
(void)sensorDidOutputDepthFrame:(STDepthFrame *)depthFrame;

/// Benachrichtigt den Delegate, dass der Sensor dem Controller einen neuen Infrarot Frame zur Verfügung gestellt hat. Wenn die Daten über den Umfang dieser Methode hinaus genutzt werden sollen, müssen der irFrame und die dazugehörigen Daten vom Empfänger kopiert werden.
(void)sensorDidOutputInfraredFrame:(STInfraredFrame *)irFrame;

/// Benachrichtigt den Delegate, dass der Sensor dem Controller ein neues Paar von Tiefen- und Farb-Frames zur Verfügung gestellt hat.
(void)sensorDidOutputSynchronizedDepthFrame:(STDepthFrame *)depthFrame
andColorFrame:(STColorFrame *)colorFrame;

/// Benachrichtigt den Delegate, dass der Sensor dem Controller ein neues Paar von synchronisierten Infrarot- und Farb-Frames zur Verfügung gestellt hat.
(void)sensorDidOutputSynchronizedInfraredFrame:(STInfraredFrame *)irFrame
andColorFrame:(STColorFrame *)colorFrame;

13. In der Library ist auch noch ein tolles Feature integriert. Da das Gerät mit dem iPad über den Lightning Connector verbunden ist, kannst du die App nicht im Debug Modus ausführen. Das heißt, dass du daher deine NSLogs nicht sehen kannst. Aber mithilfe des STWirelessLog Features, kannst du jeden NSLog zu einem Remote Host pushen. Benutze einfach dieses Snippet, aber denk daran, die IP auf deine eigene zu ändern.

// STWirelessLog ist hilfreich, um zu debuggen, während der Sensor angesteckt ist
// Lies in der SDK Dokumentation nach, wie du einen Listener auf deinem Computer starten kannst.
NSError* error = nil;
NSString *remoteLogHost = @"192.168.10.69";
[STWirelessLog broadcastLogsToWirelessConsoleAtAddress:remoteLogHost usingPort:4999 error:&error];
if (error)
NSLog(@"Oh no! Can't start wireless log: %@", [error localizedDescription]);

14. Wenn du Mac OS X User bist, kannst du netcat benutzen, um die Notifications vom jeweiligen Port abzufragen. Starte Terminal und gib folgendes ein:
nc -lk 4999

So, jetzt kennst du also alle nötigen Schritte, um mit dem iSense Struktursensor auf iOS loszulegen. Wenn du diesen Schritten folgst, sollte es dir möglich sein,  Daten vom Sensor auszulesen und in deiner App zu nutzen.

Weitere Informationen findest du im Structure SDK Referenzfile, das im SDK Package enthalten ist.

Falls dir hier etwas unklar sein sollte, oder du dazu fragen hast, kannst du mich gerne kontaktieren!

Weiterführende Links

Mehr Information zum iSense Struktursensor, findest du hier. Und schau dir auch die Demos im SDK Package an. Außerdem solltest du auch einen Blick auf diese nützliche App werfen.

Ich habe ein Beispiel für Objective-C auf GitHub vorbereitet, und für Swift findest du hier ein Beispiel.