Lesson 7: Making Ends Meet

So far we have accomplished to define an environment model with a simple pedal and a body model containing a pelvis and the right leg. What the application is still missing is specifications of how the different elements are connected and how the model moves. With kinematics it is usually a good idea to begin with an inventory of degrees of freedom (DOFs) in the model.

The pedal is simple: It is hinged to the global reference frame and therefore just has just one movement capability, namely a rotation about the hinge. The body model is more complicated. It is disconnected from everything and is therefore floating around in space. Furthermore it has a number of internal degrees of freedom that must be controlled: Three rotations in the hip, one rotation in the knee, and two rotations in the ankle. With the six DOFs of the entire body model in space and the single DOF of the pedal, this adds up to 13 DOFs. In other words, we need 13 constraints before the model is kinematically determinate.

This is what we plan to do:

  1. The pelvis will be fixed completely at a point corresponding to the contact to a seat. This will do away with 6 DOFs leaving us with 7 more to specify.
  2. The foot will be connected to the pedal by a spherical joint having 3 constraints. This leaves us with 4 more constraints to specify.
  3. The ankle angle will be presumed fixed by two constraints. This leaves 2 DOFs to be constrained.
  4. The lateral position of the knee will be specified by a driver. This leaves a single degree of freedom in the system.
  5. Finally, we are going to drive the pedal angle. With the aforementioned constraints this will allow us to specify the posture of the entire system by this single driver.

1. Fixing the pelvis to the global reference frame

We previously joined the pedal to the origin of the global reference frame. This means that the 'seat' to which we shall fix the pelvis must be displaced a suitable distance from the origin. In the Environment.any file, add the following to the definition of the GlobalReferenceFrame:

AnyFixedRefFrame GlobalRef = {
  AnyRefNode Hpoint = {
    sRel = {-0.7, 0.5, 0};
  };
}; // Global reference frame

The name Hpoint is a term used in the seating industry to characterize the position of the pelvis in a seat. Here we shall simply attach the pelvis to this point by means of a rigid connection.

All such specifications are traditionally put into a folder called ModelEnvironmentConnection, and for historical reasons it is placed in an include file called JointsAndDrivers.any. Hit the 'New Include' button

on the toolbar. It brings up an empty window where we can define the objects we need.:

// This file contains the connections between the model
// and the environment along with the motion drivers
AnyFolder Joints = {
  AnyStdJoint SeatPelvis = {
    AnyRefNode &Seat = ;
    AnySeg &Pelvis = ;
  };

};

The local pointer variables &Seat and &Pelvis need something to point to. The best way of locating the necessary points is to use the object tree at the left hand side of the editor window. Place your cursor in the editor window on the &Seat line just before the final semicolon. Then expand the three in the left hand side of the window through MyPedal, EnvironmentModel, GlobalRef to find the Hpoint that we defined previously. Right-click Hpoint and choose 'Insert Object Name'. The full name of the Hpoint is inserted at the position of the cursor.

We must repeat the procedure for the Pelvis. Place the cursor on the &Pelvis line just before the semicolon and subsequently expand the object tree through MyPedal, HumanModel, Trunk, SegmentsLumbar. Inside the lumbar segments folder you will find the PelvisSeg. Right-click and insert the object name. You should now have the following:

AnyFolder Joints = {
  AnyStdJoint SeatPelvis = {
    AnyRefNode &Seat = Main.MyPedal.EnvironmentModel.GlobalRef.Hpoint;
    AnySeg &Pelvis = Main.MyPedal.HumanModel.Trunk.SegmentsLumbar.PelvisSeg;
  };
};

Save the file under the name JointsAndDrivers.any. Then insert the necessary include statement into the main file:

AnyFolder HumanModel={
  #include  "../../../BRep/Aalborg/BodyModels/RightLeg/BodyModel_NoMuscles.any"
  #include "../../../BRepAalborg/Scaling/ScalingStandard.any"
  AnyFolder StrengthParameters={
    AnyVar SpecificMuscleTensionSpine= 90; //N/cm^2
    AnyVar StrengthIndexLeg= 1;
    AnyVar SpecificMuscleTensionShoulderArm= 90; //N/cm^2
  };
};

AnyFolder ModelEnvironmentConnection = {
  #include "JointsAndDrivers.any"
};

Hit F7 to reload the model. The model still loads in the same position as before. If you run the SetInitialConditions operation, the body model may move backward with the pelvis to the point you have specified (You may have to click the model view to update the picture). This brings up the challenge of getting the model elements aligned reasonably at load time. This is an important topic because the model will be unable to resolve the initial conditions when we add more constraints if all the segments are loaded in a big mess on top of each other.

