mercoledì 26 novembre 2025

Cinematica inversa delta robot

Ho trovato gratuito su Internet  "Modern Robotics: Mechanics, Planning, and Control," Kevin M. Lynch and Frank C. Park, Cambridge University Press, 2017, ISBN 9781107156302. (link) https://hades.mech.northwestern.edu/images/2/2e/MR-largefont-v2.pdf

 


 Interessante e' il delta robot, un robot parallelo parente stretto della piattaforma Stewart–Gough (il primo ha l'attuatore che trasla parallelo al piano superiore, utilizzato anche per classe di stampanti 3D

 

 

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from mpl_toolkits.mplot3d import Axes3D

# --- Delta Robot Geometric Parameters (Units in mm) ---

# Fixed Base Triangle side length (equilateral)
E = 400.0
# Moving Platform Triangle side length (equilateral)
F = 150.0

# Base and Platform Radii
e = E / (2 * np.sqrt(3))
f = F / (2 * np.sqrt(3))

# Arm Lengths
L = 200.0  # Upper arm (Motor arm)
l = 400.0  # Forearm (Rod)

# Fixed Motor Arm Angles (for the base)
alpha = np.radians(30)
beta = np.radians(150)
gamma = np.radians(270)
motor_angles = [alpha, beta, gamma]

# --- Kinematics Functions ---

def delta_calcInverse(x0, y0, z0):
    """
    Calculates the motor angles (theta1, theta2, theta3) for a given
    end-effector position (x0, y0, z0).
    Returns an array of angles in radians, or None if the position is unreachable.
    """
   
    theta = np.zeros(3)

    for i, angle in enumerate(motor_angles):
        x = x0 - f * np.cos(angle)
        y = y0 - f * np.sin(angle)
       
        y1 = -e + x * np.cos(angle) + y * np.sin(angle)
       
        a = x**2 + y**2 + z0**2 + L**2 - l**2 + e**2 + 2 * e * y1
        b = -2 * z0 * L
        c = 2 * y1 * L
       
        try:
            D = (a**2 - (b**2 + c**2))
           
            if D < 0:
                return None
           
            D_sqrt = np.sqrt(D)
            theta[i] = np.arctan2(c * a - b * D_sqrt, b * a + c * D_sqrt)
           
        except ZeroDivisionError:
            return None

    return theta

# --- Animation Setup ---

z_fixed = -150.0  # Safe operating height
radius = 40.0     # Adjusted Radius of the drawn circle
center_x = 0.0
center_y = 0.0

# Time steps for the animation
t = np.linspace(0, 2 * np.pi, 100)
x_path = center_x + radius * np.cos(t)
y_path = center_y + radius * np.sin(t)
z_path = np.full_like(t, z_fixed)

# Store the calculated end-effector points for the trail
effector_x, effector_y, effector_z = [], [], []

# --- Plotting Helper Functions ---

def get_motor_coords(theta):
    """Returns the base pivot coordinates B_i"""
    x_b = e * np.cos(theta)
    y_b = e * np.sin(theta)
    z_b = 0
    return np.array([x_b, y_b, z_b])

def get_elbow_coords(theta, angle):
    """Returns the elbow joint coordinates C_i"""
    x_c = (e - L * np.cos(theta)) * np.cos(angle)
    y_c = (e - L * np.cos(theta)) * np.sin(angle)
    z_c = -L * np.sin(theta)
    return np.array([x_c, y_c, z_c])

# --- Main Plotting Setup ---

fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')

# Set limits and labels
plot_lim = max(E/2, l+L) * 1.1
ax.set_xlim([-plot_lim, plot_lim])
ax.set_ylim([-plot_lim, plot_lim])
ax.set_zlim([-plot_lim, 0])
ax.set_xlabel('X-axis (mm)')
ax.set_ylabel('Y-axis (mm)')
ax.set_zlabel('Z-axis (mm)')
ax.set_title(f'Delta Robot Simulating a Circle (R={radius:.0f}mm, Z={z_fixed:.0f}mm)')

