Uav toolbox VTOL Ref.App Plant model with PX4 Sitl

14 Comments
Hi @AbdulKadir, thanks for your message. To better assist you, could you please provide a few more details?
- What version of PX4 and MATLAB/Simulink are you using?
- Are you running the SITL simulation on the same machine as Simulink, or are they on separate systems?
- What is the communication interface you're using (e.g., UDP, serial)?
- How exactly are you connecting and sending actuator commands from Simulink to PX4?
- Are you observing any delay or packet loss logs in PX4 or Simulink?
From your description, it sounds like a possible communication bottleneck or synchronization issue between Simulink and PX4 SITL, but more context would help narrow it down.
Looking forward to your response so we can help you troubleshoot further!
Hi @AbdulKadir,
Thank you for reaching out. I've thoroughly researched your PX4 v1.14.0.3/v1.15 SITL + MATLAB 2025B issue with the VTOL plant model, and I've identified the root causes of the delayed response and erratic vehicle behavior you're experiencing. After examining your setup as shown in attached screenshots and reviewing MathWorks documentation, PX4 resources, and the interfaces you've tried (Kiril Boychev's connector, Simulink TCP blocks, and Michael Skadan's UDP interface), I can confirm your suspicion: the problem isn't the interface—it's fundamental configuration issues affecting all three approaches. You're experiencing classic lockstep synchronization failure combined with WSL2 networking problems. This explains why: * Commands take "almost seconds" to respond * The vehicle behaves independently and spins uncontrollably * Message frequencies drop in QGC's MAVLink Inspector * All three interfaces show identical symptoms
So, basically there are three critical problems
WSL2 Network Configuration (CRITICAL!) When PX4 SITL runs in WSL2 and MATLAB/QGC run in Windows, you cannot use localhost (127.0.0.1) for TCP connections. Windows applications must connect to WSL2's ethernet adapter IP address.
Fix:
In WSL2 terminal:
# Find your WSL2 IP address ip addr show eth0 | grep inet # Example output: inet 172.31.64.1/20
# Use this IP in your MATLAB TCP blocks instead of 127.0.0.1
In Windows Firewall: * Create inbound rules for TCP port 4560, UDP ports 14540 and 14550
In Simulink TCP blocks:
tcpAddress = '172.31.64.1'; % Your actual WSL2 IP, NOT 127.0.0.1 tcpPort = 4560;
This alone may be causing most of your delay issues, as packets are being routed through WSL2's network translation layer with significant latency.
Missing Simulation Pacing Your Simulink plant model is running as fast as possible without pacing, while PX4 lockstep expects real-time synchronized sensor updates. This causes timing drift and desynchronization.
Fix: In your plant model: * Go to Simulation > Pacing Options * Enable "*Enable pacing to slow down simulation*" * Set pacing rate to 1.0 (real-time)
Or programmatically:
set_param('your_plant_model', 'EnablePacing', 'on');
set_param('your_plant_model', 'PacingRate', '1.0');
Sample Time and Solver Configuration
Running your plant as continuous with a 0.004s discrete interface creates interpolation issues and timing jitter.
Fix:
% Model configuration:
set_param('your_model', 'SolverType', 'Fixed-step');
set_param('your_model', 'FixedStep', '0.004'); % 250 Hz
% Plant subsystem: Change from continuous to discrete % Sample time: 0.004 (explicit, not inherited)
Some additional required checks along with that I will recommend
HIL_SENSOR Message Completeness
Your HIL_SENSOR messages must include ALL required fields with proper timestamps:
% Critical fields that must be present: hil_sensor.time_usec = monotonic_timestamp; % Must increment by 4000 each step hil_sensor.xacc, yacc, zacc % Accelerometer (m/s²) hil_sensor.xgyro, ygyro, zgyro % Gyroscope (rad/s) hil_sensor.xmag, ymag, zmag % Magnetometer (Gauss) hil_sensor.abs_pressure % Barometric pressure (hPa) hil_sensor.pressure_alt % Altitude from pressure (m) hil_sensor.temperature % Temperature (°C) hil_sensor.fields_updated = uint32(127); % Bitmask: 0x7F = all fields valid
The timestamp must be monotonically increasing by exactly 4000 microseconds per step. Any gaps, reversals, or incorrect increments will break lockstep.
HIL_STATE_QUATERNION Message
You also need to send vehicle state updates:
hil_state.time_usec = same_timestamp_as_sensor; hil_state.attitude_quaternion = [q0, q1, q2, q3]; hil_state.rollspeed, pitchspeed, yawspeed % rad/s hil_state.lat, lon, alt % Position (scaled: lat/lon *1e7, alt in mm) hil_state.vx, vy, vz % Velocity (cm/s) hil_state.xacc, yacc, zacc % Accelerometer (mg)
Here are debugging steps
1. Verify WSL2 IP connectivity first: # In WSL2, start PX4: 2. cd ~/PX4-Autopilot 3. make px4_sitl none 4. # In Windows MATLAB, test TCP connection to WSL2 IP 5. Add diagnostic logging in Simulink: * Log timestamps sent in HIL_SENSOR messages * Log time intervals between received HIL_ACTUATOR_CONTROLS * Check for TCP send/receive errors 6. Verify timing after simulation: time_diffs = diff(timestamps_sent); 7. % Should consistently equal 4000 microseconds 8. actuator_intervals = diff(actuator_receive_times); 9. % Should be ~4ms (250 Hz), not seconds 10. Check QGC MAVLink Inspector: * Look for HIL_SENSOR frequency (~250 Hz) * Verify ATTITUDE, GLOBAL_POSITION_INT rates (~50 Hz) * Check for dropped messages or rate degradation
If you continue experiencing issues, I strongly recommend switching to the official MathWorks UAV Toolbox approach, which handles all timing, lockstep, and message formatting automatically:
1. Install UAV Toolbox Support Package for PX4 Autopilots 2. Use two separate MATLAB instances: * Instance 1: Controller on PX4 Host Target * Instance 2: Plant model with simulation pacing 3. Follow the official example:openExample('px4/MonitorTunePX4HostTargetFlightPlantModelExample') This approach is specifically designed for your use case and eliminates manual timing management. Once properly configured, you should see:
- Immediate response to QGC commands (<100ms latency)
- Stable control throughout the flight with no autonomous spinning
- Steady message rates in MAVLink Inspector
- Successful arming without timestamp errors
- Proper vehicle dynamics matching your commanded inputs
Next Steps
I recommend addressing these issues in order: 1. Fix WSL2 networking (highest priority—likely causing most of your delay) 2. Enable simulation pacing 3. Convert plant to fixed-step discrete 4. Verify HIL message completeness 5. Add diagnostic logging to confirm timing
Please let me know which interface you'd like to continue with, and I can provide more specific implementation details for that particular connector. All three interfaces should work once these fundamental issues are resolved.
The fact that you're seeing identical behavior across all three interfaces confirms this is a timing/configuration problem, not an interface bug—which is actually good news, as it means the solution will work regardless of your interface choice.
Additional Resources:
Hi @Abdulkadir,
That is good. You made some progress. Now, after going through your screen shots and comments, the caveat is Kiril’s S-func runs in lockstep but PX4 still sees low message rates. That usually means timing or message format mismatch, not network delay.
Try this next:
1. Run `listener sensor_combined` and `listener actuator_controls` in PX4 — check actual update rates (should be ~250 Hz). 2. Make sure `time_usec` increases exactly by 4000 µs each step and `fields_updated = 127`. 3. PX4 expects `HIL_STATE_QUATERNION`; Euler-only messages can cause drops. Try converting to quaternion before sending. 4. Log what your S-func sends (timestamps, attitude, accel, gyro) and compare with what PX4 reports. 5. If you still see 10–20 Hz, post a short `listener` dump and MAVLink Inspector screenshot — likely a field or scaling mismatch.
Let me know after going through these steps.
Hi @AbdulKadir,
Let me think about this little more carefully, I will have response in few hours.
Hi @AbdulKadir,
Thanks for your patience. Let me walk through each of your observations step by step.
Comment #1: Understanding the actuator_controls Topic Issue
You're getting "Topic actuator_controls did not match any known topics" because this topic doesn't exist in the way you're trying to access it.Here's what's actually happening: In PX4's uORB messaging system, actuator_controls isn't a standalone topic you can listen to directly. Instead, you need to listen to one of these topics:
For HIL/SITL simulation (which is what you're running):
listener actuator_outputs_sim
For general actuator outputs:
listener actuator_outputs
The actuator_outputs_sim topic is specifically designed for SITL, HITL, and SIH (Simulation-In-Hardware) with an output range of [-1, 1], which matches what you're seeing in your rotor commands. This is documented in the PX4 uORB message reference: https://docs.px4.io/main/en/msg_docs/ActuatorOutputs.html
So try running:
pxh> listener actuator_outputs_sim
This should show you exactly what PX4 is commanding to your motors in real-time. If you see the same 0-1 oscillations here that you're seeing in your Simulink model, then we know the problem is on the PX4 controller side, not in your plant or interface.
Comment #2: Your MAVLink Message Frequencies (30-50 Hz)
Looking at your QGC MAVLink Inspector screenshot, I can see: * ALTITUDE: 1.0 Hz * ATTITUDE: 40.2 Hz * ATTITUDE_QUATERNION: 40.2 Hz * ATTITUDE_TARGET: 40.2 Hz * GLOBAL_POSITION_INT: 39.4 Hz
This is completely normal. These are telemetry stream rates to QGC, not your internal simulation rates. Here's why: 1. Your internal PX4 simulation is running at 250 Hz (confirmed by your sensor_combined dt of 4000 microseconds) 2. Your HIL sensor updates from Simulink are also at 250 Hz (confirmed by the same dt) 3. But telemetry to QGC is intentionally downsampled to save bandwidth - attitude at 40 Hz, position at 40 Hz, and low-priority messages at 1 Hz
This is by design. PX4 doesn't send every internal update to QGC because: * It would overwhelm the MAVLink connection * QGC's UI only updates at 30-60 fps anyway * Bandwidth is limited, especially on radio links
So don't worry about the 30-50 Hz you're seeing in QGC - your simulation is still running at full 250 Hz internally.
Comment #3: CRITICAL Discovery in Your Sensor Data I noticed something very important in your sensor_combined output: gyro_rad: [-16.21060, -12.10875, -14.12814] accelerometer_m_s2: [-1.66517, 0.68474, -15.94180]
These gyro rates are extremely high! Specifically: * Roll rate: -16.2 rad/s ≈ -928 degrees/s * Pitch rate: -12.1 rad/s ≈ -693 degrees/s * Yaw rate: -14.1 rad/s ≈ -808 degrees/s
This means your vehicle is already spinning violently in your simulation, even before you command it to take off. A stable vehicle sitting on the ground (or hovering steadily) should show gyro rates close to zero, maybe ±0.1 rad/s at most.This confirms what you're experiencing - the "turning around itself" behavior. The vehicle is genuinely out of control, and these sensor readings prove it. The question is: why?
Comment #4: The Rotor Command Oscillations - This is Your Real Problem
You made a critical observation: "I also saw same response of rotor commands in a Quadcopter example which works properly. So i guess the response is normal but behaviour of Uav to this commands are not suitable"
You're absolutely right. If the quadcopter example shows similar command patterns but flies correctly, while your VTOL doesn't, then the issue is not the commands themselves - it's how your plant is responding to those commands. Combined with those massive gyro rates, this points to one primary issue: motor configuration mismatch.
Comment #5: "I tried to change rotation direction of rotors but it didn't work"
This is a crucial clue. Let me explain exactly what needs to be changed and why simply "reversing" motor direction often doesn't work: The Standard Quadcopter X Configuration:
- Motor 1 (front-right): Counter-clockwise
- Motor 2 (front-left): Clockwise
- Motor 3 (rear-left): Counter-clockwise
- Motor 4 (rear-right): Clockwise
What You Must Change in Your Simulink Plant Model:
When changing motor rotation direction, you need to modify TWO things simultaneously, not just one:
1. Thrust Vector (usually already correct):
% For all motors, thrust always points downward in body frame Thrust_vector = [0; 0; -Thrust_magnitude];
2. Reaction Torque (this is what you probably missed):
% For CCW motors (1 and 3): Torque_reaction = +K_torque * Thrust_magnitude; % Positive = CCW
% For CW motors (2 and 4): Torque_reaction = -K_torque * Thrust_magnitude; % Negative = CW
The reaction torque is applied about the motor's axis (vertical Z-axis in body frame). This torque acts on the vehicle body in the opposite direction of the propeller spin.
Common mistake: People change the thrust vector direction or reverse the motor ordering, but forget to change the sign of the reaction torque. This causes exactly the spinning behavior you're seeing.
How to Verify Your Motor Configuration: In your Simulink plant model, find the section where motor forces and torques are applied to the 6DOF block. For each motor, verify:
Motor 1 (Front-Right): Position: [+L, -L, 0] % L = arm length Thrust: [0, 0, -T1] Torque: [0, 0, +K*T1] % Positive for CCW
Motor 2 (Front-Left): Position: [+L, +L, 0] Thrust: [0, 0, -T2] Torque: [0, 0, -K*T2] % Negative for CW
Motor 3 (Rear-Left): Position: [-L, +L, 0] Thrust: [0, 0, -T3] Torque: [0, 0, +K*T3] % Positive for CCW
Motor 4 (Rear-Right): Position: [-L, -L, 0] Thrust: [0, 0, -T4] Torque: [0, 0, -K*T4] % Negative for CW
Where K is your motor torque constant (typically 0.01 to 0.05 for small quads). If your torque signs don't match this pattern, that's why the vehicle spins uncontrollably.
Now some additional potential issues to consider
1. Motor Command Mapping
Verify that HIL_ACTUATOR_CONTROLS array indices map correctly: * HIL_ACTUATOR_CONTROLS[0] → Motor 1 (front-right) * HIL_ACTUATOR_CONTROLS[1] → Motor 2 (front-left) * HIL_ACTUATOR_CONTROLS[2] → Motor 3 (rear-left) * HIL_ACTUATOR_CONTROLS[3] → Motor 4 (rear-right) If these are swapped, even with correct torque signs, the vehicle won't fly correctly.
2. Thrust Scaling The actuator_outputs_sim outputs values in range [-1, 1]: * -1.0 = Full reverse thrust * 0.0 = No thrust * 1.0 = Full thrust
Your plant must map these to actual thrust. For most multirotors:
% Correct mapping: Thrust_N = max(0, actuator_command) * Thrust_max;
% Or with quadratic relationship (more realistic): Thrust_N = (max(0, actuator_command))^2 * Thrust_max;
If you're using a linear relationship when you should use quadratic, or vice versa, the controller gains will be mismatched.
3. Moments of Inertia
VTOL aircraft have different mass distributions than pure quadcopters. Your 6DOF block needs realistic inertia values:
% Typical small VTOL in quad mode: Ixx = 0.01 to 0.05 kg⋅m^2 % Roll inertia Iyy = 0.01 to 0.05 kg⋅m²^2 % Pitch inertia Izz = 0.02 to 0.08 kg⋅m^2 % Yaw inertia (usually highest)
If these are way off, the vehicle will respond too sluggishly or too aggressively.
So, here is summarized version of answers to your questions
1. "Is something missing with actuator_controls?" No, nothing's missing. The topic name changed in newer PX4 versions. Use: listener actuator_outputs_sim This is the correct topic for SITL/HIL simulation and will show you the actual motor commands PX4 is sending.
2. "Why do frequencies change between 30-50 Hz in QGC?" This is completely normal and expected. Those are just telemetry rates to QGC (downsampled to save bandwidth). Your internal simulation loop is still running at the full 250 Hz - confirmed by your sensor_combined dt of 4000 microseconds. No issues here.
3. "Same error continuous again" The continuous blocks in your 6DOF dynamics are perfectly fine - don't change them. Those blocks represent physical dynamics which are naturally continuous. Only your interface blocks need to be discrete (0.004s), which you already have correct.
4. "Can I use PX4 Support Package for SITL without hardware?" Yes, absolutely! Use "PX4 Host Target" mode - it runs PX4 SITL on your computer with no Pixhawk hardware needed. The UAV Toolbox Support Package specifically supports this. Try the MathWorks example: openExample('px4/MonitorTunePX4HostTargetFlightPlantModelExample') Or search for "Monitor and Tune PX4 Host Target with Simulink Plant Model" in MATLAB documentation.
5. "UAV behavior to commands is not suitable - spinning/uncontrolled" Based on your massive gyro rates (-16 rad/s) and your observation that the quadcopter example works with similar commands, the problem is definitely in your plant model configuration. Most likely causes in order of probability:
1. Motor reaction torque signs are wrong (most common issue) 2. Motor position/ordering is incorrect 3. Thrust scaling doesn't match controller expectations 4. Inertia values are unrealistic 5. Wrong PX4 airframe/mixer configuration
So, please perform the following in order
Step 1: Verify What PX4 is Commanding pxh> listener actuator_outputs_sim Run this while commanding takeoff in QGC. Tell me: * Are the values smooth and reasonable (0.4 to 0.7 range for hover)? * Or are they oscillating wildly (0→1→0 square waves)? If they're smooth but your plant still spins, the problem is definitely in your plant. If they're oscillating, the problem is in PX4's controller tuning.
Step 2: Check Your Airframe Configuration pxh> param show SYS_AUTOSTART VTOL airframes should be: * 13xxx for Standard VTOL (QuadPlane) * 14xxx for Tailsitter VTOL * 15xxx for Tiltrotor VTOL If you're using 4xxx (standard quad), you need to change this. The mixer and control allocation will be wrong.
Step 3: Double-Check Motor Torque Signs
In your Simulink plant model, find where you apply motor torques to the 6DOF block. Print or log the torque values. Verify:
- Motors 1 and 3 (front-right, rear-left): Positive torque about Z-axis
- Motors 2 and 4 (front-left, rear-right): Negative torque about Z-axis If any of these are wrong, fix them immediately. This is almost certainly your issue.
Step 4: Compare with Working Quadcopter Example
Since you have Kiril Boychev's working quadcopter example: 1. Open that model 2. Find the motor torque application section 3. Note the exact torque signs and motor ordering 4. Compare with your VTOL plant 5. Make yours match exactly
Step 5: Test with Reduced Gains (Temporary Workaround) While you're debugging, you can reduce PX4's controller aggressiveness to make the vehicle more stable:
pxh> param set MC_ROLLRATE_P 0.05 # Default is usually 0.15 pxh> param set MC_PITCHRATE_P 0.05 pxh> param set MC_YAWRATE_P 0.1 pxh> param save
This won't fix the root cause, but it might make the vehicle controllable enough to test other things.
Given the complexity you're facing with manual MAVLink message handling and plant configuration, I strongly recommend switching to the official MathWorks UAV Toolbox approach. Here's why:
Current approach (manual): * You manually handle all MAVLink messages * You manually ensure timing synchronization * You manually configure motor mixing * You manually debug control issues * Any mismatch causes mysterious failures
Official UAV Toolbox approach: * Automatic MAVLink message handling * Built-in timing synchronization * Pre-configured motor mixing for standard vehicles * Better diagnostic tools * Works with PX4 SITL (no hardware needed)
The toolbox's "PX4 Host Target" mode is specifically designed for your exact use case - running PX4 SITL with a custom Simulink plant model. It eliminates 90% of the integration headaches you're experiencing.
To get started: 1. Install UAV Toolbox Support Package for PX4 Autopilots 2. Run: openExample('px4/MonitorTunePX4HostTargetFlightPlantModelExample') 3. Follow the example to connect your VTOL plant model 4. The toolbox handles all timing, messaging, and synchronization automatically Documentation: https://www.mathworks.com/help/supportpkg/px4/
Here is What I need from you To help diagnose this further, please share:
*Output of listener actuator_outputs_sim during a takeoff command (screenshot or copy-paste)
*Value of SYS_AUTOSTART parameter
*A screenshot or scope plot showing your 4 motor command values over 5-10 seconds
*If possible: The section of your Simulink model where motor torques are applied to the 6DOF block (just that subsystem, not the whole model)
With these pieces of information, I can pinpoint exactly which motor(s) have the wrong configuration and give you the precise fix.
Looking forward to hearing what you find! We're getting close to solving this - the sensor data tells us exactly what's wrong (vehicle spinning wildly), we just need to find which motor configuration is causing it.
P.S.- That -16 rad/s gyro rate you're seeing is actually helpful for debugging. When you fix the motor configuration, you should immediately see those rates drop to near-zero when the vehicle is sitting on the ground. That's how you'll know you've fixed it.
Answers (0)
Categories
Find more on Vehicle Network Toolbox in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!





