Simulation is no longer deterministic after reset.

JoostHuizinga
Posts: 6
Joined: Sun Jan 20, 2013 10:45 pm

Simulation is no longer deterministic after reset.

Post by JoostHuizinga »

Hello everyone,

Here is my problem, I have a robot that move around using a very simple controller. It moves around for 1000 fixed time-steps and after that I remove the robot completely and rebuild it. However, during the second 1000 time-steps it does not always have the same behavior. In the first 1000 time-steps the robot always behaves the same but, after reseting there seems to be a chance that the first simulation step happens slightly different. Here is a log showing the starting location of my robot, its location after the first step, and its fitness after 1000 time-steps, measured as the distance from its starting location:

Code: Select all

0  X: 0 Y: 1 Z: 0 After step X: -3.39179e-05 Y: 0.999519 Z: -2.18484e-05 fitness: 4.13157
0  X: 0 Y: 1 Z: 0 After step X: -0.00623131  Y: 0.998084 Z: -0.000101492 fitness: 4.27342
0  X: 0 Y: 1 Z: 0 After step X: -0.0112506   Y: 1.00676  Z: -0.00113736  fitness: 5.23277
0  X: 0 Y: 1 Z: 0 After step X: -3.39179e-05 Y: 0.999519 Z: -2.18484e-05 fitness: 4.13157
0  X: 0 Y: 1 Z: 0 After step X: -0.00564403  Y: 0.997333 Z: 0.00378123   fitness: 6.52574
0  X: 0 Y: 1 Z: 0 After step X: -0.0100982   Y: 1.0179   Z: -0.00769315  fitness: 4.67956
0  X: 0 Y: 1 Z: 0 After step X: -3.39179e-05 Y: 0.999519 Z: -2.18484e-05 fitness: 4.13157
0  X: 0 Y: 1 Z: 0 After step X: -3.39179e-05 Y: 0.999519 Z: -2.18484e-05 fitness: 4.13157
0  X: 0 Y: 1 Z: 0 After step X: -0.0100982   Y: 1.0179   Z: -0.00769315  fitness: 4.67956
0  X: 0 Y: 1 Z: 0 After step X: -3.48349e-05 Y: 0.999552 Z: -2.60479e-05 fitness: 4.34444
As you can see, the location after the first time step is different for different runs, and the location after the first time-step correlates perfectly with its ending fitness which means that there is no variation after the first time step of each run. Also, there seems to be a discrete number of "alternate realities" as the position X: -0.0100982 Y: 1.0179 Z: -0.00769315, occurs twice, even though I do not think this position is correct.

This is the main loop of my program:

Code: Select all

		while(true){
			for(int i = 0; i< NR_OF_IDS; i++) touches[i]=0;

			if(internalTimer == 0){
				std::cout << internalTimer << " ";
				std::cout << " X: " << body[0]->getCenterOfMassPosition().getX();
				std::cout << " Y: " << body[0]->getCenterOfMassPosition().getY();
				std::cout << " Z: " << body[0]->getCenterOfMassPosition().getZ();
			}
			m_dynamicsWorld->stepSimulation(0.01, 1, 0.01);
			if(internalTimer == 0){
				std::cout << " After step X: " << body[0]->getCenterOfMassPosition().getX();
				std::cout << " Y: " << body[0]->getCenterOfMassPosition().getY();
				std::cout << " Z: " << body[0]->getCenterOfMassPosition().getZ() << " fitness: ";
			}

			for(int i=0; i < NR_OF_HINGES; i++){
				btScalar motorCommand = 0;
				for(int j=0; j < NR_OF_SENSORS; j++){
					motorCommand += touches[j+4]*weights[j][i];
				}
				motorCommand = tanh(motorCommand);
				motorCommand = motorCommand*45;
				ActuateJoint(i, motorCommand, 0.01);
			}

			internalTimer++;
			if (internalTimer == 1000) {
				internalTimer = 0;
				FitnessSave();
				sleep(1);
				DeleteRobot();
				m_solver->reset();
				CreateRobot();
			}
		}
