dimforge / rapier

2D and 3D physics engines focused on performance.
https://rapier.rs
Apache License 2.0
3.93k stars 244 forks source link

Is it not possible to make a vehicle using revolute joints for the wheels? #432

Closed torkeldanielsson closed 5 months ago

torkeldanielsson commented 1 year ago

I am trying to create a vehicle and my approach is to use a RigidBody for each of:

I then set up revolute joints for the wheels and the steering. When I apply steering using set_motor_position and throttle/brake using set_motor_velicity I get the expected result if I let things hover in the air with no ground contact: https://user-images.githubusercontent.com/634877/210060114-e120e304-5c10-478d-8323-8b1f28a0a2fe.mp4

But, when I drop it to the ground it starts behaving super weird, glitching and twitching and rolling back and forth: https://user-images.githubusercontent.com/634877/210060212-96a725d8-5b99-49db-b7b1-1ff7ea4c2012.mp4 (Sorry for shakiness from filming screen, hopefully the glitches are obvious... And this is after reducing the weight of things drastically - at higher weights it was really nuts!)

Now, my main question here is to check that I am not totally on a wrong track with this? It seems logical to be able to use revolute joints like this. But even without steering, just four wheels, if I set ridiculous amount for max_motor_force and set_motor_velocity to zero this thing will still creep slowly and randomly back and forth.... That to me seems like I have some major misunderstanding what revolute joints can be used for? Are they only for "show" and can't handle contact with ground?

Then I also have a minor separate question - how am I to retrieve a joint so I can set speed for it during the update loop, given a MultibodyJointHandle? I came up with this code:

let motor = self
    .multibody_joint_set
    .get_mut(self.wheel_front_left_motor)
    .unwrap();

let joint = &mut motor.0.link_mut(3).unwrap().joint;

joint
    .data
    .as_revolute_mut()
    .unwrap()
    .set_motor_position(steering_angle, 500.0, 50.0);

The call there to link_mut(3)... that surely is not correct 😅

torkeldanielsson commented 1 year ago

Update on this: it seems I used too few substeps for the simulation. Allowing more substeps makes things better. Still seeing undesired behaviour when lifting things up with the forks though...

dimpolo commented 10 months ago

I'm also running into problems with revolute joints. There's movement without a reason and constraints being ignored...

Here's a clip:

https://github.com/dimforge/rapier/assets/33688001/78d3f40b-45ba-49d3-a670-a3289bfc7392

This is with no extra forces, default integrator parameters and dt = 1.0/60.0

Physics Code

