六轴工业机器人可视化模拟平台 (Vue + Three.js + Blender)

Three.js 的六轴机械臂仿真,可通过图形化界面控制机械臂六个关节旋转。

用户可通过滑块控制每个关节角度,动作实时反馈到 3D 视图。

支持鼠标旋转、缩放、平移视角,查看机器人不同角度操作效果。

1.模型实现

下载自BlenderKit 的在线资源库

2.代码与模型对接

Blender里面的模型有各种的类,在前端代码里获取这些类并控制就行。

3.代码实现

APP.VUE

复制代码
<template>
  <div class="container">
    <div class="controls-top">
      <div class="controls-row">
        <div class="slider-group" v-for="(joint, index) in joints" :key="index">
          <h3>{{ joint.name }}</h3>
          <div class="slider-container">
            <div class="slider-row">
              <label>X: {{ joint.rotation.x.toFixed(2) }}</label>
              <input type="range" min="-3.14" max="3.14" step="0.01" v-model="joint.rotation.x" @input="updateRoboticArm" />
            </div>
            <div class="slider-row">
              <label>Y: {{ joint.rotation.y.toFixed(2) }}</label>
              <input type="range" min="-3.14" max="3.14" step="0.01" v-model="joint.rotation.y" @input="updateRoboticArm" />
            </div>
            <div class="slider-row">
              <label>Z: {{ joint.rotation.z.toFixed(2) }}</label>
              <input type="range" min="-3.14" max="3.14" step="0.01" v-model="joint.rotation.z" @input="updateRoboticArm" />
            </div>
          </div>
        </div>
      </div>
    </div>
    
    <div class="canvas-container">
      <canvas ref="threeCanvas"></canvas>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { onMounted, ref, reactive } from 'vue';

export default {
  setup() {
    const threeCanvas = ref(null);
    let scene, camera, renderer, model, controls;
    let animationFrameId = null;
    let jointMeshes = {};
    const nodeNames = ref([]);

    // 定义六个关节的数据
    const joints = reactive([
      { name: 'Link1N', rotation: { x: 0, y: 0, z: 0 } },
      { name: 'Link2N', rotation: { x: 0, y: 0, z: 0 } },
      { name: 'Link3N', rotation: { x: 0, y: 0, z: 0 } },
      { name: 'Link4N', rotation: { x: 0, y: 0, z: 0 } },
      { name: 'Link5N', rotation: { x: 0, y: 0, z: 0 } },
      { name: 'Link6N', rotation: { x: 0, y: 0, z: 0 } }
    ]);

    // 初始化Three.js场景
    const initThree = () => {
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0x333333);

      // 相机设置
      camera = new THREE.PerspectiveCamera(
        75,
        threeCanvas.value.clientWidth / threeCanvas.value.clientHeight,
        0.1,
        1000
      );
      camera.position.set(5, 5, 5);
      camera.lookAt(0, 0, 0);

      // 渲染器设置
      renderer = new THREE.WebGLRenderer({ canvas: threeCanvas.value, antialias: true });
      renderer.setSize(threeCanvas.value.clientWidth, threeCanvas.value.clientHeight);
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.shadowMap.enabled = true;

      // 添加光源
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
      scene.add(ambientLight);

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

      // 添加网格地面
      const gridHelper = new THREE.GridHelper(20, 20);
      scene.add(gridHelper);

      // 设置轨道控制器
      controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.05;

      // 加载机械臂模型
      loadRoboticArm();

      // 响应窗口大小变化
      window.addEventListener('resize', onWindowResize);

      // 启动动画循环
      animate();
    };

    // 加载机械臂模型
    const loadRoboticArm = () => {
      const loader = new GLTFLoader();
      loader.load('public/models/六轴机械臂.glb', (gltf) => {
        model = gltf.scene;
        
        // 坐标系校正(关键修复)
        model.rotation.set(-Math.PI, 0, Math.PI); // X轴-90度 + Z轴180度
        model.scale.set(0.8, 0.8, 0.8); // 可选缩放

        // 精确地面定位
        const bbox = new THREE.Box3().setFromObject(model);
        const minY = bbox.min.y * model.scale.y; // 考虑缩放后的实际高度
        model.position.y = -minY; // 将模型底部对齐y=0平面

        // 节点绑定逻辑
        const jointPattern = /Link([1-6])N/; // 精确匹配关节节点
        model.traverse((child) => {
          if (child.parent?.name.match(jointPattern)) {
            const match = child.parent.name.match(jointPattern);
            const index = parseInt(match[1]) - 1;
            if (joints[index]) {
              jointMeshes[joints[index].name] = child.parent;
              console.log(`关节绑定:${joints[index].name} ↔ ${child.parent.name}`);
              
              // 调试标记(可选)
              child.parent.add(new THREE.AxesHelper(0.2));
            }
          }
        });

        scene.add(model);
        
        // 添加地面参考(调试用)
        const gridHelper = new THREE.GridHelper(10, 10);
        scene.add(gridHelper);

        // 优化摄像机初始位置
        const center = new THREE.Vector3();
        bbox.getCenter(center);
        camera.position.set(
          center.x + 2,
          center.y + 1.5, // 降低观察高度
          center.z + 2
        );
        controls.target.copy(center);
        controls.update();
      });
    };

    // 窗口大小变化处理
    const onWindowResize = () => {
      camera.aspect = threeCanvas.value.clientWidth / threeCanvas.value.clientHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(threeCanvas.value.clientWidth, threeCanvas.value.clientHeight);
    };

    // 更新机械臂位置
    const updateRoboticArm = () => {
      joints.forEach((joint, index) => {
        const node = jointMeshes[joint.name];
        if (node) {
          // 关键修复4:使用欧拉角顺序控制
          node.rotation.set(
            joint.rotation.x,  // X轴旋转
            joint.rotation.y,  // Y轴旋转
            joint.rotation.z,  // Z轴旋转
            'XYZ' // 明确旋转顺序
          );
          
          // 关键修复5:强制更新世界矩阵
          node.updateMatrixWorld(true);
        }
      });
      
      // 立即重绘
      renderer.render(scene, camera);
    };

    // 动画循环
    const animate = () => {
      animationFrameId = requestAnimationFrame(animate);
      controls.update();
      renderer.render(scene, camera);
    };

    // 组件加载完成后执行
    onMounted(() => {
      initThree();
    });

    // 组件卸载时清理
    const cleanUp = () => {
      if (animationFrameId !== null) {
        cancelAnimationFrame(animationFrameId);
      }
      window.removeEventListener('resize', onWindowResize);
      
      // 释放资源
      if (model) {
        scene.remove(model);
      }
      
      if (renderer) {
        renderer.dispose();
      }
    };

    return {
      threeCanvas,
      joints,
      nodeNames,
      updateRoboticArm,
      cleanUp
    };
  },
  unmounted() {
    this.cleanUp();
  }
}
</script>

