Skip to main content

Implementing HCTM in Java

In this post, we implement the Homogeneous Coordinate Transformation Matrix (HCTM) in Java. HCTM is a fundamental concept in robotics and computer graphics for representing rotation and translation of a coordinate frame in 3D space. Using a 4×4 matrix allows both rotation and translation to be combined into a single matrix multiplication, making it efficient to apply multiple transformations in sequence.

What is HCTM?

A Homogeneous Coordinate Transformation Matrix extends a standard 3D rotation matrix to 4×4 by appending a translation vector and a homogeneous row [0 0 0 1]. This program implements three rotations (Yaw around X-axis, Pitch around Y-axis, Roll around Z-axis) and one translation. Given a rotation angle (in degrees) and a 4-element coordinate frame vector, the program computes the transformed frame using matrix–vector multiplication.


Java Code Implementation

// ============================================================
// Homogeneous Coordinate Transformation Matrix (HCTM) in Java
// Supports: Yaw (X-axis), Pitch (Y-axis), Roll (Z-axis), Translation
// Input: rotation angle (degrees) + 4-element coordinate frame
// Output: transformed coordinate frame after matrix multiplication
// ============================================================

import java.io.*;

// Main class: reads user choice and delegates to rotation or translation
public class HCTM {
    public static void main(String[] args) throws IOException {
        BufferedReader obj = new BufferedReader(new InputStreamReader(System.in));

        System.out.println("Which operation do you want to perform?");
        System.out.println("1. Rotation - Yaw   (rotation about X-axis)");
        System.out.println("2. Rotation - Pitch (rotation about Y-axis)");
        System.out.println("3. Rotation - Roll  (rotation about Z-axis)");
        System.out.println("4. Translation");

        int choice = Integer.parseInt(obj.readLine());

        Rotation rot = new Rotation();  // Create rotation handler
        switch (choice) {
            case 1: rot.yaw();   break;
            case 2: rot.pitch(); break;
            case 3: rot.roll();  break;
            case 4: new Translation(); break;
            default: System.out.println("Invalid option");
        }
    }
}

// Rotation class: implements Yaw, Pitch, and Roll rotation matrices
class Rotation {

    // Yaw: rotation about the X-axis
    // Rotation matrix:
    //  [1,    0,         0,    0]
    //  [0,  cos(a),  -sin(a),  0]
    //  [0,  sin(a),   cos(a),  0]
    //  [0,    0,         0,    1]
    public void yaw() throws IOException {
        BufferedReader obj = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("\nEnter angle (degrees):");
        double angleDeg = Double.parseDouble(obj.readLine());
        double a = angleDeg * 3.14159265 / 180;  // Convert degrees to radians

        double[][] rotMatrix = {
            {1, 0,            0,           0},
            {0, Math.cos(a), -Math.sin(a), 0},
            {0, Math.sin(a),  Math.cos(a), 0},
            {0, 0,            0,           1}
        };

        int[] frame = readFrame(obj);    // Read the 4-element coordinate frame
        printResult(rotMatrix, frame);   // Multiply and print result
    }

    // Pitch: rotation about the Y-axis
    // Rotation matrix:
    //  [ cos(a),  0,  sin(a),  0]
    //  [   0,     1,    0,     0]
    //  [-sin(a),  0,  cos(a),  0]
    //  [   0,     0,    0,     1]
    public void pitch() throws IOException {
        BufferedReader obj = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("\nEnter angle (degrees):");
        double angleDeg = Double.parseDouble(obj.readLine());
        double a = angleDeg * 3.14159265 / 180;

        double[][] rotMatrix = {
            { Math.cos(a), 0, Math.sin(a), 0},
            { 0,           1, 0,           0},
            {-Math.sin(a), 0, Math.cos(a), 0},
            { 0,           0, 0,           1}
        };

        int[] frame = readFrame(obj);
        printResult(rotMatrix, frame);
    }

    // Roll: rotation about the Z-axis
    // Rotation matrix:
    //  [ cos(a),  -sin(a),  0,  0]
    //  [ sin(a),   cos(a),  0,  0]
    //  [   0,         0,    1,  0]
    //  [   0,         0,    0,  1]
    public void roll() throws IOException {
        BufferedReader obj = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("\nEnter angle (degrees):");
        double angleDeg = Double.parseDouble(obj.readLine());
        double a = angleDeg * 3.14159265 / 180;

        double[][] rotMatrix = {
            {Math.cos(a), -Math.sin(a), 0, 0},
            {Math.sin(a),  Math.cos(a), 0, 0},
            {0,            0,           1, 0},
            {0,            0,           0, 1}
        };

        // Roll accepts double-precision frame values
        BufferedReader obj2 = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Enter mobile coordinate frame (4 values, one per line):");
        double[] frameD = new double[4];
        for (int idx = 0; idx < 4; idx++) {
            frameD[idx] = Double.parseDouble(obj2.readLine());
        }

        // Matrix-vector multiplication: result[i] = sum of rotMatrix[i][j] * frameD[j]
        double[] result = new double[4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result[i] += rotMatrix[i][j] * frameD[j];
            }
        }
        System.out.println("Answer: " + result[0] + " " + result[1] + " " + result[2] + " " + result[3]);
    }

    // Helper: read 4 integer frame values from stdin
    private int[] readFrame(BufferedReader obj) throws IOException {
        System.out.println("Enter mobile coordinate frame (4 values, one per line):");
        int[] frame = new int[4];
        for (int idx = 0; idx < 4; idx++) {
            frame[idx] = Integer.parseInt(obj.readLine());
        }
        return frame;
    }

    // Helper: multiply 4x4 matrix by 4-element int vector and print result
    private void printResult(double[][] matrix, int[] frame) {
        double[] result = new double[4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result[i] += matrix[i][j] * frame[j];
            }
        }
        System.out.println("Answer: " + result[0] + " " + result[1] + " " + result[2] + " " + result[3]);
    }
}