# 1. Initial setup: Draw the fixed base triangle
B1 = get_motor_coords(alpha)
B2 = get_motor_coords(beta)
B3 = get_motor_coords(gamma)
base_x = [B1[0], B2[0], B3[0], B1[0]]
base_y = [B1[1], B2[1], B3[1], B1[1]]
base_z = [0, 0, 0, 0]
ax.plot(base_x, base_y, base_z, 'k-', linewidth=3, label='Base E')

# 2. Draw the ENTIRE target circle path (as a dashed line) for reference
ax.plot(x_path, y_path, z_path, 'c--', linewidth=1, label='Target Path')

# 3. Initialize dynamic artists (the robot arms and platform)
motor_arms = [] # L arms
rods = []       # l rods
for i in range(3):
    # Initial state (set to base coords as dummy data)
    motor_arm_line, = ax.plot([B1[0], B1[0]], [B1[1], B1[1]], [B1[2], B1[2]], 'r-', linewidth=3)
    rod_line, = ax.plot([B1[0], B1[0]], [B1[1], B1[1]], [B1[2], B1[2]], 'b-', linewidth=2)
    motor_arms.append(motor_arm_line)
    rods.append(rod_line)

# The moving platform triangle
platform_line, = ax.plot([], [], [], 'g--', linewidth=1)

# The traced path (trail) and end-effector point
path_line, = ax.plot([], [], [], 'y-', linewidth=1.5, label='Traced Path')
end_effector_point, = ax.plot([], [], [], 'ko', markersize=5, label='End Effector')

# Collect all dynamic artists for return in update
dynamic_artists = motor_arms + rods + [platform_line, path_line, end_effector_point]

# --- Update Function ---

def update(frame):
    """Update function for the animation."""
   
    # 1. Get the target position for the current frame
    x0, y0, z0 = x_path[frame], y_path[frame], z_path[frame]
   
    # 2. Calculate the required motor angles
    angles_rad = delta_calcInverse(x0, y0, z0)
   
    if angles_rad is None:
        print(f"Error: Unreachable position at frame {frame} ({x0:.1f}, {y0:.1f}, {z0:.1f}). Stopping.")
        # This returns the current artists without updating them, effectively stopping the movement
        return dynamic_artists

    # Store the end-effector position for the trail
    effector_x.append(x0)
    effector_y.append(y0)
    effector_z.append(z0)

    # Update the traced path (trail)
    path_line.set_data(effector_x, effector_y)
    path_line.set_3d_properties(effector_z)
   
    # Update the end-effector tip marker
    end_effector_point.set_data([x0], [y0])
    end_effector_point.set_3d_properties([z0])

    # 3. Update the robot linkage for the current frame
    for i in range(3):
        # Coordinates of the platform pivot point P_i relative to the base center
        P_i = np.array([x0, y0, z0])
       
        # Calculate motor and elbow coordinates
        B_i = get_motor_coords(motor_angles[i])
        C_i = get_elbow_coords(angles_rad[i], motor_angles[i])
       
        # Update the Motor arm (L): B_i -> C_i
        motor_arms[i].set_data([B_i[0], C_i[0]], [B_i[1], C_i[1]])
        motor_arms[i].set_3d_properties([B_i[2], C_i[2]])
       
        # Update the Rod (l): C_i -> P_i
        rods[i].set_data([C_i[0], P_i[0]], [C_i[1], P_i[1]])
        rods[i].set_3d_properties([C_i[2], P_i[2]])
   
    # Update the moving platform
    P1 = np.array([x0, y0, z0]) + f * np.array([np.cos(alpha), np.sin(alpha), 0])
    P2 = np.array([x0, y0, z0]) + f * np.array([np.cos(beta), np.sin(beta), 0])
    P3 = np.array([x0, y0, z0]) + f * np.array([np.cos(gamma), np.sin(gamma), 0])

    platform_x = [P1[0], P2[0], P3[0], P1[0]]
    platform_y = [P1[1], P2[1], P3[1], P1[1]]
    platform_z = [P1[2], P2[2], P3[2], P1[2]]
   
    platform_line.set_data(platform_x, platform_y)
    platform_line.set_3d_properties(platform_z)

    # Return all updated artists
    return dynamic_artists

