threeJS-URDF Loading a Robot model and control it with DAT.GUI

Can i load a urdf model that uses .STL files as meshes using three.js and then use DAT GUI for the controlling the robot joints ?

Yes - all loaders parse to Three.Mesh, and Three.Mesh can in turn be controlled by Dat.GUI. You’ll need to code that controlling part though.

hey, thanks for the reply !
I got your point but for the controlling part of robot can i get some resources for coding that part
Thank you :slight_smile:

I believe you can take that models “nodes” or “children meshes” by traversing the model. Just ask chat gpt to help you understand traversing 3d objects in three js. Let me know if you cannot still figure it out, and I’ll leave the code below.

1 Like

Hey thanks for the reply mate :slight_smile: ,
I want you tp please send me the code , it will be very helpful for me , also i am making the GUI that looks like below , i want to connect the slider of a PyQt5 GUI with the three.js application made with react , How can i achieve it .
thanks

I’ll see what I can do…I can’t work on the code till the weekend, and even then I’m busy with my own project. However…here is the basic code to take a model(in my case gltf) and traverse through its nodes:
// Import Three.js library
import * as THREE from ‘three’;

// Import GLTF loader
import { GLTFLoader } from ‘three/examples/jsm/loaders/GLTFLoader.js’;

// Create a new Three.js scene
const scene = new THREE.Scene();

// Create a camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;

// Create a renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// Add lights to the scene
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(0, 1, 0);
scene.add(directionalLight);

// Load GLTF model
const loader = new GLTFLoader();
loader.load(
‘path/to/your/model.gltf’,
function (gltf) {
// Add the loaded model to the scene
scene.add(gltf.scene);

    // Traverse through the nodes of the GLTF model
    gltf.scene.traverse(node => {
        // Check if the node has a name
        if (node.name) {
            // Console log the name of the node
            console.log(node.name);
        }
    });
},
undefined,
function (error) {
    console.error(error);
}

);

// Function to update the scene
function animate() {
requestAnimationFrame(animate);

// Rotate the model
if (scene.children.length > 0) {
    scene.children[0].rotation.y += 0.01;
}

renderer.render(scene, camera);

}

// Call the animate function
animate();

Hey , thank you for the help and sending me the code , I wanted to ask something out of context , that is if i have an application that is a GUI sending the slider data to the server and i have an application that is react based in which we load an URDF Model using THREE.JS and visualizing it and also loading the model in PyQt5 GUI .
so can we make two clients at the same time and one server ,the two clients will be react application and PYQT5 Application that will send slider data and i want the same data to get send to the react application too. and server will act as a communication medium . How can we do this task and also if u get time , please look into this project , will be very helpful

Thank You Again :slight_smile:

That’s a good question…however I haven’t done any work with making my own database/server. I have worked with firebase(a database by google) and have used it to make multiplayer before.

I also haven’t used the gui before, but I can say that what your talking about seems reasonable; the way I would do it is send the id and the info to firebase and on the client recieve it.

Also, why do you want more than one instance of this. You could make it all one thing and just use the export and imports to get and set data across the application.

Maybe I don’t understand, but that’s the best I got. Thanks! -Jackson

1 Like

This urdf three.js project provides tools and examples on how to load an manipulate a URDF model as well as a drag-and-drop viewer:

How can we do this task and also if u get time , please look into this project , will be very helpful

If you’re having a specific problem you should post code showing the problem you’re having so people can help. But simply asking people to do work for you is generally frowned upon when asking questions. It’s your project, not ours.

Hey thanks for the reply
Can u please tell me what do u mean by more than one instance
Do u mean about the workflow I am following?
Thanks
Rohan

Yes…I wasn’t quite sure what you were meaning.(as far as your workflow)

Did my reply earlier help or no? I can reread this stuff too

import React, { Component } from “react”;
import * as THREE from “three”;
import OrbitControls from “three-orbitcontrols”;
import URDFLoader from “urdf-loader”;
//import io from “socket.io-client”;
import * as dat from ‘dat.gui’;

const gui = new dat.GUI();
const controls = {
BJ: 0, // Joint angles for each joint
SJ: 0,
EJ: 0,
W1J: 0,
W2J: 0,
W3J: 0
};
gui.add(controls, ‘BJ’, -180, 180).name(‘Base Joint’); // Adjust range as needed
gui.add(controls, ‘SJ’, -180, 180).name(‘Shoulder Joint’);
gui.add(controls, ‘EJ’, -180, 180).name(‘Elbow Joint’);
gui.add(controls, ‘W1J’, -180, 180).name(‘W1 Joint’);
gui.add(controls, ‘W2J’, -180, 180).name(‘W2 Joint’);
gui.add(controls, ‘W3J’, -180, 180).name(‘W3 Joint’);

