Robotics and robot learning 15 min read

How Drones Fly Without GPS: IMU Drift, Visual Odometry and Map Matching, with Python Code

How drones fly without GPS: why an IMU drifts, how visual odometry and map matching fix it, this month's Navy and ESA news, and a Python particle filter.

How drones fly without GPS: after a 10 minute flight with GPS jammed, an IMU alone is 4.3 km off, visual odometry 139 m off, and map matching 5 m off

GPS tells a drone where it is to within a few metres, almost anywhere on Earth, for free. That makes it easy to forget how fragile it is. The signal arrives from satellites about 20,000 km away, weaker than the background noise, and a cheap transmitter on the ground can drown it out. Fly into a tunnel, a forest, a warehouse or a war zone, and the position fix that everything else depends on can disappear in a second.

October 2026 has been a busy month for the problem. The US Navy showed drones landing on a moving ship with GPS jammed, using only onboard cameras and motion sensors. The European Space Agency signed a contract to keep drones flying for 10 minutes with satellite signals jammed or spoofed. Sightec, an Israeli company that builds camera based positioning for drones, raised about $5.5 million. As one Navy engineer put it, "GPS is the first operational link an adversary attempts to sever."

This post explains how a drone keeps track of where it is without GPS, why every method on its own drifts, and how the best systems correct that drift by checking a map. At the end, a short Python example flies a simulated drone for 10 minutes with GPS off and shows the difference in numbers: about 4.3 km of error with an IMU alone, 139 m with visual odometry, and 5 m once the drone checks a map with a particle filter. This is the same problem behind Veg, our quadcopter that flies without GPS.

GPS free flight at a glance

The problemGPS can be jammed, spoofed or blocked, and a drone that loses it no longer knows where it is
Relative methodsIMU dead reckoning, visual odometry, LiDAR odometry: they measure motion, so their error keeps growing
Absolute methodsMatching camera images or terrain to a stored map, known landmarks, radio signals such as cell towers: they fix the error, but only when there is something to match
How they combineA filter, often a Kalman filter or a particle filter, uses motion to predict and the map to correct
In the news, October 2026US Navy GPS free ship landings, ESA's 10 minute goal with Rift Dynamics, Sightec funding, Elsight cellular positioning
Our Python resultAfter 10 minutes: IMU alone 4.3 km off, visual odometry 139 m off, visual odometry plus map matching 5 m off

Why GPS fails, and why it is news now

A drone can lose GPS in three ways. The signal can be blocked, under a bridge, inside a building or under a forest canopy. It can be jammed, where a transmitter floods the GPS frequencies with noise. Or it can be spoofed, where a transmitter sends fake satellite signals so the receiver reports a confident, wrong position. Spoofing is the worst of the three, since the drone does not even know it has a problem.

Jamming used to be a military concern. Now it affects ordinary flights, ships and farm machinery near conflict zones, and governments have started to put a price on it. Norway's government estimated that losing satellite positioning for seven days would cost the country NOK 3.4 billion, about EUR 320 million.

Here is what happened this month:

  • US Navy, ship landings without GPS (October 2026). The Naval Air Warfare Center Aircraft Division showed a tactical drone flying missions and landing on a pitching ship at sea with satellite signals jammed. The drone's camera recognises the ship's shape, and its control software predicts how the deck will move so it can touch down smoothly. It replaces radar systems that cost millions and take months to install on a ship.
  • ESA and Rift Dynamics, 10 minutes without GPS (28 October 2026). Under ESA's NAVISP programme, the Norwegian drone maker Rift Dynamics signed a contract to keep drones flying for 10 minutes while satellite signals are jammed or spoofed, with testing planned in the Arctic.
  • Sightec funding (29 October 2026). Sightec, which builds visual positioning for drones flying without GPS, raised about $5.5 million and says its system has over 4,000 deployments.
  • Elsight cellular positioning (13 October 2026). Elsight added a fallback that estimates position from cellular network signals, and raises an alert when GPS and the cellular estimate disagree, a simple way to catch spoofing.

Each of these uses a different trick. They all solve the same two part problem.

The two halves of flying without GPS

Robotics courses usually split localization into two questions: "How did I move?" and "Where am I?" A drone without GPS has to answer both.

The first question is answered by relative methods. They measure motion, then add it up. They work everywhere and update hundreds of times a second, but each small error is added to every position after it, so the total error keeps growing. This is called drift.

The second question is answered by absolute methods. They compare what the drone senses with something whose position is already known, such as a map, a landmark or a radio tower. Their error does not grow with time, but they only work when there is something to match.

Every serious system uses both. Relative methods carry the drone between fixes, and absolute methods pull it back when it drifts.

IMU dead reckoning: why the error explodes

