|
You are not likely to have any sort of idea what a kinematic measure is. Don't worry - you're not supposed to know about it. The concept was invented by AnyBody Technology as a way of describing dimensions in a kinematic model that you might want to get information about or control with drivers. A joint angle or a distance between two points are examples of kinematic measures. The position of the center of gravity of the entire model or a subset of its segments are other examples.
If you define a kinematic measure in your model, then you can study its development. But more importantly you can control it. You can add a driver to a kinematic measure, and that way control the movement of the mechanism. Such a driver can be added even when the measure is a less tangible quantity like the collective center of gravity that is not attached to a particular segment.
Joints can be understood as kinematic measures equipped with drivers. For instance, a spherical joint is a distance between two points on two different segments that is driven to be zero. This means that, using kinematic measures, you can define types of joints that are not available as predefined objects in AnyScript.
Do you remember the simple arm example of the "Getting Started with AnyScript" tutorial? That was a 2-D model of an arm where we produced the movement by driving the angles of the shoulder and elbow joints directly.

But let us imagine that we wanted the hand to reach out an grab something at a specific position. It would probably be difficult to figure out precisely how to drive the two joint angles to put the hand in the position we wanted it to attain. Instead, we would want to be able to put the hand (actually the wrist since this simple model has no hand) directly at the desired position in space and have the elbow and shoulder joints follow implicitly. This is where the kinematic measures come into play. Let's start off with the model just about where we left in the "Getting Started with AnyScript" tutorial. Please click here to download the necessary file, and save it in some working directory on your own hard disk.
Let us try initially to create a kinematic measure that will allow us to track the movement of the wrist. To honor the folder structure of the model we shall create a new folder for the kinematic measure. Let's place it just below the Jnts folder to reflect the kinship between kinematic measures and joints:
}; // Jnts folder
AnyFolder KinematicMeasures = {
AnyKinLinear WristPos = {
// These are the nodes that the measure refers to
AnyFixedRefFrame &Ground = Main.ArmModel.GlobalRef;
AnyRefNode &UpperArmNode = Main.ArmModel.Segs.LowerArm.HandNode;
Ref = 0;
};
}; // KinematicMeasures
An AnyKinLinear is a kinematic measure that gauges the spatial vector between two points. The line Ref = 0 means that the coordinates of this linear distance are measured in the coordinate system first of the measure's end points which in this case happens to be the global reference frame. For other options, please refer to the Reference Manual.
So far, we have just added a measure that allows us to track the movement of the hand, but it is still driven by the joint drivers as before. Let's investigate what we have. Load the model and run a KinematicAnalysis or an InverseDynamicAnalysis, and subsequently open a ChartFX view.
Expanding the tree though Main.ArmModelStudy.Output.Model.KinematicMeasures.WristPos will give you the options shown to the below.

Click Pos, and you will get three graphs tracking the x, y, and z components of the WristPos kinematic measure.

