Skip to content

Fix stock Simulink example lifecycle - #227

Merged
jonyMarino merged 2 commits into
iamaisim:mainfrom
starbelt:fix-stock-simulink-lifecycle
Oct 16, 2026
Merged

jonyMarino merged 2 commits into
iamaisim:mainfrom
starbelt:fix-stock-simulink-lifecycle

Conversation

@khoanguyen25-aero

@khoanguyen25-aero khoanguyen25-aero commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

About

This PR fixes startup, interruption, and cleanup in the four stock Python Simulink examples in client/python/example_user_scripts/:

  • simulink_physics_quadrotor.py
  • simulink_physics_quadtiltrotor.py
  • simulink_controller_quadrotor.py
  • simulink_combined_quadrotor.py

On current main, these examples have the following problems:

  • Errors during startup or flight are logged and then swallowed, so a failed run exits with status 0.
  • Cleanup runs as one sequence. If one step fails (for example drone.disarm() or the model stop), the remaining steps are skipped, including client.disconnect(), and the next run cannot connect.
  • Waiting for the model to start (start_future.result()) and stopping it (background=False) have no time limit. If the model never starts or never stops, the script hangs.
  • The model is compiled as part of start, while the scene is loading. Once the scene runs, the simulator's MATLAB physics or controller step retries the model's TCP connection until the model is running, so the simulation waits for compilation, and compilation errors appear only after the scene has loaded.
  • MATLAB Engine handles Ctrl+C during a wait by cancelling the call and returning None. The examples treat that as success, so pressing Ctrl+C while the model starts continues to the flight sequence.
  • The usage text in simulink_controller_quadrotor.py names load_quadrotor_simulink_control_model, but the example uses the model loaded by load_quadrotor_simulink_control_model_navigation.

This change:

  • Adds simulink_utils.py, a small helper module shared by the four examples.
  • Connects to the shared MATLAB session in the background. If the connection fails, the script raises an error that tells the user to run the Simulink model loader script in MATLAB first; the original MATLAB error is kept as the cause.
  • Compiles the model (SimulationCommand update) before connecting to the simulator, then starts it in the background while the scene loads.
  • Gives every MATLAB wait a time limit, defined once as named constants: connect 60 s, compile 120 s, start 120 s, stop 15 s. On a timeout or failure, the pending MATLAB call is cancelled.
  • Treats a MATLAB call cancelled by Ctrl+C as an interruption instead of success. If Ctrl+C arrives just as a call finishes, the call's result is read again so a MATLAB error is not lost.
  • Runs each cleanup step separately: cancel a pending model start, cancel the last task, disarm, release API control, stop the model, and disconnect the client. A failing step is logged and the remaining steps still run. A cleanup error does not replace the error that caused the shutdown.
  • Re-raises errors after logging a one-line message, so the traceback is printed once and the script exits with a non-zero status.
  • Corrects the loader script name in the simulink_controller_quadrotor.py usage text.
  • Adds offline tests that run each example against simulated MATLAB and simulator connections.

Behavior that stays the same:

  • The flight sequences, scenes, model names, and loader scripts.
  • The MATLAB controller keeps control of the vehicle in the controller-only and combined examples; the examples do not arm or command the drone there.
  • The shared MATLAB session and desktop stay open after the script exits.

Behavior differences users will notice:

  • A failed run now exits with a non-zero status and prints a traceback. Previously it logged the error and exited with status 0.
  • A missing MATLAB session now raises an error with the setup hint instead of logging the hint and exiting normally.
  • The model compiles before the script connects to the simulator, and the log reads "Connecting to Matlab and compiling Simulink model".
  • If MATLAB does not connect, compile, start, or stop the model within the limits above, the script reports a timeout instead of waiting indefinitely. A stop timeout is reported and the client still disconnects; the model may then need to be stopped in MATLAB.

How Has This Been Tested?

Offline tests, run from client/python/projectairsim/tests/ with Python 3.14 on Linux. These tests do not need MATLAB or a simulator:

PYTHONPATH=../src python -m pytest test_simulink_examples.py --sim-host offline -q
# 100 passed, 8 skipped

PYTHONPATH=../src python -m pytest test_*.py --sim-host offline -q
# 129 passed, 8 skipped, 117 deselected
  • The 8 skipped cases are flight-control cleanup cases that do not apply to the controller-only and combined examples.
  • The 117 deselected tests belong to the Unreal, Runtime, and PX4 suites.
  • Running the whole test directory stops during collection because test_datacollection/conftest.py requires scikit-image, which was not installed in the test environment.
  • The new tests cover MATLAB connection failure (setup hint with the original error as the cause), Ctrl+C during connect, compile, and start, a MATLAB error hidden by a late Ctrl+C, single-traceback logging, timeouts, and partial-startup and cleanup failures.
  • git diff --check passes.

Earlier live validation of the lifecycle changes, before the latest update, used MATLAB R2026b on Linux with a Project AirSim Runtime build that also contained other changes, not a rendered Unreal session:

  • simulink_physics_quadrotor.py completed takeoff, flight, and landing.
  • simulink_controller_quadrotor.py started and stopped.
  • simulink_combined_quadrotor.py stalled during startup and shutdown inside the native MATLAB transport, with both the original and the revised script. The revised script reports the stop timeout and disconnects its client, but a Python example cannot release an S-function that is blocked waiting for a request. The native fix is proposed separately in Fix MATLAB Runtime contact and transport lifecycle #229.

After this update, the revised examples were run again together with the native fixes from #229 (headless Runtime on Linux, MATLAB R2026b): the physics example completed takeoff, flight, and landing; the controller-only example started and stopped; and the combined example started and stopped twice in one MATLAB session. Each model ended stopped and each Runtime exited with code 0.

Not tested:

Related pull requests

Suggested merge order: #227 → #228 → #229 → #230. #230 also edits client/python/projectairsim/tests/README.md, so whichever of the two merges later will be rebased.

Screenshots and videos (if appropriate):

Not applicable.

Connect to the shared MATLAB session in the background so Ctrl+C cannot
leave the example with no engine, and report a missing session with the
loader-script hint. Re-read finished MATLAB calls so an interrupt that
arrives as a call completes does not hide its error. Name the lifecycle
timeouts and print each failure traceback once.
@jonyMarino

Copy link
Copy Markdown
Contributor

Thanks for your contribution! keep it up!

@khoanguyen25-aero

Copy link
Copy Markdown
Contributor Author

Thanks for your contribution! keep it up!

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants