.. _VehicleDynamics: ======================= Vehicle Dynamics ======================= The Vehicle Dynamics extension provides tools for creating vehicle simulations that include tire, engine, clutch, transmission and suspension models. Getting Started ----------------------------- 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. Vehicle Dynamics Introduction ################################## The Vehicle Extension provides some sample scenes that can be loaded quickly to see how the vehicle drives and works in |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. .. image:: /content/images/vehicle_samples.jpg :align: center :alt: 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. .. image:: /content/images/vehicle_gamepad.jpg :align: center :alt: 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 tree window. Scroll in the Property tab until the Procedural Cameras section becomes visible. Press the **Look Follow Camera** button. .. image:: /content/images/vehicle_add_follow_camera_button.jpg :align: center :alt: 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. .. image:: /content/images/vehicle_select_follow_camera.jpg :align: center :alt: 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 |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. .. raw:: html
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, |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, |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_Vehicles_Rendered_Mesh_label: 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 |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. .. image:: /content/images/vehicle_edit_preferences.jpg :align: center :alt: 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 :ref:`Animating_The_Wheels_label` 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. .. raw:: html
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. .. raw:: html
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_label: Basic Page ################## .. image:: /content/images/vehicle_basic_page.jpg :align: center :alt: VehicleBasicPage In the **Chassis Box** panel, enter the **Length**, **Width** and **Height** in the same units that are being used in |composer| to model the world. These dimensions will be used to create a collision box that should contain most of the vehicle mesh geometry and will also be used to compute the mass distribution, or moments of inertia, of the vehicle. 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 in kilograms if you want to deviate from the automatically computed value. Position the center of the chassis box by entering the **X**, **Y** and **Z** positions in the appropriate fields. 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 and position settings. The mass will get adjusted too unless the field was previously overridden. Any of the automated entries can be adjusted after the scan is complete. 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. 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 ================================== =========================================================================================================================== 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 kg X The world X position of the center of the chassis in world units Y The world Y position of the center of the chassis in world units Z The world Z position of the center of the chassis in world units 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, position 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_label: Axles Page ################## .. image:: /content/images/vehicle_axles_page.jpg :align: center :alt: 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 measure and automatically fill in the **Radius**, **Width** and **Mass** settings for each of the tires scanned. Just like the chassis box on the :ref:`Basic_Page_label`, 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 :ref:`Basic_Page_label` 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. 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. 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. Any wheels that are not scanned will use the defaults. Press, the **Back** button to re-adjust the Basic page settings. Changing the number of axles will clear all scanned wheels. The **Create** and **Reset** button behave the same as on the :ref:`Basic_Page_label`. ======================= =========================================================================================================================== 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 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. The dimensions and mass can be adjusted after the scan is complete. Right Button 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 kg # Left The **Radius** and **Width**, in world units, and the **Mass**, in kg, of scanned wheels on the left side of the specified axle. Unscanned wheels will use the **Default** settings. # 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_label: Next Steps Page ###################### .. image:: /content/images/vehicle_next_steps_page.jpg :align: center :alt: VehicleNextStepsPage Once the Vehicle Wizard has created all of the needed prims to run a vehicle simulation, there is still more work to do. The rendered mesh must be animated by linking it to the vehicle created by the wizard, 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 ======================= =========================================================================================================================== .. raw:: html
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. .. image:: /content/images/vehicle_collision_group.jpg :align: center :alt: VehicleCollisionGroup Animating the Vehicle’s Rendered Mesh ----------------------------------------------- Once the physics representation of the vehicle has been created, the vehicle’s rendered mesh can be linked to it. 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 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. .. image:: /content/images/vehicle_tire_radius.jpg :align: center :alt: VehicleTireRadius The vehicle wheels also have a render and potentially a collision representation 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. .. image:: /content/images/vehicle_authoring.jpg :align: center :alt: VehicleAuthoring Once the tires align with the physics representation, child all the non-rotating parts to their respective “Wheel” transforms and all of the rotating parts to their respective “Tire” transforms. The transforms themselves will have to be animated through a script, which will be described in the next section. If the tire render primitives are no longer needed, they can be deleted or hidden. .. _Animating_The_Wheels_label: 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. 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 :ref:`Authoring_The_Vehicles_Rendered_Mesh_label` and animated in :ref:`Copying_Transforms_label` is required. .. raw:: html
.. _Copying_Transforms_label: 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. .. code-block:: python :linenos: import math import omni.kit.app import omni.usd import omni.physx from omni.physx.bindings._physx import ( SimulationEvent, VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION, VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION ) from pxr import Usd, UsdGeom, Sdf, Gf # Put the name of your USD file here. myStageName = "C:/Users/username/Desktop/PodWorkVehicle.usd" myVehiclePrimName = "/World/WizardVehicle1/Vehicle" myFrontLeftName = "/LeftWheel1References" myFrontRightName = "/RightWheel1References" myRearLeftName = "/LeftWheel2References" myRearRightName = "/RightWheel2References" frontLeftTirePath = myVehiclePrimName + myFrontLeftName frontRightTirePath = myVehiclePrimName + myFrontRightName rearLeftTirePath = myVehiclePrimName + myRearLeftName rearRightTirePath = myVehiclePrimName + myRearRightName myFrontLeftSuspensionName = "/FrontLeftSuspension" myFrontRightSuspensionName = "/FrontRightSuspension" myRearLeftSuspensionName = "/RearLeftSuspension" myRearRightSuspensionName = "/RearRightSuspension" frontLeftSuspensionPath = myVehiclePrimName + myFrontLeftSuspensionName frontRightSuspensionPath = myVehiclePrimName + myFrontRightSuspensionName rearLeftSuspensionPath = myVehiclePrimName + myRearLeftSuspensionName rearRightSuspensionPath = myVehiclePrimName + myRearRightSuspensionName myFrontLeftWheelName = "/FrontLeftWheel" myFrontRightWheelName = "/FrontRightWheel" myRearLeftWheelName = "/RearLeftWheel" myRearRightWheelName = "/RearRightWheel" frontLeftWheelPath = frontLeftSuspensionPath + myFrontLeftWheelName frontRightWheelPath = frontRightSuspensionPath + myFrontRightWheelName rearLeftWheelPath = rearLeftSuspensionPath + myRearLeftWheelName rearRightWheelPath = rearRightSuspensionPath + myRearRightWheelName class AnimatedVehicleClass: def load_animation(self): # Get all of the necessary prims self._vehiclePrim = self._stage.GetPrimAtPath(myVehiclePrimName) self._frontLeftTire = self._stage.GetPrimAtPath(frontLeftTirePath) self._frontLeftSuspension = self._stage.GetPrimAtPath(frontLeftSuspensionPath) self._frontLeftWheel = self._stage.GetPrimAtPath(frontLeftWheelPath) self._frontLeftSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._frontLeftWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._frontLeftSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._frontLeftWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._frontRightTire = self._stage.GetPrimAtPath(frontRightTirePath) self._frontRightSuspension = self._stage.GetPrimAtPath(frontRightSuspensionPath) self._frontRightWheel = self._stage.GetPrimAtPath(frontRightWheelPath) self._frontRightSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._frontRightWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._frontRightSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._frontRightWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._rearLeftTire = self._stage.GetPrimAtPath(rearLeftTirePath) self._rearLeftSuspension = self._stage.GetPrimAtPath(rearLeftSuspensionPath) self._rearLeftWheel = self._stage.GetPrimAtPath(rearLeftWheelPath) self._rearLeftSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._rearLeftWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._rearLeftSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._rearLeftWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._rearRightTire = self._stage.GetPrimAtPath(rearRightTirePath) self._rearRightSuspension = self._stage.GetPrimAtPath(rearRightSuspensionPath) self._rearRightWheel = self._stage.GetPrimAtPath(rearRightWheelPath) self._rearRightSuspension.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._rearRightWheel.CreateAttribute("xformOpOrder", Sdf.ValueTypeNames.String, False).Set(["xformOp:translate", "xformOp:orient", "xformOp:scale"]) self._rearRightSuspension.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) self._rearRightWheel.CreateAttribute("xformOp:orient", Sdf.ValueTypeNames.Quatf, False) rearRightWheelPosition = self._rearRightTire.GetAttribute("xformOp:translate").Get() self._rearWheelRestHeight = rearRightWheelPosition[2] self._swingArmRadius = rearRightWheelPosition.GetLength() def load_stage(self, success, errorMsg): if success == True: self._stage = self._usd_context.get_stage() self._physxInterface = omni.physx.get_physx_interface() self.load_animation() # Subscribe to events self._physxSimEventSubscription = self._physxInterface.get_simulation_event_stream_v2().create_subscription_to_pop( self._on_simulation_event ) self._stageEventSubscription = self._usd_context.get_stage_event_stream().create_subscription_to_pop(self.on_stage_event) self._updateEventSubscription = omni.kit.app.get_app().get_update_event_stream().create_subscription_to_pop(self.update) def __init__(self): self._simStarted = False print("Loading Stage " + myStageName) self._usd_context = omni.usd.get_context() self._physxInterface = None # Load the level self._usd_context.open_stage(myStageName, self.load_stage) def _on_simulation_event(self, event): if event.type == int(SimulationEvent.RESUMED): self._simStarted = True elif event.type == int(SimulationEvent.STOPPED): self._simStarted = False def on_stage_event(self, event): if (event.type == int(omni.usd.StageEventType.CLOSING)): self.shutdown() def shutdown(self): self._physxSimEventSubscription = None self._updateEventSubscription = None self._stageEventSubscription = None def animate(self): # Front Left wheelState = self._physxInterface.get_wheel_state(frontLeftTirePath) position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION] rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION] frontLeftPosition = Gf.Vec3f(position[0], position[1], position[2]) self._frontLeftSuspension.GetAttribute("xformOp:translate").Set(frontLeftPosition) frontLeftSuspensionRotation = Gf.Quatf(rotation[3], 0.0, 0.0, rotation[2]) frontLeftSuspensionRotation.Normalize() self._frontLeftSuspension.GetAttribute("xformOp:orient").Set(frontLeftSuspensionRotation) frontLeftWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0) frontLeftWheelRotation.Normalize() self._frontLeftWheel.GetAttribute("xformOp:orient").Set(frontLeftWheelRotation) # Front Right wheelState = self._physxInterface.get_wheel_state(frontRightTirePath) position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION] rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION] frontRightPosition = Gf.Vec3f(position[0], position[1], position[2]) self._frontRightSuspension.GetAttribute("xformOp:translate").Set(frontRightPosition) frontRightSuspensionRotation = Gf.Quatf(rotation[3], 0.0, 0.0, rotation[2]) frontRightSuspensionRotation.Normalize() self._frontRightSuspension.GetAttribute("xformOp:orient").Set(frontRightSuspensionRotation) frontRightWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0) frontRightWheelRotation.Normalize() self._frontRightWheel.GetAttribute("xformOp:orient").Set(frontRightWheelRotation) # Rear Left wheelState = self._physxInterface.get_wheel_state(rearLeftTirePath) position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION] rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION] angle = (position[2] - self._rearWheelRestHeight) / self._swingArmRadius rearLeftSuspensionRotation = Gf.Quatf(math.cos(0.5 * angle), 0.0, math.sin(0.5 * angle), 0.0) self._rearLeftSuspension.GetAttribute("xformOp:orient").Set(rearLeftSuspensionRotation) rearLeftWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0) rearLeftWheelRotation.Normalize() self._rearLeftWheel.GetAttribute("xformOp:orient").Set(rearLeftWheelRotation) # Rear Right wheelState = self._physxInterface.get_wheel_state(rearRightTirePath) position = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_POSITION] rotation = wheelState[VEHICLE_WHEEL_STATE_LOCAL_POSE_QUATERNION] angle = (position[2] - self._rearWheelRestHeight) / self._swingArmRadius rearRightSuspensionRotation = Gf.Quatf(math.cos(0.5 * angle), 0.0, math.sin(0.5 * angle), 0.0) self._rearRightSuspension.GetAttribute("xformOp:orient").Set(rearRightSuspensionRotation) rearRightWheelRotation = Gf.Quatf(rotation[3], 0.0, rotation[1], 0.0) rearRightWheelRotation.Normalize() self._rearRightWheel.GetAttribute("xformOp:orient").Set(rearRightWheelRotation) def update(self, event): if (self._simStarted): self.animate() 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. .. image:: /content/images/vehicle_physx_properties.jpg :align: center :alt: 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.