You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过MQTT传输Kinect v2 Body[]数据时反序列化失败问题求助

嘿,我刚好碰到过类似的Kinect骨骼数据传输问题,你的核心问题其实出在直接序列化Kinect自带的Body类上——这个类根本不是为序列化设计的!它内部包含大量和传感器绑定的非序列化成员(比如硬件引用、未公开的内部字段),不管用Json.Net还是其他工具直接序列化,要么会抛出异常,要么会生成无效的JSON,反序列化自然就失败了。

我给你一套靠谱的解决方案,分分钟搞定:

核心思路:用自定义DTO(数据传输对象)中转

不要直接序列化Body对象,而是提取你需要的骨骼数据,放到一个自己定义的、可序列化的类里,只传输必要的纯数据。

第一步:创建可序列化的DTO类

在发送端和接收端的Unity项目里,都要定义完全一样的这两个类(确保字段名、枚举类型完全匹配):

using UnityEngine;
using System.Collections.Generic;
using Microsoft.Kinect;

[System.Serializable]
public class KinectJointData
{
    public JointType JointType;
    public Vector3 Position;
    public Quaternion Rotation;
    public TrackingState TrackingState;
}

[System.Serializable]
public class KinectBodyData
{
    public ulong BodyId;
    public bool IsTracked;
    public List<KinectJointData> Joints;
    // 如需手部状态等额外数据,可在此添加
    public HandState LeftHandState;
    public HandState RightHandState;
}

第二步:发送端(带Kinect的Unity项目):转换Body为DTO并发布

在你的Update()方法里,先把Kinect的Body[]转换成DTO列表,再序列化后通过MQTT发送:

private Body[] _bodies;
private KinectSensor _sensor;
// 你的MQTT客户端实例
private MqttClient _mqttClient;

void Start()
{
    // 初始化Kinect和MQTT客户端的代码...
    _bodies = new Body[6];
}

void Update()
{
    if (_sensor != null && _sensor.IsOpen)
    {
        _sensor.GetAndRefreshBodyData(_bodies);
        // 把Body数组转成DTO列表
        var bodyDTOs = ConvertBodiesToDTO(_bodies);
        // 序列化为JSON字符串
        string jsonPayload = Newtonsoft.Json.JsonConvert.SerializeObject(bodyDTOs);
        // 发布到MQTT主题
        _mqttClient.Publish("kinect/skeleton-data", System.Text.Encoding.UTF8.GetBytes(jsonPayload));
    }
}

private List<KinectBodyData> ConvertBodiesToDTO(Body[] bodies)
{
    var dtoList = new List<KinectBodyData>();
    foreach (var body in bodies)
    {
        if (!body.IsTracked) continue; // 跳过未被跟踪的身体

        var bodyDTO = new KinectBodyData
        {
            BodyId = body.BodyId,
            IsTracked = body.IsTracked,
            LeftHandState = body.HandLeftState,
            RightHandState = body.HandRightState,
            Joints = new List<KinectJointData>()
        };

        // 遍历所有关节,提取数据
        foreach (var jointPair in body.Joints)
        {
            Joint joint = jointPair.Value;
            JointOrientation orientation = body.JointOrientations[jointPair.Key];

            var jointDTO = new KinectJointData
            {
                JointType = jointPair.Key,
                // 把Kinect的CameraSpacePoint转为Unity坐标系(Z轴反转)
                Position = new Vector3(joint.Position.X, joint.Position.Y, -joint.Position.Z),
                // 同样处理旋转的Z轴,适配Unity坐标系
                Rotation = new Quaternion(orientation.Orientation.X, orientation.Orientation.Y, -orientation.Orientation.Z, orientation.Orientation.W),
                TrackingState = joint.TrackingState
            };

            bodyDTO.Joints.Add(jointDTO);
        }

        dtoList.Add(bodyDTO);
    }
    return dtoList;
}

第三步:接收端(无Kinect的Unity项目):反序列化并映射骨骼

在接收端的MQTT消息回调里,把JSON反序列化为DTO列表,然后更新你的角色骨骼:

// MQTT消息接收回调方法
private void OnMqttMessageReceived(string topic, byte[] payload)
{
    if (topic != "kinect/skeleton-data") return;

    string json = System.Text.Encoding.UTF8.GetString(payload);
    // 反序列化为DTO列表
    var bodyDTOs = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KinectBodyData>>(json);

    // 遍历每个DTO,更新对应的角色模型
    foreach (var bodyDTO in bodyDTOs)
    {
        // 根据BodyId找到你场景中对应的角色模型(比如提前创建的空对象或骨骼模型)
        GameObject targetModel = GetBodyModelById(bodyDTO.BodyId);
        if (targetModel == null) continue;

        // 更新每个关节的位置和旋转
        foreach (var jointDTO in bodyDTO.Joints)
        {
            // 假设你的角色关节Transform的名字和JointType枚举名一致(比如"SpineBase")
            Transform jointTransform = targetModel.transform.Find(jointDTO.JointType.ToString());
            if (jointTransform != null)
            {
                jointTransform.localPosition = jointDTO.Position;
                jointTransform.localRotation = jointDTO.Rotation;
            }
        }

        // 如需处理手部状态,可在此添加逻辑
        UpdateHandVisuals(targetModel, bodyDTO.LeftHandState, bodyDTO.RightHandState);
    }
}

// 示例:根据BodyId获取对应的角色模型
private GameObject GetBodyModelById(ulong bodyId)
{
    // 这里可以根据你的需求实现,比如用字典存储BodyId和模型的映射
    return GameObject.Find($"Body_{bodyId}");
}

额外优化建议

  • 减少带宽占用:只发送被跟踪的身体数据,或者记录上一帧的关节数据,只发送有变化的部分
  • 压缩JSON:用Gzip压缩JSON字符串后再发送,适合网络环境较差的场景
  • 枚举兼容性:如果两端无法引用相同的Kinect枚举,可以把JointType换成字符串(比如jointPair.Key.ToString()),反序列化时再转成对应的枚举或直接用字符串匹配关节名

内容的提问来源于stack exchange,提问作者Charlie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:16:22