The forward simulation gives incorrect results for object collision. The total energy of the whole system doubles after collision, a phenomenon that violates the law of conservation of energy. The collision is fully elastic, and there is no external force applied to the system, so the total energy should not change after collision.
Detailed description
When an object is colliding with multiple objects simultaneously, the simulation results of Nimble physics engine doubles the total energy of the system. As a minimal example to reproduce the issue, consider a simple scene with two balls colliding with each other while also colliding with a wall:
Initially, the two balls are located at x1 = 0.78 and x2 = 0.99, and are moving with initial speed v1 = 2 and v2 = 0, respectively, along the positive direction of x-axis. Ball 1 is only 0.01 away from ball 2, and ball 2 is also 0.01 away from a wall located at xw = 1.1. Since the ball 1 is moving to the right while ball 2 is standing still, ball 1 would collide with ball 2. After collision, ball 1 would become still with v1 = 0 while ball 2 would move to the right with speed v2 = 2. Almost immediately following its collision with ball 1, ball 2 would collide with the wall and bounce back to collide with ball 1 again.
Ideally, the total energy of the two balls should not change after the all the collisions, as the collision is fully elastic. However, the nimble engine outputs that ball 1 and ball 2 would move with v1 = v2 = -2 in the end, meaning that the system's total energy is doubled. The simulation results are shown in the following figure (the time step of each frame is 0.01):
Summary of the simulation output by the Nimble physics engine in each time step:
At time step 0, the two balls move with v1 = 2 and v2 = 0, respectively.
At time step 1, the two balls collide with each other.
At time step 2, the engine detects collision in the last time step and decides that the two balls now move with v1 = 0 and v2 = 2, respectively.
At time step 3, ball 2 bumps into the ball. Also, ball 1 is colliding with ball 2 at the same time, as the distance between them is negative (-0.01 actually).
At time step 4, the engine detects collision in the last time step and decides that the two balls now move with v1 = -2 and v2 = -2, respectively. This is where the incorrect result comes in, as the total energy of the system is doubled.
From time step 5 to 7, the two balls keep moving to the left with speed 2.
Besides, when the initial velocity of ball 2 is set to v2 = 3, the energy-doubling phenomenon disappears. Although the correctness of the final output results in this case is not verified, at least the total energy of the system is not doubled. The detailed execution results can be found in the source code below.
How to reproduce
Prerequisites
Version of Nimblephysics: 0.8.91
Version of Python: 3.8.16
Operating System: Ubuntu 18.04.6 LTS
Source code
import nimblephysics as nimble
import torch
world = nimble.simulation.World()
world.setGravity([0, 0, 0]) # Disable gravity
world.setTimeStep(1e-2)
radius = 0.1 # The radius of the two balls.
wall_pos = 1 # The position of the wall on the x-axis. A ball will collide with the wall
# when its x-position value reaches `wall_pos`, i.e., 1 in this case.
thickness = 1 # The thickness of the wall (for visualization purpose).
width = 6 # The width of the wall on the yz axes (for visualization purpose).
# Create a wall object
wall = nimble.dynamics.Skeleton()
wall_joint, wall_body = wall.createWeldJointAndBodyNodePair()
wall_offset = nimble.math.Isometry3()
position = [wall_pos + radius + thickness / 2, 0, 0]
shape_size = [thickness, width, width]
wall_offset.set_translation(position)
wall_joint.setTransformFromParentBodyNode(wall_offset)
wall_shape = wall_body.createShapeNode(nimble.dynamics.BoxShape(shape_size))
wall_body.setRestitutionCoeff(1.0) # 1.0 means no energy loss during collision.
wall_shape.createCollisionAspect()
world.addSkeleton(wall)
def create_ball():
ball = nimble.dynamics.Skeleton()
sphere_joint, sphere_body = ball.createTranslationalJoint2DAndBodyNodePair()
offset = nimble.math.Isometry3()
offset.set_translation([0., 0., 0.])
sphere_joint.setTransformFromParentBodyNode(offset)
sphereShape = sphere_body.createShapeNode(nimble.dynamics.SphereShape(radius))
sphereShape.createCollisionAspect()
sphere_body.setFrictionCoeff(0.0) # Disable friction
sphere_body.setRestitutionCoeff(1.0) # RestitutionCoeff as 1.0 means no energy loss during collision.
sphere_body.setMass(1)
return ball
# Create two balls with the same parameters
b1 = create_ball()
b2 = create_ball()
world.addSkeleton(b1)
world.addSkeleton(b2)
def simulate(init_state, n_steps):
state = init_state
act = torch.zeros(4) # No action force applied. The balls would do uniform linear motion.
trace = [init_state] # The states of the balls during the whole simulation process.
for _ in range(n_steps): # Simulate for `n_steps` time steps.
state = nimble.timestep(world, state, act)
trace.append(state)
return trace
ball1_init_pos, ball2_init_pos = 0.78, 0.99
#=========================
# When the initial velocity of the two balls is set to 2 and 0, respectively,
# the final velocities of them become -2 and -2 after collision, meaning that
# the total energy of the two balls is doubled.
# Such phenomenon obviously violates the energy-conservation law.
ball1_init_vel, ball2_init_vel = 2.0, 0.0
#========================
# When the initial velocity of ball 2 is tweaked to 3,
# the final velocity of ball 1 and 2 would be -5 and 0, respectively, after collision.
# Although such output is still questionable, at least the energy-conservation law is
# respected in this case.
# Uncomment the following line of code to see the effects:
#
# ball2_init_vel = 3.0
# Run simulation for 10 time steps and collect the states of the two balls during simulation.
trace = simulate(
# The positions and velocities of the two balls are set to 0 on the y-axis so that the
# balls would only move along the x-axis.
torch.tensor([
ball1_init_pos, 0., ball2_init_pos, 0., ball1_init_vel, 0., ball2_init_vel, 0.]),
10)
for i, t in enumerate(trace):
print(f"Time step {i}, "
f"x1 = {t[0].item():.2f}, "
f"x2 = {t[2].item():.2f}, "
f"v1 = {t[4].item():.0f}, "
f"v2 = {t[6].item():.0f}")
Incorret simulation results for object collision
The forward simulation gives incorrect results for object collision. The total energy of the whole system doubles after collision, a phenomenon that violates the law of conservation of energy. The collision is fully elastic, and there is no external force applied to the system, so the total energy should not change after collision.
Detailed description
When an object is colliding with multiple objects simultaneously, the simulation results of Nimble physics engine doubles the total energy of the system. As a minimal example to reproduce the issue, consider a simple scene with two balls colliding with each other while also colliding with a wall:
Initially, the two balls are located at x1 = 0.78 and x2 = 0.99, and are moving with initial speed v1 = 2 and v2 = 0, respectively, along the positive direction of x-axis. Ball 1 is only 0.01 away from ball 2, and ball 2 is also 0.01 away from a wall located at xw = 1.1. Since the ball 1 is moving to the right while ball 2 is standing still, ball 1 would collide with ball 2. After collision, ball 1 would become still with v1 = 0 while ball 2 would move to the right with speed v2 = 2. Almost immediately following its collision with ball 1, ball 2 would collide with the wall and bounce back to collide with ball 1 again.
Ideally, the total energy of the two balls should not change after the all the collisions, as the collision is fully elastic. However, the nimble engine outputs that ball 1 and ball 2 would move with v1 = v2 = -2 in the end, meaning that the system's total energy is doubled. The simulation results are shown in the following figure (the time step of each frame is 0.01):
Summary of the simulation output by the Nimble physics engine in each time step:
Besides, when the initial velocity of ball 2 is set to v2 = 3, the energy-doubling phenomenon disappears. Although the correctness of the final output results in this case is not verified, at least the total energy of the system is not doubled. The detailed execution results can be found in the source code below.
How to reproduce
Prerequisites
Source code
Output
Output after uncommenting line 81 and setting v2 = 3