class RobotScene extends Component {
constructor(props) {
super(props);
this.state = {
sliderData: {}
};
// Setting up the initial state and references
this.scene = new THREE.Scene();
this.camera = null;
this.renderer = null;
this.robotBase = null; // Reference to the robot’s base

    // Connect to the Socket.IO server
    //this.socket = io("http://localhost/4000"); // Adjust server address and port as needed

    // Bind event listener to handle incoming slider data
    //this.socket.on("sliderData", this.handleSliderData);
    


    this.initScene();
    this.addRobot();
    this.addControls();
    this.startAnimationLoop();
    window.addEventListener('resize', this.handleWindowResize);
}

// componentDidMount() {
//     // Clean up Socket.IO connection on component unmount
//     // window.addEventListener("beforeunload", () => {
//     //     this.socket.disconnect();
//     // });

    
// }

// handleSliderData = (data) => {
//     // Update state with received slider data
//     //this.setState({ sliderData: data });  
//     // Log received slider data
//     //console.log("Received slider data:", data);
//     // You can update your URDF visualization based on the received data here
// };

initScene() {
    // Set up camera
    this.camera = new THREE.PerspectiveCamera(
        75, // fov = field of view
        window.innerWidth / window.innerHeight, // aspect ratio
        0.1, // near plane
        1000 // far plane
    );
    this.camera.position.z = 5; // Move the camera back

    // Set up renderer
    this.renderer = new THREE.WebGLRenderer();
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(this.renderer.domElement); // Append to the document body

    // Set up orbit controls
    new OrbitControls(this.camera, this.renderer.domElement);

    // Set up lighting
    const ambientLight = new THREE.AmbientLight(0x404040);
    this.scene.add(ambientLight);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(1, 1, 1);
    this.scene.add(directionalLight);
}

addRobot() {
    const loader = new URDFLoader();
    loader.load("/robot_active.urdf", (robot) => {
        this.robotBase = robot; // Assuming the robot base is the root of the loaded URDF model
        this.scene.add(robot);
    });
}

updateRobotJoints() {
    if (this.robotBase) {
        // Replace these names with the actual joint names from your URDF file
        const BJ = this.robotBase.getObjectByName('BJ');
        // const SJ = this.robotBase.getObjectByName('SJ');
        // const EJ = this.robotBase.getObjectByName('EJ');
        // const W1J = this.robotBase.getObjectByName('W1J');
        // const W2J = this.robotBase.getObjectByName('W2J');
        // const W3J = this.robotBase.getObjectByName('W3J');

        if (BJ) {
            const BJRotationRadians =  THREE.MathUtils.degToRad(controls.BJ);
            BJ.rotation.z = THREE.MathUtils.clamp(BJRotationRadians, -2.0944, 2.0944);
        }

        // if (SJ) SJ.rotation.y = THREE.MathUtils.degToRad(controls.SJ);
        // if (EJ) EJ.rotation.y = THREE.MathUtils.degToRad(controls.EJ);
        // if (W1J) W1J.rotation.y = THREE.MathUtils.degToRad(controls.W1J);
        // if (W2J) W2J.rotation.y = THREE.MathUtils.degToRad(controls.W2J);
        // if (W3J) W3J.rotation.y = THREE.MathUtils.degToRad(controls.W3J);
    }
}




startAnimationLoop = () => {

    this.updateRobotJoints(); // Update the robot joints in each frame
    // Animation loop
    this.renderer.render(this.scene, this.camera);
    
    // Update any animations or rendering operations here

    // Keep looping
    this.frameId = window.requestAnimationFrame(this.startAnimationLoop);
};

handleWindowResize = () => {
    // Update camera and renderer on window resize
    const width = window.innerWidth;
    const height = window.innerHeight;
    this.renderer.setSize(width, height);
    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();
};

render() {
    return null; // No need to render anything in this component
}

}

export default RobotScene;

This is my code where i am using Dat.GUI for the control of the robot urdf model and i want to connect each slider with the 6 joints respectively , so can anyone please check what is missing in the code as i am not been able to connect the slider with the robot joints

BJ.rotation.z = THREE.MathUtils.clamp(BJRotationRadians, -2.0944, 2.0944);

This is not the correct way to set joint rotations. I recommend reading the urdf-loader documentation.

can you please tell me what is wrong and i have looked the documentation also but didn’t find anything related to it , can you please help me

Joint angles and positions are set using the URDFJoint.setJointValue function or the URDFRobot.setJointValue function. There is no need, and in fact it may provide incorrect results, to set a joint via three.js’ rotation object.

i have looked the documentation also but didn’t find anything related to it

I accept that there’s always room for improvement in documentation but if you take even a cursory look at the documentation page or the simple loader example you’ll see that there are other classes and member functions for a robot class that should cue you to at least look a little deeper or ask more specific questions.

So is there any other solution that we could work on so as to rotate the joints of the urdf model
using three.js (DAT.GUI for controls )

how do i get the logic for controlling the robot urdf , if u could provide me some resource that will be helpful