Moving from Bullet 2 to Pybullet

Official Python bindings with a focus on reinforcement learning and robotics.
Post Reply
maiklof
Posts: 17
Joined: Wed Nov 20, 2013 10:01 am

Moving from Bullet 2 to Pybullet

Post by maiklof » Thu Oct 04, 2018 10:18 pm

Hi everyone!
First of all, I want to thank you for the great job you are doing with the Bullet Physics library!
I have been using Bullet 2 for many years now, but mostly in python, thanks to a wrapper I found on the internet and I have been maintaining and developing further. Now I am considering moving to PyBullet but I have some questions. I think it is important to say that I just need a physics engine for my project, without the GUI, VR, AI and the new advanced extra features, so I am interested in using the DIRECT mode. The visualization will be done using a 3D engine. I will use the GUI mode just for debugging. I will need to create different kind of single and multi-body objects, using meshes, constraints and create robots and mechanisms. So, as far as I can see in the documentation, there is plenty of useful methods to make all this very easily... But I miss some stuff from Bullet 2, at least looking to the documentation:

1. Have bodyes MotionStates in pyBullet? Or is there any way to get notifications of the objects that are moving? I can have many dynamic objects in a system and need to know which are moving to get the transform and render them.
2. I see that collision filter masks are implemented for shapes, but what happens with ray tests in pybullet? (Batch Tests are a great feature btw.!)
3. I have been using collision callbacks to modify collision points to simulate Conveyor belt-like behavior in some shapes, among other things. I use the CF_CUSTOM_MATERIAL_CALLBACK and the user-data pointer of the shapes that I am interested to provide some "material" information, and then change the behavior of the surface in the callback function. I think that being able to handle material information of the surfaces to modify the manifolds (like friction, damping, contact motion, etc...) brings many possibilities. I do not know if could implement this as a plugin or there is another way to do this kind of things in pybullet...

I think I will need to check pybullet in more detail to get a better idea of the limitations compared to the C++ library. But because I am interested in using it in python I would be happy to help if there is anything I can contribute and bring these features if they can be implemented.

Thanks in advance!

User avatar
Erwin Coumans
Site Admin
Posts: 4184
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Moving from Bullet 2 to Pybullet

Post by Erwin Coumans » Fri Oct 05, 2018 12:56 am

Yes, the GUI mode is primarily as debug visualizer.

PyBullet has a plugin system (loadPlugin) to customize things.

>> 1. Have bodies MotionStates in pyBullet?

No motion states, but you can write a plugin that gets all notifications of changes, we have some internal plugin for this to notify an external renderer through GRPC. You/we could expose changes to Python, but Python may be not the best choice (a bit slow). I'll see if we can write a default plugin that allows Python to query for the changes since last Python call. The plugin needs to implement a "processNotifications" function, see also the b3PluginManager.cpp for details.


>> ray tests in pybullet?
This is also supposed to happen in a plugin. At the moment, the collisionFilterPlugin only implement a callback if a collision should happen. I need to check if rayTests apply the same filter, we can improve it if not.

>>3. I have been using collision callbacks

This is also a good candidate to write in a C plugin.

Just give PyBullet a try, and dig in the code.

maiklof
Posts: 17
Joined: Wed Nov 20, 2013 10:01 am

Re: Moving from Bullet 2 to Pybullet

Post by maiklof » Sat Oct 06, 2018 7:23 pm

Thanks for your answer Erwin!
I will definitely give pybullet a try and see if I can learn how to program a plugin. Do you have any documentation or should I check the source code and figure it out?
As I wrote, I will be happy to contribute or help if possible. I leave the link to my old project (I am working on a new platform) so you know which kind of simulation I do: www.simumatik.com.
Thanks, you are great!
Mikel

maiklof
Posts: 17
Joined: Wed Nov 20, 2013 10:01 am

Re: Moving from Bullet 2 to Pybullet

Post by maiklof » Thu Mar 07, 2019 8:49 am

Hi Erwin!
I have been using pybullet for some days now and I would like to start implementing the plugins we discussed above:
- Motion states.
- Collision filters for ray tests.
- Using Custom materials.
Can you help me to get started? Any document that explains how to introduce new plugins?
Thanks in advance!

Mikel

User avatar
Erwin Coumans
Site Admin
Posts: 4184
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Moving from Bullet 2 to Pybullet

Post by Erwin Coumans » Sat Mar 09, 2019 1:18 am

>> The visualization will be done using a 3D engine.
>> - Motion states.

Is your 3d engine also Python or C++?
How do you want to synchronize the graphics transforms? Through C++ / shared memory, or using Python?

If it happens in Python, we could add a method that receives only the updated objects. Then no plugin is needed.

For the ray cast, we can add a flag to also apply collision filters. It has to be implemented in PhysicsServerCommandProcessor.
Same for custom materials. PyBullet allows custom properties, that could be useful.

maiklof
Posts: 17
Joined: Wed Nov 20, 2013 10:01 am

Re: Moving from Bullet 2 to Pybullet

Post by maiklof » Sat Mar 09, 2019 12:42 pm

Hi Erwin,
Right now I would prefer to use Python to interface the physics engine. If we see any performance issue we could move to C++. I think it is great to have the option to run the physics server in another computer, so I guess shared memory/C++ is not an option...

- Motion States: A method that we could call after the simulation step to receive the updated objects and their transform (pos, quat) would be great. Right now we are crawling all the objects after some steps instead (60 fps).

