写一段代码,将mediapipe捕捉的人体关键点数据转换成unity中avatar支持的形式 以下是一个将MediaPipe人体关键点数据转换为Unity Avatar控制的示例代码方案,分为Python数据采集和C# Unity控制两部分:
# Python部分 - MediaPipe数据处理(示例)
import mediapipe as mp
import socket
import json
mp_pose = mp.solutions.pose
# Unity连接配置
HOST = '127.0.0.1' # Unity所在IP
PORT = 65432 # 通信端口
# 关键点索引映射(MediaPipe Pose定义的33个关键点)
JOINT_INDICES = {
"LEFT_HIP": 23,
"RIGHT_HIP": 24,
"SPINE": 11, # 肩部中点近似为脊柱上端
"LEFT_KNEE": 25,
"RIGHT_KNEE": 26,
"LEFT_SHOULDER": 11,
"RIGHT_SHOULDER": 12,
"NECK": 0, # 鼻部近似为颈部
}
# 坐标系转换函数
def convert_coordinates(mediapipe_point):
"""将MediaPipe坐标系转换为Unity坐标系"""
# MediaPipe: 右手坐标系,Y轴向下
# Unity: 左手坐标系,Y轴向上,Z轴向前
return {
'x': mediapipe_point.x,
'y': -mediapipe_point.y, # 反转Y轴
'z': -mediapipe_point.z # 反转Z轴
}
# 数据发送函数
def send_to_unity(sock, landmarks):
joint_data = {}
for joint_name, index in JOINT_INDICES.items():
if landmarks[index].visibility < 0.5: # 过滤低可见度关键点
continue
joint_data[joint_name] = convert_coordinates(landmarks[index])
# 计算臀部中心点
if "LEFT_HIP" in joint_data and "RIGHT_HIP" in joint_data:
lh = joint_data["LEFT_HIP"]
rh = joint_data["RIGHT_HIP"]
joint_data["HIP_CENTER"] = {
'x': (lh['x'] + rh['x']) / 2,
'y': (lh['y'] + rh['y']) / 2,
'z': (lh['z'] + rh['z']) / 2
}
sock.sendto(json.dumps(joint_data).encode(), (HOST, PORT))
# MediaPipe处理主循环
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
with mp_pose.Pose(
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as pose:
cap = cv2.VideoCapture(0)
while cap.isOpened():
success, image = cap.read()
if not success:
continue
results = pose.process(image)
if results.pose_landmarks:
send_to_unity(s, results.pose_landmarks.landmark)
// C#部分 - Unity控制脚本
using UnityEngine;
using System.Net.Sockets;
using System.Text;
using System;
public class MotionReceiver : MonoBehaviour
{
public Transform hipCenter;
public Transform spine;
public Transform leftLeg, rightLeg;
public Transform leftArm, rightArm;
public Transform neck;
private UdpClient udpClient;
private const int port = 65432;
void Start()
{
udpClient = new UdpClient(port);
udpClient.Client.ReceiveTimeout = 1000;
}
void Update()
{
try {
IPEndPoint remoteEP = null;
byte[] data = udpClient.Receive(ref remoteEP);
string json = Encoding.UTF8.GetString(data);
ProcessPoseData(JsonUtility.FromJson<PoseData>(json));
} catch (Exception e) {
Debug.LogWarning(e.ToString());
}
}
void ProcessPoseData(PoseData data)
{
// 臀部位置控制
if (data.HIP_CENTER != null)
{
Vector3 hipPos = new Vector3(
data.HIP_CENTER.x * 2, // 缩放系数根据实际调整
data.HIP_CENTER.y * 2,
data.HIP_CENTER.z * 2
);
hipCenter.localPosition = hipPos;
}
// 腿部旋转计算
if (data.LEFT_HIP != null && data.LEFT_KNEE != null)
{
Vector3 hip = ToVector3(data.LEFT_HIP);
Vector3 knee = ToVector3(data.LEFT_KNEE);
leftLeg.rotation = Quaternion.LookRotation(knee - hip, Vector3.up);
}
// 脊柱旋转
if (data.SPINE != null && data.NECK != null)
{
Vector3 spinePos = ToVector3(data.SPINE);
Vector3 neckPos = ToVector3(data.NECK);
spine.rotation = Quaternion.LookRotation(neckPos - spinePos, Vector3.up);
}
}
Vector3 ToVector3(JointPoint point)
{
return new Vector3(point.x, point.y, point.z);
}
[System.Serializable]
public class PoseData
{
public JointPoint HIP_CENTER;
public JointPoint LEFT_HIP;
public JointPoint RIGHT_HIP;
public JointPoint LEFT_KNEE;
// 其他需要的关键点...
}
[System.Serializable]
public class JointPoint
{
public float x;
public float y;
public float z;
}
}
关键实现细节说明:
- 坐标系转换:
- MediaPipe使用右手坐标系(Y轴向下)
- Unity使用左手坐标系(Y轴向上,Z轴向前)
- 转换公式:y = -mediapipe.y, z = -mediapipe.z
- 数据优化:
- 使用UDP协议实现低延迟传输
- 仅传输必要关键点数据
- 添加可见性过滤(visibility < 0.5)
- Unity端处理:
- 使用Quaternion.LookRotation计算骨骼朝向
- 根据实际场景调整位置缩放系数
- 建议的骨骼层级关系:
Hips (root)
├─ LeftUpLeg
├─ RightUpLeg
└─ Spine
├─ Neck
├─ LeftArm
└─ RightArm - 校准建议:
- 在Unity中创建校准场景
- 添加位置/旋转偏移量补偿
- 实现动态缩放系数调整
实际使用时需要: 1. 在Unity Avatar中正确设置Humanoid骨骼映射 2. 根据摄像头分辨率调整坐标缩放参数 3. 添加数据平滑滤波器(移动平均/Kalman滤波) 4. 实现断线重连机制 5. 添加调试可视化(显示原始关键点数据)
Q:unity中怎样直接控制avatar做动作 在Unity中直接控制Avatar动作主要通过操作骨骼的Transform来实现,以下是详细的实现方法和步骤:
1. 基础准备
1.1 配置Humanoid Avatar
- 导入模型后,在模型导入设置中:
- 选择 Rig 标签
- Animation Type: Humanoid
- 点击 Configure 检查骨骼映射
- 确保所有关键骨骼(如Hips、Spine、Legs等)正确映射
1.2 创建空物体控制器
public class AvatarController : MonoBehaviour
{
// 通过Unity Inspector绑定骨骼
public Transform hips;
public Transform spine;
public Transform leftUpperLeg;
public Transform rightUpperLeg;
public Transform neck;
// 其他需要控制的骨骼...
}
2. 直接骨骼控制核心逻辑
2.1 位置控制(适用于根节点)
void UpdateHipsPosition(Vector3 newPosition)
{
// 保持Y轴高度(根据需求调整)
Vector3 adjustedPos = new Vector3(
newPosition.x * positionScale,
Mathf.Clamp(newPosition.y, minHeight, maxHeight),
newPosition.z * positionScale
);
hips.localPosition = Vector3.Lerp(hips.localPosition, adjustedPos, 0.2f);
}
2.2 旋转控制(关节控制关键)
void UpdateLimbRotation(Transform upperJoint, Transform lowerJoint, Vector3 jointPos, Vector3 nextJointPos)
{
// 计算骨骼方向
Vector3 boneDirection = (nextJointPos - jointPos).normalized;
// 计算旋转(使用LookRotation+轴调整)
if(boneDirection != Vector3.zero)
{
Quaternion targetRot = Quaternion.LookRotation(boneDirection, Vector3.up);
// 调整轴向(根据模型骨骼方向)
targetRot *= Quaternion.Euler(90, 0, 0); // 示例修正
upperJoint.rotation = Quaternion.Slerp(
upperJoint.rotation,
targetRot,
rotationSmoothness * Time.deltaTime
);
}
}
2.3 脊柱链式控制
void UpdateSpine(Vector3 hipPos, Vector3 spinePos, Vector3 neckPos)
{
// 臀部到脊柱的旋转
UpdateLimbRotation(hips, spine, hipPos, spinePos);
// 脊柱到脖子的旋转
UpdateLimbRotation(spine, neck, spinePos, neckPos);
}
3. 完整数据驱动示例
public class MotionCaptureDriver : MonoBehaviour
{
// 参数调整
[Header("Settings")]
[Range(0.1f, 2f)] public float positionScale = 1.5f;
[Range(0.1f, 5f)] public float rotationSmoothness = 2f;
void ProcessMotionData(PoseData data)
{
// 根节点位置
UpdateHipsPosition(data.hipCenter);
// 左腿
UpdateLimbRotation(
leftUpperLeg,
leftLowerLeg,
data.leftHip,
data.leftKnee
);
// 右腿
UpdateLimbRotation(
rightUpperLeg,
rightLowerLeg,
data.rightHip,
data.rightKnee
);
// 脊柱链
UpdateSpineChain(
data.hipCenter,
data.spine,
data.neck
);
// 手臂控制(同理)
// ...
}
}
4. 高级控制技巧
4.1 逆向动力学(IK)控制
// 在Animator上添加IK控制
void OnAnimatorIK(int layerIndex)
{
if(useFootIK)
{
// 左脚IK
animator.SetIKPositionWeight(AvatarIKGoal.LeftFoot, 1);
animator.SetIKRotationWeight(AvatarIKGoal.LeftFoot, 1);
animator.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootTarget.position);
animator.SetIKRotation(AvatarIKGoal.LeftFoot, leftFootTarget.rotation);
// 同样处理右脚...
}
}
4.2 数据平滑处理
// 使用低通滤波器
Vector3 LowPassFilter(Vector3 newValue, Vector3 previousValue)
{
return Vector3.Lerp(previousValue, newValue, 0.3f);
}
// 使用在关键数据上
smoothedHipPosition = LowPassFilter(rawHipPosition, smoothedHipPosition);
4.3 运动重定向校准
IEnumerator CalibrationRoutine()
{
// 记录初始偏移
Vector3 initialHipOffset = hips.position - rawData.hipCenter;
// 等待3秒校准时间
yield return new WaitForSeconds(3);
// 应用永久性偏移
permanentOffset = initialHipOffset;
}
5. 调试与优化
5.1 可视化调试
void OnDrawGizmos()
{
// 绘制骨骼连线
Gizmos.color = Color.red;
Gizmos.DrawLine(hips.position, spine.position);
Gizmos.DrawLine(spine.position, neck.position);
// 绘制关键点
if(showRawData)
{
Gizmos.color = Color.green;
Gizmos.DrawSphere(rawHipPosition, 0.1f);
}
}
5.2 性能优化
- 使用 Job System + Burst Compiler 处理数据
- 控制Update频率:
csharp [SerializeField] int updateInterval = 2; // 每2帧更新一次 void Update() { if(Time.frameCount % updateInterval == 0) { UpdateAvatar(); } }
6. 常见问题解决
- 骨骼翻转问题:
- 在
LookRotation后添加轴向修正:csharp targetRotation *= Quaternion.Euler(90, 0, 0); // 根据实际模型调整 - 动作抖动:
csharp // 添加阈值过滤 if((newPosition - currentPosition).magnitude > 0.01f) { ApplyPosition(newPosition); } - 比例不匹配:
csharp // 动态缩放计算 float CalculateScaleRatio(Vector3 realWorldShoulderWidth) { float modelWidth = Vector3.Distance(leftShoulder.position, rightShoulder.position); return modelWidth / realWorldShoulderWidth.magnitude; }
通过以上方法,可以实现从MediaPipe数据到Unity Avatar的精准动作控制。实际开发时需要根据具体模型比例和动作需求调整参数,建议配合Unity的Humanoid重定向功能实现更好的跨模型兼容性。
Q:将动作数据应用到ik系统,实现平滑自然的效果,有什么要点方法
将动作数据应用到IK系统实现平滑自然效果的要点与方法
在Unity中通过IK系统实现平滑自然的动作效果,需要结合数据预处理、IK参数调节、运动学协调等多个环节。以下是关键步骤与实施方法:
1. 选择合适的IK系统
- Unity内置Animator IK:
- 适用于Humanoid Avatar,通过
OnAnimatorIK回调控制。 - 支持设置
AvatarIKGoal(如手、脚)和AvatarIKHint(如肘、膝)的权重与目标位置。 - 优点:集成方便,适合基础需求。
- 缺点:功能较为基础,复杂动作需结合其他逻辑。
- 第三方插件(如Final IK):
- 提供更强大的IK解算器(如CCD、FABRIK)。
- 支持全身IK、骨骼链约束等高级功能。
- 适用场景:复杂动作(攀爬、抓取)或高精度需求。
2. 数据预处理:减少抖动与噪声
- 低通滤波:
csharp Vector3 LowPassFilter(Vector3 rawData, Vector3 prevData, float smoothFactor = 0.2f) { return Vector3.Lerp(prevData, rawData, smoothFactor); } - 对关节位置和旋转进行平滑处理,避免数据突变。
- 可见性过滤:
csharp if (landmark.visibility < 0.5f) return; // 忽略低置信度数据 - 速度限制:
csharp Vector3 ClampVelocity(Vector3 target, Vector3 current, float maxSpeed) { Vector3 direction = target - current; if (direction.magnitude > maxSpeed) { return current + direction.normalized * maxSpeed; } return target; }
3. 坐标系与比例校准
- 坐标系对齐:
- MediaPipe使用右手坐标系(Y轴向下),Unity为左手坐标系(Y轴向上,Z轴向前)。
- 转换公式:
csharp Vector3 ConvertToUnitySpace(Vector3 mediapipePoint) { return new Vector3( mediapipePoint.x, -mediapipePoint.y, // Y轴反转 -mediapipePoint.z // Z轴反转 ) * scaleFactor; // 根据场景调整缩放系数 } - 动态比例校准:
csharp // 校准步骤:用户摆出T-Pose时计算缩放系数 float CalculateScaleFactor(Vector3 realWorldShoulderWidth) { Vector3 modelShoulderWidth = rightShoulder.position - leftShoulder.position; return modelShoulderWidth.magnitude / realWorldShoulderWidth.magnitude; }
4. IK权重与过渡控制
- 渐进式权重调整:
csharp float ikWeight = Mathf.Clamp01(Vector3.Distance(currentPos, targetPos) * 0.5f); animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, ikWeight); animator.SetIKPosition(AvatarIKGoal.LeftHand, targetPos); - 根据目标距离动态调整权重,避免动作突变。
- 平滑插值:
csharp currentPos = Vector3.Lerp(currentPos, targetPos, 0.1f); currentRot = Quaternion.Slerp(currentRot, targetRot, 0.1f);
5. 身体协调与约束
- 脊柱与头部跟随:
void UpdateBodyOrientation(Vector3 headPosition) { // 身体朝向跟随头部 Vector3 lookDirection = headPosition - spine.position; spine.rotation = Quaternion.LookRotation(lookDirection, Vector3.up);</p> <p>// 头部微调 neck.rotation = Quaternion.Lerp(neck.rotation, targetHeadRot, 0.3f); } - 关节旋转约束:
csharp // 限制肘部弯曲角度(示例) float ClampElbowAngle(float angle) { return Mathf.Clamp(angle, 20f, 160f); // 限制在20°~160° }
6. 高级技巧:逆向动力学与正向动力学结合
- 混合使用IK与FK:
- 使用IK控制手脚位置,FK控制躯干旋转。
- 示例代码:
csharp void UpdateArm(bool useIK) { if (useIK) { // IK模式:控制手部位置 animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandTarget.position); } else { // FK模式:直接设置关节旋转 upperArm.rotation = CalculateArmRotation(); } } - 脚部地面适配:
csharp void AdjustFootToGround(Transform foot, Vector3 targetPos) { if (Physics.Raycast(targetPos + Vector3.up, Vector3.down, out RaycastHit hit, 2f)) { foot.position = hit.point + Vector3.up * 0.1f; // 保持轻微悬空 foot.rotation = Quaternion.FromToRotation(foot.up, hit.normal) * foot.rotation; } }
7. 调试与优化
- 可视化调试:
csharp void OnDrawGizmos() { Gizmos.color = Color.blue; Gizmos.DrawSphere(leftHandTarget.position, 0.1f); Gizmos.DrawLine(leftShoulder.position, leftHandTarget.position); } - 性能优化:
- 降低更新频率:
csharp [SerializeField] int updateInterval = 2; // 每2帧更新一次 void Update() { if (Time.frameCount % updateInterval == 0) { UpdateIK(); } } - 使用Job System并行处理:
csharp // 定义IJobParallelFor处理多关节计算 public struct IKJob : IJobParallelFor { public NativeArray<Vector3> positions; public void Execute(int index) { // 并行计算每个关节的目标位置 } }
8. 常见问题解决
- 动作抖动:
- 增加数据平滑系数,或添加移动平均滤波。
- 限制最小移动距离(如位移小于0.01单位时不更新)。
- 骨骼扭曲:
- 调整轴向修正参数:
csharp targetRotation *= Quaternion.Euler(90, 0, 0); // 根据模型调整 - 穿透问题:
- 添加碰撞检测,限制关节位置:
csharp if (Physics.CheckSphere(targetPos, 0.1f)) { targetPos = FindNearestValidPosition(targetPos); }
总结
实现平滑自然的IK动作需要: 1. 数据清洗:滤波与校准消除噪声。 2. 权重控制:渐进式过渡避免突变。 3. 运动协调:结合IK与FK保持身体平衡。 4. 约束优化:限制关节范围与物理检测。 5. 高效计算:通过并行处理与频率控制优化性能。
通过逐步调试参数(如平滑系数、权重过渡速度)并配合可视化工具,最终可实现接近真实人体运动的自然效果。