An inertial measurement unit, or IMU, contains accelerometers and gyroscopes. In principle, integrate acceleration once to get velocity, again to get position, and you always know where you are, with no outside signal at all. Submarines and airliners use very expensive versions of this.

The catch is the double integration. Suppose the accelerometer has a tiny constant bias of 0.02 m/s², about two thousandths of gravity, which is ordinary for the cheap MEMS sensors in small drones. Integrated twice, that bias becomes a position error of ½ × 0.02 × t². After 1 minute that is 36 m. After 10 minutes it is 3.6 km. The error grows with the square of time, and nothing inside the IMU can notice it.

Gyroscopes have the same problem with heading, and a heading error is worse than it sounds: the drone then integrates its acceleration in the wrong direction. This is why an IMU on its own is useful for seconds, not minutes.

Visual and LiDAR odometry: drift that grows with distance

Visual odometry tracks features, such as corners and small patches of texture, from one camera frame to the next and works out how the camera moved between them. Visual inertial odometry, or VIO, fuses this with the IMU, and LiDAR inertial odometry does the same with laser scans. FAST-LIO, from the University of Hong Kong, is a widely used open source example of the LiDAR version.

These methods measure velocity directly instead of integrating it twice from acceleration, so their error grows far more slowly. A small error in scale, or a slow drift in heading, still adds up over distance. On a long flight, a fraction of a percent of drift turns into tens or hundreds of metres. Visual SLAM can remove some of this by recognising places it has seen before, as we covered in our visual SLAM guide, but a drone flying a straight route never revisits anywhere.

Map matching: the absolute fix

To stop drift, the drone needs to compare what it senses now with something fixed in the world. Common choices are:

  • Satellite image matching. A downward camera image is matched against stored satellite or aerial maps. This is the idea behind companies such as Sightec. It works well over textured ground such as towns, roads and fields with clear edges.
  • Terrain matching. A laser or radar altimeter, together with a barometer, gives the height of the ground under the drone. A sequence of ground heights is matched against a stored elevation map. This is a decades old idea from cruise missile guidance, and it is the method in our Python example, chosen as it is easy to simulate.
  • Known landmarks. The Navy's drone recognises the ship itself. Indoors, a robot can use fiducial markers or the walls of a known building.
  • Radio signals. Cell towers, Wi Fi and other transmitters give a rough absolute position, as in Elsight's system.
  • The Earth's magnetic field. A compass gives heading without GPS, which stops heading drift even when position still drifts.

Putting it together with a particle filter

The relative and absolute measurements are fused by a filter. An extended Kalman filter works well when the map gives one clear answer. Map matching often gives several possible answers at once: one hill looks much like another, and a field looks the same in many places. A particle filter handles this better.

A particle filter keeps thousands of guesses of the drone's position, called particles. Every time step it does three things:

  1. Predict. Move every particle by the motion the drone measured, plus a little random spread for the uncertainty in that measurement.
  2. Weight. Compare what the drone senses now with what the map says it should sense at each particle's position. Particles that agree get a high weight, particles that disagree get a low one.
  3. Resample. Draw a new set of particles, copying the high weight ones and dropping the low weight ones.

Wrong guesses die out, and the cloud of particles follows the drone's true position. Robotics calls this Monte Carlo localization. It was introduced for ground robots in 1999, and it is still the standard way a robot finds itself on a known map.

A Python example: 10 minutes without GPS

The example below flies a simulated drone at 20 m/s for 10 minutes, a 12 km route, over hilly ground, with GPS off from takeoff. It compares three ways to track position:

  1. IMU only. A MEMS style accelerometer with a small bias, integrated twice.
  2. Visual odometry. A camera that measures velocity with an error of 0.5 to 1.5% in scale, a slow drift in heading and some noise, integrated once.
  3. Visual odometry plus map matching. A particle filter with 2,000 particles that moves with the visual odometry and checks the ground height under the drone against a stored terrain map.

Each method runs on 5 different random flights and terrains, and we report the average. The code needs only NumPy and runs in about 10 seconds.

import numpy as np

# A drone flies for 10 minutes with GPS jammed from takeoff.
# We compare three ways to keep track of where it is:
#   1. IMU only: integrate the accelerometer twice (dead reckoning)
#   2. Visual odometry: integrate the velocity a downward camera measures
#   3. Visual odometry + map matching: a particle filter compares the
#      ground height under the drone with a stored terrain map
DT, MINUTES, SPEED = 1.0, 10, 20.0           # 1 s steps, 20 m/s
STEPS = int(MINUTES * 60 / DT)


