通过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
相关产品推荐
相关产品推荐

