VSLAM Drone Navigation
I explored how a moving monocular camera can track visual landmarks and recover scene structure. The project combines feature matching, relative camera motion, and learned-depth experiments as a foundation for drone navigation.
The problem
A moving camera sees the same room from changing viewpoints. To estimate its motion, the system needs to associate visible features across frames and relate their image positions to the camera’s geometry.
With a single camera, scale and motion are intertwined. Feature tracks supply geometric constraints, while learned depth supplies another representation of the visible scene. This project explored both using recorded room imagery and several small computer-vision programs.
My contribution
I built the monocular vision study, feature and geometry experiments, learned-depth integration, pose experiments, and reconstruction visualizations. My work moved between camera calibration, visual correspondences, and different ways of turning an image into a three-dimensional representation.
Architecture
The repository contains separate experimental paths. ORB scripts detect and match image features. A later path uses Lucas–Kanade optical flow, estimates an essential matrix with RANSAC, and calls recoverPose to obtain relative rotation. MiDaS_small supplies depth-like image values for separate visualization and projection studies.
The shared input is recorded imagery. The drone-facing script imports a Tello adapter, while its executable path reads a room video. Map-update and flight-control work remains sketched in the source.
Feature tracking
ORB produces keypoints and binary descriptors. The matching path uses a brute-force matcher with Hamming distance and cross-checking, sorts matches by descriptor distance, and extracts paired image coordinates. Those correspondences are the bridge between two views of the same scene.
def detect_and_match_features(orb, bf, prev_frame, frame):
kp1, des1 = orb.detectAndCompute(prev_frame, None)
kp2, des2 = orb.detectAndCompute(frame, None)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
pts1 = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
pts2 = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
return pts1, pts2The figure projects six fixed points into two synthetic cameras with matching intrinsics. Their horizontal displacement illustrates how camera separation and scene depth affect the image coordinates. The fixture is independent of the recorded room footage.
The optical-flow experiment filters tracks by their status flags before estimating motion. The code also includes SIFT and homography studies. These paths capture different stages of the exploration, with their descriptor choices and failure cases recorded in the local source audit.
Depth and geometry
The depth scripts load MiDaS_small, resize its output back to the working image dimensions, and normalize the values to a range from zero to one. MiDaS supplies relative scene depth; connecting its output to physical distances requires an additional scale model.
One experiment builds a grid of image coordinates, applies the inverse camera matrix, and multiplies those rays by the normalized values. This exposes the back-projection calculation and its dependence on the camera intrinsics.
x, y = np.meshgrid(np.arange(0, width), np.arange(0, height))
image_points = np.vstack((x.flatten(), y.flatten(), np.ones_like(x.flatten())))
normalized_points = np.linalg.inv(K) @ image_points
x_3d = normalized_points[0, :] * depth_map.flatten()
y_3d = normalized_points[1, :] * depth_map.flatten()
z_3d = depth_map.flatten()The figure uses a small fixed depth grid and a camera matrix matched to its dimensions. Its twelve coordinates make the arithmetic inspectable. In the source experiment, resizing the image while retaining the original intrinsics limits the geometric interpretation of the resulting cloud.
Other scripts explore essential-matrix pose recovery, triangulation, and Perspective-n-Point. Their outputs document experiments with camera motion and image geometry. The opening scene uses a repeatable illustrative camera path through fixed landmarks.
Evaluation
The project record supports a visual account of the experiments: paired room frames, feature displays, depth views, and coordinate plots. The repository does not provide a verified trajectory benchmark, calibrated distance result, or autonomous-flight evaluation.
The inspected paths also contain unfinished map updates, inconsistent depth sampling, and a weak-track branch that leaves rotation undefined. The local audit records those details. The article therefore follows the individual experiments and the evidence each produces.
Offline fixtures check match ordering, correspondence indices, two-camera projection, and back-projection consistency. They also preserve the observed weak-track failure. No camera, drone, or external depth model runs in this page.
Experiments

These frames show the indoor visual material used during development. The new diagrams isolate correspondence and projection geometry so those ideas can be inspected alongside the original imagery.
Explore the VSLAM experiments ↗The linked repository requires GitHub access.