Vehicle Dynamics#

The Vehicle Dynamics extension provides tools for creating vehicle simulations that include tire, engine, clutch, transmission and suspension models.

Prerequisite#

To use the full feature set of samples, tools and utilities for the vehicle dynamics simulation, the vehicle extension must be enabled in the Window > Extensions window. Search for the omni.physx.vehicle extension and enable it.

Quickstart tutorials#

This section will present two step-by-step guides on how to create a PhysX based vehicle in Omniverse USD Composer. These guides offer a way to quickly get something to play with before diving into details. To read detailed documentation on the topic, read the next section.

Vehicle Dynamics Introduction#

The Vehicle Extension provides some sample scenes that can be loaded quickly to see how the vehicle drives and works in Omniverse USD Composer.

Open the Physics Demo Scenes: Window > Simulation > Demo Scenes

In the Physics Demo Scenes Tab, click on the Vehicle Demos list to show the list of available samples.

As an introduction, select the Vehicle sounds sample and click Load scene.

VehicleSamples

If you have a gamepad connected to your PC, and would like to drive the vehicle with it, click on the settings icon in the top left corner of the Viewport window. Then, uncheck the Gamepad Camera Control setting. This allows the gamepad to control the vehicle, instead of the camera. If you would prefer to control the vehicle with the keyboard arrow keys, this step can be skipped.

VehicleGamepad

If the omni.physx.camera extension is enabled, a special camera that follows the vehicle can be added. Select the Vehicle primitive in the Stage window. Right click on the Vehicle, cursor over the Add menu, then the Cameras menu and select the Follow Look menu item.

VehicleAddFollowCameraButton

A camera will appear in the Viewport. To view the scene through that camera, click on the camera icon in the top left corner of the Viewport window, click on the Cameras button and select the VehicleLookFollowCamera0. For additional information on the camera extension and how to tune all of the camera settings, please refer to the PhysX Cameras documentation.

VehicleSelectFollowCamera

This demo plays engine and tire sounds interactively as you control the vehicle. To hear it, make sure your PC audio is not muted and is set to a reasonable volume. Press the Play button on the left side of Omniverse USD Composer to start the simulation.

If you are using a gamepad, use the right trigger to accelerate, the left trigger to brake and the left analog joystick to steer the vehicle left and right. If you are using a keyboard, use the up arrow key to accelerate, the down arrow key to brake and the left and right arrow keys to steer the vehicle. Press the Stop button to end the simulation.

Creating a Vehicle Using the Vehicle Wizard#

Introduction#

The physical simulation of a vehicle requires many components and settings that must be created and tuned. To make the process a bit easier, Omniverse USD Composer provides a Vehicle Wizard that asks for some initial parameters as inputs and creates a whole vehicle setup as a result. If the first vehicle created has to be changed, it can be deleted and the Vehicle Wizard settings can be adjusted to try again.

Alternatively, Omniverse USD Composer provides a couple of Vehicle Demos in the Physics Demo Scenes that can be used as a starting point for a vehicle simulation. The Setup demos in particular show vehicles where all the pieces are put together (through a script). One of those demos uses a Basic Drive setup another uses a Standard Drive setup, for example. The differences between these two types are discussed in the Vehicle Wizard Basic Page documentation, below.

Authoring the Vehicle’s Rendered Mesh#

The Vehicle Wizard and the Vehicle Demos create a vehicle out of primitive geometric shapes, a box for the chassis and four cylinders for the wheels. An artist authored vehicle mesh used for rendering can then be linked to these transforms and animated.

The physics representation created by Omniverse USD Composer and the rendered vehicle mesh should be authored in the same coordinate system and units. This will avoid the need to add additional rotations or scale to make the two representations match. The Vehicle Demos are hard coded to use the Y axis as the up axis. However, the Vehicle Wizard can be configured to match any coordinate system. Prior to using the Vehicle Wizard, open the Edit > Preferences window, select Stage and pick the Default Up Axis to match the coordinate system used to author the rendered vehicle. Create a new stage (File > New) to make sure the changed defaults take effect.

VehicleEditPreferences

There are many different types of vehicle suspensions and there are several ways to articulate them. A full simulation of the suspension using rigid-bodies and constraints is a separate topic and will not be discussed here. Simulating the wheels and then animating the suspension pieces to maintain a connection between the wheels and the chassis is a simpler, faster, technique. Animating the suspension in this way can also be accomplished in different ways depending on the need for accuracy.

The easiest way to animate the tires on the rendered vehicle is to connect them to the tires animated by the physics simulation (refer to Animating the Wheels to see how to do this). The drawback of this method is that the suspension parts themselves will be rigidly connected to the chassis and will not animate. This is useful if the vehicles are not the primary focus of the project. If this technique is suitable, the only parts of the rendered mesh that must be separated from the original mesh are the rotating parts of the wheel and tire. All the other parts can remain part of the chassis mesh.