The z component (blue curve) of the measure remains zero throughout the movement because the model is two-dimensional. The top curve (red) is the x component, and the bottom curve (green) is the y component.
Now comes the beauty of kinematic measures: Rather than just observing them, you can actually drive them!
We shall replace the existing drivers on the shoulder and elbow joints by drivers on the x and y components of the WristPos kinematic measure.
We need to remove the existing elbow and shoulder drivers to avoid kinematic redundancy. You can enclose the drivers in comment characters /* */, or you can simply erase them, leaving you with an empty drivers folder:
AnyFolder Drivers = {
}; // Drivers folder
The next step is to fill drivers for the WristPos measure into the Drivers folder. We initially make an empty skeleton. Notice that we are using an AnyKinSimpleDriver here. If you had measured the hand position by a motion tracking technology and wanted to reproduce the movement, you would probably want to use an interpolation driver instead.
AnyFolder Drivers = {
AnyKinEqSimpleDriver HandMotionXY = {
};
}; // Drivers folder
We can now fill contents into the HandMotionXY driver that will guide the hand through space:
AnyFolder Drivers = {
AnyKinEqSimpleDriver HandMotionXY = {
AnyKinLinear &Jnt = ..KinematicMeasures.WristPos;
MeasureOrganizer = {0,1};
DriverPos = {0.4,-0.5};
DriverVel = {0.2,0.5};
DriverAcc = {0.0,0.0};
Reaction.Type = {Off,Off}; // The muscles must do the work
};
}; // Drivers folder
The first of the red lines above refers to the WristPos kinematic measure. It simply specifies that this is the measure we want to drive. Notice, however that this measure has three components, namely the x, y and z coordinates. But we only want to drive two of them. The MeasureOrganizer handles that problem. It lines up the coordinates of the measure in a row for driving. MeasureOrganizer = {0,1} means that the vectors of driver specifications, such as DriverPos and DriverVel, refer to the x (number 0) and y (number 1) coordinates of the measure.
The values we suggest for DriverPos and DriverVel have been found by inspection of the graphs depicted above showing the development of the measure coordinates when we used the shoulder and elbow drivers. This is good practice because it is so easy to specify wrist positions that the arm cannot reach and thereby provoke a kinematic incompatibility that may be difficult to find in more complex cases.
To conclude, the special feature about kinematic measures is that you can drive them. In AnyBody, you can drive anything that you can measure, and this is really a unique facility. If something went wrong for you along the way, you can download a commented version of the final result here.
Driving models by motion capture marker trajectories
One very common use of kinematic measures is to impose a measured movement on a model. The measurement can for instance be in terms of optical marker trajectories or joint angles measured by goniometers. In this tutorial we shall focus on optical markers.
Marker trajectories are recorded by a motion capture (MOCAP) system. It comprises multiple synchronized video cameras observing a set of spherical markers attached to a moving body and software to compute the marker coordinates in space by triangulation of the simultaneous pictures recorded by the cameras. Different types of MOCAP systems produce output on different formats. AnyBody needs to read the marker trajectories from text files on the following format:
Time x y z 0.00000000000 0.84147098599 -0.54030230550 0.00000000000 0.00100100100 0.84142823805 -0.54036887714 0.00000000000 0.00200200200 0.84129997953 -0.54056856433 0.00000000000 0.00300300300 0.84108613608 -0.54090127577 0.00000000000 0.00400400400 0.84078658373 -0.54136685730 0.00000000000 0.00500500500 0.84040114917 -0.54196509307 0.00000000000 0.00600600600 0.83992961007 -0.54269570542 0.00000000000 0.00700700700 0.83937169543 -0.54355835492 0.00000000000 0.00800800800 0.83872708599 -0.54455264021 0.00000000000 0.00900900900 0.83799541478 -0.54567809793 0.00000000000 0.01001001000 0.83717626771 -0.54693420265 0.00000000000 0.01101101100 0.83626918423 -0.54832036673 0.00000000000 0.01201201200 0.83527365810 -0.54983594020 0.00000000000 0.01301301300 0.83418913820 -0.55148021065 0.00000000000 0.01401401400 0.83301408734 -0.55325177839 0.00000000000 0.01501501500 0.83174961018 -0.55515095782 0.00000000000 0.01601601600 0.83039421700 -0.55717631355 0.00000000000 0.01701701700 0.82894718724 -0.55932688186 0.00000000000 0.01801801800 0.82740776078 -0.56160163587 0.00000000000 0.01901901900 0.82577513921 -0.56399948535 0.00000000000 0.02002002000 0.82404848719 -0.56651927660 0.00000000000
The first column is time, and the subsequent columns are values of a kinematic measure. In general you can have any number of columns, but in the case of marker trajectories the number of columns will be three, corresponding to the x, y and z coordinates of the marker through space. The columns are separated by spaces or tabs. The first line in the file is ignored if it does not contain numbers, so it can be used for a header as is shown here.
Driving a pendulum
We need a model to work on. Please download and save the file mocap.any in a working directory. Load the model into AnyBody and open a new model view. You should see a vertical segment with a point in each end. It is in fact a pendulum model linked to the global reference frame by a revolute joint at its upper end point. We use this example because it is very simple and has a remote similarity with a human limb.
The pendulum only has one single degree of freedom. Let us presume that we have conducted a MOCAP experiment that has tracked the movement of the end point of the pendulum and that we have saved the marker trajectories on the file p1.txt. Please download the file and save it in the same directory as mocap.any.
The mocap.any model that you have downloaded cannot analyze because it is lacking a movement driver. We are going to use the MOCAP trajectory saved in p1.txt to drive the model. The straightforward way to do this would be to define a linear kinematic measure between the laboratory origin and the end point of the pendulum and subsequently drive this measure by the measured trajectory. Let us do precisely that. Place the cursor in the mocap.any file just below the Joint definition, click the Classes tab in the tree view on the left hand side of the editor window, locate the AnyKinLinear class, right-click it, and insert a class template:
AnyRevoluteJoint Joint = {
AnyRefFrame &Ground = .GlobalRef;
AnyRefFrame &Pendulum = .Pendulum.Origin;
};
AnyKinLinear <ObjectName>
{
//Ref = -1;
AnyRefFrame &<Insert name0> = <Insert object reference (or full object definition)>;
//AnyRefFrame &<Insert name1> = <Insert object reference (or full object definition)>;
};
}; // MyModel
Next we fill in the necessary information to point to the correct elements in the model:
AnyKinLinear P1Lin = {
//Ref = -1;
AnyRefFrame &LabOrigin = .GlobalRef;
AnyRefFrame &P1 = .Pendulum.P1;
};
We now have a linear measure that is in fact a three-dimensional vector from the origin of the global reference frame to the end point of the pendulum. But so far we are only measuring the vector. The next step is to actually drive it by the measured trajectory. We do so by introducing an interpolation driver. Place the cursor right below the linear measure, go to the class tree, locate the AnyKinEqInterPolDriver class, and insert a class template in the model:
AnyKinLinear P1Lin = {
//Ref = -1;
AnyRefFrame &LabOrigin = .GlobalRef;
AnyRefFrame &P1 = .Pendulum.P1;
};
AnyKinEqInterPolDriver <ObjectName>
{
//MeasureOrganizer = ;
Type = ;
//BsplineOrder = 4;
//T = ;
//Data = ;
//FileName = "";
//AnyKinMeasure &<Insert name0> = <Insert object reference
(or full object definition)>; You can make any number of these objects!
};
The interpolation driver allows you to either type the vector of interpolated values directly into the AnyScript file, or to read the data off an external file. It would be very messy to have all the MOCAP'ed data directly in the AnyScript model, so we opt for reading them directly from the P1.any file. This is done by the following changes:
AnyKinEqInterPolDriver P1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P1.txt";
//AnyKinMeasure &<Insert name0> = <Insert object reference
(or full object definition)>; You can make any number of these objects!
};
Notice that we have selected a Bspline interpolation and that the order is set to 4. A Bspline is a smooth approximation of the data points, so AnyBody is converting the discrete measured values to a continuous interpolation function. The advantage of this is that you do not have to perform the analysis precisely at the sampled times in the MOCAP experiment. You can do more or less time steps exactly as you like.
The final step is to specify what the driver should drive, i.e. point at the linear measure we defined before:
AnyKinEqInterPolDriver P1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P1.txt";
AnyKinMeasure &Lin = .P1Lin;
};
The model now loads, but it also produces the following warning:
The model may be statically indeterminate. There are 8 reactions and only 6 rigid body degrees of freedom.
The message warns you that there are too many elements in the model that can provide reaction forces. In general, we MOCAP markers are just registering the positions in space. They do not provide any sort of forces to realize this movement. So we need to switch the reaction forces of the new driver off like this:
AnyKinEqInterPolDriver P1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P1.txt";
AnyKinMeasure &Lin = .P1Lin;
Reaction.Type = {Off, Off, Off};
};
Try loading the model again and run the Kinematic Analysis (In this tutorial we do not use the inverse dynamic analysis). Most likely it will not work. You will get the following error message:
Model is kinematically over-constrained : Position analysis failed : 1 unsolvable constraint(s) found
The reason will be obvious if you instead run the ModelInformation operation. It produces a whole lot of output in the message window among which you find:
1) List of segments: 0: Main.MyModel.Pendulum
Total number of rigid-body d.o.f.: 6 ------------------------------------------------------------- 2) List of joints and kinematic constraints: Joints: 0: Main.MyModel.Joint (5constr., 1coords.) Total number of joint coordinates: 1
Drivers: 0: Main.MyModel.P1Driver (3constr.)
Other: - none!
Total number of constraints: Joints: 5 Drivers: 3 Other: 0 Total: 8
The model appears to have six degrees of freedom (one segment in space has six degrees of freedom) but eight kinematic constraints. Therefore, it is over-determinate. The reason for this problem is that the pendulum only has one degree of freedom when the constraints of the hinge have been subtracted, but we have added three drivers to it by means of the three coordinates of the linear measure. To solve this problem we need to select one of the three coordinates for driving and leave the other two coordinates to their own devices. This way we avoid driving more degrees of freedom than the model actually has. So, which one should we choose? Does it matter or is it enough that we have one driver for the one degree of freedom? Unfortunately it does indeed matter. The coordinate we choose to drive should be as descriptive as possible for the movement of the mechanism. Let us look at the first few lines of the P1.any file:
Time x y z 0.00000000000 0.84147098599 -0.54030230550 0.00000000000 0.00100100100 0.84142823805 -0.54036887714 0.00000000000 0.00200200200 0.84129997953 -0.54056856433 0.00000000000
The movement is basically in two dimensions, so the z coordinate is constantly zero. This coordinate does not provide any information about the oscillating movement of the pendulum, so it cannot be used. The x and y directions will work ? perhaps ? but the x coordinate seems to be the better choice given the pendulum?s typical oscillating movement.
There are at least to ways to only drive the pendulum by the x coordinate. One is obvious and the other is smart. We shall begin with the obvious way for instructional purposes and switch to the smart way afterwards. To directly drive just one coordinate we need a driver file with just one column of data in addition to the time column. Please download this version: p1x.txt and save it in the same directory as the other files of the model. Then make the following changes:
AnyKinEqInterPolDriver P1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P1x.txt";
AnyKinMeasure &Lin = .P1Lin;
MeasureOrganizer = {0};
Reaction.Type = {Off};
};
The interpolation driver now uses the new file with just the x coordinates. The MeasureOrganizer is a property you can use to select the pertinent components of the kinematic measure you are driving. The measure returns an {x,y,z} vector, and MeasureOrganizer = {0} means that we are picking only the first component (number 0 because all numbers begin with zero in AnyBody) to drive. Finally, since we are only driving one coordinate, the Reaction.Type vector should now only have one component.
Load the model again, and run the kinematic analysis. You should now see it moving from side to side as you would expect a pendulum to do. So far so good!
Let us briefly review what we have learned so far: - Models can be driven by MOCAP data
- You need to drive as many degrees of freedom as the model has ? no more and no less.
- You must choose the coordinates to drive carefully.
There are in fact more issues to consider and smarter ways to create the drivers, and these are the subjects of the next section.
Local and global coordinates
Before we begin, here?s a link to a functioning model in case you had trouble with the preceding section: mocap2.any.
We begin this section with a question: Will this also work if the oscillations are larger? We can try it very easily. Please download and save this file: p2x.txt. Then make the following change:
AnyKinEqInterPolDriver P1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P2x.txt";
AnyKinMeasure &Lin = .P1Lin;
MeasureOrganizer = {0};
Reaction.Type = {Off};
};
Run the kinematic analysis again. You will see the pendulum moving in a non-pendulum-like fashion. More precisely the pendulum makes an additional cycle at each end of its primary movement. This was not the movement that was motion captured, so the analysis is in fact wrong.
The reason for the problem is that the global x direction does not determine the movement very well when the pendulum is close to horizontal. Here, driving in the y direction would be much better.
The elegant solution to the problem is to change coordinate system of the driver. If the driver works in the pendulum coordinate system rather than the global coordinate system, then the x direction will be tangential to the path of the pendulum regardless of which direction the pendulum has. But before we can make that shift, we shall implement the smart way of picking a degree of freedom to drive that we promised in the preceding section. The idea is based on definition of a new segment representing the MOCAP marker:
AnyKinEqInterPolDriver P1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P2x.txt";
AnyKinMeasure &Lin = .P1Lin;
MeasureOrganizer = {0};
Reaction.Type = {Off};
};
AnySeg M1 = {
Mass = 0;
Jii = {0, 0, 0}/10;
};
The new segment has no mass and no rotational inertia. The first thing to do is to lock its rotation, and the most compact way of doing it is this:
AnySeg M1 = {
Mass = 0;
Jii = {0, 0, 0}/10;
};
AnyKinEq RotLock = {
AnyKinRotational rot = {
Type = RotAxesAngles;
AnyRefFrame &ground = ..GlobalRef;
AnyRefFrame &Marker = ..M1;
};
};
Now the marker can only translate, and we shall drive it in all three translations by means of the original MOCAP'ed data:
AnyKinLinear M1Lin = {
//Ref = -1;
AnyRefFrame &LabOrigin = .GlobalRef;
AnyRefFrame &M1 = .M1;
};
AnyKinEqInterPolDriver M1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P1.txt";
AnyKinMeasure &Lin = .M1Lin;
// MeasureOrganizer = {0};
Reaction.Type = {On, On, On};
};
There are several things to notice here: We have created a new linear measure between the laboratory and the marker segment, M1. Then we have modified the interpolation driver pretty much back to what it was when we first defined it with the exception that it now drives M1 instead of the pendulum, and it does so using all three coordinates. Notice also that the reactions are on. This is because when we start making kinetic analysis, we cannot have a segment floating freely in the air with nothing to keep it in place.
Now all that is missing is to create a link between M1 and the pendulum:
AnyKinEqInterPolDriver M1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P1.txt";
AnyKinMeasure &Lin = .M1Lin;
// MeasureOrganizer = {0};
Reaction.Type = {On, On, On};
};
AnyKinEq MarkerBodyConstraint = {
AnyKinLinear lin = {
AnyRefFrame &Marker = ..M1;
AnyRefFrame &Body = ..Pendulum.P1;
};
MeasureOrganizer = {0};
};
This brings us exactly back to where we started. If you have done everything right, you now have a model that oscillates like it did at the end of the preceding section. So, why did we go to all that trouble for nothing? Well, the new approach has two advantages: - You do not have to edit columns out of the marker trajectory file if you only want to drive one degree of freedom. The degrees of freedom to drive are controlled by the link between the marker and the corresponding point on the body.
- It allows you to drive the body using local instead of global coordinates.
The second advantage is what will justify the trouble of setting the system up; it is going to allow us to drive the pendulum correctly also for larger displacements. As the picture indicates, the pendulum has its own local reference frame that moves with it. As you can se in the figure, this reference frame has its x axis perpendicular to the length axis of the pendulum and therefore directed along the movement, no matter which position the pendulum has. So, even when the pendulum has large oscillations, the local x direction remains a good direction for driving the pendulum.