# Create the animation
ani = FuncAnimation(fig, update, frames=len(t), interval=50, blit=False, repeat=True)

plt.legend()
plt.show()

 

Broken Debian Update

Aggiornamento

 Si sono perse anche le configurazioni di Cups...niente di drammatico, solo fastidioso

-------------------------------------------- 

Diciamocelo...e' talmente tanto che accada che quando succede fa notizie

Ieri sera aggiornamento automatico di Debian al riavvio della macchina ed impossibilita' di loggarsi

Per un caso fortuito oltre a Gnome ho installato I3 ed entrando con questo window manager funzionava tutto...l'attenzione e' andata tutta su Gnome che infatti risultava rotto

Ho cancellato le estensioni ed e' partito ma rimangono ancora problemi occasionali ma non cosi' gravi da non poter utilizzare la macchina

Di seguito il log dell'errore 

 

===========================================================
[   68.164837] VBoxNetAdp: Successfully started.
[   91.779137] traps: gnome-session-i[3594] trap int3 ip:7fc415d3074b sp:7ffd5e4b5020 error:0 in libglib-2.0.so.0.8600.2[6874b,7fc415ce7000+a5000]
[   92.781729] slim[3069]: segfault at 0 ip 00007f7e55d33c7f sp 00007ffc0e775f70 error 4 in libX11.so.6.4.0[45c7f,7f7e55d0a000+90000] likely on CPU 1 (core 1, socket 0)
[   92.781740] Code: 00 48 89 fd 49 89 f5 48 89 d3 48 39 87 b0 00 00 00 75 09 48 85 d2 0f 84 28 01 00 00 48 8b 85 30 0a 00 00 4c 8b a5 98 00 00 00 <48> 8b 38 8b 48 48 48 8b 50 40 48 89 3c 24 85 c9 0f 84 2b 01 00 00
[   93.008089] audit: type=1400 audit(1764098950.300:196): apparmor="ALLOWED" operation="capable" class="cap" profile="Xorg" pid=3759 comm="Xorg" capability=12  capname="net_admin"
[   93.009749] audit: type=1400 audit(1764098950.300:197): apparmor="ALLOWED" operation="capable" class="cap" profile="Xorg" pid=3759 comm="Xorg" capability=26  capname="sys_tty_config"
[   93.273176] audit: type=1400 audit(1764098950.564:198): apparmor="ALLOWED" operation="capable" class="cap" profile="Xorg" pid=3759 comm="Xorg" capability=12  capname="net_admin"
[  136.816358] traps: gnome-session-i[3829] trap int3 ip:7fde4053a74b sp:7ffebe209ba0 error:0 in libglib-2.0.so.0.8600.2[6874b,7fde404f1000+a5000]
[  137.818699] slim[3756]: segfault at 50 ip 00007f658ae0724c sp 00007fff6224c220 error 4 in libX11.so.6.4.0[4624c,7f658addd000+90000] likely on CPU 0 (core 0, socket 0)
[  137.818711] Code: 28 00 00 00 75 06 48 83 c4 10 5b c3 e8 fd 68 fd ff 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 53 48 8b 87 30 0a 00 00 <48> 8b 58 50 48 83 fb ff 0f 84 68 8c fd ff 48 c7 40 50 ff ff ff ff
[  138.017888] audit: type=1400 audit(1764098995.309:199): apparmor="ALLOWED" operation="capable" class="cap" profile="Xorg" pid=3994 comm="Xorg" capability=12  capname="net_admin"
[  138.020324] audit: type=1400 audit(1764098995.313:200): apparmor="ALLOWED" operation="capable" class="cap" profile="Xorg" pid=3994 comm="Xorg" capability=26  capname="sys_tty_config"
[  150.175775] traps: gnome-session-i[4069] trap int3 ip:7f8f60a9474b sp:7ffcbe8a94a0 error:0 in libglib-2.0.so.0.8600.2[6874b,7f8f60a4b000+a5000]
[  151.178115] slim[3991]: segfault at 50 ip 00007ffbca41824c sp 00007ffc84aeb9b0 error 4 in libX11.so.6.4.0[4624c,7ffbca3ee000+90000] likely on CPU 2 (core 0, socket 0)
[  151.178127] Code: 28 00 00 00 75 06 48 83 c4 10 5b c3 e8 fd 68 fd ff 66 66 2e 0f 1f 84 00 00 00 00 00 66 90 f3 0f 1e fa 53 48 8b 87 30 0a 00 00 <48> 8b 58 50 