def make_terrain(rng, n_waves=40, hills=True):
    # smooth hills: a sum of random waves, heights in metres
    # hills=False gives flat ground, like a lake or a large field
    k = rng.uniform(1 / 3000, 1 / 400, n_waves)       # wavelengths 400 m to 3 km
    ang = rng.uniform(0, 2 * np.pi, n_waves)
    kx, ky = k * np.cos(ang) * 2 * np.pi, k * np.sin(ang) * 2 * np.pi
    amp = rng.uniform(5, 25, n_waves) * (1.0 if hills else 0.0)
    ph = rng.uniform(0, 2 * np.pi, n_waves)

    def height(x, y):
        x, y = np.asarray(x)[..., None], np.asarray(y)[..., None]
        return 150 + (amp * np.sin(kx * x + ky * y + ph)).sum(-1)
    return height


def fly(rng):
    # true path: 20 m/s on a mostly straight route with gentle turns
    turn = np.zeros(STEPS)
    for t in range(1, STEPS):
        turn[t] = 0.97 * turn[t - 1] + rng.normal(0, 0.002)   # rad/s
    heading = 0.3 + np.cumsum(turn) * DT
    vel = SPEED * np.stack([np.cos(heading), np.sin(heading)], 1)
    pos = np.cumsum(vel * DT, 0)
    return pos, vel


def imu_only(rng, vel):
    # MEMS accelerometer: a small constant bias plus noise, integrated twice
    bias = rng.normal(0, 0.02, 2)                    # m/s^2
    acc_true = np.diff(np.vstack([vel[:1], vel]), axis=0) / DT
    acc_meas = acc_true + bias + rng.normal(0, 0.05, (STEPS, 2))
    v_est = vel[0] + np.cumsum(acc_meas * DT, 0)
    return np.cumsum(v_est * DT, 0)


def visual_odometry(rng, vel):
    # camera velocity: about 1% scale error, a slow heading drift, and noise
    scale = 1 + rng.choice([-1, 1]) * rng.uniform(0.005, 0.015)
    drift = np.cumsum(rng.normal(0, 0.0005, STEPS))  # radians, random walk
    c, s = np.cos(drift), np.sin(drift)
    v = scale * np.stack([c * vel[:, 0] - s * vel[:, 1],
                          s * vel[:, 0] + c * vel[:, 1]], 1)
    return v + rng.normal(0, 0.3, (STEPS, 2))        # measured velocity


def map_matching(rng, v_meas, true_pos, terrain, n=2000):
    # particle filter: each particle is a guess of (x, y)
    p = np.zeros((n, 2))                             # takeoff point is known
    est = np.zeros((STEPS, 2))
    for t in range(STEPS):
        # move every guess by the camera velocity, plus random spread
        p += v_meas[t] * DT + rng.normal(0, 1.5, (n, 2))
        # laser altimeter + barometer give the ground height under the drone
        z = terrain(*true_pos[t]) + rng.normal(0, 2.0)
        w = np.exp(-0.5 * ((terrain(p[:, 0], p[:, 1]) - z) / 4.0) ** 2) + 1e-300
        w /= w.sum()
        est[t] = w @ p
        # resample: keep guesses that fit the map, drop the rest
        idx = np.searchsorted(np.cumsum(w), (rng.random() + np.arange(n)) / n)
        p = p[np.minimum(idx, n - 1)]
    return est


def run(seed, hills=True):
    rng = np.random.default_rng(seed)
    terrain = make_terrain(rng, hills=hills)
    pos, vel = fly(rng)
    v_meas = visual_odometry(rng, vel)
    paths = {"IMU only": imu_only(rng, vel),
             "visual odometry": np.cumsum(v_meas * DT, 0),
             "VO + map matching": map_matching(rng, v_meas, pos, terrain)}
    return {k: np.linalg.norm(v - pos, axis=1) for k, v in paths.items()}


if __name__ == "__main__":
    results = [run(seed) for seed in range(5)]
    print(f"{MINUTES} minute flight, {SPEED * MINUTES * 60 / 1000:.0f} km, GPS off")
    print(f"{'method':<20}{'error at 1 min':>16}{'error at 10 min':>17}")
    for name in results[0]:
        e1 = np.mean([r[name][59] for r in results])
        e10 = np.mean([r[name][-1] for r in results])
        print(f"{name:<20}{e1:>14.0f} m{e10:>15.0f} m")

    flat = [run(seed, hills=False) for seed in range(5)]
    e10 = np.mean([r["VO + map matching"][-1] for r in flat])
    print(f"\nsame flights over flat ground: VO + map matching, {e10:.0f} m at 10 min")

Running it prints:

10 minute flight, 12 km, GPS off
method                error at 1 min  error at 10 min
IMU only                        44 m           4284 m
visual odometry                 14 m            139 m
VO + map matching                3 m              5 m