<style scoped>
/* 全屏容器 */
.container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 顶部控制条 */
.controls-top {
  width: 100%;
  background: rgba(20, 20, 20, 0.95);
  padding: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  z-index: 10;
}

/* 水平控制条排列 */
.controls-row {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  overflow-x: auto;
  gap: 10px;
  padding-bottom: 5px; /* 添加滚动条的空间 */
}

/* 关节控制组 */
.slider-group {
  min-width: 160px;
  flex: 1;
  padding: 8px;
  background: rgba(40, 40, 40, 0.6);
  border-radius: 4px;
  border-bottom: 2px solid #00ff88;
}

.slider-group h3 {
  margin: 0 0 8px 0;
  font-size: 14px;
  font-weight: bold;
  color: #00ff88;
  text-align: center;
}

/* 滑块容器 */
.slider-container {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* 滑块行 */
.slider-row {
  display: flex;
  flex-direction: column;
}

.slider-row label {
  font-size: 12px;
  color: #eee;
  margin-bottom: 2px;
}

/* 滑动条样式 */
input[type="range"] {
  width: 100%;
  height: 4px;
  background: #555;
  border-radius: 2px;
  margin: 2px 0;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  background: #00ff88;
  border-radius: 50%;
  cursor: pointer;
}

/* 主画布区域 - 占据大部分空间 */
.canvas-container {
  flex: 1;
  width: 100%;
  position: relative;
  background: #333;
}

.canvas-container canvas {
  width: 100%;
  height: 100%;
  display: block;
}

/* 去除默认边距 */
body, html {
  margin: 0;
  padding: 0;
  overflow: hidden;
  font-family: Arial, sans-serif;
}

/* 响应式调整 */
@media (max-height: 700px) {
  .controls-top {
    padding: 4px;
  }
  
  .slider-group {
    padding: 4px;
  }
  
  .slider-group h3 {
    font-size: 12px;
    margin-bottom: 4px;
  }
}
</style>

地址:https://github.com/2501918976/six-axis-robot-by-vue3-threejshttps://github.com/2501918976/six-axis-robot-by-vue3-threejs

相关推荐
沐曦可期4 小时前
标准编码与算法
javascript·python
又是忙碌的一天4 小时前
前端学习 JavaScript(2)
前端·javascript·学习
2501_915106324 小时前
JavaScript编程工具有哪些?老前端的实用工具清单与经验分享
开发语言·前端·javascript·ios·小程序·uni-app·iphone
GISer_Jing4 小时前
计算机基础——浏览器、算法、计算机原理和编译原理等
前端·javascript·面试
蓝瑟5 小时前
React 项目实现拖拽排序功能,如何在众多库中选对 “它”
前端·javascript·react.js
Rhys..5 小时前
Cucumber自学导航
javascript·python·bdd·cucumber
小刘不知道叫啥6 小时前
React 源码揭秘 | 合成事件
前端·javascript·react.js
ziyue75756 小时前
vue修改element-ui的默认的class
前端·vue.js·ui
程序定小飞8 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端