# 自定义任务创建指南:从 USDC 资产到完整任务 本文档记录了将外部 USDC 资产(以试管为例)转换为 InternDataEngine 可用任务的完整流程。 ## 前提条件 - InternDataEngine 项目已配置完成(IS 5.0.0 + banana500 环境) - 有现成的 USD/USDC 3D 模型文件 - conda 环境 `banana450`(用于 grasp 标注生成,pyarmor 兼容) ## 完整流程 ### Step 1: 准备资产目录 在 `workflows/simbox/example_assets/task/` 下创建任务目录: ```bash mkdir -p workflows/simbox/example_assets/task/pick_test_tube/test_tube mkdir -p workflows/simbox/example_assets/task/pick_test_tube/test_tube_rack ``` ### Step 2: 从 USDC 导出三角化 OBJ InternDataEngine 的 grasp 生成工具需要 OBJ 格式输入,且必须是纯三角面。使用 `migrate/usdc_to_obj.py` 从 USDC 提取并三角化: ```bash conda activate banana500 python migrate/usdc_to_obj.py \ --input /path/to/your/model.usdc \ --output workflows/simbox/example_assets/task/pick_test_tube/test_tube/ ``` 输出文件:`Aligned_obj.obj`(三角化的 OBJ) **注意**:原始 USDC 中的面可能是四边形或多边形,脚本会自动进行 fan 三角化。 ### Step 3: 重新打包 USD 结构 InternDataEngine 要求 USD 资产具有特定的层级结构: ``` /World (defaultPrim, Xform, 无物理属性) ├── /Looks (Scope, 材质) │ ├── Material_0 │ └── Material_1 ├── /Aligned (Xform, PhysicsRigidBodyAPI + PhysicsMassAPI) │ └── /mesh (Mesh, PhysicsCollisionAPI + PhysicsMeshCollisionAPI) └── /PhysicsMaterial (Material, PhysicsMaterialAPI) ``` **关键要求**: - `/World` 必须是 defaultPrim,且不能有任何物理 schema - `/Aligned` 是 `prim_path_child`,必须有 `PhysicsRigidBodyAPI` - 碰撞体必须使用 `convexHull` 近似(不能用 triangle mesh) - `/Looks` 必须和 `/Aligned` 平级(在同一引用范围内) 使用 `migrate/repackage_test_tube.py` 自动重构: ```bash python migrate/repackage_test_tube.py ``` 脚本逻辑: 1. 创建新 USD,定义 `/World` 为 defaultPrim(纯净 Xform,不挂引用) 2. 在 `/World/Aligned` 上添加引用,指向源 USD 的子节点(如 `/Test_Tube_AA_01/Test_Tube`) 3. 在 `/World/Looks` 上添加引用,指向源 USD 的材质(如 `/Test_Tube_AA_01/Looks`) 4. 给 `/World/Aligned` 添加 `PhysicsRigidBodyAPI` 和 `PhysicsMassAPI` 5. 给碰撞 mesh 设置 `convexHull` 近似 **重新绑定材质**:由于引用子节点后,原始材质绑定路径超出引用范围,需要手动重新绑定: ```bash python migrate/fix_test_tube_materials.py ``` ### Step 4: 生成 Grasp 抓取标注 使用项目自带的 grasp 生成工具(需要 `banana450` 环境,因为 pyarmor 加密对 Python 版本敏感): ```bash conda activate banana450 python workflows/simbox/tools/grasp/gen_sparse_label.py \ --obj_path workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.obj \ --unit m --sparse_num 3000 --max_widths 0.1 ``` 输出:`Aligned_grasp_sparse.npy`(3000 x 17 的数组) **可视化验证**: ```bash python workflows/simbox/tools/grasp/vis_grasp.py \ --obj_path workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.obj \ --unit m --N 200 ``` 会弹出 Open3D 窗口,蓝色线条表示 gripper 姿态,应该合理地分布在物体周围。 **Grasp 标注格式(N x 17)**: | 列 | 维度 | 含义 | |---|---|---| | 0 | 1 | score(质量分数,0.1-1.0,越小越好) | | 1 | 1 | width(夹持器开口宽度) | | 2 | 1 | height | | 3 | 1 | depth | | 4:13 | 9 | 旋转矩阵(3x3,row-major) | | 13:16 | 3 | 抓取中心点位置(物体坐标系) | | 16 | 1 | obj_id(通常为 -1) | ### Step 5: 创建任务配置 YAML 文件:`workflows/simbox/core/configs/tasks/example/pick_test_tube.yaml` ```yaml tasks: - name: banana_base_task asset_root: workflows/simbox/example_assets task: BananaBaseTask task_id: 0 offset: null render: True neglect_collision_names: ["table"] arena_file: workflows/simbox/core/configs/arenas/example.yaml env_map: envmap_lib: envmap_lib apply_randomization: False intensity_range: [5000, 5000] rotation_range: [0, 0] robots: - name: "split_aloha" robot_config_file: workflows/simbox/core/configs/robots/split_aloha.yaml euler: [0.0, 0.0, 90.0] ignore_substring: ["material", "table", "test_tube_rack"] objects: - name: test_tube_right path: task/pick_test_tube/test_tube/Aligned_obj.usd target_class: RigidObject dataset: custom category: test_tube prim_path_child: Aligned translation: [0.0, 0.0, 0.0] euler: [0.0, 0.0, 0.0] scale: [1, 1, 1] apply_randomization: False orientation_mode: keep regions: - object: test_tube_right target: table random_type: A_on_B_region_sampler random_config: pos_range: [[0.05, -0.05, 0.0], [0.15, 0.05, 0.0]] yaw_rotation: [0.0, 0.0] - object: split_aloha target: table random_type: A_on_B_region_sampler random_config: pos_range: [[0.0, -0.86, -0.75], [0.0, -0.86, -0.75]] yaw_rotation: [0.0, 0.0] cameras: # ... 复用 split_aloha 的相机配置(详见完整配置文件) data: task_dir: "pick_test_tube" language_instruction: "Pick up the test tube from the table." detailed_language_instruction: "Use the right arm to grasp the test tube." collect_info: "Test tube picking task" version: "v1.0" update: True max_episode_length: 2000 skills: - split_aloha: - right: - name: pick objects: [test_tube_right] npy_name: Aligned_grasp_sparse.npy filter_y_dir: ["forward", 60] filter_z_dir: ["downward", 150] pre_grasp_offset: 0.05 gripper_change_steps: 10 t_eps: 0.025 o_eps: 1 process_valid: True lift_th: 0.02 post_grasp_offset_min: 0.10 post_grasp_offset_max: 0.15 - name: heuristic__skill mode: home gripper_state: 1.0 ``` **Skill 名称注意**:使用 `register_skill` 装饰器注册的名称(类名转 snake_case),例如: - `Pick` → `pick` - `Heuristic_Skill` → `heuristic__skill`(双下划线) - `Goto_Pose` → `goto__pose`(双下划线) ### Step 6: 创建 Pipeline 配置 文件:`configs/simbox/de_pick_test_tube.yaml` ```yaml name: simbox_pick_test_tube load_stage: scene_loader: type: env_loader args: workflow_type: SimBoxDualWorkFlow cfg_path: workflows/simbox/core/configs/tasks/example/pick_test_tube.yaml simulator: physics_dt: 1/30 rendering_dt: 1/30 stage_units_in_meters: 1.0 headless: False # 调试时用 False(GUI),生产时改 True renderer: "RayTracedLighting" anti_aliasing: 0 layout_random_generator: type: env_randomizer args: random_num: 1 # 调试时用 1,生产时增大 strict_mode: true plan_stage: seq_planner: type: env_planner render_stage: renderer: type: env_renderer store_stage: writer: type: env_writer args: batch_async: true output_dir: output/${name}/ ``` ### Step 7: 运行测试 ```bash conda activate banana500 # GUI 模式调试(headless: False) python launcher.py --config configs/simbox/de_pick_test_tube.yaml # 确认后改 headless: True,增大 random_num 批量生产 ``` ### Step 8: 检查输出 输出目录:`output/simbox_pick_test_tube/` ``` output/simbox_pick_test_tube/ ├── pick_test_tube/split_aloha//right/ │ ├── / │ │ ├── images.rgb.head/ │ │ ├── images.rgb.hand_right/ │ │ ├── images.rgb.hand_left/ │ │ └── lmdb/ │ └── ... └── de_config.yaml ``` ## 常见问题 ### Q: `gen_sparse_label.py` 报 `_PyFloat_Pack8` 错误 A: pyarmor 加密对 Python 版本敏感,使用 `banana450` 环境(Python 3.10 + 旧版依赖)。 ### Q: USD 打开后材质丢失 A: 引用子节点时材质路径超出引用范围。需要用 `fix_test_tube_materials.py` 重新绑定。 ### Q: `RigidContactView` 报 `sensor_count` 错误 A: USD 结构不对。确保 `/World` (defaultPrim) 没有 `PhysicsRigidBodyAPI`,物理属性只在 `/World/Aligned` 子节点上。 ### Q: OBJ 导出后 grasp 生成为空(0 条) A: OBJ 文件包含非三角形面。使用 `migrate/usdc_to_obj.py` 导出时会自动三角化。 ### Q: Plan 不收敛 A: 检查 grasp 标注质量(用 `vis_grasp.py` 可视化),确认姿态合理。调整 `filter_y_dir` / `filter_z_dir` 放宽过滤条件。 ## 工具脚本一览 | 脚本 | 用途 | |------|------| | `migrate/usdc_to_obj.py` | USDC → 三角化 OBJ | | `migrate/repackage_test_tube.py` | 重构 USD 层级结构 | | `migrate/fix_test_tube_materials.py` | 修复材质绑定 | | `workflows/simbox/tools/grasp/gen_sparse_label.py` | 生成 grasp 标注(需 banana450) | | `workflows/simbox/tools/grasp/vis_grasp.py` | 可视化 grasp 标注 |