For a higher fidelity animation of the vehicle suspension, the mesh will need to be further subdivided. The rendered mesh will need to be broken apart into parts of the wheel that steer and move up and down with the suspension, but do not rotate and parts of the wheel that do rotate. Next, separate all the suspension parts that connect the non-rotating wheel parts to the chassis. Do this for each wheel. Although there are many different suspension configurations, the following primitive hierarchy should work for most:

  • Chassis (All vehicle parts that are rigidly connected to the body of the vehicle.)

    • FrontLeftSuspension (All non-rotating parts that steer and move vertically as the suspension compresses.)

      • FrontLeftWheel (All parts of the wheel and tire that rotate.)

Duplicate this hierarchy for each of the wheels and tires.

The Wheel and Tire transforms should be positioned in the exact center of rotation for each wheel. The Suspension Part transforms should be centered on their axes of rotation if they rotate.

It is recommended, but not required, that the vehicle mesh is loaded into the stage prior to using the Vehicle Wizard so that all primitives are on the same layer. This makes it possible to simply drag and drop the appropriate mesh pieces onto the appropriate physics vehicle hierarchy nodes. If the Vehicle Wizard is used first, the vehicle mesh may end up being referenced on a different layer. This prevents it from being separated and linked to different pieces of the physics hierarchy.

The next step is to create or load the physics representation of the vehicle by using the Vehicle Wizard or loading one of the Vehicle Demos. Refer to the Animating the Vehicle’s Rendered Mesh section, below, for the next steps to connecting the rendered mesh to the vehicle’s physics simulation.

Vehicle Wizard Description#

The Vehicle Wizard can be opened through the Create > Physics > Vehicle menu command.

The Vehicle Wizard contains three pages. The first asks for the basic dimensions and configuration of the vehicle, the second sets the dimensions of the tires and other wheel and suspension related settings. The last page provides a check list of optional Next Steps to complete the setup of the vehicle and its physics.

The vehicle created by the wizard will be oriented according to Create’s coordinate frame. If Z-up is selected, the X or Y axes can be the forward direction. If Y-up is selected, the X or Z axes can be forward. The Vehicle Wizard will create a PhysicsScene primitive with the PhysxVehicleContextAPI applied which will reflect the chosen configuration. However, if there already exists a physics scene primitive with PhysxVehicleContextAPI applied in the stage, its vertical axis and longitudinal axis information will be used to orient the new vehicle.

Basic Page#

VehicleBasicPage

The Vehicle Prim panel allows to specify the path of the prim that should be used as the vehicle prim. The vehicle prim is the root of a vehicle hierarchy containing all parts that move with the vehicle. This option can be considered, if there is a vehicle render asset available and if the goal is to directly apply vehicle related properties to the prims of that asset to turn it into a vehicle simulation asset. It has to be noted that the specified vehicle prim needs to be a UsdGeomXform. Also, see Authoring the Vehicle’s Rendered Mesh for some recommendations on stage setup and how to arrange the prims of the asset. If no vehicle prim is defined, the wizard will create a prim (and child prims for wheels etc.). The buttons Selected and Clear are helpers to allow to pick the path of the currently selected prim or to clear the path respectively.

In the Chassis Box panel, enter the Length, Width and Height in the same units that are being used in Omniverse USD Composer to model the world. These dimensions will be used to compute the mass distribution, or moments of inertia, of the vehicle. Furthermore, the dimensions will influence where the wheels will be placed (unless the wheel geometry is scanned or the wheel attachment prims are specified explicitly). If no vehicle prim has been defined, the dimensions will also be used to create a collision box that should contain most of the vehicle mesh geometry. The collision box that is created by the wizard can be deleted and replaced with a more form fitting representation later, if desired. However, these inputs are still required to compute the vehicle’s basic mass properties.

Enter the weight of the vehicle in the Mass edit box if you want to deviate from the automatically computed value. Use the same weight units as defined in the stage.

The forward (longitudinal) direction of the vehicle can be selected from the Longitudinal Axis drop down list. If the prim with PhysxVehicleContextAPI applied exists on the stage, the Longitudinal Axis drop down will be disabled and the longitudinal axis specified in PhysxVehicleContextAPI will be displayed. If a longitudinal direction was not previously selected, the available options will be presented.

