Revolute Joint Stifness

Aug 7, 2010 at 8:42 AM

I try to increase the stiffness of a joint Revolute. 

Box2D manual says : "You can use a joint motor to simulate joint friction. Just set the joint speed to zero, and set the maximum torque to some small, but significant value. The motor will try to prevent the joint from rotating, but will yield to a significant load."

So i create a revolute joint with this values : 

        MAXMOTORTORQUE = float.MaxValue; 

        MOTORSPEED = 0.0f;

         MOTORENABLE = true;

I've tried dozens of values (in settings class also), but nothing seems able to block the angle of the motor completely :(

Does anyone have any ideas?

I can provide a testbed to illustrate.

Coordinator
Aug 7, 2010 at 1:01 PM

What do you mean by the stiffness? There is a practical stiffness and a mathematical stiffness.

The Box2D manual describes practical stiffness. It describes how you limit the revolute joint using the motor settings. The quote you wrote from the manual tells you to use a small but significant value in the max motor torque variable, float.MaxValue is not a small value at all, it is the largest value possible.

Could you explain what you are trying to accomplish?

Aug 7, 2010 at 3:38 PM

To stop a joint rotating completely you should use limits. Joint.LimitEnabled, LowerLimit, UpperLimit. The upper and lower limit are angles in radians relative to the initial angle when you created the joint.

The motor just makes it difficult to move, as if it was a screw that was tightened really tight. However, if you're strong you can still turn it.

Another method to stop rotation is to use a PrismaticJoint together with a RevoluteJoint, but Id suggest trying limits first.

Aug 7, 2010 at 5:39 PM

I've already try to set the LowerLimit and UpperLimit to the current JointAngle.

 

Here is a TestBed to illustrate my probleme, maybe it's juste a scaling problem, but i try to use real size objects.

 

I want to stabilize/freeze the two segments when i shake the character in this sample :

 

using FarseerPhysics.Collision;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics.TestBed.Framework;
using Microsoft.Xna.Framework;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework.Input;

namespace FarseerPhysics.TestBed.Tests
{
    public class RevoluteJointStifnessTest : Test
    {
        //BODIES
        private static float DENSITY = 0.1f;
        private static float WIDTH = 0.2f;
        private static float HEIGHT = 0.5f;

        //WHEEL
        private RevoluteJoint _wheelMotor;

        //segments joints
        private static float MAXMOTORTORQUE = float.MaxValue;
        private static float MOTORSPEED = 0.0f;
        private static bool MOTORENABLE = true;

        private RevoluteJointStifnessTest()
        {
            Fixture ground = FixtureFactory.CreateEdge(World, new Vector2(-40.0f, 0.0f), new Vector2(40.0f, 0.0f), 0);
            ground.Friction = 30.0f;

            //One Wheel Character
            Fixture _Character = FixtureFactory.CreateRectangle(World, 1, 1, 1);
            _Character.CollisionGroup = 1;
            _Character.Body.Position = new Vector2(5.0f, 6.0f);
            _Character.Body.BodyType = BodyType.Dynamic;
            _Character.Body.FixedRotation = true;
            _Character.Body.SleepingAllowed = false;

            Fixture wheel = FixtureFactory.CreateCircle(World, 0.5f, 1.0f);
            wheel.CollisionGroup = 1;
            wheel.Body.Position = _Character.Body.Position + new Vector2(0.0f, -0.5f);
            wheel.Body.BodyType = BodyType.Dynamic;
            wheel.Body.SleepingAllowed = false;
            wheel.Friction = 30.0f;

            _wheelMotor = new RevoluteJoint(_Character.Body, wheel.Body, new Vector2(0, -0.5f), Vector2.Zero);
            _wheelMotor.MaxMotorTorque = 100.0f;
            _wheelMotor.MotorEnabled = true;
            World.AddJoint(_wheelMotor);

            //First segment
            Fixture segment1 = FixtureFactory.CreateRectangle(World, WIDTH, HEIGHT, DENSITY, _Character.Body.Position + new Vector2(0, 0.5f) + new Vector2(0, HEIGHT / 2.0f));
            segment1.IsSensor = true;
            segment1.Body.BodyType = BodyType.Dynamic;

            RevoluteJoint RJB1 = new RevoluteJoint(_Character.Body, segment1.Body, new Vector2(0, 0.5f), new Vector2(0, -HEIGHT / 2.0f));
            RJB1.MotorEnabled = true;
            RJB1.MotorSpeed = 0.0f;
            RJB1.MaxMotorTorque = MAXMOTORTORQUE;
            World.AddJoint(RJB1);

            //Second segment
            Fixture segment2 = FixtureFactory.CreateRectangle(World, WIDTH, HEIGHT, DENSITY, segment1.Body.Position + new Vector2(0, HEIGHT / 2.0f));
            segment2.IsSensor = true;
            segment2.Body.BodyType = BodyType.Dynamic;

            RevoluteJoint RJB2 = new RevoluteJoint(segment1.Body, segment2.Body, new Vector2(0, HEIGHT / 2.0f), new Vector2(0, -HEIGHT / 2.0f));
            RJB2.MotorEnabled = true;
            RJB2.MotorSpeed = 0.0f;
            RJB2.MaxMotorTorque = MAXMOTORTORQUE;
            World.AddJoint(RJB2);
        }

        public override void Keyboard(KeyboardState state, KeyboardState oldState)
        {
            _wheelMotor.MotorSpeed = 0.0f;
            if (state.IsKeyDown(Keys.S))
            {
                _wheelMotor.MotorSpeed = 8.0f;
            }

            if (state.IsKeyDown(Keys.D))
            {
                _wheelMotor.MotorSpeed = -8.0f;
            }
        }


        public override void Update(GameSettings settings, GameTime gameTime)
        {
            DebugView.DrawString(50, TextLine, "Shake it ! <-S-|-D->");
            base.Update(settings, gameTime);
        }

        public static Test Create()
        {
            return new RevoluteJointStifnessTest();
        }
    }
}

Aug 8, 2010 at 3:12 AM
Edited Aug 8, 2010 at 3:20 AM

Using limits makes it much more stable, but they still get out of place sometimes and move back quite slowly. I added the following lines to both joints to enable limits:

            RJB1.LowerLimit = RJB1.ReferenceAngle;
            RJB1.UpperLimit = RJB1.ReferenceAngle;
            RJB1.LimitEnabled = true;

The best solution though would probably be to use a WeldJoint. I tried the following instead of RevoluteJoints and it was much better:

            WeldJoint WJ1 = new WeldJoint(_Character.Body, segment1.Body, new Vector2(0, 0.5f), new Vector2(0, -HEIGHT / 2.0f));
            World.AddJoint(WJ1);

            WeldJoint WJ2 = new WeldJoint(segment1.Body, segment2.Body, new Vector2(0, HEIGHT / 2.0f), new Vector2(0, -HEIGHT / 2.0f));
            World.AddJoint(WJ2);

WeldJoints stop both rotation and movement, so you won't be able to enable / disable it like you would with a RevoluteJoint. If you need that functionality you could Add and Remove the two joints as you need them.

Aug 8, 2010 at 8:36 AM

Thanks to take time for me.

I'll try to switch between this two joints.

To be more precise, i'm using revolute joint to animate a mechanical arm on a truck, and obviously, when i move the truck, the arm wriggles...

thanks again for your help.