px4
PX4 SITL integration for MuJoCo simulation.
This module provides a MAVLink-based interface for PX4 hardware-in-the-loop simulation with MuJoCo, following the Pegasus Simulator design pattern.
Coordinate Systems
- MuJoCo/IsaacSim: ENU (East-North-Up) / FLU (Forward-Left-Up)
- PX4: NED (North-East-Down) / FRD (Forward-Right-Down)
The backend automatically handles coordinate conversions internally.
Classes:
| Name | Description |
|---|---|
CoordinateConverter |
Coordinate system converter between ENU/FLU (MuJoCo) and NED/FRD (PX4). |
SensorSource |
Binary codes for MAVLink HIL_SENSOR message fields_updated field. |
SensorMsg |
Container for all sensor data to be sent via MAVLink. |
ThrusterControl |
Manages rotor control inputs received from PX4. |
PX4MavlinkBackendConfig |
Configuration for PX4 MAVLink backend. |
PX4MavlinkBackend |
MAVLink backend for PX4 SITL integration with MuJoCo. |
MuJoCoPX4Config |
Configuration for MuJoCo PX4 SITL simulation. |
MuJoCoPX4Runner |
MuJoCo runner for PX4 SITL simulation. |
CoordinateConverter
Coordinate system converter between ENU/FLU (MuJoCo) and NED/FRD (PX4).
Follows Pegasus Simulator's convention. Uses [w, x, y, z] quaternion format to match MAVLink standard.
Methods:
| Name | Description |
|---|---|
enu_to_ned |
Convert vector from ENU to NED frame. |
ned_to_enu |
Convert vector from NED to ENU frame. |
flu_to_frd |
Convert vector from FLU to FRD body frame. |
frd_to_flu |
Convert vector from FRD to FLU body frame. |
quat_enu_flu_to_ned_frd |
Convert a [w, x, y, z] attitude from world-ENU/body-FLU to NED/FRD. |
mag_body_frd |
Synthesize a body-FRD magnetometer reading (gauss) from attitude. |
enu_to_ned
classmethod
enu_to_ned(v_enu: ndarray) -> np.ndarray
Convert vector from ENU to NED frame.
ned_to_enu
classmethod
ned_to_enu(v_ned: ndarray) -> np.ndarray
Convert vector from NED to ENU frame.
flu_to_frd
classmethod
flu_to_frd(v_flu: ndarray) -> np.ndarray
Convert vector from FLU to FRD body frame.
frd_to_flu
classmethod
frd_to_flu(v_frd: ndarray) -> np.ndarray
Convert vector from FRD to FLU body frame.
quat_enu_flu_to_ned_frd
classmethod
quat_enu_flu_to_ned_frd(q_enu_flu: ndarray) -> np.ndarray
Convert a [w, x, y, z] attitude from world-ENU/body-FLU to NED/FRD.
Uses the Pegasus convention q_ned_frd = q_enu_to_ned * q * q_flu_to_frd,
with the frame swaps composed via scipy: 180 deg about [1, 1, 0]/sqrt(2)
for ENU->NED and 180 deg about X for FLU->FRD. Input/output are [w, x, y, z].
mag_body_frd
classmethod
mag_body_frd(quat_enu_flu: ndarray, mag_world_ned: ndarray) -> np.ndarray
Synthesize a body-FRD magnetometer reading (gauss) from attitude.
quat_enu_flu is the MuJoCo world-ENU-from-body-FLU quaternion
[w, x, y, z]; mag_world_ned is the earth field in NED (gauss). The
result is the field in the PX4 body-FRD frame, consistent with the true
attitude so the EKF recovers the true heading.
SensorSource
Binary codes for MAVLink HIL_SENSOR message fields_updated field.
Credit: Pegasus Simulator https://github.com/PegasusSimulator/PegasusSimulator
Attributes:
| Name | Type | Description |
|---|---|---|
ACCEL |
int
|
Accelerometer update flag (0b0000000000111 = 7) |
GYRO |
int
|
Gyroscope update flag (0b0000000111000 = 56) |
MAG |
int
|
Magnetometer update flag (0b0000111000000 = 448) |
BARO |
int
|
Barometer update flag (0b1101000000000 = 6656) |
DIFF_PRESS |
int
|
Differential pressure update flag (0b0010000000000 = 1024) |
SensorMsg
dataclass
SensorMsg(new_imu_data: bool = False, received_first_imu: bool = False, xacc: float = 0.0, yacc: float = 0.0, zacc: float = 0.0, xgyro: float = 0.0, ygyro: float = 0.0, zgyro: float = 0.0, new_bar_data: bool = False, abs_pressure: float = 101325.0, pressure_alt: float = 0.0, temperature: float = 25.0, new_mag_data: bool = False, xmag: float = 0.0, ymag: float = 0.0, zmag: float = 0.0, new_press_data: bool = False, diff_pressure: float = 0.0, new_gps_data: bool = False, fix_type: int = 3, latitude_deg: int = 0, longitude_deg: int = 0, altitude: int = 0, eph: int = 100, epv: int = 100, velocity: int = 0, velocity_north: int = 0, velocity_east: int = 0, velocity_down: int = 0, cog: int = 0, satellites_visible: int = 10, new_vision_data: bool = False, vision_x: float = 0.0, vision_y: float = 0.0, vision_z: float = 0.0, vision_roll: float = 0.0, vision_pitch: float = 0.0, vision_yaw: float = 0.0, vision_covariance: tuple = (lambda: (0.0,) * 21)(), new_sim_state: bool = False, sim_attitude: ndarray = (lambda: np.array([1.0, 0.0, 0.0, 0.0]))(), sim_acceleration: ndarray = (lambda: np.array([0.0, 0.0, 0.0]))(), sim_angular_vel: ndarray = (lambda: np.array([0.0, 0.0, 0.0]))(), sim_lat: int = 0, sim_lon: int = 0, sim_alt: int = 0, sim_ind_airspeed: int = 0, sim_true_airspeed: int = 0, sim_velocity_inertial: ndarray = (lambda: np.array([0.0, 0.0, 0.0]))())
Container for all sensor data to be sent via MAVLink.
ThrusterControl
dataclass
ThrusterControl(num_rotors: int = 4, input_offset: list = (lambda: [0.0] * 4)(), input_scaling: list = (lambda: [1000.0] * 4)(), zero_position_armed: list = (lambda: [100.0] * 4)())
Manages rotor control inputs received from PX4.
Attributes:
| Name | Type | Description |
|---|---|---|
num_rotors |
int
|
Number of rotors on the vehicle. |
input_offset |
list
|
Offset values for each rotor. |
input_scaling |
list
|
Scaling factors for each rotor. |
zero_position_armed |
list
|
Offset when armed but zero throttle. |
_input_reference |
ndarray
|
Computed angular velocities in rad/s. |
Methods:
| Name | Description |
|---|---|
__post_init__ |
Validate and initialize input reference array. |
update_input_reference |
Update rotor speeds from PX4 control inputs. |
zero_input_reference |
Set all rotor references to zero (disarmed state). |
input_reference
property
input_reference: ndarray
Computed angular velocities for each rotor in rad/s.
__post_init__
__post_init__()
Validate and initialize input reference array.
update_input_reference
update_input_reference(controls: Sequence[float]) -> None
Update rotor speeds from PX4 control inputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Sequence[float]
|
List of control values from HIL_ACTUATOR_CONTROLS. |
required |
zero_input_reference
zero_input_reference() -> None
Set all rotor references to zero (disarmed state).
PX4MavlinkBackendConfig
dataclass
PX4MavlinkBackendConfig(vehicle_id: int = 0, connection_type: str = 'tcpin', connection_ip: str = 'localhost', connection_baseport: int = 4560, px4_autolaunch: bool = False, px4_dir: str = '', px4_vehicle_model: str = 'gazebo-classic_iris', enable_lockstep: bool = True, lockstep_timeout: float = 0.1, num_rotors: int = 4, input_offset: list = (lambda: [0.0] * 4)(), input_scaling: list = (lambda: [1000.0] * 4)(), zero_position_armed: list = (lambda: [100.0] * 4)(), reference_lat: float = 47.6061, reference_lon: float = -122.3328, reference_alt: float = 0.0, ground_left_control_index: int = 4, ground_right_control_index: int = 5, mag_field_ned: tuple = (0.25, 0.0, 0.43), baro_sea_level_hpa: float = 1013.25, baro_temperature_c: float = 25.0, imu_accel_noise_density: float = 0.004, imu_accel_random_walk: float = 0.006, imu_accel_bias_correlation_time: float = 300.0, imu_accel_turn_on_bias_sigma: float = 0.196, imu_gyro_noise_density: float = 0.0003394, imu_gyro_random_walk: float = 3.8785e-05, imu_gyro_bias_correlation_time: float = 1000.0, imu_gyro_turn_on_bias_sigma: float = 0.0087, mag_noise_density: float = 0.0004, mag_random_walk: float = 6.4e-06, mag_bias_correlation_time: float = 600.0, mag_update_rate_hz: float = 100.0, baro_pressure_noise_std: float = 0.01, baro_update_rate_hz: float = 50.0, gps_update_rate_hz: float = 5.0, gps_xy_random_walk: float = 2.0, gps_z_random_walk: float = 4.0, gps_xy_noise_density: float = 0.0002, gps_z_noise_density: float = 0.0004, gps_vxy_noise_density: float = 0.2, gps_vz_noise_density: float = 0.4, gps_bias_correlation_time: float = 60.0, noise_seed: int = 7)
Configuration for PX4 MAVLink backend.
Attributes:
| Name | Type | Description |
|---|---|---|
vehicle_id |
int
|
Vehicle identifier (affects port number). |
connection_type |
str
|
MAVLink connection type (tcpin, udpin, etc). |
connection_ip |
str
|
IP address for MAVLink connection. |
connection_baseport |
int
|
Base port number. |
px4_autolaunch |
bool
|
Whether to auto-launch PX4. |
px4_dir |
str
|
Directory containing PX4-Autopilot. |
px4_vehicle_model |
str
|
PX4 vehicle model name. |
enable_lockstep |
bool
|
Enable lockstep simulation. |
num_rotors |
int
|
Number of rotors on the vehicle. |
input_offset |
list
|
Control input offsets. |
input_scaling |
list
|
Control input scaling factors. |
zero_position_armed |
list
|
Armed zero throttle offsets. |
reference_lat |
float
|
Reference latitude for GPS simulation (degrees). |
reference_lon |
float
|
Reference longitude for GPS simulation (degrees). |
reference_alt |
float
|
Reference altitude for GPS simulation (meters). |
ground_left_control_index |
int
|
HIL_ACTUATOR_CONTROLS index carrying the left ground-drive output-shaft speed target (rad/s). AeroCar (MAV_TYPE 30) packs it into controls[4]. |
ground_right_control_index |
int
|
HIL_ACTUATOR_CONTROLS index carrying the right ground-drive output-shaft speed target (rad/s). AeroCar packs it into controls[5]. |
mag_field_ned |
tuple
|
Synthetic earth magnetic field (gauss, NED) used to populate HIL_SENSOR mag from the ground-truth attitude when the MJCF has no magnetometer. Sent so PX4's default pre-arm heading check passes. |
baro_sea_level_hpa |
float
|
Sea-level pressure (hPa) for the synthetic barometer (standard-atmosphere model from world altitude). |
baro_temperature_c |
float
|
Synthetic barometer temperature (degC). |
imu_accel_noise_density |
float
|
Accelerometer white-noise density (m/s^2/sqrt(Hz)). |
imu_accel_random_walk |
float
|
Accelerometer bias random walk (m/s^2/s/sqrt(Hz)). |
imu_accel_bias_correlation_time |
float
|
Accelerometer bias correlation time (s). |
imu_accel_turn_on_bias_sigma |
float
|
Accelerometer turn-on bias sigma (m/s^2). |
imu_gyro_noise_density |
float
|
Gyroscope white-noise density (rad/s/sqrt(Hz)). |
imu_gyro_random_walk |
float
|
Gyroscope bias random walk (rad/s/s/sqrt(Hz)). |
imu_gyro_bias_correlation_time |
float
|
Gyroscope bias correlation time (s). |
imu_gyro_turn_on_bias_sigma |
float
|
Gyroscope turn-on bias sigma (rad/s). |
mag_noise_density |
float
|
Magnetometer white-noise density (gauss/sqrt(Hz)). |
mag_random_walk |
float
|
Magnetometer bias random walk (gauss*sqrt(Hz)). |
mag_bias_correlation_time |
float
|
Magnetometer bias correlation time (s). |
mag_update_rate_hz |
float
|
Magnetometer update rate (Hz). |
baro_pressure_noise_std |
float
|
Per-sample pressure noise sigma (hPa). |
baro_update_rate_hz |
float
|
Barometer update rate (Hz). |
gps_update_rate_hz |
float
|
GPS update rate (Hz). |
noise_seed |
int
|
Seed for the synthetic noise RNG (reproducible SITL). The noise prevents PX4's DataValidator from flagging STALE_DATA when the vehicle is still and synthesized mag/baro would be bit-identical. |
PX4MavlinkBackend
PX4MavlinkBackend(config: PX4MavlinkBackendConfig | None = None)
MAVLink backend for PX4 SITL integration with MuJoCo.
This class handles communication between MuJoCo simulation and PX4 autopilot via MAVLink protocol. It sends simulated sensor data to PX4 and receives actuator control commands.
Usage
config = PX4MavlinkBackendConfig( vehicle_id=0, connection_type="tcpin", connection_ip="localhost", enable_lockstep=True ) backend = PX4MavlinkBackend(config) backend.start()
In simulation loop:
backend.update_imu_data(accel, gyro) backend.update_gps_data(position, velocity) backend.update(dt) controls = backend.get_actuator_controls()
Initialize the PX4 MAVLink backend.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
PX4MavlinkBackendConfig | None
|
Configuration object. Uses defaults if None. |
None
|
Methods:
| Name | Description |
|---|---|
start |
Start the MAVLink interface and wait for PX4 connection. |
stop |
Stop the MAVLink interface and cleanup. |
reset |
Reset the backend state. |
update_imu_data |
Update IMU sensor data. |
update_gps_data |
Update GPS sensor data. |
update_magnetometer_data |
Update magnetometer data. |
update_barometer_data |
Update barometer data. |
update_airspeed_data |
Update airspeed sensor data. |
update_ground_truth |
Update ground truth state data. |
update |
Main update method - call this in your simulation loop. |
get_actuator_controls |
Get the raw actuator controls from PX4 (HIL_ACTUATOR_CONTROLS). |
get_motor_commands |
Get motor commands scaled to angular velocities. |
get_ground_wheel_speeds |
Get ground-drive wheel-speed targets received from PX4. |
is_armed |
Check if the vehicle is armed. |
__del__ |
Cleanup on deletion. |
start
start() -> None
Start the MAVLink interface and wait for PX4 connection.
When px4_autolaunch is set, a PX4 SITL subprocess is spawned from
px4_dir using the px4_vehicle_model airframe (ported from the
Pegasus PX4LaunchTool). The MAVLink listener is opened first so the
spawned PX4 can connect to it.
stop
stop() -> None
Stop the MAVLink interface and cleanup.
reset
reset() -> None
Reset the backend state.
update_imu_data
update_imu_data(linear_acceleration: ndarray, angular_velocity: ndarray, frame: str = 'FLU') -> None
Update IMU sensor data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
ndarray
|
Acceleration in m/s^2 [x, y, z] in body frame. |
required |
|
ndarray
|
Angular velocity in rad/s [x, y, z] in body frame. |
required |
|
str
|
Input coordinate frame, either "FLU" (MuJoCo) or "FRD" (PX4). Defaults to "FLU" for MuJoCo compatibility. |
'FLU'
|
update_gps_data
update_gps_data(position: ndarray, velocity: ndarray, frame: str = 'ENU') -> None
Update GPS sensor data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
ndarray
|
Position [x, y, z] in meters. |
required |
|
ndarray
|
Velocity [vx, vy, vz] in m/s. |
required |
|
str
|
Input coordinate frame, either "ENU" (MuJoCo) or "NED" (PX4). Defaults to "ENU" for MuJoCo compatibility. |
'ENU'
|
update_magnetometer_data
update_magnetometer_data(magnetic_field: ndarray, frame: str = 'FLU') -> None
Update magnetometer data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
ndarray
|
Magnetic field in Tesla [x, y, z] in body frame. |
required |
|
str
|
Input coordinate frame, either "FLU" (MuJoCo) or "FRD" (PX4). Defaults to "FLU" for MuJoCo compatibility. |
'FLU'
|
update_barometer_data
update_barometer_data(pressure: float, altitude: float, temperature: float = 25.0) -> None
Update barometer data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
float
|
Absolute pressure in hPa (millibar), as carried by the MAVLink HIL_SENSOR abs_pressure field (PX4 multiplies by 100 to Pa). |
required |
|
float
|
Pressure altitude in meters. |
required |
|
float
|
Temperature in Celsius. |
25.0
|
update_airspeed_data
update_airspeed_data(differential_pressure: float) -> None
Update airspeed sensor data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
float
|
Differential pressure in Pascals. |
required |
update_ground_truth
update_ground_truth(attitude: ndarray, angular_velocity: ndarray, linear_acceleration: ndarray, velocity: ndarray, airspeed: float = 0.0, frame: str = 'ENU_FLU') -> None
Update ground truth state data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
ndarray
|
Quaternion [w, x, y, z]. |
required |
|
ndarray
|
Angular velocity [wx, wy, wz] in rad/s in body frame. |
required |
|
ndarray
|
Linear acceleration [x, y, z] in m/s^2 in body frame. |
required |
|
ndarray
|
Velocity [vx, vy, vz] in m/s in world frame. |
required |
|
float
|
Indicated airspeed in m/s. |
0.0
|
|
str
|
Input coordinate frames, either "ENU_FLU" (MuJoCo) or "NED_FRD" (PX4). Defaults to "ENU_FLU" for MuJoCo compatibility. |
'ENU_FLU'
|
update
update(dt: float) -> None
Main update method - call this in your simulation loop.
Sends sensor data to PX4 and polls for actuator controls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
float
|
Time step in seconds. |
required |
get_actuator_controls
get_actuator_controls() -> np.ndarray
Get the raw actuator controls from PX4 (HIL_ACTUATOR_CONTROLS).
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of 16 control values as received from PX4 (typically [-1, 1] range). |
get_motor_commands
get_motor_commands() -> np.ndarray
Get motor commands scaled to angular velocities.
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of rotor speeds in rad/s for each motor. |
get_ground_wheel_speeds
get_ground_wheel_speeds() -> np.ndarray
Get ground-drive wheel-speed targets received from PX4.
AeroCar packs left/right output-shaft angular-velocity targets (rad/s)
into HIL_ACTUATOR_CONTROLS.controls alongside the rotor commands.
Returns a length-2 array [left, right] in rad/s, zeroed when the
vehicle is disarmed.
Returns:
| Type | Description |
|---|---|
ndarray
|
Array |
is_armed
is_armed() -> bool
Check if the vehicle is armed.
__del__
__del__()
Cleanup on deletion.
MuJoCoPX4Config
dataclass
MuJoCoPX4Config(mjcf_path: str, px4_config: PX4MavlinkBackendConfig = PX4MavlinkBackendConfig(), params: VehicleParams = VehicleParams(), physics_dt: float | None = None, enable_ground_drive: bool = True, ground_left_actuator: str = 'track_left_drive', ground_right_actuator: str = 'track_right_drive', ground_actuator: GroundDriveActuatorConfig = GroundDriveActuatorConfig())
Configuration for MuJoCo PX4 SITL simulation.
Attributes:
| Name | Type | Description |
|---|---|---|
mjcf_path |
str
|
Path to MuJoCo XML scene file. |
px4_config |
PX4MavlinkBackendConfig
|
PX4 MAVLink configuration. |
params |
VehicleParams
|
Vehicle parameters for rotor dynamics. |
physics_dt |
float | None
|
Optional MuJoCo physics time step override. When unset, the time step declared by the MJCF is preserved. |
enable_ground_drive |
bool
|
Apply AeroCar ground-drive wheel-speed targets from HIL_ACTUATOR_CONTROLS to the track actuators. |
ground_left_actuator |
str
|
MuJoCo actuator name for the left track. |
ground_right_actuator |
str
|
MuJoCo actuator name for the right track. |
ground_actuator |
GroundDriveActuatorConfig
|
MuJoCo-only actuator adaptation applied to PX4's physical-side wheel-speed targets. |
MuJoCoPX4Runner
MuJoCoPX4Runner(cfg: MuJoCoPX4Config)
MuJoCo runner for PX4 SITL simulation.
Integrates MuJoCo physics simulation with PX4 autopilot via MAVLink. Automatically handles coordinate conversions from MuJoCo (ENU/FLU) to PX4 (NED/FRD).
Usage
from lav2.dynamics.params import VehicleParams
config = MuJoCoPX4Config( mjcf_path="path/to/scene.xml", px4_config=PX4MavlinkBackendConfig( connection_type="tcpin", connection_ip="localhost", connection_baseport=4560, ), params=VehicleParams(), ) runner = MuJoCoPX4Runner(config) runner.run()
Initialize the PX4 SITL runner.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
MuJoCoPX4Config
|
Configuration for simulation. |
required |
Methods:
| Name | Description |
|---|---|
apply_actions |
Apply rotor dynamics and ground drive to MuJoCo actuators. |
control_callback |
Main control callback for MuJoCo. |
load_callback |
Load MuJoCo model and data, then resolve ground-drive actuators. |
run |
Start PX4 SITL simulation. |
apply_actions
apply_actions(d: MjData)
Apply rotor dynamics and ground drive to MuJoCo actuators.
Runs every simulation step. RotorDynamics is batched
(num_envs=1, num_rotors), so the RPM command is promoted to
(1, N), row 0 is taken back as a 1-D array, and the 0-indexed
spin/thrust/torque actuators are written. Then the AeroCar
ground-drive wheel-speed targets are applied to the track actuators.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
MjData
|
MuJoCo data. |
required |
control_callback
control_callback(m: MjModel, d: MjData)
Main control callback for MuJoCo.
Sends sensor data to PX4, receives actuator commands, and applies rotor dynamics + ground drive every simulation step. PX4 control runs at the simulation frequency (no decimation) for lockstep correctness. Called automatically by MuJoCo at each step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
MjModel
|
MuJoCo model (unused; kept for the mjcb_control signature). |
required |
|
MjData
|
MuJoCo data. |
required |
load_callback
load_callback() -> tuple[mujoco.MjModel, mujoco.MjData]
Load MuJoCo model and data, then resolve ground-drive actuators.
Returns:
| Type | Description |
|---|---|
tuple[MjModel, MjData]
|
Tuple of (model, data). |
run
run()
Start PX4 SITL simulation.