当前位置: 首页 > news >正文

基于光学动捕定位下的Unity-VR手柄交互

Unity VR 场景手柄交互实现方案

需求

在已创建好的 Unity VR 场景中,接入游戏手柄,通过结合动捕系统与 VRPN,建立刚体,实时系统获取到手柄的定位数据与按键数据,通过编写代码实现手柄的交互逻辑,实现手柄抓起物体移动放置。

演示视频

在这里插入图片描述

资源工程附件

(评论区回复:VR交互)

实现方案

1. 控制抓取物体的平移运动(不考虑旋转)

仅控制抓取物体的前后上下左右方向的平移运动,不考虑手柄的旋转

using UnityEngine;
using UVRPN.Core;public class HandleInteraction : MonoBehaviour
{public LayerMask targetLayer; // 目标物体所在的层public float rayLength = 5f; // 射线的长度private GameObject selectedObject; // 当前选中的物体private Vector3 offset; // 物体与手柄的相对位置private bool isHoldingObject = false; // 是否正在抓取物体的标志private VRPN_Button vrpnButton; // VRPN_Button 组件的引用void Start(){vrpnButton = GetComponent<VRPN_Button>();// 或者,如果 VRPN_Button 组件在其他 GameObject 上// vrpnButton = GameObject.Find("GameObjectName").GetComponent<VRPN_Button>();}// Update is called once per framevoid Update(){// 发射射线Ray ray = new Ray(transform.position, -transform.forward);RaycastHit hit;// 如果射线击中了目标层上的物体if (Physics.Raycast(ray, out hit, rayLength, targetLayer)){Debug.DrawLine(ray.origin, hit.point, Color.red); // 绘制射线(仅在 Scene 视图中可见)// 检测手柄按钮是否被按下if (vrpnButton.ButtonDown){// 抓取物体selectedObject = hit.collider.gameObject;offset = selectedObject.transform.position - transform.position;isHoldingObject = true;}}// 如果当前有选中的物体,并且手柄按钮仍然被按住if (isHoldingObject && vrpnButton.ButtonHold){// 移动物体,使其保持与手柄的相对位置selectedObject.transform.position = transform.position + offset;}// 如果手柄按钮被松开if (isHoldingObject && vrpnButton.ButtonUp){// 放置物体isHoldingObject = false;selectedObject = null;}}
}

2. 考虑手柄的旋转处理

将选中物体与手柄作为整体,同时增加重力响应与Game视图中的射线渲染

using UnityEngine;
using UVRPN.Core;public class HandleMove : MonoBehaviour
{public LayerMask targetLayer; // 目标物体所在的层public float rayLength = 5f; // 射线的长度public float lineWidth = 0.01f; // 线条宽度    private GameObject selectedObject; // 当前选中的物体private Vector3 originalLocalPosition; // 物体相对于手柄的初始局部位置private Quaternion originalLocalRotation; // 物体相对于手柄的初始局部旋转private VRPN_Button vrpnButton; // VRPN_Button 组件的引用private Rigidbody rigidbody; // 选中物体的刚体组件private LineRenderer lineRenderer; // Game视图中的射线渲染void Start(){vrpnButton = GetComponent<VRPN_Button>();lineRenderer = GetComponent<LineRenderer>();// 设置 LineRenderer 的宽度lineRenderer.startWidth = lineWidth;lineRenderer.endWidth = lineWidth;lineRenderer.enabled = false;}void Update(){// 发射射线Ray ray = new Ray(transform.position, -transform.forward);RaycastHit hit;// 如果射线击中了目标层上的物体if (Physics.Raycast(ray, out hit, rayLength, targetLayer)){Debug.DrawLine(ray.origin, hit.point, Color.red); // 绘制射线(仅在 Scene 视图中可见)// 启用 Line Renderer 并设置位置lineRenderer.enabled = true;lineRenderer.SetPosition(0, ray.origin);lineRenderer.SetPosition(1, hit.point);// 检测手柄按钮是否被按下if (vrpnButton.ButtonDown && !selectedObject){// 抓取物体selectedObject = hit.collider.gameObject;selectedObject.transform.SetParent(transform, true); // 将物体设置为手柄的子对象// 禁用物理引擎响应rigidbody = selectedObject.GetComponent<Rigidbody>();if (rigidbody){rigidbody.isKinematic = true;}// 记录物体相对于手柄的初始局部位置和旋转originalLocalPosition = selectedObject.transform.localPosition;originalLocalRotation = selectedObject.transform.localRotation;}}else{// 如果射线没有击中任何物体,禁用 Line RendererlineRenderer.enabled = false;}// 如果手柄按钮被松开if (vrpnButton.ButtonUp && selectedObject){// 放置物体selectedObject.transform.SetParent(null); // 移除物体的父对象selectedObject.transform.position = transform.TransformPoint(originalLocalPosition); // 重置物体的世界位置selectedObject.transform.rotation = transform.rotation * originalLocalRotation; // 重置物体的世界旋转selectedObject = null;// 启用物理引擎响应if (rigidbody){rigidbody.isKinematic = false;}}}
}