// Translation class: applies a pure translation matrix
// Translation matrix:
//  [1, 0, 0, tx]
//  [0, 1, 0, ty]
//  [0, 0, 1, tz]
//  [0, 0, 0,  w]
class Translation {
    public Translation() throws IOException {
        BufferedReader obj = new BufferedReader(new InputStreamReader(System.in));

        // Read translation vector [tx, ty, tz, w]
        System.out.println("Enter Transformation vector (4 values: tx, ty, tz, w):");
        int[] t = new int[4];
        for (int idx = 0; idx < 4; idx++) {
            t[idx] = Integer.parseInt(obj.readLine());
        }

        // Build the 4x4 translation matrix
        double[][] transMatrix = {
            {1, 0, 0, t[0]},
            {0, 1, 0, t[1]},
            {0, 0, 1, t[2]},
            {0, 0, 0, t[3]}
        };

        // Read the mobile coordinate frame
        System.out.println("Enter mobile coordinate frame (4 values, one per line):");
        int[] frame = new int[4];
        for (int idx = 0; idx < 4; idx++) {
            frame[idx] = Integer.parseInt(obj.readLine());
        }

        // Apply translation: result = transMatrix * frame
        double[] result = new double[4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result[i] += transMatrix[i][j] * frame[j];
            }
        }
        System.out.println("Answer: " + result[0] + " " + result[1] + " " + result[2] + " " + result[3]);
    }
}

Explanation of the Code

  1. Menu and Dispatch — The main() method presents a numbered menu and reads the user’s choice. A switch statement delegates to the appropriate method in the Rotation class or instantiates the Translation class.
  2. Rotation Matrices — Each rotation method builds a 4×4 homogeneous rotation matrix using Math.cos() and Math.sin(). The angle is converted from degrees to radians before use. All rotation matrices include a homogeneous row [0, 0, 0, 1] so that they can be multiplied with homogeneous coordinate vectors.
  3. Matrix–Vector Multiplication — The transformed coordinate is computed via the standard formula: result[i] = Σ matrix[i][j] * frame[j] for j = 0..3. This is the core operation that applies the rotation or translation to the input frame.
  4. Translation Matrix — The Translation class builds an identity matrix with the translation vector inserted in the last column. When multiplied with a homogeneous coordinate [x, y, z, 1], this adds the translation offsets to each axis.

Sample Input and Output

Yaw (Rotation about X-axis), Angle = 60°, Frame = [2, 0, 3, 1]

Which operation do you want to perform?
1. Rotation - Yaw   (rotation about X-axis)
2. Rotation - Pitch (rotation about Y-axis)
3. Rotation - Roll  (rotation about Z-axis)
4. Translation
1

Enter angle (degrees): 60
Enter mobile coordinate frame (4 values, one per line):
2
0
3
1

Answer: 2.0  -2.598  1.500  1.0

Pitch (Rotation about Y-axis), Angle = 60°, Frame = [2, 0, 3, 1]

2

Enter angle (degrees): 60
Enter mobile coordinate frame (4 values, one per line):
2
0
3
1

Answer: 3.598  0.0  -0.232  1.0

Roll (Rotation about Z-axis), Angle = 45°, Frame = [0.5, 1.5, 0.6, 1]

3

Enter angle (degrees): 45
Enter mobile coordinate frame (4 values, one per line):
0.5
1.5
0.6
1

Answer: -0.707  1.414  0.6  1.0

Translation, Vector = [3, 0, 5, 1], Frame = [0, 0, 1, 1]

4

Enter Transformation vector (4 values: tx, ty, tz, w):
3
0
5
1
Enter mobile coordinate frame (4 values, one per line):
0
0
1
1

Answer: 3.0  0.0  6.0  1.0

Step-by-Step Output Explanation

  • Yaw at 60°: Rotates the frame around the X-axis by 60°. The X component (2.0) is unchanged. The Y and Z components are a linear combination of the original Y and Z values using cos(60°)=0.5 and sin(60°)≈0.866.
  • Pitch at 60°: Rotates around the Y-axis. The Y component (0.0) stays zero. X and Z components combine via the pitch rotation matrix.
  • Roll at 45°: Rotates around the Z-axis. The Z component (0.6) is unchanged. X and Y components rotate by 45° (cos(45°)≈0.707).
  • Translation [3,0,5,1]: Adds tx=3 to X and tz=5 to Z. Starting frame [0,0,1,1] becomes [0+3, 0+0, 1+5, 1] = [3, 0, 6, 1]. The homogeneous coordinate w remains 1.

See Also

Conclusion

Homogeneous Coordinate Transformation Matrices are a cornerstone of robotics forward kinematics. By representing rotation and translation in a single 4×4 matrix, you can chain multiple transformations simply by multiplying their matrices together. This program covers the three fundamental rotation axes (Yaw/Pitch/Roll) and pure translation. The natural next step from HCTM is to chain these matrices across multiple robot links to compute the end-effector position, which is exactly what Direct Kinematics does.

No Comments yet!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.