google / brax

Massively parallel rigidbody physics simulation on accelerator hardware.
Apache License 2.0
2.34k stars 255 forks source link

Issue with bodies flying apart in simulation [Question] #208

Open csimo005 opened 2 years ago

csimo005 commented 2 years ago

I am trying to implement my own simulation of a robot claw. I have an issue where for some reason all the bodies are exploding. This is odd to me because it only happens when I include all bodies together, pictured below. If I only include one claw it works just fine and all the joints work as expected. But for some reason I don't understand including both claws will cause the bodies to spontaneously explode away from each other at high speed. Additionally I have found that I can include both claws if I don't include a link to the ground plane. Is this a problem with my approach or is this a bug? I am unsure how investigate further and would appreciate any advice.

Screenshot from 2022-07-03 13-14-59

import functools
import time

from IPython.display import HTML, Image 
import gym

try:
  import brax
except ImportError:
  from IPython.display import clear_output 
  !pip install git+https://github.com/google/brax.git@main
  clear_output()
  import brax

from brax import envs
from brax import jumpy as jp
from brax.envs import to_torch
from brax.io import html
from brax.io import image

import numpy as np

right_claw = True
left_claw = True

conf = brax.Config(dt=0.05, substeps=20, angular_damping=-0.05, dynamics_mode='pbd')
conf.gravity.z = -9.8

ground = conf.bodies.add(name='floor', mass=1)
ground.frozen.all = True
ground.inertia.x = 1
ground.inertia.y = 1
ground.inertia.z = 1
plane = ground.colliders.add().plane
plane.SetInParent()

link1 = conf.bodies.add(name='link1', mass=1)
cap = link1.colliders.add().capsule
cap.radius = 0.05
cap.length = 0.5
cap.SetInParent()

link2 = conf.bodies.add(name='link2', mass=1)
cap = link2.colliders.add().capsule
cap.radius = 0.05
cap.length = 0.3
cap.SetInParent()

cap = link2.colliders.add().capsule
cap.radius = 0.05
cap.length = 0.5
cap.SetInParent()

cap = link2.colliders.add().capsule
cap.radius = 0.05
cap.length = 0.5
cap.SetInParent()

link2.colliders[0].rotation.y = 90.0
link2.colliders[1].rotation.x = 90.0
link2.colliders[2].rotation.x = 90

link2.colliders[1].position.x = 0.1
link2.colliders[2].position.x = -0.1

if (right_claw):
  link3 = conf.bodies.add(name='link3', mass=1)
  cap = link3.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  link3.colliders[0].rotation.y = 90.0

  link4 = conf.bodies.add(name='link4', mass=1)
  cap = link4.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  link4.colliders[0].rotation.y = 90.0

  link5 = conf.bodies.add(name='link5', mass=1)
  cap = link5.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 1
  cap.SetInParent()

  cap = link5.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  cap = link5.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  link5.colliders[0].rotation.x = 90.0
  link5.colliders[1].rotation.x = 90.0
  link5.colliders[1].rotation.y = 45
  link5.colliders[1].position.x = -0.15
  link5.colliders[1].position.y = 0.6
  link5.colliders[2].rotation.x = 90.0
  link5.colliders[2].position.x = -0.30
  link5.colliders[2].position.y = 0.5 + 0.28 + 0.15

if(left_claw):
  link6 = conf.bodies.add(name='link6', mass=1)
  cap = link6.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  link6.colliders[0].rotation.y = 90.0

  link7 = conf.bodies.add(name='link7', mass=1)
  cap = link7.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  link7.colliders[0].rotation.y = 90.0

  link8 = conf.bodies.add(name='link8', mass=1)
  cap = link8.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 1
  cap.SetInParent()

  cap = link8.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  cap = link8.colliders.add().capsule
  cap.radius = 0.05
  cap.length = 0.5
  cap.SetInParent()

  link8.colliders[0].rotation.x = 90.0
  link8.colliders[1].rotation.x = 90.0
  link8.colliders[1].rotation.y = -45
  link8.colliders[1].position.x = 0.15
  link8.colliders[1].position.y = 0.6
  link8.colliders[2].rotation.x = 90.0
  link8.colliders[2].position.x = 0.30
  link8.colliders[2].position.y = 0.5 + 0.28 + 0.15

