A Java simulation and visualization project for a 3-DOF, two-link manipulator with a fixed base in 3D space. The arm executes a simple pick-and-place loop using an analytic inverse kinematics solver and a linear end-effector trajectory.
- Joint 0 (base): revolute yaw about the +Z axis at the origin
(0,0,0). - Joints 1–2 (links): revolute pitch angles defining motion in the arm’s vertical plane (relative to the X–Y plane).
- IK: reduces the 3D target to a 2D planar problem in
(r,z), solves with the law of cosines, and supports elbow-up / elbow-down branches. - Visualization: rendered with raylib via Jaylib, including an overlay panel and a suction-tool “ball” pick-and-place animation.
Build system: Gradle • Rendering: raylib (Jaylib) • Language: Java 17 • IK: Closed-form
- About this repository
- Repository structure
- Robotics: kinematics, dynamics, and inverse kinematics
- Building the project
- Running the simulator
- Repository file guide (full explanation)
- Simulation video
- Troubleshooting
This project simulates a 3-DOF, two-link manipulator mounted on a fixed base at the origin:
- The base joint rotates around the Z axis (yaw).
- The two link joints rotate as pitch angles (relative to the X–Y plane) in the arm’s vertical plane.
- The end-effector (EE) tracks a sequence of target points with analytic inverse kinematics.
- A small “ball” is used to visualize a pick-and-place loop:
- HOME → START
- PICK at START (attach ball)
- START → GOAL
- PLACE at GOAL (detach ball)
- GOAL → HOME
- wait briefly and repeat
User inputs (via console prompt):
- START position
(x y z)and GOAL position(x y z) - Constraint enforced by IK: z must be ≥ 0
- Reachability enforced by IK:
|p|must be within the arm’s reachable shell.
Manipulator3D_Java/
build.gradle
settings.gradle
README.md
resources/
fonts/
Inter-Regular.ttf (optional)
src/
main/
java/
manipulator3d/
Main.java
robot/
Vec3f.java
LinkParams.java
JointAngles.java
FKResult.java
IKResult.java
RobotArm.java
sim/
LinearTrajectory.java
ui/
Overlay.java
render/
DrawUtils.java- World frame origin is at the center of the base revolute joint: (0,0,0).
- Joint angles:
q0Yaw: rotation about +Z (sets the arm’s radial direction in the X–Y plane)q1Pitch: shoulder elevation angle relative to the X–Y planeq2Pitch: elbow pitch angle (relative bend in the same vertical plane)
We use the radial unit direction in the X–Y plane:
Let link lengths be
- Elbow position:
- End-effector position:
This is implemented in:
manipulator3d.robot.RobotArm#forwardKinematics(...)
This manipulator is effectively a 2-link arm in a vertical plane, rotated by yaw. Therefore the reachable radius must satisfy:
The implementation enforces:
target.z >= 0|p|within[minReach(), maxReach()]
Given target
Step A — base yaw
Step B — reduce to planar IK in
Now solve a 2-link planar problem for the triangle formed by
Step C — elbow angle from law of cosines
Clamp to
This sign is the elbow-up / elbow-down branch selection.
Step D — shoulder angle
Define:
Then:
This is implemented in:
manipulator3d.robot.RobotArm#solveIK(...)
Practical note (this repo):
- The main program uses the default elbow branch (elbow-down) by passing
elbowUp = false.
This repository is primarily a kinematic + trajectory simulator:
- The EE follows a commanded Cartesian trajectory.
- IK converts EE targets into joint angles.
- FK is used for rendering joint/link positions.
However, the code also defines mass and inertia properties for each link (uniform rod approximations):
- About center of mass:
- About the joint at one end:
These are computed in:
manipulator3d.robot.LinkParams#recomputeInertia()
Currently, those values are used for display/inspection (overlay panel) and as a foundation for extending the project to:
- forward dynamics (torques → accelerations),
- gravity and Coriolis terms,
- joint-space controllers (PD, computed torque, etc.).
- Java 17+ (JDK)
- Gradle 7+ (Gradle 8+ recommended)
- raylib bindings are resolved automatically through Gradle via Jaylib
From the project root:
gradle buildFrom the project root:
gradle runmacOS note:
- Some macOS configurations require starting Java on the first thread. If you see a startup error, run with:
gradle run --jvm-args="-XstartOnFirstThread"
- At startup, the console asks for:
- START
(x y z)and GOAL(x y z)
- START
- Mouse wheel: zoom camera
- F11: toggle fullscreen
- Overlay includes a PAUSE/PLAY button and reachability feedback
This section explains every important file in the repository and its role.
Key responsibilities:
- configures a Java 17 application
- fetches Jaylib from Maven Central
- sets the main entry point:
manipulator3d.Main
This is the application entry point and runtime loop:
- prompts user for START and GOAL targets
- validates reachability using IK for:
- fixed HOME EE point
- START
- GOAL
- defines a pick-and-place finite-state machine:
- HOME → START → PICK → GOAL → PLACE → HOME → WAIT → LOOP
- generates a linear Cartesian trajectory between targets
- runs IK each frame to get joint angles for the current EE target
- calls FK for rendering joint/link positions
- renders:
- robot geometry, thick axes, ball, suction tool
- overlay panel (status, parameters, pause button)
Implements analytic FK and IK:
- IK:
- rejects invalid targets (z < 0)
- checks radius bounds (min/max reach)
- computes yaw from
atan2(y,x) - reduces to planar IK in
(r,z) - solves elbow angle via law of cosines
- solves shoulder angle via triangle decomposition
- FK:
- constructs radial axis from yaw
- builds elbow and EE positions from link lengths and pitch angles
Implements linear interpolation in 3D:
- uses a normalized time parameter:
$\alpha = \mathrm{clamp}(t/T, 0, 1)$ - outputs:
$\mathbf{p}(\alpha) = \mathbf{a} + (\mathbf{b}-\mathbf{a})\alpha$
Implements the overlay rendering:
- draws a semi-transparent panel
- displays:
- link lengths, masses, inertias
- workspace bounds
- start/goal norms and coordinates
- phase text and reachability warnings
- implements a clickable PAUSE/PLAY button
Implements rendering utilities using raylib primitives:
- text helpers:
drawTextBold()drawTextSmall()
- “machined” robot look:
- pedestal + base flange
- joint housings (sphere + collar)
- tapered link cylinders with end caps
- suction tool at the EE
Below is a link to the simulation video on YouTube.
- Make sure you are running a supported desktop platform (Windows/macOS/Linux x86_64 or ARM64).
- If you are on macOS, try starting with:
-XstartOnFirstThread(see “Running the simulator”).
- Ensure:
z >= 0|p|is within the workspace shell:[|L1 - L2|, L1 + L2]