Hey,
I’ve been looking at the TestPhysicsCar.java and associated tutorial and wondering how to implement skid-steering, i.e where wheels don’t physically turn, but you can a have situation like the two right wheels going forwards and the two left wheels going backward allowing the vehicle make tight turns and maneuvers. From what I can see, the jBullet raycast car moves foward by applying a force to the entire vehicle and not by driving with the wheels, so this may require an entirely different design… is this right, or am i missing something?
Cheers
The jBullet Vehicle class has no way of doing this as far as i know.
You can crate a vehicle by simulating each each wheel with a HingeJoint attached to a Sphere/cylinder rigidbody and to the vehicle, see TestHingeJoint for help.
You are both wrong, bullet always uses separate forces on all wheels. Theres methods for everything with a wheel index parameter. And you can get the wheelInfo for each wheel to set separate config values.
hey normen,
so there are separate forces on all wheels, but do the wheels actually revolve->create friction with the ground → and push the car forwards? I thought because it was a raycast vehicle that it doesn’t model tyre friction very accurately? do you know where i can find any documentation on just which simplifying approximations are used in modeling the vehicle?
cheers
try:
http://tinyurl.com/ydfb7lm
or if link doesnt work, go directly:
Vehicle Simulation With Bullet
so it turns out for my purposes that the raycast is too coarse grained a simulation, is there any sort of guide or tips available for creating a vehicle with rigid body wheels?
Whats the problem? Just attach some cylinders to a box using hinge joints…
I made this extention to HingeJoint a while ago to simplify adding wheels to a rigidbody.
-Position and rotate two spatials (wheel and vehicle) like you want them to be physically attached and add rigidbodycontrols to them.
-Pass the two spatials, a rotational axis in world coordinate, and a connection point in world coordinate to the WheelJoint constructor.
// Add package
import com.jme3.bullet.joints.HingeJoint;
import com.jme3.math.Vector3f;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.scene.Spatial;
/**
*
-
@author makeshift
*/
public class WheelJoint extends HingeJoint {
public WheelJoint(Spatial A,Spatial B,Vector3f rotationAxis,Vector3f center){
super(A.getControl(RigidBodyControl.class),B.getControl(RigidBodyControl.class),A.worldToLocal(center,Vector3f.ZERO),B.worldToLocal(center,Vector3f.ZERO),Vector3f.ZERO,Vector3f.ZERO);
axisA = A.localToWorld(Vector3f.ZERO, null); // Extract center of A in world coord. (RigidBody of car)
axisA = axisA.add(rotationAxis); // Add world rotation axis
A.worldToLocal(axisA, axisA); // Extract local rotation axis
axisB = B.localToWorld(Vector3f.ZERO, null); // Extract center of B in world coord. (RigidBody of wheel)
axisB = axisB.add(rotationAxis); // Add world rotation axis
B.worldToLocal(axisB, axisB); // Extract local rotation axis
createJoint(); // Make the joint
}
}
hey guys, so I did what you both suggested and connected 4 cylinders to a box using hingejoints. I can sort of drive the car by applying a torque impulse to the wheel rigidbodies, but it only works intermittently because ( i think ) the wheels distort from their vertical orientation and kind of slip under the body of the car, causing all kinds of strange behaviour… is there a way to ‘clamp’ the wheels so they stay correctly oriented?
thanks!
Sounds like the ratio of mass on the box to the cylinders is to high.
Increase the mass of the wheels.
i have played with the mass ratio, and it can improve the situation, but not under high angular rotation. is there a constraint or joint that i can implement to lock the wheels in the vertical position?
Please post some examplecode.
Hello, I don’t want to start new topic because it is actually already dicussed here.
I was playing with jbullet raycast vehicle, trying to simmulate tank, with several wheel and skid steering.
However, it look’s like the friction is anisotropic. You can check it yourselfs in my code, if you hold KEY_NUMPAD4 ( or KEY_NUMPAD6) the vehicle is supposed to turn in place by appyling +force on left and -force on right side wheels.
However, if you turn 360° this way, you can see that the angular velocity of turning change depending on orientation relatively to “world” or more probably relatively to terrain mesh tesselation. For some orientations the vehicle almost can’t turn at all, and for some orientations it turns very quickly.
I was reading the paper Vehicle Simulation With Bullet
Vehicle_Simulation_With_Bullet - Google Docs
And I think it could be somehow connected with the pyramid friction model
Have you any idea how to solve this? (it means how to make turning speed independent on orientation of the vehicle)
Please try this code
[java]
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.VehicleControl;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Cylinder;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.input.ChaseCamera;
public class Main extends SimpleApplication implements ActionListener {
int nwheels = 5;
float radius = 0.4f;
float restLength = 1.3f; // set legth of suspension
float yOff = 1.2f; // set length of mass point
float xOff = 1f;
float zOff = 2f;
float roll_influence = 0.0f;
float stiffness = 10.0f;//200=f1 car
float compValue = 0.1f; //(should be lower than damp)
float dampValue = 0.2f; //
float maxSuspensionForce = 6000.0f;
float mass = 200.0f;
float accelerationForce = 100.0f;
float brakeForce = 5.0f;
float steeringValue = 0;
float accelerationValue = 0;
float left_acc=0;
float right_acc=0;
float terrainRughtness = 0.0f; // flat terrain
//float terrainRughtness = 0.5f; // rugh terrain
private Vector3f jumpForce = new Vector3f(0, 3000, 0);
private BulletAppState bulletAppState;
private VehicleControl vehicle;
Node vehicleNode;
Vector3f direction = new Vector3f();
boolean rotate;
public static void main(String[] args) {
Main app = new Main();
app.start();
}
@Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
bulletAppState.getPhysicsSpace().enableDebug(assetManager);
//PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace(), getCamera());
createPhysicsTestWorld();
setupKeys();
buildPlayer();
//flyCam.setMoveSpeed(100);
flyCam.setEnabled(false);
ChaseCamera chaseCam = new ChaseCamera(cam, vehicleNode, inputManager);
}
private PhysicsSpace getPhysicsSpace(){
return bulletAppState.getPhysicsSpace();
}
private void buildTrack( VehicleControl vehicle, int nwheels, Node vehicleNode, Material mat){
Cylinder wheelMesh = new Cylinder(4, 16, radius, radius * 0.6f, true);
Vector3f wheelDirection = new Vector3f(0, -1, 0); // was 0, -1, 0
Vector3f wheelAxle = new Vector3f(-1, 0, 0); // was -1, 0, 0
float zstep = 2.0fzOff/(float)(nwheels-1);
for (int i=0; i<nwheels; i++){
Node node1 = new Node(“left_”+i);
Geometry wheels1 = new Geometry(“left_”+i, wheelMesh);
node1.attachChild(wheels1);
wheels1.rotate(0, FastMath.HALF_PI, 0);
wheels1.setMaterial(mat);
vehicle.addWheel(node1, new Vector3f(-xOff, yOff, zstepi-zOff),
wheelDirection, wheelAxle, restLength, radius, false);
vehicle.setRollInfluence(i2,roll_influence);
vehicleNode.attachChild(node1);
Node node2 = new Node("right "+i);
Geometry wheels2 = new Geometry("right "+i, wheelMesh);
node2.attachChild(wheels2);
wheels2.rotate(0, FastMath.HALF_PI, 0);
wheels2.setMaterial(mat);
vehicle.addWheel(node2, new Vector3f(xOff, yOff, zstepi-zOff),
wheelDirection, wheelAxle, restLength, radius, false);
vehicle.setRollInfluence(i2+1,roll_influence);
vehicleNode.attachChild(node2);
}
};
public void createPhysicsTestWorld() {
//Material material = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
//material.setTexture(“ColorMap”, assetManager.loadTexture(“Interface/Logo/Monkey.jpg”));
Material matWire = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
matWire.getAdditionalRenderState().setWireframe(true);
matWire.setColor(“Color”, ColorRGBA.Green);
int mapsize = 513;
float freqx = 0.5f;
float freqy = 0.5f;
float[] map = new float[mapsizemapsize];
for (int iy=0;iy<mapsize;iy++){
for (int ix=0;ix<mapsize;ix++){
// map[iymapsize+ix]=10FastMath.sin(6.28fixfreqx)+10FastMath.sin(6.28fiyfreqy);
map[iymapsize+ix]=FastMath.nextRandomFloat();
}
}
TerrainQuad terrain;
terrain = new TerrainQuad(“terrain”, 65, mapsize, map);
terrain.setLocalTranslation(0f, -5.0f, 0f);
terrain.setLocalScale(1f,terrainRughtness, 1f); // rugh terrain
TerrainLodControl control = new TerrainLodControl(terrain, cam);
control.setLodCalculator( new DistanceLodCalculator(65, 1.7f) );
terrain.addControl(control);
terrain.setMaterial( matWire );
terrain.setLocked(false);
rootNode.attachChild(terrain);
terrain.addControl(new RigidBodyControl(0));
bulletAppState.getPhysicsSpace().addAll(terrain);
}
void accelerateTrack( VehicleControl vehicle, int nwheels, float left, float right){
for (int i=0; i<nwheels; i++){
vehicle.accelerate(i2 , left );
vehicle.accelerate(i2+1, right );
}
}
private void buildPlayer() {
Material mat = new Material(getAssetManager(), “Common/MatDefs/Misc/Unshaded.j3md”);
mat.getAdditionalRenderState().setWireframe(true);
mat.setColor(“Color”, ColorRGBA.Red);
CompoundCollisionShape compoundShape = new CompoundCollisionShape();
BoxCollisionShape box = new BoxCollisionShape(new Vector3f(0.5f, 0.5f, 2.5f));
compoundShape.addChildShape(box, new Vector3f(0, 1, 0));
vehicleNode=new Node(“vehicleNode”);
vehicle = new VehicleControl(compoundShape, mass);
vehicleNode.addControl(vehicle);
//setting suspension values for wheels, this can be a bit tricky
//see also https://docs.google.com/Doc?docid=0AXVUZ5xw6XpKZGNuZG56a3FfMzU0Z2NyZnF4Zmo&hl=en
vehicle.setSuspensionCompression ( compValue * 2.0f * FastMath.sqrt(stiffness) );
vehicle.setSuspensionDamping ( dampValue * 2.0f * FastMath.sqrt(stiffness) );
vehicle.setSuspensionStiffness ( stiffness );
vehicle.setMaxSuspensionForce ( maxSuspensionForce );
buildTrack( vehicle, nwheels, vehicleNode, mat);
rootNode.attachChild(vehicleNode);
getPhysicsSpace().add(vehicle);
}
@Override
public void simpleUpdate(float tpf) {
// cam.lookAt(vehicle.getPhysicsLocation(), Vector3f.UNIT_Y);
}
boolean Left_forward = false;
boolean Right_forward = false;
boolean Left_backward = false;
boolean Right_backward = false;
boolean Forward = false;
boolean Backward = false;
boolean Left = false;
boolean Right = false;
boolean Break = false;
void steerCar(){
left_acc = 0.0f;
right_acc = 0.0f;
if ( Left_backward || Backward || Right ){ left_acc =-accelerationForce;}
if ( Right_backward || Backward || Left ){ right_acc =-accelerationForce;}
if ( Left_forward || Forward || Left ){ left_acc =+accelerationForce;}
if ( Right_forward || Forward || Right ){ right_acc =+accelerationForce;}
accelerateTrack(vehicle,nwheels,left_acc,right_acc);
System.out.println("left_acc= “+left_acc+” | right_acc= " +right_acc );
if ( Break ){ vehicle .brake(brakeForce); }else{ vehicle .brake(0.0f); }
}
private void setupKeys() {
inputManager.addMapping(“Left_forward” , new KeyTrigger(KeyInput.KEY_NUMPAD7));
inputManager.addMapping(“Right_forward” , new KeyTrigger(KeyInput.KEY_NUMPAD9));
inputManager.addMapping(“Left_backward” , new KeyTrigger(KeyInput.KEY_NUMPAD1));
inputManager.addMapping(“Right_backward”, new KeyTrigger(KeyInput.KEY_NUMPAD3));
inputManager.addMapping(“Forward” , new KeyTrigger(KeyInput.KEY_NUMPAD8));
inputManager.addMapping(“Backward”, new KeyTrigger(KeyInput.KEY_NUMPAD2));
inputManager.addMapping(“Left” , new KeyTrigger(KeyInput.KEY_NUMPAD4));
inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_NUMPAD6));
inputManager.addMapping(“Break”, new KeyTrigger(KeyInput.KEY_NUMPAD5));
inputManager.addListener(this, “Left_forward” );
inputManager.addListener(this, “Right_forward” );
inputManager.addListener(this, “Left_backward” );
inputManager.addListener(this, “Right_backward”);
inputManager.addListener(this, “Forward” );
inputManager.addListener(this, “Backward” );
inputManager.addListener(this, “Left” );
inputManager.addListener(this, “Right”);
inputManager.addListener(this, “Break”);
inputManager.addMapping(“Space”, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping(“Reset”, new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addListener(this, “Space”);
inputManager.addListener(this, “Reset”);
}
public void onAction(String binding, boolean value, float tpf) {
System.out.println(binding);
if(binding.equals(“Left_forward” )) Left_forward =value;
if(binding.equals(“Right_forward” )) Right_forward =value;
if(binding.equals(“Left_backward” )) Left_backward =value;
if(binding.equals(“Right_backward”)) Right_backward =value;
if(binding.equals(“Forward” )) Forward =value;
if(binding.equals(“Backward” )) Backward =value;
if(binding.equals(“Right” )) Right =value;
if(binding.equals(“Left” )) Left =value;
if(binding.equals(“Break” )) Break =value;
steerCar();
if (binding.equals(“Space”)) { if (value) {
vehicle.applyImpulse(jumpForce, Vector3f.ZERO);
}}
if (binding.equals(“Reset”)) { if (value) {
System.out.println(“Reset”);
vehicle.setPhysicsLocation(Vector3f.ZERO);
vehicle.setPhysicsRotation(new Matrix3f());
vehicle.setLinearVelocity(Vector3f.ZERO);
vehicle.setAngularVelocity(Vector3f.ZERO);
vehicle.resetSuspension();
}
}
}
}
[/java]
maybe try making them all steerable and rotate them to even out the results
It would help if the the anisotropy would change fast depending on angle. However that is not the case.
The effective friction looks to depend on angle like ~ sin( angle )^2 .... which means that there is one "fast" orientation and one "slow" perpendicular to it. And it looks like that the "slow" direction is paraell to diagonal line (seen on wireframe) of the terrain quads (it can be just coincidence).
If I would like to "smear it out" as you recommand, it would be necessary to randomize direction of wheels in whole ~360° which would make the car immobile.
I was trying to use the vehicle to simulate real physics of tank movement, where the turning speed is importaint tactical parameter. Probably it would be neccessary to create own physical model, which is however much of work.
maybe try making them all steerable and rotate them to even out the results
Well in fact the rayCast vehicle is not too much code… You can achieve the same with some ray casting…