Natural Coordinates
Natural Coordinates is the builtin coordinate formulations used in Rible for modeling rigid bodies. It is implemented as a submodule NCF living in src/coordinates/natural_coordinates, and is wired through multiple dispatch to the mass matrix, constraint equations, and joint solving workflows.
Core features: This approach describes pose directly using point coordinates and direction vectors. It features a non-singular orientation description and a constant mass matrix.
Basic Representation
In the natural coordinates framework, the state of a rigid body is described by a basic point and direction vectors:
2D bar representation: 1 base point + 1 base vector
2D rigid body representation: 1 base point + 2 base vectors
3D rigid body representation: 1 base point + 3 base vectors
Variants, such as 2 base points + 2 base vectors, are also fully supported to meet special needs.
Correspondingly, NC is a unified coordinate structure; the specific dimension and degrees of freedom are determined by the constructor.
Module Structure and Code Entry Points
The main files under src/coordinates/natural_coordinates are:
types.jl: definesNCand underlying data structures.constructors.jl: defines common constructors, such asNC2D1P1V,NC1P2V,NC1P3V.functions.jl: coordinate mappings and Jacobian calculations, e.g.,to_position,to_position_jacobian.constraints.jl: intrinsic constraints and Jacobian implementations, core interfacescstr_function!,cstr_jacobian!.mass_matrix.jl: mass matrix construction, e.g.,make_M.joints.jl: joint caches, constraint violations, and Jacobian assembly.
Common Usage
1 Construct Coordinate Objects
Constructors of natural coordinates expect at least a basic point ri and optionally other basic points and base vectors like u, all expressed in the global frame.
# 2D bar: 1 point + 1 vector
nc_bar_2d = NC2D1P1V(ri, u)
# 2D rigid body: 1 point + 2 vectors
nc_body_2d = NC1P2V(ri)
# 3D rigid body: 1 point + 3 vectors
nc_body_3d = NC1P3V(ri)2 Position Mapping and Jacobian
# Position of local point c in global frame
pos = to_position(nc_body_3d, q, c)
# Jacobian with respect to coordinates
Jpos = to_position_jacobian(nc_body_3d, q, c)3 Constraint Values and Constraint Jacobians
constraints.jl core interface uses in-place update variants.
ret_c = zeros(nc_num_constraints)
ret_J = zeros(nc_num_constraints, length(q))
cstr_function!(ret_c, nc_body_3d, q)
cstr_jacobian!(ret_J, nc_body_3d, similar(ret_J), q)4 Mass Matrix
Natural coordinates produce a constant mass matrix for a rigid body.
M = make_M(nc_body_3d, mass, inertia, mass_center)Joint Development Guide
This section targets developers who need to add new joint types. Rible's joint implementation uses a "base constraint composition" strategy: first define the basic constraint types, then in src/joint/joints.jl’s get_joint_info combine masks to construct the specific joint.
Theoretical Foundation: Basic Constraints
In the joint module, all constraints are written in a unified "joint coordinates" form. Let
q_h: parent (hen) body coordinatesq_e: child (egg) body coordinatesq_j = [q_h; q_e]: concatenated joint coordinates
build_joint_cache aims to precompile geometric constraints into two matrix objects:
transformations: first-order (linear) term matriceshalves: list of quadratic form matrices (one per constraint isH_i)
Thus each constraint can be written as:
Where T_i is the i-th row of transformations, and H_i is halves[i]. violations stores constant offsets c_i for the reference configuration, which are subtracted at runtime to center the baseline at zero violation.
Linear vs quadratic meaning:
Linear constraint:
C_i(q_j)is linear inq_j; the Jacobian is constant.Quadratic constraint:
C_i(q_j)is a quadratic polynomial inq_j; the Jacobian is linear inq_j, but the Hessian is constant.
Mask 1st Coincidence Constraint Type 1
Constraint that two points coincide:
In code this constraint mainly comes from the linear part of the point-mapping Jacobian concatenation J = [-C_h, C_e]:
Code index:
mask_1stMatrix contribution: added to
transformations = J[mask_1st, :]Constraint order: Linear (corresponding to
H_i = 0, onlyT_i q_jremains)
Mask 2nd Distance Constraint Type 4
Constraint that the distance between two points is a constant d:
These constraints are constructed in code via a quadratic form:
Code index:
mask_2ndMatrix contribution:
half_2nd[1] = J' * JConstraint order: Quadratic (only
q_j^T H_i q_j; the constant term is provided byviolations)
Mask 3rd Orthogonal Constraint I Type 3
Constraint that the direction vector is orthogonal to the relative displacement:
Its quadratic form is obtained by combining the direction-selection matrix with a Kronecker product:
Code index:
mask_3rdMatrix construction: generated via
kron(...)to createhalf_3rdConstraint order: Quadratic
Common uses: planar motion constraints, slider direction constraints
Mask 4th Orthogonal Constraint II Type 2
Constraint that two direction vectors are orthogonal:
This constraint is entirely formed from the orientation coordinates, i.e., a pure pose-coupling term:
Code index:
mask_4thMatrix construction: built by combining orientation bases with
kron(...)to formhalf_4thConstraint order: Quadratic
Common uses: shaft alignment, universal joint constraint composition
Implementation Details and Performance
Cache Mechanism: ApparatusCache
build_joint_cache returns cache and violations at initialization. The cache contains all runtime sparse structures:
transformations: linear constraint matriceshalves: list of quadratic form matriceshessians: each constraint’s symmetric HessianH_i + H_i^Tjoint_q,joint_work,trf_work: in-place computation buffers to avoid allocations
This design ensures the runtime only performs value substitutions; the structure matrices are not rebuilt.
Key Functions in joints.jl
build_joint_cache: precomputetransformations,halves,hessiansaccording to the masks.get_joint_violations!: first computeq_j^T H_i q_j, then add the linear termT_i q_j, and finally subtractviolations.get_joint_jacobian!: assembleC_qusing the precomputedhessianswith the currentq_j.
Extending to New Joints
Extending to new joints typically does not require new underlying mathematical kernels. A recommended workflow:
In
src/joint/joints.jl, inget_joint_infodefine the target joint’s mask combination.Use the existing
mask_1stthroughmask_4thto compose the desired degrees-of-freedom constraint.Reuse
build_joint_cache,get_joint_violations!, andget_joint_jacobian!to hook into the solver.
This approach keeps new joints fully compatible with the existing solving pipeline and leverages the caching performance benefits.