Creating chains using multiple DistanceJoint classes

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

I'm attempting to create a connection between two entities using a defined number of links:

Code: Select all

            Entity eA;
            Entity eB;
            DistanceJoint j;

            Vector3 forward = game.AudioObjects[anchorB_UniqueID].Entity.Position - game.AudioObjects[anchorA_UniqueID].Entity.Position;
            float linkDistance = forward.Length();
            linkDistance /= numLinks;
            
            forward.Normalize();

            for (i = 0; i < numLinks; ++i)
            {
                eA = i == 0 ? game.AudioObjects[anchorA_UniqueID].Entity : new Sphere(game.AudioObjects[anchorA_UniqueID].Entity.Position + (forward * linkDistance * i), 0.05f, 1f);
                eB = i == numLinks - 1 ? game.AudioObjects[anchorB_UniqueID].Entity : new Sphere(game.AudioObjects[anchorA_UniqueID].Entity.Position + (forward * linkDistance * (i + 1)), 0.05f, 1f);

                j = new DistanceJoint(eA, eB, eA.Position, eB.Position);

                game.Space.Add(j);
                joints.Add(j);

                obbs.Add(new OBB((j.WorldAnchorA + j.WorldAnchorB) * 0.5f, 0.5f, 0.5f, j.Distance, ToolBox.RotationFrom(j.WorldAnchorA - j.WorldAnchorB)));
            }
The links are created as small spheres along the direction vector between the two anchor objects either side. The problem is that the link behaves as expected when only one link is present (i.e. one joint) when 2 or more are present it does not behave as a chain link but instead stretches at the joints.

What could be the cause of this behaviour? Why would the stiffness remain constant for one link and not for multiple?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

What could be the cause of this behaviour? Why would the stiffness remain constant for one link and not for multiple?
The stiffness of a single joint may be constant, but adding additional weight to a spring of constant stiffness will stretch it more. Constraints are equivalent to very rigid springs (although their spring settings can be made quite weak if desired).

However, it sounds like there's more going on than just extra weight. My first guess would be mass ratios. If the link entities are very light compared to the endpoints, solving becomes a lot harder. Increasing the Space.Solver.IterationLimit and SolverSettings.DefaultMinimumIterations will help, but it's cheaper to just make the links heavier relative to the endpoints.

With long chains, even if all the masses are the same, a single link can experience a massive amount of dependent weight. This can cause the same sort of mass ratio issue. Increasing the iteration count or shortening the chain will mitigate the issue.

Also, because the links are light, constraints associated with them will likely early out faster due to the fixed SolverSettings.DefaultMinimumImpulse threshold. Just making them heavier will force it to do more iterations before short circuiting.

Sometimes, though, very tiny objects with high relative velocities just need a shorter Space.TimeStepSettings.TimeStepDuration. That's the 'hammer' in the stability toolbox; it will increase simulation quality across the board and solve stability issues that higher iteration counts can't efficiently mitigate. Using fewer, bigger individual links can accomplish something similar.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

Just making them heavier will force it to do more iterations before short circuiting.
That has helped stability, thank you.

I would like the links to behave as a chain link would do so but when I move the anchor points the middle link doesn't move. I have uploaded a video to demonstrate here: http://youtu.be/JknTxAgIxfc

How can I get the middle link to dynamically move when the anchor points it is attached to move also?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

I'm not clear on the situation; if all relevant entities are dynamic, it should move. In the video, it looks like something is kinematic.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

The setup is as follows:

[ ] - Kinematic Anchor // game.AudioObjects[anchorA_UniqueID].Entity
|
|
- - Dynamic Sphere Anchor Node // new Sphere(game.AudioObjects[anchorA_UniqueID].Entity.Position + (forward * linkDistance * i), 0.05f, 1f);
|
|
[] - Dynamic Entity Anchor // game.AudioObjects[anchorB_UniqueID].Entity

I don't want the top kinematic anchor to move unless it is moved by the mouse. The dynamic entity anchor moves freely under the simulation. However the dynamic sphere should also move freely but it remains fixed in position?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

Has everything been added to the space? If the link entity isn't in the space, the position, among other things, won't update.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

Has everything been added to the space?
I overlooked adding the chain links to the space, thank you :)

I'm now trying to move around an anchor/kinentic entity connection using a servomechanism based mouse control:

Code: Select all

            angularMotor.Settings.Servo.Goal = e.Orientation;
            angularMotor.Settings.Servo.SpringSettings.StiffnessConstant = 600000f * Entity.Mass;
            angularMotor.Settings.Servo.SpringSettings.DampingConstant = 9000f * Entity.Mass;
            angularMotor.Settings.MaximumForce = 100000f * Entity.Mass;
            angularMotor.Settings.VelocityMotor.Softness = .1f / Entity.Mass;

            // The stiffness, damping, and maximum force could be assigned during setup if the motor
            // needs to behave similarly for entities of varying masses.  
            // When using a fixed configuration, the grabspring will behave weakly when trying to move extremely heavy objects,
            // while staying very responsive for sufficiently light objects.
            linearMotor.Settings.Servo.SpringSettings.StiffnessConstant = 600000f * Entity.Mass;
            linearMotor.Settings.Servo.SpringSettings.DampingConstant = 9000f * Entity.Mass;

            // An unlimited motor will gladly push the entity through other objects.
            // Putting a limit on the strength of the motor will prevent it from doing so.
            linearMotor.Settings.MaximumForce = 100000f * Entity.Mass;
