六轴工业机器人可视化模拟平台 (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