For the purpose of initial alignment, most applications have a file called InitialPositions.any. It contains specifications of positions and rotations of all segments in the model at load time. It is possible to type reasonable positions manually into this file, but it is much easier to use the version from the standing model we used in the first lessons in this tutorial. It allows us to set the load-time positions of the model by means of anatomical joint angles, which is more intuitive for most users.

As a first step, go to /ARep/Aalborg/StandingModel, and make copies of the two files InitialPositions.any and Mannequin.any. Paste these files into the directory of the model you are working on here. Then insert a couple of new include statements into the main file:

AnyFolder MyPedal = {
  #include "Environment.any"
  #include "Mannequin.any"

  AnyFolder HumanModel={
    #include  "../../../BRep/Aalborg/BodyModels/RightLeg/BodyModel_NoMuscles.any"
    #include "../../../BRepAalborg/Scaling/ScalingStandard.any"
    AnyFolder StrengthParameters={
      AnyVar SpecificMuscleTensionSpine= 90; //N/cm^2
      AnyVar StrengthIndexLeg= 1;
      AnyVar SpecificMuscleTensionShoulderArm= 90; //N/cm^2
    };
  };

  AnyFolder ModelEnvironmentConnection = {
    #include "JointsAndDrivers.any"
    #include "InitialPositions.any"
  };
}; // MyPedal

When you load the model you will get an error in the InitialPositions.any file:

ERROR(SCR.PRS9) :  Repository.6.3/ARep/Aalborg/BBTutorial/InitialPositions.any(5)  :   'ref'  :  Unresolved object

Double-click the line number, and the file opens with the cursor placed at the infamous line. The error is that the file is referring to a folder called Model, which in our application is called MyPedal. Change the name in the two subsequent lines:

AnyFolder &ref=Main.MyPedal.HumanModel;
AnyFolder &JointPos=Main.MyPedal.Mannequin.Posture;

and reload again. This time you get another error in the same file. This time it is in a line dealing with the thorax. The standing model comprises the entire body, while the pedal model only has a pelvis and one leg. Therefore, we must erase all sections in the file that do not deal with the pelvis or the right leg. When you are done, the file should have only these definitions:

AnyFolder &ref=Main.MyPedal.HumanModel;
AnyFolder &JointPos=Main.MyPedal.Mannequin.Posture;
ref.Trunk.SegmentsLumbar.PelvisSeg.r0 
{JointPos.PelvisPosX,JointPos.PelvisPosY,JointPos.PelvisPosZ};
ref.Trunk.SegmentsLumbar.PelvisSeg.Axes0=
RotMat((pi/180)*JointPos.PelvisRotZ ,z)*
RotMat((pi/180)*JointPos.PelvisRotY ,y)*
RotMat((pi/180)*JointPos.PelvisRotX ,x);

//Right leg
ref.Right.Leg.Seg.Thigh.Axes0 
ref.Trunk.SegmentsLumbar.PelvisSeg.Axes0*
ref.Trunk.SegmentsLumbar.PelvisSeg.HipJointRight.RotNode.ARel*
RotMat((pi/180)*JointPos.Right.HipAbduction,x)*
RotMat((pi/180)*JointPos.Right.HipExternalRotation,y)*
RotMat((pi/180)*JointPos.Right.HipFlexion,z)*
ref.Right.Leg.Seg.Thigh.HipJoint.RotNode.ARel';

//shank
ref.Right.Leg.Seg.Shank.Axes0 
ref.Right.Leg.Seg.Thigh.Axes0*
ref.Right.Leg.Seg.Thigh.KneeJoint.ARel*
ref.Right.Leg.Seg.Thigh.KneeJoint.RotNode.ARel*
RotMat((-pi/180)*JointPos.Right.KneeFlexion,z)*
//RotMat(pi,y)*
ref.Right.Leg.Seg.Shank.KneeJoint.RotNode.ARel'*
ref.Right.Leg.Seg.Shank.KneeJoint.ARel';

//Foot
ref.Right.Leg.Seg.Foot.Axes0 
ref.Right.Leg.Seg.Shank.Axes0*
ref.Right.Leg.Seg.Shank.AnkleJoint.ARel*
ref.Right.Leg.Seg.Shank.AnkleJoint.RotNode.ARel*
RotMat((pi/180)*JointPos.Right.AnklePlantarFlexion ,z)*
RotMat((pi/180)*JointPos.Right.AnkleEversion ,y)*
ref.Right.Leg.Seg.Foot.AnkleJoint.RotNode.ARel'*
ref.Right.Leg.Seg.Foot.AnkleJoint.ARel';

