• Bullet 学习笔记之 Bullet User Manual


    Bullet User Manual 第三部分,参见 bullet3/docs/Bullet_User_Manual.pdf

    总体上绍了 Bullet 物理引擎的整体架构、仿真流程、基础数据结构等。


    Bullet 物理引擎的主要功能为 碰撞检测、碰撞/约束求解、物体状态的更新。
    The main task of a physics engine is to perform collision detection, resolve collisions and other constraints, and provide the updated world transform1 for all the objects.


    Bullet 物理引擎的主要组件及其组织结构如下图所示:


    Rigid Body Physics Pipeline

    (这一部分非常重要,可以帮助理解仿真计算流程,也便于后续基于 Bullet 物理引擎进行软体机器人仿真编程)

    Pipeline 如下图所示:

    计算流程

    btDiscreteDynamicsWorld 中,所有的 Pipeline 步骤均包含在函数 stepSimulation 中。结合 btDiscreteDynamicsWorld::stepSimulation(t) 中的代码,各个仿真流程大致对应的代码有:

    saveKinematicState(fixedTimeStep * clampedSimulationSteps);
    
    applyGravity();
    
    for (int i = 0; i < clampedSimulationSteps; i++)
    {
    	internalSingleStepSimulation(fixedTimeStep);
    	synchronizeMotionStates();
    }
    
    clearForces();
    

    其中,各个步骤分别为:

    • save Kinematic State
      代码为: saveKinematicState(fixedTimeStep * clampedSimulationSteps); 这部分代码,其核心部分就是遍历 dynamicWorld 中的非静态、且非睡眠的刚体,执行body->saveKinematicState(timeStep);

    • Apply Gravity
      代码为:applyGravity(); 具体来说,就是遍历 dynamicWorld 中的非静态刚体,执行 body->applyGravity();

    • internal Single Step Simulation
      代码为:internalSingleStepSimulation(fixedTimeStep); 其中的具体内容非常丰富,有:

    BT_PROFILE("internalSingleStepSimulation");
    
    if (0 != m_internalPreTickCallback)
    {
    	(*m_internalPreTickCallback)(this, timeStep);
    }
    
    ///apply gravity, predict motion
    predictUnconstraintMotion(timeStep);
    
    btDispatcherInfo& dispatchInfo = getDispatchInfo();
    
    dispatchInfo.m_timeStep = timeStep;
    dispatchInfo.m_stepCount = 0;
    dispatchInfo.m_debugDraw = getDebugDrawer();
    
    createPredictiveContacts(timeStep);
    
    ///perform collision detection
    performDiscreteCollisionDetection();
    
    calculateSimulationIslands();
    
    getSolverInfo().m_timeStep = timeStep;
    
    ///solve contact and other joint constraints
    solveConstraints(getSolverInfo());
    
    ///CallbackTriggers();
    
    ///integrate transforms
    
    integrateTransforms(timeStep);
    
    ///update vehicle simulation
    updateActions(timeStep);
    
    updateActivationState(timeStep);
    
    if (0 != m_internalTickCallback)
    {
    	(*m_internalTickCallback)(this, timeStep);
    }
    

    以上内容可以说是几乎囊括了 Pipeline 的所有内容。这个会在后面详细的再分析。

    • synchronize Motion States
      代码为:synchronizeMotionStates(); 这部分代码,其核心内容,是遍历 dynamic World 中的非静态、非Kinematic的刚体/碰撞对象,执行 body->getMotionState()->setWorldTransform(interpolatedTransform); 看起来是更新世界坐标系变化对刚体的影响。

    • clearForces();
      代码为:clearForces(); 这部分代码,就是 body->clearForces();


    结合 Pipeline 的流程图,各部分对应的代码应该是:

    • Apply Gravity
    body->saveKinematicState(timeStep);
    
    body->applyGravity();
    
    • Predict Tranforms
    ///apply gravity, predict motion
    predictUnconstraintMotion(timeStep);
    

    //don't integrate/update velocities here, it happens in the constraint solver
    
    body->applyDamping(timeStep);
    
    body->predictIntegratedTransform(timeStep, body->getInterpolationWorldTransform());
    
    • Cpmpute AABBs
    btCollisionShape::calculateTemporalAabb(...);
    

    可能是在这部分代码中实现的,很小的一部分代码,有点被隐藏起来了。

    • Detect Paris
      应该是在这部分代码中,即btDiscreteDynamicsWorld::createPredictiveContacts(timeStep); 具体来说,为:
    releasePredictiveContacts();
    
    createPredictiveContactsInternal(&m_nonStaticRigidBodies[0], m_nonStaticRigidBodies.size(), timeStep);
    

    包括了清空 m_predictiveManifolds

    for (int i = 0; i < m_predictiveManifolds.size(); i++)
    {
    	btPersistentManifold* manifold = m_predictiveManifolds[i];
    	this->m_dispatcher1->releaseManifold(manifold);
    }
    m_predictiveManifolds.clear();
    

    以及重新计算可能的 manifold

    void btDiscreteDynamicsWorld::createPredictiveContactsInternal(btRigidBody** bodies, int numBodies, btScalar timeStep)
    {
    	btTransform predictedTrans;
    	for (int i = 0; i < numBodies; i++)
    	{
    		btRigidBody* body = bodies[i];
    		body->setHitFraction(1.f);
    
    		if (body->isActive() && (!body->isStaticOrKinematicObject()))
    		{
    			body->predictIntegratedTransform(timeStep, predictedTrans);
    
    			btScalar squareMotion = (predictedTrans.getOrigin() - body->getWorldTransform().getOrigin()).length2();
    
    			if (getDispatchInfo().m_useContinuous && body->getCcdSquareMotionThreshold() && body->getCcdSquareMotionThreshold() < squareMotion)
    			{
    				BT_PROFILE("predictive convexSweepTest");
    				if (body->getCollisionShape()->isConvex())
    				{
    					gNumClampedCcdMotions++;
    #ifdef PREDICTIVE_CONTACT_USE_STATIC_ONLY
    					class StaticOnlyCallback : public btClosestNotMeConvexResultCallback
    					{
    					public:
    						StaticOnlyCallback(btCollisionObject* me, const btVector3& fromA, const btVector3& toA, btOverlappingPairCache* pairCache, btDispatcher* dispatcher) : btClosestNotMeConvexResultCallback(me, fromA, toA, pairCache, dispatcher)
    						{
    						}
    
    						virtual bool needsCollision(btBroadphaseProxy* proxy0) const
    						{
    							btCollisionObject* otherObj = (btCollisionObject*)proxy0->m_clientObject;
    							if (!otherObj->isStaticOrKinematicObject())
    								return false;
    							return btClosestNotMeConvexResultCallback::needsCollision(proxy0);
    						}
    					};
    
    					StaticOnlyCallback sweepResults(body, body->getWorldTransform().getOrigin(), predictedTrans.getOrigin(), getBroadphase()->getOverlappingPairCache(), getDispatcher());
    #else
    					btClosestNotMeConvexResultCallback sweepResults(body, body->getWorldTransform().getOrigin(), predictedTrans.getOrigin(), getBroadphase()->getOverlappingPairCache(), getDispatcher());
    #endif
    					//btConvexShape* convexShape = static_cast<btConvexShape*>(body->getCollisionShape());
    					btSphereShape tmpSphere(body->getCcdSweptSphereRadius());  //btConvexShape* convexShape = static_cast<btConvexShape*>(body->getCollisionShape());
    					sweepResults.m_allowedPenetration = getDispatchInfo().m_allowedCcdPenetration;
    
    					sweepResults.m_collisionFilterGroup = body->getBroadphaseProxy()->m_collisionFilterGroup;
    					sweepResults.m_collisionFilterMask = body->getBroadphaseProxy()->m_collisionFilterMask;
    					btTransform modifiedPredictedTrans = predictedTrans;
    					modifiedPredictedTrans.setBasis(body->getWorldTransform().getBasis());
    
    					convexSweepTest(&tmpSphere, body->getWorldTransform(), modifiedPredictedTrans, sweepResults);
    					if (sweepResults.hasHit() && (sweepResults.m_closestHitFraction < 1.f))
    					{
    						btVector3 distVec = (predictedTrans.getOrigin() - body->getWorldTransform().getOrigin()) * sweepResults.m_closestHitFraction;
    						btScalar distance = distVec.dot(-sweepResults.m_hitNormalWorld);
    
    						btPersistentManifold* manifold = m_dispatcher1->getNewManifold(body, sweepResults.m_hitCollisionObject);
    						btMutexLock(&m_predictiveManifoldsMutex);
    						m_predictiveManifolds.push_back(manifold);
    						btMutexUnlock(&m_predictiveManifoldsMutex);
    
    						btVector3 worldPointB = body->getWorldTransform().getOrigin() + distVec;
    						btVector3 localPointB = sweepResults.m_hitCollisionObject->getWorldTransform().inverse() * worldPointB;
    
    						btManifoldPoint newPoint(btVector3(0, 0, 0), localPointB, sweepResults.m_hitNormalWorld, distance);
    
    						bool isPredictive = true;
    						int index = manifold->addManifoldPoint(newPoint, isPredictive);
    						btManifoldPoint& pt = manifold->getContactPoint(index);
    						pt.m_combinedRestitution = 0;
    						pt.m_combinedFriction = gCalculateCombinedFrictionCallback(body, sweepResults.m_hitCollisionObject);
    						pt.m_positionWorldOnA = body->getWorldTransform().getOrigin();
    						pt.m_positionWorldOnB = worldPointB;
    					}
    				}
    			}
    		}
    	}
    }
    

    这部分代码,非常的,xx

    • Compute Contacts
    BT_PROFILE("performDiscreteCollisionDetection");
    
    btDispatcherInfo& dispatchInfo = getDispatchInfo();
    
    updateAabbs();
    
    computeOverlappingPairs();
    
    btDispatcher* dispatcher = getDispatcher();
    {
    	BT_PROFILE("dispatchAllCollisionPairs");
    	if (dispatcher)
    		dispatcher->dispatchAllCollisionPairs(m_broadphasePairCache->getOverlappingPairCache(), dispatchInfo, m_dispatcher1);
    }
    
    • Solve constraints
    calculateSimulationIslands();
    
    getSolverInfo().m_timeStep = timeStep;
    
    ///solve contact and other joint constraints
    solveConstraints(getSolverInfo());
    
    • Integrate Position
    integrateTransforms(timeStep);
    
    ///update vehicle simulation
    updateActions(timeStep);
    

    目前来说,大致清楚了 Bullet 物理引擎的工作流程。另外一项需要搞清楚的,是 Bullet 内的数据结构。比如,RigidBody 是如何存储刚体的,表面网格在哪里;得到的碰撞信息是怎么样存储的;约束求解过程具体是什么样子的。

    之后的随笔会进一步解释这些内容。

  • 相关阅读:
    为什么要使用智能指针?
    C++如何定义一个函数指针
    Python三个处理excel表格的库
    Python的一个mysql实例
    Python利用xlutils统计excel表格数据
    PHP连接数据库的方式
    利用xlutils第三方库复制excel模板
    Python自动化办公第三方库xlwt
    Python之excel第三方库xlrd和xlwt
    Python生成器
  • 原文地址:https://www.cnblogs.com/wghou09/p/12813968.html
Copyright © 2020-2023  润新知