[SimpleFlight] takeoff_async() ends early and returns False in stock Blocks
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 50/100
Research direction
Start with vehicle_apis/multirotor_api/src/multirotor_api_base.cpp, especially MoveOnPathInternal and MoveToPathPosition, then compare their completion behavior with the reported takeoff measurements. Reproduce with the provided Python script against stock Blocks v1.0.1 and inspect the default scene and quadrotor settings if needed. Done means takeoff reaches the requested 2 m height and reports success or a failure reason; the report says latest main was not built or runtime-tested.
Written by the indexing model from the issue text.
Description
Bug report
What's the issue you encountered?
In the official Blocks demo, the drone starts taking off, but the command ends before it reaches its takeoff height. takeoff_async() returns False after about 3 seconds. Increasing the timeout from 20 to 60 seconds does not resolve it. This happens with the default scene and drone configuration.
Expected: the drone completes takeoff and reports success, or reports a failure with a reason.
Include full error message in text form
No exception or error message is returned—only False.
| Command | Return | Elapsed simulation time | Actual ascent |
|---|---|---|---|
| Takeoff, 20 s timeout | False | 2.709 s | 1.258 m |
| Takeoff, 60 s timeout | False | 3.234 s | 1.517 m |
| Height-setpoint control, 6 s duration | True | 6.000 s | 1.998 m |
The stock Simple Flight setting requests a 2 m ascent (takeoff_z=-2.0f). For comparison, move_by_velocity_z_async() reaches that height in the same scene. Its arrival was checked using actual position, not just its return value.
Environment
- Project AirSim: official Windows UE 5.2 Blocks v1.0.1; server/package commit
deff6aa11dd8961ae5de05ea64630995a12bbf67. - Autopilot: built-in Simple Flight, quadrotor-x, Fast Physics.
- OS: Windows 11 x64. Python 3.11.17;
projectairsim==1.0.2. - Latest
main: relevant source inspected at7b1f4124ca2e275674ab2911da62c531ccc1cd8a; not built or runtime-tested.
Settings
Unmodified v1.0.1 scene_basic_drone.jsonc and robot_quadrotor_fastphysics.jsonc. Default 3 ms steppable clock; Blocks running windowed with a 60 FPS cap.
How can the issue be reproduced?
-
Start official Blocks v1.0.1 and wait for it to finish loading.
-
Save the script below as
repro_stock_takeoff.pyand run it with Python client 1.0.2:python repro_stock_takeoff.py --sim-config <release-example-sim_config-directory> --output evidence-newSet
--sim-configto the release'sclient/python/example_user_scripts/sim_configdirectory. -
The script reloads the scene for each case, waits for the drone to settle, enables control, arms, and records the command result and height. Each case ends with landing and disarming.
Possible cause—please confirm
- Once the lookahead target reaches the final segment,
MoveOnPathInternalcan exit whengoal_dist <= 0.01. That variable measures movement along the path, not the remaining distance to the endpoint. Could this end takeoff before completion is marked? MoveToPathPositionalso commands zero vertical speed within 0.5 m of its local target. The comment describes an oscillation workaround. How should this interact with the takeoff completion check?
These are investigation leads; I have not validated a source fix. I found no matching Issue or pending PR during the 2026-10-23 search.
Reproduction code
Complete script used for the stock Blocks reproduction
"""Reproduce takeoff in stock Blocks using unmodified release JSONC files."""
import argparse
import asyncio
import hashlib
import json
import math
import platform
import time
from datetime import datetime
from importlib.metadata import version
from pathlib import Path
from projectairsim import Drone, ProjectAirSimClient, World
async def resolve(request, timeout=90):
return await asyncio.wait_for(await request, timeout)
def speed(state):
return math.sqrt(sum(v*v for v in state["twist"]["linear"].values()))
async def run_case(config, output, mode, timeout):
path = output / f"{mode}-{timeout}.json"
if path.exists():
raise FileExistsError(path)
record = {"mode": mode, "timeout_sec": timeout,
"started_at": datetime.now().astimezone().isoformat(),
"python": platform.python_version(), "client_version": version("projectairsim"),
"scene": "scene_basic_drone.jsonc", "config_hashes": {}}
for name in ("scene_basic_drone.jsonc", "robot_quadrotor_fastphysics.jsonc"):
record["config_hashes"][name] = hashlib.sha256((config/name).read_bytes()).hexdigest()
client = ProjectAirSimClient(address="127.0.0.1")
drone = None
armed = False
try:
client.connect()
client.socket_services.recv_timeout = 90000
record["server_commit"] = client.get_build_commit_hash()
world = World(client, "scene_basic_drone.jsonc", sim_config_path=str(config),
delay_after_load_sec=3)
drone = Drone(client, world, "Drone1")
initial = drone.get_ground_truth_kinematics()
if speed(initial) > 0.05 or int(drone.get_landed_state()) != 0:
raise RuntimeError("Virtual drone has not settled on the ground")
record["initial"] = initial
if not drone.enable_api_control() or not drone.arm():
raise RuntimeError("Virtual drone API control/arming rejected")
armed = True
start = time.perf_counter()
sim_start = world.get_sim_time()
if mode == "native":
record["api_result"] = await resolve(drone.takeoff_async(timeout_sec=timeout))
else:
target_z = initial["pose"]["position"]["z"] - 2.0
record["api_result"] = await resolve(
drone.move_by_velocity_z_async(0, 0, target_z, duration=6))
record["wall_seconds"] = time.perf_counter()-start
record["sim_seconds"] = (world.get_sim_time()-sim_start)/1e9
record["final"] = drone.get_ground_truth_kinematics()
record["ascent_m"] = (initial["pose"]["position"]["z"]
- record["final"]["pose"]["position"]["z"])
record["height_error_m"] = abs(record["ascent_m"]-2.0)
record["arrival_verified"] = record["height_error_m"] <= 0.2
except Exception as exc: # noqa: BLE001 - preserve evidence, fail after cleanup
record["error"] = repr(exc)
finally:
if drone is not None and armed and client.state:
try:
record["land_result"] = await resolve(drone.land_async(timeout_sec=25), 35)
ground = drone.get_ground_truth_kinematics()
record["ground"] = ground
if abs(ground["pose"]["position"]["z"]-initial["pose"]["position"]["z"]) > 0.1 or speed(ground) > 0.1:
raise RuntimeError("Touchdown not verified")
record["disarm_result"] = drone.disarm()
await asyncio.sleep(0.5)
record["final_landed_state"] = int(drone.get_landed_state())
drone.disable_api_control()
except Exception as exc: # noqa: BLE001 - retain cleanup failure
record["cleanup_error"] = repr(exc)
if client.state:
client.disconnect()
path.write_text(json.dumps(record, indent=2), encoding="utf-8")
print(json.dumps({k: v for k, v in record.items()
if k not in {"initial", "final", "ground"}}), flush=True)
if record.get("error") or record.get("cleanup_error"):
raise RuntimeError(f"Diagnostic infrastructure failed; inspect {path}")
async def main(args):
args.output.mkdir(parents=True, exist_ok=True)
for mode, timeout in (("native", 20), ("native", 60), ("altitude", 20)):
await run_case(args.sim_config, args.output, mode, timeout)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--sim-config", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
asyncio.run(main(parser.parse_args()))
- Dominant language
- C++
- Stars
- 890
- Forks
- 170
- Avg merge
- 17h 15m
- Merged PRs (30d)
- 8
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from iamaisim/ProjectAirSim
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
iamaisim/ProjectAirSim#241 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
iamaisim/ProjectAirSim#239 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
iamaisim/ProjectAirSim#240 ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
iamaisim/ProjectAirSim#169 ·
Maintainers usually reply within 1 day
-
Crash (SIGSEGV) in setParameter()/getParameter() retry for COM_OBL_ACT after HIL TCP send failuresOpen
Difficulty 4/5 3-5 days Newbie friendliness 48/100
iamaisim/ProjectAirSim#168 · 4 comments ·
Maintainers usually reply within 1 day
All issues in iamaisim/ProjectAirSim
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
scp-fs2open/fs2open.github.com#7965 ·
Maintainers usually reply within 1 day
-
[Bug][CuTeDSL][Fuzz] `utils.gemm.sm100.epilogue` releases the accumulator before `tcgen05.ld` completes: no `tcgen05.wait::ld` before the arrive that lets the next MMA overwrite TMEMPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 4 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
elf-alchemist/elfbsp#113 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
patrick-hudson/put.gd-desktop#979 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Dao-AILab/flash-attention#3097 ·
Maintainers usually reply within 3 days