Now reload the model again. This time it should work, and you will get the following picture:

We can now forget about the InitialPositions.any file. All positioning from now on takes place in the Mannquin.any file. Open it up and make the following changes:

AnyFolder Mannequin = {

  AnyFolder Posture = {
    //This controls the position of the pelvis wrt. to the global reference frame
    AnyVar PelvisPosX = -0.7;
    AnyVar PelvisPosY = 0.5;
    AnyVar PelvisPosZ = 0;

What we have done here is to specify the load-time position of the pelvis to the place where we have the seat. After reload you should be able to see in the model view that the body model has moved to a new position. It is also a good idea to specify the initial joint angles so that the foot comes closer to the pedal. This can be done further down in the Mannequin file:

AnyFolder Right = {
  //Arm
  AnyVar SternoClavicularProtraction=-23;   //This value is not used for initial position
  AnyVar SternoClavicularElevation=11.5;    //This value is not used for initial position
  AnyVar SternoClavicularAxialRotation=-20; //This value is not used for initial position

  AnyVar GlenohumeralFlexion =-0;
  AnyVar GlenohumeralAbduction = 10;
  AnyVar GlenohumeralExternalRotation = 0;

  AnyVar ElbowFlexion = 0.01;
  AnyVar ElbowPronation = 10.0;

  AnyVar WristFlexion =0;
  AnyVar WristAbduction =0;

  AnyVar HipFlexion = 110.0;
  AnyVar HipAbduction = 5.0;
  AnyVar HipExternalRotation = 0.0;

  AnyVar KneeFlexion = 100.0;

On reload you will see that the body now loads in pretty much the desired position. Notice that this is only to bring the body close to where it will eventually be. It is not necessary to align the model exactly with the pedal. The kinematic constraints will take care of this once they are properly defined.

2. Connecting the foot to the pedal

The foot will be connected to the pedal by a spherical joint. This is defined inside the JointsAndDrivers file in the following way:

AnySphericalJoint PedalFoot = {
  AnyRefNode &Pedal = Main.MyPedal.EnvironmentModel.Pedal.FootNode;
  AnyRefNode &Foot = Main.MyPedal.HumanModel.Right.Leg.Seg.Foot.MetatarsalJoint2Node;
};

We have cheated just a little. It is possible to define new nodes on the foot for attachment to a specific place, but we have taken the cheap-and-dirty solution of picking an existing point close to where we presume the contact with the pedal will be. The MetatarsalJoint2Node is a good approximation.

You will not see any change when you reload the model, but if you run the SetInitialConditions operation, you might get this (notice you may have to click the model view window to update the picture):

Notice that the leg has moved slightly to honor the constraint that the foot must be on the pedal.

3. Setting the ankle angle

The ankle in this body model is a universal joint, which means that it has two degrees of freedom. We wish to constrain these to degrees of freedom to predefined values. This can be done by a so-called simple driver. In the JointsAndDrivers file we shall introduce a driver section below the Joints folder:

AnyFolder Joints = {
  AnyStdJoint SeatPelvis = {
    AnyRefNode &Seat = Main.MyPedal.EnvironmentModel.GlobalRef.Hpoint;
    AnySeg &Pelvis = Main.MyPedal.HumanModel.Trunk.SegmentsLumbar.PelvisSeg;
  };
  AnySphericalJoint PedalFoot = {
    AnyRefNode &Pedal = Main.MyPedal.EnvironmentModel.Pedal.FootNode;
    AnyRefNode &Foot = Main.MyPedal.HumanModel.Right.Leg.Seg.Foot.MetatarsalJoint2Node;
  };
};
AnyFolder Drivers = {
};

We then insert the simple driver into the Drivers folder:

AnyFolder Drivers = {
  AnyKinEqSimpleDriver AnkleDriver = {
    AnyUniversalJoint &Ankle = Main.MyPedal.HumanModel.Right.Leg.Jnt.Ankle;
    DriverPos = {};
    DriverVel = {0, 0};
  };

};

Most of this came about the same way as we have done previously: The definition of the AnyKinEqSimpleDriver (and indeed its complex name) came from the object inserter in the Classes tree at the left hand side of the editor window. The complete name of the ankle joint was inserted from the object tree. The joint is going to be static, so the DriverVel specification was also easy to do. What is remaining is the DriverPos specification. It is currently an empty pair of braces. The problem here is to know which of the two degrees of freedom is which. Fortunately, the model is already loaded, and we can get the current values for the ankle angles from the object tree. Click your way through the tree to HumanModel->Right->Leg->Jnt->Ankle, and double-click the Pos property. The current angles are dumped in the message window:

Main.MyPedal.HumanModel.Right.Leg.Jnt.Ankle.Pos = {1.570796, 0};

Now we know which value to assign to the joint angle driver:

AnyFolder Drivers = {
  AnyKinEqSimpleDriver AnkleDriver = {
    AnyUniversalJoint &Ankle = Main.MyPedal.HumanModel.Right.Leg.Jnt.Ankle;
    DriverPos = {1.570796,  0};
    DriverVel = {0, 0};
  };

};

The model should load again with no significant difference.

4. Fix the lateral position of the knee

Imagine your pelvis on a seat and your foot resting on a point like the model is right now. You can still move your knee sideways either medially or laterally rotating the leg about an axis through the foot contact point and the hip joint. We must constrain this movement, and the easiest way to do it is by fixing the knee laterally.

We shall do this by another simple driver in conjunction with a linear measure. Let us add another driver to the Drivers folder:

AnyFolder Drivers = {
  AnyKinEqSimpleDriver AnkleDriver = {
    AnyUniversalJoint &Ankle = Main.MyPedal.HumanModel.Right.Leg.Jnt.Ankle;
    DriverPos = {1.570796,  0};
    DriverVel = {0, 0};
  };

  AnyKinEqSimpleDriver KneeDriver = {
    DriverPos = {0};
    DriverVel = {0};
  };
};
 

This empty driver needs something to drive. We are going to create a linear measure between the global reference frame and the knee:

AnyKinEqSimpleDriver KneeDriver = {
  AnyKinLinear GlobKnee = {
    AnyRefFrame &Glob = Main.MyPedal.EnvironmentModel.GlobalRef;
    AnyRefFrame &Knee = Main.MyPedal.HumanModel.Right.Leg.Seg.Thigh.KneeJoint;
  };
  DriverPos = {0};
  DriverVel = {0};
};
};
 

The AnyKinLinear is really a vector between the two points it refers to, i.e. in this case the position of the knee in the global reference frame. However, we only wish to drive one of the coordinates of this vector, namely the lateral coordinate. This is the z coordinate, which in an AnyScript model has number two, because numbering begins at 0. To drive only this one coordinate, we insert a measure organizer:

AnyKinEqSimpleDriver KneeDriver = {
  AnyKinLinear GlobKnee = {
    AnyRefFrame &Glob = Main.MyPedal.EnvironmentModel.GlobalRef;
    AnyRefFrame &Knee = Main.MyPedal.HumanModel.Right.Leg.Seg.Thigh.KneeJoint;
  };
  MeasureOrganizer = {2};
  DriverPos = {0};
  DriverVel = {0};
};
};
 

This has the effect of neglecting the x and y coordinates of the vector returned by the linear measure. You should be able to load the model again, but there is no visible difference.

5. Drive the pedal

The final step is to drive the movement of the pedal. It is hinged to the origin of the coordinate system, and we shall add a driver to the joint angle pretty much like we did with the ankle and the knee.

AnyKinEqSimpleDriver KneeDriver = {
  AnyKinLinear GlobKnee = {
    AnyRefFrame &Glob = Main.MyPedal.EnvironmentModel.GlobalRef;
    AnyRefFrame &Knee = Main.MyPedal.HumanModel.Right.Leg.Seg.Thigh.KneeJoint;
  };
  MeasureOrganizer = {2};
  DriverPos = {0};
  DriverVel = {0};
};

AnyKinEqSimpleDriver Pedal = {
  AnyRevoluteJoint &Hinge = Main.MyPedal.EnvironmentModel.HingeJoint;
  DriverPos = {100*pi/180};
  DriverVel = {45*pi/180};
};

This puts the pedal in an initial 100 degree angle compared to vertical. It also specifies a movement with an angular velocity of 45 degrees per second, but let us postpone the investigation of that for later.

For now, hit F7 again to reload the model. Notice that the system no longer complains about the model being kinematically indeterminate. Run the SetInitialConditions operation to get things connected. With a little luck you will get this picture:

 

With the model kinematically determinate we can proceed and run the KinematicAnalysis operation. Doing so will show you the movement of the entire system as the pedal is rotating.

Now that the kinematics is in order, let us move on to the kinetic analysis in Lesson 8 and see what the model is good for.

AnyBody Technology A/S · Niels Jernes Vej 10 · DK-9220 Aalborg Ø · Denmark · Tel. +45 9635 4286 · Fax. +45 9635 4599
Copyright (c) AnyBody Technology A/S · 2006 · All rights reserved · Email webmaster@anybodytech.com