joint1 = conf.joints.add(name='joint1', parent='floor', child='link1', angular_damping=50.0)
joint1.angle_limit.add(min=-360, max=360)
joint1.parent_offset.z = 0.25
joint1.rotation.y=90

joint2 = conf.joints.add(name='joint2', parent='link1', child='link2', angular_damping = 50.0)
joint2.angle_limit.add(min=-360, max=360)
joint2.parent_offset.z = 0.2
joint2.rotation.x = 90

if(right_claw):
  joint3 = conf.joints.add(name='joint3', parent='link2', child='link3', angular_damping=50.0)
  joint3.angle_limit.add(min=-75, max=75)
  joint3.parent_offset.y = -0.2
  joint3.parent_offset.x =  0.1
  joint3.child_offset.x = -0.2
  joint3.rotation.y = -90

  joint4 = conf.joints.add(name='joint4', parent='link2', child='link4', angular_damping=50.0)
  joint4.angle_limit.add(min=-360, max=360)
  joint4.parent_offset.y = 0.2
  joint4.parent_offset.x =  0.1
  joint4.child_offset.x = -0.2
  joint4.rotation.y = -90

  joint5 = conf.joints.add(name='joint5', parent='link3', child='link5', angular_damping=50.0)
  joint5.angle_limit.add(min=-360, max=360)
  joint5.parent_offset.x = 0.2
  joint5.child_offset.y = -0.45
  joint5.rotation.y = -90

  joint6 = conf.joints.add(name='joint6', parent='link4', child='link5', angular_damping=50.0)
  joint6.angle_limit.add(min=-360, max=360)
  joint6.parent_offset.x = 0.2
  joint6.child_offset.y = -0.05
  joint6.rotation.y = -90

if(left_claw):
  joint7 = conf.joints.add(name='joint7', parent='link2', child='link6', angular_damping=50.0)
  joint7.angle_limit.add(min=-75, max=75)
  joint7.parent_offset.y = -0.2
  joint7.parent_offset.x = -0.1
  joint7.child_offset.x = 0.2
  joint7.rotation.y = 90

  joint8 = conf.joints.add(name='joint8', parent='link2', child='link7', angular_damping=50.0)
  joint8.angle_limit.add(min=-360, max=360)
  joint8.parent_offset.y = 0.2
  joint8.parent_offset.x = -0.1
  joint8.child_offset.x = 0.2
  joint8.rotation.y = 90

  joint9 = conf.joints.add(name='joint9', parent='link6', child='link8', angular_damping=50.0)
  joint9.angle_limit.add(min=-360, max=360)
  joint9.parent_offset.x = -0.2
  joint9.child_offset.y = -0.45
  joint9.rotation.y = 90

  joint10 = conf.joints.add(name='joint10', parent='link7', child='link8', angular_damping=50.0)
  joint10.angle_limit.add(min=-360, max=360)
  joint10.parent_offset.x = -0.2
  joint10.child_offset.y = -0.05
  joint10.rotation.y = 90

actuator1 = conf.actuators.add(name='joint1', joint='joint1', strength=100.).torque
actuator1.SetInParent()

actuator2 = conf.actuators.add(name='joint2', joint='joint2', strength=100.).torque
actuator2.SetInParent()

if(right_claw):
  actuator3 = conf.actuators.add(name='joint3', joint='joint3', strength=100.).torque
  actuator3.SetInParent()

if(left_claw):
  actuator4 = conf.actuators.add(name='joint7', joint='joint7', strength=100.).torque
  actuator4.SetInParent()

sys = brax.System(conf)
qps = [sys.default_qp()]

for i in range(100):
  qps.append(sys.step(qps[-1], [0, 0, 1, 1])[0])
HTML(html.render(sys, qps))
EelcoHoogendoorn commented 2 years ago

Is the angular damping supposed to take negative values? Not sure, just shooting from the hip.

Ive had similar issues, when dealing with situations where multiple contacts can be generated between a single pair of bodies; they are updated together which compromises unconditional stability. Hacking in some contact compliance can be one simple fix; but im not positive thats your issue here.

EDIT: that is, fiddle a positive constant into here instead of the zero. Note you might need to do this in to TwoWayCollider class lower in the code as well if the problem arises between the two bodies rather than with the ground plane.