Discover Learn Reference Get OpenPLXSearch Contact

Vehicle With Steering Tutorial

This tutorial adapts the AGX wheeled-vehicle modeling walkthrough into a browser-runnable OpenPLX tutorial. It introduces an elastic wheel model, a chassis that is compatible with suspension and steering, a front-wheel-drive vehicle, and finally a simple drivetrain plus signal interface.

The code blocks below are intentionally incremental. Open in Editor always assembles the full runnable source by pulling in the hidden prerequisite blocks automatically.

Step 1: Create a reusable elastic wheel

We begin with a reusable CarWheel model based on Vehicles.Wheels.ElasticWheel. The wheel has separate rim and tire bodies, a rendered mesh, and a connector that later suspensions can attach to.

const VehicleVisualMaterial is Visuals.Materials.TextureMaterial:
    path: @"wheeled-vehicle/Kangoo.png"
CarWheel is Vehicles.Wheels.ElasticWheel:
    rim becomes Vehicles.Wheels.Rim:
        radius: 0.2
        width: 0.2
        inertia.mass: 10
        rotational_axis: {0, 1, 0}

    tire becomes Vehicles.Wheels.Tire:
        outer_radius: 0.45
        inner_radius: 0.2
        width: 0.2
        inertia.mass: 10
        rotational_axis: {0, 1, 0}
        visual is Visuals.Geometries.ExternalTriMeshGeometry:
            path: @"wheeled-vehicle/Ram_Wheel.obj"
            material: VehicleVisualMaterial
            local_transform.rotation: Math.Quat.angle_axis(visual_rotation, {0, 0, 1})

    connector:
        main_axis: {0, -1, 0}
        normal: {1, 0, 0}

    visual_rotation is Real: 0
WheelPreview is Physics3D.System:
    wheel is CarWheel

Step 2: Build a chassis that supports suspension and steering

The chassis uses a simple collision box, a local mesh for rendering, and redirected connectors that compute wheel axes consistently from the vehicle up and forward vectors.

ChassisBody is Physics3D.Bodies.RigidBody:
    geometry is Physics3D.Geometries.Box:
        size: {2, 5.5, 1}
        local_transform.position.z: 1.5

    inertia.mass: 1000
    kinematics.local_cm_transform.position: {0, 0, 0.5}

    visual is Visuals.Geometries.ExternalTriMeshGeometry:
        path: @"wheeled-vehicle/Ram_Chassi.obj"
        material: VehicleVisualMaterial
ChassisConnector is Physics3D.Interactions.RedirectedMateConnector:
    with Vehicles.Interfaces.UpForwardVectors
    with Vehicles.Suspensions.Properties.WheelAxisFromUpForward
    .agx_debug_render_frame: true
Scene is Physics3D.System:
    chassis is ChassisBody

Step 3: Create a front-wheel-drive vehicle

Now we upgrade Vehicles.FourWheel.FrontWheelDrive with our custom chassis connectors, our elastic wheels, steering, and a collision filter that avoids chassis-wheel self-collision.

VehicleWithSteering is Vehicles.FourWheel.FrontWheelDrive:
    chassis:
        up_vector: up_vector
        forward_vector: forward_vector

        front_left_connector becomes ChassisConnector:
            position: {-0.923879, 2.00055, 0.439071}
            up_vector: up_vector
            forward_vector: forward_vector
            redirected_parent: chassis.body

        rear_left_connector becomes ChassisConnector:
            position: {-0.923879, -1.96483, 0.439071}
            up_vector: up_vector
            forward_vector: forward_vector
            redirected_parent: chassis.body

        front_right_connector becomes ChassisConnector:
            position: {0.923879, 2.00055, 0.439071}
            up_vector: up_vector
            forward_vector: forward_vector
            redirected_parent: chassis.body

        rear_right_connector becomes ChassisConnector:
            position: {0.923879, -1.96483, 0.439071}
            up_vector: up_vector
            forward_vector: forward_vector
            redirected_parent: chassis.body

        body becomes ChassisBody

    up_vector: {0, 0, 1}
    forward_vector: {0, 1, 0}

    front_left_wheel becomes CarWheel:
        visual_rotation: 3 * Math.PI / 2
    front_right_wheel becomes CarWheel:
        visual_rotation: -3 * Math.PI / 2
    rear_left_wheel becomes CarWheel:
        visual_rotation: 3 * Math.PI / 2
    rear_right_wheel becomes CarWheel:
        visual_rotation: -3 * Math.PI / 2

    steering becomes Vehicles.Steering.Kinematic.RackAndPinion:
        left_suspension: front_left_suspension
        right_suspension: front_right_suspension
        interaction.steering_angle_output.enabled: true

    chassis_wheels_collision_group is Simulation.CollisionGroup:
        bodies: [
            chassis.body,
            front_left_wheel.rim, front_left_wheel.tire,
            front_right_wheel.rim, front_right_wheel.tire,
            rear_left_wheel.rim, rear_left_wheel.tire,
            rear_right_wheel.rim, rear_right_wheel.tire
        ]

    disable_wheel_chassis_collisions is Simulation.DisableCollisionPair:
        group1: chassis_wheels_collision_group
        group2: chassis_wheels_collision_group
