Tarea 10

Trayectorias de robots

Regresar a Teoría

Introducción

Se utiliza la librería de roboticstoolbox-python para el análisis.

Descargar teoría PDF
...

Código de trayectorias para movimientos por articulación

    import numpy as np
    import time
    import matplotlib.pyplot as plt
    from spatialmath import SE3
    import roboticstoolbox as rtb
    from roboticstoolbox import jtraj, ctraj
    
    def inverse_kinematics(robot, Td, q0, ilimit, mask, save_path, name="IK_Solution", status_block=False):
        print("Matriz de transformación (Inversa):")
        print(Td)
    
        # Método 1: Levenberg-Marquardt (Numérico)
        sol_LM = robot.ikine_LM(Td, q0=q0, ilimit=ilimit, slimit=ilimit, mask=mask, )
        print("Levenberg-Marquardt (ikine_LM):", sol_LM)
        if sol_LM.success:
            print("IK LM (Grados): ", np.rad2deg(sol_LM.q))
            print("FK con NR:\n", robot.fkine(sol_LM.q))
            robot.plot(sol_LM.q, block=status_block, jointaxes=True, eeframe=True, jointlabels=True)
            plt.savefig(f"{save_path}/IK_LM_{name}.png", dpi=600, bbox_inches='tight', pad_inches=0.1)
        else:
            print("IK LM: No se encontró solución")
    
        print("\nResumen de success:")
        print("Levenberg-Marquardt:", sol_LM.success)
    
        return {"Levenberg-Marquardt": sol_LM}
    
    def forward_kinematics(robot, q0, save_path, name="FK_Solution", status_block=False):
        """Calcula la cinemática directa con Denavit-Hartenberg de un robot"""
        T = robot.fkine(q0)  # Forward kinematics
        print("Matriz de transformación (Directa):")
        print(T)
        try:
            robot.plot(q0, block=status_block, jointaxes=True, eeframe=True, jointlabels=True)
            plt.savefig(f"{save_path}/FK_{name}.png", dpi=600, bbox_inches='tight', pad_inches=0.1)
        except Exception as e:
            print("Error al graficar la cinemática directa:", e)
    
    #---------------------------
    #    1. Definicion del robot
    #---------------------------
    a1 = 0.02
    a2 = 0.10
    a3 = 0.08
    
    save_path = "Web/Reportes/Teoria/T10/assets"
    name = "Robot_3ejes"
    robot = rtb.DHRobot(
        [
            rtb.RevoluteDH(alpha=-np.pi/2,  a=0,    d=a1,  offset=0,  qlim=(-np.pi, np.pi)),
            rtb.RevoluteDH(alpha=0,         a=a2,   d=0,   offset=0,  qlim=(-np.pi, np.pi)),
            rtb.RevoluteDH(alpha=0,         a=a3,   d=0,   offset=0,  qlim=(-np.pi, np.pi))
        ],
        name=name
    )
    print("Robot details:")
    print(robot)
    
    # Define the displacement values for each revolute joint
    t_values = [None, None, None, None, None]
    t_values[0] = [0, 0, 0]
    t_values[1] = [0, np.pi/4, 0]
    t_values[2] = [np.pi/4, np.pi/4, 0]
    t_values[3] = [np.pi/2, np.pi/4, 0]
    t_values[4] = [0, np.pi/4, np.pi/2]
    q_values = np.array(t_values).T
    print("Joint values:")
    print(q_values)
    
    listaXYZ = []
    
    # Ahora, itera sobre los valores de las articulaciones y calcula la cinemática directa (FK)
    for i, q in enumerate(q_values.T):  # q_values.T transpone la matriz de q_values para iterar por columnas
        print(f"\nCalculando FK para el conjunto de posiciones {i + 1}:")
        forward_kinematics(robot, q, save_path, name=f"{name}_Pos_{i + 1}", status_block=False)
    
    print("\n\n\n")
    #Imprimir los puntos de la trayectoria
    print("Puntos de la trayectoria:")
    for i, q in enumerate(q_values.T):
        x, y, z = robot.fkine(q).t
        print(f"Posición {i + 1}: ({x:.3f}, {y:.3f}, {z:.3f})")
        # Guardar los puntos de la trayectoria
        listaXYZ.append([x, y, z])
    
    #print(listaXYZ)
    #Convertir lista a array
    listaXYZ = np.array(np.round(listaXYZ, 3)).T
    print(listaXYZ)
    
    P= listaXYZ
    
    
    # -------------------------
    # Creacion de matriz de Rotacion para la orientacion de las coordenadas
    # -------------------------
    '''
    theta = np.pi / 4
    R = np.array([[np.cos(theta), -np.sin(theta), 0],
                  [np.sin(theta), np.cos(theta) , 0],
                  [0            , 0             ,-1]])
    '''
    R = np.array([[1, 0, 0],
                  [0, 1, 0],
                  [0, 0, 1]])
    # Utilizando SE3 se crea un serie de coordenadas, dada la matriz de rotacion y vector de posicion
    coordenadas = [SE3.Rt(R, [x, y, z]) for x, y, z in zip(P[0], P[1], P[2])]
    
    print("\n Coordenadas dentro de espacio de trabajo:")
    for i, pos in enumerate(coordenadas):
        print(f"WP{i+1}: {pos}")
    '''
    -------------------------
        2. Resolver la Cinematica inversa de cada coordenada
    -------------------------'
    '''
    q_sol = []                            # Lista para guardar soluciones de cada articulacion
    q_actual = np.zeros(robot.n)         # Suposicion inicial (0,0,0,0,0,0)
    ik_init = time.time()
    
    for i, pos in enumerate(coordenadas):
        # Usando LM o el metodo de IK preferente se busca la solucion
        sol = robot.ikine_LM(pos, q0=q_actual, mask=(1,1,1,0,0,0) )
        if sol.success:
            q_sol.append(sol.q)
            q_actual = sol.q             # update initial guess for next IK solve
            print(f"IK para coord{i+1} resuelta: {sol.q}")
        else:
            print(f"IK fallo para coord: {i+1}")
            
    ik_end = time.time()
    print("\nTiempo total de calculo de IK: {:.4f} segs".format(ik_end - ik_init))
    '''
    -------------------------
        3. Generar la trayectoria para movimiento por articulacion
    -------------------------
    '''
    tray_segment = []  # Lista de trayectorias finales
    T_segment = 5      # Duracion de cada segmento en segundo
    n_points = 50      # Numero de puntos por trayectoria
    
    # -----------------------
    # Las funciones de trayectorias requieren un vecotr de tiempo para interpolar
    # las velocidades, recordemos que las velocidades no son constantes a lo largo
    # de mis movimientos entonces se le da el tiempo deseado para que haga el computo
    # necesario para generar la trayectoria
    # -----------------------
    for i in range(len(q_sol) - 1):
        # Genera un vector de tiempo que va desde 0 hasta T_segment (5 segundos), dividido en 50 puntos.
        t_segment = np.linspace(0, T_segment, n_points) 
        # Genera la trayectoria de la solucion actual a la siguiente, interpolando con el vector de tiempo
        traj_seg = jtraj(q_sol[i], q_sol[i+1], t_segment)
        #Se agrega la trayectoria generada la lista
        tray_segment.append(traj_seg.q)
    
    # Se repite la generacion para regresar a la coordenada 1
    t_segment = np.linspace(0, T_segment, n_points)
    traj_return = jtraj(q_sol[-1], q_sol[0], t_segment)
    tray_segment.append(traj_return.q)
    
    # Apila verticalmente las trayectorias
    q_traj = np.vstack(tray_segment)
    '''
    -------------------------
        4. Evaluamos la trayectoria generada usando FK
    -------------------------
    '''
    task_traj = []
    for q in q_traj:
        T_fk = robot.fkine(q)
        task_traj.append(T_fk.t)    # Extraemos la traslacion
    task_traj = np.array(task_traj)
    
    # Creamos una trayectoria ideal, usando el vector de posicion y cerramos agregando la pos1 de nuevo
    P_closed = np.hstack((P, P[:, 0:1]))
    '''
    -------------------------
        5. Hacemos los plots, para las trayectorias ideal vs real y animamos el robot
    -------------------------
    '''
    # Creamos la ventana del plot de trayectorias
    traj_fig = plt.figure("Trayectorias")
    ax = traj_fig.add_subplot(111, projection='3d')
    ax.plot(task_traj[:, 0], task_traj[:, 1], task_traj[:, 2],
            'b-', label='Trayectoria J')
    ax.plot(P_closed[0], P_closed[1], P_closed[2],
            'r:', linewidth=2, label='Trayectoria Ideal')
    ax.scatter(P[0], P[1], P[2],
               color='red', marker='o', s=50, label='Coordenadas')
    for i in range(P.shape[1]):
        ax.text(P[0, i], P[1, i], P[2, i], f' WP{i+1}', 
                color='black', fontsize=12)
    
    ax.set_title('Trayectoria Espacio Articular')
    ax.set_xlabel('X [m]')
    ax.set_ylabel('Y [m]')
    ax.set_zlabel('Z [m]')
    ax.legend()
    
    # Creamos la animacion del robot en otra ventana
    robot.plot(q_traj, block=False, loop=True)
    plt.show()
    