Let us try to do precisely that. Please download and save this file in the same directory as the model: P2.txt. Then make the following change:
AnyKinEqInterPolDriver M1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P2.txt";
AnyKinMeasure &Lin = .M1Lin;
// MeasureOrganizer = {0};
Reaction.Type = {On, On, On};
};
The model now uses the new P2.txt file containing all three coordinates to drive the movement. If you reload the model by pressing F7 and re-run the kinematic analysis, then you should see the same erroneous pendulum movement as before. So, here comes the trick: We are going to switch the constraint between the marker and the pendulum to work in the pendulum?s local coordinate system:
AnyKinEq MarkerBodyConstraint = {
AnyKinLinear lin = {
AnyRefFrame &Marker = ..M1;
AnyRefFrame &Body = ..Pendulum.P1;
Ref = 1;
};
MeasureOrganizer = {0};
};
This is a very simple little trick. The line Ref = 1 simply states that the linear measure between the marker and the pendulum point should be measured in the local coordinate system of the pendulum. Every linear measure has a Ref specification. If you do not set it explicitly, the system initiates Ref to -1, which means the global reference frame. After the global reference frame, the reference frames of the measure components are listed in numerical order starting from zero. Since M1 is the first measure component, its reference frame would be numbered zero. So Ref = 0 would refer the linear measure to the marker coordinate system. Pendulum.P1 is the second component and is therefore numbered 1. Please load the model again (F7) and re-run the kinematic analysis (F5). The movement should now be correct. Please notice that even though we are driving in the local pendulum coordinate system we did not have to make any manual conversion of the MOCAP marker data. The MOCAP marker is still driven in the global laboratory coordinate system. It is only the measure between the marker and the body that has been converted to local coordinates, and this is handled automatically by AnyBody when you make the switch of reference, Ref = 1, to the local system.
The extremities of living creatures work much like the pendulum we have studied here in the sense that they twist and turn in the global reference frame making it tricky to drive the segments in global coordinates. Therefore it is usually a major advantage to drive models with MOCAP data using local reference frames.
If you have trouble getting the model to work, then you can download a workable copy here: mocap3.any. The topic of the next lesson is noise and accuracy, which is a major pitfall of driving models by MOCAP data.
Noise and accuracy
Nothing man-made is completely perfect and this also goes for experimental data. Motion capture data is infested with a range of different errors such as soft tissue artifacts, marker placement inaccuracy and random noise. The data we have used so far is very accurate because it has been manufactured artificially and stored with 10 decimals in the marker trajectory files we have used. So if we plot the accelerations of the pendulum marker point, we get the following nice set of curves:

Notice that the accelerations are between approximately -500 and 600.
Now, let us introduce some random noise. An easy way to do so is to simply truncate the decimals off the columns in the marker trajectory file. Please download this file, where the numbers only have three decimals: P2trunc.txt. Then make the following change in the model:
AnyKinEqInterPolDriver M1Driver = {
Type = Bspline;
BsplineOrder = 4;
FileName = "P2trunc.txt";
AnyKinMeasure &Lin = .M1Lin;
// MeasureOrganizer = {0};
Reaction.Type = {On, On, On};
};
Please reload (F7) the model and re-run (F5) the kinematic analysis. The pendulum appears to move as it did before. If we investigate the positions of the driven point, then we get the following:

This appears to work very nicely. Plotting velocities produces this nice result:

Still, there is not much indication that anything might be wrong with the result, except perhaps a tiny hint of visible noise near the turning points of the graphs. Finally, let us study the accelerations:

Oops, something appears to have gone completely wrong here. The accelerations are noisy and several times larger than before. What could be the problem? The problem is actually, that the tiny amount of random noise we introduced by truncating some of the decimals from the marker trajectory gets amplified by an order of magnitude for each differentiation. The noise is indistinguishable on the position, hardly detectable on the velocities, but very large on the accelerations. This is a significant problem because the inertia forces in a mechanical system depend on the accelerations, so the noise will completely dominate the computation of forces in the system.
AnyBody interpolates the data by a smooth spline interpolation, and it is actually possible to dampen the noise somewhat by increasing the order of the spline interpolation and/or downsampling the data. Especially the latter is a good choice because most MOCAP systems sample at a much higher frequency than necessary for most movements. However, the best way to solve the problem is to low pass filter the data before sending them to AnyBody and making sure that the data is stored in the text file with as many decimals as possible. This will get rid of most of the high frequency noise in the MOCAP data. Furthermore, whenever a model is driven by MCOAP data it is advisable to check that accelerations are not oscillating more than expected and that accelerations of body parts are not higher than expected. Distal body parts are likely to sustain more acceleration than proximal parts.
The last lesson in the tutorial on mechanical elements is Lesson 5: Forces.
|