Unity开发记录_7_使用 奥比中光相机 Femto Bolt 进行动作识别

概要

最近研究了一下奥比中光相机 Femto Bolt ,尝试做了个Demo 进行简单的 动作识别。
最后得到的效果是 能够识别:抬起左手、抬起右手、抬起左腿、抬起右腿、抬起双手,五个动作。

项目开始

1.采用Unity插件进行Femto Bolt 连接: Body Tracking for Orbbec Femto Bolt, Mega, & Azure Kinect
在这里插入图片描述
2.采用UI资源 : Fantasy Warrior HUD - Synty INTERFACE - GUI
3.采用模型资源: Modular Stylized Character 1
4.采用场景资源: Virtual Interior 2

动作识别效果:

在这里插入图片描述

关键类

关节数据类JointData、身体数据类BodyData、事件数据类ActionData、玩家行为类PeopleBodyAction

using ATF;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using static System.Collections.Specialized.BitVector32;

namespace AT
{
    /// <summary>
    /// 身体类型
    /// </summary>
    public enum EJointType
    {
        Hips = 0,
        LeftUpperLeg,
        RightUpperLeg,
        LeftLowerLeg,
        RightLowerLeg,
        LeftFoot,
        RightFoot,
        Spine,
        Chest,
        UpperChest,
        Neck,
        Head,
        LeftShoulder,
        RightShoulder,
        LeftUpperArm,
        RightUpperArm,
        LeftLowerArm,
        RightLowerArm,
        LeftHand,
        RightHand,
        LeftToes,
        RightToes,
        LeftEye,
        RightEye,
        Jaw
    }

    /// <summary>
    /// 事件类型
    /// </summary>
    public enum EActionType
    {
        EAction_1 = 0, EAction_2, EAction_3, EAction_4, EAction_5, EAction_6, EAction_7, EAction_8, EAction_9, EAction_10
    }

    /// <summary>
    /// 关节数据
    /// </summary>
    [ES3Serializable]
    public class JointData
    {
        public bool IsOpen;         //是否打开对比
        public float Ratio;         //四元数近似率
        public float Angle;         //角度对比值
        public EJointType Type;     //关节类型
        public Quaternion BodyQua;  //关节数据
        public JointData(EJointType bodyType)
        {
            IsOpen = true;
            Ratio = 1.0f;
            Angle = 0.0f;
            Type = bodyType;
            BodyQua = new Quaternion();
        }
    }

    /// <summary>
    /// 身体数据
    /// </summary>
    [ES3Serializable]
    public class BodyData
    {
        public JointData[] JointList;    //身体数组
        [ES3NonSerializable]
        public bool BodyTrigger;         //身体事件是否触发
        public float Duration;           //持续期间---默认为1s
        public float CountDown;          //倒计时---默认为0
        public int PassingRate;          //通过率---0-100
        [ES3Serializable]
        private string m_uniqueID;       //唯一ID
        public string UniqueID
        {
            get { return m_uniqueID; }
        }

        public BodyData(bool[] bodyArray = null)
        {
            JointList = new JointData[Constant.MAXBODYCOUNT];
            m_uniqueID = UniqueIDUtility.Ins.GetUniqueID();
            BodyTrigger = false;
            Duration = Constant.DURATION;
            CountDown = Constant.COUNTDOWN;
            PassingRate = Constant.MAXPASSINGRATE;
            for (int i = 0; i < Constant.MAXBODYCOUNT; i++)
            {
                JointList[i] = new JointData((EJointType)i);
                if (bodyArray == null)
                {
                    JointList[i].IsOpen = true;
                }
                else
                {
                    foreach (bool state in bodyArray)
                    {
                        JointList[i].IsOpen = state;
                    }
                }
            }
        }
    }

    /// <summary>
    /// 事件数据---每个事件数据里面有25个关节数据
    /// </summary>
    [ES3Serializable]
    public class ActionData
    {
        public string ActionName;                               //事件名称
        public int ActionCount;                                 //事件数量
        public Dictionary<EActionType, BodyData> BodyDataDic;   //身体数据字典
        [ES3NonSerializable]
        public bool ActionTrigger;                              //事件触发
        public ActionData(string name = "", int count = 1, bool[] bodyArray = null)
        {
            ActionName = name;
            ActionCount = count;
            BodyDataDic = new Dictionary<EActionType, BodyData>();
            ActionTrigger = false;
            for (int i = 0; i < ActionCount; i++)
            {
                EActionType eActionType = (EActionType)i;
                BodyData bodyData = new BodyData(bodyArray);
                BodyDataDic.Add(eActionType, bodyData);
            }
        }
    }