3. 正确处理 VRPN 的坐标转换关系

坐标系转换的原理

已知动捕坐标系(右手)与 Unity 坐标系(左手),转动到同一视角将坐标系对齐如下:

3.1 动捕 Y-UP

即两坐标系 X 轴反向。
在这里插入图片描述
在这里插入图片描述

3.2 动捕 Z-UP

即 Z 与 Y 对调。

注意:转换的方式不止一种,这里选择比较方便的处理方式。
在这里插入图片描述
在这里插入图片描述

3.3 解决坐标系转换问题
1.通过 VRPN 反转设置
  1. 若动捕 Y 轴向上
    在这里插入图片描述

  2. 若动捕Z轴向上
    不支持

2.通过修改 VRPN 脚本(编辑 VRPN_NativeBridge.cs),实现坐标系转换
  1. 若动捕 Y 轴向上
internal static Vector3 TrackerPos(string address, int channel)
{return new Vector3(-(float)vrpnTrackerExtern(address, channel, 0, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 1, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 2, Time.frameCount));
}internal static Quaternion TrackerQuat(string address, int channel)
{return new Quaternion(-(float)vrpnTrackerExtern(address, channel, 3, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 4, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 5, Time.frameCount),-(float)vrpnTrackerExtern(address, channel, 6, Time.frameCount));
}
  1. 若动捕 Z 轴向上
internal static Vector3 TrackerPos(string address, int channel)
{return new Vector3((float)vrpnTrackerExtern(address, channel, 1, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 2, Time.frameCount),-(float)vrpnTrackerExtern(address, channel, 0, Time.frameCount));
}internal static Quaternion TrackerQuat(string address, int channel)
{return new Quaternion((float)vrpnTrackerExtern(address, channel, 3, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 5, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 4, Time.frameCount),-(float)vrpnTrackerExtern(address, channel, 6, Time.frameCount));
}
3.4 创建刚体时的朝向,下面以Y轴向上为例进行讲解通用的处理方式
  1. 打开VR_box的空间交互场景

  2. 选中手柄,按W键进行Move状态,点击工具栏上的Toggle Tool Handle Rotation,选择Local
    在这里插入图片描述

  3. 观察确定场景中手柄道具的朝向,此时手柄物体指向自身的Z轴负方向
    在这里插入图片描述

  4. 编辑VRPN_Tracker的设置,勾选local(由于此物体为顶层物体无父对象,其world=local, 这里通用处理)
    在这里插入图片描述

  5. 在动捕系统中放置实体手柄,由于此时动捕Z轴与UnityZ轴(世界坐标系)重合,朝向Z轴的负方向创建刚体

  6. 参考3.1坐标系转换的关系,对vrpn参数进行设置,其中position反转X,rotation反转Y与Z,轴向反转一次,左右手法则角度再反一次,所以X的rotation不变

4. 使用说明

  1. 解压工程附件(Unity_Joystick_Demo V1.0.zip),打开 Assets->Scenes->VR_box.unity
  2. 运行动捕软件,完成坐标系的标定,与蓝牙手柄的连接,并启用 VRPN,选择刚体类型,设置数据单位为米。
  3. 在动捕软件中创建手柄对应的刚体,其中手柄朝向参考 3.4。
  4. 配置 VRPN_TrackerVRPN_Button 组件,设置正确的 Tracker 名称。
  5. 运行 Unity 程序,测试不同方向的移动与旋转是否正常,按下手柄按键,观察是否有打印输出。

思考题

若动捕坐标系如下,其中 X 轴正方向指向显示器/墙面,对应着 Unity 中手柄前方的交互场景,如何处理转换关系?
在这里插入图片描述
在这里插入图片描述

参考答案

手柄面向 X 轴正方向创建刚体,同时数据处理如下:

internal static Vector3 TrackerPos(string address, int channel)
{return new Vector3((float)vrpnTrackerExtern(address, channel, 1, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 2, Time.frameCount),-(float)vrpnTrackerExtern(address, channel, 0, Time.frameCount));
}internal static Quaternion TrackerQuat(string address, int channel)
{return new Quaternion((float)vrpnTrackerExtern(address, channel, 4, Time.frameCount),(float)vrpnTrackerExtern(address, channel, 5, Time.frameCount),-(float)vrpnTrackerExtern(address, channel, 3, Time.frameCount),-(float)vrpnTrackerExtern(address, channel, 6, Time.frameCount));
}

