Lesson 3: Connecting Segments by Joints#
Note
Here’s an AnyScript file to start on if you have not completed the
previous lesson: demo.lesson3.any
.
Some quick theory#
You can think of joints in two different ways:
Providers of freedom, when compared to a system of rigidly joined segments
Constraints on freedom, when compared to a system of free-floating segments
In AnyBody, we consider joints as being constraints on freedom. When you create two independent segments in AnyBody, they will have 2 x 6 = 12 degrees of freedom in total.
Joints in AnyBody take away (or constrain) some of these degrees of freedom with the different joint types (e.g. revolute, spherical, prismatic etc.) differing only in the number and type of degrees of freedom they constrain.
The global reference frame#
Every model needs a global reference frame to serve as a ground reference point for measuring positions & orientations from. It is already defined in the model template that you originally used, and is shown below:
// Global Reference Frame
AnyFixedRefFrame GlobalRef = {
// Todo: Add points for grounding of the model here
}; // Global Reference Frame
Just like with segments, you can add point nodes to the global reference frame for attaching joints and muscles. There is however no concept of mass associated with this frame.
You will now add some nodes to the global frame by copy-pasting the following lines into your model (Alternatively use the class inserter and then copy-paste the class properties alone):
// Global Reference Frame
AnyFixedRefFrame GlobalRef = {
AnyDrawRefFrame DrwGlobalRef = {};
AnyRefNode Shoulder = {
sRel = {0,0,0};
};
AnyRefNode DeltodeusA = {
sRel = {0.05,0,0};
};
AnyRefNode DeltodeusB = {
sRel = {-0.05,0,0};
};
AnyRefNode BicepsLong = {
sRel = {0.1,0,0};
};
AnyRefNode TricepsLong = {
sRel = {-0.1,0,0};
};
}; // Global Reference Frame
The first line, “AnyDrawRefFrame
…” merely displays the global
reference system in the graphics window. The remaining lines define point nodes attached to the global reference
system.
Let us scale down the display size and change its color to distinguish it from the yellow segments:
AnyDrawRefFrame DrwGlobalRef = {
ScaleXYZ = {0.1, 0.1, 0.1};
RGB = {0,1,0};
};
Creating a revolute joint#
We can now connect the upper arm to the global reference frame through a shoulder joint.
For this planar 2-D model, where we simplify the shoulder as a simple revolute joint (also known as a hinge joint).
We create a new AnyFolder
object, to contain all joints in the model:
}; // ForeArm
}; // Segs folder
// Joints
AnyFolder Jnts = {
//---------------------------------
AnyRevoluteJoint Shoulder = {
AnyRefNode &GroundNode = ..GlobalRef.Shoulder;
AnyRefNode &UpperArmNode = ..Segs.UpperArm.ShoulderNode;
Axis = z;
}; // Shoulder joint
}; // Jnts folder
The AnyRevoluteJoint
class creates a revolute joint object connecting two nodes on different segments.
Relative folder paths - Why use ‘.’ and ‘..’ in AnyScript?#
The AnyRevoluteJoint
object named “Shoulder”, needs to know which nodes on each segment to connect. For this
purpose, we have the lines:
AnyRefNode &GroundNode = ..GlobalRef.Shoulder;
AnyRefNode &UpperArmNode = ..Segs.UpperArm.ShoulderNode;
They refer to two nodes that we created earlier, located on the GlobalRef
and UpperArm
segments. Notice the two dots in front of the names. They signify that
the GlobalRef
and Segs
folders are defined two levels outside the folder
we are writing into, in the model tree.
If you neglected the two dots, then AnyBody would search for the two objects in the “Shoulder” folder and fail to find them. This “dot” system is quite similar to the system you may know from directory structures in Dos, Windows, Unix, or just about any other computer operating system.
Reference objects and the ‘&’ symbol#
You can also see that the Shoulder point on GlobalRef has been given the local name of GroundNode
.
This means that, within the current folder, we can simply refer to
the point as “GroundNode” instead of the longer external reference.
You will also realize that GroundNode
is merely a reference (a pointer) to GlobalRef.Shoulder
rather than a copy of it. We denote this by the &
sign.
If “Main.ArmModel.GlobalRef.Shoulder” moves around, “Main.ArmModel.Jnts.Shoulder.GroundNode” will keep up with those changes in position. Hit F7 to reload the model again to make sure that the definition is correct.
Customizing the revolute joint#
We then have:
Axis = z;
Note
Every node on a rigid body segment has its own reference frame which moves along with the segment.
By default, these coordinate systems are parallel to the segment frame. However, the relative orientations
between these two frames can be altered by the user. Check the ARel
property and AnyRefNode
object in the AnyScript Reference Manual for more information.
The AnyBody Modeling System is always three-dimensional, even when our model is
two dimensional. The property Axis = z
simply specifies that both
segments connected by that joint will rotate about the z axis of two nodes forming the joint.
In other words, the z-axes of the nodes on either connected segment will always be parallel, and so the mechanism will rotate in the plane perpendicular to these axes. The out-of-plane relative orientation of the two segments can be adjusted by rotating the reference frames of the nodes being connected. This is relevant if you want one of the segments to rotate about some skewed axis.
Caution
The first of the two
nodes declared in the joint (in this case GroundNode
) becomes the
default reference frame for the joint. When directly accessing the
post-simulation values of constraint reaction forces etc., you must
remember to interpret them in the joint’s default reference frame.
Creating a revolute elbow joint#
We will next add a revolute elbow joint. While the definition is similar to the shoulder, you will learn to use a handy tool to define the references. Copy-paste the skeleton of the elbow joint as follows:
// Joints
AnyFolder Jnts = {
//---------------------------------
AnyRevoluteJoint Shoulder = {
AnyRefNode &GroundNode = ..GlobalRef.Shoulder;
AnyRefNode &UpperArmNode = ..Segs.UpperArm.ShoulderNode;
Axis = z;
}; // Shoulder joint
AnyRevoluteJoint Elbow = {
Axis = z;
AnyRefNode &UpperArmNode = ;
AnyRefNode &ForeArmNode = ;
}; // Elbow joint
}; // Jnts folder
The node references within “Elbow” are not pointing at anything yet. In this simple model it is easy to find the relative path of the pertinent nodes on the upper arm and the forearm, but in a complex model, this can be difficult. So we use “Absolute folder paths”.
Absolute folder path (and some useful tips)#
Place the cursor just before the semicolon in the line that reads AnyRefNode &UpperArmNode = ;
.
Open the model tree where the UpperArm folder should expand as shown below.
👉 Now Right-click the folder named “ElbowNode”, and select “Insert object name” from the context menu. This writes the full, absolute path of the node into the Elbow joint definition where you placed the cursor.
Repeat the process to expand the ForeArm segment and insert its ElbowNode in the line below to obtain this:
AnyRevoluteJoint Elbow = {
Axis = z;
AnyRefNode &UpperArmNode = Main.ArmModel.Segs.UpperArm.ElbowNode;
AnyRefNode &ForeArmNode = Main.ArmModel.Segs.ForeArm.ElbowNode;
}; // Elbow joint
It seems like everything is connected now, but we still get this annoying error message when we reload the model:
Model Warning: Study 'Main.ArmStudy' contains too few kinematic
constraints to be kinematically determinate.
The explanation is that we have created joints, but we have not specified the joint posture yet. The shoulder and elbow joints can still take any angular position, so there are two degrees of freedom whose kinematic states need to specified before AnyBody can solve for the mechanism’s kinematics. This is taken care of by kinematic drivers. They are one of the subjects of Lesson 4: Imparting Movement with Drivers.