LinkagePy mechanism simulation and motion control panel running on dark grid
During sine wave playback, joint movements and end-effector traces are updated in real time.

From an inverse kinematics demo to an engineering workplace

LinkagePy didn't start out as a single-purpose IK demo that dragged a target point across the screen and watched the movement of a few arms. My goal was to develop an engineering tool with which I could experiment with all the basic layers of a two-dimensional mechanism problem in the same place: establishing the geometry and connection topology, checking the validity of the mechanism, calculating positions from known joint values, solving for the joint values ​​required for an end-effector target, and analyzing the resulting motion in the time domain.

Therefore, at the heart of the product is not just a canvas, but a reusable kinematic core. The interface is a work surface built on this core. When the user edits the mechanism, the data model changes; the solver generates new positions from the same model; animation and analysis layers also visualize the same result. Thus, each line and control appearing on the screen corresponds to defined engineering data rather than a separate visual number.

Mechanism model and forward kinematics

In the core, I distinguished the concepts of point, joint, connection and mechanism. The joint model carries position, type, angle, displacement, stability and movement limits; The link model defines the physical connection and length between two joints. The Mechanism class keeps these elements as a graph, determines ground joints, queries the connections, and checks whether the topology is valid before solving. The degree of freedom calculation is also done using the same model as the Grübler approach.

The Forward Kinematics solver applies a breadth-first traversal starting from fixed ground joints. At each connection, the type of the next joint is read: for the revolute joint, the new end point is calculated from the connection length and angle, for the prismatic joint, the effective length is calculated from the defined axis and displacement. The result is a solution map that maps all joint IDs to two-dimensional Point2D locations. This map becomes the common, deterministic data source of other layers, from canvas drawing to speed analysis.

Joints drawer, grid canvas and properties panel in LinkagePy mechanism editor
The left panel shows the topology, the right panel shows the numerical values ​​of the selected joint; The middle canvas shows the geometric equivalent of the mechanism.

Inverse kinematics with Jacobian and Damped Least Squares

In the Inverse Kinematics layer, the goal is to find the joint angles that will move the selected end-effector joint to the given X–Y coordinate. In each iteration, the solver first calculates the current tip position with forward kinematics, measures the error vector between it and the target, and completes convergence when the error falls below the tolerance. The Jacobian matrix is ​​obtained by the numerical finite difference method, which changes each joint angle by a small epsilon and measures the response of the end point.

In the update step, I use Damped Least Squares instead of the classic pseudoinverse: `Δθ = Jᵀ (J Jᵀ + λ²I)⁻¹ e`. The damping term keeps the matrix solution more stable when the mechanism reaches a singular or near-singular configuration. Each calculated angle is passed through the minimum and maximum limits of the relevant joint; Maximum iteration, error tolerance, dumping and Jacobian epsilon values ​​are the explicit parameters of the solver. At the end of the solution, the convergence status, number of iterations and the last error are stored. This approach gave me not only a working target tracker, but also a controlled experimental environment where I could observe behavior in unsuccessful or difficult geometries.

Interactive studio and editing flow

The desktop interface I developed with PySide6 is divided into three main working layers. The dark grid canvas in the middle draws the mechanism, joints, end-effector target and movement traces. The Joints drawer allows searching, selecting, adding or removing elements in the mechanism. The Properties drawer opens the selected joint's X-Y position, angle, minimum-maximum limits and connected link information for numerical editing. Project name, view panels, settings and registration in the top bar; At the bottom are all the time and target parameters of the simulation.

I handled the editing commands through a command stack that can run undo/redo. Canvas interaction and model mutation are not lumped into the same class; The user action becomes a command, the command becomes a controlled change in mechanism state. The save and load layer mechanism serializes the data as JSON. So the app doesn't just play prepared samples; It offers real editor behavior where new mechanisms can be created, closed and reopened.

Joint angle, velocity, acceleration and IK error graphs in LinkagePy analysis panel
By monitoring angle, velocity, acceleration and IK error on the same time axis, mechanism behavior is moved from the visual result to numerical analysis.

Motion generation, waypoints and time history analysis

Animation Controller runs the mechanism in different time-based scenarios instead of just driving it to a fixed target. While Sine Wave mode produces periodic angles to the motor joints with phase difference, Pendulum mode calculates pendulum behavior using gravity, damping and effective connection length. Desired Location, Orbit and Sweep modes move the target point to a fixed position, elliptical orbit or scanning path through a controlled dynamic system. Speed, amplitude, duration, loop, orbit radius and height can be changed instantly from the interface.

The Waypoint system allows the user to define multiple X–Y targets and the duration of each pass. Points can be added from the bottom toolbar, edited in a separate timeline editor, and saved and reloaded as a path file. Analysis panel shows angle, velocity and acceleration for the selected joint; It shows the IK error value for the solver as a time series. The locus trail on the canvas and the numerical history in the graphics are two different readings of the same run: one explains the trace of the movement in space, the other explains the system's response to time.

Real-time application architecture

Separating the numerical solution from the Qt interface loop was a critical decision for the usability of the product. HR operations are executed on the solver thread; The input state is passed to the decoder and the result is returned to the main UI layer via the signal. Thus, the heavy account does not directly block the paint or input event while the user moves the target. Canvas, dashboard, and analysis view consume only completed states.

Pydantic models keep the boundaries of mechanism data open, while NumPy handles linear algebra and vector calculations; pyqtgraph draws time series graphs. This distinction makes the project both testable and extensible: geometry and solvers can be verified without Qt, the UI uses the same mathematical conventions. Adding a new motion mode does not require rewriting the drawing code, nor does adding a new graphic require rewriting the mechanism model.

New mechanism, sample installation and recent projects on the LinkagePy start screen
The starting surface combines creating new mechanisms, exploring with examples, and returning to recent studies in one place.

A tested compute core

In this project, it was not enough for the visual result to be convincing on its own; Confidence in a kinematic tool depends on the same input reaching an accurate and reproducible result. The testing scope covers geometry operations, joint and link verifications, mechanism topology, forward and inverse solvers, joint limits, singularity behavior, animation control, command history, persistence round-trip, analysis buffers, and basic contracts between the UI and the kernel.

When adding it to the portfolio, I reinstalled the project in a clean Python 3.12 environment and ran the existing test suite: 60 tests passed. In addition, three smoke acceptance scenarios, which control the actual movement generation during playback, the lossless restoration of the mechanism, and the graphics buffer limit, were also successful. LinkagePy has been a comprehensive engineering effort for me, combining mechanical thinking, linear algebra, real-time interface development and verifiable software architecture in the same product.

Available for work