相关文章:

基于光学动捕定位下的Unity-VR手柄交互

Unity VR 场景手柄交互实现方案 需求 在已创建好的 Unity VR 场景中&#xff0c;接入游戏手柄&#xff0c;通过结合动捕系统与 VRPN&#xff0c;建立刚体&#xff0c;实时系统获取到手柄的定位数据与按键数据&#xff0c;通过编写代码实现手柄的交互逻辑&#xff0c;实现手柄…...

php json_decode 带反斜杠字符串json解析

PHP json_decode 带反斜杠字符串json解析 今天再次遇到了json字符串中包含反斜杠的问题&#xff0c;记录下解决方法 在JSON字符串中&#xff0c;反斜杠\用作转义字符。当JSON_UNESCAPED_SLASHES选项被用于json_encode()函数时&#xff0c;不会在slashes前面添加反斜杠。 但是…...

【NLP】文本张量表示方法【word2vec、词嵌入】

文章目录 1、文本张量表示2、one-hot词向量表示2.1、one-hot编码代码实现&#xff1a;2.2、onehot编码器的使用2.3、one-hot编码的优劣势 3、word2vec模型3.1、模型介绍3.2、CBOW模式3.3、skipgram模式3.4、word2vec的训练和使用3.4.1、获取训练数据3.4.2、训练词向量3.4.3、查…...

疯狂Java讲义_08_泛型

文章目录 泛型的传参若函数里的参数使用基类接受所有的派生类&#xff0c;怎么做&#xff1f; 类型通配符的上限类型通配符的下限 泛型的传参 注意 若类 Base 是类 Derived 的基类&#xff08;父类&#xff09;&#xff0c;那么数组类型 Base[] 是 Derived[] 的基类&#xff0…...

HCIA、OSPF笔记

一、OSI参考模型 1、OSI的结构 应用层&#xff1a;把人类语言转化成编码&#xff0c;为各种应用程序提供网络服务。 表示层&#xff1a;定义一些数据的格式&#xff0c;&#xff08;对数据进行加密、解密、编码、解码、压缩、解压缩&#xff0c;每一层都可以实现&#xff0c…...

Python删除lru_cache缓存

在 Python 中,lru_cache 是一个装饰器,用于添加缓存功能以提高函数的性能。如果你想清除或者删除 lru_cache 中的缓存,有几种方法可以做到: 手动清除缓存: lru_cache 对象有一个方法叫做 cache_clear(),可以手动清除所有缓存。示例:@lru_cache(maxsize=128) def some_fun…...

Android面试必问题:大白文讲透Android View工作原理

目录 第一章 引言 第二章 Android View 基础概念 2.1 视图(View) 2.2 布局(Layout) 2.3 绘制(Drawing) 第三章 Android View 工作原理详解 3.1 测量过程剖析 3.2 布局流程探究 第四章 Android View 性能优化建议 4.1 视图层级优化 4.2 避免过度的视觉效果 4.…...

WinDbg配置远程调试

WinDbg配置远程调试 1、为什么需要远程调试 某些特殊的场合需要远程调试&#xff0c;如&#xff1a; ①调试特殊的程序&#xff0c;比如在调试全屏程序&#xff0c;内核。 ②需要别人帮助调试或者帮助别人调试。比如由于商业性质不能直接给你pdb和源代码。 ③还有一类就是…...

spl注入实战thinkphp

目录 一、环境的部署 二、本地创建数据库 三、填写数据库连接文件 四、编写控制器 五、访问分析 debug报错会显示物理路径 原因是config.php文件相关配置 六、注入分析 七、进入断点调试 八、通过mysql执行语句查看结果 九、总结&#xff1a; 一、环境的部署 二、本地…...

整理深度学习时最常用的Linux命令(自用)

清华大学镜像源&#xff1a; https://pypi.tuna.tsinghua.edu.cn/simple/tar文件解压 tar -xzvf xxx.tar.gztar xvf xxx.tarzip文件解压 unzip xxx.zip -d path/to/your/fold清理GPU异常内存占用 杀掉 1 号显卡的所有进程 fuser -v /dev/nvidia1 | xargs -t -n 1 kill -9杀掉…...

LVS——>linux 虚拟服务器知识汇总