An option to automatically fill in most of these settings is available by selecting a set of prims from the stage that represent the chassis and then pressing the Scan button. Make sure to define the Longitudinal Axis as desired prior to running the scan. The Vehicle Wizard will fit an axis aligned bounding box to the selected prims and fill in the dimensions settings. The mass will get adjusted too unless the field was previously overridden. The bounding box will also be used to define the vehicle position (if no vehicle prim is specified) and for computing the vehicle’s center of mass. Prims have a purpose that describes what they are used for, such as render, guide, default and proxy. Only render and default prims are scanned. Also, only geometric meshes and shapes can be used to fit a bounding box. Note that selecting the vehicle prim and scanning the chassis box are separated because the vehicle prim does not have to be close to the chassis box and not all render meshes or geometry under the vehicle prim are good candidates to be included in the bounding box computation for the chassis box.

The Drive panel asks for one of three methods to propel the vehicle: Standard, Basic or None.

The Number of Axles edit box sets the number of tire pairs on the vehicle. The tire settings are tuned on the next page.

Press the Next button to access these settings. Alternatively, the Create button can be pressed to use the Axle Page’s default settings to create the vehicle. Press Reset at any time to reset the wizard settings to their default values (this will also clear fields overridden by the user and return to computing those values automatically).

Property

Description

Path

The path to the prim that should be used as the vehicle prim (leave empty to have the wizard create the prim)

Selected Button

Use the path of the currently selected prim for the Path property

Clear Button

Clear the content of the Path property

Length

The longitudinal dimension of the vehicle chassis in world units

Width

The lateral dimension of the vehicle chassis in world units

Height

The vertical dimension of the vehicle chassis in world units

Mass

The weight of the entire vehicle, in units as defined in the stage

Longitudinal Axis

The longitudinal (forward) direction of the vehicle

Scan Button

Computes an axis aligned bounding box around the selected prims and fills in the dimensions and mass settings automatically.

Type

Selects which propulsion model is used to generate tire torques (Standard, Basic, None)

Horsepower

For the Standard and Basic drive type, specifies how much power the engine can generate at its peak

RPM

For the Standard drive type, specifies the maximum revolutions per minute the engine can reach

Number of Gears

For the Standard drive type, specifies the number of gears the transmission has.

Tank Mode

For the Standard drive type, specifies whether special wheel constraints should be added such that the wheels on each side emulate a tank track. This will also add special controller attributes to set the thrust on each track.

Number of Axles

Specifies the number of pairs of tires the vehicle has.

Create Shareable Components

Vehicle components like wheel, tire, suspension, engine etc. will each get their own prim

Use Ackermann Steering Correction

The wheels on the first axle can have Ackermann correction applied. The inner wheel will turn more than the outer wheel when following the path around a curve. This avoids wheels having to slip sideways to stay on the path.

Reset Button

Reset the wizard settings to their default values

Create Button

Create a vehicle simulation using the current settings

Next Button

Go to the Axles page

Standard Drive#

The Standard drive type utilizes an engine and transmission to transfer torque to the driven tires. When using this Drive Type, enter the maximum engine Horsepower, the maximum engine RPM and the Number of Gears in the transmission.

The vehicle engine will be configured to follow an horsepower versus engine RPM curve that is defined by the following reference points:

RPM

Horsepower (HP)

0

80% of max HP

33% of max RPM

100% of max HP

max RPM

80% of max HP

This power curve outputs peak horsepower when the engine reaches a third of its maximum RPM and drops off to 80% at engine idle and max RPM. This curve is not very realistic, but it generates more power at idle for better acceleration from rest.

The Number of Gears specifies how many gears are in the transmission. The top gear is always 1:1 and the 1st gear ratio is set to the same value as the number of gears, 5:1 in a transmission with 5 gears, for example. The remaining gears evenly reduce the gear ratios between the 1st gear and the top gear. The final gear ratio is set to 4:1.

Gears scale the torque generated by the engine before it is applied to the tires. However, the higher the gear ratio, the sooner the transmission must shift up to the next gear before the engine reaches its maximum RPM. Heavier vehicles require higher gear ratios to generate more tire torque to accelerate but require additional gears and more time shifting to compensate.

The vehicle’s top speed will be determined by the RPM and to some degree by the Horsepower setting, while the vehicle’s acceleration profile will be controlled by the Number of Gears and Horsepower setting. If increasing the Horsepower does not increase the vehicle acceleration, then it is likely that the driven tires are spinning or burning out. To further increase the vehicle acceleration, the tire longitudinal stiffness or friction will need to be increased.

Basic Drive#

The Basic drive type works by simply setting a maximum torque that is applied to the driven tires. No engine or transmission is created. The torque is scaled by the amount of throttle applied. The input device is also used to control the steer angle of the tires that steer. This Drive Type is useful for simulating electric vehicles. The maximum torque is computed from the Horsepower setting (and the RPM value which is kept locked when this drive type is selected: Horsepower * 7120 / RPM).

