Skip to content

案例展示

设置设备姿态(setModel)

TIP

根据运动信息(jointsParams)与运动数据(data),批量设置设备各关节的姿态。常用于数字孪生中根据实时数据驱动设备模型。

关节1:
关节2:
关节3:
关节4:
关节5:
关节6:
<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角度单位(优先于 unitstring-
reverse反向系数,最终值 = 原始值 × reversenumber1
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] = value
    • axis'free' → 以两点构造自由轴方向进行平移

使用要点

  • dataUrl"root." 开头时,前缀会被自动去除,剩余路径按 . 分级访问 data。例如 dataUrl: 'root.WS30-1'data['WS30-1']
  • 关节对象必须存在于 robot 的子级中,且 namemodelName 一致。本案例 ABB 模型(56201403)的关节为 WS30-1WS30-2WS30-3WS30-4WS30-5柱体
  • setModel 设置的是绝对姿态:每次调用都会用 data 中的当前值重新设定各关节,适合实时数据驱动的场景。