A Ruby 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 raylib-bindings, including an overlay panel and a suction-tool “ball” pick-and-place animation.
Language: Ruby • Rendering: raylib • Binding: raylib-bindings • IK: Closed-form
- About this repository
- Repository structure
- Robotics: kinematics, dynamics, and inverse kinematics
- Installing dependencies
- 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 UI overlay):
- START position
(x, y, z)and GOAL position(x, y, z)are entered in the on-screen panel. - Press PAUSE to edit fields safely.
- Press PLAY (apply inputs) to validate and start a new loop with the new START/GOAL.
- Constraint enforced by IK: z must be ≥ 0
- Reachability enforced by IK:
|p|must be within the arm’s reachable shell.
Font:
- The UI loads
resources/fonts/Inter-Regular.ttfwhen present (recommended).
Manipulator3D_Ruby/
Gemfile
Gemfile.lock
README.md
resources/
fonts/
Inter-Regular.ttf
src/
main.rb
raylib_bootstrap.rb
util/
math3d.rb
robot/
robot_arm.rb
sim/
trajectory.rb
ui/
overlay.rb
render/
draw_utils.rb- World frame origin is at the center of the base revolute joint: (0,0,0).
- Joint angles:
q0_yaw: rotation about +Z (sets the arm’s radial direction in the X–Y plane)q1_pitch: shoulder elevation angle relative to the X–Y planeq2_pitch: 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:
Implemented in:
Robot::RobotArm#forward_kinematics
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[min_reach, max_reach]
Given target
Step A — base yaw
Step B — reduce to planar IK in
Step C — elbow angle from law of cosines
Clamp to
Step D — shoulder angle
Define:
Then:
Implemented in:
Robot::RobotArm#solve_ik
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.
The code also defines mass and inertia properties for each link (uniform rod approximations):
- About center of mass:
- About the joint at one end:
Computed in:
Robot::LinkParams#recompute_inertia
These values are displayed in the overlay panel and can be used as a foundation for extending the simulator to include:
- forward dynamics (torques → accelerations),
- gravity and Coriolis terms,
- joint-space controllers (PD, computed torque, etc.).
- Ruby (recommended: Ruby 3.x)
- Bundler (usually included with Ruby)
This project uses:
raylib-bindings(raylib + helper libraries shipped as platform binaries)ffi(pulled automatically as a dependency)
From the repository root:
bundle installFrom the repository root:
bundle exec ruby src/main.rb- Mouse wheel: zoom camera
- F11: toggle fullscreen
- Overlay includes:
- PAUSE / PLAY (apply inputs)
- START/GOAL editable fields
- reachability feedback
This section explains every important file in the repository and its role.
Declares Ruby dependencies. The core dependency is raylib-bindings.
Loads the raylib shared libraries shipped by raylib-bindings and enables direct calls to raylib functions (e.g., InitWindow, BeginDrawing, ...).
Application entry point and runtime loop:
- initializes window + camera
- loads UI font (prefers
resources/fonts/Inter-Regular.ttf) - runs the 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, axes, ball, suction tool
- overlay panel (inputs, status, pause/play)
Small vector helpers and utility functions:
- vector constructors
v2,v3 add,sub,scale,length,normalizeclamp,color_u8,rect
Robot model and kinematics API:
LinkParams: link length, mass, inertia approximations (rod model)JointAngles: the 3 joint variables (yaw + 2 pitches)IKResultandFKResultRobotArm:solve_ikforward_kinematics- reach limits:
min_reach,max_reach
Minimal trajectory generator:
Sim::LinearTrajectory:reset(from,to,duration)update(dt)positionfinished?
Overlay panel:
- editable START/GOAL input fields (when paused)
- PLAY applies inputs and starts a new loop
- shows:
- link lengths, masses, inertias
- workspace bounds
- reachability and validation messages
Rendering utilities using raylib primitives:
draw_text_bold,draw_text_small- robot visuals:
- 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 using a supported Ruby (3.x recommended).
- Update RubyGems and Bundler:
gem update --system gem install bundler
- Ensure your environment is 64-bit Ruby and you are using the
x64-mingwgem build. - If you have multiple Rubies installed, confirm:
ruby -v which ruby
- Confirm the font exists:
resources/fonts/Inter-Regular.ttf
- If the file is missing or the font fails to load, the UI falls back to the default raylib font or a Windows font.
- Ensure:
z >= 0|p|is within[|L1 - L2|, L1 + L2]
- Use PAUSE to edit inputs, then PLAY to apply them.