No Drive (None)#

When the None drive type is selected, no tire torques are passed to the tires by the vehicle. The tire torques and steer angles are set manually, instead. This allows customized user control of each tire independently. This can be useful for robotics applications, for example, where tires may be rotated in opposite directions to spin the robot in place.

Axles Page#

VehicleAxlesPage

The Axles panel is used to specify which tires can steer in the Standard and Basic drive types and how much those tires steer. Tire torques are passed to all the tires that have the Driven check box selected. These settings are disabled for the, None drive type. Check the front axle for front wheel drive, check the rear axle for rear wheel drive, or check both axles for all wheel drive. If Tank Mode was enabled, it will not be possible to set the steer values.

The Weight Distribution edit box specifies the percentage of the vehicle weight that rests on each axle. Best results are observed when the weight is evenly distributed, with slight adjustments. The percentages should all sum to 100%, but if they do not, each percentage will be normalized.

The Suspension Damping is used to control the bounce of the vehicle after it is disturbed, like going over a bump. A normal value of 0.3 is the default. This setting causes the vehicle to bounce once before settling. Higher values damp out oscillations faster. A setting of 0.7 causes the vehicle to settle without a single oscillation. For a bouncy ride, try settings between 0.1 and 0.3.

Scan Selected Prims buttons labeled Left and Right are provided to fit an axis aligned bounding box to the selected prims. The bounding box will be used to measure and automatically fill in the Radius, Width and Mass settings for each of the tires scanned. The center of the bounding box will also be used to position the wheel (unless a wheel attachment prim was specified explicitly). Just like the chassis box on the Basic Page, only render and default purpose prims are scanned and only geometric meshes and shapes can be used to fit a bounding box. Make sure to define the Longitudinal Axis on the Basic Page as desired prior to running the scan. Once a tire is scanned, its dimensions will appear below the Default wheel settings in the Wheels panel.

Select Wheel Attachment buttons labeled Left and Right are provided to get the path of the currently selected prim and use it as the wheel attachment prim. The wheel attachment prim is the prim that will take on the position and orientation of the wheel as it spins, steers and moves up and down along the suspension while simulating. The wizard will use the position of the specified wheel attachment prim to define the reference position of the suspension. It has to be noted that the specified wheel attachment prim needs to be a UsdGeomXformable. If no wheel attachment prim is defined, the wizard will create a prim. Once a wheel attachment prim is selected, a corresponding entry will appear below the Default wheel settings in the Wheels panel.

Inside the Wheels panel on the Axles page, select the tire Tire Type to use. The racing Slicks are stickiest on smooth surfaces. Summer tires provide a good grip on smooth, dry surfaces while All Season tires have the least grip on smooth, dry surfaces. The Enable Collisions check box is provided to generate collision objects for the tires themselves (note: will be disabled if any wheel attachment prim was specified). This is useful for open wheeled vehicles, for example, but requires filtering and distinction between geometry to collide against and geometry to drive on. Also note that if the chassis collision box encloses the tires, performance can be improved by disabling the tire collisions.

The Default tire Radius and Width, entered in the same units used to specify the chassis dimensions, are used to set the size of the tire used in the tire physics and tire collision detection. The Mass includes the wheel and tire. Any wheels that were scanned in the Axles panel will be presented below the Default settings (the same goes for selected wheel attachment prims). Any wheels that are not scanned will use the defaults. If a wheel attachment prim was selected, the corresponding entry will show the path of the prim in the Path field.

Press, the Back button to re-adjust the Basic page settings. Changing the number of axles will clear all scanned wheels and selected wheel attachment prims. The Create and Reset button behave the same as on the Basic Page.

Property

Description

Steer Angle

The maximum angle the tire can rotate to one side, in degrees.

Driven

When checked, the tires on this axle receive a drive torque in the Standard or Basic drive types.

Weight Distribution

Specifies the percentage of the vehicle weight supported by this axle. The percentages should sum to 100% but will be normalized if they do not.

Suspension Damping

The “damping ratio” of the shock absorbers, from 0 to 1. A value of 0.3 is normal, a smaller number yields more bounce, larger is more rigid.

Left Button (Scan)

Computes an axis aligned bounding box around the selected prims and fills in the Radius, Width and Mass settings for the left wheel on the specified axle.

Right Button (Scan)

Same as Left Button but applying to the right wheel on the specified axle.

Left Button (Select)

The path to the prim that should be used as the wheel attachment prim (leave empty to have the wizard create the prim)

Right Button (Select)

Same as Left Button but applying to the right wheel on the specified axle.

Tire Type

