案例展示
设置设备姿态(setModel)
TIP
根据运动信息(jointsParams)与运动数据(data),批量设置设备各关节的姿态。常用于数字孪生中根据实时数据驱动设备模型。
<template>
<div style="position: relative">
<div id="TO"></div>
<el-form id="formPose" label-width="86px">
<el-form-item v-for="j in joints" :key="j.key" :label="j.label + ':'">
<div style="display: flex; align-items: center; width: 100%">
<el-slider
v-model="j.val"
:min="-180"
:max="180"
:step="1"
@input="applyPose"
style="flex: 1; margin-right: 5px" />
<span class="pos-value">{{ j.val }}°</span>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPose()" size="small">复位</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElForm, ElFormItem, ElSlider, ElButton } from 'element-plus'
import { getResourceBase, resUrl } from '../../utils/resourceUrl'
let TO = null
let robot = null
// 关节配置:modelName 为模型中关节对象名称,axis 为旋转轴(取自模型关节定义)
const jointDefs = [
{ key: 'WS30-1', label: '关节1', axis: 'y' },
{ key: 'WS30-2', label: '关节2', axis: 'z' },
{ key: 'WS30-3', label: '关节3', axis: 'z' },
{ key: 'WS30-4', label: '关节4', axis: 'x' },
{ key: 'WS30-5', label: '关节5', axis: 'z' },
{ key: '柱体', label: '关节6', axis: 'x' }
]
// 运动信息 animateInfo:描述每个关节如何运动
// dataUrl 为数据在 data 中的取值路径("root." 前缀会被自动去除)
const jointsParams = jointDefs.map((j) => ({
modelName: j.key,
animateType: 'rotate',
axis: j.axis,
rotateUnit: 'deg',
dataUrl: `root.${j.key}`
}))
// 运动数据 data:各关节目标角度(度)
const joints = reactive(jointDefs.map((j) => ({ ...j, val: 0 })))
onMounted(() => {
TO = new ThingOrigin('setModelScene', document.getElementById('TO'), {
params: {
resource: {
serverAddress: getResourceBase()
}
},
scene: {
background: { type: 'sky' }
},
camera: { position: { x: -20, y: 30, z: 80 } }
})
// 导入 ABB 机械臂模型
TO.model
.loadModel({
modelType: 'glb',
modelName: 'abb',
base: {
modelUrl: resUrl('/editor/model2/loadFileAsId/56201403')
},
position: { x: -20, y: -10, z: 0 },
scale: { x: 1, y: 1, z: 1 },
rotation: { x: 0, y: 0, z: 0 }
})
.then((model) => {
robot = model
TO.scene.add(robot)
applyPose()
})
})
// 组装运动数据并调用 setModel 设置设备姿态
function applyPose() {
if (!TO || !robot) return
const data = {}
joints.forEach((j) => {
data[j.key] = j.val
})
TO.machine.setModel(robot, jointsParams, data)
}
function resetPose() {
joints.forEach((j) => {
j.val = 0
})
applyPose()
}
</script>
<style scoped>
#TO {
width: 100%;
height: 400px;
position: relative;
}
#formPose {
position: absolute;
top: 10px;
right: 10px;
min-width: 280px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.3);
z-index: 1000;
background: rgba(0, 0, 0, 0.6);
color: #fff;
padding: 12px 16px;
border-radius: 4px;
font-size: 12px;
}
.pos-value {
min-width: 50px;
text-align: right;
font-size: 12px;
color: #fff;
}
#formPose :deep(.el-form-item__label) {
color: #fff;
font-size: 12px;
}
</style>API 介绍
machine.setModel
| 方法签名 | 返回值 | 描述 |
|---|---|---|
setModel(robot: Object3D, animateInfo: jointsParams[], data: any) | - | 根据运动信息与运动数据,对设备各关节应用 move / rotate 动画,设置设备姿态 |
参数说明:
| 参数名 | 说明 | 类型 | 必填 | 默认值 |
|---|---|---|---|---|
robot | 设备模型(根节点),关节通过 getObjectByName 查找 | Object3D | 是 | - |
animateInfo | 运动信息,描述每个关节如何运动 | jointsParams[] | 是 | - |
data | 运动数据,存放各关节的目标值 | any | 是 | - |
jointsParams
| 属性 | 说明 | 类型 | 必填 | 默认值 |
|---|---|---|---|---|
modelName | 模型名称(在 robot 子级中按名称查找) | string | 是 | - |
animateType | 动画类型:'move' / 'rotate' | string | 是 | - |
axis | 关节轴向 | Vector3 | xyz | 'x' | 'y' | 'z' | 'free' | 是 | - |
unit | 单位:'rad'(弧度)/ 'deg'(度) | 'rad' | 'deg' | 是 | 'rad' |
dataUrl | 数据在 data 中的取值路径("root." 前缀会被自动去除) | string | 是 | - |
rotateUnit | 角度单位(优先于 unit) | string | 否 | - |
reverse | 反向系数,最终值 = 原始值 × reverse | number | 否 | 1 |
pivot | 绕中心点旋转时的中心坐标(仅 rotate) | any | 否 | - |
freeAxis | 自由轴定义 { p1, p2 }(axis 为 'free' 时使用) | any | 否 | - |
pose | 姿态(setRotateAxis) | number | 否 | - |
correct | 旋转校正量(setRotateAxis) | number | 否 | - |
simulateRange | 模拟范围 | number[] | 否 | - |
formula | 公式 | string | 否 | - |
readMode | 取值方式:'json'(JSON 路径)/ 'key' | 'json' | 'key' | 否 | 'json' |
keySeek_root | 键值查找根节点 | string | 否 | - |
运动类型说明
animateType: 'rotate':axis为普通轴('x'/'y'/'z')且无pivot→ 调用animate.setRotateAxis(joint, axis, value, correct, unit)axis为'free'→ 调用animate.setRotatePivot(自由轴 + 中心点)- 提供
pivot→ 调用animate.setRotatePivot(指定轴 + 中心点)
animateType: 'move':axis为普通轴 → 调用animate.setMove(joint, axis, value),直接设置joint.position[axis] = valueaxis为'free'→ 以两点构造自由轴方向进行平移
使用要点
dataUrl以"root."开头时,前缀会被自动去除,剩余路径按.分级访问data。例如dataUrl: 'root.WS30-1'取data['WS30-1']。- 关节对象必须存在于
robot的子级中,且name与modelName一致。本案例 ABB 模型(56201403)的关节为WS30-1、WS30-2、WS30-3、WS30-4、WS30-5、柱体。 setModel设置的是绝对姿态:每次调用都会用data中的当前值重新设定各关节,适合实时数据驱动的场景。