martedì 25 novembre 2025

Disegnatore robot a pantografo

 Questo post deriva dalla curiosita' suscitata dal giocattolo Lisciani Step

 

 

 

 


 

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# ---------------------------------------------------------
# PARAMETRI DEL PANTOGRAFO
# ---------------------------------------------------------

L = 1.0          # lunghezza dei bracci
cx, cy = 0.5, 0.5    # centro del cerchio
R = 0.3          # raggio del cerchio (deve essere <= 2L)
omega = 1.0      # velocità angolare

# ---------------------------------------------------------
# TRAIETTORIA DEL PENNINO
# ---------------------------------------------------------

t = np.linspace(0, 6*np.pi, 600)

Px = cx + R * np.cos(omega * t)
Py = cy + R * np.sin(omega * t)

# ---------------------------------------------------------
# Cinematica inversa (soluzione simmetrica)
# ---------------------------------------------------------
def inv_kin(px, py):
    p = np.array([px, py]) / L
    rho = np.linalg.norm(p)

    if rho > 2:
        raise ValueError("Punto fuori dal workspace del pantografo: ", (px,py))

    phi = np.arctan2(p[1], p[0])
    alpha = np.arccos(rho / 2)

    theta1 = phi + alpha
    theta2 = phi - alpha

    return theta1, theta2

# ---------------------------------------------------------
# Calcolo posizioni dei giunti A, B e del punto P
# ---------------------------------------------------------

A, B, P = [], [], []

for px, py in zip(Px, Py):
    th1, th2 = inv_kin(px, py)

    a = np.array([L*np.cos(th1), L*np.sin(th1)])
    b = np.array([L*np.cos(th2), L*np.sin(th2)])
    p = a + b

    A.append(a)
    B.append(b)
    P.append(p)

A = np.array(A)
B = np.array(B)
P = np.array(P)

# ---------------------------------------------------------
# ANIMAZIONE
# ---------------------------------------------------------

fig, ax = plt.subplots(figsize=(6,6))
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
ax.set_aspect('equal')
ax.set_title("Pantografo – cerchio centrato in (0.5, 0.5) r=0.3")

line_OA, = ax.plot([], [], lw=2)
line_OB, = ax.plot([], [], lw=2)
line_AP, = ax.plot([], [], lw=1, ls='--')
line_BP, = ax.plot([], [], lw=1, ls='--')
pen,      = ax.plot([], [], 'o')
traj,     = ax.plot(P[:,0], P[:,1], alpha=0.2)

def init():
    line_OA.set_data([], [])
    line_OB.set_data([], [])
    line_AP.set_data([], [])
    line_BP.set_data([], [])
    pen.set_data([], [])
    return line_OA, line_OB, line_AP, line_BP, pen

def update(i):
    O = np.array([0,0])
    a = A[i]
    b = B[i]
    p = P[i]

    line_OA.set_data([O[0], a[0]], [O[1], a[1]])
    line_OB.set_data([O[0], b[0]], [O[1], b[1]])
    line_AP.set_data([a[0], p[0]], [a[1], p[1]])
    line_BP.set_data([b[0], p[0]], [b[1], p[1]])

    # FIX richiesto da Matplotlib: sequenze, non scalari
    pen.set_data([p[0]], [p[1]])

    return line_OA, line_OB, line_AP, line_BP, pen

ani = FuncAnimation(fig, update, frames=len(t), init_func=init, blit=True)
plt.show()


 

 

 

Analisi MNF su spettri di riflettanza di plastica

Devo cerca di lavorare su spettri di riflettanza di plastica e la prima domanda e': quale sono le bande significative? Sono partito dal ...