为了实现使用MediaPipe实时姿势特征点数据驱动Final IK以达到动作捕捉效果,可以按照以下步骤进行:
步骤概览
- 使用MediaPipe获取姿势数据:通过Python脚本捕获摄像头数据,检测姿势关键点。
- 数据传输:通过UDP将关键点坐标实时发送至Unity。
- Unity接收与处理数据:解析数据并转换为Unity的3D坐标。
- 驱动Final IK:将处理后的数据映射到角色骨骼,利用Final IK实现自然运动。
详细步骤与代码示例
1. MediaPipe姿势检测(Python端)
安装所需库:
pip install mediapipe opencv-python
编写Python脚本捕获摄像头数据并发送关键点:
import cv2
import mediapipe as mp
import socket
import json
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.5)
# UDP配置
UDP_IP = "127.0.0.1" # Unity所在机器的IP
UDP_PORT = 8051
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
cap = cv2.VideoCapture(0) # 摄像头设备号
while cap.isOpened():
success, frame = cap.read()
if not success:
continue
# 转换为RGB并检测姿势
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = pose.process(image)
if results.pose_landmarks:
# 提取所有33个关键点的归一化坐标
landmarks = []
for landmark in results.pose_landmarks.landmark:
landmarks.extend([landmark.x, landmark.y, landmark.z])
# 发送JSON格式数据
sock.sendto(json.dumps(landmarks).encode(), (UDP_IP, UDP_PORT))
# 显示画面(可选)
cv2.imshow('MediaPipe Pose', cv2.flip(frame, 1))
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
2. Unity接收数据(C#脚本)
创建UDPReceiver.cs脚本,接收并解析数据:
using UnityEngine;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
public class UDPReceiver : MonoBehaviour
{
public int port = 8051;
private Thread receiveThread;
private UdpClient udpClient;
private bool isReceiving = false;
// 存储解析后的关键点坐标(33个点,每个点有x,y,z)
public Vector3[] receivedLandmarks = new Vector3[33];
void Start()
{
StartReceiving();
}
void StartReceiving()
{
receiveThread = new Thread(new ThreadStart(ReceiveData));
receiveThread.IsBackground = true;
isReceiving = true;
receiveThread.Start();
}
private void ReceiveData()
{
udpClient = new UdpClient(port);
while (isReceiving)
{
try
{
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, port);
byte[] data = udpClient.Receive(ref remoteEndPoint);
string jsonString = System.Text.Encoding.UTF8.GetString(data);
ParseLandmarks(jsonString);
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
}
}
void ParseLandmarks(string jsonData)
{
List<float> values = new List<float>(JsonUtility.FromJson<float[]>(jsonData));
for (int i = 0; i < 33; i++)
{
int index = i * 3;
float x = values[index];
float y = 1 - values[index + 1]; // 反转Y轴适配Unity
float z = values[index + 2];
receivedLandmarks[i] = new Vector3(x, y, z) * 10; // 缩放坐标(根据场景调整)
}
}
void OnDisable()
{
isReceiving = false;
udpClient.Close();
receiveThread.Abort();
}
}
3. 坐标映射与Final IK驱动
在Unity中设置角色模型和Final IK组件:
-
角色准备:
- 导入带骨骼的角色模型。
- 为需要驱动的身体部位(如手、脚)添加Final IK的
LimbIK组件,并设置对应的IK目标(空物体)。
- 脚本映射关键点:
- 创建
IKController.cs脚本,将接收到的关键点数据映射到IK目标:
- 创建
using UnityEngine;
using RootMotion.FinalIK;
public class IKController : MonoBehaviour
{
public UDPReceiver udpReceiver;
// Final IK组件
public LimbIK leftArmIK;
public LimbIK rightArmIK;
public LimbIK leftLegIK;
public LimbIK rightLegIK;
// IK目标物体
public Transform leftHandTarget;
public Transform rightHandTarget;
public Transform leftFootTarget;
public Transform rightFootTarget;
void Update()
{
if (udpReceiver.receivedLandmarks == null) return;
// 左手腕(MediaPipe索引15)
leftHandTarget.position = udpReceiver.receivedLandmarks[15];
// 右手腕(索引16)
rightHandTarget.position = udpReceiver.receivedLandmarks[16];
// 左脚踝(索引27)
leftFootTarget.position = udpReceiver.receivedLandmarks[27];
// 右脚踝(索引28)
rightFootTarget.position = udpReceiver.receivedLandmarks[28];
// 更新IK计算
leftArmIK.solver.Update();
rightArmIK.solver.Update();
leftLegIK.solver.Update();
rightLegIK.solver.Update();
}
}
- 场景配置:
- 将
UDPReceiver脚本挂载到任意物体(如Camera),并设置port与Python端一致。 - 将
IKController脚本挂载到角色,并拖拽对应IK组件和Target物体到Inspector面板。
- 将
优化与注意事项
- 坐标系调整:根据角色大小调整缩放系数(如上述代码中的
*10),可能需要通过实验确定最佳值。 - 数据平滑:添加滤波(如指数平滑)减少抖动:
// 在ParseLandmarks方法中添加滤波 Vector3 filteredPosition = Vector3.Lerp(receivedLandmarks[i], newPosition, 0.1f); - 根骨骼控制:使用髋部关键点(索引23、24)控制角色整体位置,使角色移动。
- 性能优化:确保Python和Unity的帧率匹配,避免数据堆积。
通过以上步骤,即可实现用MediaPipe实时驱动Final IK的动作捕捉效果。根据实际需求调整关键点映射和IK参数,可获得更自然的动作效果。