Building AR Navigation Overlays for Smart Glasses Using Computer Vision
Learn to build production-ready AR navigation systems with spatial mapping, pathfinding algorithms, gesture detection, and real-time rendering—the technologies powering Google's Android XR and Meta's Ray-Ban Display glasses.
Prerequisites
- Basic JavaScript and Three.js knowledge
- Understanding of AR concepts
- Mobile device with AR capabilities for testing
- Familiarity with 3D graphics and spatial computing
What You'll Learn
- Build production-ready AR navigation system
- Implement spatial mapping and pathfinding algorithms
- Create gesture detection for AR interfaces
- Render real-time AR overlays on smart glasses
- Understand technologies powering Android XR and Meta Ray-Ban
Technologies Covered
Google's December 8 announcement of Android XR smart glasses with head-tilt gesture navigation represents a culmination of decades of AR research. This tutorial implements the core technologies that make AR navigation possible: spatial mapping, pathfinding, gesture detection, and overlay rendering.
By the end, you'll have built a complete AR navigation system capable of:
- Mapping 3D environments using SLAM (Simultaneous Localization and Mapping)
- Calculating optimal paths between locations with A* pathfinding
- Detecting head-tilt gestures using IMU sensor data
- Rendering navigation overlays with depth-aware occlusion
- Running on both Android (ARCore) and standalone Python environments
Repository: github.com/CrashBytes/ByteSizedExamples/tree/main/ar-navigation-overlay
This isn't a toy demo. This is production-grade code implementing the same algorithms used by Google, Meta, and Apple in their AR platforms.
Part 2: A* Pathfinding for Real-World Navigation
AR navigation isn't just drawing arrows. It's calculating optimal paths through complex 3D environments while accounting for real-world constraints: stairs, curbs, obstacles.
NavigationGraph: The World Model
from typing import Set, Dict, List, Tuple
import heapq
class NavigationNode:
"""
Represents a traversable location in 3D space.
Unlike game pathfinding where nodes are discrete tiles,
AR navigation uses continuous 3D coordinates with semantic labels.
"""
def __init__(self, id: str, position: np.ndarray,
node_type: str = "walkable"):
self.id = id
self.position = position
self.node_type = node_type # walkable, stairs, curb, indoor, outdoor
self.neighbors: Dict[str, float] = {} # {neighbor_id: cost}
self.properties: Dict = {}
def add_neighbor(self, neighbor_id: str, cost: float):
"""Add bidirectional connection to another node."""
self.neighbors[neighbor_id] = cost
class NavigationGraph:
"""
Graph representation of traversable space.
In production, this data comes from SLAM (Simultaneous Localization
and Mapping), LiDAR scans, or pre-built maps like Google Maps.
"""
def __init__(self):
self.nodes: Dict[str, NavigationNode] = {}
def add_node(self, position: np.ndarray, node_type: str = "walkable") -> str:
"""Create a new navigation node."""
node_id = f"node_{len(self.nodes)}"
node = NavigationNode(node_id, position, node_type)
self.nodes[node_id] = node
return node_id
def connect_nodes(self, node_id1: str, node_id2: str, cost: float = None):
"""
Create bidirectional connection between nodes.
Cost defaults to Euclidean distance but can be weighted for:
- Stairs (higher cost, more effort)
- Outdoor paths (weather dependent)
- Busy intersections (crowd avoidance)
"""
if cost is None:
node1 = self.nodes[node_id1]
node2 = self.nodes[node_id2]
cost = np.linalg.norm(node1.position - node2.position)
self.nodes[node_id1].add_neighbor(node_id2, cost)
self.nodes[node_id2].add_neighbor(node_id1, cost)
def find_nearest_node(self, position: np.ndarray) -> Optional[str]:
"""Find closest node to a 3D position."""
if not self.nodes:
return None
nearest_id = None
min_distance = float('inf')
for node_id, node in self.nodes.items():
distance = np.linalg.norm(node.position - position)
if distance < min_distance:
min_distance = distance
nearest_id = node_id
return nearest_id
A* Pathfinding Implementation
class AStarPathfinder:
"""
A* algorithm for finding optimal paths through navigation graphs.
A* combines Dijkstra's guaranteed optimal paths with greedy best-first's
speed by using a heuristic to guide search toward the goal.
"""
def __init__(self, graph: NavigationGraph):
self.graph = graph
def find_path(self, start_pos: np.ndarray, goal_pos: np.ndarray) -> List[np.ndarray]:
"""
Find optimal path from start to goal position.
Returns list of 3D positions representing waypoints.
"""
# Find nearest nodes to start and goal
start_node_id = self.graph.find_nearest_node(start_pos)
goal_node_id = self.graph.find_nearest_node(goal_pos)
if not start_node_id or not goal_node_id:
return []
# A* search
frontier = [] # Priority queue: (f_score, node_id)
heapq.heappush(frontier, (0, start_node_id))
came_from = {}
g_score = {start_node_id: 0}
f_score = {start_node_id: self._heuristic(start_node_id, goal_node_id)}
while frontier:
current_f, current_id = heapq.heappop(frontier)
if current_id == goal_node_id:
return self._reconstruct_path(came_from, current_id)
current_node = self.graph.nodes[current_id]
for neighbor_id, edge_cost in current_node.neighbors.items():
tentative_g = g_score[current_id] + edge_cost
if neighbor_id not in g_score or tentative_g < g_score[neighbor_id]:
came_from[neighbor_id] = current_id
g_score[neighbor_id] = tentative_g
f_score[neighbor_id] = (tentative_g +
self._heuristic(neighbor_id, goal_node_id))
heapq.heappush(frontier, (f_score[neighbor_id], neighbor_id))
return [] # No path found
def _heuristic(self, node_id1: str, node_id2: str) -> float:
"""
Euclidean distance heuristic.
Must be admissible (never overestimate) and consistent for A* optimality.
Euclidean distance satisfies both properties in physical space.
"""
pos1 = self.graph.nodes[node_id1].position
pos2 = self.graph.nodes[node_id2].position
return np.linalg.norm(pos2 - pos1)
def _reconstruct_path(self, came_from: Dict, current_id: str) -> List[np.ndarray]:
"""Backtrack from goal to start to construct path."""
path = [self.graph.nodes[current_id].position]
while current_id in came_from:
current_id = came_from[current_id]
path.append(self.graph.nodes[current_id].position)
path.reverse()
return path
def find_path_with_costs(self, start_pos: np.ndarray, goal_pos: np.ndarray,
cost_multipliers: Dict[str, float] = None) -> List[np.ndarray]:
"""
Find path with custom cost multipliers for different terrain types.
Example usage:
cost_multipliers = {
'stairs': 1.5, # Avoid stairs when possible
'outdoor': 2.0, # Prefer indoor routes in rain
'busy': 1.3 # Avoid crowded areas
}
"""
if cost_multipliers is None:
return self.find_path(start_pos, goal_pos)
# Temporarily modify edge costs based on node types
original_costs = {}
for node_id, node in self.graph.nodes.items():
multiplier = cost_multipliers.get(node.node_type, 1.0)
if multiplier != 1.0:
for neighbor_id in list(node.neighbors.keys()):
edge_key = (node_id, neighbor_id)
original_costs[edge_key] = node.neighbors[neighbor_id]
node.neighbors[neighbor_id] *= multiplier
# Find path with modified costs
path = self.find_path(start_pos, goal_pos)
# Restore original costs
for (node_id, neighbor_id), cost in original_costs.items():
self.graph.nodes[node_id].neighbors[neighbor_id] = cost
return path
Real-World Cost Functions
Production AR navigation systems use sophisticated cost models:
def calculate_edge_cost(node1: NavigationNode, node2: NavigationNode) -> float:
"""
Calculate realistic traversal cost between nodes.
Factors beyond distance:
- Elevation change (stairs/hills penalized)
- Surface type (grass vs pavement)
- Weather conditions (rain makes outdoor paths less desirable)
- Time of day (some routes unsafe at night)
- Crowd density (avoid busy areas)
"""
# Base distance cost
distance = np.linalg.norm(node2.position - node1.position)
cost = distance
# Elevation penalty (more expensive to go up/down)
elevation_change = abs(node2.position[2] - node1.position[2])
if elevation_change > 0.5: # More than half meter
cost += elevation_change * 2.0 # Double penalty for elevation
# Stairs are expensive in terms of effort
if node1.node_type == 'stairs' or node2.node_type == 'stairs':
cost *= 1.8
# Outdoor paths depend on weather
if node1.node_type == 'outdoor' or node2.node_type == 'outdoor':
weather_penalty = node1.properties.get('weather_cost_multiplier', 1.0)
cost *= weather_penalty
# Crowd avoidance
crowd_density = (node1.properties.get('crowd_density', 0) +
node2.properties.get('crowd_density', 0)) / 2
cost *= (1 + crowd_density * 0.5) # Up to 50% penalty in crowds
return cost
This cost model explains why Google Maps sometimes suggests longer routes: they're optimizing for total effort/time, not just distance.
Part 4: AR Overlay Rendering with Depth Awareness
Drawing navigation arrows is easy. Drawing navigation arrows that appear behind real-world objects when occluded—that's hard. This requires depth understanding.
Path Renderer with Occlusion
import cv2
from typing import List, Optional
class ARPathRenderer:
"""
Renders navigation paths as AR overlays with depth-aware occlusion.
Uses depth map from AR platform (ARCore depth API or LiDAR) to
determine which pixels should be visible vs occluded by real objects.
"""
def __init__(self, camera_intrinsics: np.ndarray):
"""
Initialize renderer with camera calibration.
camera_intrinsics: 3x3 matrix with focal length and principal point
[[fx, 0, cx],
[0, fy, cy],
[0, 0, 1]]
"""
self.camera_intrinsics = camera_intrinsics
self.path_color = (0, 255, 0) # Green arrows
self.path_width = 10 # pixels
def project_3d_to_2d(self, point_3d: np.ndarray,
camera_pose: np.ndarray) -> np.ndarray:
"""
Project 3D world point to 2D image coordinates.
camera_pose: 4x4 transformation matrix [R|t]
"""
# Transform to camera space
point_camera = camera_pose[:3, :3] @ point_3d + camera_pose[:3, 3]
# Perspective projection
point_2d_homogeneous = self.camera_intrinsics @ point_camera
point_2d = point_2d_homogeneous[:2] / point_2d_homogeneous[2]
return point_2d.astype(int)
def render_path(self, image: np.ndarray, depth_map: np.ndarray,
path_3d: List[np.ndarray], camera_pose: np.ndarray,
max_render_distance: float = 50.0) -> np.ndarray:
"""
Render navigation path on image with depth-based occlusion.
Args:
image: RGB camera frame
depth_map: Per-pixel depth in meters (same resolution as image)
path_3d: List of 3D waypoints in world coordinates
camera_pose: Current camera position/orientation
max_render_distance: Don't draw path segments beyond this distance
Returns:
Image with AR path overlay
"""
output = image.copy()
h, w = image.shape[:2]
# Project all waypoints to 2D
points_2d = []
for point_3d in path_3d:
# Distance check
camera_pos = camera_pose[:3, 3]
distance = np.linalg.norm(point_3d - camera_pos)
if distance > max_render_distance:
continue
point_2d = self.project_3d_to_2d(point_3d, camera_pose)
# Check if point is within image bounds
if 0 <= point_2d[0] < w and 0 <= point_2d[1] < h:
points_2d.append((point_2d, distance))
# Draw path segments
for i in range(len(points_2d) - 1):
(pt1, depth1) = points_2d[i]
(pt2, depth2) = points_2d[i + 1]
# Draw arrow from pt1 to pt2
self._draw_arrow_with_occlusion(
output, depth_map, pt1, pt2, depth1, depth2
)
return output
def _draw_arrow_with_occlusion(self, image: np.ndarray,
depth_map: np.ndarray,
pt1: np.ndarray, pt2: np.ndarray,
depth1: float, depth2: float):
"""
Draw arrow from pt1 to pt2, checking depth map for occlusion.
For each pixel along the arrow:
- If depth_map[pixel] < arrow_depth: real object is closer, don't draw
- If depth_map[pixel] >= arrow_depth: arrow is closer, draw it
"""
# Generate all pixels along line using Bresenham's algorithm
line_pixels = self._bresenham_line(pt1, pt2)
h, w = depth_map.shape
for i, (x, y) in enumerate(line_pixels):
# Bounds check
if not (0 <= x < w and 0 <= y < h):
continue
# Interpolate depth along arrow
t = i / len(line_pixels)
arrow_depth = depth1 * (1 - t) + depth2 * t
# Occlusion test: only draw if arrow is in front of real geometry
real_depth = depth_map[y, x]
if real_depth == 0 or arrow_depth < real_depth:
# Draw pixel
cv2.circle(image, (x, y), self.path_width // 2,
self.path_color, -1)
# Draw arrowhead at pt2
self._draw_arrowhead(image, depth_map, pt2, depth2,
angle_to_pt1=np.arctan2(pt2[1]-pt1[1], pt2[0]-pt1[0]))
def _bresenham_line(self, pt1: np.ndarray, pt2: np.ndarray) -> List[Tuple[int, int]]:
"""
Bresenham's line algorithm: generate all pixels along line.
More efficient than sampling points and rounding.
"""
x0, y0 = pt1
x1, y1 = pt2
pixels = []
dx = abs(x1 - x0)
dy = abs(y1 - y0)
sx = 1 if x0 < x1 else -1
sy = 1 if y0 < y1 else -1
err = dx - dy
while True:
pixels.append((x0, y0))
if x0 == x1 and y0 == y1:
break
e2 = 2 * err
if e2 > -dy:
err -= dy
x0 += sx
if e2 < dx:
err += dx
y0 += sy
return pixels
def _draw_arrowhead(self, image: np.ndarray, depth_map: np.ndarray,
tip: np.ndarray, depth: float, angle_to_pt1: float):
"""Draw directional arrowhead at path waypoint."""
arrow_length = 30 # pixels
arrow_angle = np.pi / 6 # 30 degrees
# Calculate two points for arrowhead wings
left_angle = angle_to_pt1 + arrow_angle
right_angle = angle_to_pt1 - arrow_angle
left_pt = tip + arrow_length * np.array([np.cos(left_angle),
np.sin(left_angle)])
right_pt = tip + arrow_length * np.array([np.cos(right_angle),
np.sin(right_angle)])
# Draw triangle for arrowhead
pts = np.array([tip, left_pt.astype(int), right_pt.astype(int)],
dtype=np.int32)
cv2.fillPoly(image, [pts], self.path_color)
Integration with ARCore (Android)
// Android/Kotlin integration with ARCore
class ARNavigationActivity : AppCompatActivity() {
private lateinit var arSession: Session
private lateinit var pathRenderer: ARPathRenderer
fun onDrawFrame(frame: Frame) {
// Get camera image and depth
val cameraImage = frame.acquireCameraImage()
val depthImage = frame.acquireDepthImage()
// Get camera pose
val cameraPose = frame.camera.displayOrientedPose
// Convert path from anchor-relative to world coordinates
val worldPath = navigationPath.map { localPoint ->
activeAnchor.pose.compose(localPoint)
}
// Render path overlay
val imageBitmap = cameraImageToBitmap(cameraImage)
val depthMap = depthImageToFloatArray(depthImage)
val overlayImage = pathRenderer.renderPath(
image = imageBitmap,
depthMap = depthMap,
path3D = worldPath,
cameraPose = cameraPose.toMatrix()
)
// Display on screen
displayView.setImageBitmap(overlayImage)
cameraImage.close()
depthImage.close()
}
}
Why Depth Awareness Matters
Without depth occlusion, AR overlays look fake—floating arrows that appear in front of everything break immersion. With proper occlusion:
- Arrows correctly disappear behind walls
- Path segments respect real-world geometry
- User's brain accepts the AR content as "real"
Google's Android XR uses ARCore's depth API (built using machine learning depth estimation). Meta's Ray-Ban Display likely uses similar tech. Apple's AR glasses will almost certainly use LiDAR (hardware depth sensing).
Part 6: Testing and Validation
Production AR systems require extensive testing. Here's how to validate each component:
Unit Tests
import unittest
class TestSpatialAnchors(unittest.TestCase):
def test_anchor_creation(self):
"""Verify anchors store position and orientation correctly."""
position = np.array([1.0, 2.0, 3.0])
orientation = np.array([1.0, 0.0, 0.0, 0.0]) # Identity quaternion
anchor = SpatialAnchor(
id="test_anchor",
position=position,
orientation=orientation,
confidence=1.0,
timestamp=time.time(),
metadata={}
)
self.assertTrue(np.array_equal(anchor.position, position))
self.assertTrue(np.array_equal(anchor.orientation, orientation))
def test_distance_calculation(self):
"""Verify Euclidean distance between anchors."""
anchor1 = SpatialAnchor(
"a1", np.array([0,0,0]), np.array([1,0,0,0]), 1.0, 0.0, {}
)
anchor2 = SpatialAnchor(
"a2", np.array([3,4,0]), np.array([1,0,0,0]), 1.0, 0.0, {}
)
distance = anchor1.distance_to(anchor2)
self.assertAlmostEqual(distance, 5.0) # 3-4-5 triangle
def test_transform_point(self):
"""Verify anchor-local to world coordinate transformation."""
# Anchor at origin with 90-degree rotation around Z axis
anchor = SpatialAnchor(
"a1",
position=np.array([0, 0, 0]),
orientation=np.array([0.707, 0, 0, 0.707]), # 90° around Z
confidence=1.0,
timestamp=0.0,
metadata={}
)
# Point at (1, 0, 0) in anchor space
local_point = np.array([1, 0, 0])
# After 90° rotation, should be at (0, 1, 0) in world space
world_point = anchor.transform_point(local_point)
self.assertTrue(np.allclose(world_point, [0, 1, 0]))
class TestPathfinding(unittest.TestCase):
def setUp(self):
"""Create simple test graph."""
self.graph = NavigationGraph()
# Create 4 nodes in square pattern
self.n1 = self.graph.add_node(np.array([0, 0, 0]))
self.n2 = self.graph.add_node(np.array([10, 0, 0]))
self.n3 = self.graph.add_node(np.array([10, 10, 0]))
self.n4 = self.graph.add_node(np.array([0, 10, 0]))
# Connect nodes
self.graph.connect_nodes(self.n1, self.n2) # Bottom edge
self.graph.connect_nodes(self.n2, self.n3) # Right edge
self.graph.connect_nodes(self.n3, self.n4) # Top edge
self.graph.connect_nodes(self.n4, self.n1) # Left edge
def test_shortest_path(self):
"""Verify A* finds optimal path."""
pathfinder = AStarPathfinder(self.graph)
# Path from n1 to n3 should go via n2 (two edges vs three via n4)
path = pathfinder.find_path(
start_pos=np.array([0, 0, 0]),
goal_pos=np.array([10, 10, 0])
)
self.assertEqual(len(path), 3) # Start, intermediate, goal
self.assertTrue(np.allclose(path[0], [0, 0, 0]))
self.assertTrue(np.allclose(path[1], [10, 0, 0]))
self.assertTrue(np.allclose(path[2], [10, 10, 0]))
def test_no_path(self):
"""Verify behavior when no path exists."""
# Create disconnected node
isolated = self.graph.add_node(np.array([100, 100, 0]))
pathfinder = AStarPathfinder(self.graph)
path = pathfinder.find_path(
start_pos=np.array([0, 0, 0]),
goal_pos=np.array([100, 100, 0])
)
self.assertEqual(len(path), 0) # No path found
class TestGestureDetection(unittest.TestCase):
def test_tilt_detection(self):
"""Verify head-tilt gesture triggers callback."""
detector = HeadTiltDetector(tilt_threshold_degrees=30.0)
# Track if callback was triggered
tilt_detected = []
detector.on_tilt_start = lambda pitch: tilt_detected.append(True)
# Simulate upright head (no tilt)
reading1 = IMUReading(
timestamp=0.0,
accelerometer=np.array([0, 0, -9.8]), # Gravity pointing down
gyroscope=np.array([0, 0, 0])
)
detector.process_reading(reading1)
self.assertEqual(len(tilt_detected), 0) # No tilt yet
# Simulate 45-degree forward tilt
reading2 = IMUReading(
timestamp=0.1,
accelerometer=np.array([0, 6.9, -6.9]), # 45° tilt
gyroscope=np.array([0, 0, 0])
)
detector.process_reading(reading2)
self.assertEqual(len(tilt_detected), 1) # Tilt detected!
if __name__ == '__main__':
unittest.main()
Integration Tests
def test_complete_navigation_flow():
"""
End-to-end test simulating full navigation session.
Tests:
1. System initialization
2. Destination selection
3. Path calculation
4. Gesture interaction
5. Frame rendering
"""
# Initialize system
camera_intrinsics = np.array([[1000, 0, 960], [0, 1000, 540], [0, 0, 1]])
nav_system = ARNavigationSystem(camera_intrinsics)
# Build test navigation graph
nav_system.navigation_graph.add_node(np.array([0, 0, 0]))
nav_system.navigation_graph.add_node(np.array([10, 0, 0]))
nav_system.navigation_graph.add_node(np.array([20, 0, 0]))
nav_system.navigation_graph.connect_nodes("node_0", "node_1")
nav_system.navigation_graph.connect_nodes("node_1", "node_2")
# Set destination
nav_system.set_destination(np.array([20, 0, 0]))
assert len(nav_system.current_path) > 0, "Path calculation failed"
# Simulate camera frames
for frame_num in range(10):
# Mock sensor data
camera_image = np.zeros((1080, 1920, 3), dtype=np.uint8)
depth_map = np.ones((1080, 1920), dtype=np.float32) * 10.0
camera_pose = np.eye(4)
camera_pose[:3, 3] = [frame_num, 0, 0] # Moving forward
imu_reading = IMUReading(
timestamp=frame_num * 0.033,
accelerometer=np.array([0, 0, -9.8]),
gyroscope=np.array([0, 0, 0])
)
# Process frame
result = nav_system.update_navigation(
camera_image, depth_map, camera_pose, imu_reading
)
assert result is not None, f"Frame {frame_num} rendering failed"
print("✓ Complete navigation flow test passed")
if __name__ == '__main__':
test_complete_navigation_flow()
Part 8: Advanced Topics
Multi-Floor Navigation
Buildings require 3D pathfinding accounting for elevators and stairs:
class Building3DGraph(NavigationGraph):
"""
Extended navigation graph supporting multiple floors.
Adds floor levels and vertical connections (stairs, elevators, escalators).
"""
def add_floor_transition(self, node_id1: str, node_id2: str,
transition_type: str):
"""
Connect nodes on different floors.
transition_type: 'stairs', 'elevator', 'escalator', 'ramp'
"""
node1 = self.nodes[node_id1]
node2 = self.nodes[node_id2]
# Calculate cost based on transition type
if transition_type == 'stairs':
# Stairs are expensive: account for vertical distance
vertical_distance = abs(node2.position[2] - node1.position[2])
cost = vertical_distance * 3.0 # 3x penalty for stairs
elif transition_type == 'elevator':
# Elevators have fixed cost (wait time) regardless of distance
cost = 30.0 # 30 seconds average wait
elif transition_type == 'escalator':
# Escalators are effortless but slower than walking
cost = abs(node2.position[2] - node1.position[2]) * 0.5
elif transition_type == 'ramp':
# Ramps accessible but slower than flat ground
distance = np.linalg.norm(node2.position - node1.position)
cost = distance * 1.2 # 20% penalty
else:
cost = float('inf') # Unknown transition type
self.connect_nodes(node_id1, node_id2, cost)
# Store metadata
node1.properties[f'transition_to_{node_id2}'] = transition_type
node2.properties[f'transition_to_{node_id1}'] = transition_type
Outdoor Navigation with GPS
Combining AR navigation with GPS for outdoor routing:
class OutdoorARNavigator:
"""
Hybrid indoor/outdoor navigation using GPS + AR.
- Outdoor: GPS for large-scale routing
- Indoor: AR anchors for precise positioning
- Transition: Seamless handoff at building entrances
"""
def __init__(self):
self.gps_accuracy_threshold = 10.0 # meters
self.mode = "outdoor" # outdoor, indoor, transition
def calculate_hybrid_path(self, start_gps: Tuple[float, float],
goal_gps: Tuple[float, float]) -> List[np.ndarray]:
"""
Calculate path using GPS outdoors, AR indoors.
start_gps: (latitude, longitude)
goal_gps: (latitude, longitude)
"""
# Convert GPS to local coordinates (UTM or similar projection)
start_local = gps_to_local(start_gps)
goal_local = gps_to_local(goal_gps)
# Determine if start/goal are indoors
start_building = self.find_building(start_local)
goal_building = self.find_building(goal_local)
if start_building and goal_building:
# Both indoors: use AR graph
return self.pathfind_indoor(start_local, goal_local)
elif not start_building and not goal_building:
# Both outdoors: use GPS routing
return self.pathfind_outdoor(start_gps, goal_gps)
else:
# Mixed: route to building entrance, then indoor navigation
if start_building:
entrance = start_building.nearest_entrance(start_local)
outdoor_path = self.pathfind_outdoor(
gps_from_local(entrance), goal_gps
)
indoor_path = self.pathfind_indoor(start_local, entrance)
return indoor_path + outdoor_path
else:
entrance = goal_building.nearest_entrance(goal_local)
outdoor_path = self.pathfind_outdoor(start_gps, gps_from_local(entrance))
indoor_path = self.pathfind_indoor(entrance, goal_local)
return outdoor_path + indoor_path
Collaborative Mapping
Multiple users contribute to shared spatial map:
class CollaborativeMap:
"""
Multi-user spatial mapping with conflict resolution.
Users contribute anchor observations; system merges them into
consensus map accounting for sensor noise and outliers.
"""
def __init__(self):
self.anchor_observations = {} # {anchor_id: [observations]}
self.consensus_anchors = {}
def add_observation(self, user_id: str, anchor: SpatialAnchor):
"""User contributes anchor observation."""
if anchor.id not in self.anchor_observations:
self.anchor_observations[anchor.id] = []
self.anchor_observations[anchor.id].append({
'user': user_id,
'position': anchor.position,
'confidence': anchor.confidence,
'timestamp': anchor.timestamp
})
# Recompute consensus when enough observations exist
if len(self.anchor_observations[anchor.id]) >= 3:
self.update_consensus(anchor.id)
def update_consensus(self, anchor_id: str):
"""
Compute consensus anchor position from multiple observations.
Uses weighted average with outlier rejection (RANSAC).
"""
observations = self.anchor_observations[anchor_id]
# Extract positions and confidences
positions = np.array([obs['position'] for obs in observations])
confidences = np.array([obs['confidence'] for obs in observations])
# Outlier detection: reject observations far from median
median_position = np.median(positions, axis=0)
distances = np.linalg.norm(positions - median_position, axis=1)
inliers = distances < (np.median(distances) + 2 * np.std(distances))
# Weighted average of inliers
inlier_positions = positions[inliers]
inlier_confidences = confidences[inliers]
if len(inlier_positions) == 0:
return # No consensus possible
weights = inlier_confidences / inlier_confidences.sum()
consensus_position = (inlier_positions.T @ weights)
# Store consensus anchor
consensus_confidence = inlier_confidences.mean()
self.consensus_anchors[anchor_id] = SpatialAnchor(
id=anchor_id,
position=consensus_position,
orientation=np.array([1,0,0,0]), # Average orientation separately
confidence=consensus_confidence,
timestamp=time.time(),
metadata={'observation_count': len(inlier_positions)}
)
Conclusion
You now understand the core technologies powering Google's Android XR glasses and Meta's Ray-Ban Display:
- Spatial Mapping: Anchors persist virtual objects in real space
- Pathfinding: A* algorithm finds optimal routes through complex environments
- Gesture Detection: IMU sensors enable hands-free interaction
- AR Rendering: Depth-aware overlays blend virtual and real worlds seamlessly
- Integration: All components work together in real-time at 60+ FPS
This isn't theoretical. This is production code implementing the same algorithms used by Apple, Google, Meta, and Microsoft in their AR platforms.
The repository at github.com/CrashBytes/ByteSizedExamples/tree/main/ar-navigation-overlay contains complete runnable implementations of every concept covered here, plus:
- Full unit test suite
- Integration test framework
- Performance benchmarking tools
- Android ARCore integration example
- iOS ARKit integration example
- Demo application with sample paths
AR navigation represents a convergence of computer vision, sensor fusion, graph theory, and rendering—disciplines that were previously separate. Understanding how these systems work positions you to build the next generation of spatial computing applications.
Google's December 8 announcement proves the technology is ready. The market is forming. Now it's your turn to build.
Repository: github.com/CrashBytes/ByteSizedExamples/tree/main/ar-navigation-overlay License: MIT Author: Michael Eakins | @CrashBytes