The kinematic object does not move immediately to the mouse position. The joints it is connected to seem to resist movement as demonstrated in this video: http://youtu.be/SjDixqZ5DfU

How can I get the links to behave more like a chain (i.e. ball and chain with the dynamic box behaving as the ball and the kinematic box controlling the movement)?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

Kinematic entities cannot be moved by any force; they have effectively infinite mass. In other words, that servomechanism shouldn't be doing anything. I'm not sure what's going on in that video; maybe another control scheme is in use and is freaking out.

If you want to move a kinematic entity in a way that handles constraints well, just set its velocity directly. The EntityMover can be used here; set a goal each update and it will calculate the velocity for you each update. Watch out if Space.Update(dt) is being used: the EntityMover runs on every time step, so if the goal is only set once per frame and multiple time steps occur, the kinematic could lurch forward and halt within a single Space.Update(dt). It's better if the goal is set continuously every time step.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

Kinematic entities cannot be moved by any force; they have effectively infinite mass. In other words, that servomechanism shouldn't be doing anything.
Sorry I should have stated that I make the Kinematic entities dynamic when they are grabbed by the mouse (and therefore the servomechanism)

Code: Select all

        public void Setup(Entity e, Vector3 grabLocation, GrabMode grabMode)
        {
            Entity = e;

            isDynamic = e.IsDynamic;

            // Make entity dynamic for the grabspring to work correctly
            // If the entity isn't dynamic its mass is float.MaxValue and will cause NaN errors
            // It will also cause the translation widget axes to jump on selection
            if (!isDynamic)
            {
                // Preserve velocities for kinematic entities
                // The forces added by the motors in the grab spring will be zeroed out when the object is released
                // This will zero out all previous forces (before grab) so they need to be reapplied
                kinematicAngularVelocity = e.AngularVelocity;
                kinematicLinearVelocity = e.LinearVelocity;

                Entity.BecomeDynamic(1f);
            }

            // Get the original entity properties that will need to be changed for this GrabSpring to function correctly
            originalEntityCollisonRule = Entity.CollisionInformation.CollisionRules.Personal;

            // Can I assign a rule to this entity's collision pairs?
            // This way the other object in the collision pair will also react in the same way as this object for that particular collision
            if (e.CollisionInformation.Tag as HollowPrimitive == null)
            {
                Entity.CollisionInformation.CollisionRules.Personal = CollisionRule.NoBroadPhase;
            }

            // If you just want the object to rotate around the center of mass while eliminating other linear motion
            // Set the linear motor's point to the entity's position

            bool rotationOn = (grabMode & GrabMode.Rotation) != 0;
            //bool scaleOn = (grabMode & GrabMode.Scale) != 0;
            //bool translationOn = (grabMode & GrabMode.Translation) != 0;

            LocalOffset = rotationOn ? Vector3.Zero : Vector3.Transform(grabLocation - e.Position, Quaternion.Conjugate(e.Orientation));
            localOffsetForRotation = grabLocation - e.Position;

            GrabbedOrientation = e.Orientation;
            GoalPosition = rotationOn ? e.Position : grabLocation;
            
            angularMotor.Settings.Servo.Goal = e.Orientation;
            angularMotor.Settings.Servo.SpringSettings.StiffnessConstant = 60000f * Entity.Mass;
            angularMotor.Settings.Servo.SpringSettings.DampingConstant = 900f * Entity.Mass;
            angularMotor.Settings.MaximumForce = 10000f * Entity.Mass;
            angularMotor.Settings.VelocityMotor.Softness = .1f / Entity.Mass;

            // The stiffness, damping, and maximum force could be assigned during setup if the motor
            // needs to behave similarly for entities of varying masses.  
            // When using a fixed configuration, the grabspring will behave weakly when trying to move extremely heavy objects,
            // while staying very responsive for sufficiently light objects.
            linearMotor.Settings.Servo.SpringSettings.StiffnessConstant = 60000f * Entity.Mass;
            linearMotor.Settings.Servo.SpringSettings.DampingConstant = 900f * Entity.Mass;

            // An unlimited motor will gladly push the entity through other objects.
            // Putting a limit on the strength of the motor will prevent it from doing so.
            linearMotor.Settings.MaximumForce = 10000f * Entity.Mass;
        }
