Trajectory Planning

This tutorial covers planning and executing smooth multi-point trajectories.

Simple Waypoint Trajectory

from mini_arm import MiniArm

arm = MiniArm()
arm.connect()

# Define waypoints (joint angles in degrees)
waypoints = [
    [0, 0, 0, 0, 0, 0],        # Start (home)
    [45, -30, 60, 0, 30, 0],   # Point A
    [90, -45, 90, 0, 45, 0],   # Point B
    [-45, -30, 60, 0, 30, 0],  # Point C
    [0, 0, 0, 0, 0, 0],        # End (home)
]

# Execute trajectory
arm.execute_trajectory(waypoints, duration=10.0)

Cartesian Trajectories

Define waypoints in Cartesian space:

# XYZ waypoints
cartesian_waypoints = [
    {"x": 100, "y": 0, "z": 150},
    {"x": 100, "y": 50, "z": 100},
    {"x": 100, "y": -50, "z": 100},
    {"x": 100, "y": 0, "z": 150},
]

arm.execute_cartesian_trajectory(cartesian_waypoints, duration=8.0)

Interpolation Methods

from mini_arm import InterpolationType

# Linear interpolation (default)
arm.execute_trajectory(waypoints, interpolation=InterpolationType.LINEAR)

# Cubic spline (smoother)
arm.execute_trajectory(waypoints, interpolation=InterpolationType.CUBIC)

# Quintic (smoothest, no jerk at waypoints)
arm.execute_trajectory(waypoints, interpolation=InterpolationType.QUINTIC)

Velocity Profiles

from mini_arm import VelocityProfile

# Trapezoidal velocity (accelerate, cruise, decelerate)
arm.execute_trajectory(
    waypoints,
    velocity_profile=VelocityProfile.TRAPEZOIDAL,
    max_velocity=90,        # deg/s
    max_acceleration=180,   # deg/s²
)

# S-curve (smooth acceleration)
arm.execute_trajectory(
    waypoints,
    velocity_profile=VelocityProfile.SCURVE,
)

Timed Waypoints

Specify exact timing for each waypoint:

timed_waypoints = [
    {"time": 0.0, "joints": [0, 0, 0, 0, 0, 0]},
    {"time": 2.0, "joints": [45, -30, 60, 0, 30, 0]},
    {"time": 3.5, "joints": [90, -45, 90, 0, 45, 0]},  # Faster segment
    {"time": 6.0, "joints": [-45, -30, 60, 0, 30, 0]},
    {"time": 8.0, "joints": [0, 0, 0, 0, 0, 0]},
]

arm.execute_timed_trajectory(timed_waypoints)

Real-time Streaming

For applications requiring real-time control:

import time
import math

# Stream positions at high rate
arm.start_streaming(rate=50)  # 50 Hz

t = 0
while t < 10:
    # Generate sinusoidal motion
    angle = 45 * math.sin(2 * math.pi * 0.5 * t)
    arm.stream_joint(0, angle)

    time.sleep(0.02)  # 50 Hz
    t += 0.02

arm.stop_streaming()

Recording and Playback

Record a trajectory by moving the arm manually:

# Enable gravity compensation / freedrive mode
arm.enable_freedrive()

print("Move the arm... Press Enter when done.")
arm.start_recording(rate=20)  # Record at 20 Hz
input()
trajectory = arm.stop_recording()

arm.disable_freedrive()

# Save trajectory
trajectory.save("my_trajectory.json")

# Load and replay
loaded = Trajectory.load("my_trajectory.json")
arm.execute_trajectory(loaded)

Visualization

Visualize trajectories before execution:

from mini_arm.viz import plot_trajectory

# Plot joint space trajectory
plot_trajectory(waypoints, show=True)

# Plot Cartesian path
plot_trajectory(waypoints, space="cartesian", show=True)

# Animate in 3D viewer
arm.visualize_trajectory(waypoints, animate=True)

Complete Example: Drawing a Square

from mini_arm import MiniArm

arm = MiniArm()
arm.connect()

# Define square in XY plane at Z=50mm
z_height = 50
size = 80  # mm

square_path = [
    {"x": size/2, "y": size/2, "z": z_height},
    {"x": -size/2, "y": size/2, "z": z_height},
    {"x": -size/2, "y": -size/2, "z": z_height},
    {"x": size/2, "y": -size/2, "z": z_height},
    {"x": size/2, "y": size/2, "z": z_height},  # Close the square
]

# Move to start position (raised)
arm.move_to(x=size/2, y=size/2, z=z_height + 50)

# Lower to drawing height
arm.move_to(x=size/2, y=size/2, z=z_height)

# Draw the square
arm.execute_cartesian_trajectory(square_path, duration=8.0)

# Raise and home
arm.move_relative(dz=50)
arm.home()

Next Steps