feat: add test tube pick task with custom assets and grasp annotations
- Add pick_test_tube task: USDC asset repackaging, grasp generation, task config - Add tools: usdc_to_obj.py, repackage_test_tube.py, fix_test_tube_materials.py - Add custom_task_guide.md: full Chinese documentation for creating custom tasks - Add crawled InternDataEngine online docs (23 pages) - Add grasp generation script (gen_tube_grasp.py) and pipeline config
This commit is contained in:
303
migrate/custom_task_guide.md
Normal file
303
migrate/custom_task_guide.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# 自定义任务创建指南:从 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/<task>/right/
|
||||
│ ├── <timestamp>/
|
||||
│ │ ├── 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 标注 |
|
||||
Reference in New Issue
Block a user