Body plan¶
A body plan describes the kinematic tree QuickIK solves against – a robot's joints, or an animal's skeleton.
In QuickIK, the kinematic tree is defined using joints. Each joint can contain multiple degrees of freedom (DOFs). There are two types of DOFs: hinges that rotate, and slides that move translationally. For example, a 3-axis ball joint would have three hinge DOFs and no slide DOF.
All joints trace back to a single parent – the root of the kinematic tree. In principle, the definition of the root is arbitrary (you can say the whole human body stems from the left index fingertip if you wish), but practically it should be defined as a central body part like the pelvis or thorax. The root can be freely floating – useful when the body is a robot or an animal that can move around, or fixed – useful for fixed-base robotic arms.
In QuickIK, joints double as keypoints1 – the points on the body whose positions are recorded and used to constrain the state. If you need a keypoint in the middle of a body segment, add a "pseudo joint" where they keypoint is supposed to be with no associated DOFs.
Each joint has the following properties:
- A name.
- A parent joint (the parent of the root joint is
null). - A position offset and a rotation offset from its parent joint, representing the properties of the rigid-body link connecting them. Rotation offsets are specified in quaternions in wxyz format.
- A weight scaler that controls the scale2 of how hard the solver should try to minimize the mismatch of this joint's position. If some joints are intrinsically harder to measure (e.g., if they are usually occluded or embedded in soft tissues), it's useful to lower this number.
- A list of degrees of freedom (DOFs). The order of the DOFs is important, as 3D rotations do not commute.
Each DOF has the following properties:
- A type: can be
hingeorslide. - An axis: this is the rotational axis for hinge joints or translational axis for slide joints.
- A neutral value: the "natural" rotation angle or slide position the solver favors poses being closer to. For hinge DOFs, this is in radians; for slide DOFs, it's in whatever unit the joint's position offset uses.
- Optionally, the limits for the value of this DOF (angle for hinge joints, positions for slide joints). Same unit as the neutral value. Set to
nullif unbounded. - A weight scaler controlling the scale3 of how strongly the solver favors the neutral state defined above.
JSON body plan format¶
In QuickIK, the body plan is specified in JSON. An example JSON file is as follows.
JSON schema
A formal schema of the JSON format is available here. You can use it for formal syntax check with your IDE.
{
"fixed_base": false,
"joints": [
{
"name": "root",
"parent": null,
"offset_pos": [0.0, 0.0, 0.0],
"offset_quat": [1.0, 0.0, 0.0, 0.0],
"weight_scaler": 1.0,
"dofs": []
},
{
"name": "elbow",
"parent": "root",
"offset_pos": [1.0, 0.0, 0.0],
"offset_quat": [1.0, 0.0, 0.0, 0.0],
"weight_scaler": 1.0,
"dofs": [
{
"type": "hinge",
"axis": [0.0, 0.0, 1.0],
"neutral": 0.0,
"limits": [-3.0, 3.0],
"weight_scaler": 1.0
}
]
},
{
"name": "wrist",
"parent": "elbow",
"offset_pos": [1.0, 0.0, 0.0],
"offset_quat": [1.0, 0.0, 0.0, 0.0],
"weight_scaler": 1.0,
"dofs": []
}
],
"x-anything": [
"Keys starting with 'x-' are allowed at any level and are ignored.",
"They can be of any type and are useful for custom metadata/documentation."
]
}
-
This is merely a practical choice, as articulated joints are usually easier to track than arbitrary points on the body and indeed they are what's typically available in MoCap/pose estimation data. ↩
-
During inverse kinematics, the user can also supply a weight for each keypoint on a frame-to-frame basis (e.g., using the uncertainty measure of the pose estimation model on that particular frame). The final weight is the product of the weight supplied at runtime and this scaler. ↩
-
Upon initiating the inverse kinematics solver, the user can define a weight for the pull toward the neutral pose. Like the weight scaler for the joint's weight, the final weight toward the neutral value for each DOF is the product of the weight supplied at runtime and the scaler specified here in the body plan. ↩