```rust use std::f32::consts::FRAC_PI_2 as RIGHT_ANGLE; use rapier3d::pipeline::{DebugRenderBackend, DebugRenderPipeline}; use rapier3d::prelude::*; const WHEEL_RADIUS: f32 = 0.1; const WHEEL_WIDTH: f32 = 0.05; const AXLE_LENGTH: f32 = 0.5; const AXLE_DISTANCE: f32 = 0.4; const FRAME_WIDTH: f32 = 0.04; pub struct Simulation { physics: Physics, } impl Simulation { pub fn new() -> Self { let mut physics = Physics::new(); // Ground let ground_collider = ColliderBuilder::cuboid(2.0, 0.1, 2.0) .translation(vector![0.0, -0.1, 0.0]) .build(); physics.collider_set.insert(ground_collider); // Frame let frame_body = RigidBodyBuilder::dynamic() .translation(vector![0.0, WHEEL_RADIUS, 0.0]) .build(); let frame_handle = physics.rigid_body_set.insert(frame_body); let frame_collider = ColliderBuilder::cuboid(0.05, 0.05, 0.05) .translation(vector![0.0, 0.0, AXLE_DISTANCE / 2.0]) .density(100.0) .build(); physics.collider_set.insert_with_parent( frame_collider, frame_handle, &mut physics.rigid_body_set, ); // Back wheels let back_left_wheel_body = RigidBodyBuilder::dynamic() .translation(vector![AXLE_LENGTH / 2.0, WHEEL_RADIUS, 0.0]) .build(); let back_right_wheel_body = RigidBodyBuilder::dynamic() .translation(vector![-AXLE_LENGTH / 2.0, WHEEL_RADIUS, 0.0]) .build(); let back_left_wheel_collider = ColliderBuilder::cylinder(WHEEL_WIDTH / 2.0, WHEEL_RADIUS) .rotation(Vector::z() * RIGHT_ANGLE) .build(); let back_right_wheel_collider = ColliderBuilder::cylinder(WHEEL_WIDTH / 2.0, WHEEL_RADIUS) .rotation(Vector::z() * RIGHT_ANGLE) .build(); let back_left_wheel_joint = RevoluteJointBuilder::new(Vector::x_axis()) .local_anchor1(point![AXLE_LENGTH / 2.0, 0.0, 0.0]) .build(); let back_right_wheel_joint = RevoluteJointBuilder::new(Vector::x_axis()) .local_anchor1(point![-AXLE_LENGTH / 2.0, 0.0, 0.0]) .build(); let back_left_wheel_handle = physics.rigid_body_set.insert(back_left_wheel_body); physics.collider_set.insert_with_parent( back_left_wheel_collider, back_left_wheel_handle, &mut physics.rigid_body_set, ); physics.multibody_joint_set.insert( frame_handle, back_left_wheel_handle, back_left_wheel_joint, true, ); let back_right_wheel_handle = physics.rigid_body_set.insert(back_right_wheel_body); physics.collider_set.insert_with_parent( back_right_wheel_collider, back_right_wheel_handle, &mut physics.rigid_body_set, ); physics.multibody_joint_set.insert( frame_handle, back_right_wheel_handle, back_right_wheel_joint, true, ); // Front wheel let front_wheel_diameter = WHEEL_RADIUS - FRAME_WIDTH / 2.0; let front_wheel_body = RigidBodyBuilder::dynamic() .translation(vector![0.0, front_wheel_diameter / 2.0, AXLE_DISTANCE]) .build(); let front_wheel_collider = ColliderBuilder::cylinder(WHEEL_WIDTH / 4.0, front_wheel_diameter / 2.0) .rotation(Vector::z() * RIGHT_ANGLE) .friction(0.0) .friction_combine_rule(CoefficientCombineRule::Min) .build(); let front_wheel_joint = RevoluteJointBuilder::new(Vector::x_axis()) .local_anchor1(point![ 0.0, -(front_wheel_diameter + FRAME_WIDTH) / 2.0, AXLE_DISTANCE ]) .build(); let front_wheel_handle = physics.rigid_body_set.insert(front_wheel_body); physics.collider_set.insert_with_parent( front_wheel_collider, front_wheel_handle, &mut physics.rigid_body_set, ); physics.multibody_joint_set.insert( frame_handle, front_wheel_handle, front_wheel_joint, true, ); Simulation { physics } } pub fn step(&mut self, dt: f32) { self.physics.step(dt); } pub fn reset(&mut self) { *self = Simulation::new(); } pub fn debug_render(&mut self, renderer: &mut impl DebugRenderBackend) { self.physics.debug_render(renderer); } } struct Physics { rigid_body_set: RigidBodySet, collider_set: ColliderSet, gravity: Vector, integration_parameters: IntegrationParameters, physics_pipeline: PhysicsPipeline, island_manager: IslandManager, broad_phase: BroadPhase, narrow_phase: NarrowPhase, impulse_joint_set: ImpulseJointSet, multibody_joint_set: MultibodyJointSet, ccd_solver: CCDSolver, physics_hooks: (), event_handler: (), debug_render_pipeline: DebugRenderPipeline, } impl Physics { fn new() -> Self { let rigid_body_set = RigidBodySet::new(); let collider_set = ColliderSet::new(); let gravity = vector![0.0, -9.81, 0.0]; let integration_parameters = IntegrationParameters::default(); let physics_pipeline = PhysicsPipeline::new(); let island_manager = IslandManager::new(); let broad_phase = BroadPhase::new(); let narrow_phase = NarrowPhase::new(); let impulse_joint_set = ImpulseJointSet::new(); let multibody_joint_set = MultibodyJointSet::new(); let ccd_solver = CCDSolver::new(); let physics_hooks = (); let event_handler = (); let debug_render_pipeline = DebugRenderPipeline::new( DebugRenderStyle { rigid_body_axes_length: 0.1, contact_normal_length: 0.1, ..DebugRenderStyle::default() }, DebugRenderMode::all() - DebugRenderMode::COLLIDER_AABBS, ); Self { rigid_body_set, collider_set, gravity, integration_parameters, physics_pipeline, island_manager, broad_phase, narrow_phase, impulse_joint_set, multibody_joint_set, ccd_solver, physics_hooks, event_handler, debug_render_pipeline, } } fn step(&mut self, dt: f32) { self.integration_parameters.dt = dt; self.physics_pipeline.step( &self.gravity, &self.integration_parameters, &mut self.island_manager, &mut self.broad_phase, &mut self.narrow_phase, &mut self.rigid_body_set, &mut self.collider_set, &mut self.impulse_joint_set, &mut self.multibody_joint_set, &mut self.ccd_solver, None, &self.physics_hooks, &self.event_handler, ); } pub fn debug_render(&mut self, renderer: &mut impl DebugRenderBackend) { self.debug_render_pipeline.render( renderer, &self.rigid_body_set, &self.collider_set, &self.impulse_joint_set, &self.multibody_joint_set, &self.narrow_phase, ); } } ```

If helpful I could post a full project.

Did I make a mistake somewhere or are revolute joints broken?

sebcrozet commented 5 months ago

Please try again with the latest rapier version. It has a new constraints solver that improved joint convergence. I’m closing this now, but feel free to open a new issue with a tets case if that happens again.