feat: 开发移动AGV节点

This commit is contained in:
zhanghao 2026-06-29 16:56:12 +08:00
parent 751cb3cd8e
commit 6dfa2be64e
4 changed files with 22 additions and 90 deletions

View File

@ -1,7 +1,7 @@
{
"name": "CMVR-IOT-UI",
"name": "changan-inspection",
"version": "3.8.8",
"description": "CMVR-IOT平台管理系统前端UI框架",
"description": "长安巡检项目",
"author": "CMVR",
"license": "MIT",
"type": "module",

View File

@ -1186,10 +1186,6 @@
</el-collapse>
</el-form>
</div>
<!-- <template #footer>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="confirm">保存</el-button>
</template> -->
</el-drawer>
</template>

View File

@ -363,12 +363,28 @@ function handler(params) {
{
collapseTitle: "机器人",
nodeList: [
{
icon: videoSvg,
name: "AGV控制",
type: "serviceNode",
desc: "控制机器人的底盘",
action: 'AGV_MOVE_TO_POINT',
nodeType: 'EDGE',
nodeParams: [
{ name: "deviceId", type: "input", input: "", disabled: true },
{ name: "x", type: "input", input: "", disabled: true },
{ name: "y", type: "input", input: "", disabled: true },
{ name: "theta", type: "input", input: "", disabled: true },
],
outputType: 'json'
},
{
icon: videoSvg,
name: "机械臂",
type: "serviceNode",
desc: "控制机器人的手臂",
action: 'ALM',
nodeType: 'EDGE',
nodeParams: [
{ name: "deviceId", type: "input", input: "", disabled: true }
],

View File

@ -34,89 +34,13 @@ let scene, camera, renderer, controls;
const animationId = ref(null);
const jointControls = reactive([]);
// ============ ============
const cleanMeshPath = (path) => {
if (!path) return '';
path = path.replace(/^file:\/\//, '');
path = path.replace(/^package:\/\/[^/]+\//, '');
const findMatch = path.match(/\$\(find\s+([^)]+)\)/);
if (findMatch) path = path.replace(/\$\(find\s+[^)]+\)/, '');
path = path.replace(/\\/g, '/').replace(/^\/+/, '');
return path;
};
const parseXYZ = (str) => {
if (!str) return { x: 0, y: 0, z: 0 };
const [x, y, z] = str.trim().split(/\s+/).map(Number);
return { x: x || 0, y: y || 0, z: z || 0 };
};
const parseRPY = (str) => {
if (!str) return { roll: 0, pitch: 0, yaw: 0 };
const [r, p, y] = str.trim().split(/\s+/).map(Number);
return { roll: r || 0, pitch: p || 0, yaw: y || 0 };
};
// RPY URDF XYZ = Three.js ZYX
const rpyToQuaternion = (rpy) => {
const euler = new THREE.Euler(rpy.roll, rpy.pitch, rpy.yaw, 'ZYX');
return new THREE.Quaternion().setFromEuler(euler);
};
// ============ STL ============
const loadSTLWithRetry = (url, group, visualPos, visualQuat) => {
return new Promise((resolve) => {
const loader = new STLLoader();
const loadMesh = (attemptUrl) => {
loader.load(
attemptUrl,
(geometry) => {
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial({
color: props.modelColor,
roughness: 0.4,
metalness: 0.3,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(visualPos.x, visualPos.y, visualPos.z);
mesh.quaternion.copy(visualQuat); // 使
mesh.castShadow = true;
mesh.receiveShadow = true;
while (group.children.length > 0) {
const child = group.children[0];
if (child.isMesh) {
child.geometry?.dispose();
child.material?.dispose();
}
group.remove(child);
}
group.add(mesh);
resolve(mesh);
},
undefined,
(error) => {
console.warn(`加载STL失败: ${attemptUrl}`, error);
if (!attemptUrl.startsWith('/') && !attemptUrl.startsWith('http')) {
loadMesh('/' + attemptUrl);
} else {
resolve(null);
}
}
);
};
loadMesh(url);
});
};
// ============ ============
const initScene = () => {
const container = viewerContainer.value;
const width = container.clientWidth || 800;
const height = container.clientHeight || 600;
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
scene.background = new THREE.Color('#000d2e');
camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.set(2, 2, 3);
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
@ -145,9 +69,9 @@ const initScene = () => {
const rim = new THREE.DirectionalLight(0xffffff, 0.3);
rim.position.set(0, -5, 5);
scene.add(rim);
const grid = new THREE.GridHelper(3, 20, 0x444466, 0x333355);
grid.position.y = -0.5;
scene.add(grid);
// const grid = new THREE.GridHelper(3, 20, 0x444466, 0x333355);
// grid.position.y = -0.5;
// scene.add(grid);
};
@ -174,7 +98,6 @@ const updateJoint = (jointName, angle) => {
if (control) control.value = angle;
};
const resetJoints = () => {
for (const joint of jointControls) {
joint.value = 0;
@ -225,7 +148,6 @@ const loadRobot = async () => {
const robot = model.threeObject; // URDFRobot
//
scene.add(robot); // robot.scene robot
robot.rotation.x = -Math.PI / 2;
@ -246,8 +168,6 @@ const loadRobot = async () => {
console.error('未知的 joints 结构:', robot.joints);
}
//
for (const [name, joint] of jointEntries) {
if (joint._jointType === 'fixed') continue;