一、概念&#xff1a; LVS&#xff08;Linux Virtual Server&#xff09;&#xff0c;是Linux Virtual Server的简写&#xff0c;也就是Linux 虚拟服务器&#xff0c;是一个虚拟的服务器集群系统负载均衡解决方案&#xff0c;它将一个真实服务器集群虚拟成一台服务器来对外提供…...

AI赋能周界安防:智能视频分析技术构建无懈可击的安全防线

周界安全防范是保护机场、电站、油库、监狱、工业园区等关键设施免受非法入侵和破坏的重要措施。传统的周界安防手段主要依靠人员巡查和物理屏障&#xff0c;但这种方式不仅人力成本高&#xff0c;而且效率较低&#xff0c;难以满足日益复杂多变的安全需求。随着AI技术的引入&a…...

FastAPI+Vue3工程项目管理系统项目实战私教课 上课笔记20240808 课程和学习计划制定

学习目标 将Word和Excel做的东西放到数据库里面去工程类公司&#xff0c;甲方&#xff0c;劳务存到数据库存储的信息主要是人员的信息 基本信息&#xff1a; 人员信息&#xff0c;资料库&#xff0c;甲方的人出现在哪些项目上&#xff0c;考勤材料信息&#xff0c;进货记录&…...

Robot Operating System——发布相对湿度数据

大纲 应用场景定义字段解释 案例 sensor_msgs::msg::RelativeHumidity 是 ROS (Robot Operating System) 中的一个消息类型&#xff0c;用于表示相对湿度数据。 应用场景 环境监测 气象站&#xff1a;在气象站中&#xff0c;相对湿度传感器可以用于监测环境湿度&#xff0c;帮…...

一文搞懂后端面试之不停机数据迁移【中间件 | 数据库 | MySQL | 数据一致性】

数据迁移方面的工作&#xff1a; 重构老系统&#xff1a;使用新的表结构来存储数据单库拆分分库分表、分库分表扩容大表修改表结构定义 数据备份工具 MySQL上常用的两款数据备份工具&#xff1a;mysqldump和XtraBackup mysqldump&#xff1a;一个用于备份和恢复数据库的命令…...

【ESP01开发实例】- ISD1820录音控制

ISD1820录音控制 文章目录 ISD1820录音控制1、ISD1820模块介绍2、硬件准备及接线3、代码以实现录音技术已经取得了长足的进步,它已成为从语音助手到安全系统的各种应用不可或缺的一部分。如果您有兴趣构建自己的录音系统,将 ISD1820 模块与 ESP01 微控制器相结合可能是一个不…...

Linux驱动面试高频考点后面继续改整理

Linux驱动开发是将硬件设备与操作系统内核连接起来的重要环节&#xff0c;它涉及到设备模型、中断处理、文件操作等方面&#xff0c;是一项挑战性且充满乐趣的工作。今天给大家分享45道Linux驱动面试高频考点&#xff0c;直接上干货。 1、驱动程序分为几类&#xff1f; 内核驱动…...

【Python】nn.ConvTranspose1、2、3d()函数详解和示例

前言 在深度学习中&#xff0c;特别是在处理图像、音频和三维数据时&#xff0c;转置卷积&#xff08;Transposed Convolution&#xff09;或称为反卷积&#xff08;Deconvolution&#xff09;是一种非常重要的操作。PyTorch提供了nn.ConvTranspose1d、nn.ConvTranspose2d和nn…...

vtkConnectivityFilter提取连通区域中的问题

直接使用vtkConnectivityFilter提取连通区域&#xff0c;渲染上没问题&#xff0c;但是打印出polydata中的点数&#xff0c;发现跟原始数据是一致的。 for (int i 0; i < numRegions; i){vtkSmartPointer<vtkConnectivityFilter> connectivityFilter vtkSmartPointe…...

购物系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;特价商品管理&#xff0c;用户管理&#xff0c;留言板管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

OCR MLLM Evaluation

为什么需要评测体系&#xff1f;——背景与矛盾 ​​ 能干的事&#xff1a;​​ 看清楚发票、身份证上的字&#xff08;准确率>90%&#xff09;&#xff0c;速度飞快&#xff08;眨眼间完成&#xff09;。​​干不了的事&#xff1a;​​ 碰到复杂表格&#xff08;合并单元…...

JDK 17 序列化是怎么回事

如何序列化&#xff1f;其实很简单&#xff0c;就是根据每个类型&#xff0c;用工厂类调用。逐个完成。 没什么漂亮的代码&#xff0c;只有有效、稳定的代码。 代码中调用toJson toJson 代码 mapper.writeValueAsString ObjectMapper DefaultSerializerProvider 一堆实…...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...