diff --git a/docs/pico4_ultra_udp_teleop_setup.md b/docs/pico4_ultra_udp_teleop_setup.md index 9461e7b..0bcfb1c 100644 --- a/docs/pico4_ultra_udp_teleop_setup.md +++ b/docs/pico4_ultra_udp_teleop_setup.md @@ -231,6 +231,24 @@ PICO UDP JSON -> udp_controller_receiver -> /xr/left_controller, /xr/right_contr -> UDP 发到 Ubuntu ROS 主机:15000 ``` +当前仓库进度,2026-05-28 检查: + +- Unity 工程已存在:`unity/XR_RM_PICO_UDP_Sender`。 +- PICO SDK 已放在:`unity/PICO-Unity-Integration-SDK-release_3.4.0`。 +- 5.2 的本地 Unity 工程配置基本完成:项目版本是 Unity `2022.3.16f1c1`,Android 包名是 `com.local.xr_rm_udp_sender`,最小 API 是 29,网络权限 `ForceInternetPermission` 已开启,PICO XR Loader 已配置,`Assets/Scenes/Main.unity` 已加入 build scenes。 +- 5.5 的最小 UDP 发送脚本已经完成:`Assets/Scripts/PicoControllerUdpSender.cs` 会读取 `XRNode.LeftHand/RightHand`,发送 `controllers.left/right` JSON,默认 `host=192.168.9.89`、`port=15000`、`sendHz=60`、`convertUnityToProjectCoordinates=true`。 +- 场景中已经挂载 `PicoControllerUdpSender` 对象,序列化参数与脚本默认值一致。 +- 本机已经生成过 APK:`unity/XR_RM_PICO_UDP_Sender/Builds/XR-RM-PICO-UDP-Sender.apk`。这是 Unity 生成产物,通常不提交到 git。 + +仍需现场确认: + +- `192.168.9.89` 是否仍然是 Ubuntu ROS 主机当前局域网 IP。如果不是,先在 Unity Inspector 或 `Assets/Editor/XrRmUdpSenderProjectSetup.cs` 中改成新的 IP,再重新 build APK。 +- 还没有证据表明 APK 已安装到 PICO。 +- 还没有证据表明 PICO 真机运行时已经向 Ubuntu 发 UDP 包。 +- 还没有证据表明 ROS 端 `/xr/left_controller` 和 `/xr/right_controller` 已收到 PICO 真机数据。 + +因此当前应从 5.4 继续:先把已经生成的 APK 安装到 PICO。如果你已经用 Unity `Build And Run` 安装成功,则跳过 5.4,从 5.3 和第 6 节的 ROS 端验证继续。 + ### 5.1 PICO 和 Ubuntu 网络配置 1. PICO 4 Ultra 和 Ubuntu ROS 主机连接到同一个局域网。 diff --git a/docs/ubuntu22_unity_pico_udp_sender_prompt.md b/docs/ubuntu22_unity_pico_udp_sender_prompt.md new file mode 100644 index 0000000..f5cd246 --- /dev/null +++ b/docs/ubuntu22_unity_pico_udp_sender_prompt.md @@ -0,0 +1,797 @@ +# Ubuntu 22.04 Unity Prompt:复现 XR_RM_PICO_UDP_Sender + +本文档用于在 Ubuntu 22.04 下搭建 Unity + PICO 4 Ultra Android 开发环境,并给出一份可以直接交给 Codex、Claude Code 或其他代码代理的 Unity 项目生成提示词。目标是最终做出与当前仓库中 `unity/XR_RM_PICO_UDP_Sender` 等价的 APK: + +```text +PICO 4 Ultra 双手柄 6DoF pose / grip / trigger + -> Unity Android APK + -> 60 Hz UDP JSON,默认发往 ROS2 主机 15000 端口 + -> xr_rm_input/udp_controller_receiver + -> /xr/left_controller 与 /xr/right_controller + -> xr_rm_teleop/single_arm_velocity_teleop +``` + +截至 2026-05-29,当前项目事实如下: + +- 当前 Unity 工程路径:`unity/XR_RM_PICO_UDP_Sender` +- 当前工程版本:`2022.3.16f1c1` +- 当前 PICO SDK:`unity/PICO-Unity-Integration-SDK-release_3.4.0` +- 当前 PICO package 引用方式:`Packages/manifest.json` 中使用本地路径 `file:../../PICO-Unity-Integration-SDK-release_3.4.0` +- 当前默认 UDP 目标:`192.168.9.89:15000` +- 当前默认发送频率:`60 Hz` +- 当前 Android 包名:`com.local.xr_rm_udp_sender` +- 当前 APK 名称:`Builds/XR-RM-PICO-UDP-Sender.apk` + +## 1. Unity 版本选择建议 + +优先建议使用 Unity 2022.3 LTS,而不是直接升级到 Unity 6。 + +原因: + +- 当前工程已经用 Unity 2022.3 系列创建。 +- PICO 官方 PICO 4 Ultra 资源页说明 PICO Unity Integration SDK 3.0 支持 Unity 2020-23,Unity 6 支持仍属于计划项。 +- Unity 6 对 Ubuntu 22.04 的官方 Linux Editor 支持更清晰,但当前 PICO SDK 主线更适合 Unity 2022.3。 +- Unity 2022.3 官方系统要求页列出的 Linux Editor 发行版是 Ubuntu 20.04 和 18.04,不是 22.04;因此在 Ubuntu 22.04 上使用 2022.3 时,建议保持 GNOME + X11、使用官方显卡驱动,并避免 Wayland 相关变量干扰。 + +推荐策略: + +1. 如果要最大程度复现当前工程:使用 Unity `2022.3.16f1` 或最接近的 2022.3 LTS 国际版补丁。当前工程的 `2022.3.16f1c1` 是中国版编辑器标识,在 Ubuntu 国际版 Hub 中通常选择不带 `c1` 的对应版本或相近补丁。 +2. 如果是新建工程并希望减少 Linux/Android 构建问题:使用 Unity `2022.3.31f1` 或 Unity Hub 中较新的 `2022.3.x LTS` 补丁,但不要升到 Unity 6,除非你已经确认所用 PICO SDK 版本明确支持 Unity 6。 +3. 如果首要目标是严格官方支持 Ubuntu 22.04 Linux Editor,而不是 PICO SDK 稳定性,可以评估 Unity 6;但这会偏离当前项目,本文后续步骤不以 Unity 6 为默认路线。 + +## 2. Ubuntu 22.04 环境准备 + +### 2.1 系统与图形环境 + +建议使用: + +- Ubuntu 22.04 x86_64 +- GNOME 桌面 +- X11 会话优先 +- NVIDIA 用户使用官方 proprietary driver;AMD 用户使用 Mesa 驱动 +- 至少 16 GB RAM,磁盘预留 40 GB 以上 + +检查当前会话: + +```bash +echo $XDG_SESSION_TYPE +``` + +如果输出是 `wayland` 且 Unity Editor 或 Hub 出现黑屏、鼠标偏移、构建窗口异常,注销后在登录界面选择 `Ubuntu on Xorg`。 + +如果 Unity Editor 报 `Pipe error !`,先在启动 Unity 的终端里执行: + +```bash +ulimit -n 4096 +unityhub +``` + +### 2.2 安装 Unity Hub + +```bash +sudo apt update +sudo apt install -y curl gpg ca-certificates + +sudo install -d /etc/apt/keyrings +curl -fsSL https://hub.unity3d.com/linux/keys/public \ + | sudo gpg --dearmor -o /etc/apt/keyrings/unityhub.gpg + +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/unityhub.gpg] https://hub.unity3d.com/linux/repos/deb stable main" \ + | sudo tee /etc/apt/sources.list.d/unityhub.list + +sudo apt update +sudo apt install -y unityhub +``` + +启动 Hub: + +```bash +unityhub +``` + +首次启动需要登录 Unity 账号并激活 Personal 或其他可用 license。 + +### 2.3 安装 Unity Editor 和 Android 模块 + +在 Unity Hub 中: + +1. 打开 `Installs`。 +2. 点击 `Install Editor`。 +3. 选择 `2022.3 LTS`。 +4. 模块必须勾选: + - `Android Build Support` + - `Android SDK & NDK Tools` + - `OpenJDK` +5. 安装完成后,在 `Installs` 页面确认对应 Editor 存在。 + +不要优先使用系统 Android Studio 的 SDK、NDK、JDK。Unity Android 构建最稳的方式是使用 Unity Hub 随 Editor 安装的 embedded Android SDK/NDK/OpenJDK。 + +### 2.4 安装 ADB + +ADB 用于安装 APK、查看设备和抓取 logcat: + +```bash +sudo apt update +sudo apt install -y android-tools-adb +adb version +``` + +### 2.5 准备 PICO 4 Ultra + +在 PICO 4 Ultra 中: + +1. 登录设备。 +2. 开启开发者模式。 +3. 开启 USB 调试。 +4. 用 USB-C 线连接 Ubuntu 主机。 +5. 在 Ubuntu 上执行: + +```bash +adb devices +``` + +PICO 内会弹出 USB 调试授权,选择允许。再次执行: + +```bash +adb devices +``` + +应看到类似: + +```text + device +``` + +如果是 `unauthorized`,重新插拔 USB 并在 PICO 内确认授权。 + +## 3. 打开当前 Unity 工程 + +如果你是在本仓库中继续工作,目录应类似: + +```text +XR_Realman/ + docs/ + unity/ + PICO-Unity-Integration-SDK-release_3.4.0/ + XR_RM_PICO_UDP_Sender/ + xr_rm_input/ + xr_rm_teleop/ +``` + +在 Unity Hub 中: + +1. 点击 `Projects`。 +2. 点击 `Add`。 +3. 选择: + +```text +XR_Realman/unity/XR_RM_PICO_UDP_Sender +``` + +4. 用 Unity 2022.3 LTS 打开。 +5. 如果提示升级工程版本,先备份或确认 git 状态,再继续。 + +本工程的 `Packages/manifest.json` 依赖: + +```json +{ + "dependencies": { + "com.unity.xr.picoxr": "file:../../PICO-Unity-Integration-SDK-release_3.4.0", + "com.unity.xr.management": "4.4.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} +``` + +如果 Unity 打开后找不到 `com.unity.xr.picoxr`,先确认 PICO SDK 文件夹确实位于: + +```text +XR_Realman/unity/PICO-Unity-Integration-SDK-release_3.4.0 +``` + +## 4. Android 构建设置 + +当前工程已经提供菜单脚本: + +```text +Assets/Editor/XrRmUdpSenderProjectSetup.cs +``` + +在 Unity Editor 菜单中依次执行: + +```text +XR-RM -> Apply UDP Sender Android Settings +XR-RM -> Create Or Refresh UDP Sender Scene +XR-RM -> Build Android APK +``` + +这些菜单会配置或刷新: + +- `Product Name = XR-RM-PICO-UDP-Sender` +- `Package Name = com.local.xr_rm_udp_sender` +- Android min SDK = API 29 +- Android target SDK = Auto +- Internet permission = enabled +- Scripting Backend = IL2CPP +- Target Architectures = ARM64 +- Graphics API = OpenGLES3 +- XR Management Android loader = PICO `PXR_Loader` +- `Assets/Scenes/Main.unity` +- 场景中的 `PicoControllerUdpSender` 对象 +- `PicoControllerUdpSender` 和 `PicoUdpConfigPanel` 组件 + +也可以用 batchmode 构建: + +```bash +cd /path/to/XR_Realman + +UNITY_EDITOR="$HOME/Unity/Hub/Editor/2022.3.31f1/Editor/Unity" + +"$UNITY_EDITOR" \ + -batchmode \ + -quit \ + -projectPath "$(pwd)/unity/XR_RM_PICO_UDP_Sender" \ + -executeMethod XrRmUdpSenderProjectSetup.BuildAndroidApk \ + -logFile "$(pwd)/unity_build.log" +``` + +根据你实际安装版本修改 `2022.3.31f1`。构建成功后 APK 在: + +```text +unity/XR_RM_PICO_UDP_Sender/Builds/XR-RM-PICO-UDP-Sender.apk +``` + +安装: + +```bash +adb install -r unity/XR_RM_PICO_UDP_Sender/Builds/XR-RM-PICO-UDP-Sender.apk +``` + +## 5. 网络和 ROS2 联调 + +### 5.1 找到 Ubuntu 主机 IP + +PICO 和 Ubuntu 主机必须在同一个局域网中。查看 Ubuntu IP: + +```bash +hostname -I +ip -4 addr +``` + +假设 Ubuntu 主机 IP 是: + +```text +192.168.9.89 +``` + +则 Unity APK 中 Target IP 使用 `192.168.9.89`,Target Port 使用 `15000`。 + +如果启用了 UFW: + +```bash +sudo ufw allow 15000/udp +sudo ufw status +``` + +### 5.2 启动 ROS2 mock 验证链路 + +在 Ubuntu 的 ROS2 workspace 根目录: + +```bash +source /opt/ros/humble/setup.bash +colcon build --symlink-install +source install/setup.bash + +ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=true +``` + +另开终端检查话题: + +```bash +source /opt/ros/humble/setup.bash +source install/setup.bash + +ros2 topic echo /xr/left_controller +ros2 topic echo /xr/right_controller +ros2 topic echo /xr_rm/left_rm75/cmd_vel +ros2 topic echo /xr_rm/right_rm75/cmd_vel +``` + +### 5.3 在 PICO 内配置和操作 APK + +戴上 PICO 后打开 `XR-RM-PICO-UDP-Sender`。 + +面板字段: + +| 行 | 含义 | 推荐值 | +| --- | --- | --- | +| `Target IP` | ROS2 Ubuntu 主机 IP | 例如 `192.168.9.89` | +| `Target Port` | ROS2 UDP 接收端口 | `15000` | +| `Send Rate` | UDP 发送频率 | `60 Hz` 或 `90 Hz` | +| `Coordinates` | 是否转换 Unity 坐标 | `Project` | +| `UDP Sending` | 是否发送 | `ON` | +| `Save & Apply` | 保存到 PlayerPrefs 并应用 | 按 trigger | + +手柄操作: + +- 摇杆上/下:选择行 +- 摇杆左/右:调整端口、频率、坐标模式或开关发送 +- Trigger / A / X:编辑当前行或触发当前行 +- B / Y:保存并应用 +- Menu:显示或隐藏面板 +- Grip:ROS 侧遥操作使能;未按下时机械臂停止 + +验证现象: + +- 面板中 `L ok / R ok` 表示 Unity 能读到左右手柄。 +- `UDP Sending ON` 后,ROS2 的 `/xr/left_controller` 与 `/xr/right_controller` 应持续刷新。 +- 按住 grip 并移动手柄时,mock 模式下 `/xr_rm/*/cmd_vel` 应出现非零速度。 +- 松开 grip 后,`cmd_vel` 应回到零速度。 + +## 6. UDP JSON 协议 + +Unity APK 每个周期发送一个双手柄 JSON 包: + +```json +{ + "t": 12.345, + "frame_id": "xr_world", + "controllers": { + "left": { + "grip": true, + "trigger": 0.0, + "pos": [-0.12, 1.05, 0.30], + "quat": [0.0, 0.0, 0.0, 1.0] + }, + "right": { + "grip": true, + "trigger": 0.4, + "pos": [0.12, 1.05, 0.30], + "quat": [0.0, 0.0, 0.0, 1.0] + } + } +} +``` + +字段要求: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `t` | number | `Time.realtimeSinceStartupAsDouble` | +| `frame_id` | string | 固定 `xr_world` | +| `controllers.left` | object | 左手柄 | +| `controllers.right` | object | 右手柄 | +| `grip` | bool | grip 使能,ROS 遥操作使用它启停 | +| `trigger` | float | `0.0-1.0` | +| `pos` | float[3] | `[x, y, z]` | +| `quat` | float[4] | `[qx, qy, qz, qw]` | + +坐标转换必须与当前工程保持一致: + +```csharp +payload.pos[0] = position.x; +payload.pos[1] = position.y; +payload.pos[2] = -position.z; + +payload.quat[0] = -rotation.x; +payload.quat[1] = -rotation.y; +payload.quat[2] = rotation.z; +payload.quat[3] = rotation.w; +``` + +当禁用发送或 App 退出时,必须额外发一次左右手柄 `grip=false` 的包,让 ROS 侧安全停止。 + +## 7. 给代码代理的 Unity 生成 Prompt + +下面整段可以直接交给 Codex 或 Claude Code,让它在一个空 Unity 工程中生成与当前 `XR_RM_PICO_UDP_Sender` 等价的项目。 + +```text +你是一个 Unity + PICO 4 Ultra + ROS2 UDP 遥操作项目的代码代理。请在 Ubuntu 22.04 上可打开和构建的 Unity Android 工程中,实现一个与 XR_RM_PICO_UDP_Sender 等价的 PICO UDP 发送端。 + +目标效果: + +PICO 4 Ultra APK 运行后读取左右手柄 6DoF pose、grip、trigger,以 60 Hz 或用户配置频率向 ROS2 Ubuntu 主机发送 UDP JSON。ROS2 侧的 xr_rm_input/udp_controller_receiver 能直接解析,并发布 /xr/left_controller 与 /xr/right_controller。用户在 PICO 内可以通过世界空间 UI 面板修改 Target IP、Target Port、Send Rate、坐标转换模式、UDP Sending 开关,并保存到 PlayerPrefs。松开 grip、暂停发送或退出 App 时,ROS 侧必须能收到 grip=false 或因超时安全停止。 + +开发环境约束: + +- Unity 版本优先使用 2022.3 LTS。 +- Android 平台。 +- PICO 4 Ultra。 +- PICO Unity Integration SDK 使用本地 package 引用。 +- 不引入额外第三方依赖。 +- 使用 UnityEngine.XR.InputDevices / CommonUsages 获取手柄输入。 +- 使用 System.Net.Sockets.UdpClient 发送 UDP。 +- 使用 Unity JsonUtility 序列化 JSON。 +- 使用内置 UGUI Text/Image/Canvas 生成配置面板,不依赖 TextMeshPro。 + +工程结构要求: + +Assets/ + Scripts/ + PicoControllerUdpSender.cs + PicoUdpConfigPanel.cs + Editor/ + XrRmUdpSenderProjectSetup.cs + Scenes/ + Main.unity + +Packages/manifest.json 需要包含: + +{ + "dependencies": { + "com.unity.xr.picoxr": "file:../../PICO-Unity-Integration-SDK-release_3.4.0", + "com.unity.xr.management": "4.4.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} + +一、实现 PicoControllerUdpSender.cs + +public 字段: + +- host,默认 "192.168.9.89" +- port,默认 15000 +- sendHz,默认 60.0f +- convertUnityToProjectCoordinates,默认 true +- sendOnStart,默认 true + +只读状态属性: + +- SendEnabled +- HasEndpoint +- LeftDeviceValid +- RightDeviceValid +- LastError +- LastSendTime + +PlayerPrefs key: + +- xr_rm_udp_sender.host +- xr_rm_udp_sender.port +- xr_rm_udp_sender.send_hz +- xr_rm_udp_sender.convert_coordinates + +生命周期: + +- OnEnable: + - LoadConfig() + - ReopenClient() + - sendEnabled = sendOnStart + - nextSendTime = 0 +- Update: + - 如果 sendEnabled=false,直接 return + - 按 sendHz 节流 + - packet.t = Time.realtimeSinceStartupAsDouble + - FillController(XRNode.LeftHand, left payload) + - FillController(XRNode.RightHand, right payload) + - SendPacket() +- OnDisable: + - 将左右 payload.grip=false + - SendPacket() + - 关闭 UdpClient + +JSON 数据结构: + +Packet: +- double t +- string frame_id = "xr_world" +- Controllers controllers + +Controllers: +- ControllerPayload left +- ControllerPayload right + +ControllerPayload: +- bool grip +- float trigger +- float[] pos = new float[] {0.0f, 1.0f, 0.0f} +- float[] quat = new float[] {0.0f, 0.0f, 0.0f, 1.0f} + +ApplyConfig(newHost, newPort, newSendHz, newConvert, save): + +- Trim host +- IPAddress.TryParse 校验 IP +- port 必须在 1-65535 +- sendHz clamp 到 1-120 +- 更新字段并 ReopenClient(address) +- save=true 时写 PlayerPrefs +- 失败时写 LastError 并返回 false + +SetSending(enabled): + +- 如果从 true 切到 false,先发送一次左右 grip=false +- 更新 sendEnabled +- nextSendTime = 0 + +FillController: + +- InputDevice device = InputDevices.GetDeviceAtXRNode(node) +- 更新 LeftDeviceValid / RightDeviceValid +- device invalid 时 payload.grip=false, payload.trigger=0, return +- 读取: + - CommonUsages.devicePosition -> Vector3 position + - CommonUsages.deviceRotation -> Quaternion rotation + - CommonUsages.trigger -> float trigger + - CommonUsages.grip -> float gripValue + - CommonUsages.gripButton -> bool gripButton +- payload.grip = gripButton || gripValue > 0.5f +- payload.trigger = Mathf.Clamp01(trigger) +- 如果 convertUnityToProjectCoordinates=true: + - pos = [position.x, position.y, -position.z] + - quat = [-rotation.x, -rotation.y, rotation.z, rotation.w] +- 否则: + - pos = [position.x, position.y, position.z] + - quat = [rotation.x, rotation.y, rotation.z, rotation.w] + +SendPacket: + +- client 或 endpoint 为空时 return +- JsonUtility.ToJson(packet) +- UTF8 编码 +- client.Send(bytes, bytes.Length, endpoint) +- 成功时 LastSendTime=Time.realtimeSinceStartupAsDouble,LastError="" +- 异常时 LastError=exception.Message + +二、实现 PicoUdpConfigPanel.cs + +功能: + +- 和 PicoControllerUdpSender 放在同一个 GameObject。 +- Awake 时查找 sender,缺失则 AddComponent。 +- Start 时: + - Application.targetFrameRate = targetFrameRate,默认 90 + - QualitySettings.vSyncCount = 0 + - Screen.sleepTimeout = SleepTimeout.NeverSleep + - XRSettings.eyeTextureResolutionScale = 1.0f + - 创建世界空间配置面板 + - 从 sender 加载 draft + - showOnStart 控制面板初始显示 +- Update 时处理系统键盘、手柄输入、刷新面板。 + +public 字段: + +- targetFrameRate = 90 +- showOnStart = true +- togglePanelWithMenuButton = true + +面板: + +- 如果没有 Camera.main,创建 "XR-RM Main Camera",tag=MainCamera,near=0.05,far=100,SolidColor 深色背景,并尽量添加 TrackedPoseDriver。 +- 创建 "XR-RM UDP Config Canvas",WorldSpace,挂在 camera 下,localPosition=(0,0,2),localScale=0.0014,sizeDelta=(980,640)。 +- 背景 Image 深色半透明。 +- 标题:"XR-RM PICO UDP Sender" +- 状态行显示: + - endpoint + - sending/paused + - L ok / --, R ok / -- + - panelMessage 或 sender.LastError +- 6 行配置: + 0. Target IP + 1. Target Port + 2. Send Rate + 3. Coordinates + 4. UDP Sending + 5. Save & Apply +- 页脚: + "Stick Up/Down: select Stick Left/Right: adjust Trigger/A/X: edit B/Y: save Menu: show/hide" + +输入: + +- CommonUsages.menuButton:显示/隐藏面板。 +- CommonUsages.primary2DAxis: + - y > 0.55 上移选择 + - y < -0.55 下移选择 + - x > 0.65 当前值增加或切换 + - x < -0.65 当前值减少或切换 + - 需要 AxisRepeatDelay=0.22f 防连发 +- Trigger / primaryButton / triggerButton / trigger value > 0.75:编辑或激活当前行。 +- secondaryButton:保存并应用。 + +编辑: + +- Target IP 使用 TouchScreenKeyboardType.URL。 +- Target Port 使用 NumberPad。 +- Send Rate 使用 NumberPad。 +- 端口 clamp 1-65535。 +- Send Rate clamp 1-120。 +- Coordinates 行切换 convertDraft。 +- UDP Sending 行调用 sender.SetSending(!sender.SendEnabled)。 +- Save & Apply 行调用 sender.ApplyConfig(hostDraft, portDraft, sendHzDraft, convertDraft, true)。 + +UI 文本: + +- 使用 Resources.GetBuiltinResource("Arial.ttf")。 +- Text 支持 wrap、truncate、best fit。 +- selected 行用亮蓝色和 bold,普通行用浅色。 + +三、实现 XrRmUdpSenderProjectSetup.cs + +仅在 UNITY_EDITOR 下编译。 + +菜单: + +1. XR-RM/Apply UDP Sender Android Settings +2. XR-RM/Create Or Refresh UDP Sender Scene +3. XR-RM/Build Android APK + +Apply UDP Sender Android Settings: + +- PlayerSettings.productName = "XR-RM-PICO-UDP-Sender" +- Android application identifier = "com.local.xr_rm_udp_sender" +- minSdkVersion = AndroidApiLevel29 +- targetSdkVersion = AndroidApiLevelAuto +- forceInternetPermission = true +- Scripting Backend = IL2CPP +- Android targetArchitectures = ARM64 +- Graphics API = OpenGLES3 +- SwitchActiveBuildTarget Android +- EnsurePicoSettings() +- EnablePicoLoader() +- CreateOrRefreshScene() +- EditorBuildSettings.scenes = Main.unity +- AssetDatabase.SaveAssets() + +Create Or Refresh UDP Sender Scene: + +- 确保 Assets/Scenes 存在。 +- 打开或新建 Assets/Scenes/Main.unity。 +- 找到或创建 GameObject "PicoControllerUdpSender"。 +- 添加/配置 PicoControllerUdpSender: + - host="192.168.9.89" + - port=15000 + - sendHz=60 + - convertUnityToProjectCoordinates=true + - sendOnStart=true +- 添加/配置 PicoUdpConfigPanel: + - targetFrameRate=90 + - showOnStart=true + - togglePanelWithMenuButton=true +- 保存 scene。 + +EnsurePicoSettings: + +- 确保 Assets/XR/Settings 存在。 +- 创建或加载 Assets/XR/Settings/PXR_Settings.asset。 +- 默认 stereoRenderingModeAndroid = MultiPass。 +- EditorBuildSettings.AddConfigObject("Unity.XR.PXR.Settings", settings, true)。 + +EnablePicoLoader: + +- 查找或创建 XRGeneralSettingsPerBuildTarget。 +- 获取 Android 的 XRGeneralSettings。 +- 确保 XRManagerSettings 存在。 +- 移除当前 active loaders。 +- 使用 XRPackageMetadataStore.AssignLoader(generalSettings.Manager, "PXR_Loader", BuildTargetGroup.Android)。 +- 失败时 Debug.LogWarning 提醒检查 PICO Integration package。 + +Build Android APK: + +- 调用 ApplyAndroidSettings() +- 确保 Builds 目录存在。 +- BuildPlayerOptions: + - scenes = Assets/Scenes/Main.unity + - locationPathName = Builds/XR-RM-PICO-UDP-Sender.apk + - target = Android + - targetGroup = Android + - options = None +- BuildPipeline.BuildPlayer(options) +- 如果 result 不是 Succeeded,throw Exception。 + +四、验收标准 + +1. Unity Editor 打开工程无编译错误。 +2. XR-RM/Apply UDP Sender Android Settings 能执行。 +3. XR-RM/Create Or Refresh UDP Sender Scene 后,Main.unity 中有 PicoControllerUdpSender 对象和两个组件。 +4. XR-RM/Build Android APK 能生成 Builds/XR-RM-PICO-UDP-Sender.apk。 +5. adb install -r 安装到 PICO 4 Ultra 后能启动。 +6. PICO 内能看到世界空间配置面板。 +7. 面板可修改 Target IP、Port、Send Rate、Coordinates、UDP Sending 并保存。 +8. 左右手柄有效时面板显示 L ok / R ok。 +9. UDP Sending ON 时,ROS2 端 udp_controller_receiver 能收到双手柄 JSON。 +10. /xr/left_controller 和 /xr/right_controller 持续刷新。 +11. 按住 grip 时 ROS2 遥操作链路输出速度;松开 grip、暂停发送、退出 App 时安全停止。 + +五、不要实现的内容 + +- 不实现 XRoboToolkit PC-Service。 +- 不实现视频流。 +- 不实现夹爪控制。 +- 不实现手势识别、hand tracking 或 motion tracker。 +- 不引入 XR Interaction Toolkit,除非 PICO SDK 必须要求。 +- 不引入 TextMeshPro。 +- 不改 ROS2 端协议。 +``` + +## 8. 常见问题排查 + +### Unity 找不到 PICO package + +检查 `Packages/manifest.json` 的相对路径是否和实际目录一致: + +```text +XR_RM_PICO_UDP_Sender/Packages/manifest.json +../../PICO-Unity-Integration-SDK-release_3.4.0 +``` + +也就是从 `unity/XR_RM_PICO_UDP_Sender/Packages` 往上两级到 `unity/`,再进入 `PICO-Unity-Integration-SDK-release_3.4.0`。 + +### Gradle 或 Android 构建失败 + +优先检查: + +1. Unity Editor 是否安装了 `Android Build Support`、`Android SDK & NDK Tools`、`OpenJDK`。 +2. `Project Settings -> Player -> Android` 是否是 IL2CPP + ARM64。 +3. 是否已切换到 Android Build Target。 +4. 是否使用 Unity embedded SDK/NDK/JDK。 +5. PICO SDK package 是否无编译错误。 + +### PICO 收不到 APK 或安装失败 + +```bash +adb kill-server +adb start-server +adb devices +adb install -r unity/XR_RM_PICO_UDP_Sender/Builds/XR-RM-PICO-UDP-Sender.apk +``` + +如果设备显示 `unauthorized`,在 PICO 内重新授权 USB 调试。 + +### ROS2 收不到 UDP + +按顺序检查: + +1. PICO 和 Ubuntu 是否同一个 Wi-Fi。 +2. Target IP 是否是 Ubuntu 主机 IP,不是路由器 IP,也不是 PICO IP。 +3. Target Port 是否是 `15000`。 +4. `udp_controller_receiver` 是否已启动。 +5. UFW 是否允许 UDP 15000。 +6. PICO 面板是否显示 `UDP Sending ON`。 +7. 面板是否显示 `L ok / R ok`。 + +可以临时关闭 ROS2 receiver,用 netcat 独占端口看是否有 UDP 包: + +```bash +nc -ul 15000 +``` + +注意:`nc` 和 ROS2 receiver 不能同时绑定同一个 UDP 端口。 + +### 有 UDP 但机械臂不动 + +先在 mock 模式验证: + +```bash +ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=true +``` + +然后检查: + +```bash +ros2 topic echo /xr/left_controller +ros2 topic echo /xr/right_controller +ros2 topic echo /xr_rm/left_rm75/cmd_vel +ros2 topic echo /xr_rm/right_rm75/cmd_vel +``` + +如果 `/xr/*_controller` 有数据但 `cmd_vel` 为零,多半是 grip 没按下、UDP 超时、工作空间限幅、或 `xr_to_robot_matrix` 方向/尺度需要调整。 + +## 9. 参考链接 + +- Unity Hub Linux 安装文档:`https://docs.unity.com/en-us/hub/install-hub-linux` +- Unity 2022.3 系统要求:`https://docs.unity.cn/Documentation/Manual/system-requirements.html` +- Unity 6.0 系统要求:`https://docs.unity3d.com/6000.0/Documentation/Manual/system-requirements.html` +- PICO 4 Ultra 开发资源:`https://developer.picoxr.com/pico4-ultra/` +- PICO Unity Integration SDK GitHub:`https://github.com/Pico-Developer/PICO-Unity-Integration-SDK` +