plane to frame with python for prc

Started by bobrobot, June 30, 2024, 08:30:38 PM

Previous topic - Next topic


Hi Forum. Thanks in advance for your advice!!!
I am trying to get a little deeper into using python in combination with KUKA|prc for robotics :)
working with ChatGPT to create a class that takes planes and generates robot frames...
But its not going well :(

I have read that KUKA uses the Euler ZYX system to calculate the TCP orientation. This means that three rotations happen in a specific sequence to achieve the final orientation. The order is:

A rotation about the world Z axis
B rotation about the new Y axis
C rotation about the new X axis

Diving deeper to understand this I found the following site:
Which is super interesting.

ok so i have a sample plane: O(0.00,0.00,0.00) Z(0.17,-0.17,0.97)
which when passed through the LIN motion component outputs: LIN: X 0, Y 0, Z 0, A 135.4, B 75.9, C 134.6, E1 0, E2 0, E3 0, E4 0
and when I pass through the following code generates: LIN: X 0.00, Y 0.00, Z 0.00, A 1.75, B 9.85, C 10.15, E1 0.00, E2 0.00, E3 0.00, E4 0.00
not sure whats gone wrong. any advice is appreciated!!! Thanks!!!

here is the python code (file attached):

import Rhino.Geometry as rg
import math

class PlaneProcessor:
    def __init__(self, plane):
        # Initialize with the input plane
        self.plane = plane
        self.frame = None

    def calculate_frame(self):
        # Deconstruct the plane to get its components
        origin = self.plane.Origin  # Origin point of the plane
        x_axis = self.plane.XAxis  # X-axis vector of the plane
        y_axis = self.plane.YAxis  # Y-axis vector of the plane
        z_axis = self.plane.ZAxis  # Z-axis vector of the plane

        # Construct the frame using the origin and axes
        self.frame = rg.Plane(origin, x_axis, y_axis)
    def get_euler_angles(self):
        if self.frame is None:
            # Ensure the frame is calculated

        # Extract the axes from the frame
        x_axis = self.frame.XAxis
        y_axis = self.frame.YAxis
        z_axis = self.frame.ZAxis

        # Construct the transformation matrix from the axes
        transformation_matrix = [
            [x_axis.X, y_axis.X, z_axis.X],
            [x_axis.Y, y_axis.Y, z_axis.Y],
            [x_axis.Z, y_axis.Z, z_axis.Z]

        # Calculate Euler angles using the ZYX convention (intrinsic rotations)
        # A is the rotation around the world Z-axis
        A = math.atan2(transformation_matrix[1][0], transformation_matrix[0][0])
        # B is the rotation around the new Y-axis
        B = math.atan2(-transformation_matrix[2][0], math.sqrt(transformation_matrix[2][1] ** 2 + transformation_matrix[2][2] ** 2))
        # C is the rotation around the new X-axis
        C = math.atan2(transformation_matrix[2][1], transformation_matrix[2][2])

        # Convert the angles from radians to degrees
        A = math.degrees(A)
        B = math.degrees(B)
        C = math.degrees(C)

        # Adjust angles to ensure they are in the correct range (0-360 degrees)
        if A < 0:
            A += 360
        if B < 0:
            B += 360
        if C < 0:
            C += 360
        # Round angles to two decimal places
        A = round(A, 2)
        B = round(B, 2)
        C = round(C, 2)
        return A, B, C

    def get_kuka_frame(self):
        if self.frame is None:
            # Ensure the frame is calculated
        # Get the Euler angles
        A, B, C = self.get_euler_angles()
        return self.frame.Origin, A, B, C

    def create_lin_command(self, X, Y, Z, A, B, C):
        # Generate the KUKA LIN command string
        E1, E2, E3, E4 = 0.0, 0.0, 0.0, 0.0  # External axes placeholders
        lin_command = "LIN: X {:.2f}, Y {:.2f}, Z {:.2f}, A {:.2f}, B {:.2f}, C {:.2f}, E1 {:.2f}, E2 {:.2f}, E3 {:.2f}, E4 {:.2f}".format(X, Y, Z, A, B, C, E1, E2, E3, E4)
        return lin_command

# Initialize the PlaneProcessor class with the input plane
plane_processor = PlaneProcessor(plane)

# Get the TCP pose values (X, Y, Z, A, B, C)
origin, A, B, C = plane_processor.get_kuka_frame()

# Extract X, Y, Z from the origin point
X = round(origin.X, 2)
Y = round(origin.Y, 2)
Z = round(origin.Z, 2)

# Create the LIN command
lin_command = plane_processor.create_lin_command(X, Y, Z, A, B, C)

# Output the TCP pose values and LIN command to Grasshopper
a = lin_command  # LIN command

happy Roboting!

Johannes @ Robots in Architecture


In general the code "looks" plausible, but I guess that is the thing with AI-generated code, there could still be issues. Some things to consider are that KUKA|prc by default uses X as the tool-axis. Take a look at the "Frame - KUKA|prc" component, where you can toggle between the tool axes (also that option is in the settings if you want to use the Z-axis everyhwere). The second thing is that there are multiple ways to express the same coordinate system. So just because the ABC values differ, it does not have to be incorrect. So turn your ABC angles into a coordinate system and compare the coordinate axes.



Thanks for the fast reply! I will keep working on it.

one other thing is the error :
2. Data conversion failed from Text to KUKA|prc Command.

Is there a way to put the python generated code into the CMDS of the KUKA|prc core

For instance the python outputs:
LIN: X 529.21, Y 193.84, Z 582.43, A 1.75, B 9.85, C 10.15, E1 0, E2 0, E3 0, E4 0
but the core doesn't like it as its a text instead of a KUKA|prc generated command.
Tried to put the text into the KRL code command but no luck

Thanks again!

Johannes @ Robots in Architecture


Your Python code is only generating a string, so you could write that into a file and run it.
For KUKA|prc you need to bring it into a KUKA|prc component.
The easiest would be just to get the XYZABC values as doubles and plug them into a Frame component (or you do the three rotations yourself, the Frame component doesn't do much more).
To create a linear movement by code, use KUKAprcLibrary.PRC_Commands.PRC_LINMove(Plane pln, double velocity = -1, string interpolation = "C_DIS")Let me know if there are any problems!


Johannes @ Robots in Architecture


I sent the reply a bit too quickly yesterday because there are some extra steps. Internally KUKA|prc works with a different Plane definition, and the resulting commands also need to be formatted for GH. So generating a LIN movement with a plane would look as below:

            KUKAprcCore.Geometry.Plane ghPlane = KUKAprcGH.PRC_GH_Methods.PRC_GH_Methods.PRC_RHtoPRC_Plane(pln);
            KUKAprcCore.PRC_Classes.PRC_CommandData cmd = KUKAprcLibrary.PRC_Commands.PRC_LINMove(ghPlane, 1.0, "C_DIS");
            KUKAprcGH.PRC_IOClasses.GH_PRC_CommandData ghCommand = new KUKAprcGH.PRC_IOClasses.GH_PRC_CommandData(cmd);

An example made in Rhino 8 is attached!