The type of tire to use on the vehicle, which principally sets the tire friction.

Enable Collisions

When checked, collision objects for the tires will be created.

Query Type

The type of scene query to use for detecting collision of the wheel with the ground surface. Raycast is more efficient but less precise than Sweep. Since Raycast is using one ray per wheel only, collision with a bump etc. might not get detected immediately or not at all.

Default Radius

The vertical size of the tire from its center to an edge in world units

Default Width

The lateral size of the tire from edge to edge in world units

Default Mass

The mass of the wheel and tire, in units as defined in the stage

# Left

The Radius, Width and Mass, in world/stage units, of scanned wheels on the left side of the specified axle. Unscanned wheels will use the Default settings. Path will show the path of the prim to use as the wheel attachment prim (leave empty to have the wizard create the prim).

# Right

Same as # Left but applying to the right wheel on the specified axle.

Reset Button

Reset the wizard settings to their default values

Create Button

Create a vehicle simulation using the current settings

Back Button

Return to the Basic page

Next Button

Go to the Next Steps page

Next Steps Page#

VehicleNextStepsPage

Once the Vehicle Wizard has set up the prims to run a vehicle simulation, there is still more work to do. A ground surface to drive on is needed and the physics settings of the vehicle can be tuned. The Next Steps page will be updated and provides additional detail.

Property

Description

Reset Button

Reset the wizard settings to their default values

Create Button

Create a vehicle simulation using the current settings

Back Button

Return to the Axles page

Vehicle Wizard Collision Groups#

The Vehicle Wizard does not create a drivable surface, but it does create collision groups to prevent the wheels of the car from colliding with the chassis, for example. One of the collision groups created is called GroundSurfaceCollisionGroup, which is set up to prevent collisions with objects in the VehicleWheelCollisionGroup. This prevents the wheels from colliding with the ground surface (instead, tire collisions with the surface are detected using ray casts or sweeps). If Enable Collisions was ticked, be sure the driving surface is a member of the GroundSurfaceCollisionGroup.

To add a simple, drivable surface for testing, select Create > Physics > Ground Plane. If Enable Collisions was ticked, the surface must be added to the GroundSurfaceCollisionGroup. Select the GroundSurfaceCollisionGroup prim on the stage. In the Property panel, find the Collision Group section and add the created collision plane under Includes. When using custom drivable surfaces, ensure they are all members of the GroundSurfaceCollisionGroup.

VehicleCollisionGroup

Animating the Vehicle’s Rendered Mesh#

If the Vehicle Wizard operates on a render mesh hierarchy directly (by defining the vehicle prim and the wheel attachment prims), the render meshes will animate automatically along with the simulation. If the wizard was configured to create a separate simulation prim hierarchy instead or if one of the samples is used as a starting point, it is up to the user to link the render meshes to this simulation representation. Select the Chassis root primitive, created as described in the Authoring the Vehicle’s Rendered Mesh section, above, and child it to the /WizardVehicle/Vehicle primitive, if the vehicle was created with the Vehicle Wizard, or the /Vehicle primitive if a Vehicle Sample was loaded. The Chassis transform may need to be moved so the rendered vehicle lines up with the physics box as much as possible. The rendered vehicle mesh should now move around with the vehicle.

The Vehicle Wizard creates a box to visualize the chassis when no other mesh is connected to the vehicle. It can be removed by selecting the /WizardVehicle/Vehicle/ChassisRender primitive on the stage and deleting it or hiding it, by clicking on the eye icon, next to it.

Aligning the Tires with the Vehicle#

If the Vehicle Wizard tire scan or wheel attachment selection feature was not used to measure the position and radius of each tire, the physics representation of the tires will not align properly with the rendered, mesh representation. Move the physics representation of the tires by selecting each of them in the Viewport window and then dragging them into position.

For improved accuracy, the X, Y and Z position of each wheel in the rendered mesh hierarchy can be copied into the physics Position transforms. The Vehicle Wizard primitives that need updating are named, “/WizardVehicle1/Vehicle/LeftWheel1References”, “/WizardVehicle1/Vehicle/RightWheel1References”, “/WizardVehicle1/Vehicle/LeftWheel2References”, “/WizardVehicle1/Vehicle/RightWheel2References” etc. The Vehicle Sample wheels are named “FrontLeftWheel”, “FrontRightWheel”, “RearLeftWheel” and “RearRightWheel”.

Adjusting the Tire Radii#

