NVIDIA-Omniverse / PhysX

NVIDIA PhysX SDK
BSD 3-Clause "New" or "Revised" License
2.62k stars 376 forks source link

Instant change in joint angles when using PxArticulationReducedCoordinate. #204

Open z80 opened 1 year ago

z80 commented 1 year ago

Hello!

I'm working with PxArticulationGeneralizedCoordinate. No forces are applied, gravity is zero, no collisions are created. Some of the joint angles instantly change after a single scene->simulate() call. I've tested several PhysX versions: PhysX 4, PhysX 5.1.3 and PhysX 5.3.0 on Windows 10 and Ubuntu Linux 18.04. The behavior is identical in all of them.

The issue happens only with a specific armature topology provided in the snippet code below and works as expected with different topologies.

All links have mass of 1kg and identity inertia matrix. All joints are either eSPHERICAL or eREVOLUTE. No control is applied. I expect all angles to remain zero. But angles 17, 18, 19 change instantly. Please see the example application attached (for PhysX 5.1.3) and the result output "image.png".

Thank you!

Library and Version

PhysX v5.1.3

Operating System

Windows 10, Ubuntu Linux 18.04.

Steps to Trigger Behavior

  1. The issue happens only with certain Armature configurations. To reproduce the issue use the attached code.
  2. Build the file, run the executable, and observe terminal output.

Code Snippet to Reproduce Behavior

#include "PxPhysicsAPI.h"
#include "PxSceneQueryDesc.h"

#include <vector>
#include <iostream>
#include <sstream>

physx::PxFilterFlags contactReportFilterShader(physx::PxFilterObjectAttributes attributes0, physx::PxFilterData filterData0, 

    physx::PxFilterObjectAttributes attributes1, physx::PxFilterData filterData1,
    physx::PxPairFlags& pairFlags, const void* constantBlock, physx::PxU32 constantBlockSize)
{
    PX_UNUSED(attributes0);
    PX_UNUSED(attributes1);
    PX_UNUSED(filterData0);
    PX_UNUSED(filterData1);
    PX_UNUSED(constantBlockSize);
    PX_UNUSED(constantBlock);

    // all initial and persisting reports for everything, with per-point data
    pairFlags = physx::PxPairFlag::eSOLVE_CONTACT | physx::PxPairFlag::eDETECT_DISCRETE_CONTACT
        |   physx::PxPairFlag::eNOTIFY_TOUCH_FOUND 
        | physx::PxPairFlag::eNOTIFY_TOUCH_PERSISTS
        | physx::PxPairFlag::eNOTIFY_CONTACT_POINTS;
    return physx::PxFilterFlag::eDEFAULT;
}

class WrapperErrorCallback: public physx::PxErrorCallback
{
public:
    WrapperErrorCallback()
    {}

    ~WrapperErrorCallback()
    {}

    void reportError( physx::PxErrorCode::Enum code, const char* message, const char * file, int line) override
    {
        std::cout << "ERROR: code=" << code << "; message=\"" << message << "\"; file=\"" << file << "\"; line=" << line << std::endl;
    }

};