Trayectoria por articulación

Se presenta el video de la simulación de movimietos por articulación.

Código de trayectorias para movimientos lineales

 import numpy as np
    import time
    import matplotlib.pyplot as plt
    from spatialmath import SE3
    import roboticstoolbox as rtb
    from roboticstoolbox import jtraj, ctraj
    
    def inverse_kinematics(robot, Td, q0, ilimit, mask, save_path, name="IK_Solution", status_block=False):
        print("Matriz de transformación (Inversa):")
        print(Td)
    
        # Método 1: Levenberg-Marquardt (Numérico)
        sol_LM = robot.ikine_LM(Td, q0=q0, ilimit=ilimit, slimit=ilimit, mask=mask, )
        print("Levenberg-Marquardt (ikine_LM):", sol_LM)
        if sol_LM.success:
            print("IK LM (Grados): ", np.rad2deg(sol_LM.q))
            print("FK con NR:\n", robot.fkine(sol_LM.q))
            robot.plot(sol_LM.q, block=status_block, jointaxes=True, eeframe=True, jointlabels=True)
            plt.savefig(f"{save_path}/IK_LM_{name}.png", dpi=600, bbox_inches='tight', pad_inches=0.1)
        else:
            print("IK LM: No se encontró solución")
    
        print("\nResumen de success:")
        print("Levenberg-Marquardt:", sol_LM.success)
    
        return {"Levenberg-Marquardt": sol_LM}
    
    def forward_kinematics(robot, q0, save_path, name="FK_Solution", status_block=False):
        """Calcula la cinemática directa con Denavit-Hartenberg de un robot"""
        T = robot.fkine(q0)  # Forward kinematics
        print("Matriz de transformación (Directa):")
        print(T)
        try:
            robot.plot(q0, block=status_block, jointaxes=True, eeframe=True, jointlabels=True)
            plt.savefig(f"{save_path}/FK_{name}.png", dpi=600, bbox_inches='tight', pad_inches=0.1)
        except Exception as e:
            print("Error al graficar la cinemática directa:", e)
    
    #---------------------------
    #    1. Definicion del robot
    #---------------------------
    a1 = 0.02
    a2 = 0.10
    a3 = 0.08
    
    save_path = "Web/Reportes/Teoria/T10/assets"
    name = "Robot_3ejes"
    robot = rtb.DHRobot(
        [
            rtb.RevoluteDH(alpha=-np.pi/2,  a=0,    d=a1,  offset=0,  qlim=(-np.deg2rad(360), np.deg2rad(360))),
            rtb.RevoluteDH(alpha=0,         a=a2,   d=0,   offset=0,  qlim=(-np.deg2rad(210), np.deg2rad(30))),
            rtb.RevoluteDH(alpha=0,         a=a3,   d=0,   offset=0,  qlim=(-np.deg2rad(170), np.deg2rad(170)))
        ],
        name=name
    )
    print("Robot details:")
    print(robot)
    
    # Define the displacement values for each revolute joint
    t_values = [None, None, None, None, None]
    t_values[0] = [0, 0, 0]
    t_values[1] = [0, np.pi/4, 0]
    t_values[2] = [np.pi/4, np.pi/4, 0]
    t_values[3] = [np.pi/2, np.pi/4, 0]
    t_values[4] = [0, np.pi/4, np.pi/2]
    q_values = np.array(t_values).T
    print("Joint values:")
    print(q_values)
    
    listaXYZ = []
    
    # Ahora, itera sobre los valores de las articulaciones y calcula la cinemática directa (FK)
    for i, q in enumerate(q_values.T):  # q_values.T transpone la matriz de q_values para iterar por columnas
        print(f"\nCalculando FK para el conjunto de posiciones {i + 1}:")
        forward_kinematics(robot, q, save_path, name=f"{name}_Pos_{i + 1}", status_block=False)
    
    print("\n\n\n")
    #Imprimir los puntos de la trayectoria
    print("Puntos de la trayectoria:")
    for i, q in enumerate(q_values.T):
        x, y, z = robot.fkine(q).t
        print(f"Posición {i + 1}: ({x:.3f}, {y:.3f}, {z:.3f})")
        # Guardar los puntos de la trayectoria
        listaXYZ.append([x, y, z])
    
    #print(listaXYZ)
    #Convertir lista a array
    listaXYZ = np.array(np.round(listaXYZ, 3)).T
    print(listaXYZ)
    
    P= listaXYZ
    # Matriz de rotacion para la orientacion 
    R = np.array([[1, 0, 0],
                  [0, 1, 0],
                  [0, 0, 1]])
    
    
    # Se crea una lista de objetos SE3 que representan los waypoints
    coordenadas = [SE3.Rt(R, [x, y, z]) for x, y, z in zip(P[0], P[1], P[2])]
    
    print("\nCoordenadas en el espacio de trabajo:")
    for i, pos in enumerate(coordenadas):
        print(f"WP{i+1}: {pos}")
    
    '''
    ---------------------------
        3. Generar la trayectoria usando ctraj (trayectoria cartesiana)
    ---------------------------
    '''
    tray_segment = []  # Lista para guardar trayectorias por segmento
    T_segment = 5      # Duracion (conceptual) de cada segmento (no se usa directamente en ctraj)
    n_points = 50      # Numero de puntos por segmento
    
    # Usamos ctraj para generar trayectorias en el espacio cartesiano y resolvemos IK para cada pose.
    q_traj_segments = []  # Lista para almacenar trayectorias en espacio articular
    q_current = np.zeros(robot.n)  # Suposicion inicial para IK
    
    ik_start = time.time()
    
    # Para cada segmento entre waypoints consecutivos
    for i in range(len(coordenadas) - 1):
        # Genera una trayectoria cartesiana entre dos poses
        cart_traj = ctraj(coordenadas[i], coordenadas[i+1], n_points)
        q_traj_seg = []  # Para guardar la trayectoria articular de este segmento
        for pose in cart_traj:
            sol = robot.ikine_LM(pose, q0=q_current, mask=(1,1,1,0,0,0))
            if sol.success:
                q_traj_seg.append(sol.q)
                q_current = sol.q  # Actualiza la condicion inicial para el siguiente paso
            else:
                print(f"IK fallo en el segmento {i+1} para una pose intermedia")
        q_traj_segments.append(np.array(q_traj_seg))
    
    # Agregar trayectoria de retorno de la ultima pose al primer waypoint
    cart_traj = ctraj(coordenadas[-1], coordenadas[0], n_points)
    q_traj_seg = []
    for pose in cart_traj:
        sol = robot.ikine_LM(pose, q0=q_current, mask=(1,1,1,0,0,0))
        if sol.success:
            q_traj_seg.append(sol.q)
            q_current = sol.q
        else:
            print("IK fallo en el segmento de retorno para una pose intermedia")
    q_traj_segments.append(np.array(q_traj_seg))
    
    ik_end = time.time()
    print("\nTiempo total de calculo de IK: {:.4f} segs".format(ik_end - ik_start))
    
    # Concatena todas las trayectorias en una sola secuencia
    q_traj = np.vstack(q_traj_segments)
    
    '''
    ---------------------------
        4. Evaluamos la trayectoria generada usando FK
    ---------------------------
    '''
    task_traj = []
    for q in q_traj:
        T_fk = robot.fkine(q)
        task_traj.append(T_fk.t)  # Extraemos la posicion (traslacion)
    task_traj = np.array(task_traj)
    
    # Para comparacion, se cierra la trayectoria ideal agregando el primer waypoint al final
    P_closed = np.hstack((P, P[:, 0:1]))
    
    '''
    ---------------------------
        5. Hacemos los plots, para las trayectorias ideal vs real y animamos el robot
    ---------------------------
    '''
    # Plot de la trayectoria en el espacio de trabajo
    traj_fig = plt.figure("Trayectorias")
    ax = traj_fig.add_subplot(111, projection='3d')
    ax.plot(task_traj[:, 0], task_traj[:, 1], task_traj[:, 2],
            'b-', label='Trayectoria generada (IK)')
    ax.plot(P_closed[0], P_closed[1], P_closed[2],
            'r:', linewidth=2, label='Trayectoria Ideal')
    ax.scatter(P[0], P[1], P[2],
               color='red', marker='o', s=50, label='Coordenadas')
    for i in range(P.shape[1]):
        ax.text(P[0, i], P[1, i], P[2, i], f' WP{i+1}', 
                color='black', fontsize=12)
    
    ax.set_title('Trayectoria en el Espacio de Trabajo')
    ax.set_xlabel('X [m]')
    ax.set_ylabel('Y [m]')
    ax.set_zlabel('Z [m]')
    ax.legend()
    
    # ---------------
    # Grafica de Angulo contra tiempo por articulacion
    # ---------------
    tiempo_total = len(q_traj_segments) * T_segment  # Se usa el número de segmentos reales
    eje_t = np.linspace(0, tiempo_total, q_traj.shape[0]) 
    
    fig_joint = plt.figure("Angulo Articulacion vs Tiempo")
    for i in range(q_traj.shape[1]):
        plt.plot(eje_t, q_traj[:, i], label=f'J {i+1}')
    plt.xlabel("Tiempo (s)")
    plt.ylabel("Ángulo (rad)")
    plt.title("Ángulo vs Tiempo")
    plt.legend()
    plt.grid(True)
    
    # Animacion del robot utilizando la trayectoria en espacio articular
    robot.plot(q_traj, block=False, loop=True)
    plt.show()    
        
Trayectoria lineal

Se presenta el video de la simulación por movimientos lineales.

Referencias Bibliográficas