Kinematics Library
A serial-chain kinematics library designed to close the gap between the math on the whiteboard and code that actually runs on a robot — with symbolic expressions, batched numerics, and a live 3D visualizer built in from the start.
View on GitHubWhy I built this
The problem with existing tools
Most kinematics libraries are either too academic (dense notation, no interactivity) or too hardware-specific (tied to ROS, a particular robot brand, or a GUI that hides what's actually happening). When you're learning, neither helps.
This library starts from first principles — SE(3) rigid-body math, Rodrigues rotation, homogeneous transforms — and builds up to a usable API without burying the math. The goal is that reading the source code teaches you something, not just using it.
Architecture
How the library is structured
The library is split into a math core and a visualization layer, kept deliberately separate so you can use the FK engine without pulling in Plotly or Dash.
Modules
What's in the codebase
The math engine. SerialChain, joint definitions
(R / P), SE(3) helpers (Rodrigues,
skew-symmetric, homogeneous transforms), numeric FK, batched FK,
symbolic FK, and the geometric Jacobian — all cached and
design-change aware.
Computes all 3D geometry for the current joint configuration — joint cylinder positions, rotation-axis arrows, arc sweep indicators, and home-pose ghost. Decoupled from rendering so it can be tested without Plotly.
Batched workspace sampler: draws random joint configs, runs vectorized FK over all samples at once, and optionally wraps the result in a SciPy convex hull to produce a solid mesh.
Low-level geometry helpers: cylinder mesh (verts + triangle indices), orthonormal frame construction from an arbitrary axis, and cheap 3D arrowheads as line segments.
Reusable Dash UI components: slider + numeric-input pairs with live value pills, workspace toggle checkbox, and the overall two-panel layout (3D viewport left, controls right).
Top-level preview(chain) entry point. Builds the
Dash app, registers reactive callbacks for slider sync and
graph updates via Plotly Patch, and launches the
local server.
Under the hood
Technical highlights
fk_ee_batch() propagates N configurations simultaneously
using np.einsum — workspace sampling over 40,000 configs
runs in under a second.
Patch for partial figure
updates — only changed traces are re-sent, keeping the 3D view smooth
while dragging sliders.
Usage
Code examples
Defining a 3-DOF arm and running forward kinematics:
import sympy as sp from robot_sketch import SerialChain, R, P, Point # Symbolic link lengths — fix them later L1, L2, L3 = sp.symbols("L1 L2 L3", positive=True) chain = SerialChain( joints=[ R("z", at=(0, 0, 0), q="q1"), # base rotation R("y", at=(L1, 0, 0), q="q2"), # shoulder P("z", at=(L2, 0, 0), q="q3"), # extending prismatic ], ee=(L3, 0, 0), ) design = {L1: 0.4, L2: 0.3, L3: 0.2} q = {"q1": 0.5, "q2": -0.3, "q3": 0.1} # All joint origins + EE as (N, 3) array pts = chain.fk_points(design, q, include_ee=True) print(pts) # shape: (4, 3)
Getting the symbolic Jacobian — useful for IK or singularity analysis:
# Symbolic EE position (cached after first call) p_ee = chain.fk_ee_symbolic() print(p_ee) # SymPy Matrix (3×1) in terms of q1, q2, q3, L1, L2, L3 # Geometric Jacobian — ∂p_ee / ∂q J = chain.jacobian_symbolic() print(J) # SymPy Matrix (3×3) # Substitute numbers to evaluate at a config J_num = J.subs({**design, **{sp.Symbol("q1"): 0.5, sp.Symbol("q2"): -0.3, sp.Symbol("q3"): 0.1}})
Sampling the reachable workspace and launching the interactive visualizer:
from robot_sketch.preview_plotly_modular import preview # Opens a Dash app at localhost:8050 preview( chain, init_design=design, revolute_range_deg=(-180, 180), prismatic_range=(-0.5, 0.5), reachable_cloud_samples=40_000, # batched FK, <1 s show_reachable_cloud=True, )
Roadmap
What's built, what's next
SerialChain