- Custom materials: Before I had my own python wrapper that I could share with you if you want. This is what I was doing:
1. Add in the constructor of btDiscreteDynamicsWorld():
gContactAddedCallback = CustomMaterialCombinerCallback;

2. To simulate conveyor like surfaces, I linked a custom material to some user pointers:

Code: Select all

class  btCustomMaterial{
	// Custom material class. If attached as userpointer to a CollisionObject (body), it makes possible to modify the contact point behaviour
public:

	btVector3 	m_lateralFrictionDir1; //Lateral friction 1
	btScalar	m_contactMotion1;  //Motion speed for lateral friction 1
	int	m_useCounter1; //For debuging
	int	m_useCounter2; //For debuging

	btCustomMaterial()
	{
		m_lateralFrictionDir1.setValue(0.f,0.f,0.f);
		m_contactMotion1 = 0.f;
		m_useCounter1 = 0; 
		m_useCounter2 = 0;
	}

	void setMotionSpeed (btScalar speed)
	{
		m_contactMotion1 = speed;
	}

	void setMotion (btVector3& direction, btScalar speed)
	{
		m_lateralFrictionDir1 = direction;
		m_contactMotion1 = speed;
	}
	
	int getUse1()
	{
		return m_useCounter1;
	}
	
	int getUse2()
	{
		return m_useCounter2;
	}

};
3. Finally, this was the implementation of the callback

Code: Select all

static bool CustomMaterialCombinerCallback(btManifoldPoint& cp,	const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
{
	// Find material to modify cp
	btCustomMaterial* material = NULL;
	// object with custom material
	const btCollisionObject* obj;

	// Check if Object 0 has custom material (Usually this is executed)
	obj = colObj0Wrap->getCollisionObject();
	material = (btCustomMaterial*) obj->getUserPointer();
	if (material != NULL)
	{
		// Motion Friction Speed
		cp.m_lateralFrictionInitialized = true;
		// Motion Friction Direction
		cp.m_lateralFrictionDir1 = quatRotate(obj->getWorldTransform().getRotation(), material->m_lateralFrictionDir1);
		cp.m_contactMotion1 = - material->m_contactMotion1;
		cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB);
		material->m_useCounter1 ++;
		return true;
	}
	
	// If not, has Object 1 custom material?
	obj = colObj1Wrap->getCollisionObject();
	material = (btCustomMaterial*) obj->getUserPointer();
	if (material != NULL)
	{
		cp.m_lateralFrictionInitialized = true;
		// Motion Friction Direction
		cp.m_lateralFrictionDir1 = quatRotate(obj->getWorldTransform().getRotation(), material->m_lateralFrictionDir1);
		cp.m_contactMotion1 = material->m_contactMotion1;
		cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB); //is it ok?
		material->m_useCounter2 ++;
		return true;
	}
    return false;
}
- RayTests: If both the simple and batch could have the collision flag for each test would be great. In addition, I had some extra RayTest methods to get not just if a collision happens but even distance (simulates a laser sensor) or collided object index, useful to simulate for example an RFID antenna and then get user data (RFID data) from the collided object.

Some more code that shows that:

Code: Select all

static bool btRayTestHit(btCollisionWorld* world, const btVector3& rayFrom, const btVector3& rayTo, short int collisionmask, short int collideswithmask)
{
	// Start and End are vectors
	btCollisionWorld::ClosestRayResultCallback RayCallback(rayFrom, rayTo);
	// Perform raycast
	world->rayTest(rayFrom, rayTo, RayCallback);
	// Test results
	if (RayCallback.hasHit())
	{
		// Check collision filter
		return (RayCallback.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup & collideswithmask) && (collisionmask & RayCallback.m_collisionObject->getBroadphaseHandle()->m_collisionFilterMask);
	}
	// None hit
	return false;
}

static float btRayTestDist(btCollisionWorld* world, const btVector3& rayFrom, const btVector3& rayTo, short int collisionmask, short int collideswithmask)
{
	// Start and End are vectors
	btCollisionWorld::ClosestRayResultCallback RayCallback(rayFrom, rayTo);
	// Perform raycast
	world->rayTest(rayFrom, rayTo, RayCallback);
	// Test results
	if (RayCallback.hasHit())
	{
		// Check collision filter
		if ((RayCallback.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup & collideswithmask) && (collisionmask & RayCallback.m_collisionObject->getBroadphaseHandle()->m_collisionFilterMask))
		{
			return rayFrom.distance(RayCallback.m_hitPointWorld);
		}
	}
	// None hit
	return -1.f;
}

static int btRayTestObjIndex(btCollisionWorld* world, const btVector3& rayFrom, const btVector3& rayTo, short int collisionmask, short int collideswithmask)
{
	// Start and End are vectors
	btCollisionWorld::ClosestRayResultCallback RayCallback(rayFrom, rayTo);
	// Perform raycast
	world->rayTest(rayFrom, rayTo, RayCallback);
	// Test results
	if (RayCallback.hasHit())
	{
		// Check collision filter
		if ((RayCallback.m_collisionObject->getBroadphaseHandle()->m_collisionFilterGroup & collideswithmask) && (collisionmask & RayCallback.m_collisionObject->getBroadphaseHandle()->m_collisionFilterMask))
		{
			return RayCallback.m_collisionObject->getUserIndex();
		}
	}
	// None hit
	return -1;
}
Thank you very much for your time. Let me know how I can help you to get all this inside pybullet. Would be great to use it in the new emulation platform we are developing now.
If you want more details you can send me a private or contact by email.

//Mikel

Post Reply