Some vehicles have different tire radii for the front and rear tires. If the Vehicle Wizard tire scan feature was not used, only a single tire radius is used for all of the tires. The tire radii that are different must be manually adjusted. Find the wheel components created by the wizard and adjust their tire radii. The wheel components are called “/WizardVehicle1/LeftWheel1”, “/WizardVehicle1/RightWheel1”, “/WizardVehicle1/LeftWheel2” and “/WizardVehicle1/RightWheel2” (or the “/WizardVehicle1/Vehicle/__Wheel_References” prims in case the wizard was told to not create shareable components). Each have a radius edit box that can be adjusted.

VehicleTireRadius

The vehicle wheels might also have a render and potentially a collision representation (depending on how the Vehicle Wizard was configured) whose radii should be modified, too. Click the “/WizardVehicle1/Vehicle/LeftWheel1References/Render” primitive (or the appropriate primitive for the tire that must be changed), select the Property panel and scroll to the bottom to find the radius.

Likewise, to update the collision tire radius, find the “/WizardVehicle1/Vehicle/LeftWheel1References/Collision” primitive (or the appropriate primitive for the tire that must be changed), select the Property panel and scroll to the bottom to find the radius.

Authoring Tool#

Whenever the physics tires are moved, however, there are physics properties that must be updated. A tool has been provided to simplify this process. First select the Vehicle primitive, click on the Property tab and scroll to the Vehicle Authoring Helpers panel. Press the Apply button next to Suspension Frame Transforms Autocompute. The physics properties that are updated by the Authoring tool are the suspension frame transforms. The suspension frame refers to the transform of the suspension at maximum compression. For default setups this means that the wheels have to be placed assuming the suspension is fully compressed, before using the tool.

VehicleAuthoring

Animating the Wheels#

If it is satisfactory to allow the physics to animate the wheels and tires, all of the rotating wheel and tire parts of the rendered mesh can simply be linked to the “/Vehicle/LeftWheel1References”, “/Vehicle/RightWheel1References”, “/Vehicle/LeftWheel2References”, “/Vehicle/RightWheel2References” etc. primitives. If the wheel attachment prims were specified in the Vehicle Wizard, this will be the case already.

However, if it is preferred to have non-rotating wheel parts that steer and compress, but not rotate with the tires, then a more sophisticated approach is required and the more complicated hierarchy described in Authoring the Vehicle’s Rendered Mesh and animated in Copying Transforms is required.

Copying Transforms#

The position and orientation of the physics representation of the wheels must be broken apart into three components and copied into the rendered mesh representation after each simulation step, or update.