Scene is Physics3D.System:
    vehicle is VehicleWithSteering

Step 4: Add drivetrain and signal interface

The last step adds a simple two-wheel drivetrain template and a signal interface so the vehicle is ready for later external control.

DriveTrain is DriveTrain.MechanicalDriveTrains.TwoWheelTemplate:
    front_left_suspension is Vehicles.Suspensions.SingleMate.Base reference
    front_right_suspension is Vehicles.Suspensions.SingleMate.Base reference

    left_actuator becomes Vehicles.Wheels.Actuator:
        mate_3d: front_left_suspension.mate
    right_actuator becomes Vehicles.Wheels.Actuator:
        mate_3d: front_right_suspension.mate

    gear_shaft.inertia.inertia: 1.0
    engine_shaft.inertia.inertia: 1.0
    diff_right_shaft.inertia.inertia: 1.0
    diff_left_shaft.inertia.inertia: 1.0
    left_brake_shaft.inertia.inertia: 1.0
    right_brake_shaft.inertia.inertia: 1.0
    clutch_shaft.inertia.inertia: 1.0

    engine becomes DriveTrain.MeanValueEngine with DriveTrain.Traits.TwoStrokeEngine:
        displacement_volume: 0.0074
        max_torque: 806.0
        max_torque_RPM: 3200.0
        max_power_RPM: 4200.0
        idle_RPM: 500.0
        max_RPM: 10000.0
        crank_shaft_inertia: 20
        air_fuel_ratio: 14.5
        heat_value: 45400000
        torque_output.enabled: true
        rpm_output.enabled: true

    gear_box.forward_gears: [10]
    gear_box.reverse_gears: [10]
    gear_box.initial_gear: 0

    clutch.initial_engagement_fraction: 0.0
    clutch.torque_capacity: 6000
    clutch.min_relative_slip_ratio: 0.0001

    left_brake.initial_engagement_fraction: 0.0
    left_brake.torque_capacity: 6000
    left_brake.min_relative_slip_ratio: 0.0001

    right_brake.initial_engagement_fraction: 0.0
    right_brake.torque_capacity: 6000
    right_brake.min_relative_slip_ratio: 0.0001
DrivenVehicleWithSteering is VehicleWithSteering:
    drive is DriveTrain:
        front_left_suspension: front_left_suspension
        front_right_suspension: front_right_suspension

    signal_interface is Physics.Signals.SignalInterface:
        enable: true

        throttle is Physics.Signals.Input: drive.engine.throttle_input
        clutch is Physics.Signals.Input: drive.clutch.engagement_fraction_input
        gear is Physics.Signals.Input: drive.gear_box.gear_selection_input
        steering_angle is Physics.Signals.Input: steering.interaction.steering_angle_input
        brake_left is Physics.Signals.Input: drive.left_brake.engagement_fraction_input
        brake_right is Physics.Signals.Input: drive.right_brake.engagement_fraction_input

Scene is Physics3D.System:
    vehicle is DrivenVehicleWithSteering
OpenPLX is a work in progress. This draft version will evolve with user feedback and experience. We welcome your input and collaboration.
X