ARKit Indoor Navigation (Beacon/Marker Based) Tutorial

Bryan Ung
4 min readJun 13, 2021

While searching for ways to create an indoor navigation app with augmented reality, I found a solution that uses indoor beacons or markers which allow navigation from point A to point B through the use of instructions that the user has to take from each node to the destination. This works without needing to enable geolocation.

Link to Full Demo

Proof Of Concept

When you scan a specific marker/beacon node, it is in a specific orientation in relation to the camera. From here, you set the AR World configuration origin to that marker node. Now everything is based off of the beacon/marker and you can create intermediate nodes all based off that initial origin. You apply translations from one node to another based on real life directions and save the current offset from the origin. You keep applying translations to the new offset and adding it to the origin node to construct a path.

GitHub

The link to my GitHub with the application is here.

Demo

Here is an example of something you could do. I created this as a proof of concept working on a small innovative project. A full blown demo of the applications capabilities will be provided at the end of the article.

Demo Explanation

In the demo above, the user first clicks the top right button in order to present a search controller. This search controller retrieves data from a Firebase server with information on the path that needs to be constructed from a given marker node. The app’s state then changes to prepare the user for navigation and presents onscreen markers.

How its done?

In order to achieve the above scenario, you first need to create markers that the AR configuration will recognize. I put my image inside the Assets.xcassets folder. I was able to load the image like this.

Now when the camera scans this marker node, I can detect if the user is navigating or not within the renderer function provided by ARKit. I have a centralized data model instance that stores the singleton instances of my data classes. When I kick off navigation, I change the state of the program to navigating and use that scanned node to handle the rest.

If all my checks pass and I am navigating, the function generateNodeList() is called on line 29. This function request navigation info if the user is searching up a destination from within the server. The variable isNodeListGenerate checks whether or not this is the case.

Information is stored on the Firebase server in a JSON format. With my implementation users are able to store their own custom maps, and download them anytime. This information is decoded and broken down into a custom model I have created within the application.

I store this information in my data model and am able to reference it. Referring back to the renderer function provided by ARKit, I call setUpNavigation(). This function lies in the same file as the renderer function.

You can see on line 5 that the buildingNode which is the origin of all the nodes is the renderedBeaconNode or the marker node retrieved from the renderer function. I am able to place nodes down now on the screen adding it to the origin node with placeNode() on line 13.

With a little bit of linear algebra, I am able to figure out the distance calculation from one node to another. After the for loop running the above function finishes, the buildingNode now has new intermediate nodes. I then place lines connecting the nodes and add a little arrow on top to help guide the user. This is possible because I keep an internal reference of nodes in nodeList.

Full Demo

Further Implementation

Users can create their own custom map which can be saved on either their device or the Firebase server. After running a sequence of events, the user can enter the custom node building mode. From here, once the beacon/marker node is scanned, a different code block is ran.

This code block is from the same renderer function above. Essentially what happens is that the UIView changes and adds buttons which users can then use to construct a custom map.

This function handles the add button. The delegate refers to the main view housing the buttons. The main part occurs in the function getNodeDataAndPlotBuildingNode()

Users add nodes until they hit the end button. Once the end button is hit, users can save their newly created map on their device.

Users can also upload their map to the server. Here is an example of the user on the server.

Here is the code for uploading the map.

Users can download maps.

Finally, on the saved maps view, if users click on a map, they can kick off navigation with stored data.

Again, all the code is available on my GitHub.

Conclusion

This application was not intended to be a full blown production app. Further implementation can allow the use of geolocation. The skeleton code is already there. In addition, further implementation can allow users to share their custom maps and download others. Finally, gamification elements can be added.

--

--