The control scheme works fine with single links (1 Distance Joint) but not with anything more than a single link (>1 Distance Joints. It doesn't matter if the two visible objects connected are dynamic or kinematic.
I'm not sure what's going on in that video; maybe another control scheme is in use and is freaking out.
This is what I am using to create the links. There is nothing other than a series of connected DistanceJoints:

Code: Select all

        private void CreateJoints()
        {
            int i;

            // Create new joints and associated OBBs
            Entity eA;
            Entity eB;
            Entity previousLink = null;
            DistanceJoint j;
            float obbThickness = 0.05f;

            Vector3 forward = game.AudioObjects[anchorB_UniqueID].Entity.Position - game.AudioObjects[anchorA_UniqueID].Entity.Position;
            float linkDistance = forward.Length();
            linkDistance /= numLinks;
            
            forward.Normalize();

            // Always make the links heavier than the end anchors to improve stability        
            //float linkMass = ToolBox.Max(game.AudioObjects[anchorA_UniqueID].Entity.Mass, game.AudioObjects[anchorB_UniqueID].Entity.Mass) * 10f;
            float linkMass = Constants.MAXMASS;

            for (i = 0; i < numLinks; ++i)
            {
                if (i == 0)
                {
                    eA = game.AudioObjects[anchorA_UniqueID].Entity;
                }
                else
                {
                    if (previousLink == null)
                    {
                        eA = new Sphere(game.AudioObjects[anchorA_UniqueID].Entity.Position + (forward * linkDistance * i), 0.05f, linkMass);
                        game.Space.Add(eA);
                    }
                    else
                    {
                        eA = previousLink;
                    }
                }

                if (i == numLinks - 1)
                {
                    eB = game.AudioObjects[anchorB_UniqueID].Entity;
                }
                else
                {
                    eB = new Sphere(game.AudioObjects[anchorA_UniqueID].Entity.Position + (forward * linkDistance * (i + 1)), 0.05f, linkMass);;
                    game.Space.Add(eB);

                    previousLink = eB;
                }

                j = new DistanceJoint(eA, eB, eA.Position, eB.Position);

                game.Space.Add(j);
                joints.Add(j);

                obbs.Add(new OBB((j.WorldAnchorA + j.WorldAnchorB) * 0.5f, obbThickness, obbThickness, j.Distance, ToolBox.RotationFrom(j.WorldAnchorA - j.WorldAnchorB)));
            }
        }
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

Is everything being created in the right spots, without conflicts? For example, if a link entity is created in collision with another link entity or in collision with one of the anchors without a special collision rule in place to stop contacts or contact solving, then the constraints will fight and things will tend to explode.

Isolating the simulation component in a simplified-as-much-as-possible demo or something to see more clearly what the physics-only of things is doing would be informative too.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

I've created the small demo for this problem (see attached .zip file)

WASD keys to translate the camera
Mouse to rotate the camera

Search for the following line: 'settings.NumLinks = 4'

Anything greater than 1 causes a problem.
Attachments
PhysicsProblem.zip
(703.8 KiB) Downloaded 259 times
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

The behavior with NumLinks set to 1, 2, or 3 produces expected results; is the whipping effect the unwanted behavior? If so, it's caused by the links being so much heavier than the last box. Being so heavy, the links dominate the movement; the connected box just follows along limply. The links don't need to be that heavy, so reducing link masses to roughly equal would mitigate the whip effect while preserving stability. Something like the commented mass calculation would work, except without the big multiplier:

Code: Select all

    float linkMass = ToolBox.Max(game.ConnectableObjects[anchorA_UniqueID].Entity.Mass, game.ConnectableObjects[anchorB_UniqueID].Entity.Mass);
It could probably go even lighter if the number of links isn't really high.

With 4 and above links, a link entity gets created too close to the box and so it gets stuck on top. With even more links, the link entity is pulled by the constraint into the connected object, triggering a constraint battle between two rigid combatants.

To solve that, either filter the collisions with collision rules or improve link placement. For example, instead of creating link entities uniformly along a line segment between the centers of the two connected objects (and thus possibly creating link entities colliding with the connected objects), create the links uniformly along the line segment connecting the exteriors of the entities, being careful not to create a link too close to the surface (which, due to the link radius, would intersect still). Immediately touching links could still be filtered by collision rules to prevent unnecessary contact generation and solving work.
Spankenstein
Posts: 249
Joined: Wed Nov 17, 2010 1:49 pm

Re: Creating chains using multiple DistanceJoint classes

Post by Spankenstein »

To solve that, either filter the collisions with collision rules or improve link placement.
All done, that certainly improves things somewhat :)

When the simulation is offline/paused and the space.Update() is skipped I would still like the joints to restrict movement when an object is moved via mouse control.

Currently I am updating all the joints as follows in offline mode:

Code: Select all

        public void Update(GameTime gameTime)
        {
            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

            int numJoints = joints.Count;

            for (int i = 0; i < numJoints; ++i)
            {
                joints[i].Update(dt);
                joints[i].UpdateSolverActivity();
            }
        }
This still allows the joint to stretch and not maintain its rigidity.

How can I get the behaviour in offline mode to work like it does in online mode?

The following video shows the correct behaviour in online mode followed by the incorrect behaviour when offline: http://youtu.be/2J6ppT4HtQU
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Creating chains using multiple DistanceJoint classes

Post by Norbo »

Constraints require almost all of the dynamics simulation to work. While it's technically possible to perform just the updates required, the set of 'just the updates required' spans a lot of different systems and it would require a lot of familiarity with the internals of the engine and simulation in general.

Basically, if physical simulation is desired, run the physical simulation.
Post Reply