Native Wheeled Vehicles
Project AirSim can control an Unreal AWheeledVehiclePawn directly, without a
Blueprint implementing IProjectAirSimVehicle or a Blueprint
SetParameterSignal event. This integration is selected with the
wheeled-vehicle-class robot setting.
Use the existing Unreal Vehicle integration when a Blueprint must define its
own indexed parameter behavior. Use the native Wheeled Vehicle integration
when throttle, steering, and brake should go directly to the pawn’s
ChaosWheeledVehicleMovementComponent.
Integration |
Robot setting |
Blueprint interface required |
Control mapping |
|---|---|---|---|
Wheeled Vehicle |
|
No |
Fixed: throttle, steering, brake |
Unreal Vehicle |
|
Yes for parameter forwarding |
Defined by the Blueprint |
The two class settings are mutually exclusive in one robot configuration.
Configuration
A minimal native wheeled-vehicle robot uses Unreal physics and points to a
Blueprint class derived from AWheeledVehiclePawn:
{
"physics-type": "unreal-physics",
"wheeled-vehicle-class": "/ProjectAirSim/VehicleAdv/SUV/SuvCarPawn.SuvCarPawn_C"
}
The example configurations are:
client/python/example_user_scripts/sim_config/robot_wheeled_vehicle.jsoncclient/python/example_user_scripts/sim_config/scene_wheeled_vehicle.jsoncclient/python/example_user_scripts/sim_config/robot_wheeled_vehicle_simpledrive.jsoncclient/python/example_user_scripts/sim_config/scene_wheeled_vehicle_simpledrive.jsonc
The pawn must have a usable ChaosWheeledVehicleMovementComponent, wheel
classes, skeletal wheel bones, and drivetrain settings. When Chaos mechanical
simulation is enabled, Project AirSim applies only Chaos inputs. When it is
disabled, Project AirSim uses its direct-force fallback instead; the two
propulsion paths are never active together.
For an engine-driven Unreal scene, enable physics substepping when using a
small step-ns:
"clock": {
"type": "engine-driven",
"step-ns": 3000000,
"engine-substepping": true
}
This treats 3 ms as the maximum Chaos physics substep instead of requiring the outer Unreal render loop to sustain 333 FPS.
Vehicle control
Python and C++ use dedicated SetThrottle, SetSteering, and SetBrakes
RPCs with a value argument and boolean result. There is no SetParameter
alias for WheeledVehicle. ROS 2 retains its indexed set_parameter service:
Index |
Control |
Accepted range |
|---|---|---|
|
Throttle |
|
|
Steering |
|
|
Brake |
|
The bridge rejects unknown wheeled indices; the simulator rejects non-finite values and clamps valid finite values to the ranges above.
Python
From client/python/example_user_scripts:
python3 hello_wheeled_vehicle.py
The Python WheeledVehicle interface provides named control methods:
vehicle.set_throttle(0.7)
vehicle.set_steering(0.45)
vehicle.set_brakes(1.0)
Each method sends its corresponding named RPC directly.
C++
From the repository root:
cmake -S client/cpp -B client/cpp/build_linux/Debug -DCMAKE_BUILD_TYPE=Debug
cmake --build client/cpp/build_linux/Debug --target hello_wheeled_vehicle -j"$(nproc)"
./client/cpp/build_linux/Debug/hello_wheeled_vehicle
The public call is:
bool applied = false;
vehicle.SetThrottle(0.7f, &applied);
vehicle.SetSteering(0.45f, &applied);
vehicle.SetBrakes(1.0f, &applied);
ROS 2
Build and source the bridge, then configure its single-vehicle service name:
cd ros
colcon build --packages-select projectairsim_ros2_cpp
source install/setup.bash
ros2 run projectairsim_ros2_cpp projectairsim_ros2_cpp_node --ros-args \
-p scene_config:=scene_wheeled_vehicle.jsonc \
-p sim_config_path:=../client/python/example_user_scripts/sim_config \
-p vehicle_name:=WheeledVehicle
In another sourced terminal, apply throttle, steering, and brake:
The bridge queries /Sim/<scene>/robots/<robot>/GetRobotType lazily and caches
valid results by scene/robot path until reconnect or a successful bridge scene
load. Robot names are arbitrary. wheeled-vehicle enables the mapping below;
unreal-vehicle forwards SetParameter with Blueprint-defined indices. drone,
jsbsim, and other reject indexed controls, leaving other services unchanged.
Missing/failed queries, malformed results and unknown types return explicit
errors without a control RPC or name-based fallback; failures are not cached.
An empty requested name uses vehicle_name. No JSONC index mapping is required.
The simulator must provide GetRobotType; its result reports the configured
backend rather than spawn or mechanical-drivetrain readiness.
To connect to an already loaded scene, omit scene_config; C++ World
discovers the scene and robot names from topics without fetching configuration,
reloading or resetting the scene. External scene changes require reconnecting
the bridge or loading the scene through its load service with
is_primary_client: true.
ros2 service call /projectairsim/WheeledVehicle/set_parameter \
projectairsim_ros2_cpp/srv/SetParameter "{index: 0, value: 0.7}"
ros2 service call /projectairsim/WheeledVehicle/set_parameter \
projectairsim_ros2_cpp/srv/SetParameter "{index: 1, value: 0.45}"
ros2 service call /projectairsim/WheeledVehicle/set_parameter \
projectairsim_ros2_cpp/srv/SetParameter "{index: 2, value: 1.0}"
Zero-wiring SimpleDrive
For SimpleDrive, use hello_wheeled_vehicle_simpledrive.py. The robot config
contains a simple-drive-api controller but no Unreal Vehicle actuators.
UnrealRobot maps the controller output directly as
[throttle, steering, brake] and holds those values as Chaos inputs. Client
control for this workflow continues to use the normal Rover/SimpleDrive API;
the Blueprint requires no signal wiring.
Runtime diagnostics
The Unreal log identifies the active path:
WheeledVehicle zero-wiring SimpleDrive mapping activeconfirms direct SimpleDrive output mapping.Chaos mechanical drivetrain (direct-force fallback disabled)confirms the pawn is using its configured Chaos drivetrain.direct-force fallback activeconfirms mechanical simulation is disabled.
If the pawn fails to load, verify that the asset path exists in the active
Unreal version and that its generated class derives from
AWheeledVehiclePawn.