Here FitnessSave() does nothing more then print the fitness, DeleteRobot() removes every rigid body and hinge from the world and deletes them and CreateRobot() creates a new robot from scratch. ActuateJoint is implemented as: joints[jointIndex]->setMotorTarget(desiredAngle*3.14159/180., timeStep);. The touches array is used for a touch sensor, it is reset to 0 before the call to step, and is then set using a gContactProcessedCallback. Note the sleep(1) statement. If it is removed it delays the problem, and the variations only happen after several runs, which suggests to me that it is a time related bug or a race condition. Also note that I do not render anything. I have done the same thing with rendering but it gets the same results, it only happens slower (because of the rendering).

Now, in order to fix this I have:
  • - Tried different values for simulation step, including: stepSimulation(0.01, 1), stepSimulation(0.01, 0) and stepSimulation(0.01, 0, 0.01).
    - Tried to reset the solver and reset srand().
    - Tried to reset the internal time-step of m_dynamicsWorld by calling stepSimulation(0, 0, 0)
    - Tried to make sure I am using the btSortedOverlappingPairCache instead of the btHashedOverlappingPairCache
    - Tried to use the build in clientResetScene() function, both with and without rebuilding my robots.
    - Tried to create an entirely new dynamic world, including recreating every single object that is passed to dynamic world on construction.
But without any luck, the parameters do influence the results somewhat, but since it is a non-deterministic problem, I am never sure this is because of the parameter I set, or if it is just that the simulation decided to act weird at a different point in time. Now I am pretty new Bullet, so do not mistake my rigor in trying different solutions for knowledge, it might be a very obvious or silly mistake that I made, so any suggestions are welcome.

As a side note, I am using the ragdollDemo.cpp as a template, and my implementation extends GlutDemoApplication. For everything I did not know how to set, I used the settings of this Demo.
JoostHuizinga
Posts: 6
Joined: Sun Jan 20, 2013 10:45 pm

Re: Simulation is no longer deterministic after reset.

Post by JoostHuizinga »

Hello again everyone,

I have solved my problem. Apparently you have to call enableMotor(true) at every time-step, instead of just after you create the hinge, in order to prevent the simulation from ending up in a slightly different state after deleting all objects and then adding all objects.

So the ActuateJoint() function now looks like this:

Code: Select all

void RagdollDemo::ActuateJoint(int jointIndex, double desiredAngle, double timeStep) {
	joints[jointIndex]->enableMotor(true);
	joints[jointIndex]->setMotorTarget(desiredAngle*3.14159/180., timeStep);
}
The question that remains is, why is this the case?
xexuxjy
Posts: 225
Joined: Wed Jan 07, 2009 11:43 am
Location: London

Re: Simulation is no longer deterministic after reset.

Post by xexuxjy »

Seem's a bit odd as far as I can see nothing in bullet disables the motor after you've asked for it to be enabled.... it's default value on construction is false, but once you've asked for it to be enabled I don't see why you would have to do that on each frame.
JoostHuizinga
Posts: 6
Joined: Sun Jan 20, 2013 10:45 pm

Re: Simulation is no longer deterministic after reset.

Post by JoostHuizinga »

That is correct, the motor does not get disabled, and in most circumstances the motor seems to works fine. However, when the robot, including all its hinges, gets destroyed and then rebuild, there is a small difference in how the components of the new robot act during the first time-step of that 'run'.

Now I have gone through a tiny bit of Bullet code, and apparently the boolean value for enableAngularMotor (which is set by the enableMotor function) ends up in a struct as an integer. My guess is that this integer changes value over time, and resetting the motor causes this integer to be reset to one. Why this would affect the behavior of the newly created robot in the next time-step after the old robot gets destroyed, I don't know. However, do note that it is not necessary for the motor to actually be used, just enabling the motor, run for a thousand time-steps, and then recreating the robot, might cause an error at the first time-step.