This can be accomplished using a Python script, for example. The exact math to do this is different for every vehicle and suspension configuration, but the two very common configurations are provided as an example in the following script for a double wishbone and a swing arm configuration. The front tires use a double wishbone and simply move up and down to compress the tire. The rear tires use a swing arm configuration. The angle of the swingarm is calculated from the compression of the tires and the swing arm radius. This code is used to load a USD file, register for an update callback, and then animate the suspension.

  1 import math
  2
  3 import omni.kit.app
  4 import omni.usd
  5 import omni.physx
  6 from omni.physx.bindings._physx import (
  7     SimulationEvent,
  8
  9     VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION,
 10     VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION
 11 )
 12
 13 from pxr import Usd, UsdGeom, Sdf, Gf
 14
 15
 16 # Put the name of your USD file here.
 17 myStageName = "C:/Users/username/Desktop/PodWorkVehicle.usd"
 18
 19 myVehiclePrimName = "/World/WizardVehicle1/Vehicle"
 20
 21 myFrontLeftName = "/LeftWheel1References"
 22 myFrontRightName = "/RightWheel1References"
 23 myRearLeftName = "/LeftWheel2References"
 24 myRearRightName = "/RightWheel2References"
 25
 26 frontLeftTirePath = myVehiclePrimName + myFrontLeftName
 27 frontRightTirePath = myVehiclePrimName + myFrontRightName
 28 rearLeftTirePath = myVehiclePrimName + myRearLeftName
 29 rearRightTirePath = myVehiclePrimName + myRearRightName
 30
 31 myFrontLeftSuspensionName = "/FrontLeftSuspension"
 32 myFrontRightSuspensionName = "/FrontRightSuspension"
 33 myRearLeftSuspensionName = "/RearLeftSuspension"
 34 myRearRightSuspensionName = "/RearRightSuspension"
 35
 36 frontLeftSuspensionPath = myVehiclePrimName + myFrontLeftSuspensionName
 37 frontRightSuspensionPath = myVehiclePrimName + myFrontRightSuspensionName
 38 rearLeftSuspensionPath = myVehiclePrimName + myRearLeftSuspensionName
 39 rearRightSuspensionPath = myVehiclePrimName + myRearRightSuspensionName
 40
 41 myFrontLeftWheelName = "/FrontLeftWheel"
 42 myFrontRightWheelName = "/FrontRightWheel"
 43 myRearLeftWheelName = "/RearLeftWheel"
 44 myRearRightWheelName = "/RearRightWheel"
 45
 46 frontLeftWheelPath = frontLeftSuspensionPath + myFrontLeftWheelName
 47 frontRightWheelPath = frontRightSuspensionPath + myFrontRightWheelName
 48 rearLeftWheelPath = rearLeftSuspensionPath + myRearLeftWheelName
 49 rearRightWheelPath = rearRightSuspensionPath + myRearRightWheelName
 50
 51
 52 class AnimatedVehicleClass:
 53
 54     def load_animation(self):
 55         # Get all of the necessary prims
 56         self._vehiclePrim = self._stage.GetPrimAtPath(myVehiclePrimName)
 57
 58         self._frontLeftTire = self._stage.GetPrimAtPath(frontLeftTirePath)
 59         self._frontLeftSuspension = self._stage.GetPrimAtPath(frontLeftSuspensionPath)
 60         self._frontLeftWheel = self._stage.GetPrimAtPath(frontLeftWheelPath)
 61
 62         self._frontLeftSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 63         self._frontLeftWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 64         self._frontLeftSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 65         self._frontLeftWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 66
 67         self._frontRightTire = self._stage.GetPrimAtPath(frontRightTirePath)
 68         self._frontRightSuspension = self._stage.GetPrimAtPath(frontRightSuspensionPath)
 69         self._frontRightWheel = self._stage.GetPrimAtPath(frontRightWheelPath)
 70
 71         self._frontRightSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 72         self._frontRightWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 73         self._frontRightSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 74         self._frontRightWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 75
 76         self._rearLeftTire = self._stage.GetPrimAtPath(rearLeftTirePath)
 77         self._rearLeftSuspension = self._stage.GetPrimAtPath(rearLeftSuspensionPath)
 78         self._rearLeftWheel = self._stage.GetPrimAtPath(rearLeftWheelPath)
 79
 80         self._rearLeftSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 81         self._rearLeftWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 82         self._rearLeftSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 83         self._rearLeftWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 84
 85         self._rearRightTire = self._stage.GetPrimAtPath(rearRightTirePath)
 86         self._rearRightSuspension = self._stage.GetPrimAtPath(rearRightSuspensionPath)
 87         self._rearRightWheel = self._stage.GetPrimAtPath(rearRightWheelPath)
 88
 89         self._rearRightSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 90         self._rearRightWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"])
 91         self._rearRightSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 92         self._rearRightWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False)
 93
 94         rearRightWheelPosition = self._rearRightTire.GetAttribute("xformOp:translate").Get()
 95         self._rearWheelRestHeight = rearRightWheelPosition[2]
 96         self._swingArmRadius = rearRightWheelPosition.GetLength()
 97
 98     def load_stage(self, success, errorMsg):
 99         if success == True:
100             self._stage = self._usd_context.get_stage()
101             self._physxInterface = omni.physx.get_physx_interface()
102
103             self.load_animation()
104
105             # Subscribe to events
106             self._physxSimEventSubscription = self._physxInterface.get_simulation_event_stream_v2().create_subscription_to_pop(
107                 self._on_simulation_event
108             )
109             self._stageEventSubscription = self._usd_context.get_stage_event_stream().create_subscription_to_pop(self.on_stage_event)
110             self._updateEventSubscription = omni.kit.app.get_app().get_update_event_stream().create_subscription_to_pop(self.update)
111
112     def __init__(self):
113         self._simStarted = False
114
115         print("Loading Stage " + myStageName)
116
117         self._usd_context = omni.usd.get_context()
118
119         self._physxInterface = None
120
121         # Load the level
122         self._usd_context.open_stage(myStageName, self.load_stage)
123
124     def _on_simulation_event(self, event):
125         if event.type == int(SimulationEvent.RESUMED):
126             self._simStarted = True
127         elif event.type == int(SimulationEvent.STOPPED):
128             self._simStarted = False
129
130     def on_stage_event(self, event):
131         if (event.type == int(omni.usd.StageEventType.CLOSING)):
132             self.shutdown()
133
134     def shutdown(self):
135         self._physxSimEventSubscription = None
136         self._updateEventSubscription = None
137         self._stageEventSubscription = None
138
139     def animate(self):
140
141         # Front Left
142         wheelState = self._physxInterface.get_wheel_state(frontLeftTirePath)
143         position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION]
144         rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION]
145         frontLeftPosition = Gf.Vec3f(position[0], position[1], position[2])
146
147         self._frontLeftSuspension.GetAttribute("xformOp:translate").Set(frontLeftPosition)
148
149         frontLeftSuspensionRotation = Gf.Quatf(rotation[3], 0.0, 0.0, rotation[2])
150         frontLeftSuspensionRotation.Normalize()
151         self._frontLeftSuspension.GetAttribute("xformOp:orient").Set(frontLeftSuspensionRotation)
152
153         frontLeftWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
154         frontLeftWheelRotation.Normalize()
155         self._frontLeftWheel.GetAttribute("xformOp:orient").Set(frontLeftWheelRotation)
156
157         # Front Right
158         wheelState = self._physxInterface.get_wheel_state(frontRightTirePath)
159         position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION]
160         rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION]
161         frontRightPosition = Gf.Vec3f(position[0], position[1], position[2])
162
163         self._frontRightSuspension.GetAttribute("xformOp:translate").Set(frontRightPosition)
164
165         frontRightSuspensionRotation = Gf.Quatf(rotation[3], 0.0, 0.0, rotation[2])
166         frontRightSuspensionRotation.Normalize()
167         self._frontRightSuspension.GetAttribute("xformOp:orient").Set(frontRightSuspensionRotation)
168
169         frontRightWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
170         frontRightWheelRotation.Normalize()
171         self._frontRightWheel.GetAttribute("xformOp:orient").Set(frontRightWheelRotation)
172
173         # Rear Left
174         wheelState = self._physxInterface.get_wheel_state(rearLeftTirePath)
175         position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION]
176         rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION]
177
178         angle = (position[2] - self._rearWheelRestHeight) / self._swingArmRadius
179         rearLeftSuspensionRotation = Gf.Quatf(math.cos(0.5 * angle), 0.0, math.sin(0.5 * angle), 0.0)
180         self._rearLeftSuspension.GetAttribute("xformOp:orient").Set(rearLeftSuspensionRotation)
181
182         rearLeftWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
183         rearLeftWheelRotation.Normalize()
184         self._rearLeftWheel.GetAttribute("xformOp:orient").Set(rearLeftWheelRotation)
185
186         # Rear Right
187         wheelState = self._physxInterface.get_wheel_state(rearRightTirePath)
188         position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION]
189         rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION]
190
191         angle = (position[2] - self._rearWheelRestHeight) / self._swingArmRadius
192         rearRightSuspensionRotation = Gf.Quatf(math.cos(0.5 * angle), 0.0, math.sin(0.5 * angle), 0.0)
193         self._rearRightSuspension.GetAttribute("xformOp:orient").Set(rearRightSuspensionRotation)
194
195         rearRightWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0)
196         rearRightWheelRotation.Normalize()
197         self._rearRightWheel.GetAttribute("xformOp:orient").Set(rearRightWheelRotation)
198
199     def update(self, event):
200         if (self._simStarted):
201             self.animate()
202
203 animatedVehicle = AnimatedVehicleClass()

Driving the Vehicle#

After the Vehicle Wizard has created the vehicle, try driving it around a bit to see how it behaves. The vehicle can be controlled using the keyboard or a connected gamepad.

Every vehicle has some additional properties to control it. On the stage, find and select the vehicle to adjust. In the Property panel, scroll down to the Vehicle Controller Settings section. To select which vehicle to control using the input system, check or un-check the Input Enabled check box. To enable the mouse, check the Mouse Enabled check box. If it is undesirable to automatically go into reverse when the brake is held while at rest, un-check the Auto Reverse Enabled checkbox. When using a gamepad, Steering Sensitivity can be used to apply smaller steer values than the gamepad input indicates. Steering Filter Time allows the steer target to be approached over a certain span of time instead of being applied instantly.

VehiclePhysXProperties

Note that the default input handling for driving a vehicle is mainly meant to quickly test the driving behavior of a vehicle. User applications often want to control the mapping from device input to steer, brake, accelerate commands themselves as the desired control behavior is often very specific. The vehicle command attributes are described in the corresponding USD API schemas PhysxVehicleControllerAPI, PhysxVehicleTankControllerAPI and PhysxVehicleWheelControllerAPI.

Dynamic Vehicle Authoring#

Vehicles can be created and destroyed while the simulation is running. This is called Dynamic Vehicle Authoring. This feature can be used to warp vehicles from one end of a street to the other in order to create the illusion of an endless stream of city traffic, for example.

Presently, there are a few restrictions with how this is done:

  1. All of the vehicle primitives (prims) must be created within a single simulation time step when creating a vehicle. Many of these prims reference other prims and the vehicle creation process will fail if the referenced prims are missing.

  2. A Physics Scene must be created before the simulation is started and it must have the PhysxVehicleContextAPI API schema applied.

  3. To destroy a vehicle, all of the vehicle prims must be deleted within a single simulation time step.