Biomechanics with natural coordinates, in Python, forward and inverse approaches.
Install · Quickstart · Examples · Docs · Cite
Inverse kinematics of a lower-limb model, animated with pyorerun.
bioNC models a biomechanism in natural coordinates: each segment is described by twelve Cartesian parameters
| 🎯 Inverse kinematics | Frame-per-frame solvers: ipopt, sqpmethod (CasADi) and dik, a QP-based differential IK backed by proxsuite |
| ⚙️ Dynamics | Forward and inverse dynamics under rigid-body + joint constraints, with Lagrange multipliers |
| 🦴 Joints | Spherical, hinge, universal, free, weld, constant-length, sphere-on-plane, point/two-points-on-ellipsoid, ellipsoid-on-plane, with ground variants |
| 💪 Muscles | Muscle paths with via points, and muscle-driven pendulum examples |
| 🏋️ External forces | Force sets applied in global or local frames, at arbitrary application points |
| 📐 Model personalization | Build a model template from marker functions, then scale it on a C3D static trial |
| 🧮 Two backends | numpy for numerics, casadi for symbolics and optimal control |
| 🎬 Visualization | 3D animation of models, markers and forces through pyorerun |
From conda-forge (recommended)
conda install -c conda-forge bioncFrom source
pip install git+https://github.com/Ipuch/bioNC.gitFor development
git clone https://github.com/Ipuch/bioNC.git && cd bioNC
conda env create -f environment.yml # brings casadi, biorbd, ezc3d, pyorerun, proxsuite...
conda activate bionc
pip install -e .
pytest tests
biorbdandezc3dare not on PyPI, so the conda environment is the smoothest route for the full feature set.
Solve inverse kinematics on an existing model and read joint angles out of it:
import numpy as np
from pyomeca import Markers
from bionc import BiomechanicalModel, InverseKinematics, NaturalCoordinates
# 1. Load a model (see `examples/model_creation` to build one from your own data)
model = BiomechanicalModel.load("examples/models/lower_limb.nc")
# 2. Load experimental markers, ordered like the model expects them
markers = Markers.from_c3d("my_trial.c3d", usecols=model.marker_names_technical).to_numpy()[:3, :, :]
# 3. Solve, "dik" is the fast QP-based differential IK, "ipopt"/"sqpmethod" are also available
ik = InverseKinematics(model, markers)
Qopt = ik.solve(method="dik")
# 4. Inspect the solution
stats = ik.sol()
print("max marker residual:", np.max(stats["marker_residuals_norm"]))
print(model.natural_coordinates_to_joint_angles(NaturalCoordinates(Qopt[:, 0])))Then animate it:
from bionc.vizualization.pyorerun_interface import BioncModelNoMesh
from pyorerun import PhaseRerun, PyoMarkers
prr = PhaseRerun(t_span=np.linspace(0, 1, Qopt.shape[-1]))
prr.add_animated_model(
BioncModelNoMesh(model),
Qopt,
tracked_markers=PyoMarkers(data=markers, marker_names=model.marker_names_technical),
)
prr.rerun()Building your own model from a static trial
A model is described generically, axes and markers are functions of marker names, then collapsed onto real data. This is the scaling/personalization step:
from bionc import (
BiomechanicalModelTemplate, SegmentTemplate, NaturalSegmentTemplate,
MarkerTemplate, AxisTemplate, C3dData, JointType,
)
model = BiomechanicalModelTemplate()
hip_joint = lambda m, bio: MarkerTemplate.middle_of(m, bio, "RFWT", "LFWT")
model["PELVIS"] = SegmentTemplate(
natural_segment=NaturalSegmentTemplate(
# from the middle of the posterior iliac spines to the middle of the anterior ones
u_axis=AxisTemplate(
start=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RBWT", "LBWT"),
end=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RFWT", "LFWT"),
),
proximal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RBWT", "LBWT"),
distal_point=hip_joint,
w_axis=AxisTemplate(start="LFWT", end="RFWT"),
)
)
model["PELVIS"].add_marker(MarkerTemplate(name="RFWT", parent_name="PELVIS", is_technical=True))
# ... more markers, more segments ...
model.add_joint(name="hip", joint_type=JointType.SPHERICAL, parent="PELVIS", child="THIGH")
personalized_model = model.update(C3dData("statref.c3d")) # <- the scaling step
personalized_model.save("my_model.nc")The full, runnable version lives in
examples/model_creation/right_side_lower_limb.py,
and docs/model_scaling.md explains precisely what does and does not get
scaled from the data.
| Example | What it shows |
|---|---|
model_creation/ |
Lower limb, two-side lower limbs, upper limb and markerless models built from C3D data |
inverse_kinematics/ |
Solving IK on noisy markers, single-frame IK, solver comparison |
forward_dynamics/ |
Pendulums (simple, 3D, n-link, universal), actuated systems, dropping a box |
inverse_dynamics/ |
Joint torques of a three-link pendulum |
muscles/ |
Pendulum and double pendulum driven by muscles with via points |
play_with_joints/ |
Constant-length, point-on-ellipsoid, plane-on-ellipsoid and scapulothoracic joints |
knee_parallel_mechanism/ |
A parallel knee mechanism (Feikes' model) |
transformation_matrix/ |
Comparing the |
| Backend | Import | Use it for |
|---|---|---|
| numpy | from bionc import ... (default) or bionc.bionc_numpy |
Numerical simulation, inverse kinematics, dynamics |
| casadi | from bionc import bionc_casadi |
Symbolic expressions, gradients, optimal control problems |
Both expose the same API, so a model written for one reads the same in the other.
The
where
-
$u$ , the proximal vector, in the global coordinate system, -
$r_p$ , the position of the proximal point, in the global coordinate system, -
$r_d$ , the position of the distal point, in the global coordinate system, -
$w$ , the distal vector, in the global coordinate system.
The generalized coordinates of the whole biomechanism are the concatenation of all body coordinates:
To rigidify the segments and articulate them, two families of holonomic constraints are used,
rigid-body constraints
Contributions, bug reports and ideas are welcome, start with the
contributing guide and the
open issues (look for good first issue).
black . -l120 # format
pytest tests # testPierre Puchaud, Alexandre Naaim, & Anais Chaumeil. Ipuch/bioNC. Zenodo.
@software{puchaud_bionc,
author = {Pierre Puchaud and Alexandre Naaim and Anais Chaumeil},
title = {Ipuch/bioNC},
year = {2025},
publisher = {Zenodo},
version = {0.11.0},
doi = {10.5281/zenodo.14976752},
url = {https://github.com/Ipuch/bioNC}
}This work is an implementation mostly inspired by the work of Dr. Raphaël Dumas (Senior Researcher at IFSTTAR – University of Lyon) on three-dimensional multi-body modeling of the human musculoskeletal system.
Released under the MIT license.