int main( int argc, char * argv[] )
{
    WrapperErrorCallback           error_callback;

    physx::PxDefaultAllocator      allocator;
    physx::PxFoundation          * foundation = PxCreateFoundation( PX_PHYSICS_VERSION, allocator, error_callback );

    const int suggested_gpu_ind = PxGetSuggestedCudaDeviceOrdinal( error_callback );

    //physx::PxCudaContextManagerDesc cuda_desc;
    //cuda_desc.appGUID = "cc8d92af-333b-4a8c-81ef-8b188dca454d";
    //physx::PxCudaContextManager * gpu_manager = PxCreateCudaContextManager( *foundation, cuda_desc, nullptr );

    physx::PxPvd * pvd = PxCreatePvd( *foundation );
    physx::PxPvdTransport * transport = physx::PxDefaultPvdSocketTransportCreate( "localhost", 5425, 10 );
    pvd->connect( *transport, physx::PxPvdInstrumentationFlag::eALL );

    physx::PxPhysics * physics    = PxCreatePhysics( PX_PHYSICS_VERSION, *foundation, physx::PxTolerancesScale(), true, pvd );
    PxInitExtensions( *physics, pvd );

    physx::PxSceneDesc scene_desc( physics->getTolerancesScale() );
    scene_desc.gravity = physx::PxVec3(0.0f, 0.0f, 0.0f);
    scene_desc.solverType    = physx::PxSolverType::eTGS;
    physx::PxDefaultCpuDispatcher * dispatcher = physx::PxDefaultCpuDispatcherCreate( 1 );
    scene_desc.cpuDispatcher = dispatcher;
    scene_desc.filterShader  = physx::PxDefaultSimulationFilterShader;
    scene_desc.cudaContextManager = nullptr; //gpu_manager;
    scene_desc.sceneQueryUpdateMode = physx::PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED;
    scene_desc.filterShader            = contactReportFilterShader;
    scene_desc.simulationEventCallback = nullptr;

    physx::PxScene * scene = physics->createScene( scene_desc );
    physx::PxPvdSceneClient * pvdClient = scene->getScenePvdClient();
    if ( pvdClient )
    {
        pvdClient->setScenePvdFlag( physx::PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS,  true );
        pvdClient->setScenePvdFlag( physx::PxPvdSceneFlag::eTRANSMIT_CONTACTS,     true );
        pvdClient->setScenePvdFlag( physx::PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true );
    }

    //physx::PxCooking * cooking    = PxCreateCooking( PX_PHYSICS_VERSION, *foundation, physx::PxCookingParams(physx::PxTolerancesScale()) );

    physx::PxArticulationReducedCoordinate * articulation = physics->createArticulationReducedCoordinate();
    articulation->setSolverIterationCounts( 32 );
    physx::PxMaterial * material = physics->createMaterial(0.5f, 0.5f, 0.1f);
    {
        physx::PxVec3 g( 0.0f, 0.0f, 0.0f );
        scene->setGravity( g );
    }
    std::vector<physx::PxArticulationLink *> links;
    {
        physx::PxArticulationLink * link = articulation->createLink( NULL, physx::PxTransform(physx::PxVec3( 0.0f, 0.0f, 0.0f ) ) );
        links.push_back( link );
    }
    {
        physx::PxArticulationLink * parent = links[ 0 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationDrive( 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE ) );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, physx::PxArticulationDrive( 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE ) );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, physx::PxArticulationDrive( 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE ) );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 1 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -1.49012e-08, 0.0861911, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 1 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -0, 0, 0 );
        physx::PxQuat q( 0.0753428, 0.00502367, -0.0240119, 0.996856 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 1 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 1 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 1 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 2 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -2.98023e-08, 0.245905, 1.19209e-07 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 2 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, -0, 0 );
        physx::PxQuat q( -0.0809529, 0.00176329, 0.00690065, 0.996692 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 2 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 2 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 2 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 3 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0.000374407, 0.266924, 0.00117648 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 3 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, -0, 0 );
        physx::PxQuat q( -0.0616295, -0.143412, 0.0262464, 0.987393 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 3 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 3 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 3 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 4 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -1.19209e-07, 0.164273, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 4 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( -0.0106251, -0.152978, 0.111901, 0.981816 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 4 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 4 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 2 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx:: PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eFIX );
    }
    {
        physx::PxArticulationLink * link = links[ 5 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0.0416163, 0.19977, -0.000588417 );
        physx::PxQuat q( -0.0674198, 0.0925076, -0.690542, 0.714177 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 5 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 5 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 5 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 5 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 6 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 1.19209e-07, 0.204275, 1.19209e-07 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 6 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, -0, 0 );
        physx::PxQuat q( -0.107063, 0.132812, 0.585933, 0.7922 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 6 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 6 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 6 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eREVOLUTE );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 7 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0.256407, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 7 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -0, 0, 0 );
        physx::PxQuat q( -0.0721417, 0.216972, -0.0799273, 0.970222 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 7 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 7 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 7 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx:: PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eFIX );
    }
    {
        physx::PxArticulationLink * link = links[ 8 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0.280569, 1.19209e-07 );
        physx::PxQuat q( -0.0480927, -0.0609469, 0.0367097, 0.996306 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 8 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 8 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 8 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 2 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx:: PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eFIX );
    }
    {
        physx::PxArticulationLink * link = links[ 9 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -0.0419907, 0.199888, -0.000588059 );
        physx::PxQuat q( 0.0790464, -0.0550587, 0.629907, 0.770673 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 9 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 9 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 9 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 9 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 10 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -1.19209e-07, 0.204275, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 10 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -0, 0, 0 );
        physx::PxQuat q( -0.184415, -0.142013, -0.645716, 0.727238 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 10 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 10 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 10 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eREVOLUTE );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 11 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -2.98023e-08, 0.256407, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 11 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( 0.0541375, 0.0369368, 0.0862153, 0.994119 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 11 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 11 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 11 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx:: PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eFIX );
    }
    {
        physx::PxArticulationLink * link = links[ 12 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 5.96046e-08, 0.280569, 0 );
        physx::PxQuat q( 0.00654356, 0.0554429, -0.0283158, 0.998039 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 12 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 12 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 12 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 0 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 13 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0.105111, 0, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 13 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( -0.0140488, -0.0102969, 0.99926, 0.0342937 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 13 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 13 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 13 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eREVOLUTE );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 14 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -2.98023e-08, 0.391671, -1.19209e-07 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 14 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, -0 );
        physx::PxQuat q( 0.0271516, -0.126356, 0.0277197, 0.991226 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 14 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 14 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 14 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 15 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 1.49012e-08, 0.448679, 3.57628e-07 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 15 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( -0.118306, 0.783688, 0.578909, -0.191576 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 15 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 15 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 15 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eREVOLUTE );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 16 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0.171834, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 16 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, -0 );
        physx::PxQuat q( 0.198763, -0.00250692, -0.00806955, 0.980011 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 16 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 16 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 0 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 17 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( -0.105111, 0, 1.19209e-07 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 17 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( -0.0113399, 0.040365, 0.999072, 0.00983218 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 17 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 17 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 17 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eREVOLUTE );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 18 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0.391671, 0 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 18 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, -0 );
        physx::PxQuat q( 0.129165, -0.0162914, 0.00515619, 0.991476 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 18 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 18 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
        physx::PxArticulationLink * parent = links[ 18 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eSPHERICAL );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING1, physx::PxArticulationMotion::eFREE );
        joint->setMotion( physx::PxArticulationAxis::eSWING2, physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING1, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setDrive( physx::PxArticulationAxis::eSWING2, 0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 19 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 7.45058e-09, 0.448679, 2.38419e-07 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 19 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, -0, 0 );
        physx::PxQuat q( 0.0413229, 0.773231, 0.627274, 0.0832655 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 19 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 19 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    {
        physx::PxArticulationLink * parent = links[ 19 ];
        physx::PxArticulationLink * link = articulation->createLink( parent, physx::PxTransform(physx::PxVec3(0.0f, 1.0f, 0.0f)));
        links.push_back( link );
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        joint->setJointType( physx::PxArticulationJointType::eREVOLUTE );
        joint->setMotion( physx::PxArticulationAxis::eTWIST,  physx::PxArticulationMotion::eFREE );
        joint->setDrive( physx::PxArticulationAxis::eTWIST,  0.0, 0.0, 0.0, physx::PxArticulationDriveType::eNONE );
        joint->setFrictionCoefficient( 0.05 );
        joint->setMaxJointVelocity( 100 );
    }
    {
        physx::PxArticulationLink * link = links[ 20 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0.171834, 5.96046e-08 );
        physx::PxQuat q( 0, 0, 0, 1 );
        const physx::PxTransform t( at, q );
        joint->setParentPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 20 ];
        physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( link->getInboundJoint() );
        physx::PxVec3 at( 0, 0, 0 );
        physx::PxQuat q( 0.196391, 0.002149, 0.00442061, 0.980513 );
        const physx::PxTransform t( at, q );
        joint->setChildPose( t );
    }
    {
        physx::PxArticulationLink * link = links[ 20 ];
        link->setMass( 1 );
    }
    {
        physx::PxArticulationLink * link = links[ 20 ];
        physx::PxVec3 in( 1, 1, 1 );
        link->setMassSpaceInertiaTensor( in );
    }
    {
    }
    //{
    scene->addArticulation( *articulation );
    physx::PxArticulationCache * cache = articulation->createCache();
    //}
    {
        const physx::PxVec3 p0( -0.004165, 0.960565, -1.60235 );
        const physx::PxQuat q0( 0.0102901, 0.0677377, -0.00454115, 0.99764 );
        physx::PxTransform t( p0, q0 );
        articulation->copyInternalStateToCache( *cache, physx::PxArticulationCacheFlag::eROOT_TRANSFORM | physx::PxArticulationCacheFlag::ePOSITION );
        physx::PxArticulationRootLinkData & data = *cache->rootLinkData;
        data.transform = t;
        articulation->applyCache( *cache, physx::PxArticulationCacheFlag::eROOT_TRANSFORM | physx::PxArticulationCacheFlag::ePOSITION );
    }
    {
        const size_t qty = 21;
        for (size_t i=0; i<qty; i++ ) 
        {
            physx::PxArticulationJointReducedCoordinate * joint = static_cast<physx::PxArticulationJointReducedCoordinate*>( links[i]->getInboundJoint() );
            if ( joint != nullptr )
                joint->setFrictionCoefficient( 0 );
        }
    }
    {
        articulation->copyInternalStateToCache( *cache, physx::PxArticulationCacheFlag::ePOSITION | physx::PxArticulationCacheFlag::eVELOCITY );

        std::cout << "articulation->copyInternalStateToCache( *cache, physx::PxArticulationCacheFlag::ePOSITION | physx::PxArticulationCacheFlag::eVELOCITY );" << std::endl;
        std::ostringstream out;
        out << "cache->jointPosition[]: ";
        for ( int i=0; i<36; i++ )
            out << " " << cache->jointPosition[i];
        std::cout << out.str() << std::endl;
    }
    scene->simulate( 0.00416667 );
    std::cout << "scene->simulate( 0.00416667 );" << std::endl;
    scene->fetchResults( true );
    {
        articulation->copyInternalStateToCache( *cache, physx::PxArticulationCacheFlag::ePOSITION | physx::PxArticulationCacheFlag::eVELOCITY );

        std::cout << "articulation->copyInternalStateToCache( *cache, physx::PxArticulationCacheFlag::ePOSITION | physx::PxArticulationCacheFlag::eVELOCITY );" << std::endl;
        std::ostringstream out;
        out << "cache->jointPosition[17, 18, 19]: ";
        for ( int i=17; i<20; i++ )
            out << " " << cache->jointPosition[i];
        std::cout << out.str() << std::endl;
    }

    return 0;
}

Expected Behavior

When created, all joint angles are zero. Environment takes a single time step of dt=1.0/240.0 seconds. Due to no forces are applied, gravity is zero, no collision volumes are created, and due to all mass and inertia are assigned to be 1, it is expected that all joint angles remain exactly zero or be negligibly small.

Actual Behavior

After a single call to scene->simulate( 1.0/240.0 ) and reading articulation->copyInternalStateToCache( *cache, physx::PxArticulationCacheFlag::ePOSITION | physx::PxArticulationCacheFlag::eVELOCITY ); cache->jontPosition[17] is -5.43, cache->jontPosition[18] is 3.11, cache->jontPosition[19] is -0.6.

image

z80 commented 1 year ago

Hello NVidia,

After debugging the phenomena for a moment it feels that the problem lays in multiplying a quaternion by a very close to it inverted quaternion in two places in /physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp. It feels that due to finite float32 data type precision sometimes the result instead of being close to (x=0, y=0, z=0, w=1) happens to become close to (x=0, y=0, z=0, w=-1).

The suggestion is instead of

if (newParentToChild.w < 0.f)

do

if ( (newParentToChild.w * relativeQuat.w) < 0.f )

It looks like it fixes the problem. Please find the patch for "105.1-physx-5.3.0" tag pasted below.

diff --git a/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp b/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp
index 7e479af8..dd20926a 100644
--- a/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp
+++ b/physx/source/lowleveldynamics/src/DyFeatherstoneForwardDynamic.cpp
@@ -1946,7 +1946,8 @@ namespace Dy
        const PxU32 dofs)
    {
        PxQuat newParentToChild = (newRot.getConjugate() * pBody2WorldRot).getNormalized();
-       if(newParentToChild.w < 0.f)
+       //if(newParentToChild.w < 0.f)
+        if ( (newParentToChild.w * relativeQuat.w) < 0.f )
            newParentToChild = -newParentToChild;
        //PxQuat newParentToChild = (newRot * pBody2WorldRot.getConjugate()).getNormalized();

@@ -1966,11 +1967,13 @@ namespace Dy
        return newParentToChild;
    }

-   PxQuat computeSphericalJointPositions(const PxQuat& /*relativeQuat*/,
-       const PxQuat& newRot, const PxQuat& pBody2WorldRot)
+   //PxQuat computeSphericalJointPositions(const PxQuat& /*relativeQuat*/,
+    PxQuat computeSphericalJointPositions(const PxQuat& relativeQuat,
+            const PxQuat& newRot, const PxQuat& pBody2WorldRot)
    {
        PxQuat newParentToChild = (newRot.getConjugate() * pBody2WorldRot).getNormalized();
-       if (newParentToChild.w < 0.f)
+        //if (newParentToChild.w < 0.f)
+        if ( (newParentToChild.w * relativeQuat.w) < 0.f)
            newParentToChild = -newParentToChild;

        /*PxQuat jointRotation = newParentToChild * relativeQuat.getConjugate();
jcarius-nv commented 1 year ago

Hi @z80 thank you very much for reporting the issue and sharing some code for reproducing it. After investigation it turns out to be a symptom of the same problem that was exposed in https://github.com/NVIDIA-Omniverse/PhysX/issues/140 . Please see there for a fix. Your own solution is correct as well as it effectively achieves the same thing.

Please re-open the issue if it still persists after applying the changes.

z80 commented 1 year ago

Hello @jcarius-nv

Thank you so much for the reference to #140. I'd like to clarify that #140 is already in place in the code and the issue persists.

Per my understanding, the fix proposed in #140 might not be enough and when it is applied, math explosions still happen. The math in #140 in both DyFeatherstoneForwardDynamic.cpp and DyFeatherstoneArticulation.cpp checks "if delta > 180, make delta=360-delta". It feels that w<0.f may be a valid solution depending on what exactly the two quaternions are. Due to that, it feels that more correct check would be "if ((both are < 180 or both are > 180) and delta is still > 180), make delta=360-delta".

In other words, instead of

PxQuat dq = a * b.conjugate();
if (dq.w < 0.f)
    dq = -dq;

do this

PxQuat dq = a * b.conjugate();
if ( ((a.w * b.w) > 0.f) && (dq.w < 0.f) )
    dq = -dq;

Thank you!

jcarius-nv commented 1 year ago

ok, interesting to hear. Can you share some numeric values where this extended check is needed and where the simpler one that I proposed is doing the wrong thing? The two places I touched in issue #140 had the quaternion converted to the angle-axis representation afterwards. If you have an example where the angle-axis is still screwed up (abs(angle) > pi) that would be great.

Also, I'm wondering why you write "if delta > 180, make delta=360-delta". I'd argue that angle x and angle 360-x are not the same, rather something like

if delta > 180:
    delta = delta - 360
else if delta < -180
    delta = delta + 360
z80 commented 1 year ago

Hello @jcarius-nv,

Thank you for the reply! Please find the information below.

1) The code provided in the very first message of this thread demonstrates the issue happening when changes proposed in #140 are in place. I.e. with "if (dq.w < 0.f) dq=-dq;" in two files it still produces finite joint coordinates change in a single time step.

2) I apologize, the text "if delta > 180, make delta=360-delta" is an oversimplification of "if (q.w < 0.f) q = -q;" operation given for brevity purposes. The operation q=-q flips both the half angle cosine and the axis and half angle sine product. Within one revolution of half angle there seem to be 3 alternatives, two of which you've provided in your message

-cos(x/2)=cos(180+x/2) and -sin(x/2)axis=sin(180+x/2)axis, which gives x->x+360, axis->axis,

-cos(x/2)=cos(x/2-180) and -sin(x/2)axis=sin(x/2-180)axis, which gives x->x-360, axis->axis,

-cos(x/2)=cos(180-x/2) and -sin(x/2)axis=sin(180-x/2)(-axis), which gives x->360-x, axis->-axis.

In my "delta=360-delta" for brevity I referred to just the 3-d case. If I understand correctly, you refer to the first two cases when you say "delta=delta-360", "delta=delta+360" based on the knowledge of what's happening with the joint.

3) The goal was to show that from a general stand point it is safe (no large instant rotations) to do "if (dq.w < 0.f) dq=-dq;" when it is known for sure that both qa.w and qb.w are either both > 0 or both < 0. For example, if qa.w > 0 but qb.w < 0, then w component of *dq = qa qb.conjugate() is not necessarily > 0**.

The proposed modification "*if ( (newParentToChild.w relativeQuat.w) < 0.f ) ...**" only makes the example code provided not produce instant joint coordinate changes. But further testing has shown that the proposed patch doesn't make all possible articulations work with TGS. It feels that there is more to that problem.

Thank you!

preist-nvidia commented 11 months ago

Internal Tracking: PX-4530

jcarius-nv commented 11 months ago

Thank you for the clarification, indeed there are multiple ways how to change the quaternion without actually changing the underlying rotation.

I tested again with the latest version and could no longer see a jump in joint angles with your example, i.e., it prints

cache->jointPosition[17, 18, 19]: 6.15611e-07 -3.52946e-07 6.86452e-08

But further testing has shown that the proposed patch doesn't make all possible articulations work with TGS. It feels that there is more to that problem.

I'd propose to close this thicket for now because your example appears to be ok. Please create a new ticket if you can reproduce further issues of joints flipping their positions unexpectedly.