From 1f7db4519e1ac2a13c46c28d7b32efcadcb2426b Mon Sep 17 00:00:00 2001 From: erwincoumans Date: Tue, 29 Aug 2017 19:14:27 -0700 Subject: [PATCH] enable motor control for maximal coordinates robots (btRigidBody, btTypedConstraint) for force, velocity, position control. --- .../ImportURDFDemo/MyMultiBodyCreator.cpp | 6 +- .../Importers/ImportURDFDemo/URDF2Bullet.cpp | 14 +- .../PhysicsClientSharedMemory.cpp | 97 ++++++- .../PhysicsServerCommandProcessor.cpp | 264 +++++++++++++++++- examples/pybullet/pybullet.c | 25 +- 5 files changed, 386 insertions(+), 20 deletions(-) diff --git a/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp b/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp index e120e9a5f..215766e1b 100644 --- a/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp +++ b/examples/Importers/ImportURDFDemo/MyMultiBodyCreator.cpp @@ -180,7 +180,7 @@ class btGeneric6DofSpring2Constraint* MyMultiBodyCreator::createFixedJoint(int u dof6->setAngularLowerLimit(btVector3(0,0,0)); dof6->setAngularUpperLimit(btVector3(0,0,0)); - + m_6DofConstraints.push_back(dof6); return dof6; } @@ -188,6 +188,10 @@ class btGeneric6DofSpring2Constraint* MyMultiBodyCreator::createFixedJoint(int u void MyMultiBodyCreator::addLinkMapping(int urdfLinkIndex, int mbLinkIndex) { + if (m_mb2urdfLink.size()<(mbLinkIndex+1)) + { + m_mb2urdfLink.resize((mbLinkIndex+1),-2); + } // m_urdf2mbLink[urdfLinkIndex] = mbLinkIndex; m_mb2urdfLink[mbLinkIndex] = urdfLinkIndex; } diff --git a/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp b/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp index ac96befea..d09eeab72 100644 --- a/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp +++ b/examples/Importers/ImportURDFDemo/URDF2Bullet.cpp @@ -378,12 +378,14 @@ void ConvertURDF2BulletInternal( { printf("Warning: joint unsupported, creating a fixed joint instead."); } + creation.addLinkMapping(urdfLinkIndex,mbLinkIndex); + if (createMultiBody) { //todo: adjust the center of mass transform and pivot axis properly cache.m_bulletMultiBody->setupFixed(mbLinkIndex, mass, localInertiaDiagonal, mbParentIndex, parentRotToThis, offsetInA.getOrigin(),-offsetInB.getOrigin()); - creation.addLinkMapping(urdfLinkIndex,mbLinkIndex); + } else { //b3Printf("Fixed joint\n"); @@ -398,13 +400,14 @@ void ConvertURDF2BulletInternal( case URDFContinuousJoint: case URDFRevoluteJoint: { + creation.addLinkMapping(urdfLinkIndex,mbLinkIndex); if (createMultiBody) { cache.m_bulletMultiBody->setupRevolute(mbLinkIndex, mass, localInertiaDiagonal, mbParentIndex, parentRotToThis, quatRotate(offsetInB.getRotation(),jointAxisInJointSpace), offsetInA.getOrigin(),//parent2joint.getOrigin(), -offsetInB.getOrigin(), disableParentCollision); - creation.addLinkMapping(urdfLinkIndex,mbLinkIndex); + if (jointType == URDFRevoluteJoint && jointLowerLimit <= jointUpperLimit) { //std::string name = u2b.getLinkName(urdfLinkIndex); //printf("create btMultiBodyJointLimitConstraint for revolute link name=%s urdf link index=%d (low=%f, up=%f)\n", name.c_str(), urdfLinkIndex, jointLowerLimit, jointUpperLimit); @@ -424,13 +427,15 @@ void ConvertURDF2BulletInternal( } case URDFPrismaticJoint: { + creation.addLinkMapping(urdfLinkIndex,mbLinkIndex); + if (createMultiBody) { cache.m_bulletMultiBody->setupPrismatic(mbLinkIndex, mass, localInertiaDiagonal, mbParentIndex, parentRotToThis, quatRotate(offsetInB.getRotation(),jointAxisInJointSpace), offsetInA.getOrigin(),//parent2joint.getOrigin(), -offsetInB.getOrigin(), disableParentCollision); - creation.addLinkMapping(urdfLinkIndex,mbLinkIndex); + if (jointLowerLimit <= jointUpperLimit) { //std::string name = u2b.getLinkName(urdfLinkIndex); @@ -444,7 +449,8 @@ void ConvertURDF2BulletInternal( } else { - btGeneric6DofSpring2Constraint* dof6 = creation.createPrismaticJoint(urdfLinkIndex,*linkRigidBody, *parentRigidBody, offsetInB, offsetInA,jointAxisInJointSpace,jointLowerLimit,jointUpperLimit); + //btGeneric6DofSpring2Constraint* dof6 = creation.createPrismaticJoint(urdfLinkIndex,*linkRigidBody, *parentRigidBody, offsetInB, offsetInA,jointAxisInJointSpace,jointLowerLimit,jointUpperLimit); + btGeneric6DofSpring2Constraint* dof6 = creation.createPrismaticJoint(urdfLinkIndex,*parentRigidBody, *linkRigidBody, offsetInA, offsetInB,jointAxisInJointSpace,jointLowerLimit,jointUpperLimit); if (enableConstraints) diff --git a/examples/SharedMemory/PhysicsClientSharedMemory.cpp b/examples/SharedMemory/PhysicsClientSharedMemory.cpp index 5e297d28f..45bd86b93 100644 --- a/examples/SharedMemory/PhysicsClientSharedMemory.cpp +++ b/examples/SharedMemory/PhysicsClientSharedMemory.cpp @@ -278,7 +278,10 @@ void PhysicsClientSharedMemory::disconnectSharedMemory() { } -bool PhysicsClientSharedMemory::isConnected() const { return m_data->m_isConnected; } +bool PhysicsClientSharedMemory::isConnected() const +{ + return m_data->m_isConnected && (m_data->m_testBlock1->m_magicId == SHARED_MEMORY_MAGIC_NUMBER); +} bool PhysicsClientSharedMemory::connect() { /// server always has to create and initialize shared memory @@ -386,6 +389,72 @@ void PhysicsClientSharedMemory::processBodyJointInfo(int bodyUniqueId, const Sha } } +template void addJointInfoFromConstraint(int linkIndex, const T* con, U* bodyJoints, bool verboseOutput) +{ + b3JointInfo info; + info.m_jointName = 0; + info.m_linkName = 0; + info.m_flags = 0; + info.m_jointIndex = linkIndex; + info.m_qIndex = linkIndex+6; + info.m_uIndex = linkIndex+6; + //derive type from limits + + if (con->m_typeConstraintData.m_name) + { + info.m_jointName = strDup(con->m_typeConstraintData.m_name); + //info.m_linkName = strDup(con->m_typeConstraintData.m_name); + } + + btVector3 linearLowerLimit(con->m_linearLowerLimit.m_floats[0],con->m_linearLowerLimit.m_floats[1],con->m_linearLowerLimit.m_floats[2]); + btVector3 linearUpperLimit(con->m_linearUpperLimit.m_floats[0],con->m_linearUpperLimit.m_floats[1],con->m_linearUpperLimit.m_floats[2]); + btVector3 angularLowerLimit(con->m_angularLowerLimit.m_floats[0],con->m_angularLowerLimit.m_floats[1],con->m_angularLowerLimit.m_floats[2]); + btVector3 angularUpperLimit(con->m_angularUpperLimit.m_floats[0],con->m_angularUpperLimit.m_floats[1],con->m_angularUpperLimit.m_floats[2]); + + //very simple, rudimentary extraction of constaint type, from limits + info.m_jointType = eFixedType; + info.m_jointDamping = 0;//mb->m_links[link].m_jointDamping; + info.m_jointFriction = 0;//mb->m_links[link].m_jointFriction; + info.m_jointLowerLimit = 0;//mb->m_links[link].m_jointLowerLimit; + info.m_jointUpperLimit = 0;//mb->m_links[link].m_jointUpperLimit; + info.m_jointMaxForce = 0;//mb->m_links[link].m_jointMaxForce; + info.m_jointMaxVelocity = 0;//mb->m_links[link].m_jointMaxVelocity; + + + if (linearLowerLimit.isZero() && linearUpperLimit.isZero() && angularLowerLimit.isZero() && angularUpperLimit.isZero()) + { + info.m_jointType = eFixedType; + } else + { + if (linearLowerLimit.isZero() && linearUpperLimit.isZero()) + { + info.m_jointType = eRevoluteType; + btVector3 limitRange = angularLowerLimit.absolute()+angularUpperLimit.absolute(); + int limitAxis = limitRange.maxAxis(); + info.m_jointLowerLimit = angularLowerLimit[limitAxis]; + info.m_jointUpperLimit = angularUpperLimit[limitAxis]; + } else + { + info.m_jointType = ePrismaticType; + btVector3 limitRange = linearLowerLimit.absolute()+linearUpperLimit.absolute(); + int limitAxis = limitRange.maxAxis(); + info.m_jointLowerLimit = linearLowerLimit[limitAxis]; + info.m_jointUpperLimit = linearUpperLimit[limitAxis]; + } + } + + //if (mb->m_links[link].m_linkName) { + + + if ((info.m_jointType == eRevoluteType) || + (info.m_jointType == ePrismaticType)) { + info.m_flags |= JOINT_HAS_MOTORIZED_POWER; + } + bodyJoints->m_jointInfo.push_back(info); + + +}; + const SharedMemoryStatus* PhysicsClientSharedMemory::processServerStatus() { // SharedMemoryStatus* stat = 0; @@ -460,7 +529,8 @@ const SharedMemoryStatus* PhysicsClientSharedMemory::processServerStatus() { b3Printf("Server loading the URDF OK\n"); } - if (serverCmd.m_numDataStreamBytes > 0) { + if (serverCmd.m_numDataStreamBytes > 0) + { bParse::btBulletFile bf( this->m_data->m_testBlock1->m_bulletStreamDataServerToClientRefactor, serverCmd.m_numDataStreamBytes); @@ -472,12 +542,31 @@ const SharedMemoryStatus* PhysicsClientSharedMemory::processServerStatus() { m_data->m_bodyJointMap.insert(bodyUniqueId,bodyJoints); bodyJoints->m_bodyName = serverCmd.m_dataStreamArguments.m_bodyName; - for (int i = 0; i < bf.m_multiBodies.size(); i++) { + for (int i = 0; i < bf.m_constraints.size(); i++) + { + int flag = bf.getFlags(); + + if ((flag & bParse::FD_DOUBLE_PRECISION) != 0) + { + Bullet::btGeneric6DofSpring2ConstraintDoubleData2* con = + (Bullet::btGeneric6DofSpring2ConstraintDoubleData2*) bf.m_constraints[i]; + addJointInfoFromConstraint(i,con,bodyJoints,m_data->m_verboseOutput); + } else + { + Bullet::btGeneric6DofSpring2ConstraintData* con = + (Bullet::btGeneric6DofSpring2ConstraintData*) bf.m_constraints[i]; + addJointInfoFromConstraint(i,con,bodyJoints,m_data->m_verboseOutput); + } + } + + for (int i = 0; i < bf.m_multiBodies.size(); i++) + { int flag = bf.getFlags(); - if ((flag & bParse::FD_DOUBLE_PRECISION) != 0) { + if ((flag & bParse::FD_DOUBLE_PRECISION) != 0) + { Bullet::btMultiBodyDoubleData* mb = (Bullet::btMultiBodyDoubleData*)bf.m_multiBodies[i]; if (mb->m_baseName) diff --git a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp index 3168c13af..d926e6b36 100644 --- a/examples/SharedMemory/PhysicsServerCommandProcessor.cpp +++ b/examples/SharedMemory/PhysicsServerCommandProcessor.cpp @@ -76,6 +76,8 @@ btScalar gRhsClamp = 1.f; struct UrdfLinkNameMapUtil { btMultiBody* m_mb; + btAlignedObjectArray m_rigidBodyJoints; + btDefaultSerializer* m_memSerializer; UrdfLinkNameMapUtil():m_mb(0),m_memSerializer(0) @@ -148,6 +150,10 @@ struct InternalBodyData btTransform m_rootLocalInertialFrame; btAlignedObjectArray m_linkLocalInertialFrames; + btAlignedObjectArray m_rigidBodyJoints; + btAlignedObjectArray m_rigidBodyJointNames; + btAlignedObjectArray m_rigidBodyLinkNames; + #ifdef B3_ENABLE_TINY_AUDIO b3HashMap m_audioSources; #endif //B3_ENABLE_TINY_AUDIO @@ -165,6 +171,9 @@ struct InternalBodyData m_bodyName=""; m_rootLocalInertialFrame.setIdentity(); m_linkLocalInertialFrames.clear(); + m_rigidBodyJoints.clear(); + m_rigidBodyJointNames.clear(); + m_rigidBodyLinkNames.clear(); } }; @@ -2235,6 +2244,32 @@ bool PhysicsServerCommandProcessor::processImportedObjects(const char* fileName, bodyHandle->m_rigidBody = rb; rb->setUserIndex2(bodyUniqueId); m_data->m_sdfRecentLoadedBodies.push_back(bodyUniqueId); + + std::string* baseName = new std::string(u2b.getLinkName(u2b.getRootLinkIndex())); + m_data->m_strings.push_back(baseName); + bodyHandle->m_bodyName = *baseName; + + int numJoints = creation.getNum6DofConstraints(); + bodyHandle->m_rigidBodyJoints.reserve(numJoints); + bodyHandle->m_rigidBodyJointNames.reserve(numJoints); + bodyHandle->m_rigidBodyLinkNames.reserve(numJoints); + for (int i=0;im_strings.push_back(linkName); + + std::string* jointName = new std::string(u2b.getJointName(urdfLinkIndex).c_str()); + m_data->m_strings.push_back(jointName); + + bodyHandle->m_rigidBodyJointNames.push_back(*jointName); + bodyHandle->m_rigidBodyLinkNames.push_back(*linkName); + + bodyHandle->m_rigidBodyJoints.push_back(con); + } } } @@ -2594,7 +2629,64 @@ int PhysicsServerCommandProcessor::createBodyInfoStream(int bodyUniqueId, char* util->m_memSerializer->finalizeChunk(chunk,structType,BT_MULTIBODY_CODE,mb); streamSizeInBytes = util->m_memSerializer->getCurrentBufferSize(); - } + } else + { + btRigidBody* rb = bodyHandle? bodyHandle->m_rigidBody :0; + if (rb) + { + UrdfLinkNameMapUtil* util = new UrdfLinkNameMapUtil; + m_data->m_urdfLinkNameMapper.push_back(util); + util->m_memSerializer = new btDefaultSerializer(bufferSizeInBytes ,(unsigned char*)bufferServerToClient); + util->m_memSerializer->startSerialization(); + util->m_memSerializer->registerNameForPointer(bodyHandle->m_rigidBody,bodyHandle->m_bodyName.c_str()); + //disable serialization of the collision objects (they are too big, and the client likely doesn't need them); + for (int i=0;im_rigidBodyJoints.size();i++) + { + const btGeneric6DofSpring2Constraint* con = bodyHandle->m_rigidBodyJoints.at(i); +#if 0 + const btRigidBody& bodyA = con->getRigidBodyA(); + const btRigidBody& bodyB = con->getRigidBodyB(); + int len = bodyA.calculateSerializeBufferSize(); + btChunk* chunk = util->m_memSerializer->allocate(len,1); + const char* structType = bodyA.serialize(chunk->m_oldPtr, util->m_memSerializer); + util->m_memSerializer->finalizeChunk(chunk,structType,BT_RIGIDBODY_CODE,(void*)&bodyA); +#endif + util->m_memSerializer->registerNameForPointer(con,bodyHandle->m_rigidBodyJointNames[i].c_str()); + util->m_memSerializer->registerNameForPointer(&con->getRigidBodyB(),bodyHandle->m_rigidBodyLinkNames[i].c_str()); + + const btRigidBody& bodyA = con->getRigidBodyA(); + + int len = con->calculateSerializeBufferSize(); + btChunk* chunk = util->m_memSerializer->allocate(len,1); + const char* structType = con->serialize(chunk->m_oldPtr, util->m_memSerializer); + util->m_memSerializer->finalizeChunk(chunk,structType,BT_CONSTRAINT_CODE,(void*)con); + } + + streamSizeInBytes = util->m_memSerializer->getCurrentBufferSize(); +#if 0 + util->m_memSerializer->m_skipPointers.insert(mb->getBaseCollider(),0); + if (mb->getBaseName()) + { + util->m_memSerializer->registerNameForPointer(mb->getBaseName(),mb->getBaseName()); + } + bodyHandle->m_linkLocalInertialFrames.reserve(mb->getNumLinks()); + for (int i=0;igetNumLinks();i++) + { + //disable serialization of the collision objects + util->m_memSerializer->m_skipPointers.insert(mb->getLink(i).m_collider,0); + util->m_memSerializer->registerNameForPointer(mb->getLink(i).m_linkName,mb->getLink(i).m_linkName); + util->m_memSerializer->registerNameForPointer(mb->getLink(i).m_jointName,mb->getLink(i).m_jointName); + } + util->m_memSerializer->registerNameForPointer(mb->getBaseName(),mb->getBaseName()); + int len = mb->calculateSerializeBufferSize(); + btChunk* chunk = util->m_memSerializer->allocate(len,1); + const char* structType = mb->serialize(chunk->m_oldPtr, util->m_memSerializer); + util->m_memSerializer->finalizeChunk(chunk,structType,BT_MULTIBODY_CODE,mb); + streamSizeInBytes = util->m_memSerializer->getCurrentBufferSize(); +#endif + } + } + return streamSizeInBytes; } @@ -4269,7 +4361,7 @@ bool PhysicsServerCommandProcessor::processCommand(const struct SharedMemoryComm switch (clientCmd.m_sendDesiredStateCommandArgument.m_controlMode) { - case CONTROL_MODE_TORQUE: + case CONTROL_MODE_TORQUE: { if (m_data->m_verboseOutput) { @@ -4432,12 +4524,171 @@ bool PhysicsServerCommandProcessor::processCommand(const struct SharedMemoryComm break; } default: - { - b3Warning("m_controlMode not implemented yet"); - break; - } + { + b3Warning("m_controlMode not implemented yet"); + break; + } } + } else + { + //support for non-btMultiBody, such as btRigidBody + + if (body && body->m_rigidBody) + { + btRigidBody* rb = body->m_rigidBody; + btAssert(rb); + + //switch (clientCmd.m_sendDesiredStateCommandArgument.m_controlMode) + { + //case CONTROL_MODE_TORQUE: + { + if (m_data->m_verboseOutput) + { + b3Printf("Using CONTROL_MODE_TORQUE"); + } + // mb->clearForcesAndTorques(); + int torqueIndex = 6; + //if ((clientCmd.m_updateFlags&SIM_DESIRED_STATE_HAS_MAX_FORCE)!=0) + { + for (int link=0;linkm_rigidBodyJoints.size();link++) + { + btGeneric6DofSpring2Constraint* con = body->m_rigidBodyJoints[link]; + + btVector3 linearLowerLimit; + btVector3 linearUpperLimit; + btVector3 angularLowerLimit; + btVector3 angularUpperLimit; + + + //for (int dof=0;dofgetLink(link).m_dofCount;dof++) + { + double torque = 0.f; + //if ((clientCmd.m_sendDesiredStateCommandArgument.m_hasDesiredStateFlags[torqueIndex]&SIM_DESIRED_STATE_HAS_MAX_FORCE)!=0) + { + torque = clientCmd.m_sendDesiredStateCommandArgument.m_desiredStateForceTorque[torqueIndex]; + btScalar qdotTarget = clientCmd.m_sendDesiredStateCommandArgument.m_desiredStateQdot[torqueIndex]; + btScalar qTarget = clientCmd.m_sendDesiredStateCommandArgument.m_desiredStateQ[torqueIndex]; + + + con->getLinearLowerLimit(linearLowerLimit); + con->getLinearUpperLimit(linearUpperLimit); + con->getAngularLowerLimit(angularLowerLimit); + con->getAngularUpperLimit(angularUpperLimit); + + if (linearLowerLimit.isZero() && linearUpperLimit.isZero() && angularLowerLimit.isZero() && angularUpperLimit.isZero()) + { + //fixed, don't do anything + } else + { + con->calculateTransforms(); + + if (linearLowerLimit.isZero() && linearUpperLimit.isZero()) + { + //eRevoluteType; + btVector3 limitRange = angularLowerLimit.absolute()+angularUpperLimit.absolute(); + int limitAxis = limitRange.maxAxis(); + const btTransform& transA = con->getCalculatedTransformA(); + const btTransform& transB = con->getCalculatedTransformB(); + btVector3 axisA = transA.getBasis().getColumn(limitAxis); + btVector3 axisB = transB.getBasis().getColumn(limitAxis); + + switch (clientCmd.m_sendDesiredStateCommandArgument.m_controlMode) + { + case CONTROL_MODE_TORQUE: + { + con->getRigidBodyA().applyTorque(torque*axisA); + con->getRigidBodyB().applyTorque(-torque*axisB); + break; + } + case CONTROL_MODE_VELOCITY: + { + con->enableMotor(3+limitAxis,true); + con->setTargetVelocity(3+limitAxis, qdotTarget); + //this is max motor force impulse + btScalar torqueImpulse = torque*m_data->m_dynamicsWorld->getSolverInfo().m_timeStep; + con->setMaxMotorForce(3+limitAxis,torqueImpulse); + break; + } + case CONTROL_MODE_POSITION_VELOCITY_PD: + { + con->setServo(3+limitAxis,true); + con->setServoTarget(3+limitAxis,qTarget); + //next one is the maximum velocity to reach target position. + //the maximum velocity is limited by maxMotorForce + con->setTargetVelocity(3+limitAxis, 100); + //this is max motor force impulse + btScalar torqueImpulse = torque*m_data->m_dynamicsWorld->getSolverInfo().m_timeStep; + con->setMaxMotorForce(3+limitAxis,torqueImpulse); + con->enableMotor(3+limitAxis,true); + break; + } + default: + { + } + }; + + + + + } else + { + //ePrismaticType; @todo + btVector3 limitRange = linearLowerLimit.absolute()+linearUpperLimit.absolute(); + int limitAxis = limitRange.maxAxis(); + + const btTransform& transA = con->getCalculatedTransformA(); + const btTransform& transB = con->getCalculatedTransformB(); + btVector3 axisA = transA.getBasis().getColumn(limitAxis); + btVector3 axisB = transB.getBasis().getColumn(limitAxis); + + switch (clientCmd.m_sendDesiredStateCommandArgument.m_controlMode) + { + case CONTROL_MODE_TORQUE: + { + con->getRigidBodyA().applyForce(-torque*axisA,btVector3(0,0,0)); + con->getRigidBodyB().applyForce(torque*axisB,btVector3(0,0,0)); + break; + } + case CONTROL_MODE_VELOCITY: + { + con->enableMotor(limitAxis,true); + con->setTargetVelocity(limitAxis, -qdotTarget); + //this is max motor force impulse + btScalar torqueImpulse = torque*m_data->m_dynamicsWorld->getSolverInfo().m_timeStep; + con->setMaxMotorForce(limitAxis,torqueImpulse); + break; + } + case CONTROL_MODE_POSITION_VELOCITY_PD: + { + con->setServo(limitAxis,true); + con->setServoTarget(limitAxis,qTarget); + //next one is the maximum velocity to reach target position. + //the maximum velocity is limited by maxMotorForce + con->setTargetVelocity(limitAxis, 100); + //this is max motor force impulse + btScalar torqueImpulse = torque*m_data->m_dynamicsWorld->getSolverInfo().m_timeStep; + con->setMaxMotorForce(limitAxis,torqueImpulse); + con->enableMotor(limitAxis,true); + break; + } + default: + { + } + }; + + } + } + }//fi + torqueIndex++; + } + } + }//fi + //break; + } + + } + } //if (body && body->m_rigidBody) } serverStatusOut.m_type = CMD_DESIRED_STATE_RECEIVED_COMPLETED; @@ -6937,7 +7188,6 @@ bool PhysicsServerCommandProcessor::processCommand(const struct SharedMemoryComm int uid = m_data->m_visualConverter.loadTextureFile(relativeFileName); if(uid>=0) { - int m_tinyRendererTextureId; texH->m_tinyRendererTextureId = uid; } diff --git a/examples/pybullet/pybullet.c b/examples/pybullet/pybullet.c index 18b184393..af356f4bb 100644 --- a/examples/pybullet/pybullet.c +++ b/examples/pybullet/pybullet.c @@ -2827,8 +2827,17 @@ static PyObject* pybullet_getJointInfo(PyObject* self, PyObject* args, PyObject* // info.m_jointDamping, // info.m_jointFriction); PyTuple_SetItem(pyListJointInfo, 0, PyInt_FromLong(info.m_jointIndex)); - PyTuple_SetItem(pyListJointInfo, 1, - PyString_FromString(info.m_jointName)); + + if (info.m_jointName) + { + PyTuple_SetItem(pyListJointInfo, 1, + PyString_FromString(info.m_jointName)); + } else + { + PyTuple_SetItem(pyListJointInfo, 1, + PyString_FromString("not available")); + } + PyTuple_SetItem(pyListJointInfo, 2, PyInt_FromLong(info.m_jointType)); PyTuple_SetItem(pyListJointInfo, 3, PyInt_FromLong(info.m_qIndex)); PyTuple_SetItem(pyListJointInfo, 4, PyInt_FromLong(info.m_uIndex)); @@ -2845,8 +2854,16 @@ static PyObject* pybullet_getJointInfo(PyObject* self, PyObject* args, PyObject* PyFloat_FromDouble(info.m_jointMaxForce)); PyTuple_SetItem(pyListJointInfo, 11, PyFloat_FromDouble(info.m_jointMaxVelocity)); - PyTuple_SetItem(pyListJointInfo, 12, - PyString_FromString(info.m_linkName)); + if (info.m_linkName) + { + + PyTuple_SetItem(pyListJointInfo, 12, + PyString_FromString(info.m_linkName)); + } else + { + PyTuple_SetItem(pyListJointInfo, 12, + PyString_FromString("not available")); + } return pyListJointInfo; }