same flights over flat ground: VO + map matching, 139 m at 10 min
Drone without GPS in Python: position error over a 10 minute flight, IMU only reaches 4.3 km, visual odometry 139 m, visual odometry with map matching stays near 5 m
The same 5 simulated flights for each method. The IMU error grows with the square of time, visual odometry grows with distance, and map matching stays flat.

The IMU alone is 44 m off after one minute and 4.3 km off after ten, close to the ½ × bias × t² estimate from earlier. Visual odometry does far better, at 139 m, but its error is still climbing at the end of the flight and would keep climbing on a longer one. With map matching the error stays at about 3 to 6 m for the whole flight, which is roughly the accuracy of the altimeter and the map, not of the time spent flying.

The last line of the output is the honest part. Run the same flights over flat ground and map matching ends 139 m off, exactly the same as visual odometry alone. Over a flat field or a lake, every particle measures the same ground height, so the map cannot tell good guesses from bad ones, and the filter can only follow the camera. Map matching is only as good as the map's variety.

What still does not work

GPS free flight is moving fast, but some problems are far from solved:

  • Featureless ground. Water, snow, desert and dense forest look the same everywhere, as our flat ground test shows. Satellite image matching has the same problem over textureless ground.
  • Maps that are out of date. Satellite images change with the seasons, the weather, construction and time of day. A map from summer can look nothing like the same field in winter or at night.
  • Spoofing. A spoofed GPS signal looks fine to the receiver. Systems need a second, independent position estimate to notice that something is wrong, as Elsight does with cellular positioning.
  • Small computers. Matching images against large maps in real time is heavy work for the computer on a small drone, and it competes with everything else the drone has to run.
  • Proof. A demo flight is not a safety case. Regulators and buyers want long, repeatable tests across weather, light and terrain, and few systems have published them.

Frequently asked questions

Can a drone fly without GPS?

Yes. A drone can track its motion with an IMU, a camera or a LiDAR, and correct the resulting drift by matching what it sees against a stored map or known landmarks. Indoor drones and research drones have done this for years, and in 2026 military and commercial drones are adopting it as GPS jamming spreads.

Why does an IMU drift so fast without GPS?

An IMU measures acceleration, which has to be integrated twice to give position. A small constant bias in the accelerometer becomes a position error that grows with the square of time. A bias of 0.02 m/s² gives about 36 m of error after one minute and about 3.6 km after ten.

What is the difference between visual odometry and visual SLAM?

Visual odometry estimates how the camera moved from frame to frame, so its error builds up over distance. Visual SLAM builds a map as it goes and corrects its position when it recognises a place it has seen before. On a straight route with no revisits, SLAM behaves much like odometry.

What is map matching for drones?

Map matching compares what the drone senses, such as a camera image or the height of the ground below, with a stored map whose positions are known. When the two match, the drone gets an absolute position fix that does not drift over time. It needs ground with enough variety to match against.

How does a particle filter help a drone localize?

A particle filter keeps thousands of guesses of the drone's position. It moves them with the measured motion, scores each one by how well the map agrees with the sensors, then keeps the good guesses and drops the bad ones. The cloud of guesses settles on the true position and can track several possibilities at once.

Closing thoughts

For twenty years, most drones have treated GPS like gravity: something that is always there. 2026 is the year that assumption broke, and the answers coming back are not new inventions. Dead reckoning, landmarks and map matching are older than GPS itself. What changed is that cameras, small computers and learned image matching are now good and cheap enough to run them on a small drone.

If you run the example, try changing the map: make the hills smaller, add a flat lake in the middle of the route, or give the altimeter more noise, and watch when the particle filter loses its grip. We would like to hear what you find.

References

  • Naval Air Warfare Center Aircraft Division, GPS denied drone flights and autonomous ship landings, reported by Ocean News and Technology, October 2026. oceannews.com
  • Marine Insight, US Navy drone lands on a moving ship without GPS or radar, October 2026. marineinsight.com
  • DroneXL, ESA funds Ondas backed drone maker to fly 10 minutes without GPS, October 2026. dronexl.co
  • Dealroom, Sightec raises funding for GPS free drone flight, October 2026. dealroom.co
  • UAS Weekly, Elsight unveils GPS independent positioning for drones at AUSA 2026, October 2026. uasweekly.com
  • Z. Lu, F. Liu and X. Lin, Vision-based localization methods under GPS-denied conditions, 2022. arXiv:2211.11988
  • F. Dellaert, D. Fox, W. Burgard and S. Thrun, Monte Carlo Localization for Mobile Robots, ICRA 1999.
  • W. Xu and F. Zhang, FAST-LIO: A Fast, Robust LiDAR-Inertial Odometry Package by Tightly-Coupled Iterated Kalman Filter, IEEE Robotics and Automation Letters, 2021. arXiv:2010.08196