claudeDroneteam-docs
documentation · reference
Docs reference

Structured knowledge from collected_doc_media/claudedrone_docs/. Browse the tree on the left; the source of truth is markdown in the repo.

37 — Manual Flight: Altitude Up/Down in Hover, plus Land

Fixing the altitude slider so it actually moves the drone up and down in hover, killing the drift, and adding a clean land command.

stablesimulationupdated 2026-06-16T00:00:00.000ZClaudeDroneSimulationDevLog

What this is: The story of why our altitude slider quietly lied to us, and how we rebuilt manual flight so the drone really climbs, descends, holds its spot, and lands when you tell it to.

Why it’s here: Manual flight is the first thing a human reaches for when they want to “just fly the drone.” If the basics — go up, go down, stay put, land — don’t feel solid, nothing built on top of them will either. This entry documents getting those basics right.

Date: 2026-06-11 Ticket: Manual flight — takeoff to a target altitude, land, and reliable up/down altitude control. “Do it properly, by the docs, and remove the junk that gets in the way.”


Glossary

A few plain-English definitions before we dive in.

  • Manual flight — the mode where a human drives the drone directly: nudge it forward, raise it, lower it, land it. The opposite of “the autopilot flies itself.”
  • Hover — the drone holding still in the air, like a helicopter parked over one spot. No forward motion, no sideways motion, just staying put.
  • Altitude hold — keeping a chosen height steady. Think of cruise control, but for vertical position: you pick a number, the drone keeps that number.
  • Altitude slider — the up/down control in our interface. Drag it up, the drone should climb; drag it down, it should descend.
  • Land command — a single button that brings the drone down gently and shuts off the motors at the bottom. Like telling an elevator “ground floor, then power off.”
  • Setpoint — the target the autopilot is chasing. A “position setpoint” says be at this spot; a “velocity setpoint” says move at this speed.
  • Drift — when the drone slowly slides sideways even though you didn’t ask it to. Annoying at best, a crash into a wall at worst.
  • SITL — Software In The Loop. The real flight-control software running on a normal computer with no actual hardware, so we can test flights safely in simulation.

1. The symptom: a slider that did nothing

Here’s the bug exactly as a user would hit it.

You take off. The drone is hovering. You grab the altitude slider and drag it up. The number on screen changes — the target altitude goes from, say, 1.0 m to 2.0 m. And the drone… just sits there at takeoff height. Nothing moves.

It got worse if you were also flying around at the time (sending movement commands). In that case the drone would overshoot the target height badly and slide sideways:

  • It blew past the target on the way down: height dropped from about 1.86 m all the way to 0.22 m when it should have settled higher.
  • It drifted roughly 1.1 m toward a wall.
  • It crashed early.

So we had two failure shapes: in a still hover the slider was ignored entirely, and during movement the altitude control overshot while the drone wandered off course.

2. Root cause: the height controller was living in the wrong place

When we traced it, the picture was clear and a little embarrassing.

The piece of logic that actually moves the drone toward a target height only existed inside the velocity path — the code that runs when you’re actively giving movement commands. The moment you stopped moving, the drone fell back to a different path that simply held a fixed height: whatever it was at when it took off.

So:

  • In hover (no movement commands): the slider updated the target number, but the active “hold this exact position” instruction was still pinned to the old height. The drone faithfully held the old height and ignored the new target. The slider was talking to a controller that wasn’t listening.
  • During flight (movement commands): the velocity path moved the drone but never held its horizontal position, so it drifted — and the height tracking overshot.

There was also a second, unrelated gotcha discovered the same day. The spawn point for our lab_room test world had been placed at the origin (0, 0) — which happened to be exactly where a floor-to-ceiling pillar stood. The drone was spawning inside the pillar: invisible, jammed in a collision, unable to take off. We moved the spawn to a clear spot.

3. The fix: one manual controller that owns altitude in every mode

The real fix was to stop having two separate paths fight over the drone, and instead have a single manual-flight controller that’s switched on right after takeoff. (We left the autonomous flight path completely untouched — this only affects manual flying.)

The new controller behaves differently depending on whether you’re actively flying or just hovering, and crucially it handles altitude correctly in both:

Situation What the controller sends What you get
Hover (no recent movement command) A position setpoint: hold the current x, y and yaw, but aim for the target altitude The autopilot holds your spot and smoothly walks the drone to the new height — the slider works in hover, no drift
Flying (a fresh movement command within the last half-second) A velocity setpoint: move at the requested speed, with a vertical speed proportional to how far you are from the target height, plus a safety clamp Smooth movement; when you let go of the stick it locks onto the spot it’s at

The key insight: in hover we let the autopilot’s own position controller do the climbing. It already knows how to glide between heights gently — we just needed to give it the new target instead of a frozen old one.

A couple of supporting details:

  • Changing the target altitude now only touches the target number. Both modes read that number, so up/down works whether you’re parked or moving.
  • The “am I still settling toward the target height?” status shown in the interface is now derived straight from the gap between current and target height, so the UI reflects reality.

Landing

The land command became its own clean path. Pressing land first tells the manual controller to stop streaming its hold-position instructions (otherwise they’d keep overriding the descent), then issues the actual land request: switch to landing mode, descend, and shut off the motors at the bottom.

A note on low takeoffs

We build on the autopilot’s standard position-and-yaw targeting and the standard ENU-to-NED coordinate conversion that mavros provides. One real-world caveat: very low takeoff targets (around half a meter) are unreliable near the ground, because the autopilot’s barometer-based height estimate gets erratic close to the floor — this is a documented behavior of altitude-hold mode. The practical rule: take off to at least 1.0 m, then use the slider to come down to lower heights once you’re stably airborne.

4. Verification

We wrote a headless smoke test in the lab_room world. It takes off to 2.0 m, then drives the slider through a sequence of heights, then lands:

set_altitude 1.00 → z=1.00m drift=0.00m  PASS
set_altitude 2.00 → z=2.00m drift=0.00m  PASS
set_altitude 0.60 → z=0.60m drift=0.00m  PASS
land            → z=0.27m armed=false    PASS
=== PASS ===

Every commanded height was reached exactly, with zero horizontal drift at each step — a direct contrast to the 1.1 m wall-ward slide we started with. The land command brought the drone down to 0.27 m and confirmed the motors were off (disarmed).

The full height trace was logged to a CSV with 136 data points, with measured height ranging from 0.27 m to 2.02 m — clean coverage of the whole tested range.

5. Known limitations

Two honest caveats remain:

  • Taking off again after landing is unreliable in SITL. For a fresh flight, restart the simulation stack rather than trying to re-launch in place.
  • Takeoff targets below 1.0 m don’t climb because of the near-ground barometer issue described above. Reach low heights by taking off higher and using the slider to descend.

6. Files touched

  • action_executor.py — the new manual-flight controller, the enter/exit hooks, the branch in the maintenance stream, and the max vertical-speed cap.
  • manual_fly_node.py — the land subscription, entering manual flight after takeoff, and handling land in the main loop.
  • sitl_comm.py — a public land call.
  • lab_room.sdf — moved the spawn point out of the pillar.
  • altitude_land_smoke.py — the smoke test.
© 2026 claudeDrone Team · auto-pipeline · Nuxt 3 SSR