    /// <summary>
    /// 身体事件
    /// </summary>
    [ES3Serializable]
    public class PeopleBodyAction
    {
        public string PeopleName;
        [ES3Serializable]
        public Dictionary<string, ActionData> ActionDataDic;
        [ES3NonSerializable]
        public ObservableValue<string,EActionType,bool> OnTriggerChange;
        [ES3NonSerializable]
        public Action<string,EActionType, bool> OnTrigger;
        public PeopleBodyAction()
        {
            PeopleName = "Player1";
            ActionDataDic = new Dictionary<string, ActionData>();
            OnTriggerChange = new ObservableValue<string,EActionType,bool>("",EActionType.EAction_1,false);
        }

        #region 单独赋值
        public void SetBodyState(string actionName,EJointType body, bool isOpen, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action].JointList[(int)body].IsOpen = isOpen;
            }
        }
        public void SetBodyQuaRatio(string actionName, EJointType body, float ratio, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action].JointList[(int)body].Ratio = ratio;
            }
        }
        public void SetBodyAngle(string actionName, EJointType body, float angle, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action].JointList[(int)body].Angle = angle;
            }
        }
        public void SetBodyQua(string actionName, EJointType body, Quaternion qua, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action].JointList[(int)body].BodyQua = qua;
            }
        }
        public void SetBodyDuration(string actionName, EJointType body, float duration, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action].Duration = duration;
            }
        }
        public void SetJointData(string actionName, EJointType body, JointData jointData, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                int bodyIndex = (int)body;
                ActionDataDic[actionName].BodyDataDic[action].JointList[(int)body] = jointData;
            }
        }
        public void SetBodyData(string actionName, BodyData bodyData, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action] = bodyData;
            }
        }
        public void SetPassingRateData(string actionName, int passingRate, EActionType action = EActionType.EAction_1)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                return;
            }
            if (ActionDataDic[actionName] != null)
            {
                ActionDataDic[actionName].BodyDataDic[action].PassingRate = passingRate;
            }
        }
        public void SetActionData(string actionName, ActionData actionData)
        {
            if (!ActionDataDic.ContainsKey(actionName))
            {
                ActionDataDic.Add(actionName,actionData);
            }
            ActionDataDic[actionName] = actionData;
        }
        #endregion
    }
}

项目流程

1.记录特定的人体动作数据---身体关节的四元数
2.将人体动作数据 实时与 当前人体动作 进行比对---利用四元数点乘进行比较
3.判断动作是否被触发---如果关节四元数比较的值在之前录制的范围之内 就判断当前动作被触发

项目地址

百度网盘链接: [https://pan.baidu.com/s/1THpnMyr_CPFIBN40v9VG-A?pwd=6666)

使用步骤

1.接上奥比中光相机 Femto Bolt,点击运行~
2.点击录制,可以录制新动作
3.可以保存存档数据和加载存档数据
4.如果触发了 之前录制的动作,会在打印区打印出来

小结

虽然我的目的是测试这套动作识别能不能识别出一套完整的动作,例如打出一套龟派气功波,但是实际上这个设备没有手指识别,而且相机识别稳定性待提高,只能说勉强验证我的想法 ,如果后面有具体的项目需求了 我再详细研究研究~

### 奥比深度相机Unity中的插件使用指南 #### 插件概述 奥比提供了专门用于Unity开发环境下的SDK工具包,允许开发者轻松集成并利用其深度相机的功能。该插件支持多种功能特性,包括但不限于实时获取RGB-D图像数据流、人体骨骼追踪以及手势识别等功能[^1]。 #### 下载方式 对于希望使用奥比深度相机Unity开发者来说,可以从官方网站下载适用于不同版本Unity引擎对应的最新版Orbbec Unity Plugin SDK软件包。确保所选平台与本地计算机操作系统相匹配[^2]。 #### 安装过程 安装此插件前需先确认已正确连接好硬件设备,并按照官方文档完成驱动程序设置。接着,在Unity项目内通过`Assets/Import Package/Custom Package...`路径导入先前下载好的`.unitypackage`文件来引入必要的脚本库和其他资源到当前工程环境中[^3]。 ```csharp using UnityEngine; using Orbbec; public class DepthCameraExample : MonoBehaviour { private Astra astraDevice; void Start() { // 初始化Astra对象实例化 astraDevice = new Astra(); // 开启颜色帧和深度帧采集服务 astraDevice.StartColorStream(); astraDevice.StartDepthStream(); } void Update() { } } ``` #### 配置说明 成功加载之后还需要进一步调整一些参数选项以适应具体应用场景需求。这通常涉及到修改Inspector面板上的属性字段或是编写自定义C#代码逻辑实现特定交互效果。例如可以设定分辨率大小、刷新率等基本性能指标;也可以启用高级模式下的人脸检测、手部姿态估计等附加模块[^4]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值