diff --git a/configs/simbox/de_pick_test_tube.yaml b/configs/simbox/de_pick_test_tube.yaml new file mode 100644 index 0000000..e6e7538 --- /dev/null +++ b/configs/simbox/de_pick_test_tube.yaml @@ -0,0 +1,31 @@ +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 # GUI mode for visual debugging first + renderer: "RayTracedLighting" + anti_aliasing: 0 + layout_random_generator: + type: env_randomizer + args: + random_num: 1 # Start with 1 sample for testing + 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}/ diff --git a/docs_crawled/api_controllers.md b/docs_crawled/api_controllers.md new file mode 100644 index 0000000..13357a6 --- /dev/null +++ b/docs_crawled/api_controllers.md @@ -0,0 +1,209 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/api/controllers.html + +# Controllers API Reference [​](#controllers-api-reference) + +This page provides API documentation for the controller module. + +## TemplateController [​](#templatecontroller) + +Base class for all robot arm controllers. + +### Constructor [​](#constructor) +python +``` +TemplateController(cfg, task, robot_file: str, **kwargs) +``` +1 + +**Parameters: ** + +| Parameter | Type | Description | +| `cfg ` | DictConfig | Controller configuration | +| `task ` | BananaBaseTask | Task instance | +| `robot_file ` | str | Path to CuRobo robot config | + +### Properties [​](#properties) + +| Property | Type | Description | +| `raw_js_names ` | List[str] | Joint names in CuRobo order | +| `cmd_js_names ` | List[str] | Joint names in simulation order | +| `arm_indices ` | np.ndarray | Arm joint indices | +| `gripper_indices ` | np.ndarray | Gripper joint indices | +| `_gripper_state ` | float | Gripper state (1.0=open, -1.0=closed) | + +### Methods [​](#methods) + +#### `_configure_joint_indices(robot_file: str) `[​](#configure-joint-indices-robot-file-str) + +Configure joint names and indices. **Must be implemented by subclass. ** + +#### `_get_default_ignore_substring() -> List[str] `[​](#get-default-ignore-substring-list-str) + +Return default collision filter substrings. **Must be implemented by subclass. ** + +#### `get_gripper_action() -> np.ndarray `[​](#get-gripper-action-np-ndarray) + +Map gripper state to joint targets. **Must be implemented by subclass. ** + +#### `_load_world(use_default: bool = True) `[​](#load-world-use-default-bool-true) + +Load world configuration for motion planning. + +**Returns: **`WorldConfig ` + +#### `_get_motion_gen_collision_cache() -> dict `[​](#get-motion-gen-collision-cache-dict) + +Return collision cache sizes. + +**Returns: **`{"obb": int, "mesh": int} ` + +#### `_get_grasp_approach_linear_axis() -> int `[​](#get-grasp-approach-linear-axis-int) + +Return grasp approach axis (0=x, 1=y, 2=z). + +**Returns: **`int `(default: 2) + +#### `_get_sort_path_weights() -> Optional[List[float]] `[​](#get-sort-path-weights-optional-list-float) + +Return weights for path selection. + +**Returns: **`List[float] `or `None ` + +#### `plan_to_pose(target_pose) `[​](#plan-to-pose-target-pose) + +Plan motion to target pose. + +**Parameters: ** + +- `target_pose `: Tuple of (position, orientation) + +**Returns: **`bool `- Success status + +#### `execute_plan() `[​](#execute-plan) + +Execute the planned trajectory. + +#### `set_gripper_state(state: float) `[​](#set-gripper-state-state-float) + +Set gripper state. + +**Parameters: ** + +- `state `: 1.0 for open, -1.0 for closed + +#### `get_current_joint_state() -> np.ndarray `[​](#get-current-joint-state-np-ndarray) + +Get current joint positions. + +**Returns: **`np.ndarray `of joint positions + +## Lift2Controller [​](#lift2controller) + +Controller for ARX-Lift2 dual-arm robot. +python +``` +@register_controller +class Lift2Controller(TemplateController): + def _get_grasp_approach_linear_axis(self) -> int: + return 0 # x-axis +``` +1 +2 +3 +4 + +**Features: ** + +- Dual-arm support +- Custom world configuration +- X-axis grasp approach + +## SplitAlohaController [​](#splitalohacontroller) + +Controller for Agilex Split Aloha dual-arm robot. +python +``` +@register_controller +class SplitAlohaController(TemplateController): + def _get_grasp_approach_linear_axis(self) -> int: + return 2 # z-axis +``` +1 +2 +3 +4 + +**Features: ** + +- Dual-arm support +- Z-axis grasp approach +- Optional joint control + +## Genie1Controller [​](#genie1controller) + +Controller for Genie1 dual-arm robot. +python +``` +@register_controller +class Genie1Controller(TemplateController): + def _get_sort_path_weights(self) -> List[float]: + return [1, 1, 1, 1, 3, 3, 1] +``` +1 +2 +3 +4 + +**Features: ** + +- 7-DOF per arm +- Path selection weights +- Custom world configuration + +## FR3Controller [​](#fr3controller) + +Controller for Franka FR3 single-arm robot. +python +``` +@register_controller +class FR3Controller(TemplateController): + def _get_motion_gen_collision_cache(self): + return {"obb": 1000, "mesh": 1000} +``` +1 +2 +3 +4 + +**Features: ** + +- Single-arm support +- Panda gripper +- Larger collision cache + +## FrankaRobotiq85Controller [​](#frankarobotiq85controller) + +Controller for Franka with Robotiq 2F-85 gripper. +python +``` +@register_controller +class FrankaRobotiq85Controller(TemplateController): + def get_gripper_action(self): + return np.clip( + -self._gripper_state * self._gripper_joint_position, + 0.0, 5.0 + ) +``` +1 +2 +3 +4 +5 +6 +7 + +**Features: ** + +- Single-arm support +- Robotiq 2F-85 gripper +- Inverted gripper mapping \ No newline at end of file diff --git a/docs_crawled/api_skills.md b/docs_crawled/api_skills.md new file mode 100644 index 0000000..329f50d --- /dev/null +++ b/docs_crawled/api_skills.md @@ -0,0 +1,230 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/api/skills.html + +# Skills API Reference [​](#skills-api-reference) + +This page provides API documentation for the skills module. + +## BaseSkill [​](#baseskill) + +Base class for all manipulation skills. + +### Constructor [​](#constructor) +python +``` +BaseSkill(cfg, task, controller, **kwargs) +``` +1 + +**Parameters: ** + +| Parameter | Type | Description | +| `cfg ` | DictConfig | Skill configuration | +| `task ` | BananaBaseTask | Task instance | +| `controller ` | TemplateController | Controller instance | + +### Properties [​](#properties) + +| Property | Type | Description | +| `skill_cfg ` | dict | Skill-specific configuration | +| `task ` | BananaBaseTask | Reference to task | +| `controller ` | TemplateController | Reference to controller | + +### Methods [​](#methods) + +#### `run() -> bool `[​](#run-bool) + +Execute the skill. **Must be implemented by subclass. ** + +**Returns: **`bool `- Success status + +## Dexpick [​](#dexpick) + +Dex-style picking skill with approach planning. +python +``` +class Dexpick(BaseSkill): + def __init__(self, cfg, task, controller, **kwargs) + def run(self) -> bool +``` +1 +2 +3 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `objects ` | List[str] | required | Objects to pick | +| `pick_pose_idx ` | int | 0 | Pick pose group index | +| `pre_grasp_offset ` | float | 0.0 | Pre-grasp approach offset | +| `post_grasp_offset ` | float | 0.1 | Post-grasp lift offset | + +## Dexplace [​](#dexplace) + +Dex-style placing skill with constraints. +python +``` +class Dexplace(BaseSkill): + def __init__(self, cfg, task, controller, **kwargs) + def run(self) -> bool +``` +1 +2 +3 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `objects ` | List[str] | required | [object, target] | +| `camera_axis_filter ` | dict | None | Camera constraint | + +## Pick [​](#pick) + +Standard pick skill. +python +``` +class Pick(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `objects ` | List[str] | required | Objects to pick | + +## Place [​](#place) + +Standard place skill. +python +``` +class Place(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `objects ` | List[str] | required | [object, target] | + +## GotoPose [​](#gotopose) + +Move to target pose skill. +python +``` +class GotoPose(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `target_pose ` | tuple | required | (position, orientation) | + +## Home [​](#home) + +Return to home configuration. +python +``` +class Home(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `mode ` | str | "default" | Home configuration mode | + +## Open / Close [​](#open-close) + +Gripper control skills. +python +``` +class Open(BaseSkill): + def run(self) -> bool + self.controller.set_gripper_state(1.0) + +class Close(BaseSkill): + def run(self) -> bool + self.controller.set_gripper_state(-1.0) +``` +1 +2 +3 +4 +5 +6 +7 + +## PourWaterSucc [​](#pourwatersucc) + +Pouring skill for liquid simulation. +python +``` +class PourWaterSucc(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `objects ` | List[str] | required | [container, target] | +| `pour_angle ` | float | 90.0 | Pouring angle (degrees) | + +## Rotate [​](#rotate) + +Rotation skill. +python +``` +class Rotate(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `axis ` | str | "z" | Rotation axis | +| `angle ` | float | 90.0 | Rotation angle (degrees) | + +## Wait [​](#wait) + +Wait skill for timing control. +python +``` +class Wait(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `duration ` | float | 1.0 | Wait duration (seconds) | + +## HeuristicSkill [​](#heuristicskill) + +Configurable skill for simple actions. +python +``` +class HeuristicSkill(BaseSkill): + def run(self) -> bool +``` +1 +2 + +**Configuration: ** + +| Parameter | Type | Default | Description | +| `mode ` | str | "home" | Action mode | +| `gripper_state ` | float | None | Gripper state to set | \ No newline at end of file diff --git a/docs_crawled/concepts_cameras.md b/docs_crawled/concepts_cameras.md new file mode 100644 index 0000000..378d6bf --- /dev/null +++ b/docs_crawled/concepts_cameras.md @@ -0,0 +1,188 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/cameras.html + +# Cameras [​](#cameras) + +Cameras capture visual data from the simulation. InternDataEngine uses a unified `CustomCamera `class that can be fully configured via YAML. + +## Camera Architecture [​](#camera-architecture) + +``` +CustomCamera +├── Pose Configuration +│ ├── Translation +│ └── Orientation +├── Intrinsics +│ ├── Focal length +│ ├── Principal point +│ └── Resolution +├── Lens Settings +│ ├── f-number +│ ├── Focus distance +│ └── Aperture +└── Outputs + ├── RGB image + └── Camera pose +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 + +## Camera Configuration [​](#camera-configuration) + +Camera configuration is split into two parts: **pose configuration **(in task YAML) and **intrinsic parameters **(in separate camera YAML files). + +### Part 1: Pose Configuration (Task YAML) [​](#part-1-pose-configuration-task-yaml) + +Configure camera pose and randomization in task YAML files: +yaml +``` +cameras: + - name: lift2_hand_left # Unique camera name + translation: [0.07, 0.01, 0.08] # Position offset [x, y, z] in meters + orientation: [0.62, 0.33, -0.33, -0.62] # Quaternion [w, x, y, z] + camera_axes: usd # Coordinate system (usd/ros/opencv) + camera_file: workflows/simbox/core/configs/cameras/realsense_d405.yaml # Path to intrinsic config + parent: "lift2/lift2/lift2/fl/link6" # Parent prim path (robot link or empty for world) + apply_randomization: True # Enable pose randomization + max_translation_noise: 0.02 # Max position noise (meters) + max_orientation_noise: 2.5 # Max rotation noise (degrees) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +### Part 2: Intrinsic Parameters (Camera YAML) [​](#part-2-intrinsic-parameters-camera-yaml) + +Define camera intrinsics in a separate YAML file (e.g., `workflows/simbox/core/configs/cameras/realsense_d405.yaml `): +yaml +``` +camera_type: "RealSense" # Camera model type +camera_params: [433.89, 433.38, 322.79, 243.14] # [fx, fy, cx, cy] intrinsic parameters +resolution_width: 640 # Image width in pixels +resolution_height: 480 # Image height in pixels +frequency: 30 # Capture frequency (Hz) +pixel_size: 3 # Physical pixel size (μm) +f_number: 2.0 # Lens aperture f-number +focus_distance: 0.6 # Focus distance (meters) +``` +1 +2 +3 +4 +5 +6 +7 +8 + +## Understanding Camera Parameters [​](#understanding-camera-parameters) + +### Intrinsic Matrix [​](#intrinsic-matrix) + +The camera intrinsic matrix K: + +``` +K = | fx 0 cx | + | 0 fy cy | + | 0 0 1 | + +fx, fy = focal lengths (pixels) +cx, cy = principal point (pixels) +``` +1 +2 +3 +4 +5 +6 + +### Sensor Settings [​](#sensor-settings) + +- **resolution_width **( int ): Image width in pixels. Typical value: 640 - 1920. +- **resolution_height **( int ): Image height in pixels. Typical value: 480 - 1080. +- **pixel_size **( float ): Physical pixel size (μm). Typical value: 1.4 - 3.0. +- **f_number **( float ): Lens aperture. Typical value: 1.8 - 4.0. +- **focus_distance **( float ): Focus distance (m). Typical value: 0.3 - 1.0. +- **frequency **( int ): Capture frequency (Hz). Typical value: 15 - 60. + +## Camera Mounting [​](#camera-mounting) + +### Robot-Mounted [​](#robot-mounted) + +Attach to robot link: +yaml +``` +parent: "lift2/lift2/lift2/fl/link6" # End-effector link +translation: [0.07, 0.01, 0.08] # Offset from link +``` +1 +2 + +### World-Fixed [​](#world-fixed) + +Fixed in world frame: +yaml +``` +parent: "" +translation: [0.5, 0.5, 1.2] +orientation: [0.707, 0.707, 0, 0] # Looking down +``` +1 +2 +3 + +## Domain Randomization [​](#domain-randomization) + +Enable camera pose randomization with the `_perturb_camera() `method defined in `workflows/simbox/core/tasks/banana.py `: +yaml +``` +cameras: + - name: head_camera + apply_randomization: true + max_translation_noise: 0.03 # ±3 cm + max_orientation_noise: 5.0 # ±5 degrees +``` +1 +2 +3 +4 +5 + +## Camera Outputs [​](#camera-outputs) + +`CustomCamera.get_observations() `returns: + +- **color_image **( ndarray ): RGB image with shape H×W×3 (float32). +- **camera2env_pose **( ndarray ): Camera to environment transform matrix with shape 4×4. +- **camera_params **( ndarray ): Intrinsic matrix K with shape 3×3. + +## Key Files [​](#key-files) + +| File | Purpose | +| `cameras/custom_camera.py ` | CustomCamera implementation | +| `cameras/__init__.py ` | Camera registry | + +## References [​](#references) + +- [Isaac Sim Camera Sensors Documentation](https://docs.isaacsim.omniverse.nvidia.com/4.5.0/sensors/isaacsim_sensors_camera.html) +- [Intel RealSense D415 Product Brief](https://simplecore.intel.com/realsensehub/wp-content/uploads/sites/63/D415_Series_ProductBrief_010718.pdf) +- [Intel RealSense D435 Product Brief](https://simplecore.intel.com/realsensehub/wp-content/uploads/sites/63/D435_Series_ProductBrief_010718.pdf) +- [Camera Frame Axes Reference](https://www.researchgate.net/figure/Axes-of-the-camera-frame-on-the-camera-CCD-and-lens_fig3_225025509) \ No newline at end of file diff --git a/docs_crawled/concepts_controllers.md b/docs_crawled/concepts_controllers.md new file mode 100644 index 0000000..dc92dd0 --- /dev/null +++ b/docs_crawled/concepts_controllers.md @@ -0,0 +1,1241 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/controllers.html + +# Controllers [​](#controllers) + +Controllers are the core component responsible for robot arm motion planning in InternDataEngine. They provide **GPU-accelerated collision-free trajectory generation **using CuRobo, while simultaneously handling gripper actions. + +## Overview [​](#overview) + +Each controller manages motion planning for a single robot arm. For dual-arm robots (like Lift2, Genie1, Split Aloha), two controller instances are created - one for each arm. + +The controller's primary responsibilities include: + +- **Collision-Free Motion Planning **: Generate safe trajectories that avoid obstacles in the scene using CuRobo's GPU-accelerated motion generation +- **Inverse Kinematics **: Solve IK queries to find joint configurations for desired end-effector poses +- **Gripper Control **: Handle gripper open/close commands integrated with arm motion +- **Collision World Management **: Update and maintain the collision world from the simulation scene + +### CuRobo Configuration Files [​](#curobo-configuration-files) + +Each robot arm requires a CuRobo configuration file that defines kinematics, collision geometry, and motion generation parameters: + +- **YAML Configs **: `workflows/simbox/curobo/src/curobo/content/configs/robot/ ` +- **URDF Files **: `workflows/simbox/curobo/src/curobo/content/assets/robot/ ` + +Available robot configs include: + +- `r5a_left_arm.yml `/ `r5a_right_arm.yml `- ARX Lift-2 (R5a arms) +- `piper100_left_arm.yml `/ `piper100_right_arm.yml `- AgiLEx Split Aloha +- `G1_120s_left_arm_parallel_gripper.yml `/ `G1_120s_right_arm_parallel_gripper.yml `- Genie-1 +- `fr3_left_arm.yml `- Franka FR3 +- `frankarobotiq_left_arm.yml `- Franka with Robotiq 2F-85 gripper + +## Controller Architecture [​](#controller-architecture) + +All controllers inherit from `TemplateController `and customize behavior via method overrides. + +``` +TemplateController (base class) +├── FR3Controller +├── FrankaRobotiq85Controller +├── Genie1Controller +├── Lift2Controller +└── SplitAlohaController +``` +1 +2 +3 +4 +5 +6 + +## Controller Wrappers [​](#controller-wrappers) + +Controller wrappers (located in `workflows/simbox/core/controllers/ `) provide a unified interface for motion planning. The base class `TemplateController `implements all core functionality, with subclasses overriding specific methods for robot-specific configurations. + +Template Code Example: +python +``` +""" +Template Controller base class for robot motion planning. + +Common functionality extracted from FR3, FrankaRobotiq85, Genie1, Lift2, SplitAloha. +Subclasses implement _get_default_ignore_substring() and _configure_joint_indices(). +""" + +import random +import time +from copy import deepcopy +from typing import List, Optional + +import numpy as np +import torch +from core.utils.constants import CUROBO_BATCH_SIZE +from core.utils.plan_utils import ( + filter_paths_by_position_error, + filter_paths_by_rotation_error, + sort_by_difference_js, +) +from curobo.cuda_robot_model.cuda_robot_model import CudaRobotModel +from curobo.geom.sdf.world import CollisionCheckerType +from curobo.geom.sphere_fit import SphereFitType +from curobo.geom.types import WorldConfig +from curobo.types.base import TensorDeviceType +from curobo.types.math import Pose +from curobo.types.robot import JointState, RobotConfig +from curobo.util.usd_helper import UsdHelper +from curobo.util_file import get_world_configs_path, join_path, load_yaml +from curobo.wrap.reacher.ik_solver import IKSolver, IKSolverConfig +from curobo.wrap.reacher.motion_gen import ( + MotionGen, + MotionGenConfig, + MotionGenPlanConfig, + PoseCostMetric, +) +from omni.isaac.core import World +from omni.isaac.core.controllers import BaseController +from omni.isaac.core.tasks import BaseTask +from omni.isaac.core.utils.prims import get_prim_at_path +from omni.isaac.core.utils.transformations import ( + get_relative_transform, + pose_from_tf_matrix, +) +from omni.isaac.core.utils.types import ArticulationAction + +class TemplateController(BaseController): + """Base controller for CuRobo-based motion planning. Supports single and batch planning.""" + + def __init__( + self, + name: str, + robot_file: str, + task: BaseTask, + world: World, + constrain_grasp_approach: bool = False, + collision_activation_distance: float = 0.03, + ignore_substring: Optional[List[str]] = None, + use_batch: bool = False, + **kwargs, + ) -> None: + super().__init__(name=name) + self.name = name + self.world = world + self.task = task + self.robot = self.task.robots[name] + self.ignore_substring = self._get_default_ignore_substring() + if ignore_substring is not None: + self.ignore_substring = ignore_substring + self.ignore_substring.append(name) + self.use_batch = use_batch + self.constrain_grasp_approach = constrain_grasp_approach + self.collision_activation_distance = collision_activation_distance + self.usd_help = UsdHelper() + self.tensor_args = TensorDeviceType() + self.init_curobo = False + self.robot_file = robot_file + self.num_plan_failed = 0 + self.raw_js_names = [] + self.cmd_js_names = [] + self.arm_indices = np.array([]) + self.gripper_indices = np.array([]) + self.reference_prim_path = None + self.lr_name = None + self._ee_trans = 0.0 + self._ee_ori = 0.0 + self._gripper_state = 1.0 + self._gripper_joint_position = np.array([1.0]) + self.idx_list = None + + self._configure_joint_indices(robot_file) + self._load_robot(robot_file) + self._load_kin_model() + self._load_world() + self._init_motion_gen() + + self.usd_help.load_stage(self.world.stage) + self.cmd_plan = None + self.cmd_idx = 0 + self._step_idx = 0 + self.num_last_cmd = 0 + self.ds_ratio = 1 + + def _get_default_ignore_substring(self) -> List[str]: + return ["material", "Plane", "conveyor", "scene", "table"] + + def _configure_joint_indices(self, robot_file: str) -> None: + raise NotImplementedError + + def _load_robot(self, robot_file: str) -> None: + self.robot_cfg = load_yaml(robot_file)["robot_cfg"] + + def _load_kin_model(self) -> None: + urdf_file = self.robot_cfg["kinematics"]["urdf_path"] + base_link = self.robot_cfg["kinematics"]["base_link"] + ee_link = self.robot_cfg["kinematics"]["ee_link"] + robot_cfg = RobotConfig.from_basic(urdf_file, base_link, ee_link, self.tensor_args) + self.kin_model = CudaRobotModel(robot_cfg.kinematics) + + def _load_world(self, use_default: bool = True) -> None: + if use_default: + self.world_cfg = WorldConfig() + else: + world_cfg_table = WorldConfig.from_dict( + load_yaml(join_path(get_world_configs_path(), "collision_table.yml")) + ) + self._world_cfg_table = world_cfg_table + self._world_cfg_table.cuboid[0].pose[2] -= 10.5 + world_cfg1 = WorldConfig.from_dict( + load_yaml(join_path(get_world_configs_path(), "collision_table.yml")) + ).get_mesh_world() + world_cfg1.mesh[0].name += "_mesh" + world_cfg1.mesh[0].pose[2] = -10.5 + self.world_cfg = WorldConfig(cuboid=world_cfg_table.cuboid, mesh=world_cfg1.mesh) + + def _get_motion_gen_collision_cache(self): + return {"obb": 700, "mesh": 700} + + def _get_grasp_approach_linear_axis(self) -> int: + return 2 + + def _get_sort_path_weights(self) -> Optional[List[float]]: + return None + + def _init_motion_gen(self) -> None: + pose_metric = None + if self.constrain_grasp_approach: + pose_metric = PoseCostMetric.create_grasp_approach_metric( + offset_position=0.1, + linear_axis=self._get_grasp_approach_linear_axis(), + ) + if self.use_batch: + self.plan_config = MotionGenPlanConfig( + enable_graph=True, + enable_opt=True, + need_graph_success=True, + enable_graph_attempt=4, + max_attempts=4, + enable_finetune_trajopt=True, + parallel_finetune=True, + time_dilation_factor=1.0, + ) + else: + self.plan_config = MotionGenPlanConfig( + enable_graph=False, + enable_graph_attempt=7, + max_attempts=10, + pose_cost_metric=pose_metric, + enable_finetune_trajopt=True, + time_dilation_factor=1.0, + ) + motion_gen_config = MotionGenConfig.load_from_robot_config( + self.robot_cfg, + self.world_cfg, + self.tensor_args, + interpolation_dt=0.01, + collision_activation_distance=self.collision_activation_distance, + trajopt_tsteps=32, + collision_checker_type=CollisionCheckerType.MESH, + use_cuda_graph=True, + self_collision_check=True, + collision_cache=self._get_motion_gen_collision_cache(), + num_trajopt_seeds=12, + num_graph_seeds=12, + optimize_dt=True, + trajopt_dt=None, + trim_steps=None, + project_pose_to_goal_frame=False, + ) + ik_config = IKSolverConfig.load_from_robot_config( + self.robot_cfg, + self.world_cfg, + rotation_threshold=0.05, + position_threshold=0.005, + num_seeds=20, + self_collision_check=True, + self_collision_opt=True, + tensor_args=self.tensor_args, + use_cuda_graph=True, + collision_checker_type=CollisionCheckerType.MESH, + collision_cache={"obb": 700, "mesh": 700}, + ) + self.ik_solver = IKSolver(ik_config) + self.motion_gen = MotionGen(motion_gen_config) + print("warming up..") + if self.use_batch: + self.motion_gen.warmup(parallel_finetune=True, batch=CUROBO_BATCH_SIZE) + else: + self.motion_gen.warmup(enable_graph=True, warmup_js_trajopt=False) + self.world_model = self.motion_gen.world_collision + self.motion_gen.clear_world_cache() + self.motion_gen.reset(reset_seed=False) + self.motion_gen.update_world(self.world_cfg) + + def update_pose_cost_metric(self, hold_vec_weight: Optional[List[float]] = None) -> None: + if hold_vec_weight: + pose_cost_metric = PoseCostMetric( + hold_partial_pose=True, + hold_vec_weight=self.motion_gen.tensor_args.to_device(hold_vec_weight), + ) + else: + pose_cost_metric = None + self.plan_config.pose_cost_metric = pose_cost_metric + + def update(self) -> None: + obstacles = self.usd_help.get_obstacles_from_stage( + ignore_substring=self.ignore_substring, reference_prim_path=self.reference_prim_path + ).get_collision_check_world() + if self.motion_gen is not None: + self.motion_gen.update_world(obstacles) + self.world_cfg = obstacles + + def reset(self, ignore_substring: Optional[str] = None) -> None: + if ignore_substring: + self.ignore_substring = ignore_substring + self.update() + self.init_curobo = True + self.cmd_plan = None + self.cmd_idx = 0 + self.num_plan_failed = 0 + if self.lr_name == "left": + self._gripper_state = 1.0 if self.robot.left_gripper_state == 1.0 else -1.0 + elif self.lr_name == "right": + self._gripper_state = 1.0 if self.robot.right_gripper_state == 1.0 else -1.0 + if self.lr_name == "left": + self.robot_ee_path = self.robot.fl_ee_path + self.robot_base_path = self.robot.fl_base_path + else: + self.robot_ee_path = self.robot.fr_ee_path + self.robot_base_path = self.robot.fr_base_path + self.T_base_ee_init = get_relative_transform( + get_prim_at_path(self.robot_ee_path), get_prim_at_path(self.robot_base_path) + ) + self.T_world_base_init = get_relative_transform( + get_prim_at_path(self.robot_base_path), get_prim_at_path(self.task.root_prim_path) + ) + self.T_world_ee_init = self.T_world_base_init @ self.T_base_ee_init + self._ee_trans, self._ee_ori = self.get_ee_pose() + self._ee_trans = self.tensor_args.to_device(self._ee_trans) + self._ee_ori = self.tensor_args.to_device(self._ee_ori) + self.update_pose_cost_metric() + + def update_specific(self, ignore_substring, reference_prim_path): + obstacles = self.usd_help.get_obstacles_from_stage( + ignore_substring=ignore_substring, reference_prim_path=reference_prim_path + ).get_collision_check_world() + if self.motion_gen is not None: + self.motion_gen.update_world(obstacles) + self.world_cfg = obstacles + + def plan(self, ee_translation_goal, ee_orientation_goal, sim_js: JointState, js_names: list): + if self.use_batch: + ik_goal = Pose( + position=self.tensor_args.to_device(ee_translation_goal.unsqueeze(0).expand(CUROBO_BATCH_SIZE, -1)), + quaternion=self.tensor_args.to_device(ee_orientation_goal.unsqueeze(0).expand(CUROBO_BATCH_SIZE, -1)), + batch=CUROBO_BATCH_SIZE, + ) + cu_js = JointState( + position=self.tensor_args.to_device(np.tile((sim_js.positions)[np.newaxis, :], (CUROBO_BATCH_SIZE, 1))), + velocity=self.tensor_args.to_device(np.tile((sim_js.positions)[np.newaxis, :], (CUROBO_BATCH_SIZE, 1))) + * 0.0, + acceleration=self.tensor_args.to_device( + np.tile((sim_js.positions)[np.newaxis, :], (CUROBO_BATCH_SIZE, 1)) + ) + * 0.0, + jerk=self.tensor_args.to_device(np.tile((sim_js.positions)[np.newaxis, :], (CUROBO_BATCH_SIZE, 1))) + * 0.0, + joint_names=js_names, + ) + cu_js = cu_js.get_ordered_joint_state(self.cmd_js_names) + return self.motion_gen.plan_batch(cu_js, ik_goal, self.plan_config.clone()) + ik_goal = Pose( + position=self.tensor_args.to_device(ee_translation_goal), + quaternion=self.tensor_args.to_device(ee_orientation_goal), + ) + cu_js = JointState( + position=self.tensor_args.to_device(sim_js.positions), + velocity=self.tensor_args.to_device(sim_js.velocities) * 0.0, + acceleration=self.tensor_args.to_device(sim_js.velocities) * 0.0, + jerk=self.tensor_args.to_device(sim_js.velocities) * 0.0, + joint_names=js_names, + ) + cu_js = cu_js.get_ordered_joint_state(self.cmd_js_names) + return self.motion_gen.plan_single(cu_js.unsqueeze(0), ik_goal, self.plan_config.clone()) + + def forward(self, manip_cmd, eps=5e-3): + ee_trans, ee_ori = manip_cmd[0:2] + gripper_fn = manip_cmd[2] + params = manip_cmd[3] + assert hasattr(self, gripper_fn) + method = getattr(self, gripper_fn) + if gripper_fn in ["in_plane_rotation", "mobile_move", "dummy_forward"]: + return method(**params) + elif gripper_fn in ["update_pose_cost_metric", "update_specific"]: + method(**params) + return self.ee_forward(ee_trans, ee_ori, eps=eps, skip_plan=True) + else: + method(**params) + return self.ee_forward(ee_trans, ee_ori, eps) + + def ee_forward( + self, + ee_trans: torch.Tensor | np.ndarray, + ee_ori: torch.Tensor | np.ndarray, + eps=1e-4, + skip_plan=False, + ): + ee_trans = self.tensor_args.to_device(ee_trans) + ee_ori = self.tensor_args.to_device(ee_ori) + sim_js = self.robot.get_joints_state() + js_names = self.robot.dof_names + plan_flag = torch.logical_or( + torch.norm(self._ee_trans - ee_trans) > eps, + torch.norm(self._ee_ori - ee_ori) > eps, + ) + if not skip_plan: + if plan_flag: + self.cmd_idx = 0 + self._step_idx = 0 + self.num_last_cmd = 0 + result = self.plan(ee_trans, ee_ori, sim_js, js_names) + if self.use_batch: + if result.success.any(): + self._ee_trans = ee_trans + self._ee_ori = ee_ori + paths = result.get_successful_paths() + position_filter_res = filter_paths_by_position_error( + paths, result.position_error[result.success] + ) + rotation_filter_res = filter_paths_by_rotation_error( + paths, result.rotation_error[result.success] + ) + filtered_paths = [ + p for i, p in enumerate(paths) if position_filter_res[i] and rotation_filter_res[i] + ] + if len(filtered_paths) == 0: + filtered_paths = paths + sort_weights = self._get_sort_path_weights() + weights_arg = self.tensor_args.to_device(sort_weights) if sort_weights is not None else None + sorted_indices = sort_by_difference_js(filtered_paths, weights=weights_arg) + cmd_plan = self.motion_gen.get_full_js(paths[sorted_indices[0]]) + self.idx_list = list(range(len(self.raw_js_names))) + self.cmd_plan = cmd_plan.get_ordered_joint_state(self.raw_js_names) + self.num_plan_failed = 0 + else: + print("Plan did not converge to a solution.") + self.num_plan_failed += 1 + else: + succ = result.success.item() + if succ: + self._ee_trans = ee_trans + self._ee_ori = ee_ori + cmd_plan = result.get_interpolated_plan() + self.idx_list = list(range(len(self.raw_js_names))) + self.cmd_plan = cmd_plan.get_ordered_joint_state(self.raw_js_names) + self.num_plan_failed = 0 + else: + print("Plan did not converge to a solution.") + self.num_plan_failed += 1 + if self.cmd_plan and self._step_idx % 1 == 0: + cmd_state = self.cmd_plan[self.cmd_idx] + art_action = ArticulationAction( + cmd_state.position.cpu().numpy(), + cmd_state.velocity.cpu().numpy() * 0.0, + joint_indices=self.idx_list, + ) + self.cmd_idx += self.ds_ratio + if self.cmd_idx >= len(self.cmd_plan): + self.cmd_idx = 0 + self.cmd_plan = None + else: + self.num_last_cmd += 1 + art_action = ArticulationAction(joint_positions=sim_js.positions[self.arm_indices]) + else: + art_action = ArticulationAction(joint_positions=sim_js.positions[self.arm_indices]) + self._step_idx += 1 + arm_action = art_action.joint_positions + gripper_action = self.get_gripper_action() + joint_positions = np.concatenate([arm_action, gripper_action]) + self._action = { + "joint_positions": joint_positions, + "joint_indices": np.concatenate([self.arm_indices, self.gripper_indices]), + "lr_name": self.lr_name, + "arm_action": arm_action, + "gripper_action": gripper_action, + } + return self._action + + def get_gripper_action(self): + return np.clip(self._gripper_state * self._gripper_joint_position, 0.0, 0.04) + + def get_ee_pose(self): + sim_js = self.robot.get_joints_state() + q_state = torch.tensor(sim_js.positions[self.arm_indices], **self.tensor_args.as_torch_dict()).reshape(1, -1) + ee_pose = self.kin_model.get_state(q_state) + return ee_pose.ee_position[0].cpu().numpy(), ee_pose.ee_quaternion[0].cpu().numpy() + + def get_armbase_pose(self): + armbase_pose = get_relative_transform( + get_prim_at_path(self.robot_base_path), get_prim_at_path(self.task.root_prim_path) + ) + return pose_from_tf_matrix(armbase_pose) + + def forward_kinematic(self, q_state: np.ndarray): + q_state = q_state.reshape(1, -1) + q_state = self.tensor_args.to_device(q_state) + out = self.kin_model.get_state(q_state) + return out.ee_position[0].cpu().numpy(), out.ee_quaternion[0].cpu().numpy() + + def close_gripper(self): + self._gripper_state = -1.0 + + def open_gripper(self): + self._gripper_state = 1.0 + + def attach_obj(self, obj_prim_path: str, link_name="attached_object"): + sim_js = self.robot.get_joints_state() + js_names = self.robot.dof_names + cu_js = JointState( + position=self.tensor_args.to_device(sim_js.positions), + velocity=self.tensor_args.to_device(sim_js.velocities) * 0.0, + acceleration=self.tensor_args.to_device(sim_js.velocities) * 0.0, + jerk=self.tensor_args.to_device(sim_js.velocities) * 0.0, + joint_names=js_names, + ) + self.motion_gen.attach_objects_to_robot( + cu_js, + [obj_prim_path], + link_name=link_name, + sphere_fit_type=SphereFitType.VOXEL_VOLUME_SAMPLE_SURFACE, + world_objects_pose_offset=Pose.from_list([0, 0, 0.01, 1, 0, 0, 0], self.tensor_args), + ) + + def detach_obj(self): + self.motion_gen.detach_object_from_robot() +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 + +__init__(self, name, robot_file, task, world, constrain_grasp_approach, collision_activation_distance, ignore_substring, use_batch, **kwargs) + +Initialize the controller, load robot configuration, and set up CuRobo motion generator. + +Parameters: + +- **name **( str ): Controller name, matching the robot name in task. +- **robot_file **( str ): Path to CuRobo robot YAML configuration file. +- **task **( BaseTask ): The task instance containing robots and scene. +- **world **( World ): Isaac Sim world instance. +- **constrain_grasp_approach **( bool , optional): Whether to add grasp approach constraint. Default is `False `. +- **collision_activation_distance **( float , optional): Distance threshold for collision activation. Default is `0.03 `. +- **ignore_substring **( List[str] , optional): Substrings for objects to ignore in collision checking. +- **use_batch **( bool , optional): Enable batch planning mode for multiple goals. Default is `False `. +- ** **kwargs **: Additional keyword arguments. + +reset(self, ignore_substring=None) + +Reset controller state, update collision world, and initialize end-effector pose. + +Parameters: + +- **ignore_substring **( List[str] , optional): New collision filter substrings to use. + +update_pose_cost_metric(self, hold_vec_weight=None) + +Update the pose cost metric for constrained motion planning. This method is used to hold specific orientations or positions during robot motion, which is useful for tasks like keeping a container upright while moving. + +Parameters: + +- **hold_vec_weight **( List[float] , optional): A 6-element weight vector `[angular-x, angular-y, angular-z, linear-x, linear-y, linear-z] `that controls constraint costs. Defaults to `None `, which corresponds to `[0, 0, 0, 0, 0, 0] `(no constraints). For example, `[1, 1, 1, 0, 0, 0] `holds the tool orientation fixed during motion. + +Reference: + +- [CuRobo Constrained Planning](https://curobo.org/advanced_examples/3_constrained_planning.html) + +update_specific(self, ignore_substring, reference_prim_path) + +Update collision world with specific ignore substrings and reference prim path. Used for fine-grained collision filtering during skill execution. + +Parameters: + +- **ignore_substring **( List[str] ): List of substrings for objects to ignore in collision checking. +- **reference_prim_path **( str ): Reference prim path for relative transform calculations. + +plan(self, ee_translation_goal, ee_orientation_goal, sim_js, js_names) + +Generate a collision-free trajectory from current joint state to target end-effector pose. + +Parameters: + +- **ee_translation_goal **( torch.Tensor | np.ndarray ): Target end-effector position `[x, y, z] `. +- **ee_orientation_goal **( torch.Tensor | np.ndarray ): Target end-effector orientation as quaternion `[w, x, y, z] `. +- **sim_js **( JointState ): Current joint state from simulation. +- **js_names **( list ): List of joint names in simulation order. + +Returns: + +- **MotionGenResult **: CuRobo planning result containing success status, trajectory, and errors. + +forward(self, manip_cmd, eps=5e-3) + +Execute a manipulation command. This is the main entry point called by skills to execute motions. + +Parameters: + +- **manip_cmd **( tuple ): Command tuple `(ee_trans, ee_ori, gripper_fn, params) `: +- `ee_trans `: Target end-effector translation. +- `ee_ori `: Target end-effector orientation. +- `gripper_fn `: Name of gripper function to call (e.g., `"open_gripper" `, `"close_gripper" `). +- `params `: Parameters for the gripper function. + +- **eps **( float , optional): Position/orientation threshold to trigger new planning. Default is `5e-3 `. + +Returns: + +- **dict **: Action dictionary containing: +- `joint_positions `: Full joint positions including gripper. +- `joint_indices `: Indices of joints to command. +- `lr_name `: Left/right arm identifier. +- `arm_action `: Arm joint positions only. +- `gripper_action `: Gripper joint positions only. + +## Example: Lift2Controller [​](#example-lift2controller) + +The `Lift2Controller `demonstrates how to create a robot-specific controller by overriding key methods from `TemplateController `. This controller manages the ARX Lift-2 dual-arm robot with R5a arms. +python +``` +"""Lift2 mobile manipulator controller – template-based.""" + +import numpy as np +from core.controllers.base_controller import register_controller +from core.controllers.template_controller import TemplateController + +@register_controller +class Lift2Controller(TemplateController): + def _get_default_ignore_substring(self): + return ["material", "Plane", "conveyor", "scene", "table", "fluid"] + + def _configure_joint_indices(self, robot_file: str) -> None: + self.raw_js_names = ["joint1", "joint2", "joint3", "joint4", "joint5", "joint6"] + if "left" in robot_file: + self.cmd_js_names = ["fl_joint1", "fl_joint2", "fl_joint3", "fl_joint4", "fl_joint5", "fl_joint6"] + self.arm_indices = np.array([10, 12, 14, 16, 18, 20]) + self.gripper_indices = np.array([23]) + self.reference_prim_path = self.task.robots[self.name].fl_base_path + self.lr_name = "left" + self._gripper_state = 1.0 if self.robot.left_gripper_state == 1.0 else -1.0 + elif "right" in robot_file: + self.cmd_js_names = ["fr_joint1", "fr_joint2", "fr_joint3", "fr_joint4", "fr_joint5", "fr_joint6"] + self.arm_indices = np.array([9, 11, 13, 15, 17, 19]) + self.gripper_indices = np.array([21]) + self.reference_prim_path = self.task.robots[self.name].fr_base_path + self.lr_name = "right" + self._gripper_state = 1.0 if self.robot.right_gripper_state == 1.0 else -1.0 + else: + raise NotImplementedError("robot_file must contain 'left' or 'right'") + self._gripper_joint_position = np.array([1.0]) + + def _get_grasp_approach_linear_axis(self) -> int: + """Lift2 uses x-axis (0) for grasp approach.""" + return 0 + + def get_gripper_action(self): + return np.clip(self._gripper_state * self._gripper_joint_position, 0.0, 0.1) + + def forward(self, manip_cmd, eps=5e-3): + ee_trans, ee_ori = manip_cmd[0:2] + gripper_fn = manip_cmd[2] + params = manip_cmd[3] + gripper_vel = manip_cmd[4] if len(manip_cmd) > 4 else None + assert hasattr(self, gripper_fn) + method = getattr(self, gripper_fn) + if gripper_fn in ["in_plane_rotation", "mobile_move", "dummy_forward", "joint_ctrl"]: + return method(**params) + elif gripper_fn in ["update_pose_cost_metric", "update_specific"]: + method(**params) + return self.ee_forward(ee_trans, ee_ori, eps=eps, gripper_vel=gripper_vel, skip_plan=True) + else: + method(**params) + return self.ee_forward(ee_trans, ee_ori, eps=eps, gripper_vel=gripper_vel) + + def ee_forward( + self, + ee_trans, + ee_ori, + eps=1e-4, + skip_plan=False, + gripper_vel=None, + ): + super().ee_forward(ee_trans, ee_ori, eps=eps, skip_plan=skip_plan) + self._action["gripper_vel"] = gripper_vel + return self._action +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 + +### Overridden Methods [​](#overridden-methods) + +The following four methods must be overridden to create a robot-specific controller: + +_get_default_ignore_substring(self) + +Return a list of substrings for objects to ignore during collision checking. These typically include visual-only objects, scene elements, and robot-specific parts. + +Returns: + +- **List[str] **: List of substrings to filter out collision objects. For Lift2, this includes `"material" `, `"Plane" `, `"conveyor" `, `"scene" `, `"table" `, and `"fluid" `. + +_configure_joint_indices(self, robot_file) + +Configure the mapping between CuRobo planner joint names and simulation joint names. This is critical for translating planned trajectories into simulation commands. + +Parameters: + +- **robot_file **( str ): Path to the CuRobo robot configuration file. Used to determine left/right arm for dual-arm robots. + +Sets the following attributes: + +- **raw_js_names **( List[str] ): Joint names in CuRobo planner order. +- **cmd_js_names **( List[str] ): Joint names in simulation order. +- **arm_indices **( np.ndarray ): Indices of arm joints in simulation. +- **gripper_indices **( np.ndarray ): Indices of gripper joints in simulation. +- **reference_prim_path **( str ): Path to robot base prim for collision reference. +- **lr_name **( str ): "left" or "right" for dual-arm robots. + +_get_grasp_approach_linear_axis(self) + +Return the axis used for constrained grasp approach motion. This defines which axis the end-effector should align with during approach. + +Returns: + +- **int **: Axis index (0=x, 1=y, 2=z). Default in `TemplateController `is 2 (z-axis). Lift2 uses 0 (x-axis) due to its gripper orientation. + +get_gripper_action(self) + +Convert internal gripper state to actual gripper joint position command. Different robots have different gripper mechanisms and ranges. + +Returns: + +- **np.ndarray **: Gripper joint position(s). The value is clipped to the robot's valid gripper range. For Lift2, the range is `[0.0, 0.1] `. + +## References [​](#references) + +- [CuRobo Documentation](https://curobo.org/) +- [Genie Sim 3.0 - Motion Generator](https://github.com/AgibotTech/genie_sim/tree/main/source/data_collection/server/motion_generator) \ No newline at end of file diff --git a/docs_crawled/concepts_objects.md b/docs_crawled/concepts_objects.md new file mode 100644 index 0000000..5d3e954 --- /dev/null +++ b/docs_crawled/concepts_objects.md @@ -0,0 +1,372 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/objects.html + +# Objects [​](#objects) + +InternDataEngine supports various types of objects for simulation tasks. All object classes are located in `workflows/simbox/core/objects/ `. + +## Supported Object Types [​](#supported-object-types) + +| Class | Description | +| `RigidObject ` | Rigid body objects with physics properties (graspable objects) | +| `GeometryObject ` | Static geometry objects without physics (tables, fixtures) | +| `ArticulatedObject ` | Articulated objects with joints (microwaves, drawers) | +| `PlaneObject ` | Simple planes with textures (floors, backgrounds) | +| `XFormObject ` | Transform-only objects | +| `ShapeObject ` | Basic geometric shapes | +| `ConveyorObject ` | Conveyor belt objects | + +## RigidObject [​](#rigidobject) + +`RigidObject `is used for objects that have physical properties and can be manipulated by robots. It inherits from Isaac Sim's `RigidPrim `and supports collision detection, mass properties, and texture randomization. + +__init__(self, asset_root, root_prim_path, cfg, *args, **kwargs) + +Initialize a rigid object in the simulation scene. + +Parameters: + +- **asset_root **( str ): Root path for asset files. +- **root_prim_path **( str ): Root prim path in USD stage. +- **cfg **( dict ): Configuration dictionary containing: +- **name **( str ): Object name. +- **path **( str ): USD file path relative to asset_root. +- **prim_path_child **( str ): Child prim path for rigid body. +- **translation **( list , optional): Initial translation [x, y, z]. +- **euler **( list , optional): Initial euler rotation [rx, ry, rz] in degrees. +- **scale **( list , optional): Scale factor [sx, sy, sz]. +- **mass **( float , optional): Object mass. + +- ** **kwargs **: Additional keyword arguments passed to `RigidPrim `. + +### Config Example [​](#config-example) +yaml +``` +objects: + - + name: pick_object_left + path: pick_and_place/pre-train-pick/assets/omniobject3d-banana/omniobject3d-banana_001/Aligned_obj.usd + target_class: RigidObject + dataset: oo3d + category: "omniobject3d-banana" + prim_path_child: Aligned + translation: [0.0, 0.0, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [0.001, 0.001, 0.001] + apply_randomization: True + orientation_mode: "suggested" +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +### Configuration Parameters [​](#configuration-parameters) + +- **name **( str ): Unique identifier for this object instance. +- **path **( str ): Path to the USD file containing the object mesh. +- **target_class **( str ): Must be `RigidObject `for rigid bodies. +- **dataset **( str ): Dataset source identifier. +- **category **( str ): Object category name. +- **prim_path_child **( str ): Name of the child prim that contains the mesh. +- **translation **( list ): Initial position [x, y, z] in world coordinates. +- **euler **( list ): Initial rotation in degrees [roll, pitch, yaw]. +- **scale **( list ): Scale factors for each axis. +- **apply_randomization **( bool ): Whether to apply domain randomization. +- **orientation_mode **( str ): Orientation mode for randomization. + +## GeometryObject [​](#geometryobject) + +`GeometryObject `is used for static objects without physics simulation. It's ideal for environmental objects like tables, shelves, or fixtures that don't need to interact physically with other objects. + +__init__(self, asset_root, root_prim_path, cfg, *args, **kwargs) + +Initialize a geometry object in the simulation scene. + +Parameters: + +- **asset_root **( str ): Root path for asset files. +- **root_prim_path **( str ): Root prim path in USD stage. +- **cfg **( dict ): Configuration dictionary containing: +- **name **( str ): Object name. +- **path **( str ): USD file path relative to asset_root. +- **prim_path_child **( str , optional): Child prim path suffix. + +- ** **kwargs **: Additional keyword arguments passed to `GeometryPrim `. + +Difference from RigidObject: + +- No physics simulation (no mass, no collision response) +- Lighter weight for static environment objects +- Cannot be grasped or moved by robots + +### Config Example [​](#config-example-1) +yaml +``` +objects: + - + name: table + path: table0/instance.usd + target_class: GeometryObject + translation: [0.0, 0.0, 0.375] + scale: [0.001, 0.001053, 0.001056] +``` +1 +2 +3 +4 +5 +6 +7 + +### Configuration Parameters [​](#configuration-parameters-1) + +- **name **( str ): Unique identifier for this object. +- **path **( str ): Path to the USD file. +- **target_class **( str ): Must be `GeometryObject `for static geometry. +- **translation **( list ): Position in world coordinates. +- **scale **( list ): Scale factors for each axis. + +## PlaneObject [​](#planeobject) + +`PlaneObject `creates simple planes with optional texture mapping. It's commonly used for floors, backgrounds, and other flat surfaces. + +__init__(self, asset_root, root_prim_path, cfg, *args, **kwargs) + +Initialize a plane object in the simulation scene. + +Parameters: + +- **asset_root **( str ): Root path for asset files. +- **root_prim_path **( str ): Root prim path in USD stage. +- **cfg **( dict ): Configuration dictionary containing: +- **name **( str ): Object name. +- **size **( list ): Plane dimensions [width, height]. +- **translation **( list , optional): Position [x, y, z]. +- **euler **( list , optional): Rotation in degrees [roll, pitch, yaw]. +- **texture **( dict , optional): Texture configuration. + +- ** **kwargs **: Additional keyword arguments. + +### Config Example [​](#config-example-2) +yaml +``` +objects: + - + name: floor + target_class: PlaneObject + size: [5.0, 5.0] + translation: [0, 0, 0] + texture: + texture_lib: "floor_textures" + apply_randomization: True + texture_id: 1 + texture_scale: [1.0, 1.0] + - + name: background0 + target_class: PlaneObject + size: [3.0, 5.0] + translation: [-2, 0, 1] + euler: [0.0, 90.0, 0.0] + texture: + texture_lib: "background_textures" + apply_randomization: True + texture_id: 1 + texture_scale: [1.0, 1.0] + - + name: background1 + target_class: PlaneObject + size: [3.0, 5.0] + translation: [2, 0, 1] + euler: [0.0, 90.0, 0.0] + texture: + texture_lib: "background_textures" + apply_randomization: True + texture_id: 1 + texture_scale: [1.0, 1.0] + - + name: background2 + target_class: PlaneObject + size: [5.0, 3.0] + translation: [0, -2, 1] + euler: [90.0, 0.0, 0.0] + texture: + texture_lib: "background_textures" + apply_randomization: True + texture_id: 1 + texture_scale: [1.0, 1.0] + - + name: background3 + target_class: PlaneObject + size: [5.0, 3.0] + translation: [0, 2, 1] + euler: [90.0, 0.0, 0.0] + texture: + texture_lib: "background_textures" + apply_randomization: True + texture_id: 1 + texture_scale: [1.0, 1.0] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 + +### Configuration Parameters [​](#configuration-parameters-2) + +- **name **( str ): Unique identifier for the plane. +- **target_class **( str ): Must be `PlaneObject `. +- **size **( list ): Plane dimensions [width, height]. +- **translation **( list ): Position [x, y, z]. +- **euler **( list ): Rotation angles [roll, pitch, yaw] in degrees. +- **texture.texture_lib **( str ): Name of texture library folder. +- **texture.apply_randomization **( bool ): Whether to randomize texture selection. +- **texture.texture_id **( int ): Specific texture ID (used when randomization is False). +- **texture.texture_scale **( list ): Scale factors for texture UV mapping. + +## ArticulatedObject [​](#articulatedobject) + +`ArticulatedObject `handles objects with movable joints, such as microwaves, drawers, and cabinets. It inherits from Isaac Sim's `Articulation `class and provides methods for joint control and state retrieval. + +__init__(self, asset_root, root_prim_path, cfg, *args, **kwargs) + +Initialize an articulated object in the simulation scene. Loads articulation info from the specified `info.json `file. + +Parameters: + +- **asset_root **( str ): Root path for asset files. +- **root_prim_path **( str ): Root prim path in USD stage. +- **cfg **( dict ): Configuration dictionary containing: +- **name **( str ): Object name. +- **path **( str ): USD file path relative to asset_root. +- **info_name **( str ): Name of the skill folder containing `info.json `. +- **category **( str ): Object category identifier. +- **euler **( list , optional): Initial rotation [roll, pitch, yaw]. +- **joint_position_range **( list , optional): Random range for initial joint position [min, max]. +- **apply_randomization **( bool , optional): Whether to apply domain randomization. + +- ** **kwargs **: Additional keyword arguments passed to `Articulation `. + +get_articulated_info(self, object_info) + +Parse and store articulation information from the object's info.json file. This method extracts keypoints, joint paths, scale information, and axis orientations. + +Parameters: + +- **object_info **( dict ): Dictionary loaded from `Kps/{skill}/info.json `containing: +- **object_keypoints **( dict ): Keypoint positions in link_0 frame. +- **object_scale **( list ): Object scale factors. +- **object_prim_path **( str ): Prim path for the object. +- **object_link_path **( str ): Prim path for the articulated link. +- **object_base_path **( str ): Prim path for the base link. +- **object_joint_path **( str ): Prim path for the joint. +- **joint_index **( int ): Index of the main joint. +- **object_link0_rot_axis **( str ): Rotation axis of the link. +- **object_base_front_axis **( str ): Front axis of the base. + +get_joint_position(self, stage) + +Count and configure joints in the articulated object. This method identifies prismatic and revolute joints, and optionally fixes the base of the articulated object. + +Parameters: + +- **stage **( UsdStage ): The USD stage containing the articulation. + +Sets the following attributes: + +- **object_joint_number **( int ): Total number of joints found. + +### Config Example [​](#config-example-3) +yaml +``` +objects: + - + name: close_v_left + target_class: ArticulatedObject + info_name: "close_v" + euler: [0.0, 0.0, 90.0] + joint_position_range: [0.6, 0.8] + apply_randomization: False + path: "art/microwave_gr/microwave7119/instance.usd" + category: "microwave_gr" +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +### Configuration Parameters [​](#configuration-parameters-3) + +- **name **( str ): Unique identifier for the articulated object. +- **target_class **( str ): Must be `ArticulatedObject `. +- **info_name **( str ): Name of the skill folder containing `info.json `. +- **euler **( list ): Initial rotation [roll, pitch, yaw] in degrees. +- **joint_position_range **( list ): Random range for initial joint position [min, max]. +- **apply_randomization **( bool ): Whether to apply domain randomization. +- **path **( str ): Path to the USD file (relative to asset_root). +- **category **( str ): Object category identifier. \ No newline at end of file diff --git a/docs_crawled/concepts_robots.md b/docs_crawled/concepts_robots.md new file mode 100644 index 0000000..13ef257 --- /dev/null +++ b/docs_crawled/concepts_robots.md @@ -0,0 +1,736 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/robots.html + +# Robots [​](#robots) + +InternDataEngine supports multiple robot platforms for manipulation tasks. Each robot has a dedicated wrapper class for articulation control and state management. + +## Supported Robots [​](#supported-robots) + +| Robot | Type | DOF | Gripper (DOF) | Arm Model | +| **ARX Lift-2 ** | Dual-arm | 6+6 | Parallel (2) | R5a | +| **Agilex Split Aloha ** | Dual-arm | 6+6 | Parallel (2) | Piper-100 | +| **Genie-1 ** | Dual-arm | 7+7 | Parallel (2) | G1-120s | +| **Franka FR3 ** | Single-arm | 7 | Panda (1) | Franka | +| **Franka Robotiq85 ** | Single-arm | 7 | Robotiq 2F-85 (2) | Franka | + +### Robot End Effector and TCP Frame Visualizations [​](#robot-end-effector-and-tcp-frame-visualizations) +FR3 (Single-arm, Franka Panda Gripper) + +![FR3 EE Head](/InternDataEngine-Docs/franka/ee_head_vis.png) + +*FR3 end-effector head frame visualization * + +![FR3 TCP Head](/InternDataEngine-Docs/franka/tcp_head_vis.png) + +*FR3 TCP (Tool Center Point) head frame visualization * + +![FR3 TCP Hand](/InternDataEngine-Docs/franka/tcp_hand_vis.png) + +*FR3 TCP hand frame visualization * +Franka Robotiq85 (Single-arm, Robotiq 2F-85 Gripper) + +![Franka Robotiq85 EE Head](/InternDataEngine-Docs/frankarobotiq/ee_head_vis.png) + +*Franka Robotiq85 end-effector head frame visualization * + +![Franka Robotiq85 TCP Head](/InternDataEngine-Docs/frankarobotiq/tcp_head_vis.png) + +*Franka Robotiq85 TCP (Tool Center Point) head frame visualization * + +![Franka Robotiq85 TCP Hand](/InternDataEngine-Docs/frankarobotiq/tcp_hand_vis.png) + +*Franka Robotiq85 TCP hand frame visualization * +Genie-1 (Dual-arm, G1-120s) + +![Genie-1 Left EE Head](/InternDataEngine-Docs/genie1/leftee_head_left_vis.png) + +*Genie-1 left arm end-effector head frame visualization * + +![Genie-1 Left TCP Head](/InternDataEngine-Docs/genie1/lefttcp_head_vis.png) + +*Genie-1 left arm TCP head frame visualization * + +![Genie-1 Left TCP Hand](/InternDataEngine-Docs/genie1/lefttcp_hand_left_vis.png) + +*Genie-1 left arm TCP hand frame visualization * + +![Genie-1 Right EE Head](/InternDataEngine-Docs/genie1/rightee_head_vis.png) + +*Genie-1 right arm end-effector head frame visualization * + +![Genie-1 Right TCP Head](/InternDataEngine-Docs/genie1/righttcp_head_vis.png) + +*Genie-1 right arm TCP head frame visualization * + +![Genie-1 Right TCP Hand](/InternDataEngine-Docs/genie1/righttcp_hand_right_vis.png) + +*Genie-1 right arm TCP hand frame visualization * +ARX Lift-2 (Dual-arm, R5a) + +![Lift-2 Left EE Head](/InternDataEngine-Docs/lift2/leftee_head_left_vis.jpeg) + +*Lift-2 left arm end-effector head frame visualization * + +![Lift-2 Left TCP Head](/InternDataEngine-Docs/lift2/lefttcp_head_vis.jpeg) + +*Lift-2 left arm TCP head frame visualization * + +![Lift-2 Left TCP Hand](/InternDataEngine-Docs/lift2/lefttcp_hand_left_vis.jpeg) + +*Lift-2 left arm TCP hand frame visualization * + +![Lift-2 Right EE Head](/InternDataEngine-Docs/lift2/rightee_head_vis.jpeg) + +*Lift-2 right arm end-effector head frame visualization * + +![Lift-2 Right TCP Head](/InternDataEngine-Docs/lift2/righttcp_head_vis.jpeg) + +*Lift-2 right arm TCP head frame visualization * + +![Lift-2 Right TCP Hand](/InternDataEngine-Docs/lift2/righttcp_hand_right_vis.jpeg) + +*Lift-2 right arm TCP hand frame visualization * +Agilex Split Aloha (Dual-arm, Piper-100) + +![Split Aloha Right EE Head](/InternDataEngine-Docs/split_aloha/rightee_head_vis.png) + +*Split Aloha right arm end-effector head frame visualization * + +![Split Aloha Right TCP Head](/InternDataEngine-Docs/split_aloha/righttcp_head_vis.png) + +*Split Aloha right arm TCP head frame visualization * + +![Split Aloha Right TCP Hand](/InternDataEngine-Docs/split_aloha/righttcp_hand_right_vis.png) + +*Split Aloha right arm TCP hand frame visualization * + +## Robot Configuration [​](#robot-configuration) + +Robot configuration is split into two parts: **task-level configuration **(in task YAML) and **robot-specific parameters **(in separate robot YAML files). + +### Part 1: Task-Level Configuration (Task YAML) [​](#part-1-task-level-configuration-task-yaml) + +Configure robot instance in task YAML files: +yaml +``` +robots: + - name: "lift2" # Robot identifier + robot_config_file: workflows/simbox/core/configs/robots/lift2.yaml # Path to robot-specific config + euler: [0.0, 0.0, 90.0] # Initial orientation [roll, pitch, yaw] in degrees + ignore_substring: ["material", "table", "gso_box"] # Collision filter substrings +``` +1 +2 +3 +4 +5 + +### Part 2: Robot-Specific Parameters (Robot YAML) [​](#part-2-robot-specific-parameters-robot-yaml) + +Define robot hardware parameters in a separate YAML file (e.g., `workflows/simbox/core/configs/robots/lift2.yaml `): +yaml +``` +# Robot info +target_class: Lift2 # Python class for robot wrapper +path: "lift2/robot_invisible.usd" # USD file path relative to asset root + +# CuRobo kinematics config files (one per arm for dual-arm robots) +robot_file: + - workflows/simbox/curobo/src/curobo/content/configs/robot/r5a_left_arm.yml + - workflows/simbox/curobo/src/curobo/content/configs/robot/r5a_right_arm.yml + +# Gripper parameters +gripper_max_width: 0.088 # Maximum gripper opening width (meters) +gripper_min_width: 0.0 # Minimum gripper closing width (meters) +tcp_offset: 0.125 # Tool center point offset from end-effector (meters) + +# Solver parameters for physics simulation +solver_position_iteration_count: 128 # Position solver iterations +solver_velocity_iteration_count: 4 # Velocity solver iterations +stabilization_threshold: 0.005 # Stabilization threshold + +# Joint indices in articulation +left_joint_indices: [10, 12, 14, 16, 18, 20] # Left arm joint indices +right_joint_indices: [9, 11, 13, 15, 17, 19] # Right arm joint indices +left_gripper_indices: [23] # Left end-effector (gripper) joint index +right_gripper_indices: [21] # Right end-effector (gripper) joint index +lift_indices: [6] # Lift joint index + +# End-effector paths +fl_ee_path: "lift2/lift2/fl/link6" # Front-left end-effector prim path +fr_ee_path: "lift2/lift2/fr/link6" # Front-right end-effector prim path +fl_base_path: "lift2/lift2/fl/base_link" # Front-left base prim path +fr_base_path: "lift2/lift2/fr/base_link" # Front-right base prim path + +# Gripper keypoints for visualization +fl_gripper_keypoints: + tool_head: [0.135, 0.0, 0.0, 1] + tool_tail: [0.085, 0.0, 0.0, 1] + tool_side: [0.135, -0.044, 0.0, 1] +fr_gripper_keypoints: + tool_head: [0.135, 0.0, 0.0, 1] + tool_tail: [0.085, 0.0, 0.0, 1] + tool_side: [0.135, -0.044, 0.0, 1] + +# Collision filter paths +fl_filter_paths: # Paths to filter from collision (gripper fingers) + - "lift2/lift2/fl/link7" + - "lift2/lift2/fl/link8" +fr_filter_paths: + - "lift2/lift2/fr/link7" + - "lift2/lift2/fr/link8" +fl_forbid_collision_paths: # Paths forbidden for self-collision + - "lift2/lift2/fl/link2" + - "lift2/lift2/fl/link3" + - "lift2/lift2/fl/link4" + - "lift2/lift2/fl/link5" +fr_forbid_collision_paths: + - "lift2/lift2/fr/link2" + - "lift2/lift2/fr/link3" + - "lift2/lift2/fr/link4" + - "lift2/lift2/fr/link5" + +# Pose processing parameters +R_ee_graspnet: [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]] # Grasp rotation correction +ee_axis: "x" # End-effector approach axis (x/y/z) + +# Default joint home positions (radians) +left_joint_home: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +right_joint_home: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +left_joint_home_std: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # Randomization std for left arm +right_joint_home_std: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # Randomization std for right arm +left_gripper_home: [0.044] # Left gripper default width (meters) +right_gripper_home: [0.044] # Right gripper default width (meters) +lift_home: [0.46] # Lift joint default position (meters) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 + +## Understanding Robot Parameters [​](#understanding-robot-parameters) + +### Task-Level Fields [​](#task-level-fields) + +- **name **( str ): Robot identifier used in skills and cameras. +- **robot_config_file **( str ): Path to robot-specific YAML configuration file. +- **euler **( list ): Initial robot orientation in degrees [roll, pitch, yaw]. +- **ignore_substring **( list ): Collision filter substrings to ignore during simulation. + +### Robot-Specific Fields [​](#robot-specific-fields) + +Some fields require detailed explanation due to their importance in grasp pose processing and robot kinematics: + +R_ee_graspnet + +A 3×3 rotation matrix that transforms the grasp pose orientation from the pre-defined graspnet frame to the robot's end-effector frame. Our generated grasp pose follows the frame definition from GraspNet. + +![GraspNet Gripper Frame Definition](/InternDataEngine-Docs/graspnet_def.png) + +*GraspNet gripper frame definition (source: graspnetAPI) * + +*Source: [graspnet/graspnetAPI](https://github.com/graspnet/graspnetAPI?tab=readme-ov-file)* + +The following examples illustrate how `R_ee_graspnet `is configured for different robots. You can verify these values by comparing the GraspNet frame definition with each robot's end-effector orientation shown in the visualizations above. + +Config Example: +yaml +``` +# FR3 +R_ee_graspnet: [[0.0, 0.0, -1.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]] + +# Genie-1 +R_ee_graspnet: [[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] + +# ARX Lift-2 +R_ee_graspnet: [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]] + +# Agilex Split Aloha +R_ee_graspnet: [[0.0, -1.0, 0.0], [0.0, 0.0, -1.0], [1.0, 0.0, 0.0]] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 + +fl_ee_path / fr_ee_path + +The USD prim path to the end-effector link (typically the last link of the arm **before **the TCP). + +Warning + +This link frame has a fixed transformation to the TCP frame. This path should be **aligned with the `ee_link `in the CuRobo config file **. + +Usage: + +- Compute the end-effector pose relative to the robot base via `get_relative_transform ` +- Define where gripper keypoints are attached +- Serve as the reference frame for TCP (Tool Center Point) calculations + +Config Example: +yaml +``` +# FR3 +fl_ee_path: "fr3/panda_hand" # relative to root robot prim path + +# Franka Robotiq +fl_ee_path: "arm/panda_link8" + +# Agilex Split Aloha +fl_ee_path: "split_aloha_mid_360_with_piper/split_aloha_mid_360_with_piper/fl/link6" +fr_ee_path: "split_aloha_mid_360_with_piper/split_aloha_mid_360_with_piper/fr/link6" +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 + +ee_axis + +The axis along from end-effector origin to gripper TCP. Valid values are `"x" `, `"y" `, or `"z" `. + +Usage: + +- Used in `pose_post_process_fn `to calculate the actual EE position from TCP +- Affects the 180° rotation variant generation for grasp pose diversity + +Config Example: +yaml +``` +# FR3 +ee_axis: "z" + +# ARX Lift-2 +ee_axis: "x" + +# Agilex Split Aloha +ee_axis: "z" +``` +1 +2 +3 +4 +5 +6 +7 +8 + +tcp_offset + +The distance from the end-effector frame origin to the Tool Center Point (TCP). The TCP is the point where the gripper fingertips meet when closed, which is the actual grasping point. + +Usage: + +- During grasp pose processing, the EE position is calculated as: `ee_center = tcp_center + approach_axis * (depth - tcp_offset) ` +- This offset accounts for the physical distance between the robot's end-effector frame and the actual grasping point + +fl_gripper_keypoints / fr_gripper_keypoints + +3D keypoints defined in the end-effector frame for articulated object manipulation planning. + +Assume the gripper is oriented upright facing the user. The keypoints are defined as follows: + +- **`tool_head `**: The point at the midpoint of the gripper fingertips, along the approach direction. +- **`tool_tail `**: The point at the midpoint of the gripper finger bases, along the same approach direction. +- **`tool_side `**: A point on the side of the right fingertip, used to indicate the gripper width. + +![Gripper Keypoints Visualization](/InternDataEngine-Docs/gripper_kps.jpg) + +*Gripper keypoints visualization showing tool_head, tool_tail, and tool_side * + +Config Example: +yaml +``` +# ARX Lift-2 +fl_gripper_keypoints: + tool_head: [0.135, 0.0, 0.0, 1] # Gripper fingertip (approach direction) + tool_tail: [0.085, 0.0, 0.0, 1] # Gripper base + tool_side: [0.135, -0.044, 0.0, 1] # Side point for width visualization +``` +1 +2 +3 +4 +5 + +### Other Fields [​](#other-fields) + +- **target_class **( str ): Python class for robot wrapper (e.g., `Lift2 `, `FR3 `). +- **path **( str ): USD file path relative to asset root. +- **robot_file **( list ): CuRobo kinematics config file(s) - one per arm. +- **gripper_max_width **( float ): Maximum gripper opening width (meters). +- **gripper_min_width **( float ): Minimum gripper closing width (meters). +- **solver_position_iteration_count **( int ): Physics solver position iterations. +- **solver_velocity_iteration_count **( int ): Physics solver velocity iterations. +- **stabilization_threshold **( float ): Physics stabilization threshold. +- **left_joint_indices **( list ): Joint indices for left arm in articulation. +- **right_joint_indices **( list ): Joint indices for right arm in articulation. +- **left_gripper_indices **( list ): Gripper joint index for left arm. +- **right_gripper_indices **( list ): Gripper joint index for right arm. +- **lift_indices **( list ): Lift joint indices (for robots with lift mechanism). +- **fl_base_path **( str ): Left arm base prim path. +- **fr_base_path **( str ): Right arm base prim path. +- **fl_filter_paths **( list ): Collision filter prims' paths for left arm. +- **fr_filter_paths **( list ): Collision filter prims' paths for right arm. +- **fl_forbid_collision_paths **( list ): Forbidden collision prims' paths for left arm. +- **fr_forbid_collision_paths **( list ): Forbidden collision prims' paths for right arm. +- **left_joint_home **( list ): Default joint positions for left arm (radians). +- **right_joint_home **( list ): Default joint positions for right arm (radians). +- **left_joint_home_std **( list ): Standard deviation for randomizing left arm home position. +- **right_joint_home_std **( list ): Standard deviation for randomizing right arm home position. +- **left_gripper_home **( list ): Default gripper joint value for left gripper (Isaac). +- **right_gripper_home **( list ): Default gripper joint value for right gripper (Isaac). +- **lift_home **( list ): Default lift joint position (meters). + +## Robot Wrappers [​](#robot-wrappers) + +Robot wrappers ( `workflows/simbox/core/robots/ `) provide a unified interface for: + +- Articulation control +- Gripper interface +- State / observation management +- Grasp pose post-processing + +All concrete robots (e.g., `FR3 `, `FrankaRobotiq `, `Genie1 `, `Lift2 `, `SplitAloha `) share the same `TemplateRobot `implementation, with differences configured through YAML files and minimal subclass code. + +Template Code Example: +python +``` +from copy import deepcopy + +import numpy as np +from core.robots.base_robot import register_robot +from omni.isaac.core.robots.robot import Robot +from omni.isaac.core.utils.prims import create_prim, get_prim_at_path +from omni.isaac.core.utils.transformations import ( + get_relative_transform, + tf_matrix_from_pose, +) +from scipy.interpolate import interp1d + +@register_robot +class TemplateRobot(Robot): + """ + Template class for manipulator robots. + + All important parameters should be prepared in cfg before instantiation. + The cfg is merged from: robot_config_file -> task_config_robots + """ + + def __init__(self, asset_root: str, root_prim_path: str, cfg: dict, *args, **kwargs): + self.asset_root = asset_root + self.cfg = cfg + + # Create prim from USD + usd_path = f"{asset_root}/{cfg['path']}" + prim_path = f"{root_prim_path}/{cfg['name']}" + create_prim(usd_path=usd_path, prim_path=prim_path) + super().__init__(prim_path, cfg["name"], *args, **kwargs) + + self.robot_prim_path = prim_path + + # Gripper parameters (from cfg) + self.gripper_max_width = cfg["gripper_max_width"] + self.gripper_min_width = cfg["gripper_min_width"] + + # Solver parameters + self.set_solver_position_iteration_count(cfg["solver_position_iteration_count"]) + self.set_stabilization_threshold(cfg["stabilization_threshold"]) + self.set_solver_velocity_iteration_count(cfg["solver_velocity_iteration_count"]) + + # Setup from config + self._setup_joint_indices() + self._setup_paths() + self._setup_gripper_keypoints() + self._setup_collision_paths() + self._load_extra_depth(usd_path) + + def initialize(self, *args, **kwargs): + super().initialize() + self._articulation_view.initialize() + self._setup_joint_velocities() + self._setup_joint_homes() + self._set_initial_positions() + + def apply_action(self, joint_positions, joint_indices, *args, **kwargs): + self._articulation_view.set_joint_position_targets(joint_positions, joint_indices=joint_indices) + + def get_observations(self) -> dict: + joint_state = self.get_joints_state() + qpos, qvel = joint_state.positions, joint_state.velocities + + T_base_ee_fl = get_relative_transform( + get_prim_at_path(self.fl_ee_path), get_prim_at_path(self.fl_base_path) + ) + T_world_base = tf_matrix_from_pose(*self.get_local_pose()) + + obs = self._build_observations(qpos, qvel, T_base_ee_fl, T_world_base) + return obs + + def pose_post_process_fn( + self, poses, *args, lr_arm="left", grasp_scale=1, tcp_offset=None, constraints=None, **kwargs + ): + if poses.shape[-2:] == (4, 4): + return poses + + R_ee_graspnet = self._get_R_ee_graspnet() + n_grasps = poses.shape[0] + T_obj_tcp = np.repeat(np.eye(4)[np.newaxis, :, :], n_grasps, axis=0) + R_ee_graspnet = np.array(R_ee_graspnet) + T_obj_tcp[:, :3, :3] = np.matmul(poses[:, 4:13].reshape(-1, 3, 3), R_ee_graspnet.T) + T_obj_tcp[:, :3, 3] = poses[:, 13:16] * grasp_scale + scores = poses[:, 0] + widths = np.clip(poses[:, 1:2], self.gripper_min_width, self.gripper_max_width) + depths = poses[:, 3:4] + + if tcp_offset is None: + tcp_offset = self.tcp_offset + + if self._gripper_ed_func is not None: + depths = depths + self._gripper_ed_func(widths) + + # ... see full implementation in workflows/simbox/core/robots/template_robot.py +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 + +__init__(self, asset_root: str , root_prim_path: str , cfg: dict , *args, **kwargs) + +Create a robot instance from USD and initialize all geometry and dynamics-related paths/parameters based on configuration. + +Parameters: + +- **asset_root **( str ): Root directory for robot assets. +- **root_prim_path **( str ): Root prim path where the robot is mounted in the USD stage. +- **cfg **( dict ): Merged robot configuration (from robot YAML + task YAML). +- ***args, **kwargs **: Passed to the `Robot `base class. + +initialize(self, *args, **kwargs) + +Perform one-time initialization after the physics engine is ready, including joint velocity limits, home poses, and initial joint positions. + +apply_action(self, joint_positions, joint_indices, *args, **kwargs) + +Send joint position targets to the Isaac articulation. This is one of the main interfaces between upper-level controllers/policies and the robot. + +Parameters: + +- **joint_positions **( np.ndarray ): Target joint positions. +- **joint_indices **( np.ndarray ): Joint indices to control. +- ***args, **kwargs **: Reserved for future extensions (e.g., velocity, torque control). + +get_observations(self) + +Collect the robot's current state for use as observation input by upper-level policies/planning modules. + +Returns: + +- **dict **: Observation dictionary for policy/planning. + +pose_post_process_fn(self, poses, *args, lr_arm="left", grasp_scale=1, tcp_offset=None, constraints=None, **kwargs) + +Convert grasp poses from a graspnet-style annotations (e.g., `(score, width, depth, R, t) `) to robot-specific end-effector pose sets, including TCP offset, grasp width clipping, and optional spatial constraints. + +Core Logic: + +- If `poses `is already a transformation matrix of shape `(N, 4, 4) `, return directly. +- Use `R_ee_graspnet = self._get_R_ee_graspnet() `to correct the grasp rotation and construct `T_obj_tcp `: +- Rotation: `R_tcp = poses_rot @ R_ee_graspnet^T `. +- Translation: `t_tcp = poses[:, 13:16] * grasp_scale `. + +- Clip grasp widths `widths `to `[gripper_min_width, gripper_max_width] `, and optionally correct insertion depth `depths `using `_gripper_ed_func `. +- Use `tcp_offset `and the end-effector axis (determined by `ee_axis `) to transform TCP to actual EE transform `T_obj_ee `. +- If `constraints = [axis, min_ratio, max_ratio] `is provided, filter grasps along the specified axis, keeping only those within the given range. +- Call `_apply_rotation_variant `to generate a 180° rotation variant around the grasp axis, returning two sets of candidate grasp poses and their scores. + +Parameters: + +- **poses **( np.ndarray ): Raw grasp annotations, typically shape `(N, 16) `. +- **lr_arm **( str ): Left/right arm marker ("left" or "right", currently mainly used by upper layers). +- **grasp_scale **( float ): Grasp position scale factor. +- **tcp_offset **( float , optional): TCP offset relative to EE; defaults to `self.tcp_offset `from config. +- **constraints **( list , optional): Spatial filtering constraint of the form `[axis, min_ratio, max_ratio] `. +- ***args, **kwargs **: Reserved for extensions. + +Returns: + +- **tuple **: +- First item: `np.ndarray `of EE poses, shape approximately `(2N_filtered, 4, 4) `. +- Second item: `np.ndarray `of corresponding scores, shape approximately `(2N_filtered,) `. + +## References [​](#references) + +- [Isaac Sim Robot Manipulators Documentation](https://docs.isaacsim.omniverse.nvidia.com/5.1.0/py/source/extensions/isaacsim.robot.manipulators/docs/index.html) \ No newline at end of file diff --git a/docs_crawled/concepts_skills.md b/docs_crawled/concepts_skills.md new file mode 100644 index 0000000..2c03012 --- /dev/null +++ b/docs_crawled/concepts_skills.md @@ -0,0 +1,61 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/skills.html + +# Skills [​](#skills) + +Skills are the fundamental building blocks of robotic manipulation in InternDataEngine. Each skill defines an atomic action that a robot can perform, from simple gripper operations to complex manipulation sequences. + +## Quick Navigation [​](#quick-navigation) + +| Category | Description | +| [Overview](/InternDataEngine-Docs/concepts/skills/overview.html) | Skill architecture and available skills | +| [Pick Skill](/InternDataEngine-Docs/concepts/skills/pick.html) | Grasping and lifting objects | +| [Place Skill](/InternDataEngine-Docs/concepts/skills/place.html) | Placing objects at target locations | +| [Articulation Skill](/InternDataEngine-Docs/concepts/skills/articulation.html) | Operating articulated objects (doors, drawers) | + +## What is a Skill? [​](#what-is-a-skill) + +A skill encapsulates: + +- **Motion Planning **: Trajectory generation using CuRobo +- **Execution Control **: Step-by-step command execution +- **State Monitoring **: Success/failure evaluation +- **Gripper Actions **: Open/close operations + +## Skill Categories [​](#skill-categories) + +### Manipulation Skills [​](#manipulation-skills) + +Skills for interacting with objects in the environment: + +- **Pick **: Grasp and lift objects using sampled grasp poses +- **Place **: Position objects at target locations +- **Articulation **: Operate doors, drawers, and other articulated objects + +### Motion Skills [​](#motion-skills) + +Skills for robot movement: + +- `goto_pose `- Move to a target end-effector pose +- `move `- Cartesian motion in a direction +- `rotate `- Rotate the end-effector + +### Utility Skills [​](#utility-skills) + +Helper skills for common operations: + +- `home `- Return to home configuration +- `open `/ `close `- Gripper control +- `wait `- Pause execution + +## Getting Started [​](#getting-started) + +- Read the [Skills Overview](/InternDataEngine-Docs/concepts/skills/overview.html)to understand the architecture +- Learn about [Pick Skill](/InternDataEngine-Docs/concepts/skills/pick.html)for grasping objects +- Explore [Place Skill](/InternDataEngine-Docs/concepts/skills/place.html)for placing operations +- Check [Articulation Skill](/InternDataEngine-Docs/concepts/skills/articulation.html)for operating doors and drawers + +## Related Topics [​](#related-topics) + +- [Tasks](/InternDataEngine-Docs/concepts/tasks/)- Combine skills into complete workflows +- [Controllers](/InternDataEngine-Docs/concepts/controllers/)- Understand motion control +- [Custom Skill](/InternDataEngine-Docs/custom/skill/)- Create your own skills \ No newline at end of file diff --git a/docs_crawled/concepts_skills_articulation.md b/docs_crawled/concepts_skills_articulation.md new file mode 100644 index 0000000..e3c6e92 --- /dev/null +++ b/docs_crawled/concepts_skills_articulation.md @@ -0,0 +1,577 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/skills/articulation.html + +# Articulation Skills [​](#articulation-skills) + +Articulation skills operate objects with joints, such as doors, drawers, microwaves, laptops, and knobs. They are all built on top of a keypoint-based planner ( **KPAMPlanner **) that solves geometric constraints online to generate keyframe trajectories. + +## Available Articulation Skills [​](#available-articulation-skills) +text +``` +workflows/simbox/core/skills/ +├── artpreplan.py # Pre-plan for long-horizon tasks +├── open.py # Open or pull articulated objects +├── close.py # Close or push articulated objects +└── rotate.py # Rotate knobs / handles +``` +1 +2 +3 +4 +5 + +| Skill | Description | Use Cases | +| `artpreplan ` | Pre-plan / fake-plan for long-horizon tasks | Search for reasonable layouts before actual execution | +| `open ` | Open or pull articulated objects | Microwave doors, cabinet doors, laptop screens, drawers | +| `close ` | Close or push articulated objects | Push microwaves closed, fold laptops, push drawers | +| `rotate ` | Rotate knobs or handles | Twist oven knobs, turn handles | + +### Skill Roles [​](#skill-roles) + +- + +**`artpreplan.py `**: Performs a **fake plan **in long-horizon tasks. It uses KPAM to search for reasonable keyframes and layouts (e.g., door open angle, drawer position) without actually executing them. The results inform subsequent skills about feasible configurations. + +- + +**`open.py `**: Opens articulated objects: + +- Horizontal opening: microwave doors, cabinet doors +- Vertical opening: laptop screens +- Pulling: drawers with handles + +- + +**`close.py `**: Closes articulated objects: + +- Horizontal closing: push microwave doors closed +- Vertical closing: fold laptop screens down +- Push closing: push drawers back in + +- + +**`rotate.py `**: Grabs and rotates knobs or rotary handles around a specified axis. + +All skills share the same planner core ( `KPAMPlanner `in `workflows/simbox/solver/planner.py `), inspired by **[GenSim2](https://gensim2.github.io/)**. + +## Core API [​](#core-api) + +Below we use `Close `as the main example. + +Code Example: +python +``` +# Source: workflows/simbox/core/skills/close.py +from copy import deepcopy + +import numpy as np +from core.skills.base_skill import BaseSkill, register_skill +from omegaconf import DictConfig +from omni.isaac.core.controllers import BaseController +from omni.isaac.core.robots.robot import Robot +from omni.isaac.core.tasks import BaseTask +from omni.isaac.core.utils.prims import get_prim_at_path +from omni.isaac.core.utils.transformations import get_relative_transform +from scipy.spatial.transform import Rotation as R +from solver.planner import KPAMPlanner + +@register_skill +class Close(BaseSkill): + def __init__(self, robot: Robot, controller: BaseController, task: BaseTask, cfg: DictConfig, *args, **kwargs): + super().__init__() + self.robot = robot + self.controller = controller + self.task = task + self.stage = task.stage + self.name = cfg["name"] + art_obj_name = cfg["objects"][0] + self.skill_cfg = cfg + self.art_obj = task.objects[art_obj_name] + self.planner_setting = cfg["planner_setting"] + self.contact_pose_index = self.planner_setting["contact_pose_index"] + self.success_threshold = self.planner_setting["success_threshold"] + self.update_art_joint = self.planner_setting.get("update_art_joint", False) + if kwargs: + self.world = kwargs["world"] + self.draw = kwargs["draw"] + self.manip_list = [] + + if self.skill_cfg.get("obj_info_path", None): + self.art_obj.update_articulated_info(self.skill_cfg["obj_info_path"]) + + lr_arm = "left" if "left" in self.controller.robot_file else "right" + self.fingers_link_contact_view = task.artcontact_views[robot.name][lr_arm][art_obj_name + "_fingers_link"] + self.fingers_base_contact_view = task.artcontact_views[robot.name][lr_arm][art_obj_name + "_fingers_base"] + self.forbid_collision_contact_view = task.artcontact_views[robot.name][lr_arm][ + art_obj_name + "_forbid_collision" + ] + self.collision_valid = True + self.process_valid = True + + def setup_kpam(self): + self.planner = KPAMPlanner( + env=self.world, + robot=self.robot, + object=self.art_obj, + cfg_path=self.planner_setting, + controller=self.controller, + draw_points=self.draw, + stage=self.stage, + ) + + def simple_generate_manip_cmds(self): + if self.skill_cfg.get("obj_info_path", None): + self.art_obj.update_articulated_info(self.skill_cfg["obj_info_path"]) + + self.setup_kpam() + traj_keyframes, sample_times = self.planner.get_keypose() + if len(traj_keyframes) == 0 and len(sample_times) == 0: + print("No keyframes found, return empty manip_list") + self.manip_list = [] + return + + T_world_base = get_relative_transform( + get_prim_at_path(self.robot.base_path), get_prim_at_path(self.task.root_prim_path) + ) + self.traj_keyframes = traj_keyframes + self.sample_times = sample_times + manip_list = [] + + p_base_ee_cur, q_base_ee_cur = self.controller.get_ee_pose() + ignore_substring = deepcopy(self.controller.ignore_substring + self.skill_cfg.get("ignore_substring", [])) + cmd = ( + p_base_ee_cur, + q_base_ee_cur, + "update_specific", + {"ignore_substring": ignore_substring, "reference_prim_path": self.controller.reference_prim_path}, + ) + manip_list.append(cmd) + + for i in range(len(self.traj_keyframes)): + p_base_ee_tgt = self.traj_keyframes[i][:3, 3] + q_base_ee_tgt = R.from_matrix(self.traj_keyframes[i][:3, :3]).as_quat(scalar_first=True) + cmd = (p_base_ee_tgt, q_base_ee_tgt, "close_gripper", {}) + manip_list.append(cmd) + + if i == self.contact_pose_index - 1: + p_base_ee = self.traj_keyframes[i][:3, 3] + q_base_ee = R.from_matrix(self.traj_keyframes[i][:3, :3]).as_quat(scalar_first=True) + ignore_substring = deepcopy( + self.controller.ignore_substring + self.skill_cfg.get("ignore_substring", []) + ) + parent_name = self.art_obj.prim_path.split("/")[-2] + ignore_substring.append(parent_name) + cmd = ( + p_base_ee, + q_base_ee, + "update_specific", + {"ignore_substring": ignore_substring, "reference_prim_path": self.controller.reference_prim_path}, + ) + manip_list.append(cmd) + self.manip_list = manip_list + + def update(self): + curr_joint_p = self.art_obj._articulation_view.get_joint_positions()[:, self.art_obj.object_joint_index] + if self.update_art_joint and self.is_success(): + self.art_obj._articulation_view.set_joint_position_targets( + positions=curr_joint_p, joint_indices=self.art_obj.object_joint_index + ) + + def is_success(self): + contact = self.get_contact() + + if self.skill_cfg.get("collision_valid", True): + self.collision_valid = ( + self.collision_valid + and len(contact["forbid_collision"]["forbid_collision_contact_indices"]) == 0 + and len(contact["fingers_base"]["fingers_base_contact_indices"]) == 0 + ) + if self.skill_cfg.get("process_valid", True): + self.process_valid = np.max(np.abs(self.robot.get_joints_state().velocities)) < 5 and ( + np.max(np.abs(self.art_obj.get_linear_velocity())) < 5 + ) + + curr_joint_p = self.art_obj._articulation_view.get_joint_positions()[:, self.art_obj.object_joint_index] + return np.abs(curr_joint_p) <= self.success_threshold and self.collision_valid and self.process_valid +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 + +__init__(self, robot, controller, task, cfg, *args, **kwargs) + +Initialize the close skill and bind it to a specific articulated object. + +Parameters: + +- **robot **( Robot ): Robot instance. +- **controller **( BaseController ): Motion controller. +- **task **( BaseTask ): Task containing scene objects. +- **cfg **( DictConfig ): Skill configuration from YAML. + +Key Operations: + +- Extract articulated object from `cfg["objects"][0] ` +- Load planner settings ( `contact_pose_index `, `success_threshold `) +- Initialize contact views for collision monitoring +- Load articulation info from `obj_info_path `if provided + +setup_kpam(self) + +Initialize the KPAM planner with world, robot, object, and configuration. + +KPAM Planner Components: + +- **env **: Simulation world +- **robot **: Robot instance +- **object **: Articulated object +- **cfg_path **: Planner configuration (constraints, solver options) +- **controller **: Motion controller +- **stage **: USD stage + +simple_generate_manip_cmds(self) + +Generate manipulation commands by solving constraints. + +Steps: + +- **Setup KPAM **: Initialize planner with current world state +- **Get keyframes **: `traj_keyframes, sample_times = self.planner.get_keypose() ` +- **Build manip_list **: +- Update collision settings +- For each keyframe, add `close_gripper `command +- Before contact pose, update collision filters to ignore parent link + +Gripper Behavior: + +- **Close skill **: Gripper remains **closed **throughout trajectory (pushing motion) +- **Open skill **: Gripper is open before contact, closes at contact point, then pulls + +update(self) + +Update articulation joint targets during execution. + +When Enabled: + +If `update_art_joint `is `True `and skill succeeds, the current joint position is written back as the target. This "locks in" the closed state for subsequent skills. + +is_success(self) + +Check if the close operation succeeded. + +Success Conditions: + +- **Collision validity **: No forbidden collisions, no palm contacts +- **Process validity **: Velocities within limits (< 5) +- **Joint position **: `|curr_joint_p| <= success_threshold `(near closed state) + +Returns: + +- bool : `True `if all conditions satisfied + +## KPAM Planner: Constraint-Based Trajectory Generation [​](#kpam-planner-constraint-based-trajectory-generation) + +The `KPAMPlanner `solves geometric constraints defined in the task YAML to generate keyframe trajectories. The solver code is in: + +- `workflows/simbox/solver/planner.py ` +- `workflows/simbox/solver/planner_utils.py ` + +### Keypoint Annotation [​](#keypoint-annotation) + +Before defining constraints, keypoints must be annotated on both the robot gripper and the articulated object. + +#### Robot Gripper Keypoints [​](#robot-gripper-keypoints) + +Defined in robot YAML under `fl_gripper_keypoints `/ `fr_gripper_keypoints `(see [Robots](/InternDataEngine-Docs/concepts/robots/)): + +- **tool_head **( list ): TCP position (fingertip center). +- **tool_tail **( list ): Gripper base position. +- **tool_side **( list ): Side fingertip position. + +![Gripper Keypoints](/InternDataEngine-Docs/gripper_kps.jpg) + +#### Articulated Object Keypoints [​](#articulated-object-keypoints) + +Annotated in the object's articulation info file. See [Assets - Articulated Objects](/InternDataEngine-Docs/custom/assets/#articulated-objects)for details. + +Common keypoints: + +- `articulated_object_head `— One end of the movable part +- `articulated_object_tail `— Other end of the movable part +- `link0_contact_axis `— Axis direction for contact + +### Constraint Types [​](#constraint-types) + +Users define constraints in the task YAML under `planner_setting.constraint_list `. The planner solves these constraints to find valid keyframe poses. + +#### Point-to-Point Constraint [​](#point-to-point-constraint) +yaml +``` +- keypoint_name: tool_head + target_keypoint_name: articulated_object_head + tolerance: 0.0001 + name: fingers_contact_with_link0 +``` +1 +2 +3 +4 + +- keypoint_name: tool_head **Effect **: Enforces that `keypoint_name `(tool_head on gripper) and `target_keypoint_name `(articulated_object_head on object) are within `tolerance `distance. Used to ensure gripper contacts the object. + +#### Keypoints Vector Parallelism [​](#keypoints-vector-parallelism) +yaml +``` +- axis_from_keypoint_name: tool_head + axis_to_keypoint_name: tool_side + cross_target_axis1_from_keypoint_name: articulated_object_head + cross_target_axis1_to_keypoint_name: articulated_object_tail + target_axis: link0_contact_axis + target_axis_frame: object + tolerance: 0.005 + target_inner_product: -1 + type: frame_axis_parallel + name: hand_parallel_to_link0_edge_door +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +**Effect **: Enforces parallelism between two vectors: + +- **Gripper vector **: `axis_from_keypoint_name → axis_to_keypoint_name `(here `tool_head → tool_side `) +- **Object vector **: `cross_target_axis1_from_keypoint_name → cross_target_axis1_to_keypoint_name `(here `articulated_object_head → articulated_object_tail `) + +The `target_inner_product `specifies the desired alignment with `tolerance `: + +- `-1 `: Vectors should be anti-parallel (opposite direction) +- `1 `: Vectors should be parallel (same direction) + +#### Parallelism with Cross Product [​](#parallelism-with-cross-product) +yaml +``` +- axis_from_keypoint_name: tool_head + axis_to_keypoint_name: tool_tail + cross_target_axis1_from_keypoint_name: articulated_object_head + cross_target_axis1_to_keypoint_name: articulated_object_tail + target_axis: link0_contact_axis + target_axis_frame: object + tolerance: 0.005 + target_inner_product: 0.7 + type: frame_axis_parallel + name: fingers_orthogonal_to_link0 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +**Effect **: Enforces parallelism between two vectors: + +- + +**Gripper vector **: `axis_from_keypoint_name → axis_to_keypoint_name `(here `tool_head → tool_tail `, the approach direction) + +- + +**Computed object vector **: Cross product of: + +- `(cross_target_axis1_from_keypoint_name → cross_target_axis1_to_keypoint_name) `(here `articulated_object_head → articulated_object_tail `) +- `target_axis `(here `link0_contact_axis `) + +The `target_inner_product `specifies the desired dot product between these two vectors: + +- `1.0 `: Parallel (same direction) +- `-1.0 `: Anti-parallel (opposite direction) +- `0.0 `: Perpendicular +- `0.7 `: At an angle (approximately 45°) + +In this example, `target_inner_product: 0.7 `enforces that the gripper's approach direction is at an angle to the door surface, which is often needed for stable grasping during manipulation. + +#### Vector Alignment Constraint (simple) [​](#vector-alignment-constraint-simple) +yaml +``` +- axis_from_keypoint_name: tool_head + axis_to_keypoint_name: tool_side + target_axis: object_link0_move_axis + target_axis_frame: object + tolerance: 0.0005 + target_inner_product: -1 + type: frame_axis_parallel + name: hand_parallel_to_link0_edge +``` +1 +2 +3 +4 +5 +6 +7 +8 + +**Effect **: Enforces that the gripper vector `tool_head → tool_side `aligns directly with `target_axis `(here `object_link0_move_axis `), without any cross product calculation. + +This is used when the target axis is already known (e.g., the pulling direction of a drawer) and you want the gripper to align with it. + +### How Constraints Are Solved [​](#how-constraints-are-solved) + +- **Keypoint lookup **: Planner reads all keypoint positions from robot and object +- **Constraint evaluation **: Each constraint computes a cost based on current EE pose +- **Optimization **: Solver minimizes total cost to find valid keyframe poses +- **Trajectory generation **: Valid poses become waypoints in `manip_list ` + +## Configuration Reference [​](#configuration-reference) + +- **objects **( list , default: required): `[articulated_object_name] `. +- **obj_info_path **( string , default: `None `): Path to articulation info YAML. +- **planner_setting.contact_pose_index **( int , default: required): Keyframe index where gripper contacts object. +- **planner_setting.success_threshold **( float , default: required): Joint displacement threshold for success. +- **planner_setting.update_art_joint **( bool , default: `False `): Update articulation joint targets on success. +- **planner_setting.constraint_list **( list , default: required): List of constraint definitions. +- **ignore_substring **( list , default: `[] `): Collision filter substrings. + +## References [​](#references) + +- [GenSim2](https://gensim2.github.io/)— Scaling Robot Data Generation with Multi-modal and Reasoning LLMs. The KPAM planner design is inspired by the keypoint-based manipulation approach in GenSim2. \ No newline at end of file diff --git a/docs_crawled/concepts_skills_overview.md b/docs_crawled/concepts_skills_overview.md new file mode 100644 index 0000000..74d450e --- /dev/null +++ b/docs_crawled/concepts_skills_overview.md @@ -0,0 +1,216 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/skills/overview.html + +# Skills Overview [​](#skills-overview) + +Skills define **atomic manipulation actions **that robots can perform, such as picking, placing, or moving objects. Each skill encapsulates a specific behavior and generates a sequence of manipulation commands for the controller to execute. + +## Architecture [​](#architecture) + +All skills inherit from `BaseSkill `defined in `workflows/simbox/core/skills/base_skill.py `. + +``` +workflows/simbox/core/skills/ +├── base_skill.py # Abstract base class +├── __init__.py # Skill registry +│ +├── Pick-and-Place Skills +│ ├── pick.py # Standard pick operation +│ ├── place.py # Standard place operation +│ ├── dexpick.py # Dex-style pick with constraints +│ ├── dexplace.py # Dex-style place with constraints +│ ├── manualpick.py # Manual grasp pose specification +│ ├── dynamicpick.py # Dynamic grasp selection +│ └── failpick.py # Failure case pick +│ +├── Articulation-Related Skills +│ ├── open.py # Open or pull articulation +│ ├── close.py # Close or push articulation +│ ├── rotate.py # Rotate articulation joint +│ └── artpreplan.py # Articulation pre-planning +│ +├── Heuristic Skills +│ ├── goto_pose.py # Move to target pose +│ ├── gripper_action.py # Generic gripper action (Open, Close) +│ ├── heuristic_skill.py # Configurable heuristic actions +│ ├── joint_ctrl.py # Joint-level control +│ └── wait.py # Wait for duration +│ +└── Task-Specific Skills + ├── move.py # Cartesian motion + ├── track.py # Trajectory tracking + ├── approach_rotate.py # Approach with rotation + ├── flip.py # Flip object + ├── scan.py # Scan motion + └── pour_water_succ.py # Pouring metric +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 + +## Skill Template [​](#skill-template) + +All skills inherit from `BaseSkill `and implement a standard interface: +python +``` +from core.skills.base_skill import BaseSkill, register_skill + +@register_skill +class MySkill(BaseSkill): + """Custom manipulation skill.""" + + def __init__(self, robot, controller, task, cfg, *args, **kwargs): + super().__init__() + self.robot = robot + self.controller = controller + self.task = task + self.skill_cfg = cfg + self.manip_list = [] + + def simple_generate_manip_cmds(self): + """Generate the manipulation command list (REQUIRED).""" + # Build manip_list with (position, quaternion, function, params) tuples + pass + + def is_feasible(self, th=5): + """Check if skill can continue based on planning failures.""" + return self.controller.num_plan_failed <= th + + def is_subtask_done(self, t_eps=1e-3, o_eps=5e-3): + """Check if current waypoint is reached.""" + pass + + def is_done(self): + """Check if all commands are executed.""" + return len(self.manip_list) == 0 + + def is_success(self): + """Check if skill succeeded.""" + pass +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 + +For detailed implementation instructions, see the [Custom Skill Guide](/InternDataEngine-Docs/custom/skill/). + +## Core Concepts [​](#core-concepts) + +### Manipulation Command List [​](#manipulation-command-list) + +Each skill generates a `manip_list `— a sequence of command tuples defining waypoint poses and actions: +python +``` +manip_list = [ + (p_base_ee_tgt, q_base_ee_tgt, function_name, params), + # ... more commands +] +``` +1 +2 +3 +4 + +**Command tuple structure: ** + +- **p_base_ee_tgt **( np.ndarray ): Target end-effector position in arm base frame, shape `(3,) `. +- **q_base_ee_tgt **( np.ndarray ): Target end-effector quaternion `(w, x, y, z) `in arm base frame, shape `(4,) `. +- **function_name **( str ): Action function to execute. +- **params **( dict ): Parameters for the action function. + +**Common action functions: ** + +- `open_gripper `/ `close_gripper `— Gripper control +- `attach_obj `/ `detach_obj `— Physics attachment +- `update_pose_cost_metric `— Planning weight adjustment +- `update_specific `— Collision avoidance settings +- `dummy_forward `— Direct action without planning + +### Execution Flow [​](#execution-flow) + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Skill Execution Pipeline │ +├──────────────────────────────────────────────────────────────┤ +│ 1. Initialize skill with YAML config │ +│ 2. Generate manip_list via simple_generate_manip_cmds() │ +│ 3. Pop next command from manip_list │ +│ 4. Plan motion trajectory with CuRobo controller │ +│ 5. Execute motion and apply action function │ +│ 6. Check is_subtask_done() → if True, pop next command │ +│ 7. Repeat until is_done() returns True │ +│ 8. Evaluate is_success() for final task status │ +└──────────────────────────────────────────────────────────────┘ +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 \ No newline at end of file diff --git a/docs_crawled/concepts_skills_pick.md b/docs_crawled/concepts_skills_pick.md new file mode 100644 index 0000000..38f3455 --- /dev/null +++ b/docs_crawled/concepts_skills_pick.md @@ -0,0 +1,975 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/skills/pick.html + +# Pick Skill [​](#pick-skill) + +The `Pick `skill performs a standard pick operation with grasp pose selection. It loads pre-annotated grasp poses from `.npy `files, filters them based on orientation constraints, and executes the pick motion. + +Code Example: +python +``` +# Source workflows/simbox/core/skills/pick.py +import os +import random +from copy import deepcopy + +import numpy as np +from core.skills.base_skill import BaseSkill, register_skill +from core.utils.constants import CUROBO_BATCH_SIZE +from core.utils.plan_utils import ( + select_index_by_priority_dual, + select_index_by_priority_single, +) +from core.utils.transformation_utils import poses_from_tf_matrices +from omegaconf import DictConfig +from omni.isaac.core.controllers import BaseController +from omni.isaac.core.robots.robot import Robot +from omni.isaac.core.tasks import BaseTask +from omni.isaac.core.utils.prims import get_prim_at_path +from omni.isaac.core.utils.transformations import ( + get_relative_transform, + tf_matrix_from_pose, +) + +@register_skill +class Pick(BaseSkill): + def __init__(self, robot: Robot, controller: BaseController, task: BaseTask, cfg: DictConfig, *args, **kwargs): + super().__init__() + self.robot = robot + self.controller = controller + self.task = task + self.skill_cfg = cfg + object_name = self.skill_cfg["objects"][0] + self.pick_obj = task.objects[object_name] + + # Get grasp annotation + usd_path = [obj["path"] for obj in task.cfg["objects"] if obj["name"] == object_name][0] + usd_path = os.path.join(self.task.asset_root, usd_path) + grasp_pose_path = usd_path.replace( + "Aligned_obj.usd", self.skill_cfg.get("npy_name", "Aligned_grasp_sparse.npy") + ) + sparse_grasp_poses = np.load(grasp_pose_path) + lr_arm = "right" if "right" in self.controller.robot_file else "left" + self.T_obj_ee, self.scores = self.robot.pose_post_process_fn( + sparse_grasp_poses, + lr_arm=lr_arm, + grasp_scale=self.skill_cfg.get("grasp_scale", 1), + tcp_offset=self.skill_cfg.get("tcp_offset", self.robot.tcp_offset), + constraints=self.skill_cfg.get("constraints", None), + ) + + # Keyposes should be generated after previous skill is done + self.manip_list = [] + self.pickcontact_view = task.pickcontact_views[robot.name][lr_arm][object_name] + self.process_valid = True + self.obj_init_trans = deepcopy(self.pick_obj.get_local_pose()[0]) + final_gripper_state = self.skill_cfg.get("final_gripper_state", -1) + if final_gripper_state == 1: + self.gripper_cmd = "open_gripper" + elif final_gripper_state == -1: + self.gripper_cmd = "close_gripper" + else: + raise ValueError(f"final_gripper_state must be 1 or -1, got {final_gripper_state}") + self.fixed_orientation = self.skill_cfg.get("fixed_orientation", None) + if self.fixed_orientation is not None: + self.fixed_orientation = np.array(self.fixed_orientation) + + def simple_generate_manip_cmds(self): + manip_list = [] + + # Update + p_base_ee_cur, q_base_ee_cur = self.controller.get_ee_pose() + cmd = (p_base_ee_cur, q_base_ee_cur, "update_pose_cost_metric", {"hold_vec_weight": None}) + manip_list.append(cmd) + + ignore_substring = deepcopy(self.controller.ignore_substring + self.skill_cfg.get("ignore_substring", [])) + ignore_substring.append(self.pick_obj.name) + cmd = ( + p_base_ee_cur, + q_base_ee_cur, + "update_specific", + {"ignore_substring": ignore_substring, "reference_prim_path": self.controller.reference_prim_path}, + ) + manip_list.append(cmd) + + # Pre grasp + T_base_ee_grasps = self.sample_ee_pose() # (N, 4, 4) + T_base_ee_pregrasps = deepcopy(T_base_ee_grasps) + self.controller.update_specific( + ignore_substring=ignore_substring, reference_prim_path=self.controller.reference_prim_path + ) + + if "r5a" in self.controller.robot_file: + T_base_ee_pregrasps[:, :3, 3] -= T_base_ee_pregrasps[:, :3, 0] * self.skill_cfg.get("pre_grasp_offset", 0.1) + else: + T_base_ee_pregrasps[:, :3, 3] -= T_base_ee_pregrasps[:, :3, 2] * self.skill_cfg.get("pre_grasp_offset", 0.1) + + p_base_ee_pregrasps, q_base_ee_pregrasps = poses_from_tf_matrices(T_base_ee_pregrasps) + p_base_ee_grasps, q_base_ee_grasps = poses_from_tf_matrices(T_base_ee_grasps) + + if self.controller.use_batch: + # Check if the input arrays are exactly the same + if np.array_equal(p_base_ee_pregrasps, p_base_ee_grasps) and np.array_equal( + q_base_ee_pregrasps, q_base_ee_grasps + ): + # Inputs are identical, compute only once to avoid redundant computation + result = self.controller.test_batch_forward(p_base_ee_grasps, q_base_ee_grasps) + index = select_index_by_priority_single(result) + else: + # Inputs are different, compute separately + pre_result = self.controller.test_batch_forward(p_base_ee_pregrasps, q_base_ee_pregrasps) + result = self.controller.test_batch_forward(p_base_ee_grasps, q_base_ee_grasps) + index = select_index_by_priority_dual(pre_result, result) + else: + for index in range(T_base_ee_grasps.shape[0]): + p_base_ee_pregrasp, q_base_ee_pregrasp = p_base_ee_pregrasps[index], q_base_ee_pregrasps[index] + p_base_ee_grasp, q_base_ee_grasp = p_base_ee_grasps[index], q_base_ee_grasps[index] + test_mode = self.skill_cfg.get("test_mode", "forward") + if test_mode == "forward": + result_pre = self.controller.test_single_forward(p_base_ee_pregrasp, q_base_ee_pregrasp) + elif test_mode == "ik": + result_pre = self.controller.test_single_ik(p_base_ee_pregrasp, q_base_ee_pregrasp) + else: + raise NotImplementedError + if self.skill_cfg.get("pre_grasp_offset", 0.1) > 0: + if test_mode == "forward": + result = self.controller.test_single_forward(p_base_ee_grasp, q_base_ee_grasp) + elif test_mode == "ik": + result = self.controller.test_single_ik(p_base_ee_grasp, q_base_ee_grasp) + else: + raise NotImplementedError + if result == 1 and result_pre == 1: + print("pick plan success") + break + else: + if result_pre == 1: + print("pick plan success") + break + + if self.fixed_orientation is not None: + q_base_ee_pregrasps[index] = self.fixed_orientation + q_base_ee_grasps[index] = self.fixed_orientation + + # Pre-grasp + cmd = (p_base_ee_pregrasps[index], q_base_ee_pregrasps[index], "open_gripper", {}) + manip_list.append(cmd) + if self.skill_cfg.get("pre_grasp_hold_vec_weight", None) is not None: + cmd = ( + p_base_ee_pregrasps[index], + q_base_ee_pregrasps[index], + "update_pose_cost_metric", + {"hold_vec_weight": self.skill_cfg.get("pre_grasp_hold_vec_weight", None)}, + ) + manip_list.append(cmd) + + # Grasp + cmd = (p_base_ee_grasps[index], q_base_ee_grasps[index], "open_gripper", {}) + manip_list.append(cmd) + cmd = (p_base_ee_grasps[index], q_base_ee_grasps[index], self.gripper_cmd, {}) + manip_list.extend( + [cmd] * self.skill_cfg.get("gripper_change_steps", 40) + ) # Default we use 40 steps to make sure the gripper is fully closed + ignore_substring = deepcopy(self.controller.ignore_substring + self.skill_cfg.get("ignore_substring", [])) + cmd = ( + p_base_ee_grasps[index], + q_base_ee_grasps[index], + "update_specific", + {"ignore_substring": ignore_substring, "reference_prim_path": self.controller.reference_prim_path}, + ) + manip_list.append(cmd) + cmd = ( + p_base_ee_grasps[index], + q_base_ee_grasps[index], + "attach_obj", + {"obj_prim_path": self.pick_obj.mesh_prim_path}, + ) + manip_list.append(cmd) + + # Post-grasp + post_grasp_offset = np.random.uniform( + self.skill_cfg.get("post_grasp_offset_min", 0.05), self.skill_cfg.get("post_grasp_offset_max", 0.05) + ) + if post_grasp_offset: + p_base_ee_postgrasps = deepcopy(p_base_ee_grasps) + p_base_ee_postgrasps[index][2] += post_grasp_offset + cmd = (p_base_ee_postgrasps[index], q_base_ee_grasps[index], self.gripper_cmd, {}) + manip_list.append(cmd) + + # Whether return to pre-grasp + if self.skill_cfg.get("return_to_pregrasp", False): + cmd = (p_base_ee_pregrasps[index], q_base_ee_pregrasps[index], self.gripper_cmd, {}) + manip_list.append(cmd) + + self.manip_list = manip_list + + def sample_ee_pose(self, max_length=CUROBO_BATCH_SIZE): + T_base_ee = self.get_ee_poses("armbase") + + num_pose = T_base_ee.shape[0] + flags = { + "x": np.ones(num_pose, dtype=bool), + "y": np.ones(num_pose, dtype=bool), + "z": np.ones(num_pose, dtype=bool), + "direction_to_obj": np.ones(num_pose, dtype=bool), + } + filter_conditions = { + "x": { + "forward": (0, 0, 1), # (row, col, direction) + "backward": (0, 0, -1), + "upward": (2, 0, 1), + "downward": (2, 0, -1), + }, + "y": {"forward": (0, 1, 1), "backward": (0, 1, -1), "downward": (2, 1, -1), "upward": (2, 1, 1)}, + "z": {"forward": (0, 2, 1), "backward": (0, 2, -1), "downward": (2, 2, -1), "upward": (2, 2, 1)}, + } + for axis in ["x", "y", "z"]: + filter_list = self.skill_cfg.get(f"filter_{axis}_dir", None) + if filter_list is not None: + # direction, value = filter_list + direction = filter_list[0] + row, col, sign = filter_conditions[axis][direction] + if len(filter_list) == 2: + value = filter_list[1] + cos_val = np.cos(np.deg2rad(value)) + flags[axis] = T_base_ee[:, row, col] >= cos_val if sign > 0 else T_base_ee[:, row, col] <= cos_val + elif len(filter_list) == 3: + value1, value2 = filter_list[1:] + cos_val1 = np.cos(np.deg2rad(value1)) + cos_val2 = np.cos(np.deg2rad(value2)) + if sign > 0: + flags[axis] = np.logical_and( + T_base_ee[:, row, col] >= cos_val1, T_base_ee[:, row, col] <= cos_val2 + ) + else: + flags[axis] = np.logical_and( + T_base_ee[:, row, col] <= cos_val1, T_base_ee[:, row, col] >= cos_val2 + ) + if self.skill_cfg.get("direction_to_obj", None) is not None: + direction_to_obj = self.skill_cfg["direction_to_obj"] + T_world_obj = tf_matrix_from_pose(*self.pick_obj.get_local_pose()) + T_base_world = get_relative_transform( + get_prim_at_path(self.task.root_prim_path), get_prim_at_path(self.controller.reference_prim_path) + ) + T_base_obj = T_base_world @ T_world_obj + if direction_to_obj == "right": + flags["direction_to_obj"] = T_base_ee[:, 1, 3] <= T_base_obj[1, 3] + elif direction_to_obj == "left": + flags["direction_to_obj"] = T_base_ee[:, 1, 3] > T_base_obj[1, 3] + else: + raise NotImplementedError + + combined_flag = np.logical_and.reduce(list(flags.values())) + if sum(combined_flag) == 0: + # idx_list = [i for i in range(max_length)] + idx_list = list(range(max_length)) + else: + tmp_scores = self.scores[combined_flag] + tmp_idxs = np.arange(num_pose)[combined_flag] + combined = list(zip(tmp_scores, tmp_idxs)) + combined.sort() + idx_list = [idx for (score, idx) in combined[:max_length]] + score_list = self.scores[idx_list] + weights = 1.0 / (score_list + 1e-8) + weights = weights / weights.sum() + + sampled_idx = random.choices(idx_list, weights=weights, k=max_length) + sampled_scores = self.scores[sampled_idx] + + # Sort indices by their scores (ascending) + sorted_pairs = sorted(zip(sampled_scores, sampled_idx)) + idx_list = [idx for _, idx in sorted_pairs] + + print(self.scores[idx_list]) + # print((T_base_ee[idx_list])[:, 0, 1]) + return T_base_ee[idx_list] + + def get_ee_poses(self, frame: str = "world"): + # get grasp poses at specific frame + if frame not in ["world", "body", "armbase"]: + raise ValueError( + f"poses in {frame} frame is not supported: accepted values are [world, body, armbase] only" + ) + + if frame == "body": + return self.T_obj_ee + + T_world_obj = tf_matrix_from_pose(*self.pick_obj.get_local_pose()) + T_world_ee = T_world_obj[None] @ self.T_obj_ee + + if frame == "world": + return T_world_ee + + if frame == "armbase": # arm base frame + T_world_base = get_relative_transform( + get_prim_at_path(self.controller.reference_prim_path), get_prim_at_path(self.task.root_prim_path) + ) + T_base_world = np.linalg.inv(T_world_base) + T_base_ee = T_base_world[None] @ T_world_ee + return T_base_ee + + def get_contact(self, contact_threshold=0.0): + contact = np.abs(self.pickcontact_view.get_contact_force_matrix()).squeeze() + contact = np.sum(contact, axis=-1) + indices = np.where(contact > contact_threshold)[0] + return contact, indices + + def is_feasible(self, th=5): + return self.controller.num_plan_failed <= th + + def is_subtask_done(self, t_eps=1e-3, o_eps=5e-3): + assert len(self.manip_list) != 0 + p_base_ee_cur, q_base_ee_cur = self.controller.get_ee_pose() + p_base_ee, q_base_ee, *_ = self.manip_list[0] + diff_trans = np.linalg.norm(p_base_ee_cur - p_base_ee) + diff_ori = 2 * np.arccos(min(abs(np.dot(q_base_ee_cur, q_base_ee)), 1.0)) + pose_flag = np.logical_and( + diff_trans < t_eps, + diff_ori < o_eps, + ) + self.plan_flag = self.controller.num_last_cmd > 10 + return np.logical_or(pose_flag, self.plan_flag) + + def is_done(self): + if len(self.manip_list) == 0: + return True + if self.is_subtask_done(t_eps=self.skill_cfg.get("t_eps", 1e-3), o_eps=self.skill_cfg.get("o_eps", 5e-3)): + self.manip_list.pop(0) + return len(self.manip_list) == 0 + + def is_success(self): + flag = True + + _, indices = self.get_contact() + if self.gripper_cmd == "close_gripper": + flag = len(indices) >= 1 + + if self.skill_cfg.get("process_valid", True): + self.process_valid = np.max(np.abs(self.robot.get_joints_state().velocities)) < 5 and ( + np.max(np.abs(self.pick_obj.get_linear_velocity())) < 5 + ) + flag = flag and self.process_valid + + if self.skill_cfg.get("lift_th", 0.0) > 0.0: + p_world_obj = deepcopy(self.pick_obj.get_local_pose()[0]) + flag = flag and ((p_world_obj[2] - self.obj_init_trans[2]) > self.skill_cfg.get("lift_th", 0.0)) + + return flag +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 + +__init__(self, robot, controller, task, cfg, *args, **kwargs) + +Initialize the pick skill and load grasp annotations. + +Parameters: + +- **robot **( Robot ): Robot instance for state queries and actions. +- **controller **( BaseController ): Controller for motion planning. +- **task **( BaseTask ): Task instance containing scene objects. +- **cfg **( DictConfig ): Skill configuration from task YAML. + +Key Operations: + +- Extract target object name from `cfg["objects"][0] ` +- Load sparse grasp poses from `Aligned_grasp_sparse.npy ` +- Transform grasp poses to EE frame via `robot.pose_post_process_fn() ` +- Initialize `manip_list `for command sequence + +simple_generate_manip_cmds(self) + +Generate the full pick motion sequence. This is the core method that defines the pick behavior. + +Steps: + +- **Update planning settings **— Reset cost metrics and collision settings +- **Sample EE poses **— Call `sample_ee_pose() `to filter valid grasp candidates +- **Generate pre-grasp poses **— Offset grasp poses along approach direction +- **Test motion feasibility **— Use CuRobo to check which candidates are reachable +- **Build manip_list **— Construct command sequence: +- Move to pre-grasp pose with open gripper +- Move to grasp pose +- Close gripper +- Attach object to gripper (physics) +- Lift object (post-grasp offset) + +sample_ee_pose(self, max_length=CUROBO_BATCH_SIZE) + +Filter grasp poses based on end-effector orientation constraints. + +Parameters: + +- **max_length **( int ): Maximum number of poses to return. + +Returns: + +- np.ndarray : Filtered grasp poses as transformation matrices `(N, 4, 4) `. + +Filtering Logic: + +- Transform all candidate grasp poses to arm base frame +- Apply `filter_x_dir `, `filter_y_dir `, `filter_z_dir `constraints +- Sort remaining poses by grasp quality score +- Sample top candidates weighted by inverse score + +is_success(self) + +Check if the pick operation succeeded. + +Success Conditions: + +- **Contact check **: Gripper is in contact with at least one object (when closing gripper) +- **Motion validity **: Joint velocities < 5 rad/s, object velocity < 5 m/s +- **Lift check **(optional): Object lifted above initial height by `lift_th `threshold + +Returns: + +- bool : `True `if all conditions are satisfied. + +## Grasp Orientation Filtering [​](#grasp-orientation-filtering) + +The pick skill uses a **direction-based filtering strategy **to select valid grasp poses. Instead of constructing specific poses, we filter pre-annotated grasp candidates based on the desired end-effector orientation. + +### Coordinate System [​](#coordinate-system) + +All arm base frames follow this convention: + +- **X-axis **: Forward (toward the table/workspace) +- **Y-axis **: Right (when facing the table) +- **Z-axis **: Upward + +**Arm Base Frame Examples: ** + +| Franka | ARX Lift-2 | Agilex Split Aloha | +| ![Franka Arm Base](/InternDataEngine-Docs/arm_base/franka.jpg) | ![Lift2 Arm Base](/InternDataEngine-Docs/arm_base/lift2.jpg) | ![Split-ALOHA Arm Base](/InternDataEngine-Docs/arm_base/split_aloha.jpg) | + +The end-effector frame has its own local X, Y, Z axes. The filter constraints control how these EE axes align with the arm base frame. + +### Filter Parameters [​](#filter-parameters) + +- **filter_x_dir **( list ): Filter based on EE's X-axis direction in arm base frame. +- **filter_y_dir **( list ): Filter based on EE's Y-axis direction in arm base frame. +- **filter_z_dir **( list ): Filter based on EE's Z-axis direction in arm base frame. + +**Format **: `[direction, angle] `or `[direction, angle_min, angle_max] ` + +### Direction Mapping [​](#direction-mapping) + +- **forward **: EE axis dot arm_base_X ≥ cos(angle) +- **backward **: EE axis dot arm_base_X ≤ cos(angle) +- **upward **: EE axis dot arm_base_Z ≥ cos(angle) +- **downward **: EE axis dot arm_base_Z ≤ cos(angle) + +**Positive sign **: Use `≥ cos(angle) `when direction is positive (forward/upward) + +**Negative sign **: Use `≤ cos(angle) `when direction is negative (backward/downward) + +## Examples [​](#examples) + +### Example 1: Franka Research 3 [​](#example-1-franka-research-3) + +Config Example: +yaml +``` +# Source: workflows/simbox/core/configs/tasks/pick_and_place/franka/single_pick/omniobject3d-banana.yaml +skills: + - franka: + - left: + - name: pick + objects: [pick_object_left] + filter_x_dir: ["forward", 90] + filter_z_dir: ["downward", 140] +``` +1 +2 +3 +4 +5 +6 +7 +8 + +Figure Example: ![Franka Pick Visualization](/InternDataEngine-Docs/pick/franka_pick_vis.jpg) + +**Analysis **: + +For Franka, the gripper's approach direction (toward fingers) is the **Z-axis **of the end-effector frame. + +- + +**`filter_z_dir: ["downward", 140] `**: We want the gripper to approach **vertically downward **. The EE's Z-axis should form an angle ≥ 140° with the arm base's Z-axis (upward). Since 140° > 90°, the EE's Z-axis points downward. + +- + +**`filter_x_dir: ["forward", 90] `**: We want the gripper to face **forward **(no reverse grasping). The EE's X-axis should form an angle ≤ 90° with the arm base's X-axis (forward), ensuring the gripper doesn't rotate backward. + +Result: Gripper approaches from above with fingers pointing down, facing forward. + +### Example 2: Agilex Split Aloha with Piper-100 arm [​](#example-2-agilex-split-aloha-with-piper-100-arm) + +Config Example: +yaml +``` +# Source: workflows/simbox/core/configs/tasks/pick_and_place/split_aloha/single_pick/left/omniobject3d-banana.yaml +skills: + - split_aloha: + - left: + - name: pick + objects: [pick_object_left] + filter_y_dir: ["forward", 90] + filter_z_dir: ["downward", 140] +``` +1 +2 +3 +4 +5 +6 +7 +8 + +Figure Example: ![Piper Pick Visualization](/InternDataEngine-Docs/pick/piper_pick_vis.jpg) + +**Analysis **: + +For Agilex Split Aloha's left arm, the gripper approach direction is still the **Z-axis **, but the forward-facing direction is the **Y-axis **. + +- + +**`filter_z_dir: ["downward", 140] `**: Same as Franka — gripper approaches vertically **downward **. + +- + +**`filter_y_dir: ["forward", 90] `**: The EE's Y-axis should form an angle ≤ 90° with the arm base's X-axis (forward). This ensures the gripper faces **forward **. + +Result: Same grasp orientation as Franka, but using Y-axis for forward direction control. + +### Example 3: ARX Lift-2 with R5a arm [​](#example-3-arx-lift-2-with-r5a-arm) + +Config Example: +yaml +``` +# Source: workflows/simbox/core/configs/tasks/pick_and_place/lift2/single_pick/left/omniobject3d-banana.yaml +skills: + - lift2: + - left: + - name: pick + objects: [pick_object_left] + filter_z_dir: ["forward", 90] + filter_x_dir: ["downward", 140] +``` +1 +2 +3 +4 +5 +6 +7 +8 + +Figure Example: ![R5A Pick Visualization](/InternDataEngine-Docs/pick/r5a_pick_vis.jpg) + +**Analysis **: + +For Lift2 with R5A gripper, the approach direction (toward fingers) is the **X-axis **of the end-effector frame. + +- + +**`filter_x_dir: ["downward", 140] `**: The EE's X-axis (approach direction) should form an angle ≥ 140° with the arm base's Z-axis, meaning the gripper approaches **downward **. + +- + +**`filter_z_dir: ["forward", 90] `**: The EE's Z-axis (gripper facing direction) should form an angle ≤ 90° with the arm base's X-axis (forward), ensuring the gripper faces **forward **. + +Result: Gripper approaches from above, facing forward — same physical outcome as Franka, but using different axes. + +## Design Philosophy [​](#design-philosophy) + +Note + +**Filtering vs. Construction **: We use a filtering strategy rather than constructing specific grasp poses. This approach: + +- + +**Leverages existing annotations **: Pre-computed grasp poses from `Aligned_grasp_sparse.npy `already contain valid grasp configurations. + +- + +**Aligns with human intuition **: Specifying "gripper should approach downward and face forward" is more intuitive than computing exact rotation matrices. + +- + +**Provides flexibility **: Different robots with different EE frame conventions can achieve the same physical grasp by filtering different axes. + +- + +**Maintains diversity **: Multiple valid grasp poses remain after filtering, allowing the planner to select based on reachability and collision constraints. + +## Configuration Reference [​](#configuration-reference) + +- **objects **( list , default: required): Target object names. +- **npy_name **( string , default: `"Aligned_grasp_sparse.npy" `): Grasp annotation file name. +- **grasp_scale **( float , default: `1 `): Scale factor for grasp poses. +- **tcp_offset **( float , default: `robot.tcp_offset `): TCP offset override. +- **constraints **( dict , default: `None `): Additional grasp constraints. +- **final_gripper_state **( int , default: `-1 `): Gripper state after pick: `1 `(open) or `-1 `(close). +- **fixed_orientation **( list , default: `None `): Fixed quaternion `[w, x, y, z] `if specified. +- **filter_x_dir **( list , default: `None `): EE X-axis filter: `[direction, angle] `. +- **filter_y_dir **( list , default: `None `): EE Y-axis filter: `[direction, angle] `. +- **filter_z_dir **( list , default: `None `): EE Z-axis filter: `[direction, angle] `. +- **direction_to_obj **( string , default: `None `): Filter by object position: `"left" `or `"right" `. +- **pre_grasp_offset **( float , default: `0.1 `): Distance to offset before grasp (meters). +- **pre_grasp_hold_vec_weight **( list , default: `None `): Hold vector weight at pre-grasp. +- **gripper_change_steps **( int , default: `40 `): Steps to close gripper. +- **post_grasp_offset_min **( float , default: `0.05 `): Minimum lift distance (meters). +- **post_grasp_offset_max **( float , default: `0.05 `): Maximum lift distance (meters). +- **return_to_pregrasp **( bool , default: `False `): Return to pre-grasp pose after lift. +- **lift_th **( float , default: `0.0 `): Lift threshold for success check (meters). +- **ignore_substring **( list , default: `[] `): Collision filter substrings. +- **test_mode **( string , default: `"forward" `): Motion test mode: `"forward" `or `"ik" `. +- **t_eps **( float , default: `1e-3 `): Translation tolerance (meters). +- **o_eps **( float , default: `5e-3 `): Orientation tolerance (radians). +- **process_valid **( bool , default: `True `): Check motion validity for success. \ No newline at end of file diff --git a/docs_crawled/concepts_skills_place.md b/docs_crawled/concepts_skills_place.md new file mode 100644 index 0000000..9078601 --- /dev/null +++ b/docs_crawled/concepts_skills_place.md @@ -0,0 +1,711 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/skills/place.html + +# Place Skill [​](#place-skill) + +The `Place `skill performs placement operations with constrained end-effector orientations. It generates valid place poses through random sampling and filtering, then executes the placement motion. + +Code Example: +python +``` +# Source workflows/simbox/core/skills/place.py +from copy import deepcopy + +import numpy as np +from core.skills.base_skill import BaseSkill, register_skill +from core.utils.box import Box, get_bbox_center_and_corners +from core.utils.constants import CUROBO_BATCH_SIZE +from core.utils.iou import IoU +from core.utils.plan_utils import ( + select_index_by_priority_dual, + select_index_by_priority_single, +) +from core.utils.transformation_utils import create_pose_matrices, poses_from_tf_matrices +from core.utils.usd_geom_utils import compute_bbox +from omegaconf import DictConfig +from omni.isaac.core.controllers import BaseController +from omni.isaac.core.robots.robot import Robot +from omni.isaac.core.tasks import BaseTask +from omni.isaac.core.utils.prims import get_prim_at_path +from omni.isaac.core.utils.transformations import ( + get_relative_transform, + pose_from_tf_matrix, + tf_matrix_from_pose, +) +from scipy.spatial.transform import Rotation as R + +@register_skill +class Place(BaseSkill): + def __init__(self, robot: Robot, controller: BaseController, task: BaseTask, cfg: DictConfig, *args, **kwargs): + super().__init__() + self.robot = robot + self.controller = controller + self.task = task + + self.name = cfg["name"] + self.pick_obj = task._task_objects[cfg["objects"][0]] + self.place_obj = task._task_objects[cfg["objects"][1]] + self.place_align_axis = cfg.get("place_align_axis", None) + self.pick_align_axis = cfg.get("pick_align_axis", None) + self.constraint_gripper_x = cfg.get("constraint_gripper_x", False) + self.place_part_prim_path = cfg.get("place_part_prim_path", None) + if self.place_part_prim_path: + self.place_prim_path = f"{self.place_obj.prim_path}/{self.place_part_prim_path}" + else: + self.place_prim_path = self.place_obj.prim_path + self.manip_list = [] + self.robot_ee_path = self.controller.robot_ee_path + self.robot_base_path = self.controller.robot_base_path + + self.skill_cfg = cfg + self.align_pick_obj_axis = self.skill_cfg.get("align_pick_obj_axis", None) + self.align_place_obj_axis = self.skill_cfg.get("align_place_obj_axis", None) + self.align_plane_x_axis = self.skill_cfg.get("align_plane_x_axis", None) + self.align_plane_y_axis = self.skill_cfg.get("align_plane_y_axis", None) + self.align_obj_tol = self.skill_cfg.get("align_obj_tol", None) + + def simple_generate_manip_cmds(self): + manip_list = [] + + p_base_ee_cur, q_base_ee_cur = self.controller.get_ee_pose() + cmd = (p_base_ee_cur, q_base_ee_cur, "update_pose_cost_metric", {"hold_vec_weight": None}) + manip_list.append(cmd) + + if self.skill_cfg.get("ignore_substring", []): + ignore_substring = deepcopy(self.controller.ignore_substring + self.skill_cfg.get("ignore_substring", [])) + cmd = ( + p_base_ee_cur, + q_base_ee_cur, + "update_specific", + {"ignore_substring": ignore_substring, "reference_prim_path": self.controller.reference_prim_path}, + ) + manip_list.append(cmd) + + result = self.sample_gripper_place_traj() + + cmd = (result[0][0], result[0][1], "close_gripper", {}) + manip_list.append(cmd) + + p_base_ee_place, q_base_ee_place = result[1][0], result[1][1] + cmd = (p_base_ee_place, q_base_ee_place, "close_gripper", {}) + manip_list.append(cmd) + + cmd = (p_base_ee_place, q_base_ee_place, "open_gripper", {}) + manip_list.extend([cmd] * self.skill_cfg.get("gripper_change_steps", 10)) + + cmd = (p_base_ee_place, q_base_ee_place, "detach_obj", {}) + manip_list.append(cmd) + + self.manip_list = manip_list + self.place_ee_trans = p_base_ee_place + + def sample_gripper_place_traj(self): + # ... sampling logic ... + pass + + def generate_constrained_rotation_batch(self, batch_size=3000): + filter_conditions = { + "x": { + "forward": (0, 0, 1), + "backward": (0, 0, -1), + "leftward": (1, 0, 1), + "rightward": (1, 0, -1), + "upward": (2, 0, 1), + "downward": (2, 0, -1), + }, + "y": { + "forward": (0, 1, 1), + "backward": (0, 1, -1), + "leftward": (1, 1, 1), + "rightward": (1, 1, -1), + "upward": (2, 1, 1), + "downward": (2, 1, -1), + }, + "z": { + "forward": (0, 2, 1), + "backward": (0, 2, -1), + "leftward": (1, 2, 1), + "rightward": (1, 2, -1), + "upward": (2, 2, 1), + "downward": (2, 2, -1), + }, + } + rot_mats = R.random(batch_size).as_matrix() + valid_mask = np.ones(batch_size, dtype=bool) + + for axis in ["x", "y", "z"]: + filter_list = self.skill_cfg.get(f"filter_{axis}_dir", None) + if filter_list is not None: + direction = filter_list[0] + row, col, sign = filter_conditions[axis][direction] + elements = rot_mats[:, row, col] + if len(filter_list) == 2: + value = filter_list[1] + cos_val = np.cos(np.deg2rad(value)) + if sign > 0: + valid_mask &= elements >= cos_val + else: + valid_mask &= elements <= cos_val + + valid_rot_mats = rot_mats[valid_mask] + if len(valid_rot_mats) == 0: + return rot_mats[:CUROBO_BATCH_SIZE] + else: + indices = np.random.choice(len(valid_rot_mats), CUROBO_BATCH_SIZE) + return valid_rot_mats[indices] + + def is_feasible(self, th=5): + return self.controller.num_plan_failed <= th + + def is_subtask_done(self, t_eps=1e-3, o_eps=5e-3): + assert len(self.manip_list) != 0 + p_base_ee_cur, q_base_ee_cur = self.controller.get_ee_pose() + p_base_ee, q_base_ee, *_ = self.manip_list[0] + diff_trans = np.linalg.norm(p_base_ee_cur - p_base_ee) + diff_ori = 2 * np.arccos(min(abs(np.dot(q_base_ee_cur, q_base_ee)), 1.0)) + pose_flag = np.logical_and(diff_trans < t_eps, diff_ori < o_eps) + self.plan_flag = self.controller.num_last_cmd > 10 + return np.logical_or(pose_flag, self.plan_flag) + + def is_done(self): + if len(self.manip_list) == 0: + return True + if self.is_subtask_done(t_eps=self.skill_cfg.get("t_eps", 1e-3), o_eps=self.skill_cfg.get("o_eps", 5e-3)): + self.manip_list.pop(0) + return len(self.manip_list) == 0 + + def is_success(self, th=0.0): + if self.skill_cfg.get("success_mode", "3diou") == "3diou": + bbox_pick_obj = compute_bbox(self.pick_obj.prim) + bbox_place_obj = compute_bbox(get_prim_at_path(self.place_prim_path)) + iou = IoU( + Box(get_bbox_center_and_corners(bbox_pick_obj)), Box(get_bbox_center_and_corners(bbox_place_obj)) + ).iou() + return iou > th + elif self.skill_cfg.get("success_mode", "3diou") == "xybbox": + bbox_place_obj = compute_bbox(get_prim_at_path(self.place_prim_path)) + pick_x, pick_y = self.pick_obj.get_local_pose()[0][:2] + place_xy_min = bbox_place_obj.min[:2] + place_xy_max = bbox_place_obj.max[:2] + return ((place_xy_min[0] + 0.015) < pick_x < (place_xy_max[0] - 0.015)) and ( + (place_xy_min[1] + 0.015) < pick_y < (place_xy_max[1] - 0.015) + ) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 + +__init__(self, robot, controller, task, cfg, *args, **kwargs) + +Initialize the place skill with target objects and constraints. + +Parameters: + +- **robot **( Robot ): Robot instance for state queries and actions. +- **controller **( BaseController ): Controller for motion planning. +- **task **( BaseTask ): Task instance containing scene objects. +- **cfg **( DictConfig ): Skill configuration from task YAML. + +Key Operations: + +- Extract pick object from `cfg["objects"][0] ` +- Extract place container from `cfg["objects"][1] ` +- Initialize alignment constraints ( `align_pick_obj_axis `, `align_place_obj_axis `) +- Set up collision filtering paths + +simple_generate_manip_cmds(self) + +Generate the full placement motion sequence. + +Steps: + +- **Update planning settings **— Reset cost metrics and collision settings +- **Generate place trajectory **— Call `sample_gripper_place_traj() `to get pre-place and place poses +- **Build manip_list **— Construct command sequence: +- Move to pre-place pose (gripper closed) +- Move to place pose +- Open gripper to release object +- Detach object from gripper (physics) +- Retreat motion (if `post_place_vector `configured) + +sample_gripper_place_traj(self) + +Generate pre-place and place poses based on placement direction mode. + +Returns: + +- list : `[T_base_ee_preplace, T_base_ee_place] `or `[T_base_ee_preplace, T_base_ee_place, T_base_ee_postplace] ` + +Position Constraint Modes: + +- **`"gripper" `**: Target pose controls gripper position directly +- **`"object" `**: Target pose controls pick object position (accounts for object offset from EE) + +Direction Modes: + +See [Placement Direction Modes](#placement-direction-modes)section below. + +generate_constrained_rotation_batch(self, batch_size=3000) + +Generate valid end-effector orientations through random sampling and filtering. + +Parameters: + +- **batch_size **( int ): Number of random rotations to sample. + +Returns: + +- np.ndarray : Filtered rotation matrices `(N, 3, 3) `. + +Filtering Logic: + +- Sample random rotation matrices using `scipy.spatial.transform.Rotation ` +- Apply direction filters ( `filter_x_dir `, `filter_y_dir `, `filter_z_dir `) +- Apply object alignment constraints (if configured) +- Return filtered rotations + +For detailed filtering explanation, see [Orientation Filtering](#orientation-filtering). + +is_success(self, th=0.0) + +Check if the placement succeeded based on configured mode. + +Parameters: + +- **th **( float ): Threshold for IoU-based success. + +Returns: + +- bool : `True `if success conditions are satisfied. + +Success Modes: + +| Mode | Condition | +| `3diou ` | 3D IoU between pick object and container > threshold | +| `xybbox ` | Pick object center within container XY bounds | +| `height ` | Pick object below height threshold | +| `left `/ `right ` | Object positioned left/right of container | +| `flower ` | IoU + object center within container bounds | +| `cup ` | IoU + object above container base | + +## Placement Direction Modes [​](#placement-direction-modes) + +### Vertical Placement [​](#vertical-placement) + +Default mode for placing objects on top of containers (e.g., placing items in boxes, on plates). +yaml +``` +place_direction: "vertical" +``` +1 + +**Algorithm **: + +- Sample (x, y) position within container top surface using ratio ranges +- Set z-height to `b_max[2] + pre_place_z_offset `(above container) +- Lower to `b_max[2] + place_z_offset `(final placement height) +yaml +``` +x_ratio_range: [0.4, 0.6] # X position ratio within container bbox +y_ratio_range: [0.4, 0.6] # Y position ratio within container bbox +pre_place_z_offset: 0.2 # Height above container for approach +place_z_offset: 0.1 # Final placement height offset +``` +1 +2 +3 +4 + +### Horizontal Placement [​](#horizontal-placement) + +For inserting objects into slots, shelves, or openings from the side. +yaml +``` +place_direction: "horizontal" +align_place_obj_axis: [1, 0, 0] # Insertion direction +offset_place_obj_axis: [0, 0, 1] # Offset direction +``` +1 +2 +3 + +**Algorithm **: + +- Sample position within container bounding box +- Compute approach position offset along alignment axis +- Move forward along alignment axis to place position +yaml +``` +# Position constraint: "object" or "gripper" +position_constraint: "object" + +# Approach distances +pre_place_align: 0.2 # Pre-place offset along alignment axis +pre_place_offset: 0.2 # Pre-place offset along offset axis +place_align: 0.1 # Place offset along alignment axis +place_offset: 0.1 # Place offset along offset axis +``` +1 +2 +3 +4 +5 +6 +7 +8 + +## Orientation Filtering [​](#orientation-filtering) + +The place skill uses the same **direction-based filtering strategy **as the pick skill. See [Pick Skill - Grasp Orientation Filtering](/InternDataEngine-Docs/concepts/skills/pick/#grasp-orientation-filtering)for detailed explanation. + +### Filter Parameters [​](#filter-parameters) + +- **filter_x_dir **( list ): Filter based on EE's X-axis direction in arm base frame. +- **filter_y_dir **( list ): Filter based on EE's Y-axis direction in arm base frame. +- **filter_z_dir **( list ): Filter based on EE's Z-axis direction in arm base frame. + +**Format **: `[direction, angle] `or `[direction, angle_min, angle_max] ` + +### Direction Mapping [​](#direction-mapping) + +- **forward **: EE axis dot arm_base_X ≥ cos(angle) +- **backward **: EE axis dot arm_base_X ≤ cos(angle) +- **leftward **: EE axis dot arm_base_Y ≥ cos(angle) +- **rightward **: EE axis dot arm_base_Y ≤ cos(angle) +- **upward **: EE axis dot arm_base_Z ≥ cos(angle) +- **downward **: EE axis dot arm_base_Z ≤ cos(angle) + +### Object Alignment Constraint [​](#object-alignment-constraint) + +Additional constraint to align pick object axis with place container axis: +yaml +``` +align_pick_obj_axis: [0, 0, 1] # Axis on pick object (e.g., height axis) +align_place_obj_axis: [0, 0, 1] # Target axis on place container +align_obj_tol: 15 # Alignment tolerance (degrees) +``` +1 +2 +3 + +This ensures that a specific axis on the held object (e.g., bottle height) aligns with a target axis on the container (e.g., cup opening direction). + +## Design Philosophy [​](#design-philosophy) + +Note + +**Random Generation + Filter **: The place skill uses a random generation and filtering strategy rather than delicate construction. This approach is chosen for three key reasons: + +- + +**Intuitive Position Control **: Specifying place position based on target EE activity direction and object bounding box range is intuitive and easy to configure. + +- + +**Simple Rotation Sampling **: Randomly generating 3x3 rotation matrices and filtering them is computationally simple. With sufficient samples, valid orientations that pass planning constraints are always found. + +- + +**Container Diversity **: Different place containers have varying volumes, shapes, and opening directions. Delicate construction of place poses for each container type is difficult. The filtering approach is general and adaptable. + +## Configuration Reference [​](#configuration-reference) + +- **objects **( list , default: required): `[pick_object, place_container] `. +- **place_part_prim_path **( string , default: `None `): Sub-path within place container prim. +- **place_direction **( string , default: `"vertical" `): `"vertical" `or `"horizontal" `. +- **position_constraint **( string , default: `"gripper" `): `"gripper" `or `"object" `. +- **pre_place_z_offset **( float , default: `0.2 `): Approach height above container. +- **place_z_offset **( float , default: `0.1 `): Final placement height. +- **x_ratio_range **( [float, float] , default: `[0.4, 0.6] `): X-position sampling range. +- **y_ratio_range **( [float, float] , default: `[0.4, 0.6] `): Y-position sampling range. +- **z_ratio_range **( [float, float] , default: `[0.4, 0.6] `): Z-position sampling range (horizontal). +- **align_place_obj_axis **( [float, float, float] , default: `None `): Insertion axis on place container (horizontal). +- **offset_place_obj_axis **( [float, float, float] , default: `None `): Offset axis on place container (horizontal). +- **pre_place_align **( float , default: `0.2 `): Pre-place offset along alignment axis. +- **pre_place_offset **( float , default: `0.2 `): Pre-place offset along offset axis. +- **place_align **( float , default: `0.1 `): Place offset along alignment axis. +- **place_offset **( float , default: `0.1 `): Place offset along offset axis. +- **filter_x_dir **( list , default: `None `): EE X-axis direction filter. +- **filter_y_dir **( list , default: `None `): EE Y-axis direction filter. +- **filter_z_dir **( list , default: `None `): EE Z-axis direction filter. +- **align_pick_obj_axis **( [float, float, float] , default: `None `): Axis on pick object to align. +- **align_place_obj_axis **( [float, float, float] , default: `None `): Target axis on place container. +- **align_obj_tol **( float , default: `None `): Alignment tolerance (degrees). +- **align_plane_x_axis **( [float, float, float] , default: `None `): X-axis alignment plane. +- **align_plane_y_axis **( [float, float, float] , default: `None `): Y-axis alignment plane. +- **pre_place_hold_vec_weight **( list , default: `None `): Hold vector weight at pre-place. +- **post_place_hold_vec_weight **( list , default: `None `): Hold vector weight at place. +- **gripper_change_steps **( int , default: `10 `): Steps for gripper open action. +- **hesitate_steps **( int , default: `0 `): Pause steps before release. +- **post_place_vector **( [float, float, float] , default: `None `): Retreat direction after placement. +- **ignore_substring **( list , default: `[] `): Collision filter substrings. +- **test_mode **( string , default: `"forward" `): Motion test mode: `"forward" `or `"ik" `. +- **t_eps **( float , default: `1e-3 `): Translation tolerance (meters). +- **o_eps **( float , default: `5e-3 `): Orientation tolerance (radians). +- **success_mode **( string , default: `"3diou" `): Success evaluation mode. +- **success_th **( float , default: `0.0 `): IoU threshold for success. +- **threshold **( float , default: `0.03 `): Distance threshold for left/right modes. + +## Example Configuration [​](#example-configuration) + +### Vertical Place on Container [​](#vertical-place-on-container) +yaml +``` +skills: + - lift2: + - left: + - name: place + objects: [pick_object, container] + place_direction: "vertical" + x_ratio_range: [0.4, 0.6] + y_ratio_range: [0.4, 0.6] + pre_place_z_offset: 0.15 + place_z_offset: 0.05 + filter_z_dir: ["downward", 140] + gripper_change_steps: 10 + success_mode: "xybbox" +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +### Horizontal Insertion [​](#horizontal-insertion) +yaml +``` +skills: + - franka: + - left: + - name: place + objects: [peg, hole] + place_direction: "horizontal" + position_constraint: "object" + align_place_obj_axis: [1, 0, 0] + offset_place_obj_axis: [0, 0, 1] + pre_place_align: 0.15 + place_align: 0.02 + filter_x_dir: ["forward", 30] + success_mode: "3diou" +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +### Place with Retreat [​](#place-with-retreat) +yaml +``` +skills: + - split_aloha: + - left: + - name: place + objects: [item, shelf] + place_direction: "vertical" + post_place_vector: [-0.05, 0.0, 0.1] + gripper_change_steps: 15 +``` +1 +2 +3 +4 +5 +6 +7 +8 \ No newline at end of file diff --git a/docs_crawled/concepts_workflows.md b/docs_crawled/concepts_workflows.md new file mode 100644 index 0000000..8f32189 --- /dev/null +++ b/docs_crawled/concepts_workflows.md @@ -0,0 +1,253 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/concepts/workflows.html + +# Workflows [​](#workflows) + +Workflows are the interfaces designed by the engine to manipulate the synthetic data pipeline. To understand workflows, you first need a basic understanding of the engine's overall architecture. + +## Overall Architecture [​](#overall-architecture) + +The framework of the engine is shown in the figure: + +![Engine Architecture Chart](/InternDataEngine-Docs/nimbus_archi.png) + +Above the optimization layer, the engine provides designs related to the Stage Runner Layer and the Components Layer. + +In the Stage Runner Layer, the standardized lifecycle of data production is defined, namely Load → Plan → Render → Store. + +Based on iterator execution flow, a strict set of abstract base class interfaces is designed, unifying the workflow control of all tasks to ensure that different components extended based on the same interface can collaborate seamlessly. + +For manipulating the data generation pipeline, they are integrated through Env series components combined with workflows. Its core extension principle is based on standardized component interfaces and lifecycle design: + +| Abstract Class | Derived Class | Core Interface | Functional Duty | +| `BaseLoader ` | `EnvLoader ` | `load_asset() ` | Resource loading and validation | +| `BaseRandomizer ` | `EnvRandomizer ` | `randomize_scene() ` | Domain randomization of the scene | +| `BasePlanner ` | `EnvPlanner ` | `generate_sequence() ` | Generate trajectory or action sequence | +| `BaseRenderer ` | `EnvRenderer ` | `generate_obs() ` | Synthesize multimodal visual data | +| `BaseWriter ` | `EnvWriter ` | `flush_to_disk() ` | Serialized storage | + +## Workflow Base Class and Interfaces [​](#workflow-base-class-and-interfaces) + +Based on the Env component architecture, a workflow base class `NimbusWorkFlow `is defined in [workflows/base.py](https://github.com/InternRobotics/InternDataEngine/blob/master/workflows/base.py), with its core API as follows: + +### Workflow Interface List [​](#workflow-interface-list) + +The workflow base class `NimbusWorkFlow `provides a complete set of interfaces. The table below lists all interfaces and their types: + +| Method | Return | Calling Component | Type | Description | +| `parse_task_cfgs(task_cfg_path) ` | `list ` | EnvLoader | ✓ Core | Parse task configuration files, return task list | +| `get_task_name() ` | `str ` | EnvLoader | ✓ Core | Get the name of the currently executing task | +| `reset(need_preload) ` | - | EnvLoader | ✓ Core | Reset environment to initial task state | +| `randomization(layout_path=None) ` | `bool ` | EnvRandomizer | ✓ Core | Execute domain randomization | +| `generate_seq() ` | `list ` | EnvPlanner | ✓ Core | Generate action sequence/trajectory | +| `seq_replay(sequence) ` | `int ` | EnvRenderer | ✓ Core | Replay sequence and generate visual data | +| `save(save_path) ` | `int ` | EnvWriter | ✓ Core | Save all data (trajectory + visual data) | +| `save_seq(save_path) ` | `int ` | EnvWriter | Optional | Only save trajectory, do not save visual data | +| `recover_seq(seq_path) ` | `list ` | EnvReader | Optional | Recover trajectory from disk | +| `generate_seq_with_obs() ` | `int ` | EnvPlanWithRender | Optional | Generate sequence with visual data | +| `dump_plan_info() ` | `bytes ` | EnvDumper | Optional | Serialize planning information | +| `dedump_plan_info(ser_obj) ` | `object ` | Dedumper | Optional | Deserialize planning information | +| `randomization_from_mem(data) ` | `bool ` | EnvRandomizer | Optional | Randomize from in-memory data | +| `recover_seq_from_mem(data) ` | `list ` | EnvReader | Optional | Recover sequence from in-memory data | + +**Interface Type Description: ** + +| Type | Description | +| ✓ Core Interface | Abstract methods that must be overridden in all workflow implementations | +| Optional | Implemented on-demand based on execution mode | + +### Execution Modes [​](#execution-modes) + +#### Plan with Render Mode [​](#plan-with-render-mode) + +Execute planning and rendering at the same time within a single stage to generate complete trajectories and visual data. Corresponding configuration template: [de_plan_and_render_template.yaml](https://github.com/InternRobotics/InternDataEngine/tree/master/configs/simbox/de_plan_and_render_template.yaml) + +**Lifecycle Flow: **Involved components and corresponding workflow interfaces: + +``` +EnvLoader <-> get_task_name() → init_task() → reset() +EnvRandomizer <-> randomization() +EnvPlanWithRender <-> generate_seq_with_obs() +EnvWriter <-> save() +``` +1 +2 +3 +4 + +**Application Scenario: **Fluid simulation tasks + +#### Plan and Render Mode [​](#plan-and-render-mode) + +Execute the planning and rendering stages sequentially. Corresponding configuration template: [de_plan_and_render_template.yaml](https://github.com/InternRobotics/InternDataEngine/tree/master/configs/simbox/de_plan_and_render_template.yaml) + +**Lifecycle Flow: **Involved components and corresponding workflow interfaces: + +``` +EnvLoader <-> get_task_name() → init_task() → reset() +EnvRandomizer <-> randomization() +EnvPlanner <-> generate_seq() +EnvRenderer <-> seq_replay(sequence) +EnvWriter <-> save() +``` +1 +2 +3 +4 +5 + +**Application Scenario: **Algorithm debugging, prototype validation + +#### Pipeline Mode (Distributed Pipeline) [​](#pipeline-mode-distributed-pipeline) + +Decouple planning and rendering stages, supporting custom dynamic pipeline scheduling. Corresponding configuration template: [de_pipe_template.yaml](https://github.com/InternRobotics/InternDataEngine/tree/master/configs/simbox/de_pipe_template.yaml) + +**Plan Process Lifecycle: **Involved components and corresponding workflow interfaces: + +``` +EnvLoader <-> get_task_name() → init_task() → reset() +EnvRandomizer <-> randomization() +EnvPlanner <-> generate_seq() +EnvDumper <-> dump_plan_info() +``` +1 +2 +3 +4 + +**Render Process Lifecycle: **Involved components and corresponding workflow interfaces: + +``` +Dedumper +EnvLoader <-> get_task_name() → init_task() → reset() → dedump_plan_info(ser_obj) +EnvRandomizer <-> randomization_from_mem(data) +EnvReader <-> recover_seq_from_mem(data) +EnvRenderer <-> seq_replay(sequence) +EnvWriter <-> save() +``` +1 +2 +3 +4 +5 +6 + +**Application Scenario: **Large-scale distributed data generation + +#### Plan Only Mode [​](#plan-only-mode) + +Generate only trajectory data, do not perform rendering operations; can reduce computational resource occupation. + +**Lifecycle Flow: **Involved components and corresponding workflow interfaces: + +``` +EnvLoader <-> get_task_name() → init_task() → reset() +EnvRandomizer <-> randomization() +EnvPlanner <-> generate_seq() +EnvWriter <-> save() +``` +1 +2 +3 +4 + +**Application Scenario: **Trajectory pre-generation, cooperate with Render Only mode to generate diversified background and material videos + +#### Render Only Mode [​](#render-only-mode) + +Perform visual rendering on previously generated trajectory data. + +**Lifecycle Flow: **Involved components and corresponding workflow interfaces: + +``` +EnvLoader <-> get_task_name() → init_task() → reset() +EnvRandomizer <-> randomization(layout_path) +EnvReader <-> recover_seq() +EnvRenderer <-> seq_replay(sequence) +EnvWriter <-> save() +``` +1 +2 +3 +4 +5 + +**Application Scenario: **Cooperate with Plan Only mode to generate diversified background and material videos, CI correctness validation + +## SimBox Workflow Implementation [​](#simbox-workflow-implementation) + +### Workflow Registration [​](#workflow-registration) + +In ****[workflows/init.py](https://github.com/InternRobotics/InternDataEngine/blob/master/workflows/__init__.py), register workflow extensions: +python +``` +def import_extensions(workflow_type): + if workflow_type == "SimBoxDualWorkFlow": + import workflows.simbox_dual_workflow + else: + raise ValueError(f"Unsupported workflow type: {workflow_type}") +``` +1 +2 +3 +4 +5 + +### Workflow Definition [​](#workflow-definition) + +In [workflows/simbox_dual_workflow.py](https://github.com/InternRobotics/InternDataEngine/blob/master/workflows/simbox_dual_workflow.py), define the specific implementation: +python +``` +@NimbusWorkFlow.register("SimBoxDualWorkFlow") +class SimBoxDualWorkFlow(NimbusWorkFlow): + def __init__( + self, + world, + task_cfg_path: str, + scene_info: str = "dining_room_scene_info", + random_seed: int = None, + ): + ... +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +### Workflow Architecture Design [​](#workflow-architecture-design) + +The core component structure of the SimBox workflow is as follows: + +``` +SimBoxDualWorkFlow + Task Module (task/banana.py) + ├── SceneObject Object Management + ├── Camera Configuration + └── Region Definition + Controller Module (controller/template_controller.py) + ├── CuRobo Motion Planner + └── Gripper Controller + Skill Module (skill/pick.py, place.py, ...) + └── Atomic Operation Units +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +## Related Documentation [​](#related-documentation) + +- [Controller Design](/InternDataEngine-Docs/concepts/controllers.html)- Robot motion planning and control +- [Skill Development](/InternDataEngine-Docs/concepts/skills.html)- Implementation and extension of manipulation skills +- [Task Definition](/InternDataEngine-Docs/concepts/tasks.html)- Construction and configuration of simulation environments \ No newline at end of file diff --git a/docs_crawled/config_assets.md b/docs_crawled/config_assets.md new file mode 100644 index 0000000..e748aec --- /dev/null +++ b/docs_crawled/config_assets.md @@ -0,0 +1,259 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/config/assets.html + +# Assets [​](#assets) + +All simulation assets are organized under `workflows/simbox/assets `. This document describes the asset structure, required assets, and organization conventions. + +## Asset Structure [​](#asset-structure) + +``` +workflows/simbox/assets/ +├── envmap_lib/ # HDR environment maps for scene lighting +├── background_textures/ # Background textures for domain randomization +├── floor_textures/ # Floor textures for domain randomization +├── table_textures/ # Table surface textures +├── table0/ # Default table USD model +├── lift2/ # ARX Lift-2 robot assets +├── G1_120s/ # Genie-1 robot assets +├── franka/ # Franka robot assets +├── frankarobotiq/ # Franka with Robotiq 2F-85 gripper assets +├── split_aloha_mid_360/ # Agilex Split ALOHA robot assets +├── basic/ # Basic task-specific assets +│ ├── arrange_the_tableware/ +│ ├── hang_the_cup_on_rack/ +│ ├── pour/ +│ ├── store_the_eggs/ +│ └── ... # Other task folders +├── art/ # Articulated object assets +│ ├── electriccooker/ +│ ├── microwave_gr/ +│ ├── laptop/ +│ └── ... +├── long_horizon/ # Long-horizon task assets +└── pick_and_place/ # Pick-and-place task assets +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 + +## Required Assets [​](#required-assets) + +To run a simulation task, the following assets are required: + +- **Environment Maps **( `envmap_lib/ `): HDR lighting for scene illumination. +- **Background Textures **( `background_textures/ `): Domain randomization backgrounds. +- **Floor Textures **( `floor_textures/ `): Floor appearance variation. +- **Table Model **( `table0/ `): Default table fixture. +- **Robot Assets **(e.g., `lift2/ `): Robot USD and kinematics configs. +- **Task Assets **(e.g., `basic// `): Objects specific to each task. + +## Rigid Object Assets [​](#rigid-object-assets) + +This section describes the asset structure for rigid objects, using `workflows/simbox/example_assets/task/sort_the_rubbish `as an example. + +### Directory Organization [​](#directory-organization) + +Different object categories are placed in separate folders at the same level. Objects of the same category are grouped together in a single folder to facilitate **category-level randomization **. + +``` +workflows/simbox/example_assets/task/sort_the_rubbish/ +├── garbage_can/ # Container category +│ ├── recyclable_can/ +│ └── nonrecyclable_can/ +├── non_recyclable_garbage/ # Non-recyclable items category +│ ├── obj_1/ +│ ├── obj_2/ +│ └── ... +└── recyclable_garbage/ # Recyclable items category + ├── bottle_0/ + ├── bottle_1/ + └── ... +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 + +### Object Instance Structure [​](#object-instance-structure) + +Each object instance folder contains the USD model, textures, and annotations: + +``` +workflows/simbox/example_assets/task/sort_the_rubbish/non_recyclable_garbage/obj_2/ +├── Aligned_obj.usd # Object USD with physics properties (mass, collision, etc.) +├── Aligned_grasp_sparse.npy # Grasp pose annotations (for pickable objects) +└── textures/ # Texture files + └── baked_mesh_*.png +``` +1 +2 +3 +4 +5 + +**File Naming Conventions: ** + +- **Aligned_obj.usd **( file ): USD file containing the 3D model with complete physics properties (mass, collision mesh, etc.). +- **Aligned_grasp_sparse.npy **( file ): Grasp pose annotations for manipulation tasks. +- **textures/ **( directory ): Directory containing texture maps for the object. + +## Articulated Object Assets [​](#articulated-object-assets) + +Articulated objects (e.g., doors, drawers, appliances) follow a similar organization pattern. This section uses `workflows/simbox/assets/art/electriccooker `as an example. + +### Directory Organization [​](#directory-organization-1) + +Different articulated object categories are placed in separate folders. Objects within the same category share consistent orientation, category classification, and functionality after preprocessing. + +``` +workflows/simbox/assets/art/electriccooker/ +├── electriccooker_0002/ +├── electriccooker_0008/ +├── electriccooker_0011/ +├── electriccooker_0017/ +├── electriccooker_0031/ +└── ... +``` +1 +2 +3 +4 +5 +6 +7 + +### Articulated Object Instance Structure [​](#articulated-object-instance-structure) + +Each articulated object instance contains the USD model, materials, and keypoint annotations: + +``` +workflows/simbox/assets/art/electriccooker/electriccooker_0031/ +├── instance.usd # Articulated object USD +├── instance.png # Preview image +├── Materials/ # Material definitions +└── Kps/ # Keypoint annotations + └── close_h/ # Keypoints for "close horizontally" action + ├── keypoints.json + ├── keypoints_final.json + └── info.json +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 + +**File Naming Conventions: ** + +- **instance.usd **( file ): USD file for articulated objects (unlike rigid objects which use `Aligned_obj.usd `). +- **instance.png **( file ): Preview/thumbnail image. +- **Materials/ **( directory ): Material and texture definitions. +- **Kps/ **( directory ): Keypoint annotations for interaction points. +- **Kps// **( directory ): Action-specific keypoints (e.g., `close_h `for closing horizontally). + +### Keypoint Annotations [​](#keypoint-annotations) + +Keypoint annotations define interaction points for articulated objects: + +``` +Kps/ +├── close_h/ # Close horizontally (e.g., laptop, pot, electric cooker) +├── close_v/ # Close vertically (e.g., microwave) +├── open_h/ # Open horizontally +├── open_v/ # Open vertically +├── pull/ # Pull (e.g., drawer) +├── push/ # Push +└── ... +``` +1 +2 +3 +4 +5 +6 +7 +8 + +Each action folder contains: + +- **keypoints.json **( file ): Initial keypoint positions. +- **keypoints_final.json **( file ): Final/processed keypoint positions. +- **info.json **( file ): Metadata about the keypoints. + +## Asset Configuration [​](#asset-configuration) + +Assets are referenced in task YAML configurations: +yaml +``` +objects: + - name: bottle_1 + path: task/sort_the_rubbish/recyclable_garbage/bottle_1/Aligned_obj.usd + target_class: RigidObject + category: bottle +``` +1 +2 +3 +4 +5 + +For more details on object configuration, see [Objects](/InternDataEngine-Docs/concepts/objects.html). + +## Best Practices [​](#best-practices) + +- + +**Category Organization **: Group objects of the same category in a single folder for efficient domain randomization. + +- + +**Consistent Naming **: Use standardized naming conventions: + +- `Aligned_obj.usd `for rigid objects +- `instance.usd `for articulated objects +- `Aligned_grasp_sparse.npy `for grasp annotations + +- + +**Complete Physics Properties **: Ensure USD files include: + +- Accurate mass properties +- Collision meshes +- Appropriate friction coefficients + +- + +**Preprocessing **: For all objects, ensure consistent frame and alignment across instances in the same category. \ No newline at end of file diff --git a/docs_crawled/config_dr.md b/docs_crawled/config_dr.md new file mode 100644 index 0000000..5f3c6b0 --- /dev/null +++ b/docs_crawled/config_dr.md @@ -0,0 +1,433 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/config/dr.html + +# Domain Randomization [​](#domain-randomization) + +Domain randomization generates diverse training data by varying simulation parameters. This helps bridge the sim-to-real gap and improves model generalization. + +## Randomization Types [​](#randomization-types) + +### 1. Environment Map Randomization [​](#_1-environment-map-randomization) + +Config Example: +yaml +``` +# Source: configs/manip/simbox/pick_and_place/lift2/single_pick/left/omniobject3d-banana.yaml +env_map: + envmap_lib: envmap_lib + apply_randomization: True + intensity_range: [4000, 7000] + rotation_range: [0, 180] +``` +1 +2 +3 +4 +5 +6 + +Code Example: +python +``` +# Source: workflows/simbox/core/tasks/banana.py +def _set_envmap(self): + """Randomize or reset the environment map (HDR dome light).""" + cfg = self.cfg["env_map"] + if cfg.get("light_type", "DomeLight") == "DomeLight": + envmap_hdr_path_list = glob.glob(os.path.join(self.asset_root, cfg["envmap_lib"], "*.hdr")) + envmap_hdr_path_list.sort() + if cfg.get("apply_randomization", False): + envmap_id = random.randint(0, len(envmap_hdr_path_list) - 1) + intensity = random.uniform(cfg["intensity_range"][0], cfg["intensity_range"][1]) + rotation = [random.uniform(cfg["rotation_range"][0], cfg["rotation_range"][1]) for _ in range(3)] + else: + envmap_id = 0 + intensity = 1000.0 + rotation = [0.0, 0.0, 0.0] + dome_prim_path = f"{self.root_prim_path}/DomeLight" + envmap_hdr_path = envmap_hdr_path_list[envmap_id] + + if not is_prim_path_valid(dome_prim_path): + self.dome_light_prim = UsdLux.DomeLight.Define(self.stage, dome_prim_path) + UsdGeom.Xformable(self.dome_light_prim).AddRotateXYZOp().Set((rotation[0], rotation[1], rotation[2])) + else: + self.dome_light_prim.GetOrderedXformOps()[0].Set((rotation[0], rotation[1], rotation[2])) + self.dome_light_prim.GetIntensityAttr().Set(intensity) + self.dome_light_prim.GetTextureFileAttr().Set(envmap_hdr_path) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 + +### 2. Texture Randomization [​](#_2-texture-randomization) + +Config Example: +yaml +``` +# Source: workflows/simbox/core/configs/arenas/pick_randomized_arena.yaml +fixtures: + - + name: table + path: table0/instance.usd + target_class: GeometryObject + translation: [0.0, 0.0, 0.375] + scale: [0.001, 0.001053, 0.001056] + texture: + texture_lib: "table_textures" + apply_randomization: True + texture_id: 0 + texture_scale: [0.001, 0.001] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +Code Example: +python +``` +# Source: workflows/simbox/core/objects/plane_object.py +def apply_texture(self, asset_root, cfg): + texture_name = cfg["texture_lib"] + texture_path_list = glob.glob(os.path.join(asset_root, texture_name, "*")) + texture_path_list.sort() + if cfg.get("apply_randomization", False): + texture_id = random.randint(0, len(texture_path_list) - 1) + else: + texture_id = cfg["texture_id"] + texture_path = texture_path_list[texture_id] + mat_prim_path = f"{self.prim_path}/Looks/Material" + if not is_prim_path_valid(mat_prim_path): + self.mat = OmniPBR( + prim_path=mat_prim_path, + name="Material", + texture_path=texture_path, + texture_scale=cfg.get("texture_scale"), + ) + self.apply_visual_material(self.mat) + else: + self.mat.set_texture( + texture_path, + ) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 + +### 3. Camera Pose Randomization [​](#_3-camera-pose-randomization) + +Config Example: +yaml +``` +# Source: configs/manip/simbox/pick_and_place/lift2/single_pick/left/omniobject3d-banana.yaml +cameras: + - + name: lift2_hand_left + translation: [0.07, 0.01, 0.08] + orientation: [0.62, 0.33, -0.33, -0.62] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/realsense_d405.yaml + parent: "lift2/lift2/lift2/fl/link6" # todo + apply_randomization: True + max_translation_noise: 0.02 + max_orientation_noise: 2.5 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 + +Code Example: +python +``` +# Source: workflows/simbox/core/tasks/banana.py +def _perturb_camera(self, camera, cfg, max_translation_noise=0.05, max_orientation_noise=10.0): + translation = np.array(cfg["translation"]) + orientation = np.array(cfg["orientation"]) + + random_direction = np.random.randn(3) + random_direction /= np.linalg.norm(random_direction) + random_distance = np.random.uniform(0, max_translation_noise) + perturbed_translation = translation + random_direction * random_distance + + original_rot = R.from_quat(orientation, scalar_first=True) + random_axis = np.random.randn(3) + random_axis /= np.linalg.norm(random_axis) + random_angle_deg = np.random.uniform(-max_orientation_noise, max_orientation_noise) + random_angle_rad = np.radians(random_angle_deg) + perturbation_rot = R.from_rotvec(random_axis * random_angle_rad) + perturbed_rot = perturbation_rot * original_rot + perturbed_orientation = perturbed_rot.as_quat(scalar_first=True) + + camera.set_local_pose( + translation=perturbed_translation, + orientation=perturbed_orientation, + camera_axes=cfg["camera_axes"], + ) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 + +### 4. Region Randomization [​](#_4-region-randomization) + +Config Example: +yaml +``` +# Source: configs/manip/simbox/pick_and_place/lift2/single_pick/left/omniobject3d-banana.yaml +regions: + - + object: pick_object_left + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [ + [-0.35, -0.25, 0.0], + [0.05, 0.05, 0.0] + ] + yaw_rotation: [-180.0, 180.0] + - + object: lift2 + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [ + [-0.025, -0.80, -0.775], + [0.025, -0.80, -0.67] + ] + yaw_rotation: [-0.0, 0.0] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 + +Code Example: +python +``` +# Source: workflows/simbox/core/tasks/banana.py +def _set_regions(self): + """Randomize object poses according to region configs.""" + random_region_list = deepcopy(self.random_region_list) + for cfg in self.cfg["regions"]: + obj = self._task_objects[cfg["object"]] + tgt = self._task_objects[cfg["target"]] + if "sub_tgt_prim" in cfg: + tgt = XFormPrim(prim_path=tgt.prim_path + cfg["sub_tgt_prim"]) + if "priority" in cfg: + if cfg["priority"]: + idx = random.choice(cfg["priority"]) + else: + idx = random.randint(0, len(random_region_list) - 1) + random_config = (random_region_list.pop(idx))["random_config"] + sampler_fn = getattr(RandomRegionSampler, cfg["random_type"]) + pose = sampler_fn(obj, tgt, **random_config) + obj.set_local_pose(*pose) + elif "container" in cfg: + container = self._task_objects[cfg["container"]] + obj_trans = container.get_local_pose()[0] + x_bias = random.choice(container.gap) if container.gap else 0 + obj_trans[0] += x_bias + obj_trans[2] += cfg["z_init"] + obj_ori = obj.get_local_pose()[1] + obj.set_local_pose(obj_trans, obj_ori) + elif "target2" in cfg: + tgt2 = self._task_objects[cfg["target2"]] + sampler_fn = getattr(RandomRegionSampler, cfg["random_type"]) + pose = sampler_fn(obj, tgt, tgt2, **cfg["random_config"]) + obj.set_local_pose(*pose) + else: + sampler_fn = getattr(RandomRegionSampler, cfg["random_type"]) + pose = sampler_fn(obj, tgt, **cfg["random_config"]) + obj.set_local_pose(*pose) + +# Source: workflows/simbox/core/utils/region_sampler.py +@staticmethod +def A_on_B_region_sampler(obj, tgt, pos_range, yaw_rotation): + # Translation + shift = np.random.uniform(*pos_range) + bbox_obj = compute_bbox(obj.prim) + obj_z_min = bbox_obj.min[2] + bbox_tgt = compute_bbox(tgt.prim) + tgt_center = (np.asarray(bbox_tgt.min) + np.asarray(bbox_tgt.max)) / 2 + tgt_z_max = bbox_tgt.max[2] + place_pos = np.zeros(3) + place_pos[0] = tgt_center[0] + place_pos[1] = tgt_center[1] + place_pos[2] = ( + tgt_z_max + (obj.get_local_pose()[0][2] - obj_z_min) + 0.001 + ) # add a small value to avoid penetration + place_pos += shift + # Orientation + yaw = np.random.uniform(*yaw_rotation) + dr = R.from_euler("xyz", [0.0, 0.0, yaw], degrees=True) + r = R.from_quat(obj.get_local_pose()[1], scalar_first=True) + orientation = (dr * r).as_quat(scalar_first=True) + return place_pos, orientation +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 + +## Key Files [​](#key-files) + +- **utils/dr.py **( file ): Randomization utilities. +- **utils/constants.py **( file ): Category parameters. +- **tasks/banana.py **( file ): Randomization implementation. + +## References [​](#references) + +- [Omni6DPose: A Benchmark and Model for Universal 6D Object Pose Estimation and Tracking (ECCV 2024)](https://jiyao06.github.io/Omni6DPose/) \ No newline at end of file diff --git a/docs_crawled/config_yaml.md b/docs_crawled/config_yaml.md new file mode 100644 index 0000000..2cc7189 --- /dev/null +++ b/docs_crawled/config_yaml.md @@ -0,0 +1,523 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/config/yaml.html + +# YAML Configuration [​](#yaml-configuration) + +Task configurations in InternDataEngine are defined using YAML files. This page explains the structure and available options. + +## Overview [​](#overview) + +A task YAML file defines all components needed for a simulation episode: environment, robots, objects, cameras, and skills. The workflow loads this configuration to set up the simulation. + +## World Settings [​](#world-settings) + +Global simulation settings are defined in the YAML files under `configs/simbox `: +yaml +``` +simulator: + physics_dt: 1/30 # Physics update rate + rendering_dt: 1/30 # Render update rate + stage_units_in_meters: 1.0 # Stage unit scale + headless: True # Run without GUI; set to False for visual debugging + anti_aliasing: 0 # Anti-aliasing level +``` +1 +2 +3 +4 +5 +6 + +### World Settings [​](#world-settings-1) + +- **physics_dt **( float ): Physics simulation time step (in seconds). +- **rendering_dt **( float ): Rendering time step (in seconds). +- **stage_units_in_meters **( float ): Unit scale used for the USD stage. +- **headless **( bool ): Run without GUI; set to `False `for visual debugging. +- **anti_aliasing **( int ): Anti-aliasing level (0 = disabled). + +## Task Basic Configuration [​](#task-basic-configuration) + +Each task begins with basic metadata and settings: +yaml +``` +tasks: + - + name: banana_base_task # Task identifier + asset_root: workflows/simbox/example_assets # Root path for all assets + task: BananaBaseTask # Task class name + task_id: 0 # Task instance ID + + offset: null # Optional position offset + render: True # Enable rendering + + neglect_collision_names: ["table"] # Collision filter names +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 + +### Task Basic Configuration [​](#task-basic-configuration-1) + +- **name **( str ): Unique task identifier. +- **asset_root **( str ): Root directory for all USD assets. +- **task **( str ): Python class that implements task logic. +- **task_id **( int ): Instance ID for multi-task scenarios. +- **offset **( list ): Optional world offset [x, y, z]. +- **render **( bool ): Enable/disable visual rendering. +- **neglect_collision_names **( list ): Object names to exclude from gripper collision checking. + +## Arena [​](#arena) + +Arena configuration defines static fixtures in the environment, such as tables, floors, and backgrounds. Each fixture specifies its USD asset path, position, orientation, scale, and optionally texture randomization settings. + +Arena configs are stored in `workflows/simbox/core/configs/arenas/ `and referenced via the `arena_file `field. +yaml +``` +name: example +fixtures: + - + name: table + path: table0/instance.usd + target_class: GeometryObject + translation: [0.0, 0.0, 0.375] + scale: [0.000525, 0.001053, 0.001056] + - + name: floor + target_class: PlaneObject + size: [5.0, 5.0] + translation: [0, 0, 0] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +## Environment Map [​](#environment-map) + +Environment map controls scene lighting and HDR backgrounds. It supports randomization of lighting intensity and rotation: +yaml +``` +env_map: + envmap_lib: envmap_lib # Path to HDR environment maps + apply_randomization: True # Enable random lighting + intensity_range: [4000, 7000] # Light intensity range + rotation_range: [0, 180] # Environment rotation range (degrees) +``` +1 +2 +3 +4 +5 + +## Robots [​](#robots) + +Robot configuration specifies the robot name, its configuration file path, initial orientation, and collision filter substrings: +yaml +``` +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", "gso_box"] +``` +1 +2 +3 +4 +5 +6 + +### Robots [​](#robots-1) + +- **name **( str ): Robot identifier (must match skill definitions). +- **robot_config_file **( str ): Path to robot config file, relative to asset_root. +- **euler **( list ): Initial rotation [roll, pitch, yaw] in degrees, in world frame. +- **ignore_substring **( list ): Substrings for collision filtering; objects with matching name prefixes are excluded from CuRobo collision checking. + +For detailed robot configuration, see [Robots](/InternDataEngine-Docs/concepts/robots.html). + +## Objects [​](#objects) + +Objects define items in the scene that can be manipulated, along with their metadata and placement properties: +yaml +``` +objects: + - + name: pick_object_left + path: task/sort_the_rubbish/non_recyclable_garbage/obj_0/Aligned_obj.usd + target_class: RigidObject + dataset: oo3d + category: bottle + prim_path_child: Aligned + translation: [0.0, 0.0, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [1, 1, 1] + apply_randomization: True + orientation_mode: random +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +### Objects [​](#objects-1) + +- **name **( str ): Unique object identifier (must match skill definitions). +- **path **( str ): USD file path relative to asset_root. +- **target_class **( str ): Object type: `RigidObject `, `GeometryObject `, `ArticulatedObject `, `XFormObject `, `ConveyorObject `. +- **dataset **( str ): Source dataset (e.g., `oo3d `, `gso `). +- **category **( str ): Object category for grasp detection. +- **prim_path_child **( str ): USD sub-prim name (default: `Aligned `). This prim contains the target mesh for collision and grasping. +- **translation **( list ): Initial position [x, y, z] in world frame. +- **euler **( list ): Initial rotation [roll, pitch, yaw] in degrees, in world frame. +- **scale **( list ): Scale factors [sx, sy, sz]. +- **apply_randomization **( bool ): Enable pose randomization within the category. +- **orientation_mode **( str ): Orientation constraint: `random `(pure random), `suggested `(follows category defaults), or `keep `(use `euler `values). + +For detailed object configuration, see [Objects](/InternDataEngine-Docs/concepts/objects.html). + +## Regions [​](#regions) + +Regions define spatial constraints for object placement, specifying which object to place, the target surface, and the allowed position/orientation ranges: +yaml +``` +regions: + - + object: pick_object_left + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [ + [-0.3, -0.20, 0.0], + [-0.025, 0.10, 0.0] + ] + yaw_rotation: [-45.0, 15.0] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 + +### Regions [​](#regions-1) + +- **object **( str ): Name of the object to place. +- **target **( str ): Target surface name from arena fixtures (default: `table `). +- **random_type **( str ): Sampler type for placement. +- **pos_range **( list ): Position range [[x_min, y_min, z_min], [x_max, y_max, z_max]] in world frame. +- **yaw_rotation **( list ): Yaw angle range [min, max] in degrees in world frame. + +## Cameras [​](#cameras) + +Camera configuration defines viewpoint, intrinsic parameters, and extrinsic randomization: +yaml +``` +cameras: + - + name: split_aloha_hand_left + translation: [0.0, 0.08, 0.05] + orientation: [0.0, 0.0, 0.965, 0.259] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/astra.yaml + parent: "split_aloha/split_aloha_mid_360_with_piper/split_aloha_mid_360_with_piper/fl/link6" + apply_randomization: False +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 + +### Cameras [​](#cameras-1) + +- **name **( str ): Camera identifier. +- **translation **( list ): Position offset [x, y, z] from parent link. +- **orientation **( list ): Rotation offset as quaternion [qx, qy, qz, qw] from parent link. +- **camera_axes **( str ): Coordinate convention: `usd `or `ros `. +- **camera_file **( str ): Path to camera parameter file. +- **parent **( str ): USD prim path to attach the camera to. +- **apply_randomization **( bool ): Enable extrinsics randomization. + +For detailed camera configuration, see [Cameras](/InternDataEngine-Docs/concepts/cameras.html). + +## Data [​](#data) + +The data section stores metadata for dataset generation: +yaml +``` +data: + task_dir: "sort_the_rubbish_part0" + language_instruction: "Sort the garbage on the desktop into recyclable and non-recyclable." + detailed_language_instruction: "Pick the bottles and place them into the recyclable trashbin with right arm, and pick the other garbage and place it into the non-recyclable trashbin with left arm." + collect_info: "" + version: "v2.0, head camera 1280x720, wrist 640x480" + update: True + max_episode_length: 4000 +``` +1 +2 +3 +4 +5 +6 +7 +8 + +### Data [​](#data-1) + +- **task_dir **( str ): Output directory name. +- **language_instruction **( str ): Short task description. +- **detailed_language_instruction **( str ): Detailed instruction for training. +- **collect_info **( str ): Additional collection metadata. +- **version **( str ): Dataset version string. +- **update **( bool ): Whether to overwrite existing data. +- **max_episode_length **( int ): Maximum steps per episode. + +## Skills [​](#skills) + +Skills define the action sequence for the robot: +yaml +``` +skills: + - + split_aloha: + - + right: + - + name: pick + objects: [pick_object_right] + 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: place + objects: [pick_object_right, gso_box_right] + place_direction: vertical + x_ratio_range: [0.3, 0.4] + y_ratio_range: [0.3, 0.4] + pre_place_z_offset: 0.3 + place_z_offset: 0.3 + success_mode: height + - + name: heuristic__skill + mode: home + gripper_state: 1.0 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 + +For detailed skill configuration, see [Skills](/InternDataEngine-Docs/concepts/skills/overview.html). + +## Complete Example [​](#complete-example) + +Here is a minimal task configuration: +yaml +``` +tasks: + - + name: banana_base_task + asset_root: workflows/simbox/assets + task: BananaBaseTask + task_id: 0 + offset: null + render: True + + arena_file: workflows/simbox/core/configs/arenas/example.yaml + + env_map: + envmap_lib: envmap_lib + apply_randomization: False + + robots: + - + name: "fr3" + robot_config_file: workflows/simbox/core/configs/robots/fr3.yaml + euler: [0.0, 0.0, 0.0] + + objects: + - + name: bottle + path: objects/bottle.usd + target_class: RigidObject + dataset: oo3d + category: bottle + prim_path_child: Aligned + translation: [0.3, 0.0, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [1, 1, 1] + apply_randomization: False + + regions: + - + object: bottle + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [[0.2, -0.1, 0.0], [0.4, 0.1, 0.0]] + yaw_rotation: [-30.0, 30.0] + + cameras: + - + name: head_camera + translation: [0.0, 0.0, 0.5] + orientation: [0.707, 0.0, 0.0, 0.707] + camera_file: workflows/simbox/core/configs/cameras/realsense_d455.yaml + parent: "" + + data: + task_dir: "pick_bottle" + language_instruction: "Pick the bottle." + detailed_language_instruction: "Pick the bottle." + collect_info: "" + version: "v1.0" + update: True + max_episode_length: 500 + + skills: + - + fr3: + - + name: pick + objects: [bottle] + pre_grasp_offset: 0.05 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 \ No newline at end of file diff --git a/docs_crawled/custom_assets.md b/docs_crawled/custom_assets.md new file mode 100644 index 0000000..2152c7b --- /dev/null +++ b/docs_crawled/custom_assets.md @@ -0,0 +1,373 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/custom/assets.html + +# New Assets [​](#new-assets) + +This guide explains how to add new assets to InternDataEngine for simulation tasks. + +## Part 1: Rigid Objects [​](#part-1-rigid-objects) + +This section describes how to introduce new rigid objects for manipulation tasks. + +### Step 1: Obtain Geometry and Textures [​](#step-1-obtain-geometry-and-textures) + +We recommend starting with an OBJ file that includes: + +- An `.mtl `file storing material properties +- With texture images + +**Sources for OBJ Files: ** + +- **Open-source datasets **( source ): Various public 3D model repositories. +- **ARCode **( source ): 3D reconstruction software for high-fidelity surface textures. +- **LiDAR-based apps **( source ): Reconstruction apps with depth sensors for mesh generation. +- **Tencent Hunyuan3D **( source ): Multi-view reconstruction for fine, non-convex, or transparent/specular objects. + +Hint + +For objects that are difficult to reconstruct (fine details, non-convex shapes, transparent or highly reflective surfaces), we recommend [Tencent Hunyuan3D](https://3d.hunyuan.tencent.com/)for high-quality multi-view reconstruction. + +### Step 2: Preprocess the OBJ File [​](#step-2-preprocess-the-obj-file) + +Before conversion, preprocess the OBJ file to ensure it meets the following requirements: + +- **Correct units **: Use real-world scale (meters recommended) +- **Centered origin **: Place origin at the object's center +- **Canonical pose **: Align with a reasonable axis (e.g., along a symmetry axis) + +You can perform these adjustments in **MeshLab **: + +![Set Origin in MeshLab](/InternDataEngine-Docs/meshlab_setorigin.jpg) + +*Setting the origin point at the object center in MeshLab * + +![Set Scale in MeshLab](/InternDataEngine-Docs/meshlab_set_scale.jpg) + +*Setting the correct scale (units) for the object * + +![Set Rotation in MeshLab](/InternDataEngine-Docs/meshlab_set_rotation.jpg) + +*Rotating the object to align with canonical pose * + +After adjustment, export and rename to `Aligned_obj.obj `. + +### Step 3: Convert OBJ to USD [​](#step-3-convert-obj-to-usd) + +Navigate to the tools directory and run the converter: +bash +``` +cd workflows/simbox/tools/rigid_obj +python asset_usd_converter.py --folders /path/to/obj/folder +``` +1 +2 + +This converts the OBJ to USD format and saves it as `Aligned_obj.usd `in the same directory. + +![OBJ to USD Conversion](/InternDataEngine-Docs/obj2usd.jpg) + +*Converting OBJ to USD format * + +### Step 4: Add Rigid Body Properties [​](#step-4-add-rigid-body-properties) + +Add rigid body physics properties (mass, etc.) to the USD file: +bash +``` +python make_rigid.py --usd_path /path/to/Aligned_obj.usd +``` +1 + +![Add Rigid Body Properties](/InternDataEngine-Docs/usd_add_rigid.jpg) + +*Adding rigid body physics properties (mass) to the USD * + +### Step 5: Add Collision and Friction [​](#step-5-add-collision-and-friction) + +Add collider properties and physics materials with friction: +bash +``` +# NOTE: This requires Isaac Sim Python +isaacsim.python.sh make_collider.py --usd_path /path/to/Aligned_obj.usd +``` +1 +2 + +The collider uses convex decomposition to tightly wrap the object for accurate collision detection. + +![Add Collider Properties](/InternDataEngine-Docs/usd_add_collider.jpg) + +*Adding collision mesh and friction properties * + +### Step 6: Verify Properties [​](#step-6-verify-properties) + +After processing, confirm the USD file has the following physics properties: + +- Rigid body dynamics +- Collision mesh +- Friction coefficients + +The USD file structure will be: + +``` +World (defaultprim) +├── Looks +│ └── materials +└── Aligned + └── mesh +``` +1 +2 +3 +4 +5 + +This hierarchical design ensures consistency. The `Aligned `prim (commonly set as `prim_path_child `) contains the mesh, which is used to detect force contact with the gripper. + +### Step 7 (Optional but Common): Generate Grasp Poses [​](#step-7-optional-but-common-generate-grasp-poses) + +If the object will be used for grasping, generate grasp pose annotations: +bash +``` +cd workflows/simbox/tools/grasp +# See README.md for detailed usage +python gen_sparse_label.py --obj_path /path/to/Aligned_obj.obj --unit m +``` +1 +2 +3 + +For detailed instructions, refer to the [README.md](https://github.com/InternRobotics/InternDataEngine/blob/master/workflows/simbox/tools/grasp/README.md)in the grasp tools directory. + +![Grasp Pose Visualization](/InternDataEngine-Docs/grasp_banana.jpg) + +*Visualization of generated grasp poses on a banana object * + +Warning + +The OBJ file must maintain consistent canonical frame and scale with the USD file. If you modify the OBJ file (scale, orientation) after USD conversion and then regenerate grasp poses, the grasp poses will be incorrect because they are generated from the OBJ mesh. + +### Step 8: Final Directory Structure [​](#step-8-final-directory-structure) + +After completing all the steps above, your task's asset directory should have the following structure: + +``` +new_task/ +└── new_objs/ # Directory containing all rigid objects + ├── new_obj0/ # Example object instance + │ ├── Aligned_obj.obj # Preprocessed OBJ mesh (source) + │ ├── Aligned.mtl # Material file for OBJ + │ ├── Aligned_obj.usd # Final USD with physics properties + │ ├── Aligned_grasp_sparse.npy # Sparse grasp poses (N × 17) + │ ├── Aligned_grasp_dense.npz # Dense grasp poses (optional) + │ └── textures/ # Texture files + │ └── Scan.jpg # Object texture image + ├── new_obj1/ # Another object instance + ├── new_obj2/ + ├── new_obj3/ + └── new_obj4/ +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 + +## Part 2: Articulated Objects [​](#part-2-articulated-objects) + +This section describes how to add new articulated objects (e.g., microwave, drawer, cabinet) for manipulation tasks. + +### Step 1: Prepare a Stable USD Asset [​](#step-1-prepare-a-stable-usd-asset) + +First, you need to prepare a stable USD asset for the articulated object. This USD can be: + +- Built from scratch +- Converted from URDF format + +The asset should have stable physical properties: + +- **Mass **: Properly defined for each link +- **Joint properties **: Appropriate stiffness and damping values +- **Collision **: Properly configured colliders for realistic interaction + +The initial hierarchy structure should look like this: + +![Initial USD Hierarchy](/InternDataEngine-Docs/pre_rehier.jpg) + +*Initial USD hierarchy structure - root positioned at articulation root * + +### Step 2: Understand the Skills Directory Structure [​](#step-2-understand-the-skills-directory-structure) + +We provide tools for different articulated object manipulation skills in `workflows/simbox/tools/art/ `. Currently available: + +- `close_v `: Close vertical articulated objects (e.g., microwave, oven) +- `open_v `: Open vertical articulated objects + +More skills will be added gradually. + +Take `close_v `as an example - this skill handles horizontal closing motions like closing a microwave from the side. A sample asset is provided at `workflows/simbox/tools/art/close_v/7265/usd/ `. + +### Step 3: Create keypoints_config.json (close_v example) [​](#step-3-create-keypoints-config-json-close-v-example) + +After obtaining the original asset (e.g., `microwave_0.usd `), create a `keypoints_config.json `file with the following structure: +json +``` +{ + "DIR": "/path/to/your/usd/directory", + "USD_NAME": "microwave_0.usd", + "INSTANCE_NAME": "microwave7265", + "link0_initial_prim_path": "/root/group_18", + "base_initial_prim_path": "/root/group_0", + "revolute_joint_initial_prim_path": "/root/group_18/RevoluteJoint", + "joint_index": 0, + "LINK0_ROT_AXIS": "y", + "BASE_FRONT_AXIS": "z", + "LINK0_CONTACT_AXIS": "-y", + "SCALED_VOLUME": 0.02 +} +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +- **DIR **( str ): Directory where USD files are stored. +- **USD_NAME **( str ): Original USD file name. +- **INSTANCE_NAME **( str ): Model identifier (name it yourself, preferably matching the asset). +- **link0_initial_prim_path **( str ): Absolute path in Isaac Sim for the "door" that interacts with the gripper. +- **base_initial_prim_path **( str ): Absolute path in Isaac Sim for the object base. +- **revolute_joint_initial_prim_path **( str ): Absolute path for the revolute joint. +- **joint_index **( int ): Joint number (default: 0). +- **LINK0_ROT_AXIS **( str ): Axis pointing vertically upward in the rotating joint's local coordinate system. +- **BASE_FRONT_AXIS **( str ): Axis facing the door in the base link's local coordinate system. +- **LINK0_CONTACT_AXIS **( str ): Axis pointing vertically downward in the contact link's local coordinate system. +- **SCALED_VOLUME **( float ): Default value 0.02 for microwave-like objects. + +For detailed axis configuration with visual examples, refer to the [readme.md](https://github.com/InternRobotics/InternDataEngine/blob/master/workflows/simbox/tools/art/close_v/readme.md). + +### Step 4: Run the Keypoint Annotation Pipeline (close_v example) [​](#step-4-run-the-keypoint-annotation-pipeline-close-v-example) + +Navigate to the tools directory and follow the pipeline in `keypoints_pipeline.sh `: +bash +``` +cd workflows/simbox/tools/art/close_v/tool +``` +1 + +**Step 4.1: Rehier - Restructure Asset Hierarchy ** +bash +``` +python rehier.py --config $CONFIG_PATH +``` +1 + +This step reorganizes the asset hierarchy to a unified standard. After rehier, the structure should look like: + +![Rehiered USD Hierarchy](/InternDataEngine-Docs/after_rehier.jpg) + +*Restructured USD hierarchy - instance prim inserted between original asset and root as articulation root * + +An `instance.usd `file will be generated, indicating success. All joints except the specified one will be locked. + +**Step 4.2: Select Keypoints ** +bash +``` +python select_keypoint.py --config $CONFIG_PATH +``` +1 + +This opens an Open3D visualization window for interactive point selection: + +- **Ctrl + Left Click **: Add a point +- **Ctrl + Right Click **: Remove a point + +You need to annotate two contact points: + +| Point | Description | +| **First point (articulated_object_head) ** | Desired base position where the gripper contacts the door | +| **Second point (articulated_object_tail) ** | The line from the first point should be perpendicular to the rotation axis | + +For visual guidance, refer to the [readme.md](https://github.com/InternRobotics/InternDataEngine/blob/master/workflows/simbox/tools/art/close_v/readme.md). + +**Step 4.3: Transfer Keypoints ** +bash +``` +python transfer_keypoints.py --config $CONFIG_PATH +``` +1 + +**Step 4.4: Overwrite Keypoints ** +bash +``` +python overwrite_keypoints.py --config $CONFIG_PATH +``` +1 + +### Step 5: Final Directory Structure (close_v example) [​](#step-5-final-directory-structure-close-v-example) + +After completing all steps, your asset directory should have the following structure: + +``` +7265/ # Asset ID +└── usd/ + ├── microwave_0.usd # Original USD asset + ├── instance.usd # Processed USD with rehiered structure + ├── keypoints_config.json # Configuration file + ├── textures/ # Texture files + │ ├── door_mesh_0_texture_0.jpg + │ └── door_mesh_1_texture_0.jpg + └── Kps/ # Keypoint annotations + └── close_v/ # Skill directory + ├── info.json # Complete metadata + ├── keypoints.json # Raw keypoints + └── keypoints_final.json # Final processed keypoints +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +You can have multiple USD files for similar objects. Each USD directory must contain: + +- `instance.usd `: The processed USD asset +- `Kps/ `: Keypoint annotations organized by skill + +Each skill directory under `Kps/ `must contain: + +- `info.json `: Complete object metadata including paths, axes, and keypoint positions +- `keypoints.json `: Raw keypoint coordinates +- `keypoints_final.json `: Processed keypoint data + +### Step 6: Test with Task Configuration [​](#step-6-test-with-task-configuration) + +With the annotated asset, you can now write a task configuration file to test the manipulation task. Refer to existing task configs in `workflows/simbox/core/configs/tasks/art/ `for examples. + +## References [​](#references) + +- [GraspNet API](https://github.com/graspnet/graspnetAPI) +- [Tencent Hunyuan3D](https://3d.hunyuan.tencent.com/) \ No newline at end of file diff --git a/docs_crawled/custom_controller.md b/docs_crawled/custom_controller.md new file mode 100644 index 0000000..7577692 --- /dev/null +++ b/docs_crawled/custom_controller.md @@ -0,0 +1,385 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/custom/controller.html + +# New Controller [​](#new-controller) + +This guide explains how to create a new robot controller for motion planning with CuRobo. + +## Part 1: Create CuRobo Configuration Files [​](#part-1-create-curobo-configuration-files) + +Our controller uses a separate CuRobo config file for each arm. You need to: + +- **Prepare URDF files for each arm **- Extract single-arm URDF from your robot's full URDF +- **Create CuRobo config files **- Follow the official CuRobo tutorial + +### Step 1.1: Prepare URDF Files [​](#step-1-1-prepare-urdf-files) + +For dual-arm robots, create separate URDF files for left and right arms. Save them in: + +``` +workflows/simbox/curobo/src/curobo/content/assets/robot/ +├── your_robot_left_arm.urdf +├── your_robot_right_arm.urdf +└── meshes/ +``` +1 +2 +3 +4 + +Hint + +Each arm URDF should be a standalone file containing only that arm's links and joints. Make sure mesh paths in the URDF are correct relative to the assets folder. + +### Step 1.2: Create CuRobo Config Files [​](#step-1-2-create-curobo-config-files) + +Follow the official CuRobo tutorial: [Configuring a New Robot](https://curobo.org/tutorials/1_robot_configuration.html) + +Key steps: + +- Convert URDF to USD using Isaac Sim +- Use Lula Robot Description Editor to generate collision spheres +- Configure self-collision parameters + +Save config files in: + +``` +workflows/simbox/curobo/src/curobo/content/configs/robot/ +├── your_robot_left_arm.yml +└── your_robot_right_arm.yml +``` +1 +2 +3 + +Warning + +Isaac Sim's Lula Robot Description Editor is only available in **Isaac Sim 4.0.0 **. Later versions have removed this feature. If you're using a newer version, you may need to manually create the collision sphere configuration or use Isaac Sim 4.0.0 for this step. + +### Example CuRobo Config Structure [​](#example-curobo-config-structure) +yaml +``` +# your_robot_left_arm.yml +robot_cfg: + kinematics: + usd_path: "robot/your_robot_left_arm.usd" + usd_robot_root: "/robot" + urdf_path: "robot/your_robot_left_arm.urdf" + asset_root_path: "robot" + base_link: "base_link" + ee_link: "link6" # End-effector link (must match fl_ee_path in robot.yaml) + + collision_link_names: + - link1 + - link2 + # ... + + collision_spheres: + link1: + - center: [0.0, 0.0, 0.0] + radius: 0.05 + # ... + + self_collision_ignore: + link1: ["link2", "link3"] + # ... + + cspace: + joint_names: ["joint1", "joint2", "joint3", "joint4", "joint5", "joint6"] + retract_config: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + null_space_weight: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + cspace_distance_weight: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + max_jerk: 500.0 + max_acceleration: 15.0 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 + +## Part 2: Update Robot YAML Configuration [​](#part-2-update-robot-yaml-configuration) + +In your `new_robot.yaml `(see [New Robot](/InternDataEngine-Docs/custom/robot.html)), fill in the `robot_file `field: + +### For Dual-Arm Robots [​](#for-dual-arm-robots) +yaml +``` +# CuRobo kinematics config files (one per arm) +robot_file: + - workflows/simbox/curobo/src/curobo/content/configs/robot/your_robot_left_arm.yml + - workflows/simbox/curobo/src/curobo/content/configs/robot/your_robot_right_arm.yml +``` +1 +2 +3 +4 + +### For Single-Arm Robots [​](#for-single-arm-robots) + +For single-arm robots, we conventionally name it as the "left" arm: +yaml +``` +# CuRobo kinematics config file +robot_file: + - workflows/simbox/curobo/src/curobo/content/configs/robot/your_robot_left_arm.yml +``` +1 +2 +3 + +## Part 3: Create Controller Python Class [​](#part-3-create-controller-python-class) + +Create a new controller file in `workflows/simbox/core/controllers/ `. Here's a new controller template: +python +``` +"""NewRobot controller implementation.""" + +import numpy as np +from core.controllers.base_controller import register_controller +from core.controllers.template_controller import TemplateController + +@register_controller +class NewRobotController(TemplateController): + """Controller for NewRobot.""" + + def _get_default_ignore_substring(self): + """Return default collision ignore substrings.""" + return ["material", "table", "floor", "scene"] + + def _configure_joint_indices(self, robot_file: str) -> None: + """ + Configure joint names and indices for motion planning. + + This method maps between CuRobo's joint names and the robot USD's joint names. + """ + # Raw joint names from CuRobo config (must match cspace.joint_names in yaml) + self.raw_js_names = ["joint1", "joint2", "joint3", "joint4", "joint5", "joint6"] + + if "left" in robot_file: + # Command joint names in robot USD (with prefix) + self.cmd_js_names = ["fl_joint1", "fl_joint2", "fl_joint3", + "fl_joint4", "fl_joint5", "fl_joint6"] + # Joint indices from robot config + self.arm_indices = np.array(self.robot.cfg["left_joint_indices"]) + self.gripper_indices = np.array(self.robot.cfg["left_gripper_indices"]) + self.reference_prim_path = self.task.robots[self.name].fl_base_path + self.lr_name = "left" + self._gripper_state = 1.0 if self.robot.left_gripper_state == 1.0 else -1.0 + + elif "right" in robot_file: + # Command joint names in robot USD (with prefix) + self.cmd_js_names = ["fr_joint1", "fr_joint2", "fr_joint3", + "fr_joint4", "fr_joint5", "fr_joint6"] + self.arm_indices = np.array(self.robot.cfg["right_joint_indices"]) + self.gripper_indices = np.array(self.robot.cfg["right_gripper_indices"]) + self.reference_prim_path = self.task.robots[self.name].fr_base_path + self.lr_name = "right" + self._gripper_state = 1.0 if self.robot.right_gripper_state == 1.0 else -1.0 + + else: + raise ValueError("robot_file must contain 'left' or 'right'") + + # Gripper joint position for open/close + # Adjust based on your gripper's joint values + self._gripper_joint_position = np.array([0.044]) # Example: 44mm for open + + def get_gripper_action(self): + """ + Map gripper state to joint positions. + + Returns: + np.ndarray: Joint positions for gripper. + + State mapping: + - _gripper_state = 1.0 -> Gripper OPEN + - _gripper_state = -1.0 -> Gripper CLOSED + """ + # When _gripper_state is 1.0, gripper opens (joint value = positive) + # When _gripper_state is -1.0, gripper closes (joint value = 0 or negative) + return np.clip(self._gripper_state * self._gripper_joint_position, 0.0, 0.1) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 + +_configure_joint_indices(self, robot_file: str ) + +Configure joint names and indices for motion planning. This method maps between CuRobo's joint names and the robot USD's joint names. + +Parameters: + +- **robot_file **( str ): Path to the CuRobo config file, used to determine left/right arm. + +Key Variables: + +- **raw_js_names **( list ): Joint names in CuRobo config (from `cspace.joint_names `in yaml). +- **cmd_js_names **( list ): Joint names in robot USD (must match actual USD joint names). +- **arm_indices **( np.ndarray ): Joint indices for arm in articulation. +- **gripper_indices **( np.ndarray ): Joint indices for gripper in articulation. + +Warning + +The order of `raw_js_names `and `cmd_js_names `must correspond! The i-th element in `raw_js_names `maps to the i-th element in `cmd_js_names `. + +get_gripper_action(self) + +Convert `_gripper_state `to actual gripper joint positions. + +Returns: + +- **np.ndarray **: Joint positions for gripper. + +State Mapping: + +- **_gripper_state = 1.0 **: Gripper OPEN (joint value that fingers open) +- **_gripper_state = -1.0 **: Gripper CLOSED (joint value that fingers close) + +Code Example: +python +``` +# Parallel gripper (single joint) +def get_gripper_action(self): + return np.clip(self._gripper_state * self._gripper_joint_position, 0.0, 0.1) + +# Two-finger gripper (two joints, e.g., Franka Panda) +def get_gripper_action(self): + finger1 = np.clip(self._gripper_state * 0.04, 0.0, 0.04) + finger2 = np.clip(-self._gripper_state * 0.04, -0.04, 0.0) + return np.array([finger1, finger2]) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 + +### Register the Controller [​](#register-the-controller) + +Add your controller to `workflows/simbox/core/controllers/__init__.py `: +python +``` +from core.controllers.newrobot_controller import NewRobotController + +__all__ = [ + # ... existing controllers + "NewRobotController", +] +``` +1 +2 +3 +4 +5 +6 + +## Checklist [​](#checklist) + +- [ ] Prepare single-arm URDF files for each arm +- [ ] Create CuRobo config YAML files using Isaac Sim 4.0.0 (Lula) +- [ ] Update `robot_file `in robot YAML configuration +- [ ] Create controller Python class +- [ ] Implement `_get_default_ignore_substring() ` +- [ ] Implement `_configure_joint_indices() `with correct joint name mapping +- [ ] Implement `get_gripper_action() `with correct open/close mapping +- [ ] Register controller in `__init__.py ` +- [ ] Test with task configuration + +## References [​](#references) + +- [CuRobo Robot Configuration Tutorial](https://curobo.org/tutorials/1_robot_configuration.html) +- [Controllers API Reference](/InternDataEngine-Docs/api/controllers.html) +- [Template Controller Source](https://github.com/your-org/DataEngine/blob/main/workflows/simbox/core/controllers/template_controller.py) \ No newline at end of file diff --git a/docs_crawled/custom_robot.md b/docs_crawled/custom_robot.md new file mode 100644 index 0000000..76c53ed --- /dev/null +++ b/docs_crawled/custom_robot.md @@ -0,0 +1,561 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/custom/robot.html + +# New Robot [​](#new-robot) + +This guide explains how to add a new robot platform to InternDataEngine. + +## Part 1: Obtain Robot USD [​](#part-1-obtain-robot-usd) + +We recommend converting **URDF to USD **for robot assets. Isaac Sim provides a URDF Importer that converts URDF files to USD format. + +### Requirements for the USD File [​](#requirements-for-the-usd-file) + +- + +**Physics Properties **: Ensure the USD has proper physics attributes - joint angles should move correctly when set, and dynamics parameters (damping, stiffness) should be configured appropriately. + +- + +**Collision Mesh **: Use **convex hull **collision meshes rather than convex decomposition. Convex decomposition creates many collision shapes, which significantly slows down motion planning and interaction physics. + +- + +**Joint Locking (Dual-arm Robots) **: For dual-arm robots, we recommend locking all joints except the two arm joints and gripper joints to simplify experiments and planning. + +Hint + +Isaac Sim's default URDF Importer typically produces stable USD files. However, URDF files themselves may have issues, and some damping/stiffness parameters might need adjustment after conversion. + +Warning + +Some grippers have underactuated joints (passive/follower joints) with a forward extension offset. These may exhibit undesirable physical interactions with objects. Test thoroughly before proceeding. + +Good luck obtaining a physically stable robot USD! + +## Part 2: Create Robot YAML Configuration [​](#part-2-create-robot-yaml-configuration) + +Create a robot configuration file in `workflows/simbox/core/configs/robots/ `. Here's a template `new_robot.yaml `: +yaml +``` +# ============================================================================= +# New Robot Configuration Template +# ============================================================================= +# Description: [e.g., Dual-arm 6-DOF robot with lift joint] + +# ----------------------------------------------------------------------------- +# Robot Info +# ----------------------------------------------------------------------------- +target_class: NewRobot # Python class name for robot wrapper +path: "YOUR_PATH_TO_ROBOT_USD_PATH (relative to asset root)" # USD file path relative to asset root + +# ----------------------------------------------------------------------------- +# CuRobo Kinematics Config (one per arm for dual-arm robots) +# ----------------------------------------------------------------------------- +robot_file: + - "" # Left arm CuRobo config path + - "" # Right arm CuRobo config path (dual-arm only) + +# ----------------------------------------------------------------------------- +# Gripper Parameters +# ----------------------------------------------------------------------------- +gripper_max_width: 0.0 # Maximum gripper opening (meters) +gripper_min_width: 0.0 # Minimum gripper closing (meters) +tcp_offset: 0.0 # Distance from EE origin to TCP (meters, MUST be positive) + +# ----------------------------------------------------------------------------- +# Physics Solver Parameters +# ----------------------------------------------------------------------------- +solver_position_iteration_count: 128 # Position solver iterations +solver_velocity_iteration_count: 4 # Velocity solver iterations +stabilization_threshold: 0.005 # Stabilization threshold + +# ----------------------------------------------------------------------------- +# Joint Indices (find in Isaac Sim's articulation inspector) +# ----------------------------------------------------------------------------- +left_joint_indices: [] # Left arm joint indices, e.g., [0, 1, 2, 3, 4, 5] +right_joint_indices: [] # Right arm joint indices (dual-arm only) +left_gripper_indices: [] # Left gripper joint index, e.g., [6] +right_gripper_indices: [] # Right gripper joint index (dual-arm only) +lift_indices: [] # Lift joint indices (if applicable) + +# ----------------------------------------------------------------------------- +# End-Effector Paths (relative to robot prim path) +# IMPORTANT: Transformation from EE to TCP must be FIXED! +# ----------------------------------------------------------------------------- +fl_ee_path: "" # Front-left end-effector prim path +fr_ee_path: "" # Front-right end-effector prim path (dual-arm only) +fl_base_path: "" # Front-left base prim path +fr_base_path: "" # Front-right base prim path (dual-arm only) + +# ----------------------------------------------------------------------------- +# Gripper Keypoints (in EE frame, for visualization) +# ----------------------------------------------------------------------------- +fl_gripper_keypoints: + tool_head: [0.0, 0.0, 0.0, 1] # Gripper fingertip (approach direction) + tool_tail: [0.0, 0.0, 0.0, 1] # Gripper base + tool_side: [0.0, 0.0, 0.0, 1] # Side point for width visualization +fr_gripper_keypoints: + tool_head: [0.0, 0.0, 0.0, 1] + tool_tail: [0.0, 0.0, 0.0, 1] + tool_side: [0.0, 0.0, 0.0, 1] + +# ----------------------------------------------------------------------------- +# Collision Filter Paths (relative to robot prim path) +# ----------------------------------------------------------------------------- +fl_filter_paths: # Gripper fingers to filter from collision + - "" +fr_filter_paths: + - "" +fl_forbid_collision_paths: # Arm links forbidden for self-collision + - "" +fr_forbid_collision_paths: + - "" + +# ----------------------------------------------------------------------------- +# Pose Processing Parameters +# ----------------------------------------------------------------------------- +# Rotation matrix from GraspNet gripper frame to robot EE frame +# See: doc/InterDataEngine-docs/concepts/robots.md#r-ee-graspnet +R_ee_graspnet: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] +ee_axis: "" # Approach axis from EE to TCP: "x", "y", or "z" + +# ----------------------------------------------------------------------------- +# Default Joint Home Positions (radians) +# ----------------------------------------------------------------------------- +left_joint_home: [] # Default left arm joint positions +right_joint_home: [] # Default right arm joint positions (dual-arm only) +left_joint_home_std: [] # Randomization std for left arm +right_joint_home_std: [] # Randomization std for right arm (dual-arm only) +left_gripper_home: [] # Default left gripper width (meters) +right_gripper_home: [] # Default right gripper width (meters, dual-arm only) +lift_home: [] # Default lift position (if applicable) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 + +Refer to [Robots](/InternDataEngine-Docs/concepts/robots.html)for detailed explanations of each parameter. Here we highlight several critical parameters that require special attention: + +### Critical Parameters [​](#critical-parameters) + +fl_ee_path / fr_ee_path + +Specify the relative path (under the robot's local prim path) to the end-effector link. + +Why It Matters: + +- CuRobo plans target poses for this link +- Grasp poses are transformed based on this link's EE pose +- The `ee_axis `(approach direction from EE origin to TCP) depends on this link + +Selection Rule: + +Common choices: + +- The last link of the gripper (e.g., Robotiq, Genie-1) +- A link on the gripper (e.g., Lift-2, Panda) + +Warning + +The transformation from EE to TCP must be **FIXED **(fixed rotation + fixed translation = tcp_offset). + +Figure Examples: + +![Genie-1 EE Path](/InternDataEngine-Docs/ee_genie1.jpg) + +*Genie-1: EE link is the last gripper link (fixed EE-to-TCP transform) * + +![Lift-2 EE Path](/InternDataEngine-Docs/ee_lift2.jpg) + +*Lift-2: EE link is a gripper link (fixed EE-to-TCP transform) * + +![Robotiq EE Path](/InternDataEngine-Docs/ee_robotiq.jpg) + +*Robotiq: EE link is the last gripper link (fixed EE-to-TCP transform) * + +![Panda EE Path](/InternDataEngine-Docs/ee_panda.jpg) + +*Panda: EE link is a gripper link (fixed EE-to-TCP transform) * + +tcp_offset + +Once `fl_ee_path `is determined, calculate the distance from EE origin to TCP. This should be a **positive value **. + +Config Example: +yaml +``` +# Example: ARX Lift-2 +tcp_offset: 0.125 # Distance in meters from EE frame origin to TCP +``` +1 +2 + +ee_axis + +The axis direction from EE origin pointing toward TCP, expressed in the EE frame. Valid values are `"x" `, `"y" `, or `"z" `. + +Config Example: +yaml +``` +# Example: ARX Lift-2 +ee_axis: "x" # Approach direction in EE frame +``` +1 +2 + +R_ee_graspnet + +The rotation matrix that transforms from the GraspNet API's canonical gripper frame to the robot's end-effector frame. + +Warning + +This parameter is important but can be tricky! For calculation details, refer to [Robots - R_ee_graspnet](/concepts/robots.html#r-ee-graspnet). + +Config Example: +yaml +``` +# Example: ARX Lift-2 +R_ee_graspnet: [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, -1.0]] +``` +1 +2 + +## Part 3: Create Robot Python Class [​](#part-3-create-robot-python-class) + +Create a Python file in `workflows/simbox/core/robots/ `. Here's a template based on `lift2.py `: +python +``` +"""New robot implementation.""" +import numpy as np +from core.robots.base_robot import register_robot +from core.robots.template_robot import TemplateRobot + +@register_robot +class NewRobot(TemplateRobot): + """New robot implementation.""" + + def _setup_joint_indices(self): + """Configure joint indices from config.""" + self.left_joint_indices = self.cfg["left_joint_indices"] + self.right_joint_indices = self.cfg["right_joint_indices"] + self.left_gripper_indices = self.cfg["left_gripper_indices"] + self.right_gripper_indices = self.cfg["right_gripper_indices"] + # Optional: lift, body, head indices for special robots + self.lift_indices = self.cfg.get("lift_indices", []) + + def _setup_paths(self): + """Configure USD prim paths from config.""" + fl_ee_path = self.cfg["fl_ee_path"] + fr_ee_path = self.cfg["fr_ee_path"] + self.fl_ee_path = f"{self.robot_prim_path}/{fl_ee_path}" + self.fr_ee_path = f"{self.robot_prim_path}/{fr_ee_path}" + self.fl_base_path = f"{self.robot_prim_path}/{self.cfg['fl_base_path']}" + self.fr_base_path = f"{self.robot_prim_path}/{self.cfg['fr_base_path']}" + self.fl_hand_path = self.fl_ee_path + self.fr_hand_path = self.fr_ee_path + + def _setup_gripper_keypoints(self): + """Configure gripper keypoints from config.""" + self.fl_gripper_keypoints = self.cfg["fl_gripper_keypoints"] + self.fr_gripper_keypoints = self.cfg["fr_gripper_keypoints"] + + def _setup_collision_paths(self): + """Configure collision filter paths from config.""" + self.fl_filter_paths_expr = [ + f"{self.robot_prim_path}/{p}" for p in self.cfg["fl_filter_paths"] + ] + self.fr_filter_paths_expr = [ + f"{self.robot_prim_path}/{p}" for p in self.cfg["fr_filter_paths"] + ] + self.fl_forbid_collision_paths = [ + f"{self.robot_prim_path}/{p}" for p in self.cfg["fl_forbid_collision_paths"] + ] + self.fr_forbid_collision_paths = [ + f"{self.robot_prim_path}/{p}" for p in self.cfg["fr_forbid_collision_paths"] + ] + + def _get_gripper_state(self, gripper_home): + """ + Determine gripper open/close state. + + Returns: + 1.0 if gripper is open, -1.0 if closed. + """ + # Adjust threshold based on your gripper's joint values + # return 1.0 if gripper_home and gripper_home[0] >= value else -1.0 + pass + + def _setup_joint_velocities(self): + """Configure joint velocity limits (optional).""" + # Override if needed for custom velocity limits + pass + + def apply_action(self, joint_positions, joint_indices, *args, **kwargs): + """Override for custom action application (optional).""" + super().apply_action(joint_positions, joint_indices, *args, **kwargs) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 + +### Key Methods [​](#key-methods) + +_get_gripper_state(self, gripper_home) + +Determine whether the gripper is open or closed based on the current joint positions. + +Parameters: + +- **gripper_home **( list ): Current gripper joint positions. + +Returns: + +- **float **: `1.0 `if gripper is open, `-1.0 `if closed. + +Hint + +Adjust the threshold value based on your gripper's specific joint values. Different grippers have different open/close positions. + +Code Example: +python +``` +# Example 1: ARX Lift-2 (single-joint parallel gripper) +# Gripper is open when joint value >= 0.044 (44mm) +def _get_gripper_state(self, gripper_home): + return 1.0 if gripper_home and gripper_home[0] >= 0.044 else -1.0 + +# Example 2: Franka with Robotiq 2F-85 (two-joint gripper) +# Gripper is open when both joints are 0.0 +def _get_gripper_state(self, gripper_home): + if not gripper_home or len(gripper_home) < 2: + return 1.0 + return 1.0 if (gripper_home[0] == 0.0 and gripper_home[1] == 0.0) else -1.0 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 + +### Register the Robot [​](#register-the-robot) + +Add your robot to `workflows/simbox/core/robots/__init__.py `: +python +``` +from core.robots.new_robot import NewRobot + +__all__ = [ + # ... existing robots + "NewRobot", +] +``` +1 +2 +3 +4 +5 +6 + +## Part 4: Create Task Robot Configuration [​](#part-4-create-task-robot-configuration) + +Add the robot configuration to your task YAML file: +yaml +``` +robots: + - name: "new_robot" + robot_config_file: workflows/simbox/core/configs/robots/new_robot.yaml + euler: [0.0, 0.0, 0.0] # Initial orientation [roll, pitch, yaw] in degrees + ignore_substring: ["material", "table"] # Collision filter substrings +``` +1 +2 +3 +4 +5 + +See [YAML Configuration](/InternDataEngine-Docs/config/yaml.html)for more details on task configuration. + +## Checklist [​](#checklist) + +- [ ] Obtain or create robot USD file (URDF → USD via Isaac Sim) +- [ ] Verify physics properties (joint movement, damping, stiffness) +- [ ] Set up collision meshes (use convex hull, not convex decomposition) +- [ ] Lock unnecessary joints for dual-arm robots +- [ ] Create robot YAML config file in `workflows/simbox/core/configs/robots/ ` +- [ ] Configure `fl_ee_path `/ `fr_ee_path `(EE link with fixed EE-to-TCP transform) +- [ ] Calculate and set `tcp_offset `(distance from EE origin to TCP) +- [ ] Determine `ee_axis `(approach direction: "x", "y", or "z") +- [ ] Calculate `R_ee_graspnet `(rotation from GraspNet frame to EE frame) +- [ ] Configure joint indices ( `left_joint_indices `, `right_joint_indices `, etc.) +- [ ] Set up gripper keypoints for visualization +- [ ] Configure collision filter paths +- [ ] Create robot Python class in `workflows/simbox/core/robots/ ` +- [ ] Implement `_setup_joint_indices() ` +- [ ] Implement `_setup_paths() ` +- [ ] Implement `_setup_gripper_keypoints() ` +- [ ] Implement `_setup_collision_paths() ` +- [ ] Implement `_get_gripper_state() `(1.0 = open, -1.0 = closed) +- [ ] Register robot in `__init__.py ` +- [ ] Add robot to task YAML configuration +- [ ] Test robot in simulation \ No newline at end of file diff --git a/docs_crawled/custom_skill.md b/docs_crawled/custom_skill.md new file mode 100644 index 0000000..04c8f2e --- /dev/null +++ b/docs_crawled/custom_skill.md @@ -0,0 +1,509 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/custom/skill.html + +# New Skill [​](#new-skill) + +This guide explains how to create a new manipulation skill for robot task execution. + +## Overview [​](#overview) + +Skills define atomic manipulation actions (e.g., pick, place, articulation). Each skill generates a sequence of manipulation commands ( `manip_list `) that the controller executes sequentially. + +## Skill Template [​](#skill-template) + +Create a new file in `workflows/simbox/core/skills/ `: +python +``` +"""NewSkill implementation.""" + +from copy import deepcopy +import numpy as np +from core.skills.base_skill import BaseSkill, register_skill +from omegaconf import DictConfig +from omni.isaac.core.controllers import BaseController +from omni.isaac.core.robots.robot import Robot +from omni.isaac.core.tasks import BaseTask + +@register_skill +class NewSkill(BaseSkill): + """New manipulation skill.""" + + def __init__( + self, + robot: Robot, + controller: BaseController, + task: BaseTask, + cfg: DictConfig, + *args, + **kwargs + ): + """Initialize the skill. + + Args: + robot: Robot instance for getting state and applying actions + controller: Controller instance for motion planning + task: Task instance containing scene information + cfg: Skill configuration from task YAML + """ + super().__init__() + self.robot = robot + self.controller = controller + self.task = task + self.skill_cfg = cfg + + # Get target object from config + object_name = self.skill_cfg["objects"][0] + self.target_obj = task.objects[object_name] + + # Initialize manip_list (will be filled in simple_generate_manip_cmds) + self.manip_list = [] + + # Initialize other skill-specific variables + self.process_valid = True + + def simple_generate_manip_cmds(self): + """ + Generate the manipulation command list. + + This is the MOST IMPORTANT method! It generates a list of manipulation + commands (manip_list) that define the sequence of waypoint poses and + intermediate states for the skill execution. + """ + manip_list = [] + + # ... generate commands ... + + self.manip_list = manip_list + + def is_feasible(self, th=5): + """Check if the skill is still feasible to execute.""" + return self.controller.num_plan_failed <= th + + def is_subtask_done(self, t_eps=1e-3, o_eps=5e-3): + """Check if the current waypoint is reached.""" + pass + + def is_done(self): + """Check if the entire skill is completed.""" + pass + + def is_success(self): + """Check if the skill executed successfully.""" + pass +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 + +__init__(self, robot, controller, task, cfg, *args, **kwargs) + +Initialize the skill and store all required references and configuration. + +Parameters: + +- **robot **( Robot ): Robot instance used to query state and apply actions. +- **controller **( BaseController ): Controller instance that handles motion planning and execution. +- **task **( BaseTask ): Task instance that owns scene objects and environment information. +- **cfg **( DictConfig ): Skill configuration loaded from the task YAML file. + +simple_generate_manip_cmds(self) + +This is the MOST IMPORTANT method of the skill. It constructs the full sequence of manipulation commands that defines how the robot executes this skill. + +**Command tuple format: ** +python +``` +(p_base_ee_tgt, q_base_ee_tgt, function_name, params) +``` +1 + +**Components: ** + +- **p_base_ee_tgt **( np.ndarray , shape `(3,) `): Target end-effector position in the arm base frame. +- **q_base_ee_tgt **( np.ndarray , shape `(4,) `): Target end-effector quaternion `(w, x, y, z) `in the arm base frame. +- **function_name **( str ): Name of the action function to execute. +- **params **( dict ): Keyword arguments passed to the action function. + +**Execution flow: ** + +- Controller pops commands from `manip_list `one by one. +- For each command, the target pose is passed to CuRobo for motion planning. +- The specified action function is applied using `params `during or after the motion. +- When the waypoint is reached (see `is_subtask_done `), the next command is processed. + +**Common function names: ** + +- **update_pose_cost_metric **– update planning cost and constraint weights: +python +``` +cmd = ( + p_base_ee_cur, + q_base_ee_cur, + "update_pose_cost_metric", + {"hold_vec_weight": [1, 1, 1, 0, 0, 0]}, # Hold orientation, free translation +) +manip_list.append(cmd) +``` +1 +2 +3 +4 +5 +6 +7 + +`hold_vec_weight `format: `[angular-x, angular-y, angular-z, linear-x, linear-y, linear-z] `. + +- **update_specific **– update collision-avoidance settings: +python +``` +cmd = ( + p_base_ee_cur, + q_base_ee_cur, + "update_specific", + { + "ignore_substring": ignore_substring, + "reference_prim_path": self.controller.reference_prim_path, + }, +) +manip_list.append(cmd) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +- **open_gripper **/ **close_gripper **– control gripper state: +python +``` +cmd = (p_base_ee_pregrasp, q_base_ee_pregrasp, "open_gripper", {}) +manip_list.append(cmd) + +cmd = (p_base_ee_grasp, q_base_ee_grasp, "close_gripper", {}) +manip_list.extend([cmd] * 40) # Repeat for duration +``` +1 +2 +3 +4 +5 + +- **attach_obj **/ **detach_obj **– attach or detach objects in the physics scene: +python +``` +cmd = ( + p_base_ee_grasp, + q_base_ee_grasp, + "attach_obj", + {"obj_prim_path": self.target_obj.mesh_prim_path}, +) +manip_list.append(cmd) +``` +1 +2 +3 +4 +5 +6 +7 + +- **dummy_forward **– apply actions directly without calling the planner: +python +``` +cmd = ( + p_base_ee_cur, + q_base_ee_cur, + "dummy_forward", + {"arm_action": arm_action, "gripper_state": gripper_state}, +) +manip_list.append(cmd) +``` +1 +2 +3 +4 +5 +6 +7 + +is_feasible(self, th=5) + +Check whether the skill should continue execution based on recent motion-planning failures. + +Parameters: + +- **th **( int , optional): Maximum number of allowed planning failures before the skill is considered infeasible. Default is `5 `. + +Returns: + +- **bool **: `True `if the skill is still feasible; `False `if too many failures occurred and the episode should terminate. + +Code Example: +python +``` +def is_feasible(self, th=5): + return self.controller.num_plan_failed <= th +``` +1 +2 + +Warning + +Typical reasons to return False: too many planning failures, unrecoverable robot state, or clearly unreachable target. + +is_subtask_done(self, t_eps=1e-3, o_eps=5e-3) + +Check whether the robot has reached the current waypoint defined by the first command in `manip_list `. + +Parameters: + +- **t_eps **( float , optional): Translation tolerance in meters (default: `1e-3 `, about 1 mm). +- **o_eps **( float , optional): Orientation tolerance in radians (default: `5e-3 `, about 0.3°). + +Returns: + +- **bool **: `True `if the current waypoint is considered reached; `False `otherwise. + +Code Example: +python +``` +def is_subtask_done(self, t_eps=1e-3, o_eps=5e-3): + assert len(self.manip_list) != 0 + + p_base_ee_cur, q_base_ee_cur = self.controller.get_ee_pose() + p_base_ee, q_base_ee, *_ = self.manip_list[0] + + diff_trans = np.linalg.norm(p_base_ee_cur - p_base_ee) + diff_ori = 2 * np.arccos(min(abs(np.dot(q_base_ee_cur, q_base_ee)), 1.0)) + + pose_flag = np.logical_and(diff_trans < t_eps, diff_ori < o_eps) + self.plan_flag = self.controller.num_last_cmd > 10 + + return np.logical_or(pose_flag, self.plan_flag) +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + +is_done(self) + +Determine whether the entire skill has finished executing all planned commands. + +Returns: + +- **bool **: `True `if all commands have been executed and `manip_list `is empty; `False `otherwise. + +Code Example: +python +``` +def is_done(self): + if len(self.manip_list) == 0: + return True + + t_eps = self.skill_cfg.get("t_eps", 1e-3) + o_eps = self.skill_cfg.get("o_eps", 5e-3) + + if self.is_subtask_done(t_eps=t_eps, o_eps=o_eps): + self.manip_list.pop(0) + + return len(self.manip_list) == 0 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 + +**Logic **: if the list is empty, the skill is done; otherwise, when the current waypoint is done, pop it and check again. + +is_success(self) + +Evaluate task-specific success conditions at the end of the skill. This method defines what "success" means for the given manipulation skill. + +Returns: + +- **bool **: `True `if all success conditions are satisfied; `False `otherwise. + +Code Example: +python +``` +def is_success(self): + flag = True + + # Check object contact + _, indices = self.get_contact() + if self.gripper_cmd == "close_gripper": + flag = len(indices) >= 1 + + # Check motion validity + self.process_valid = ( + np.max(np.abs(self.robot.get_joints_state().velocities)) < 5 + and np.max(np.abs(self.target_obj.get_linear_velocity())) < 5 + ) + flag = flag and self.process_valid + + return flag +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 + +Warning + +For pick skills, the object is in stable contact and lifted; for place skills, the object is near the target pose and released; for articulation skills, the articulated joints reach the desired configuration. + +## Registration [​](#registration) + +Add to `workflows/simbox/core/skills/__init__.py `: +python +``` +from core.skills.new_skill import NewSkill + +__all__ = [ + # ... existing skills + "NewSkill", +] +``` +1 +2 +3 +4 +5 +6 + +## Usage [​](#usage) +yaml +``` +skills: + - lift2: + - left: + - name: new_skill + objects: [target_object] + custom_param: 0.1 +``` +1 +2 +3 +4 +5 +6 + +## Checklist [​](#checklist) + +- [ ] Create skill file in `workflows/simbox/core/skills/ ` +- [ ] Implement `__init__ ` +- [ ] Implement `simple_generate_manip_cmds() ` +- [ ] Implement `is_feasible() ` +- [ ] Implement `is_subtask_done() ` +- [ ] Implement `is_done() ` +- [ ] Implement `is_success() ` +- [ ] Register in `__init__.py ` +- [ ] Test with task config \ No newline at end of file diff --git a/docs_crawled/custom_task.md b/docs_crawled/custom_task.md new file mode 100644 index 0000000..6a58ede --- /dev/null +++ b/docs_crawled/custom_task.md @@ -0,0 +1,673 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/custom/task.html + +# Creating a Custom Task [​](#creating-a-custom-task) + +This guide explains how to create a new task by combining your custom assets, robots, controllers, and skills into a complete task configuration. + +## Overview [​](#overview) + +After creating custom components (assets, robots, controllers, skills), you can combine them into a task YAML configuration. The workflow will load this configuration to set up the simulation environment. + +## Prerequisites [​](#prerequisites) + +Before creating a custom task, ensure you have: + +- **Custom Assets **- See [Assets Guide](/InternDataEngine-Docs/custom/assets.html) +- **Custom Robot **- See [Robot Guide](/InternDataEngine-Docs/custom/robot.html) +- **Custom Controller **- See [Controller Guide](/InternDataEngine-Docs/custom/controller.html) +- **Custom Skill **- See [Skill Guide](/InternDataEngine-Docs/custom/skill.html) + +## Prepare Asset Directory [​](#prepare-asset-directory) + +Organize your task assets following the structure: + +``` +new_task/ +└── new_objs/ # Rigid objects directory + ├── new_obj0/ # Object instance 0 + │ ├── Aligned_obj.obj # Preprocessed mesh + │ ├── Aligned.mtl # Material file + │ ├── Aligned_obj.usd # USD with physics properties + │ ├── Aligned_grasp_sparse.npy # Grasp poses (optional) + │ └── textures/ # Texture files + ├── new_obj1/ # Object instance 1 + └── new_obj2/ # Object instance 2 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 + +Place this directory under your asset root (e.g., `workflows/simbox/assets/ `or `workflows/simbox/example_assets/ `). + +Note + +For rigid manipulation objects, objects of the **same category **should be placed in the **same parent folder **(e.g., `new_obj0 `, `new_obj1 `, `new_obj2 `all under `new_objs/ `). This organization enables **category-level domain randomization **. Inside each object subfolder, use consistent naming conventions: `Aligned_obj.obj `, `Aligned_obj.usd `, `Aligned_grasp_sparse.npy `, etc. + +## Create Task YAML Configuration [​](#create-task-yaml-configuration) + +Create a new YAML file in `workflows/simbox/core/configs/tasks/ `: +yaml +``` +# ============================================================================= +# New Task Configuration +# ============================================================================= +tasks: + - name: new_task + asset_root: workflows/simbox/assets + task: BananaBaseTask + task_id: 0 + offset: null + render: True + + # ========================================================================= + # Arena Configuration + # ========================================================================= + arena_file: workflows/simbox/core/configs/arenas/example.yaml + + # ========================================================================= + # Environment Map (Lighting) + # ========================================================================= + env_map: + envmap_lib: envmap_lib + apply_randomization: False + intensity_range: [4000, 7000] + rotation_range: [0, 180] + + # ========================================================================= + # Robots + # ========================================================================= + robots: + - name: "new_robot" + robot_config_file: workflows/simbox/core/configs/robots/new_robot.yaml + euler: [0.0, 0.0, 0.0] + ignore_substring: ["material", "table", "floor"] + + # ========================================================================= + # Objects + # ========================================================================= + objects: + - name: obj0 + path: new_task/new_objs/new_obj0/Aligned_obj.usd + target_class: RigidObject + dataset: custom + category: new_object + prim_path_child: Aligned + translation: [0.3, 0.0, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [1, 1, 1] + apply_randomization: False + + - name: obj1 + path: new_task/new_objs/new_obj1/Aligned_obj.usd + target_class: RigidObject + dataset: custom + category: new_object + prim_path_child: Aligned + translation: [0.0, 0.2, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [1, 1, 1] + apply_randomization: False + + - name: obj2 + path: new_task/new_objs/new_obj2/Aligned_obj.usd + target_class: RigidObject + dataset: custom + category: new_object + prim_path_child: Aligned + translation: [0.0, -0.2, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [1, 1, 1] + apply_randomization: False + + # ========================================================================= + # Regions (Object Placement) + # ========================================================================= + regions: + - object: obj0 + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [[0.2, -0.15, 0.0], [0.4, 0.15, 0.0]] + yaw_rotation: [-30.0, 30.0] + + - object: obj1 + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [[-0.4, -0.15, 0.0], [-0.2, 0.15, 0.0]] + yaw_rotation: [-180.0, 180.0] + + - object: obj2 + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [[-0.4, -0.15, 0.0], [-0.2, 0.15, 0.0]] + yaw_rotation: [-180.0, 180.0] + + # ========================================================================= + # Cameras + # ========================================================================= + cameras: + - name: head_camera + translation: [0.5, 0.0, 0.8] + orientation: [0.924, 0.383, 0.0, 0.0] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/realsense_d455.yaml + parent: "" + apply_randomization: False + + - name: wrist_camera_left + translation: [0.0, 0.05, 0.03] + orientation: [0.0, 0.0, 0.0, 1.0] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/realsense_d435.yaml + parent: "new_robot/path/to/fl_ee_link" + apply_randomization: False + + - name: wrist_camera_right + translation: [0.0, -0.05, 0.03] + orientation: [0.0, 0.0, 0.0, 1.0] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/realsense_d435.yaml + parent: "new_robot/path/to/fr_ee_link" + apply_randomization: False + + # ========================================================================= + # Data Settings + # ========================================================================= + data: + task_dir: "new_task_demo" + language_instruction: "New task." + detailed_language_instruction: "New task." + collect_info: "Custom task with new robot and skill" + version: "v1.0" + update: True + max_episode_length: 500 + + # ========================================================================= + # Skills + # ========================================================================= + # Dual-arm mode: left arm operates obj0, right arm operates obj1 and obj2 + skills: + - new_robot: + - left: + - name: skill0 + objects: [obj0] + - right: + - name: skill1 + objects: [obj1, obj2] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 + +## Task Modes [​](#task-modes) + +The task configuration supports multiple execution modes. Here are common patterns: + +### Single-Arm Task [​](#single-arm-task) + +For single-arm robots, configure only one arm in the skills section. Skills execute sequentially — each skill starts only after the previous one completes. The task finishes when all skills are done. + +**Left Arm Example: ** +yaml +``` +skills: + - new_robot: + - left: + - name: skill0 + objects: [obj0] + - name: skill1 + objects: [obj1] + - name: skill2 + objects: [obj2] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 + +**Right Arm Example: ** +yaml +``` +skills: + - new_robot: + - right: + - name: skill0 + objects: [obj0] + - name: skill1 + objects: [obj1] + - name: skill2 + objects: [obj2] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 + +### Dual-Arm Sequential Task [​](#dual-arm-sequential-task) + +For dual-arm robots operating sequentially. In this pattern, one arm completes all its skills before the other arm begins. The example below shows the left arm executing first, followed by the right arm. +yaml +``` +# Left arm skills execute first, then right arm skills +skills: + - new_robot: + - left: + - name: skill0 + objects: [obj0] + - name: skill1 + objects: [obj1] + - name: skill2 + objects: [obj2] + - right: + - name: skill3 + objects: [obj3] + - name: skill4 + objects: [obj4] + - name: skill5 + objects: [obj5] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 + +### Dual-Arm Simultaneous Task [​](#dual-arm-simultaneous-task) + +For dual-arm robots operating simultaneously. Both arms start at the same time and execute their skills independently. Within each arm, skills still execute sequentially. The task finishes when all skills from both arms are complete. +yaml +``` +# Left and right arm skills start simultaneously +skills: + - new_robot: + - left: + - name: skill0 + objects: [obj0] + - name: skill1 + objects: [obj1] + - name: skill2 + objects: [obj2] + right: + - name: skill3 + objects: [obj3] + - name: skill4 + objects: [obj4] + - name: skill5 + objects: [obj5] +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 + +### Complex Task Example [​](#complex-task-example) + +You can freely combine single-arm, dual-arm sequential, and dual-arm simultaneous tasks in any order. This example demonstrates a tableware arrangement task: + +- **Phase 1 (Sequential) **: Right arm picks a plate, places it on the table, then returns home +- **Phase 2 (Simultaneous) **: Left arm picks and places the fork while right arm picks and places the spoon +yaml +``` +# Source: workflows/simbox/core/configs/tasks/basic/lift2/arrange_the_tableware/ +skills: + - lift2: + - right: + - name: pick + objects: [plate] + filter_z_dir: ["forward", 80] + filter_x_dir: ["downward", 130] + pre_grasp_offset: 0.05 + gripper_change_steps: 10 + ignore_substring: ["plate_shelf"] + post_grasp_offset_min: 0.075 + post_grasp_offset_max: 0.125 + - name: place + objects: [plate, table] + place_direction: vertical + filter_z_dir: ["forward", 10] + filter_y_dir: ["upward", 60, 30] + filter_x_dir: ["downward", 120, 150] + position_constraint: object + x_ratio_range: [0.5, 0.5] + y_ratio_range: [0.12, 0.20] + pre_place_z_offset: 0.15 + place_z_offset: 0.1 + post_place_vector: [-0.05, 0.0, 0.0] + success_mode: xybbox + - name: heuristic__skill + mode: home + gripper_state: 1.0 + - left: + - name: pick + objects: [fork] + filter_z_dir: ["forward", 80] + filter_x_dir: ["downward", 130] + pre_grasp_offset: 0.05 + gripper_change_steps: 10 + post_grasp_offset_min: 0.125 + post_grasp_offset_max: 0.175 + - name: place + objects: [fork, plate] + place_direction: vertical + filter_z_dir: ["forward", 20] + filter_x_dir: ["downward", 150] + x_ratio_range: [-0.25, -0.15] + y_ratio_range: [0.30, 0.70] + pre_place_z_offset: 0.175 + place_z_offset: 0.125 + success_mode: left + threshold: 0.01 + - name: heuristic__skill + mode: home + gripper_state: 1.0 + right: + - name: pick + objects: [spoon] + filter_z_dir: ["forward", 80] + filter_x_dir: ["downward", 130] + pre_grasp_offset: 0.05 + gripper_change_steps: 10 + post_grasp_offset_min: 0.125 + post_grasp_offset_max: 0.175 + - name: place + objects: [spoon, plate] + place_direction: vertical + filter_z_dir: ["forward", 20] + filter_x_dir: ["downward", 150] + x_ratio_range: [1.15, 1.25] + y_ratio_range: [0.30, 0.70] + pre_place_z_offset: 0.175 + place_z_offset: 0.125 + success_mode: right + threshold: 0.01 + - name: heuristic__skill + mode: home + gripper_state: 1.0 +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 + +## Run the Task [​](#run-the-task) + +Run the simulation with your task configuration: +bash +``` +# Plan with render mode (suitable for debugging) +bash scripts/simbox/simbox_plan_with_render.sh new_task [num_samples] [random_seed] + +# Plan and render mode +bash scripts/simbox/simbox_plan_and_render.sh new_task [num_samples] [random_seed] + +# DE pipeline mode (suitable for data generation) +bash scripts/simbox/simbox_pipe.sh new_task [num_samples] [random_seed] +``` +1 +2 +3 +4 +5 +6 +7 +8 \ No newline at end of file diff --git a/docs_crawled/guides_installation.md b/docs_crawled/guides_installation.md new file mode 100644 index 0000000..27cc101 --- /dev/null +++ b/docs_crawled/guides_installation.md @@ -0,0 +1,186 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/guides/installation.html + +# Installation [​](#installation) + +This guide walks you through setting up InternDataEngine, including cloning the repository, preparing Isaac Sim, downloading assets, and installing dependencies. Follow the steps below to get your simulation environment ready for data generation. + +## Prerequisites [​](#prerequisites) + +Before installing InternDataEngine, ensure the following requirements are met: + +| Dependency | Version | Description | +| NVIDIA Isaac Sim | 4.1.0 (recommended) / 4.2.0 / 4.5.0 | Simulation engine | +| Python | 3.10+ (recommended 3.10 or 3.11) | Runtime | +| CUDA | 11.8+ | GPU-accelerated motion planning | + +## Step 1: Clone the Repository [​](#step-1-clone-the-repository) +bash +``` +git clone https://github.com/InternRobotics/InternDataEngine.git +cd InternDataEngine +``` +1 +2 + +## Step 2: Prepare Isaac Sim [​](#step-2-prepare-isaac-sim) + +InternDataEngine relies on NVIDIA Isaac Sim for physics simulation. Please install it according to the version requirements above. Version **4.1.0 **is recommended. + +## Step 3: Download Assets [​](#step-3-download-assets) + +All assets and dependency packages are hosted on HuggingFace: [InternDataAssets](https://huggingface.co/datasets/InternRobotics/InternData-A1/tree/main/InternDataAssets) + +Three components need to be downloaded: +text +``` +InternDataAssets/ +├── assets/ # Scene and task assets +├── curobo/ # CuRobo motion planning library +└── panda_drake/ # Drake kinematics library +``` +1 +2 +3 +4 + +### 3.1 Scene Assets [​](#_3-1-scene-assets) + +**Required assets **(needed for all tasks): + +- `background_textures `, `envmap_lib `, `floor_textures `, `table_textures `— Domain Randomization materials +- `table0 `, `table_info.json `— Table scene + +> + +The Randomization material directories contain many files. For quick testing, you can download just a few samples from each directory. + +**Robot models **(choose as needed): + +- `lift2 `, `franka `, `frankarobotiq `, `split_aloha_mid_360 `, `G1_120s ` + +**Task assets **(choose as needed): + +- `basic `, `art `, `long_horizon `, `pick_and_place ` + +We provide a download script at `scripts/download_assets.sh `for selective downloading: +bash +``` +bash scripts/download_assets.sh [OPTIONS] +``` +1 + +| Option | Description | +| `--min ` | Download only required scene assets (for quick testing) | +| `--full ` | Download all scene assets including all robots and tasks (default) | +| `--with-curobo ` | Also download the CuRobo package | +| `--with-drake ` | Also download the panda_drake package | +| `--local-dir DIR ` | Specify download directory (default: current directory) | +bash +``` +# Minimum assets + CuRobo + panda_drake +bash scripts/download_assets.sh --min --with-curobo --with-drake + +# Full download (~200GB) +bash scripts/download_assets.sh --full --with-curobo --with-drake +``` +1 +2 +3 +4 +5 + +Hint + +The full asset collection is large, so download selectively based on your needs. Because the background, floor, and table textures contain many images for domain randomization, if your download speed is slow or disk space is limited, you can download only a small subset (e.g., 10+ images per directory) for debugging. + +### 3.2 CuRobo [​](#_3-2-curobo) + +If you already have CuRobo installed and don't need to download it via the script above, simply merge the contents of ``[curobo/src/curobo/content](https://huggingface.co/datasets/InternRobotics/InternData-A1/tree/main/InternDataAssets/curobo/src/curobo/content)from HuggingFace into your local `YOUR_CUROBO_PATH/src/curobo/content `(contains robot URDFs and CuRobo configuration files). + +If CuRobo is not yet installed, you can also get the source from [NVlabs/curobo] ( [https://github.com/NVlabs/curobo](https://github.com/NVlabs/curobo)). + +## Step 4: Create Symlinks [​](#step-4-create-symlinks) +bash +``` +cd InternDataEngine/workflows/simbox +ln -s YOUR_PATH_TO_ASSETS assets +ln -s YOUR_PATH_TO_CUROBO curobo +ln -s YOUR_PATH_TO_PANDA_DRAKE panda_drake +``` +1 +2 +3 +4 + +## Step 5: Install Python Dependencies [​](#step-5-install-python-dependencies) + +### Option A: Using Isaac Sim's Built-in Python [​](#option-a-using-isaac-sim-s-built-in-python) +bash +``` +cd InternDataEngine +YOUR_PATH_TO_ISAACSIM/python.sh -m pip install --upgrade pip +YOUR_PATH_TO_ISAACSIM/python.sh -m pip install -r requirements.txt +``` +1 +2 +3 + +### Option B: Using Conda [​](#option-b-using-conda) +bash +``` +conda create -n banana python=3.10 +conda activate banana +source YOUR_PATH_TO_ISAACSIM/setup_conda_env.sh +cd InternDataEngine +pip install --upgrade pip +pip install -r requirements.txt +``` +1 +2 +3 +4 +5 +6 + +> + +**Note **: The only difference between the two options is the Python launch command — Option A uses `YOUR_PATH_TO_ISAACSIM/python.sh `, while Option B uses `python `. This documentation uses `YOUR_PATH_TO_ISAACSIM/python.sh `throughout for consistency. + +## Step 6: Install CuRobo [​](#step-6-install-curobo) + +Refer to the [CuRobo installation docs](https://curobo.org/get_started/1_install_instructions.html), specifically the **Install for use in Isaac Sim **section, then run: +bash +``` +cd InternDataEngine/workflows/simbox/curobo +YOUR_PATH_TO_ISAACSIM/python.sh -m pip install -e .[isaacsim] --no-build-isolation +``` +1 +2 + +## Troubleshooting [​](#troubleshooting) + +### Isaac Sim Not Found [​](#isaac-sim-not-found) + +Ensure the `ISAAC_SIM_PATH `environment variable is set correctly: +bash +``` +export ISAAC_SIM_PATH=/path/to/isaac-sim +``` +1 + +### CUDA Out of Memory [​](#cuda-out-of-memory) + +Reduce the batch size in the configuration, or use a GPU with more VRAM. CuRobo motion planning benefits from larger GPU memory. + +### Import Errors [​](#import-errors) + +Ensure all symlinks are set up correctly: +bash +``` +ls -la workflows/simbox/assets +ls -la workflows/simbox/curobo +ls -la workflows/simbox/panda_drake +``` +1 +2 +3 \ No newline at end of file diff --git a/docs_crawled/guides_quickstart.md b/docs_crawled/guides_quickstart.md new file mode 100644 index 0000000..cb5c115 --- /dev/null +++ b/docs_crawled/guides_quickstart.md @@ -0,0 +1,261 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/guides/quickstart.html + +# Quick Start [​](#quick-start) + +This guide walks you through running your first InternDataEngine data generation task and understanding the core configuration concepts. + +## Running Your First Task [​](#running-your-first-task) + +The quickest way to start: +bash +``` +/isaac-sim/python.sh launcher.py \ + --config configs/simbox/de_plan_with_render_template.yaml +``` +1 +2 + +You can also use the wrapper script (same effect, simpler arguments): +bash +``` +bash scripts/simbox/simbox_plan_with_render.sh [num_samples] [random_seed] + +# Example +bash scripts/simbox/simbox_plan_with_render.sh \ + workflows/simbox/core/configs/tasks/basic/split_aloha/track_the_targets/track_the_targets.yaml 10 +``` +1 +2 +3 +4 +5 + +> + +See the script file for more usage details. + +### Command Line Arguments [​](#command-line-arguments) + +| Argument | Description | +| `--config ` | **Required **. Path to the execution config file | +| `--random_seed ` | Optional. Fix random seed for reproducibility | +| `--debug ` | Optional. Debug mode — exceptions are raised immediately | + +Any field in the config file can be overridden via the command line using dot-separated paths: +bash +``` +--load_stage.layout_random_generator.args.random_num=500 +--load_stage.scene_loader.args.simulator.headless=false +``` +1 +2 + +This is equivalent to modifying the config file directly. + +## Execution Modes [​](#execution-modes) + +The engine provides multiple execution modes. Switch between them by changing the config file passed to `--config `: + +| Config File | Mode | Description | +| `de_plan_with_render_template.yaml ` | Plan + Render simultaneous | Simplest; good for debugging. Required for fluid tasks | +| `de_pipe_template.yaml ` | Plan / Render pipelined | Best performance (memory-dependent); ideal for large-scale production | +| `de_plan_and_render_template.yaml ` | Plan and Render sequential | For serial debugging of the Pipe mode | +| `de_plan_template.yaml ` | Planning only | Generate trajectories without rendering | +| `de_render_template.yaml ` | Rendering only | Render images from existing trajectories; re-render with different backgrounds/materials | + +**Recommended **: Use `de_plan_with_render_template.yaml `or `de_plan_and_render_template.yaml `during development, and `de_pipe_template.yaml `for production. Due to potential interference between Isaac Sim multi-process instances, it is recommended to run in containerized environments — e.g., launch multiple single-GPU containers on a cluster to execute the pipeline script. +bash +``` +# Debug mode +/isaac-sim/python.sh launcher.py --config configs/simbox/de_plan_with_render_template.yaml + +# Production mode +/isaac-sim/python.sh launcher.py --config configs/simbox/de_pipe_template.yaml +``` +1 +2 +3 +4 +5 + +## Understanding the Configuration [​](#understanding-the-configuration) + +The engine uses two types of config files: + +- **Execution configs **( `configs/simbox/de_*.yaml `): Define the pipeline execution mode and stage orchestration +- **Task configs **( `workflows/simbox/core/configs/tasks/... `): Define the specific task — robots, objects, skills, etc. + +### Execution Config Details [​](#execution-config-details) + +The data pipeline consists of four stages: **Load **-> **Plan **-> **Render **-> **Store **. + +Using `de_plan_and_render_template.yaml `as an example: +yaml +``` +load_stage: + scene_loader: # Scene loader + type: env_loader + args: + workflow_type: SimBoxDualWorkFlow + cfg_path: workflows/simbox/core/configs/tasks/... # Task config path + simulator: + physics_dt: 1/30 # Physics update rate + rendering_dt: 1/30 # Render update rate + headless: True # Headless mode (no GUI) + anti_aliasing: 0 # Anti-aliasing level + layout_random_generator: # Scene randomization + type: env_randomizer + args: + random_num: 5 # Number of random samples + strict_mode: true # true: output count must equal random_num + +plan_stage: + seq_planner: + type: env_planner # Trajectory planner + +render_stage: + renderer: + type: env_renderer # Renderer + +store_stage: + writer: + type: env_writer + args: + batch_async: true # Async writes (better perf, more memory) + output_dir: output/${name}/ # Output directory +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 + +### Task Config [​](#task-config) + +A task config defines the complete scene for a data generation run: + +| Field | Description | +| robots | Robot model and parameters | +| objects | Interactive objects in the scene | +| camera | Viewpoint and image capture settings | +| skills | Manipulation skill sequence to execute | +| arena | Object placement regions and scene layout | + +## Example Tasks [​](#example-tasks) + +### Pick and Place [​](#pick-and-place) +bash +``` +/isaac-sim/python.sh launcher.py \ + --config configs/simbox/de_plan_with_render_template.yaml \ + --load_stage.scene_loader.args.cfg_path=workflows/simbox/core/configs/tasks/basic/lift2/insert_the_markpen_in_penholder/left/insert_the_markpen_in_penholder_part0.yaml +``` +1 +2 +3 + +### Pouring Task [​](#pouring-task) +bash +``` +/isaac-sim/python.sh launcher.py \ + --config configs/simbox/de_plan_with_render_template.yaml \ + --load_stage.scene_loader.args.cfg_path=workflows/simbox/core/configs/tasks/basic/lift2/pour_redwine_left.yaml +``` +1 +2 +3 + +### Long-Horizon Task [​](#long-horizon-task) +bash +``` +/isaac-sim/python.sh launcher.py \ + --config configs/simbox/de_plan_with_render_template.yaml \ + --load_stage.scene_loader.args.cfg_path=workflows/simbox/core/configs/tasks/long_horizon/lift2/dexpnp/sort_parts_0.yaml +``` +1 +2 +3 + +## Output Structure [​](#output-structure) + +Output is saved to the path specified in `store_stage.writer.args.output_dir `: +text +``` +output_dir/ +├── / +│ └── / +│ └── / +│ └── / +│ ├── / +│ │ ├── images.rgb.head/ +│ │ ├── images.rgb.hand_right/ +│ │ ├── images.rgb.hand_left/ +│ │ └── lmdb/ +│ └── / +│ └── ... +├── de_config.yaml # Copy of the execution config +├── de_time_profile_*.log # Execution time statistics +├── de_p*_w*_time_profile*.log # (Pipe mode) Per-process timing +└── de_supervisor_p*_w*.log # (Pipe mode) Process monitor logs +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 + +## Debugging Tips [​](#debugging-tips) + +### Enable Visualization [​](#enable-visualization) + +Disable headless mode to show the simulation GUI: +bash +``` +--load_stage.scene_loader.args.simulator.headless=false +``` +1 + +### Common Issues [​](#common-issues) + +| Issue | Troubleshooting | +| Scene setup errors | Check `set_up_scene() `in `workflows/simbox/core/tasks/banana.py ` | +| Reset failures | Check `reset() `in `workflows/simbox_dual_workflow.py ` | +| Motion planning failures | Check robot config in task YAML, object collision settings, `ignore_substring `list | +| UnicodeDecodeError | Set `export PYTHONUTF8=1 ` | \ No newline at end of file diff --git a/docs_crawled/policy_training.md b/docs_crawled/policy_training.md new file mode 100644 index 0000000..a21889e --- /dev/null +++ b/docs_crawled/policy_training.md @@ -0,0 +1,83 @@ +# Source: https://internrobotics.github.io/InternDataEngine-Docs/policy/training.html + +# Training [​](#training) + +This guide covers data format conversion and policy training for validating generated simulation data. + +## Part 1: LMDB to LeRobot Data Conversion [​](#part-1-lmdb-to-lerobot-data-conversion) + +The simulation data generated by InternDataEngine is stored in LMDB format. To use this data for policy training, you need to convert it to LeRobot format. + +### Step 1: Install LeRobot v2.1 [​](#step-1-install-lerobot-v2-1) + +We use LeRobot v2.1 format for data storage. Install the LeRobot 2.1 repo. + +### Step 2: Convert LMDB to LeRobot v2.1 [​](#step-2-convert-lmdb-to-lerobot-v2-1) + +Use the conversion scripts in ``[policy/lmdb2lerobotv21](https://github.com/InternRobotics/InternDataEngine/tree/master/policy/lmdb2lerobotv21)directory. + +We provide conversion scripts for different robot platforms: + +- **lmdb2lerobot_lift2_a1.py **( script ): Lift2 (ARX). +- **lmdb2lerobot_split_aloha_a1.py **( script ): Split Aloha. +- **lmdb2lerobot_genie1_a1.py **( script ): Genie1. +- **lmdb2lerobot_franka_a1.py **( script ): Franka FR3. +- **lmdb2lerobot_frankarobotiq_a1.py **( script ): Franka with Robotiq gripper. + +Example usage: +bash +``` +python lmdb2lerobot_lift2_a1.py \ + --src_path ${src_path} \ + --save_path ${save_path} \ + --repo_id ${repo_id} \ + --num-threads ${num_threads} \ + --num_demos ${num_demos} +``` +1 +2 +3 +4 +5 +6 + +**Parameters: ** + +- **--src_path **( str ): Path to the source LMDB data directory. +- **--save_path **( str ): Path to save the converted LeRobot dataset. +- **--repo_id **( str ): Dataset repository identifier. +- **--num-threads **( int ): Number of threads for parallel processing. +- **--num_demos **( int ): Number of demonstrations to convert (optional). + +### Step 3: Convert to LeRobot v3.0 (Optional) [​](#step-3-convert-to-lerobot-v3-0-optional) + +If you need LeRobot v3.0 format for training, please install LeRobot 3.0. Then use the conversion script: +bash +``` +python convertv21_to_v30.py --input_path ${v21_path} --output_path ${v30_path} +``` +1 + +The conversion code is available at ``[policy/lmdb2lerobotv21/convertv21_to_v30.py](https://github.com/InternRobotics/InternDataEngine/tree/master/policy/lmdb2lerobotv21/convertv21_to_v30.py). + +## Part 2: Policy Training with π 0 [​](#part-2-policy-training-with-π0) + +As described in the [InternData-A1 paper](https://arxiv.org/pdf/2511.16651), we used multi-machine, multi-GPU JAX-based π 0 for data validation. + +We have implemented a JAX-based, multi-nodes, multi-GPU training pipeline that supports multi-dataset mixed training for π 0 . + +### Features [​](#features) + +- **Multi-machine, multi-GPU training **: Scale training across multiple nodes +- **Multi-dataset mixed training **: Train on multiple datasets simultaneously +- **JAX-based implementation **: High-performance training with JAX/Flax + +### Installation, Training, and Deployment [​](#installation-training-and-deployment) + +For detailed instructions on installation, training, and deployment, please refer to the [openpi-InternData-A1 README](https://github.com/InternRobotics/InternDataEngine/blob/master/policy/openpi-InternData-A1/README.md). + +## References [​](#references) + +- [LeRobot](https://github.com/huggingface/lerobot)- HuggingFace LeRobot +- [InternData-A1 Paper](https://arxiv.org/pdf/2511.16651)- InternData-A1: A High-Fidelity Synthetic Data Generator for Robotic Manipulation +- [openpi-InternData-A1](https://github.com/InternRobotics/InternDataEngine/blob/master/policy/openpi-InternData-A1/)- JAX-based π 0 training code \ No newline at end of file diff --git a/migrate/crawl_docs.py b/migrate/crawl_docs.py new file mode 100644 index 0000000..129a832 --- /dev/null +++ b/migrate/crawl_docs.py @@ -0,0 +1,328 @@ +""" +Crawl InternDataEngine online docs and save as local markdown files. + +Usage: + python migrate/crawl_docs.py + python migrate/crawl_docs.py --output docs_crawled +""" +import argparse +import os +import re +import time +import urllib.request +from html.parser import HTMLParser + + +BASE_URL = "https://internrobotics.github.io/InternDataEngine-Docs" + +# All pages from the sitemap (extracted from VitePress hash map) +PAGES = [ + # Getting Started + "/guides/installation.html", + "/guides/quickstart.html", + # Core Concepts + "/concepts/workflows.html", + "/concepts/skills.html", + "/concepts/skills/overview.html", + "/concepts/skills/pick.html", + "/concepts/skills/place.html", + "/concepts/skills/articulation.html", + "/concepts/objects.html", + "/concepts/cameras.html", + "/concepts/robots.html", + "/concepts/controllers.html", + # Configuration + "/config/yaml.html", + "/config/dr.html", + "/config/assets.html", + # Customization + "/custom/assets.html", + "/custom/robot.html", + "/custom/controller.html", + "/custom/skill.html", + "/custom/task.html", + # Policy + "/policy/training.html", + # API + "/api/controllers.html", + "/api/skills.html", + # Chinese versions + "/zh/guides/installation.html", + "/zh/guides/quickstart.html", + "/zh/concepts/workflows.html", + "/zh/concepts/skills.html", + "/zh/concepts/skills/overview.html", + "/zh/concepts/skills/pick.html", + "/zh/concepts/skills/place.html", + "/zh/concepts/skills/articulation.html", + "/zh/concepts/tasks.html", + "/zh/concepts/cameras.html", + "/zh/concepts/robots.html", + "/zh/concepts/controllers.html", + "/zh/config/yaml.html", + "/zh/config/dr.html", + "/zh/config/assets.html", + "/zh/custom/assets.html", + "/zh/custom/robot.html", + "/zh/custom/controller.html", + "/zh/custom/skill.html", + "/zh/custom/task.html", +] + + +class HTMLToMarkdown(HTMLParser): + """Simple HTML to Markdown converter for VitePress content.""" + + def __init__(self): + super().__init__() + self.output = [] + self.current_tag = None + self.in_content = False + self.in_code = False + self.code_lang = "" + self.skip_tags = {"script", "style", "nav", "header", "footer", "button"} + self.skip_depth = 0 + self.heading_level = 0 + self.in_li = False + self.in_pre = False + self.in_a = False + self.a_href = "" + self.a_text = "" + + def handle_starttag(self, tag, attrs): + attrs_dict = dict(attrs) + classes = attrs_dict.get("class", "") + + # Skip navigation, header, footer + if tag in self.skip_tags: + self.skip_depth += 1 + return + if self.skip_depth > 0: + return + + # Track content area + if "vp-doc" in classes or "VPDoc" in classes: + self.in_content = True + + if not self.in_content: + return + + if tag in ("h1", "h2", "h3", "h4", "h5", "h6"): + self.heading_level = int(tag[1]) + self.output.append("\n" + "#" * self.heading_level + " ") + elif tag == "p": + self.output.append("\n\n") + elif tag == "pre": + self.in_pre = True + elif tag == "code": + if self.in_pre: + self.in_code = True + lang = attrs_dict.get("class", "") + lang_match = re.search(r"language-(\w+)", lang) + self.code_lang = lang_match.group(1) if lang_match else "" + self.output.append(f"\n```{self.code_lang}\n") + else: + self.output.append("`") + elif tag == "a": + self.in_a = True + self.a_href = attrs_dict.get("href", "") + self.a_text = "" + elif tag == "ul": + self.output.append("\n") + elif tag == "ol": + self.output.append("\n") + elif tag == "li": + self.in_li = True + self.output.append("- ") + elif tag == "strong" or tag == "b": + self.output.append("**") + elif tag == "em" or tag == "i": + self.output.append("*") + elif tag == "br": + self.output.append("\n") + elif tag == "img": + alt = attrs_dict.get("alt", "") + src = attrs_dict.get("src", "") + self.output.append(f"![{alt}]({src})") + elif tag == "table": + self.output.append("\n") + elif tag == "tr": + self.output.append("| ") + elif tag == "th" or tag == "td": + pass + elif tag == "blockquote": + self.output.append("\n> ") + + def handle_endtag(self, tag): + if tag in self.skip_tags: + self.skip_depth -= 1 + return + if self.skip_depth > 0: + return + if not self.in_content: + return + + if tag in ("h1", "h2", "h3", "h4", "h5", "h6"): + self.output.append("\n") + self.heading_level = 0 + elif tag == "pre": + self.in_pre = False + elif tag == "code": + if self.in_code: + self.in_code = False + self.output.append("\n```\n") + else: + self.output.append("`") + elif tag == "a": + self.in_a = False + if self.a_href and self.a_text: + self.output.append(f"[{self.a_text.strip()}]({self.a_href})") + self.a_text = "" + elif tag == "li": + self.in_li = False + self.output.append("\n") + elif tag == "strong" or tag == "b": + self.output.append("**") + elif tag == "em" or tag == "i": + self.output.append("*") + elif tag == "tr": + self.output.append("\n") + elif tag == "th" or tag == "td": + self.output.append(" | ") + elif tag == "p": + self.output.append("\n") + + def handle_data(self, data): + if self.skip_depth > 0: + return + + if self.in_a: + self.a_text += data + return + + if self.in_content: + if self.in_code: + self.output.append(data) + else: + text = data.strip() + if text: + self.output.append(text + " ") + + def get_markdown(self): + text = "".join(self.output) + # Clean up + text = re.sub(r"\n{3,}", "\n\n", text) + text = re.sub(r"[ \t]+\n", "\n", text) + return text.strip() + + +def fetch_page(url): + """Fetch a page and return HTML content.""" + try: + req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + with urllib.request.urlopen(req, timeout=15) as resp: + return resp.read().decode("utf-8") + except Exception as e: + print(f" ERROR: {e}") + return None + + +def html_to_markdown(html_content): + """Convert HTML to markdown.""" + parser = HTMLToMarkdown() + parser.feed(html_content) + return parser.get_markdown() + + +def main(): + parser = argparse.ArgumentParser(description="Crawl InternDataEngine docs") + parser.add_argument("--output", default="docs_crawled", help="Output directory") + parser.add_argument("--zh-only", action="store_true", help="Only crawl Chinese docs") + parser.add_argument("--en-only", action="store_true", help="Only crawl English docs") + args = parser.parse_args() + + os.makedirs(args.output, exist_ok=True) + + pages = PAGES + if args.zh_only: + pages = [p for p in PAGES if p.startswith("/zh/")] + elif args.en_only: + pages = [p for p in PAGES if not p.startswith("/zh/")] + + print(f"Crawling {len(pages)} pages to {args.output}/\n") + + for page_path in pages: + url = BASE_URL + page_path + # Convert path to filename: /guides/installation.html -> guides_installation.md + md_name = page_path.strip("/").replace("/", "_").replace(".html", ".md") + md_path = os.path.join(args.output, md_name) + + print(f" {page_path} -> {md_name}", end=" ") + + html = fetch_page(url) + if html is None: + print("FAILED") + continue + + md = html_to_markdown(html) + if not md or len(md) < 50: + # VitePress SPA - content is loaded via JS, try fetching the raw .md source + # VitePress stores source at /InternDataEngine-Docs/page.html but the actual + # markdown might be accessible differently + print(f"(sparse content: {len(md)} chars, SPA rendering)") + else: + print(f"OK ({len(md)} chars)") + + with open(md_path, "w") as f: + f.write(f"# Source: {url}\n\n") + f.write(md) + + time.sleep(0.3) + + # Also try fetching raw markdown from GitHub + print("\n\nAttempting raw markdown from GitHub...") + gh_base = "https://raw.githubusercontent.com/InternRobotics/InternDataEngine/master/docs" + # VitePress source files + raw_pages = { + "guides/installation.md": "guides_installation_raw.md", + "guides/quickstart.md": "guides_quickstart_raw.md", + "concepts/workflows.md": "concepts_workflows_raw.md", + "concepts/objects.md": "concepts_objects_raw.md", + "concepts/cameras.md": "concepts_cameras_raw.md", + "concepts/robots.md": "concepts_robots_raw.md", + "concepts/controllers.md": "concepts_controllers_raw.md", + "concepts/skills/overview.md": "concepts_skills_overview_raw.md", + "concepts/skills/pick.md": "concepts_skills_pick_raw.md", + "concepts/skills/place.md": "concepts_skills_place_raw.md", + "concepts/skills/articulation.md": "concepts_skills_articulation_raw.md", + "config/yaml.md": "config_yaml_raw.md", + "config/dr.md": "config_dr_raw.md", + "config/assets.md": "config_assets_raw.md", + "custom/assets.md": "custom_assets_raw.md", + "custom/robot.md": "custom_robot_raw.md", + "custom/controller.md": "custom_controller_raw.md", + "custom/skill.md": "custom_skill_raw.md", + "custom/task.md": "custom_task_raw.md", + "policy/training.md": "policy_training_raw.md", + "api/controllers.md": "api_controllers_raw.md", + "api/skills.md": "api_skills_raw.md", + } + + for src, dst in raw_pages.items(): + url = f"{gh_base}/{src}" + dst_path = os.path.join(args.output, dst) + print(f" {src}", end=" ") + content = fetch_page(url) + if content and len(content) > 50 and not content.strip().startswith("/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 标注 | diff --git a/migrate/fix_test_tube_materials.py b/migrate/fix_test_tube_materials.py new file mode 100644 index 0000000..4308942 --- /dev/null +++ b/migrate/fix_test_tube_materials.py @@ -0,0 +1,47 @@ +""" +Fix material bindings on repackaged test tube USD. + +The referenced meshes have bindings pointing to /Test_Tube_AA_01/Looks/... +which is outside the reference scope. This script overrides them to +point to /World/Looks/... instead. + +Usage: + python migrate/fix_test_tube_materials.py +""" +from pxr import Usd, UsdShade + +USD_PATH = "workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.usd" + +# Mapping: mesh prim path -> material path in new structure +MATERIAL_BINDINGS = { + "/World/Aligned/_______005": "/World/Looks/SimPBR_Translucent", + "/World/Aligned/tags/_______007": "/World/Looks/OmniPBR", + "/World/Aligned/Test_Tube_lid/_______006": "/World/Looks/OmniPBR_01", +} + + +def fix(): + stage = Usd.Stage.Open(USD_PATH) + + for mesh_path, mat_path in MATERIAL_BINDINGS.items(): + mesh_prim = stage.GetPrimAtPath(mesh_path) + mat_prim = stage.GetPrimAtPath(mat_path) + + if not mesh_prim.IsValid(): + print(f" SKIP: {mesh_path} not found") + continue + if not mat_prim.IsValid(): + print(f" SKIP: material {mat_path} not found") + continue + + mat = UsdShade.Material(mat_prim) + UsdShade.MaterialBindingAPI.Apply(mesh_prim) + UsdShade.MaterialBindingAPI(mesh_prim).Bind(mat) + print(f" Bound {mesh_path} -> {mat_path}") + + stage.GetRootLayer().Save() + print(f"\nSaved: {USD_PATH}") + + +if __name__ == "__main__": + fix() diff --git a/migrate/gen_tube_grasp.py b/migrate/gen_tube_grasp.py new file mode 100644 index 0000000..fccfbe1 --- /dev/null +++ b/migrate/gen_tube_grasp.py @@ -0,0 +1,51 @@ +""" +Generate test tube grasp annotations by adapting working bottle grasps. + +Strategy: Load verified working bottle grasps, scale positions to match +test tube dimensions. The rotation conventions are already correct. + +Bottle: ~5cm radius, ~10cm height +Test tube: ~1.5cm radius, ~10.6cm height (similar height, 3x smaller radius) + +Usage: + python migrate/gen_tube_grasp.py +""" +import numpy as np +import os + +BOTTLE_GRASP = "workflows/simbox/example_assets/task/sort_the_rubbish/recyclable_garbage/bottle_0/Aligned_grasp_sparse.npy" +OUTPUT = "workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_sparse.npy" + + +def main(): + # Load working bottle grasps + bottle = np.load(BOTTLE_GRASP) + print(f"Loaded {len(bottle)} bottle grasps") + print(f"Bottle pos range: X[{bottle[:,13].min():.4f},{bottle[:,13].max():.4f}] " + f"Y[{bottle[:,14].min():.4f},{bottle[:,14].max():.4f}] " + f"Z[{bottle[:,15].min():.4f},{bottle[:,15].max():.4f}]") + + tube = bottle.copy() + + # Scale XY positions (radius: bottle ~3cm -> tube ~1.5cm, factor ~0.5) + # Keep Z positions similar (both ~10cm height) + xy_scale = 0.5 + tube[:, 13] *= xy_scale # X position + tube[:, 14] *= xy_scale # Y position + # Z positions stay the same (similar height) + + # Reduce gripper width for smaller object + # Bottle width: 0.044-0.10, Tube needs: 0.02-0.06 + tube[:, 1] = np.clip(tube[:, 1] * 0.6, 0.02, 0.06) + + print(f"\nTube pos range: X[{tube[:,13].min():.4f},{tube[:,13].max():.4f}] " + f"Y[{tube[:,14].min():.4f},{tube[:,14].max():.4f}] " + f"Z[{tube[:,15].min():.4f},{tube[:,15].max():.4f}]") + print(f"Tube width range: [{tube[:,1].min():.3f},{tube[:,1].max():.3f}]") + + np.save(OUTPUT, tube) + print(f"\nSaved {len(tube)} grasps to: {OUTPUT}") + + +if __name__ == "__main__": + main() diff --git a/migrate/repackage_test_tube.py b/migrate/repackage_test_tube.py new file mode 100644 index 0000000..e14e57d --- /dev/null +++ b/migrate/repackage_test_tube.py @@ -0,0 +1,140 @@ +""" +Repackage test tube USD to match InternDataEngine's expected format. + +Target structure (same as Aligned_obj.usd): + /World (Xform, defaultPrim, NO physics schemas) + /Looks (Scope) - materials referenced from source + /Aligned (Xform) - PhysicsRigidBodyAPI, PhysicsMassAPI + /mesh (first child mesh, PhysicsCollisionAPI) + /PhysicsMaterial (Material, PhysicsMaterialAPI) + +Strategy: + - /World is a clean Xform (no reference, no inherited schemas) + - /World/Aligned references /Test_Tube_AA_01/Test_Tube from source + - /World/Looks references /Test_Tube_AA_01/Looks from source + - Physics applied as overrides on /World/Aligned + +Usage: + python migrate/repackage_test_tube.py +""" +from pxr import Usd, UsdGeom, UsdPhysics, UsdShade, Sdf, Gf +import os + +SRC_PATH = os.path.abspath( + "/home/tangger/LYT/maic_usd_assets_moudle/laboratory_equipment/Test_Tube/Test_Tube_AA_01.usdc" +) +DST_DIR = "workflows/simbox/example_assets/task/pick_test_tube/test_tube" +DST_PATH = os.path.join(DST_DIR, "Aligned_obj.usd") + + +def repackage(): + # Read source to understand structure + src_stage = Usd.Stage.Open(SRC_PATH) + src_dp = src_stage.GetDefaultPrim() + print(f"Source: {SRC_PATH}") + print(f"Source defaultPrim: {src_dp.GetPath()}") + + # Remove old output + if os.path.exists(DST_PATH): + os.remove(DST_PATH) + + # Create new stage + dst_stage = Usd.Stage.CreateNew(DST_PATH) + UsdGeom.SetStageMetersPerUnit(dst_stage, 1.0) + UsdGeom.SetStageUpAxis(dst_stage, UsdGeom.Tokens.z) + + # --- /World: clean Xform, no reference, no inherited schemas --- + world_xform = UsdGeom.Xform.Define(dst_stage, "/World") + dst_stage.SetDefaultPrim(world_xform.GetPrim()) + + # --- /World/Aligned: reference Test_Tube child from source --- + aligned_xform = UsdGeom.Xform.Define(dst_stage, "/World/Aligned") + aligned_prim = aligned_xform.GetPrim() + aligned_prim.GetReferences().AddReference( + SRC_PATH, + f"{src_dp.GetPath()}/Test_Tube" + ) + # Add RigidBody physics + UsdPhysics.RigidBodyAPI.Apply(aligned_prim) + mass_api = UsdPhysics.MassAPI.Apply(aligned_prim) + mass_api.GetMassAttr().Set(0.005) + print("Created /World/Aligned with RigidBodyAPI + MassAPI (0.005 kg)") + + # Set collision approximation on mesh children + for child_prim in aligned_prim.GetAllChildren(): + if child_prim.HasAPI(UsdPhysics.CollisionAPI): + mesh_api = UsdPhysics.MeshCollisionAPI(child_prim) + if mesh_api: + mesh_api.GetApproximationAttr().Set("convexHull") + print(f" Set convexHull on {child_prim.GetPath()}") + + # --- /World/Looks: reference Looks from source --- + looks_prim = dst_stage.DefinePrim("/World/Looks", "Scope") + looks_prim.GetReferences().AddReference( + SRC_PATH, + f"{src_dp.GetPath()}/Looks" + ) + print("Created /World/Looks referencing source materials") + + # --- Fix material bindings: remap from source paths to new paths --- + # Source materials are at /Test_Tube_AA_01/Looks/XXX + # In our new stage they are at /World/Looks/XXX + # The mesh bindings from the reference point to /Test_Tube_AA_01/Looks/XXX + # which is outside scope. We need to override the bindings. + src_material_map = { + "SimPBR_Translucent": "/World/Looks/SimPBR_Translucent", + "OmniPBR": "/World/Looks/OmniPBR", + "OmniPBR_01": "/World/Looks/OmniPBR_01", + } + + # Find all mesh prims under /World/Aligned and rebind materials + for prim in dst_stage.Traverse(): + if not str(prim.GetPath()).startswith("/World/Aligned"): + continue + binding = UsdShade.MaterialBindingAPI(prim) + if not binding: + continue + # Check if there's a direct binding + mat_path_rel = binding.GetDirectBinding().GetMaterialPath() + if mat_path_rel and str(mat_path_rel) != "": + mat_name = str(mat_path_rel).split("/")[-1] + if mat_name in src_material_map: + new_mat_path = src_material_map[mat_name] + new_mat = UsdShade.Material(dst_stage.GetPrimAtPath(new_mat_path)) + if new_mat: + UsdShade.MaterialBindingAPI.Apply(prim) + UsdShade.MaterialBindingAPI(prim).Bind(new_mat) + print(f" Rebound material on {prim.GetPath()} -> {new_mat_path}") + + # --- /World/PhysicsMaterial --- + phys_mat_prim = dst_stage.DefinePrim("/World/PhysicsMaterial", "Material") + UsdPhysics.MaterialAPI.Apply(phys_mat_prim) + phys_mat = UsdPhysics.MaterialAPI(phys_mat_prim) + phys_mat.GetStaticFrictionAttr().Set(0.5) + phys_mat.GetDynamicFrictionAttr().Set(0.5) + phys_mat.GetRestitutionAttr().Set(0.1) + print("Created /World/PhysicsMaterial") + + # Save + dst_stage.GetRootLayer().Save() + print(f"\nSaved: {DST_PATH}") + + # --- Verify --- + print(f"\n{'='*60}") + print("Verification:") + print(f"{'='*60}") + v_stage = Usd.Stage.Open(DST_PATH) + dp = v_stage.GetDefaultPrim() + print(f"DefaultPrim: {dp.GetPath()}") + print(f" Type: {dp.GetTypeName()}") + print(f" Schemas: {dp.GetAppliedSchemas()}") + for c in dp.GetChildren(): + print(f" /{c.GetName()} [{c.GetTypeName()}] schemas={c.GetAppliedSchemas()}") + for gc in c.GetAllChildren(): + schemas = gc.GetAppliedSchemas() + s = f" schemas={schemas}" if schemas else "" + print(f" /{gc.GetName()} [{gc.GetTypeName()}]{s}") + + +if __name__ == "__main__": + repackage() diff --git a/migrate/usdc_to_obj.py b/migrate/usdc_to_obj.py new file mode 100644 index 0000000..a4c1912 --- /dev/null +++ b/migrate/usdc_to_obj.py @@ -0,0 +1,64 @@ +""" +Extract mesh from USD/USDC and export as OBJ file. + +Usage: + python migrate/usdc_to_obj.py --input path/to/file.usdc --output path/to/output/ +""" +import argparse +import os +import numpy as np +from pxr import Usd, UsdGeom + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--input", required=True) + parser.add_argument("--output", default=".") + args = parser.parse_args() + + os.makedirs(args.output, exist_ok=True) + obj_path = os.path.join(args.output, "Aligned_obj.obj") + + stage = Usd.Stage.Open(args.input) + vert_offset = 0 + + with open(obj_path, "w") as f: + f.write("# Exported from USD\n\n") + + for prim in stage.Traverse(): + if prim.GetTypeName() != "Mesh": + continue + + mesh = UsdGeom.Mesh(prim) + points = mesh.GetPointsAttr().Get() + if not points: + continue + + face_counts = mesh.GetFaceVertexCountsAttr().Get() + face_indices = mesh.GetFaceVertexIndicesAttr().Get() + + name = str(prim.GetPath()).replace("/", "_").strip("_") + f.write(f"o {name}\n") + print(f" Mesh: {prim.GetPath()} ({len(points)} verts)") + + for p in points: + f.write(f"v {p[0]:.8f} {p[1]:.8f} {p[2]:.8f}\n") + + if face_counts and face_indices: + idx = 0 + for fc in face_counts: + verts = [] + for j in range(fc): + verts.append(face_indices[idx] + 1 + vert_offset) + idx += 1 + # Triangulate: fan from first vertex + for j in range(1, len(verts) - 1): + f.write(f"f {verts[0]} {verts[j]} {verts[j+1]}\n") + + vert_offset += len(points) + + print(f"\nSaved: {obj_path} ({vert_offset} total vertices)") + + +if __name__ == "__main__": + main() diff --git a/workflows/simbox/core/configs/tasks/example/pick_test_tube.yaml b/workflows/simbox/core/configs/tasks/example/pick_test_tube.yaml new file mode 100644 index 0000000..d7e16fd --- /dev/null +++ b/workflows/simbox/core/configs/tasks/example/pick_test_tube.yaml @@ -0,0 +1,152 @@ +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 configuration + 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: + # Test tube - graspable rigid object + - + 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 + + # Test tube rack - static geometry + - + name: test_tube_rack + path: task/pick_test_tube/test_tube_rack/Test_Tube_Rack_AA_01.usdc + target_class: GeometryObject + dataset: custom + category: rack + prim_path_child: Test_Tube_Rack_AA_01 + translation: [0.0, 0.0, 0.0] + euler: [0.0, 0.0, 0.0] + scale: [1, 1, 1] + apply_randomization: False + + regions: + # Test tube on table (right side of robot) + - + 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] + + # Test tube rack on table (left side) + - + object: test_tube_rack + target: table + random_type: A_on_B_region_sampler + random_config: + pos_range: [ + [-0.25, -0.05, 0.0], + [-0.25, -0.05, 0.0] + ] + yaw_rotation: [0.0, 0.0] + + # Robot position + - + 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: + - + name: split_aloha_hand_left + translation: [0.0, 0.08, 0.05] + orientation: [0.0, 0.0, 0.965, 0.259] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/astra.yaml + parent: "split_aloha/split_aloha_mid_360_with_piper/split_aloha_mid_360_with_piper/fl/link6" + apply_randomization: False + - + name: split_aloha_hand_right + translation: [0.0, 0.08, 0.04] + orientation: [0.0, 0.0, 0.972, 0.233] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/astra.yaml + parent: "split_aloha/split_aloha_mid_360_with_piper/split_aloha_mid_360_with_piper/fr/link6" + apply_randomization: False + - + name: split_aloha_head + translation: [0.0, -0.00818, 0.1] + orientation: [0.658, 0.259, -0.282, -0.648] + camera_axes: usd + camera_file: workflows/simbox/core/configs/cameras/realsense_d455_v3.yaml + parent: "split_aloha/split_aloha_mid_360_with_piper/split_aloha_mid_360_with_piper/top_camera_link" + + 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 from the table and lift it up." + collect_info: "Test tube picking task with Split Aloha" + version: "v1.0" + update: True + max_episode_length: 2000 + + skills: + - + split_aloha: + - + right: + # Pick the test tube with right arm + - + 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 + # Return to home + - + name: heuristic__skill + mode: home + gripper_state: 1.0 diff --git a/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_dense.npz b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_dense.npz new file mode 100644 index 0000000..b891bb4 Binary files /dev/null and b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_dense.npz differ diff --git a/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_sparse.npy b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_sparse.npy new file mode 100644 index 0000000..6b8cf55 Binary files /dev/null and b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_grasp_sparse.npy differ diff --git a/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.obj b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.obj new file mode 100644 index 0000000..e3e9fa3 --- /dev/null +++ b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.obj @@ -0,0 +1,6881 @@ +# Exported from USD + +o Test_Tube_AA_01_Test_Tube________005 +v 0.00405656 0.00000000 0.00025016 +v 0.00402698 0.00048896 0.00025016 +v 0.00393868 0.00097080 0.00025016 +v 0.00379295 0.00143847 0.00025016 +v 0.00359190 0.00188517 0.00025016 +v 0.00333848 0.00230438 0.00025016 +v 0.00303638 0.00268999 0.00025016 +v 0.00269000 0.00303637 0.00025016 +v 0.00230439 0.00333848 0.00025016 +v 0.00188518 0.00359190 0.00025016 +v 0.00143848 0.00379294 0.00025016 +v 0.00097080 0.00393868 0.00025016 +v 0.00048897 0.00402698 0.00025016 +v 0.00000000 0.00405655 0.00025016 +v -0.00048896 0.00402698 0.00025016 +v -0.00097079 0.00393868 0.00025016 +v -0.00143847 0.00379294 0.00025016 +v -0.00188517 0.00359190 0.00025016 +v -0.00230438 0.00333848 0.00025016 +v -0.00268999 0.00303637 0.00025016 +v -0.00303637 0.00268999 0.00025016 +v -0.00333847 0.00230438 0.00025016 +v -0.00359189 0.00188517 0.00025016 +v -0.00379294 0.00143847 0.00025016 +v -0.00393867 0.00097079 0.00025016 +v -0.00402697 0.00048896 0.00025016 +v -0.00405655 -0.00000000 0.00025016 +v -0.00402697 -0.00048897 0.00025016 +v -0.00393867 -0.00097080 0.00025016 +v -0.00379294 -0.00143848 0.00025016 +v -0.00359189 -0.00188518 0.00025016 +v -0.00333847 -0.00230439 0.00025016 +v -0.00303636 -0.00268999 0.00025016 +v -0.00268998 -0.00303638 0.00025016 +v -0.00230437 -0.00333848 0.00025016 +v -0.00188516 -0.00359190 0.00025016 +v -0.00143846 -0.00379294 0.00025016 +v -0.00097079 -0.00393868 0.00025016 +v -0.00048895 -0.00402698 0.00025016 +v 0.00000001 -0.00405655 0.00025016 +v 0.00048897 -0.00402697 0.00025016 +v 0.00097081 -0.00393867 0.00025016 +v 0.00143848 -0.00379294 0.00025016 +v 0.00188519 -0.00359190 0.00025016 +v 0.00230440 -0.00333847 0.00025016 +v 0.00269000 -0.00303637 0.00025016 +v 0.00303638 -0.00268999 0.00025016 +v 0.00333849 -0.00230438 0.00025016 +v 0.00359191 -0.00188517 0.00025016 +v 0.00379295 -0.00143847 0.00025016 +v 0.00393868 -0.00097079 0.00025016 +v 0.00402698 -0.00048895 0.00025016 +v 0.01485634 -0.00000000 0.01345215 +v 0.01474802 0.00179073 0.01345215 +v 0.01442464 0.00355536 0.01345215 +v 0.01389092 0.00526813 0.01345215 +v 0.01315464 0.00690409 0.01345215 +v 0.01222653 0.00843936 0.01345215 +v 0.01112013 0.00985158 0.01345215 +v 0.00985158 0.01112013 0.01345215 +v 0.00843936 0.01222653 0.01345215 +v 0.00690409 0.01315464 0.01345215 +v 0.00526813 0.01389092 0.01345215 +v 0.00355536 0.01442464 0.01345215 +v 0.00179073 0.01474802 0.01345215 +v -0.00000000 0.01485634 0.01345215 +v -0.00179074 0.01474802 0.01345215 +v -0.00355536 0.01442464 0.01345215 +v -0.00526813 0.01389092 0.01345215 +v -0.00690409 0.01315463 0.01345215 +v -0.00843937 0.01222652 0.01345215 +v -0.00985158 0.01112013 0.01345215 +v -0.01112013 0.00985157 0.01345215 +v -0.01222653 0.00843936 0.01345215 +v -0.01315464 0.00690408 0.01345215 +v -0.01389092 0.00526812 0.01345215 +v -0.01442464 0.00355534 0.01345215 +v -0.01474802 0.00179072 0.01345215 +v -0.01485634 -0.00000001 0.01345215 +v -0.01474802 -0.00179075 0.01345215 +v -0.01442464 -0.00355537 0.01345215 +v -0.01389091 -0.00526814 0.01345215 +v -0.01315463 -0.00690410 0.01345215 +v -0.01222652 -0.00843938 0.01345215 +v -0.01112012 -0.00985159 0.01345215 +v -0.00985156 -0.01112014 0.01345215 +v -0.00843935 -0.01222654 0.01345215 +v -0.00690407 -0.01315465 0.01345215 +v -0.00526811 -0.01389093 0.01345215 +v -0.00355533 -0.01442465 0.01345215 +v -0.00179071 -0.01474802 0.01345215 +v 0.00000002 -0.01485634 0.01345215 +v 0.00179076 -0.01474802 0.01345215 +v 0.00355538 -0.01442464 0.01345215 +v 0.00526815 -0.01389091 0.01345215 +v 0.00690411 -0.01315462 0.01345215 +v 0.00843939 -0.01222651 0.01345215 +v 0.00985160 -0.01112011 0.01345215 +v 0.01112015 -0.00985156 0.01345215 +v 0.01222655 -0.00843934 0.01345215 +v 0.01315465 -0.00690406 0.01345215 +v 0.01389093 -0.00526810 0.01345215 +v 0.01442465 -0.00355533 0.01345215 +v 0.01474803 -0.00179070 0.01345215 +v 0.01297744 0.00000000 0.00608596 +v 0.01288282 0.00156426 0.00608596 +v 0.01260034 0.00310571 0.00608596 +v 0.01213412 0.00460187 0.00608596 +v 0.01149096 0.00603092 0.00608596 +v 0.01068023 0.00737203 0.00608596 +v 0.00971376 0.00860564 0.00608596 +v 0.00860564 0.00971375 0.00608596 +v 0.00737203 0.01068022 0.00608596 +v 0.00603092 0.01149095 0.00608596 +v 0.00460187 0.01213412 0.00608596 +v 0.00310571 0.01260034 0.00608596 +v 0.00156426 0.01288282 0.00608596 +v -0.00000000 0.01297744 0.00608596 +v -0.00156426 0.01288282 0.00608596 +v -0.00310571 0.01260034 0.00608596 +v -0.00460187 0.01213412 0.00608596 +v -0.00603092 0.01149095 0.00608596 +v -0.00737203 0.01068022 0.00608596 +v -0.00860564 0.00971375 0.00608596 +v -0.00971376 0.00860563 0.00608596 +v -0.01068023 0.00737202 0.00608596 +v -0.01149096 0.00603091 0.00608596 +v -0.01213412 0.00460186 0.00608596 +v -0.01260034 0.00310570 0.00608596 +v -0.01288282 0.00156425 0.00608596 +v -0.01297744 -0.00000001 0.00608596 +v -0.01288282 -0.00156427 0.00608596 +v -0.01260034 -0.00310572 0.00608596 +v -0.01213411 -0.00460188 0.00608596 +v -0.01149095 -0.00603093 0.00608596 +v -0.01068022 -0.00737204 0.00608596 +v -0.00971374 -0.00860565 0.00608596 +v -0.00860562 -0.00971376 0.00608596 +v -0.00737201 -0.01068023 0.00608596 +v -0.00603090 -0.01149096 0.00608596 +v -0.00460185 -0.01213412 0.00608596 +v -0.00310569 -0.01260035 0.00608596 +v -0.00156424 -0.01288282 0.00608596 +v 0.00000002 -0.01297744 0.00608596 +v 0.00156428 -0.01288282 0.00608596 +v 0.00310573 -0.01260034 0.00608596 +v 0.00460189 -0.01213411 0.00608596 +v 0.00603094 -0.01149094 0.00608596 +v 0.00737205 -0.01068021 0.00608596 +v 0.00860565 -0.00971374 0.00608596 +v 0.00971377 -0.00860562 0.00608596 +v 0.01068024 -0.00737201 0.00608596 +v 0.01149097 -0.00603090 0.00608596 +v 0.01213413 -0.00460184 0.00608596 +v 0.01260035 -0.00310568 0.00608596 +v 0.01288283 -0.00156423 0.00608596 +v 0.01020867 0.00000000 0.00289123 +v 0.01013424 0.00123052 0.00289123 +v 0.00991202 0.00244309 0.00289123 +v 0.00954527 0.00362004 0.00289123 +v 0.00903933 0.00474420 0.00289123 +v 0.00840157 0.00579918 0.00289123 +v 0.00764130 0.00676960 0.00289123 +v 0.00676960 0.00764130 0.00289123 +v 0.00579919 0.00840157 0.00289123 +v 0.00474421 0.00903933 0.00289123 +v 0.00362005 0.00954527 0.00289123 +v 0.00244310 0.00991202 0.00289123 +v 0.00123052 0.01013423 0.00289123 +v 0.00000000 0.01020867 0.00289123 +v -0.00123052 0.01013423 0.00289123 +v -0.00244309 0.00991202 0.00289123 +v -0.00362004 0.00954527 0.00289123 +v -0.00474420 0.00903932 0.00289123 +v -0.00579918 0.00840157 0.00289123 +v -0.00676960 0.00764129 0.00289123 +v -0.00764130 0.00676959 0.00289123 +v -0.00840157 0.00579918 0.00289123 +v -0.00903933 0.00474420 0.00289123 +v -0.00954527 0.00362004 0.00289123 +v -0.00991202 0.00244309 0.00289123 +v -0.01013423 0.00123051 0.00289123 +v -0.01020866 -0.00000001 0.00289123 +v -0.01013423 -0.00123053 0.00289123 +v -0.00991202 -0.00244310 0.00289123 +v -0.00954526 -0.00362005 0.00289123 +v -0.00903932 -0.00474421 0.00289123 +v -0.00840156 -0.00579919 0.00289123 +v -0.00764129 -0.00676961 0.00289123 +v -0.00676959 -0.00764130 0.00289123 +v -0.00579917 -0.00840157 0.00289123 +v -0.00474419 -0.00903933 0.00289123 +v -0.00362003 -0.00954527 0.00289123 +v -0.00244308 -0.00991202 0.00289123 +v -0.00123050 -0.01013423 0.00289123 +v 0.00000002 -0.01020867 0.00289123 +v 0.00123054 -0.01013423 0.00289123 +v 0.00244311 -0.00991202 0.00289123 +v 0.00362006 -0.00954526 0.00289123 +v 0.00474422 -0.00903932 0.00289123 +v 0.00579920 -0.00840156 0.00289123 +v 0.00676962 -0.00764129 0.00289123 +v 0.00764131 -0.00676959 0.00289123 +v 0.00840158 -0.00579917 0.00289123 +v 0.00903934 -0.00474419 0.00289123 +v 0.00954528 -0.00362002 0.00289123 +v 0.00991203 -0.00244307 0.00289123 +v 0.01013424 -0.00123050 0.00289123 +v 0.00768708 0.00000000 0.00124522 +v 0.00763104 0.00092658 0.00124522 +v 0.00746371 0.00183964 0.00124522 +v 0.00718755 0.00272588 0.00124522 +v 0.00680658 0.00357237 0.00124522 +v 0.00632635 0.00436676 0.00124522 +v 0.00575387 0.00509748 0.00124522 +v 0.00509748 0.00575386 0.00124522 +v 0.00436676 0.00632634 0.00124522 +v 0.00357237 0.00680657 0.00124522 +v 0.00272588 0.00718754 0.00124522 +v 0.00183964 0.00746371 0.00124522 +v 0.00092658 0.00763103 0.00124522 +v 0.00000000 0.00768708 0.00124522 +v -0.00092657 0.00763103 0.00124522 +v -0.00183964 0.00746371 0.00124522 +v -0.00272587 0.00718754 0.00124522 +v -0.00357236 0.00680657 0.00124522 +v -0.00436676 0.00632634 0.00124522 +v -0.00509748 0.00575386 0.00124522 +v -0.00575386 0.00509747 0.00124522 +v -0.00632634 0.00436676 0.00124522 +v -0.00680657 0.00357236 0.00124522 +v -0.00718754 0.00272587 0.00124522 +v -0.00746371 0.00183963 0.00124522 +v -0.00763103 0.00092657 0.00124522 +v -0.00768708 -0.00000001 0.00124522 +v -0.00763103 -0.00092658 0.00124522 +v -0.00746370 -0.00183965 0.00124522 +v -0.00718754 -0.00272588 0.00124522 +v -0.00680656 -0.00357237 0.00124522 +v -0.00632633 -0.00436677 0.00124522 +v -0.00575385 -0.00509748 0.00124522 +v -0.00509747 -0.00575387 0.00124522 +v -0.00436675 -0.00632635 0.00124522 +v -0.00357235 -0.00680658 0.00124522 +v -0.00272586 -0.00718755 0.00124522 +v -0.00183962 -0.00746371 0.00124522 +v -0.00092656 -0.00763103 0.00124522 +v 0.00000002 -0.00768708 0.00124522 +v 0.00092659 -0.00763103 0.00124522 +v 0.00183965 -0.00746370 0.00124522 +v 0.00272589 -0.00718754 0.00124522 +v 0.00357238 -0.00680657 0.00124522 +v 0.00436678 -0.00632634 0.00124522 +v 0.00509749 -0.00575385 0.00124522 +v 0.00575387 -0.00509747 0.00124522 +v 0.00632636 -0.00436675 0.00124522 +v 0.00680658 -0.00357235 0.00124522 +v 0.00718756 -0.00272586 0.00124522 +v 0.00746372 -0.00183962 0.00124522 +v 0.00763104 -0.00092656 0.00124522 +v 0.00000001 0.00000000 0.00000000 +v 0.01462378 -0.00000000 0.00993726 +v 0.01451716 0.00176270 0.00993726 +v 0.01419884 0.00349970 0.00993726 +v 0.01367347 0.00518567 0.00993726 +v 0.01294872 0.00679601 0.00993726 +v 0.01203514 0.00830725 0.00993726 +v 0.01094606 0.00969736 0.00993726 +v 0.00969736 0.01094606 0.00993726 +v 0.00830726 0.01203514 0.00993726 +v 0.00679601 0.01294872 0.00993726 +v 0.00518567 0.01367347 0.00993726 +v 0.00349970 0.01419884 0.00993726 +v 0.00176270 0.01451716 0.00993726 +v -0.00000000 0.01462378 0.00993726 +v -0.00176270 0.01451716 0.00993726 +v -0.00349970 0.01419884 0.00993726 +v -0.00518567 0.01367347 0.00993726 +v -0.00679601 0.01294871 0.00993726 +v -0.00830726 0.01203513 0.00993726 +v -0.00969737 0.01094605 0.00993726 +v -0.01094606 0.00969736 0.00993726 +v -0.01203514 0.00830725 0.00993726 +v -0.01294872 0.00679600 0.00993726 +v -0.01367348 0.00518565 0.00993726 +v -0.01419884 0.00349969 0.00993726 +v -0.01451716 0.00176269 0.00993726 +v -0.01462378 -0.00000001 0.00993726 +v -0.01451716 -0.00176271 0.00993726 +v -0.01419884 -0.00349971 0.00993726 +v -0.01367347 -0.00518568 0.00993726 +v -0.01294871 -0.00679602 0.00993726 +v -0.01203513 -0.00830727 0.00993726 +v -0.01094605 -0.00969737 0.00993726 +v -0.00969735 -0.01094607 0.00993726 +v -0.00830724 -0.01203515 0.00993726 +v -0.00679599 -0.01294872 0.00993726 +v -0.00518565 -0.01367348 0.00993726 +v -0.00349968 -0.01419885 0.00993726 +v -0.00176268 -0.01451716 0.00993726 +v 0.00000002 -0.01462378 0.00993726 +v 0.00176272 -0.01451715 0.00993726 +v 0.00349972 -0.01419884 0.00993726 +v 0.00518569 -0.01367347 0.00993726 +v 0.00679603 -0.01294870 0.00993726 +v 0.00830728 -0.01203512 0.00993726 +v 0.00969738 -0.01094604 0.00993726 +v 0.01094608 -0.00969734 0.00993726 +v 0.01203515 -0.00830723 0.00993726 +v 0.01294873 -0.00679599 0.00993726 +v 0.01367349 -0.00518564 0.00993726 +v 0.01419885 -0.00349967 0.00993726 +v 0.01451716 -0.00176267 0.00993726 +v 0.01485634 -0.00000000 0.09898411 +v 0.01474802 0.00179073 0.09898411 +v 0.01442464 0.00355536 0.09898411 +v 0.01389092 0.00526813 0.09898411 +v 0.01315464 0.00690409 0.09898411 +v 0.01222653 0.00843936 0.09898411 +v 0.01112013 0.00985158 0.09898411 +v 0.00985158 0.01112013 0.09898411 +v 0.00843936 0.01222653 0.09898411 +v 0.00690409 0.01315464 0.09898411 +v 0.00526813 0.01389092 0.09898411 +v 0.00355536 0.01442464 0.09898411 +v 0.00179073 0.01474802 0.09898411 +v -0.00000000 0.01485634 0.09898411 +v -0.00179074 0.01474802 0.09898411 +v -0.00355536 0.01442464 0.09898411 +v -0.00526813 0.01389092 0.09898411 +v -0.00690409 0.01315463 0.09898411 +v -0.00843937 0.01222652 0.09898411 +v -0.00985158 0.01112013 0.09898411 +v -0.01112013 0.00985157 0.09898411 +v -0.01222653 0.00843936 0.09898411 +v -0.01315464 0.00690408 0.09898411 +v -0.01389092 0.00526812 0.09898411 +v -0.01442464 0.00355534 0.09898411 +v -0.01474802 0.00179072 0.09898411 +v -0.01485634 -0.00000001 0.09898411 +v -0.01474802 -0.00179075 0.09898411 +v -0.01442464 -0.00355537 0.09898411 +v -0.01389091 -0.00526814 0.09898411 +v -0.01315463 -0.00690410 0.09898411 +v -0.01222652 -0.00843938 0.09898411 +v -0.01112012 -0.00985159 0.09898411 +v -0.00985156 -0.01112014 0.09898411 +v -0.00843935 -0.01222654 0.09898411 +v -0.00690407 -0.01315465 0.09898411 +v -0.00526811 -0.01389093 0.09898411 +v -0.00355533 -0.01442465 0.09898411 +v -0.00179071 -0.01474802 0.09898411 +v 0.00000002 -0.01485634 0.09898411 +v 0.00179076 -0.01474802 0.09898411 +v 0.00355538 -0.01442464 0.09898411 +v 0.00526815 -0.01389091 0.09898411 +v 0.00690411 -0.01315462 0.09898411 +v 0.00843939 -0.01222651 0.09898411 +v 0.00985160 -0.01112011 0.09898411 +v 0.01112015 -0.00985156 0.09898411 +v 0.01222655 -0.00843934 0.09898411 +v 0.01315465 -0.00690406 0.09898411 +v 0.01389093 -0.00526810 0.09898411 +v 0.01442465 -0.00355533 0.09898411 +v 0.01474803 -0.00179070 0.09898411 +v 0.01485634 -0.00000000 0.10565948 +v 0.01474802 0.00179073 0.10565948 +v 0.01442464 0.00355536 0.10565948 +v 0.01389092 0.00526813 0.10565948 +v 0.01315464 0.00690409 0.10565948 +v 0.01222653 0.00843936 0.10565948 +v 0.01112013 0.00985158 0.10565948 +v 0.00985158 0.01112013 0.10565948 +v 0.00843936 0.01222653 0.10565948 +v 0.00690409 0.01315464 0.10565948 +v 0.00526813 0.01389092 0.10565948 +v 0.00355536 0.01442464 0.10565948 +v 0.00179073 0.01474802 0.10565948 +v -0.00000000 0.01485634 0.10565948 +v -0.00179074 0.01474802 0.10565948 +v -0.00355536 0.01442464 0.10565948 +v -0.00526813 0.01389092 0.10565948 +v -0.00690409 0.01315463 0.10565948 +v -0.00843937 0.01222652 0.10565948 +v -0.00985158 0.01112013 0.10565948 +v -0.01112013 0.00985157 0.10565948 +v -0.01222653 0.00843936 0.10565948 +v -0.01315464 0.00690408 0.10565948 +v -0.01389092 0.00526812 0.10565948 +v -0.01442464 0.00355534 0.10565948 +v -0.01474802 0.00179072 0.10565948 +v -0.01485634 -0.00000001 0.10565948 +v -0.01474802 -0.00179075 0.10565948 +v -0.01442464 -0.00355537 0.10565948 +v -0.01389091 -0.00526814 0.10565948 +v -0.01315463 -0.00690410 0.10565948 +v -0.01222652 -0.00843938 0.10565948 +v -0.01112012 -0.00985159 0.10565948 +v -0.00985156 -0.01112014 0.10565948 +v -0.00843935 -0.01222654 0.10565948 +v -0.00690407 -0.01315465 0.10565948 +v -0.00526811 -0.01389093 0.10565948 +v -0.00355533 -0.01442465 0.10565948 +v -0.00179071 -0.01474802 0.10565948 +v 0.00000002 -0.01485634 0.10565948 +v 0.00179076 -0.01474802 0.10565948 +v 0.00355538 -0.01442464 0.10565948 +v 0.00526815 -0.01389091 0.10565948 +v 0.00690411 -0.01315462 0.10565948 +v 0.00843939 -0.01222651 0.10565948 +v 0.00985160 -0.01112011 0.10565948 +v 0.01112015 -0.00985156 0.10565948 +v 0.01222655 -0.00843934 0.10565948 +v 0.01315465 -0.00690406 0.10565948 +v 0.01389093 -0.00526810 0.10565948 +v 0.01442465 -0.00355533 0.10565948 +v 0.01474803 -0.00179070 0.10565948 +v 0.00388891 0.00000000 0.00123600 +v 0.00386055 0.00046876 0.00123600 +v 0.00377590 0.00093068 0.00123600 +v 0.00363619 0.00137902 0.00123600 +v 0.00344346 0.00180726 0.00123600 +v 0.00320051 0.00220915 0.00123600 +v 0.00291089 0.00257882 0.00123600 +v 0.00257882 0.00291088 0.00123600 +v 0.00220915 0.00320050 0.00123600 +v 0.00180727 0.00344345 0.00123600 +v 0.00137903 0.00363619 0.00123600 +v 0.00093068 0.00377590 0.00123600 +v 0.00046876 0.00386055 0.00123600 +v 0.00000000 0.00388890 0.00123600 +v -0.00046875 0.00386055 0.00123600 +v -0.00093067 0.00377590 0.00123600 +v -0.00137902 0.00363618 0.00123600 +v -0.00180726 0.00344345 0.00123600 +v -0.00220914 0.00320050 0.00123600 +v -0.00257881 0.00291088 0.00123600 +v -0.00291088 0.00257882 0.00123600 +v -0.00320050 0.00220915 0.00123600 +v -0.00344345 0.00180726 0.00123600 +v -0.00363618 0.00137902 0.00123600 +v -0.00377589 0.00093067 0.00123600 +v -0.00386054 0.00046875 0.00123600 +v -0.00388889 -0.00000000 0.00123600 +v -0.00386054 -0.00046876 0.00123600 +v -0.00377589 -0.00093068 0.00123600 +v -0.00363618 -0.00137903 0.00123600 +v -0.00344344 -0.00180727 0.00123600 +v -0.00320049 -0.00220915 0.00123600 +v -0.00291088 -0.00257882 0.00123600 +v -0.00257881 -0.00291089 0.00123600 +v -0.00220914 -0.00320050 0.00123600 +v -0.00180725 -0.00344345 0.00123600 +v -0.00137901 -0.00363619 0.00123600 +v -0.00093066 -0.00377590 0.00123600 +v -0.00046874 -0.00386055 0.00123600 +v 0.00000001 -0.00388890 0.00123600 +v 0.00046877 -0.00386054 0.00123600 +v 0.00093069 -0.00377589 0.00123600 +v 0.00137903 -0.00363618 0.00123600 +v 0.00180727 -0.00344345 0.00123600 +v 0.00220916 -0.00320050 0.00123600 +v 0.00257883 -0.00291088 0.00123600 +v 0.00291089 -0.00257881 0.00123600 +v 0.00320051 -0.00220914 0.00123600 +v 0.00344346 -0.00180726 0.00123600 +v 0.00363619 -0.00137902 0.00123600 +v 0.00377590 -0.00093067 0.00123600 +v 0.00386055 -0.00046875 0.00123600 +v 0.01385688 -0.00000000 0.01348513 +v 0.01375585 0.00167026 0.01348513 +v 0.01345423 0.00331617 0.01348513 +v 0.01295641 0.00491372 0.01348513 +v 0.01226966 0.00643962 0.01348513 +v 0.01140399 0.00787161 0.01348513 +v 0.01037203 0.00918881 0.01348513 +v 0.00918882 0.01037203 0.01348513 +v 0.00787161 0.01140399 0.01348513 +v 0.00643962 0.01226966 0.01348513 +v 0.00491372 0.01295641 0.01348513 +v 0.00331617 0.01345423 0.01348513 +v 0.00167026 0.01375585 0.01348513 +v -0.00000000 0.01385688 0.01348513 +v -0.00167026 0.01375585 0.01348513 +v -0.00331617 0.01345423 0.01348513 +v -0.00491372 0.01295641 0.01348513 +v -0.00643962 0.01226966 0.01348513 +v -0.00787161 0.01140399 0.01348513 +v -0.00918882 0.01037202 0.01348513 +v -0.01037203 0.00918881 0.01348513 +v -0.01140399 0.00787160 0.01348513 +v -0.01226967 0.00643961 0.01348513 +v -0.01295641 0.00491371 0.01348513 +v -0.01345423 0.00331616 0.01348513 +v -0.01375585 0.00167025 0.01348513 +v -0.01385688 -0.00000001 0.01348513 +v -0.01375585 -0.00167027 0.01348513 +v -0.01345422 -0.00331618 0.01348513 +v -0.01295641 -0.00491373 0.01348513 +v -0.01226965 -0.00643963 0.01348513 +v -0.01140398 -0.00787162 0.01348513 +v -0.01037202 -0.00918883 0.01348513 +v -0.00918880 -0.01037204 0.01348513 +v -0.00787159 -0.01140400 0.01348513 +v -0.00643960 -0.01226967 0.01348513 +v -0.00491370 -0.01295642 0.01348513 +v -0.00331615 -0.01345423 0.01348513 +v -0.00167024 -0.01375585 0.01348513 +v 0.00000002 -0.01385688 0.01348513 +v 0.00167028 -0.01375585 0.01348513 +v 0.00331619 -0.01345422 0.01348513 +v 0.00491374 -0.01295640 0.01348513 +v 0.00643964 -0.01226965 0.01348513 +v 0.00787163 -0.01140398 0.01348513 +v 0.00918883 -0.01037201 0.01348513 +v 0.01037204 -0.00918880 0.01348513 +v 0.01140401 -0.00787159 0.01348513 +v 0.01226968 -0.00643959 0.01348513 +v 0.01295642 -0.00491369 0.01348513 +v 0.01345424 -0.00331614 0.01348513 +v 0.01375586 -0.00167023 0.01348513 +v 0.01212802 -0.00000000 0.00661367 +v 0.01203959 0.00146187 0.00661367 +v 0.01177560 0.00290242 0.00661367 +v 0.01133989 0.00430065 0.00661367 +v 0.01073883 0.00563617 0.00661367 +v 0.00998116 0.00688950 0.00661367 +v 0.00907795 0.00804236 0.00661367 +v 0.00804237 0.00907795 0.00661367 +v 0.00688950 0.00998116 0.00661367 +v 0.00563617 0.01073883 0.00661367 +v 0.00430066 0.01133989 0.00661367 +v 0.00290243 0.01177560 0.00661367 +v 0.00146187 0.01203959 0.00661367 +v -0.00000000 0.01212802 0.00661367 +v -0.00146187 0.01203959 0.00661367 +v -0.00290243 0.01177560 0.00661367 +v -0.00430066 0.01133989 0.00661367 +v -0.00563617 0.01073882 0.00661367 +v -0.00688950 0.00998116 0.00661367 +v -0.00804237 0.00907795 0.00661367 +v -0.00907795 0.00804236 0.00661367 +v -0.00998116 0.00688949 0.00661367 +v -0.01073883 0.00563616 0.00661367 +v -0.01133990 0.00430065 0.00661367 +v -0.01177560 0.00290242 0.00661367 +v -0.01203959 0.00146186 0.00661367 +v -0.01212802 -0.00000001 0.00661367 +v -0.01203959 -0.00146188 0.00661367 +v -0.01177560 -0.00290244 0.00661367 +v -0.01133989 -0.00430066 0.00661367 +v -0.01073882 -0.00563618 0.00661367 +v -0.00998115 -0.00688951 0.00661367 +v -0.00907794 -0.00804237 0.00661367 +v -0.00804235 -0.00907796 0.00661367 +v -0.00688948 -0.00998117 0.00661367 +v -0.00563616 -0.01073883 0.00661367 +v -0.00430064 -0.01133990 0.00661367 +v -0.00290241 -0.01177560 0.00661367 +v -0.00146185 -0.01203959 0.00661367 +v 0.00000002 -0.01212802 0.00661367 +v 0.00146189 -0.01203959 0.00661367 +v 0.00290244 -0.01177560 0.00661367 +v 0.00430067 -0.01133989 0.00661367 +v 0.00563619 -0.01073882 0.00661367 +v 0.00688952 -0.00998115 0.00661367 +v 0.00804238 -0.00907794 0.00661367 +v 0.00907797 -0.00804235 0.00661367 +v 0.00998118 -0.00688948 0.00661367 +v 0.01073884 -0.00563615 0.00661367 +v 0.01133990 -0.00430063 0.00661367 +v 0.01177561 -0.00290240 0.00661367 +v 0.01203960 -0.00146185 0.00661367 +v 0.00954815 -0.00000000 0.00364204 +v 0.00947853 0.00115090 0.00364204 +v 0.00927070 0.00228502 0.00364204 +v 0.00892767 0.00338582 0.00364204 +v 0.00845447 0.00443725 0.00364204 +v 0.00785797 0.00542397 0.00364204 +v 0.00714689 0.00633159 0.00364204 +v 0.00633160 0.00714689 0.00364204 +v 0.00542397 0.00785797 0.00364204 +v 0.00443725 0.00845447 0.00364204 +v 0.00338582 0.00892767 0.00364204 +v 0.00228502 0.00927070 0.00364204 +v 0.00115090 0.00947853 0.00364204 +v 0.00000000 0.00954815 0.00364204 +v -0.00115090 0.00947853 0.00364204 +v -0.00228502 0.00927070 0.00364204 +v -0.00338582 0.00892767 0.00364204 +v -0.00443724 0.00845446 0.00364204 +v -0.00542397 0.00785797 0.00364204 +v -0.00633159 0.00714689 0.00364204 +v -0.00714689 0.00633159 0.00364204 +v -0.00785797 0.00542396 0.00364204 +v -0.00845447 0.00443724 0.00364204 +v -0.00892767 0.00338581 0.00364204 +v -0.00927070 0.00228501 0.00364204 +v -0.00947853 0.00115090 0.00364204 +v -0.00954815 -0.00000001 0.00364204 +v -0.00947853 -0.00115091 0.00364204 +v -0.00927069 -0.00228503 0.00364204 +v -0.00892767 -0.00338583 0.00364204 +v -0.00845446 -0.00443725 0.00364204 +v -0.00785796 -0.00542397 0.00364204 +v -0.00714688 -0.00633160 0.00364204 +v -0.00633158 -0.00714690 0.00364204 +v -0.00542395 -0.00785798 0.00364204 +v -0.00443723 -0.00845447 0.00364204 +v -0.00338581 -0.00892768 0.00364204 +v -0.00228501 -0.00927070 0.00364204 +v -0.00115089 -0.00947853 0.00364204 +v 0.00000002 -0.00954815 0.00364204 +v 0.00115092 -0.00947853 0.00364204 +v 0.00228504 -0.00927069 0.00364204 +v 0.00338584 -0.00892767 0.00364204 +v 0.00443726 -0.00845446 0.00364204 +v 0.00542398 -0.00785796 0.00364204 +v 0.00633161 -0.00714688 0.00364204 +v 0.00714691 -0.00633158 0.00364204 +v 0.00785798 -0.00542395 0.00364204 +v 0.00845448 -0.00443723 0.00364204 +v 0.00892768 -0.00338580 0.00364204 +v 0.00927070 -0.00228500 0.00364204 +v 0.00947854 -0.00115088 0.00364204 +v 0.00727174 0.00000000 0.00215489 +v 0.00721872 0.00087651 0.00215489 +v 0.00706044 0.00174024 0.00215489 +v 0.00679919 0.00257859 0.00215489 +v 0.00643880 0.00337934 0.00215489 +v 0.00598452 0.00413082 0.00215489 +v 0.00544298 0.00482205 0.00215489 +v 0.00482206 0.00544297 0.00215489 +v 0.00413082 0.00598452 0.00215489 +v 0.00337935 0.00643880 0.00215489 +v 0.00257860 0.00679919 0.00215489 +v 0.00174024 0.00706043 0.00215489 +v 0.00087651 0.00721871 0.00215489 +v 0.00000000 0.00727173 0.00215489 +v -0.00087651 0.00721871 0.00215489 +v -0.00174024 0.00706043 0.00215489 +v -0.00257859 0.00679919 0.00215489 +v -0.00337934 0.00643880 0.00215489 +v -0.00413081 0.00598452 0.00215489 +v -0.00482205 0.00544297 0.00215489 +v -0.00544297 0.00482205 0.00215489 +v -0.00598452 0.00413081 0.00215489 +v -0.00643880 0.00337934 0.00215489 +v -0.00679919 0.00257859 0.00215489 +v -0.00706043 0.00174023 0.00215489 +v -0.00721871 0.00087651 0.00215489 +v -0.00727173 -0.00000001 0.00215489 +v -0.00721871 -0.00087652 0.00215489 +v -0.00706042 -0.00174025 0.00215489 +v -0.00679918 -0.00257860 0.00215489 +v -0.00643879 -0.00337935 0.00215489 +v -0.00598451 -0.00413082 0.00215489 +v -0.00544296 -0.00482206 0.00215489 +v -0.00482204 -0.00544298 0.00215489 +v -0.00413080 -0.00598452 0.00215489 +v -0.00337933 -0.00643880 0.00215489 +v -0.00257858 -0.00679919 0.00215489 +v -0.00174023 -0.00706043 0.00215489 +v -0.00087650 -0.00721872 0.00215489 +v 0.00000001 -0.00727173 0.00215489 +v 0.00087653 -0.00721871 0.00215489 +v 0.00174025 -0.00706043 0.00215489 +v 0.00257861 -0.00679919 0.00215489 +v 0.00337936 -0.00643880 0.00215489 +v 0.00413083 -0.00598451 0.00215489 +v 0.00482207 -0.00544296 0.00215489 +v 0.00544298 -0.00482204 0.00215489 +v 0.00598453 -0.00413080 0.00215489 +v 0.00643881 -0.00337933 0.00215489 +v 0.00679920 -0.00257858 0.00215489 +v 0.00706044 -0.00174023 0.00215489 +v 0.00721872 -0.00087650 0.00215489 +v 0.00000001 0.00000000 0.00100000 +v 0.01365092 -0.00000000 0.01016867 +v 0.01355140 0.00164544 0.01016867 +v 0.01325426 0.00326688 0.01016867 +v 0.01276384 0.00484069 0.01016867 +v 0.01208729 0.00634390 0.01016867 +v 0.01123449 0.00775461 0.01016867 +v 0.01021786 0.00905224 0.01016867 +v 0.00905224 0.01021786 0.01016867 +v 0.00775461 0.01123449 0.01016867 +v 0.00634390 0.01208729 0.01016867 +v 0.00484069 0.01276384 0.01016867 +v 0.00326688 0.01325425 0.01016867 +v 0.00164544 0.01355139 0.01016867 +v -0.00000000 0.01365092 0.01016867 +v -0.00164544 0.01355139 0.01016867 +v -0.00326688 0.01325425 0.01016867 +v -0.00484069 0.01276383 0.01016867 +v -0.00634390 0.01208729 0.01016867 +v -0.00775461 0.01123449 0.01016867 +v -0.00905224 0.01021786 0.01016867 +v -0.01021787 0.00905223 0.01016867 +v -0.01123449 0.00775460 0.01016867 +v -0.01208730 0.00634389 0.01016867 +v -0.01276384 0.00484068 0.01016867 +v -0.01325426 0.00326687 0.01016867 +v -0.01355140 0.00164543 0.01016867 +v -0.01365092 -0.00000001 0.01016867 +v -0.01355139 -0.00164545 0.01016867 +v -0.01325425 -0.00326689 0.01016867 +v -0.01276383 -0.00484070 0.01016867 +v -0.01208729 -0.00634391 0.01016867 +v -0.01123448 -0.00775462 0.01016867 +v -0.01021785 -0.00905225 0.01016867 +v -0.00905223 -0.01021787 0.01016867 +v -0.00775459 -0.01123450 0.01016867 +v -0.00634389 -0.01208730 0.01016867 +v -0.00484067 -0.01276384 0.01016867 +v -0.00326686 -0.01325426 0.01016867 +v -0.00164542 -0.01355140 0.01016867 +v 0.00000002 -0.01365092 0.01016867 +v 0.00164546 -0.01355139 0.01016867 +v 0.00326690 -0.01325425 0.01016867 +v 0.00484071 -0.01276383 0.01016867 +v 0.00634392 -0.01208728 0.01016867 +v 0.00775463 -0.01123448 0.01016867 +v 0.00905226 -0.01021785 0.01016867 +v 0.01021788 -0.00905222 0.01016867 +v 0.01123451 -0.00775459 0.01016867 +v 0.01208731 -0.00634388 0.01016867 +v 0.01276385 -0.00484066 0.01016867 +v 0.01325426 -0.00326685 0.01016867 +v 0.01355140 -0.00164541 0.01016867 +v 0.01385634 -0.00000000 0.09898411 +v 0.01375531 0.00167020 0.09898411 +v 0.01345370 0.00331604 0.09898411 +v 0.01295590 0.00491353 0.09898411 +v 0.01226918 0.00643936 0.09898411 +v 0.01140354 0.00787130 0.09898411 +v 0.01037162 0.00918845 0.09898411 +v 0.00918845 0.01037162 0.09898411 +v 0.00787130 0.01140354 0.09898411 +v 0.00643936 0.01226918 0.09898411 +v 0.00491353 0.01295590 0.09898411 +v 0.00331604 0.01345370 0.09898411 +v 0.00167020 0.01375531 0.09898411 +v -0.00000000 0.01385634 0.09898411 +v -0.00167020 0.01375531 0.09898411 +v -0.00331604 0.01345370 0.09898411 +v -0.00491353 0.01295590 0.09898411 +v -0.00643937 0.01226918 0.09898411 +v -0.00787130 0.01140354 0.09898411 +v -0.00918846 0.01037162 0.09898411 +v -0.01037162 0.00918845 0.09898411 +v -0.01140355 0.00787129 0.09898411 +v -0.01226918 0.00643935 0.09898411 +v -0.01295591 0.00491352 0.09898411 +v -0.01345370 0.00331603 0.09898411 +v -0.01375531 0.00167019 0.09898411 +v -0.01385634 -0.00000001 0.09898411 +v -0.01375531 -0.00167021 0.09898411 +v -0.01345370 -0.00331605 0.09898411 +v -0.01295590 -0.00491354 0.09898411 +v -0.01226917 -0.00643938 0.09898411 +v -0.01140354 -0.00787131 0.09898411 +v -0.01037161 -0.00918846 0.09898411 +v -0.00918844 -0.01037163 0.09898411 +v -0.00787128 -0.01140355 0.09898411 +v -0.00643935 -0.01226919 0.09898411 +v -0.00491351 -0.01295591 0.09898411 +v -0.00331602 -0.01345370 0.09898411 +v -0.00167018 -0.01375531 0.09898411 +v 0.00000002 -0.01385634 0.09898411 +v 0.00167022 -0.01375531 0.09898411 +v 0.00331606 -0.01345370 0.09898411 +v 0.00491355 -0.01295589 0.09898411 +v 0.00643938 -0.01226917 0.09898411 +v 0.00787132 -0.01140353 0.09898411 +v 0.00918847 -0.01037160 0.09898411 +v 0.01037164 -0.00918844 0.09898411 +v 0.01140356 -0.00787128 0.09898411 +v 0.01226919 -0.00643934 0.09898411 +v 0.01295591 -0.00491350 0.09898411 +v 0.01345371 -0.00331601 0.09898411 +v 0.01375532 -0.00167017 0.09898411 +v 0.01385634 -0.00000000 0.10565948 +v 0.01375531 0.00167020 0.10565948 +v 0.01345370 0.00331604 0.10565948 +v 0.01295590 0.00491353 0.10565948 +v 0.01226918 0.00643936 0.10565948 +v 0.01140354 0.00787130 0.10565948 +v 0.01037162 0.00918845 0.10565948 +v 0.00918845 0.01037162 0.10565948 +v 0.00787130 0.01140354 0.10565948 +v 0.00643936 0.01226918 0.10565948 +v 0.00491353 0.01295590 0.10565948 +v 0.00331604 0.01345370 0.10565948 +v 0.00167020 0.01375531 0.10565948 +v -0.00000000 0.01385634 0.10565948 +v -0.00167020 0.01375531 0.10565948 +v -0.00331604 0.01345370 0.10565948 +v -0.00491353 0.01295590 0.10565948 +v -0.00643937 0.01226918 0.10565948 +v -0.00787130 0.01140354 0.10565948 +v -0.00918846 0.01037162 0.10565948 +v -0.01037162 0.00918845 0.10565948 +v -0.01140355 0.00787129 0.10565948 +v -0.01226918 0.00643935 0.10565948 +v -0.01295591 0.00491352 0.10565948 +v -0.01345370 0.00331603 0.10565948 +v -0.01375531 0.00167019 0.10565948 +v -0.01385634 -0.00000001 0.10565948 +v -0.01375531 -0.00167021 0.10565948 +v -0.01345370 -0.00331605 0.10565948 +v -0.01295590 -0.00491354 0.10565948 +v -0.01226917 -0.00643938 0.10565948 +v -0.01140354 -0.00787131 0.10565948 +v -0.01037161 -0.00918846 0.10565948 +v -0.00918844 -0.01037163 0.10565948 +v -0.00787128 -0.01140355 0.10565948 +v -0.00643935 -0.01226919 0.10565948 +v -0.00491351 -0.01295591 0.10565948 +v -0.00331602 -0.01345370 0.10565948 +v -0.00167018 -0.01375531 0.10565948 +v 0.00000002 -0.01385634 0.10565948 +v 0.00167022 -0.01375531 0.10565948 +v 0.00331606 -0.01345370 0.10565948 +v 0.00491355 -0.01295589 0.10565948 +v 0.00643938 -0.01226917 0.10565948 +v 0.00787132 -0.01140353 0.10565948 +v 0.00918847 -0.01037160 0.10565948 +v 0.01037164 -0.00918844 0.10565948 +v 0.01140356 -0.00787128 0.10565948 +v 0.01226919 -0.00643934 0.10565948 +v 0.01295591 -0.00491350 0.10565948 +v 0.01345371 -0.00331601 0.10565948 +v 0.01375532 -0.00167017 0.10565948 +f 54 53 262 +f 54 262 263 +f 55 54 263 +f 55 263 264 +f 56 55 264 +f 56 264 265 +f 57 56 265 +f 57 265 266 +f 58 57 266 +f 58 266 267 +f 59 58 267 +f 59 267 268 +f 60 59 268 +f 60 268 269 +f 61 60 269 +f 61 269 270 +f 62 61 270 +f 62 270 271 +f 63 62 271 +f 63 271 272 +f 64 63 272 +f 64 272 273 +f 65 64 273 +f 65 273 274 +f 66 65 274 +f 66 274 275 +f 67 66 275 +f 67 275 276 +f 68 67 276 +f 68 276 277 +f 69 68 277 +f 69 277 278 +f 70 69 278 +f 70 278 279 +f 71 70 279 +f 71 279 280 +f 72 71 280 +f 72 280 281 +f 73 72 281 +f 73 281 282 +f 74 73 282 +f 74 282 283 +f 75 74 283 +f 75 283 284 +f 76 75 284 +f 76 284 285 +f 77 76 285 +f 77 285 286 +f 78 77 286 +f 78 286 287 +f 79 78 287 +f 79 287 288 +f 80 79 288 +f 80 288 289 +f 81 80 289 +f 81 289 290 +f 82 81 290 +f 82 290 291 +f 83 82 291 +f 83 291 292 +f 84 83 292 +f 84 292 293 +f 85 84 293 +f 85 293 294 +f 86 85 294 +f 86 294 295 +f 87 86 295 +f 87 295 296 +f 88 87 296 +f 88 296 297 +f 89 88 297 +f 89 297 298 +f 90 89 298 +f 90 298 299 +f 91 90 299 +f 91 299 300 +f 92 91 300 +f 92 300 301 +f 93 92 301 +f 93 301 302 +f 94 93 302 +f 94 302 303 +f 95 94 303 +f 95 303 304 +f 96 95 304 +f 96 304 305 +f 97 96 305 +f 97 305 306 +f 98 97 306 +f 98 306 307 +f 99 98 307 +f 99 307 308 +f 100 99 308 +f 100 308 309 +f 101 100 309 +f 101 309 310 +f 102 101 310 +f 102 310 311 +f 103 102 311 +f 103 311 312 +f 104 103 312 +f 104 312 313 +f 53 104 313 +f 53 313 262 +f 106 105 157 +f 106 157 158 +f 107 106 158 +f 107 158 159 +f 108 107 159 +f 108 159 160 +f 109 108 160 +f 109 160 161 +f 110 109 161 +f 110 161 162 +f 111 110 162 +f 111 162 163 +f 112 111 163 +f 112 163 164 +f 113 112 164 +f 113 164 165 +f 114 113 165 +f 114 165 166 +f 115 114 166 +f 115 166 167 +f 116 115 167 +f 116 167 168 +f 117 116 168 +f 117 168 169 +f 118 117 169 +f 118 169 170 +f 119 118 170 +f 119 170 171 +f 120 119 171 +f 120 171 172 +f 121 120 172 +f 121 172 173 +f 122 121 173 +f 122 173 174 +f 123 122 174 +f 123 174 175 +f 124 123 175 +f 124 175 176 +f 125 124 176 +f 125 176 177 +f 126 125 177 +f 126 177 178 +f 127 126 178 +f 127 178 179 +f 128 127 179 +f 128 179 180 +f 129 128 180 +f 129 180 181 +f 130 129 181 +f 130 181 182 +f 131 130 182 +f 131 182 183 +f 132 131 183 +f 132 183 184 +f 133 132 184 +f 133 184 185 +f 134 133 185 +f 134 185 186 +f 135 134 186 +f 135 186 187 +f 136 135 187 +f 136 187 188 +f 137 136 188 +f 137 188 189 +f 138 137 189 +f 138 189 190 +f 139 138 190 +f 139 190 191 +f 140 139 191 +f 140 191 192 +f 141 140 192 +f 141 192 193 +f 142 141 193 +f 142 193 194 +f 143 142 194 +f 143 194 195 +f 144 143 195 +f 144 195 196 +f 145 144 196 +f 145 196 197 +f 146 145 197 +f 146 197 198 +f 147 146 198 +f 147 198 199 +f 148 147 199 +f 148 199 200 +f 149 148 200 +f 149 200 201 +f 150 149 201 +f 150 201 202 +f 151 150 202 +f 151 202 203 +f 152 151 203 +f 152 203 204 +f 153 152 204 +f 153 204 205 +f 154 153 205 +f 154 205 206 +f 155 154 206 +f 155 206 207 +f 156 155 207 +f 156 207 208 +f 105 156 208 +f 105 208 157 +f 158 157 209 +f 158 209 210 +f 159 158 210 +f 159 210 211 +f 160 159 211 +f 160 211 212 +f 161 160 212 +f 161 212 213 +f 162 161 213 +f 162 213 214 +f 163 162 214 +f 163 214 215 +f 164 163 215 +f 164 215 216 +f 165 164 216 +f 165 216 217 +f 166 165 217 +f 166 217 218 +f 167 166 218 +f 167 218 219 +f 168 167 219 +f 168 219 220 +f 169 168 220 +f 169 220 221 +f 170 169 221 +f 170 221 222 +f 171 170 222 +f 171 222 223 +f 172 171 223 +f 172 223 224 +f 173 172 224 +f 173 224 225 +f 174 173 225 +f 174 225 226 +f 175 174 226 +f 175 226 227 +f 176 175 227 +f 176 227 228 +f 177 176 228 +f 177 228 229 +f 178 177 229 +f 178 229 230 +f 179 178 230 +f 179 230 231 +f 180 179 231 +f 180 231 232 +f 181 180 232 +f 181 232 233 +f 182 181 233 +f 182 233 234 +f 183 182 234 +f 183 234 235 +f 184 183 235 +f 184 235 236 +f 185 184 236 +f 185 236 237 +f 186 185 237 +f 186 237 238 +f 187 186 238 +f 187 238 239 +f 188 187 239 +f 188 239 240 +f 189 188 240 +f 189 240 241 +f 190 189 241 +f 190 241 242 +f 191 190 242 +f 191 242 243 +f 192 191 243 +f 192 243 244 +f 193 192 244 +f 193 244 245 +f 194 193 245 +f 194 245 246 +f 195 194 246 +f 195 246 247 +f 196 195 247 +f 196 247 248 +f 197 196 248 +f 197 248 249 +f 198 197 249 +f 198 249 250 +f 199 198 250 +f 199 250 251 +f 200 199 251 +f 200 251 252 +f 201 200 252 +f 201 252 253 +f 202 201 253 +f 202 253 254 +f 203 202 254 +f 203 254 255 +f 204 203 255 +f 204 255 256 +f 205 204 256 +f 205 256 257 +f 206 205 257 +f 206 257 258 +f 207 206 258 +f 207 258 259 +f 208 207 259 +f 208 259 260 +f 157 208 260 +f 157 260 209 +f 210 209 1 +f 210 1 2 +f 211 210 2 +f 211 2 3 +f 212 211 3 +f 212 3 4 +f 213 212 4 +f 213 4 5 +f 214 213 5 +f 214 5 6 +f 215 214 6 +f 215 6 7 +f 216 215 7 +f 216 7 8 +f 217 216 8 +f 217 8 9 +f 218 217 9 +f 218 9 10 +f 219 218 10 +f 219 10 11 +f 220 219 11 +f 220 11 12 +f 221 220 12 +f 221 12 13 +f 222 221 13 +f 222 13 14 +f 223 222 14 +f 223 14 15 +f 224 223 15 +f 224 15 16 +f 225 224 16 +f 225 16 17 +f 226 225 17 +f 226 17 18 +f 227 226 18 +f 227 18 19 +f 228 227 19 +f 228 19 20 +f 229 228 20 +f 229 20 21 +f 230 229 21 +f 230 21 22 +f 231 230 22 +f 231 22 23 +f 232 231 23 +f 232 23 24 +f 233 232 24 +f 233 24 25 +f 234 233 25 +f 234 25 26 +f 235 234 26 +f 235 26 27 +f 236 235 27 +f 236 27 28 +f 237 236 28 +f 237 28 29 +f 238 237 29 +f 238 29 30 +f 239 238 30 +f 239 30 31 +f 240 239 31 +f 240 31 32 +f 241 240 32 +f 241 32 33 +f 242 241 33 +f 242 33 34 +f 243 242 34 +f 243 34 35 +f 244 243 35 +f 244 35 36 +f 245 244 36 +f 245 36 37 +f 246 245 37 +f 246 37 38 +f 247 246 38 +f 247 38 39 +f 248 247 39 +f 248 39 40 +f 249 248 40 +f 249 40 41 +f 250 249 41 +f 250 41 42 +f 251 250 42 +f 251 42 43 +f 252 251 43 +f 252 43 44 +f 253 252 44 +f 253 44 45 +f 254 253 45 +f 254 45 46 +f 255 254 46 +f 255 46 47 +f 256 255 47 +f 256 47 48 +f 257 256 48 +f 257 48 49 +f 258 257 49 +f 258 49 50 +f 259 258 50 +f 259 50 51 +f 260 259 51 +f 260 51 52 +f 209 260 52 +f 209 52 1 +f 2 1 261 +f 3 2 261 +f 4 3 261 +f 5 4 261 +f 6 5 261 +f 7 6 261 +f 8 7 261 +f 9 8 261 +f 10 9 261 +f 11 10 261 +f 12 11 261 +f 13 12 261 +f 14 13 261 +f 15 14 261 +f 16 15 261 +f 17 16 261 +f 18 17 261 +f 19 18 261 +f 20 19 261 +f 21 20 261 +f 22 21 261 +f 23 22 261 +f 24 23 261 +f 25 24 261 +f 26 25 261 +f 27 26 261 +f 28 27 261 +f 29 28 261 +f 30 29 261 +f 31 30 261 +f 32 31 261 +f 33 32 261 +f 34 33 261 +f 35 34 261 +f 36 35 261 +f 37 36 261 +f 38 37 261 +f 39 38 261 +f 40 39 261 +f 41 40 261 +f 42 41 261 +f 43 42 261 +f 44 43 261 +f 45 44 261 +f 46 45 261 +f 47 46 261 +f 48 47 261 +f 49 48 261 +f 50 49 261 +f 51 50 261 +f 52 51 261 +f 1 52 261 +f 263 262 105 +f 263 105 106 +f 264 263 106 +f 264 106 107 +f 265 264 107 +f 265 107 108 +f 266 265 108 +f 266 108 109 +f 267 266 109 +f 267 109 110 +f 268 267 110 +f 268 110 111 +f 269 268 111 +f 269 111 112 +f 270 269 112 +f 270 112 113 +f 271 270 113 +f 271 113 114 +f 272 271 114 +f 272 114 115 +f 273 272 115 +f 273 115 116 +f 274 273 116 +f 274 116 117 +f 275 274 117 +f 275 117 118 +f 276 275 118 +f 276 118 119 +f 277 276 119 +f 277 119 120 +f 278 277 120 +f 278 120 121 +f 279 278 121 +f 279 121 122 +f 280 279 122 +f 280 122 123 +f 281 280 123 +f 281 123 124 +f 282 281 124 +f 282 124 125 +f 283 282 125 +f 283 125 126 +f 284 283 126 +f 284 126 127 +f 285 284 127 +f 285 127 128 +f 286 285 128 +f 286 128 129 +f 287 286 129 +f 287 129 130 +f 288 287 130 +f 288 130 131 +f 289 288 131 +f 289 131 132 +f 290 289 132 +f 290 132 133 +f 291 290 133 +f 291 133 134 +f 292 291 134 +f 292 134 135 +f 293 292 135 +f 293 135 136 +f 294 293 136 +f 294 136 137 +f 295 294 137 +f 295 137 138 +f 296 295 138 +f 296 138 139 +f 297 296 139 +f 297 139 140 +f 298 297 140 +f 298 140 141 +f 299 298 141 +f 299 141 142 +f 300 299 142 +f 300 142 143 +f 301 300 143 +f 301 143 144 +f 302 301 144 +f 302 144 145 +f 303 302 145 +f 303 145 146 +f 304 303 146 +f 304 146 147 +f 305 304 147 +f 305 147 148 +f 306 305 148 +f 306 148 149 +f 307 306 149 +f 307 149 150 +f 308 307 150 +f 308 150 151 +f 309 308 151 +f 309 151 152 +f 310 309 152 +f 310 152 153 +f 311 310 153 +f 311 153 154 +f 312 311 154 +f 312 154 155 +f 313 312 155 +f 313 155 156 +f 262 313 156 +f 262 156 105 +f 53 54 315 +f 53 315 314 +f 54 55 316 +f 54 316 315 +f 55 56 317 +f 55 317 316 +f 56 57 318 +f 56 318 317 +f 57 58 319 +f 57 319 318 +f 58 59 320 +f 58 320 319 +f 59 60 321 +f 59 321 320 +f 60 61 322 +f 60 322 321 +f 61 62 323 +f 61 323 322 +f 62 63 324 +f 62 324 323 +f 63 64 325 +f 63 325 324 +f 64 65 326 +f 64 326 325 +f 65 66 327 +f 65 327 326 +f 66 67 328 +f 66 328 327 +f 67 68 329 +f 67 329 328 +f 68 69 330 +f 68 330 329 +f 69 70 331 +f 69 331 330 +f 70 71 332 +f 70 332 331 +f 71 72 333 +f 71 333 332 +f 72 73 334 +f 72 334 333 +f 73 74 335 +f 73 335 334 +f 74 75 336 +f 74 336 335 +f 75 76 337 +f 75 337 336 +f 76 77 338 +f 76 338 337 +f 77 78 339 +f 77 339 338 +f 78 79 340 +f 78 340 339 +f 79 80 341 +f 79 341 340 +f 80 81 342 +f 80 342 341 +f 81 82 343 +f 81 343 342 +f 82 83 344 +f 82 344 343 +f 83 84 345 +f 83 345 344 +f 84 85 346 +f 84 346 345 +f 85 86 347 +f 85 347 346 +f 86 87 348 +f 86 348 347 +f 87 88 349 +f 87 349 348 +f 88 89 350 +f 88 350 349 +f 89 90 351 +f 89 351 350 +f 90 91 352 +f 90 352 351 +f 91 92 353 +f 91 353 352 +f 92 93 354 +f 92 354 353 +f 93 94 355 +f 93 355 354 +f 94 95 356 +f 94 356 355 +f 95 96 357 +f 95 357 356 +f 96 97 358 +f 96 358 357 +f 97 98 359 +f 97 359 358 +f 98 99 360 +f 98 360 359 +f 99 100 361 +f 99 361 360 +f 100 101 362 +f 100 362 361 +f 101 102 363 +f 101 363 362 +f 102 103 364 +f 102 364 363 +f 103 104 365 +f 103 365 364 +f 104 53 314 +f 104 314 365 +f 314 315 367 +f 314 367 366 +f 315 316 368 +f 315 368 367 +f 316 317 369 +f 316 369 368 +f 317 318 370 +f 317 370 369 +f 318 319 371 +f 318 371 370 +f 319 320 372 +f 319 372 371 +f 320 321 373 +f 320 373 372 +f 321 322 374 +f 321 374 373 +f 322 323 375 +f 322 375 374 +f 323 324 376 +f 323 376 375 +f 324 325 377 +f 324 377 376 +f 325 326 378 +f 325 378 377 +f 326 327 379 +f 326 379 378 +f 327 328 380 +f 327 380 379 +f 328 329 381 +f 328 381 380 +f 329 330 382 +f 329 382 381 +f 330 331 383 +f 330 383 382 +f 331 332 384 +f 331 384 383 +f 332 333 385 +f 332 385 384 +f 333 334 386 +f 333 386 385 +f 334 335 387 +f 334 387 386 +f 335 336 388 +f 335 388 387 +f 336 337 389 +f 336 389 388 +f 337 338 390 +f 337 390 389 +f 338 339 391 +f 338 391 390 +f 339 340 392 +f 339 392 391 +f 340 341 393 +f 340 393 392 +f 341 342 394 +f 341 394 393 +f 342 343 395 +f 342 395 394 +f 343 344 396 +f 343 396 395 +f 344 345 397 +f 344 397 396 +f 345 346 398 +f 345 398 397 +f 346 347 399 +f 346 399 398 +f 347 348 400 +f 347 400 399 +f 348 349 401 +f 348 401 400 +f 349 350 402 +f 349 402 401 +f 350 351 403 +f 350 403 402 +f 351 352 404 +f 351 404 403 +f 352 353 405 +f 352 405 404 +f 353 354 406 +f 353 406 405 +f 354 355 407 +f 354 407 406 +f 355 356 408 +f 355 408 407 +f 356 357 409 +f 356 409 408 +f 357 358 410 +f 357 410 409 +f 358 359 411 +f 358 411 410 +f 359 360 412 +f 359 412 411 +f 360 361 413 +f 360 413 412 +f 361 362 414 +f 361 414 413 +f 362 363 415 +f 362 415 414 +f 363 364 416 +f 363 416 415 +f 364 365 417 +f 364 417 416 +f 365 314 366 +f 365 366 417 +f 679 470 471 +f 679 471 680 +f 680 471 472 +f 680 472 681 +f 681 472 473 +f 681 473 682 +f 682 473 474 +f 682 474 683 +f 683 474 475 +f 683 475 684 +f 684 475 476 +f 684 476 685 +f 685 476 477 +f 685 477 686 +f 686 477 478 +f 686 478 687 +f 687 478 479 +f 687 479 688 +f 688 479 480 +f 688 480 689 +f 689 480 481 +f 689 481 690 +f 690 481 482 +f 690 482 691 +f 691 482 483 +f 691 483 692 +f 692 483 484 +f 692 484 693 +f 693 484 485 +f 693 485 694 +f 694 485 486 +f 694 486 695 +f 695 486 487 +f 695 487 696 +f 696 487 488 +f 696 488 697 +f 697 488 489 +f 697 489 698 +f 698 489 490 +f 698 490 699 +f 699 490 491 +f 699 491 700 +f 700 491 492 +f 700 492 701 +f 701 492 493 +f 701 493 702 +f 702 493 494 +f 702 494 703 +f 703 494 495 +f 703 495 704 +f 704 495 496 +f 704 496 705 +f 705 496 497 +f 705 497 706 +f 706 497 498 +f 706 498 707 +f 707 498 499 +f 707 499 708 +f 708 499 500 +f 708 500 709 +f 709 500 501 +f 709 501 710 +f 710 501 502 +f 710 502 711 +f 711 502 503 +f 711 503 712 +f 712 503 504 +f 712 504 713 +f 713 504 505 +f 713 505 714 +f 714 505 506 +f 714 506 715 +f 715 506 507 +f 715 507 716 +f 716 507 508 +f 716 508 717 +f 717 508 509 +f 717 509 718 +f 718 509 510 +f 718 510 719 +f 719 510 511 +f 719 511 720 +f 720 511 512 +f 720 512 721 +f 721 512 513 +f 721 513 722 +f 722 513 514 +f 722 514 723 +f 723 514 515 +f 723 515 724 +f 724 515 516 +f 724 516 725 +f 725 516 517 +f 725 517 726 +f 726 517 518 +f 726 518 727 +f 727 518 519 +f 727 519 728 +f 728 519 520 +f 728 520 729 +f 729 520 521 +f 729 521 730 +f 730 521 470 +f 730 470 679 +f 574 522 523 +f 574 523 575 +f 575 523 524 +f 575 524 576 +f 576 524 525 +f 576 525 577 +f 577 525 526 +f 577 526 578 +f 578 526 527 +f 578 527 579 +f 579 527 528 +f 579 528 580 +f 580 528 529 +f 580 529 581 +f 581 529 530 +f 581 530 582 +f 582 530 531 +f 582 531 583 +f 583 531 532 +f 583 532 584 +f 584 532 533 +f 584 533 585 +f 585 533 534 +f 585 534 586 +f 586 534 535 +f 586 535 587 +f 587 535 536 +f 587 536 588 +f 588 536 537 +f 588 537 589 +f 589 537 538 +f 589 538 590 +f 590 538 539 +f 590 539 591 +f 591 539 540 +f 591 540 592 +f 592 540 541 +f 592 541 593 +f 593 541 542 +f 593 542 594 +f 594 542 543 +f 594 543 595 +f 595 543 544 +f 595 544 596 +f 596 544 545 +f 596 545 597 +f 597 545 546 +f 597 546 598 +f 598 546 547 +f 598 547 599 +f 599 547 548 +f 599 548 600 +f 600 548 549 +f 600 549 601 +f 601 549 550 +f 601 550 602 +f 602 550 551 +f 602 551 603 +f 603 551 552 +f 603 552 604 +f 604 552 553 +f 604 553 605 +f 605 553 554 +f 605 554 606 +f 606 554 555 +f 606 555 607 +f 607 555 556 +f 607 556 608 +f 608 556 557 +f 608 557 609 +f 609 557 558 +f 609 558 610 +f 610 558 559 +f 610 559 611 +f 611 559 560 +f 611 560 612 +f 612 560 561 +f 612 561 613 +f 613 561 562 +f 613 562 614 +f 614 562 563 +f 614 563 615 +f 615 563 564 +f 615 564 616 +f 616 564 565 +f 616 565 617 +f 617 565 566 +f 617 566 618 +f 618 566 567 +f 618 567 619 +f 619 567 568 +f 619 568 620 +f 620 568 569 +f 620 569 621 +f 621 569 570 +f 621 570 622 +f 622 570 571 +f 622 571 623 +f 623 571 572 +f 623 572 624 +f 624 572 573 +f 624 573 625 +f 625 573 522 +f 625 522 574 +f 626 574 575 +f 626 575 627 +f 627 575 576 +f 627 576 628 +f 628 576 577 +f 628 577 629 +f 629 577 578 +f 629 578 630 +f 630 578 579 +f 630 579 631 +f 631 579 580 +f 631 580 632 +f 632 580 581 +f 632 581 633 +f 633 581 582 +f 633 582 634 +f 634 582 583 +f 634 583 635 +f 635 583 584 +f 635 584 636 +f 636 584 585 +f 636 585 637 +f 637 585 586 +f 637 586 638 +f 638 586 587 +f 638 587 639 +f 639 587 588 +f 639 588 640 +f 640 588 589 +f 640 589 641 +f 641 589 590 +f 641 590 642 +f 642 590 591 +f 642 591 643 +f 643 591 592 +f 643 592 644 +f 644 592 593 +f 644 593 645 +f 645 593 594 +f 645 594 646 +f 646 594 595 +f 646 595 647 +f 647 595 596 +f 647 596 648 +f 648 596 597 +f 648 597 649 +f 649 597 598 +f 649 598 650 +f 650 598 599 +f 650 599 651 +f 651 599 600 +f 651 600 652 +f 652 600 601 +f 652 601 653 +f 653 601 602 +f 653 602 654 +f 654 602 603 +f 654 603 655 +f 655 603 604 +f 655 604 656 +f 656 604 605 +f 656 605 657 +f 657 605 606 +f 657 606 658 +f 658 606 607 +f 658 607 659 +f 659 607 608 +f 659 608 660 +f 660 608 609 +f 660 609 661 +f 661 609 610 +f 661 610 662 +f 662 610 611 +f 662 611 663 +f 663 611 612 +f 663 612 664 +f 664 612 613 +f 664 613 665 +f 665 613 614 +f 665 614 666 +f 666 614 615 +f 666 615 667 +f 667 615 616 +f 667 616 668 +f 668 616 617 +f 668 617 669 +f 669 617 618 +f 669 618 670 +f 670 618 619 +f 670 619 671 +f 671 619 620 +f 671 620 672 +f 672 620 621 +f 672 621 673 +f 673 621 622 +f 673 622 674 +f 674 622 623 +f 674 623 675 +f 675 623 624 +f 675 624 676 +f 676 624 625 +f 676 625 677 +f 677 625 574 +f 677 574 626 +f 418 626 627 +f 418 627 419 +f 419 627 628 +f 419 628 420 +f 420 628 629 +f 420 629 421 +f 421 629 630 +f 421 630 422 +f 422 630 631 +f 422 631 423 +f 423 631 632 +f 423 632 424 +f 424 632 633 +f 424 633 425 +f 425 633 634 +f 425 634 426 +f 426 634 635 +f 426 635 427 +f 427 635 636 +f 427 636 428 +f 428 636 637 +f 428 637 429 +f 429 637 638 +f 429 638 430 +f 430 638 639 +f 430 639 431 +f 431 639 640 +f 431 640 432 +f 432 640 641 +f 432 641 433 +f 433 641 642 +f 433 642 434 +f 434 642 643 +f 434 643 435 +f 435 643 644 +f 435 644 436 +f 436 644 645 +f 436 645 437 +f 437 645 646 +f 437 646 438 +f 438 646 647 +f 438 647 439 +f 439 647 648 +f 439 648 440 +f 440 648 649 +f 440 649 441 +f 441 649 650 +f 441 650 442 +f 442 650 651 +f 442 651 443 +f 443 651 652 +f 443 652 444 +f 444 652 653 +f 444 653 445 +f 445 653 654 +f 445 654 446 +f 446 654 655 +f 446 655 447 +f 447 655 656 +f 447 656 448 +f 448 656 657 +f 448 657 449 +f 449 657 658 +f 449 658 450 +f 450 658 659 +f 450 659 451 +f 451 659 660 +f 451 660 452 +f 452 660 661 +f 452 661 453 +f 453 661 662 +f 453 662 454 +f 454 662 663 +f 454 663 455 +f 455 663 664 +f 455 664 456 +f 456 664 665 +f 456 665 457 +f 457 665 666 +f 457 666 458 +f 458 666 667 +f 458 667 459 +f 459 667 668 +f 459 668 460 +f 460 668 669 +f 460 669 461 +f 461 669 670 +f 461 670 462 +f 462 670 671 +f 462 671 463 +f 463 671 672 +f 463 672 464 +f 464 672 673 +f 464 673 465 +f 465 673 674 +f 465 674 466 +f 466 674 675 +f 466 675 467 +f 467 675 676 +f 467 676 468 +f 468 676 677 +f 468 677 469 +f 469 677 626 +f 469 626 418 +f 678 418 419 +f 678 419 420 +f 678 420 421 +f 678 421 422 +f 678 422 423 +f 678 423 424 +f 678 424 425 +f 678 425 426 +f 678 426 427 +f 678 427 428 +f 678 428 429 +f 678 429 430 +f 678 430 431 +f 678 431 432 +f 678 432 433 +f 678 433 434 +f 678 434 435 +f 678 435 436 +f 678 436 437 +f 678 437 438 +f 678 438 439 +f 678 439 440 +f 678 440 441 +f 678 441 442 +f 678 442 443 +f 678 443 444 +f 678 444 445 +f 678 445 446 +f 678 446 447 +f 678 447 448 +f 678 448 449 +f 678 449 450 +f 678 450 451 +f 678 451 452 +f 678 452 453 +f 678 453 454 +f 678 454 455 +f 678 455 456 +f 678 456 457 +f 678 457 458 +f 678 458 459 +f 678 459 460 +f 678 460 461 +f 678 461 462 +f 678 462 463 +f 678 463 464 +f 678 464 465 +f 678 465 466 +f 678 466 467 +f 678 467 468 +f 678 468 469 +f 678 469 418 +f 522 679 680 +f 522 680 523 +f 523 680 681 +f 523 681 524 +f 524 681 682 +f 524 682 525 +f 525 682 683 +f 525 683 526 +f 526 683 684 +f 526 684 527 +f 527 684 685 +f 527 685 528 +f 528 685 686 +f 528 686 529 +f 529 686 687 +f 529 687 530 +f 530 687 688 +f 530 688 531 +f 531 688 689 +f 531 689 532 +f 532 689 690 +f 532 690 533 +f 533 690 691 +f 533 691 534 +f 534 691 692 +f 534 692 535 +f 535 692 693 +f 535 693 536 +f 536 693 694 +f 536 694 537 +f 537 694 695 +f 537 695 538 +f 538 695 696 +f 538 696 539 +f 539 696 697 +f 539 697 540 +f 540 697 698 +f 540 698 541 +f 541 698 699 +f 541 699 542 +f 542 699 700 +f 542 700 543 +f 543 700 701 +f 543 701 544 +f 544 701 702 +f 544 702 545 +f 545 702 703 +f 545 703 546 +f 546 703 704 +f 546 704 547 +f 547 704 705 +f 547 705 548 +f 548 705 706 +f 548 706 549 +f 549 706 707 +f 549 707 550 +f 550 707 708 +f 550 708 551 +f 551 708 709 +f 551 709 552 +f 552 709 710 +f 552 710 553 +f 553 710 711 +f 553 711 554 +f 554 711 712 +f 554 712 555 +f 555 712 713 +f 555 713 556 +f 556 713 714 +f 556 714 557 +f 557 714 715 +f 557 715 558 +f 558 715 716 +f 558 716 559 +f 559 716 717 +f 559 717 560 +f 560 717 718 +f 560 718 561 +f 561 718 719 +f 561 719 562 +f 562 719 720 +f 562 720 563 +f 563 720 721 +f 563 721 564 +f 564 721 722 +f 564 722 565 +f 565 722 723 +f 565 723 566 +f 566 723 724 +f 566 724 567 +f 567 724 725 +f 567 725 568 +f 568 725 726 +f 568 726 569 +f 569 726 727 +f 569 727 570 +f 570 727 728 +f 570 728 571 +f 571 728 729 +f 571 729 572 +f 572 729 730 +f 572 730 573 +f 573 730 679 +f 573 679 522 +f 732 471 470 +f 732 470 731 +f 733 472 471 +f 733 471 732 +f 734 473 472 +f 734 472 733 +f 735 474 473 +f 735 473 734 +f 736 475 474 +f 736 474 735 +f 737 476 475 +f 737 475 736 +f 738 477 476 +f 738 476 737 +f 739 478 477 +f 739 477 738 +f 740 479 478 +f 740 478 739 +f 741 480 479 +f 741 479 740 +f 742 481 480 +f 742 480 741 +f 743 482 481 +f 743 481 742 +f 744 483 482 +f 744 482 743 +f 745 484 483 +f 745 483 744 +f 746 485 484 +f 746 484 745 +f 747 486 485 +f 747 485 746 +f 748 487 486 +f 748 486 747 +f 749 488 487 +f 749 487 748 +f 750 489 488 +f 750 488 749 +f 751 490 489 +f 751 489 750 +f 752 491 490 +f 752 490 751 +f 753 492 491 +f 753 491 752 +f 754 493 492 +f 754 492 753 +f 755 494 493 +f 755 493 754 +f 756 495 494 +f 756 494 755 +f 757 496 495 +f 757 495 756 +f 758 497 496 +f 758 496 757 +f 759 498 497 +f 759 497 758 +f 760 499 498 +f 760 498 759 +f 761 500 499 +f 761 499 760 +f 762 501 500 +f 762 500 761 +f 763 502 501 +f 763 501 762 +f 764 503 502 +f 764 502 763 +f 765 504 503 +f 765 503 764 +f 766 505 504 +f 766 504 765 +f 767 506 505 +f 767 505 766 +f 768 507 506 +f 768 506 767 +f 769 508 507 +f 769 507 768 +f 770 509 508 +f 770 508 769 +f 771 510 509 +f 771 509 770 +f 772 511 510 +f 772 510 771 +f 773 512 511 +f 773 511 772 +f 774 513 512 +f 774 512 773 +f 775 514 513 +f 775 513 774 +f 776 515 514 +f 776 514 775 +f 777 516 515 +f 777 515 776 +f 778 517 516 +f 778 516 777 +f 779 518 517 +f 779 517 778 +f 780 519 518 +f 780 518 779 +f 781 520 519 +f 781 519 780 +f 782 521 520 +f 782 520 781 +f 731 470 521 +f 731 521 782 +f 784 732 731 +f 784 731 783 +f 785 733 732 +f 785 732 784 +f 786 734 733 +f 786 733 785 +f 787 735 734 +f 787 734 786 +f 788 736 735 +f 788 735 787 +f 789 737 736 +f 789 736 788 +f 790 738 737 +f 790 737 789 +f 791 739 738 +f 791 738 790 +f 792 740 739 +f 792 739 791 +f 793 741 740 +f 793 740 792 +f 794 742 741 +f 794 741 793 +f 795 743 742 +f 795 742 794 +f 796 744 743 +f 796 743 795 +f 797 745 744 +f 797 744 796 +f 798 746 745 +f 798 745 797 +f 799 747 746 +f 799 746 798 +f 800 748 747 +f 800 747 799 +f 801 749 748 +f 801 748 800 +f 802 750 749 +f 802 749 801 +f 803 751 750 +f 803 750 802 +f 804 752 751 +f 804 751 803 +f 805 753 752 +f 805 752 804 +f 806 754 753 +f 806 753 805 +f 807 755 754 +f 807 754 806 +f 808 756 755 +f 808 755 807 +f 809 757 756 +f 809 756 808 +f 810 758 757 +f 810 757 809 +f 811 759 758 +f 811 758 810 +f 812 760 759 +f 812 759 811 +f 813 761 760 +f 813 760 812 +f 814 762 761 +f 814 761 813 +f 815 763 762 +f 815 762 814 +f 816 764 763 +f 816 763 815 +f 817 765 764 +f 817 764 816 +f 818 766 765 +f 818 765 817 +f 819 767 766 +f 819 766 818 +f 820 768 767 +f 820 767 819 +f 821 769 768 +f 821 768 820 +f 822 770 769 +f 822 769 821 +f 823 771 770 +f 823 770 822 +f 824 772 771 +f 824 771 823 +f 825 773 772 +f 825 772 824 +f 826 774 773 +f 826 773 825 +f 827 775 774 +f 827 774 826 +f 828 776 775 +f 828 775 827 +f 829 777 776 +f 829 776 828 +f 830 778 777 +f 830 777 829 +f 831 779 778 +f 831 778 830 +f 832 780 779 +f 832 779 831 +f 833 781 780 +f 833 780 832 +f 834 782 781 +f 834 781 833 +f 783 731 782 +f 783 782 834 +f 366 367 784 +f 366 784 783 +f 367 368 785 +f 367 785 784 +f 368 369 786 +f 368 786 785 +f 369 370 787 +f 369 787 786 +f 370 371 788 +f 370 788 787 +f 371 372 789 +f 371 789 788 +f 372 373 790 +f 372 790 789 +f 373 374 791 +f 373 791 790 +f 374 375 792 +f 374 792 791 +f 375 376 793 +f 375 793 792 +f 376 377 794 +f 376 794 793 +f 377 378 795 +f 377 795 794 +f 378 379 796 +f 378 796 795 +f 379 380 797 +f 379 797 796 +f 380 381 798 +f 380 798 797 +f 381 382 799 +f 381 799 798 +f 382 383 800 +f 382 800 799 +f 383 384 801 +f 383 801 800 +f 384 385 802 +f 384 802 801 +f 385 386 803 +f 385 803 802 +f 386 387 804 +f 386 804 803 +f 387 388 805 +f 387 805 804 +f 388 389 806 +f 388 806 805 +f 389 390 807 +f 389 807 806 +f 390 391 808 +f 390 808 807 +f 391 392 809 +f 391 809 808 +f 392 393 810 +f 392 810 809 +f 393 394 811 +f 393 811 810 +f 394 395 812 +f 394 812 811 +f 395 396 813 +f 395 813 812 +f 396 397 814 +f 396 814 813 +f 397 398 815 +f 397 815 814 +f 398 399 816 +f 398 816 815 +f 399 400 817 +f 399 817 816 +f 400 401 818 +f 400 818 817 +f 401 402 819 +f 401 819 818 +f 402 403 820 +f 402 820 819 +f 403 404 821 +f 403 821 820 +f 404 405 822 +f 404 822 821 +f 405 406 823 +f 405 823 822 +f 406 407 824 +f 406 824 823 +f 407 408 825 +f 407 825 824 +f 408 409 826 +f 408 826 825 +f 409 410 827 +f 409 827 826 +f 410 411 828 +f 410 828 827 +f 411 412 829 +f 411 829 828 +f 412 413 830 +f 412 830 829 +f 413 414 831 +f 413 831 830 +f 414 415 832 +f 414 832 831 +f 415 416 833 +f 415 833 832 +f 416 417 834 +f 416 834 833 +f 417 366 783 +f 417 783 834 +o Test_Tube_AA_01_Test_Tube_tags________007 +v 0.01398442 0.00530359 0.03164062 +v 0.01324318 0.00695056 0.03164062 +v 0.01230883 0.00849617 0.03164062 +v 0.01119498 0.00991789 0.03164062 +v 0.00991789 0.01119498 0.03164062 +v 0.00849617 0.01230883 0.03164062 +v 0.00695056 0.01324318 0.03164062 +v 0.00530359 0.01398442 0.03164062 +v 0.00357929 0.01452174 0.03164062 +v 0.00180279 0.01484729 0.03164062 +v -0.00000000 0.01495634 0.03164062 +v -0.00180279 0.01484729 0.03164062 +v -0.00357929 0.01452174 0.03164062 +v -0.00530359 0.01398442 0.03164062 +v -0.00695056 0.01324318 0.03164062 +v -0.00849617 0.01230882 0.03164062 +v -0.00991789 0.01119498 0.03164062 +v 0.01398442 0.00530359 0.07457005 +v 0.01324318 0.00695056 0.07457005 +v 0.01230883 0.00849617 0.07457005 +v 0.01119498 0.00991789 0.07457005 +v 0.00991789 0.01119498 0.07457005 +v 0.00849617 0.01230883 0.07457005 +v 0.00695056 0.01324318 0.07457005 +v 0.00530359 0.01398442 0.07457005 +v 0.00357929 0.01452174 0.07457005 +v 0.00180279 0.01484729 0.07457005 +v -0.00000000 0.01495634 0.07457005 +v -0.00180279 0.01484729 0.07457005 +v -0.00357929 0.01452174 0.07457005 +v -0.00530359 0.01398442 0.07457005 +v -0.00695056 0.01324318 0.07457005 +v -0.00849617 0.01230882 0.07457005 +v -0.00991789 0.01119498 0.07457005 +v 0.01428866 0.00432787 0.03164062 +v 0.01452012 0.00358511 0.03241861 +v -0.01119084 0.00992228 0.03241861 +v -0.01064072 0.01047240 0.03164062 +v 0.01452012 0.00358510 0.07379206 +v 0.01428866 0.00432787 0.07457005 +v -0.01064072 0.01047240 0.07457005 +v -0.01119084 0.00992228 0.07379206 +v 0.01445232 0.00380266 0.03186849 +v 0.01450246 0.00364175 0.03212093 +v 0.01437722 0.00404367 0.03169995 +v 0.01445232 0.00380266 0.07434218 +v 0.01437722 0.00404367 0.07451072 +v 0.01450246 0.00364175 0.07408974 +v -0.01102972 0.01008341 0.03186849 +v -0.01085121 0.01026191 0.03169995 +v -0.01114889 0.00996424 0.03212093 +v -0.01102972 0.01008341 0.07434218 +v -0.01114889 0.00996424 0.07408974 +v -0.01085121 0.01026191 0.07451072 +v 0.01389092 0.00526813 0.03164062 +v 0.01315464 0.00690409 0.03164062 +v 0.01222653 0.00843936 0.03164062 +v 0.01112013 0.00985158 0.03164062 +v 0.00985158 0.01112013 0.03164062 +v 0.00843936 0.01222653 0.03164062 +v 0.00690409 0.01315464 0.03164062 +v 0.00526813 0.01389092 0.03164062 +v 0.00355536 0.01442464 0.03164062 +v 0.00179073 0.01474802 0.03164062 +v -0.00000000 0.01485634 0.03164062 +v -0.00179074 0.01474802 0.03164062 +v -0.00355536 0.01442464 0.03164062 +v -0.00526813 0.01389092 0.03164062 +v -0.00690409 0.01315463 0.03164062 +v -0.00843937 0.01222652 0.03164062 +v -0.00985158 0.01112013 0.03164062 +v 0.01389092 0.00526813 0.07457005 +v 0.01315464 0.00690409 0.07457005 +v 0.01222653 0.00843936 0.07457005 +v 0.01112013 0.00985158 0.07457005 +v 0.00985158 0.01112013 0.07457005 +v 0.00843936 0.01222653 0.07457005 +v 0.00690409 0.01315464 0.07457005 +v 0.00526813 0.01389092 0.07457005 +v 0.00355536 0.01442464 0.07457005 +v 0.00179073 0.01474802 0.07457005 +v -0.00000000 0.01485634 0.07457005 +v -0.00179074 0.01474802 0.07457005 +v -0.00355536 0.01442464 0.07457005 +v -0.00526813 0.01389092 0.07457005 +v -0.00690409 0.01315463 0.07457005 +v -0.00843937 0.01222652 0.07457005 +v -0.00985158 0.01112013 0.07457005 +v 0.01419319 0.00429812 0.03164062 +v 0.01442464 0.00355536 0.03241861 +v -0.01112013 0.00985157 0.03241861 +v -0.01057001 0.01040169 0.03164062 +v 0.01442464 0.00355536 0.07379206 +v 0.01419319 0.00429812 0.07457005 +v -0.01057001 0.01040169 0.07457005 +v -0.01112013 0.00985157 0.07379206 +v 0.01435685 0.00377291 0.03186849 +v 0.01440699 0.00361200 0.03212093 +v 0.01428175 0.00401392 0.03169995 +v 0.01435685 0.00377291 0.07434218 +v 0.01428175 0.00401392 0.07451072 +v 0.01440699 0.00361200 0.07408974 +v -0.01095901 0.01001270 0.03186849 +v -0.01078050 0.01019120 0.03169995 +v -0.01107818 0.00989352 0.03212093 +v -0.01095901 0.01001270 0.07434218 +v -0.01107818 0.00989352 0.07408974 +v -0.01078050 0.01019120 0.07451072 +f 881 880 882 +f 882 873 870 +f 870 878 877 +f 882 870 877 +f 877 879 869 +f 869 835 852 +f 877 869 852 +f 882 877 852 +f 881 882 852 +f 881 852 874 +f 835 836 853 +f 853 852 835 +f 836 837 854 +f 854 853 836 +f 837 838 855 +f 855 854 837 +f 838 839 856 +f 856 855 838 +f 839 840 857 +f 857 856 839 +f 840 841 858 +f 858 857 840 +f 841 842 859 +f 859 858 841 +f 842 843 860 +f 860 859 842 +f 843 844 861 +f 861 860 843 +f 844 845 862 +f 862 861 844 +f 845 846 863 +f 863 862 845 +f 846 847 864 +f 864 863 846 +f 847 848 865 +f 865 864 847 +f 848 849 866 +f 866 865 848 +f 849 850 867 +f 867 866 849 +f 850 851 868 +f 868 867 850 +f 886 888 875 +f 875 868 851 +f 851 872 884 +f 875 851 884 +f 884 883 885 +f 885 871 876 +f 884 885 876 +f 875 884 876 +f 886 875 876 +f 886 876 887 +f 936 934 935 +f 935 928 906 +f 936 935 906 +f 906 889 923 +f 923 933 931 +f 906 923 931 +f 936 906 931 +f 931 932 924 +f 936 931 924 +f 936 924 927 +f 907 890 889 +f 889 906 907 +f 908 891 890 +f 890 907 908 +f 909 892 891 +f 891 908 909 +f 910 893 892 +f 892 909 910 +f 911 894 893 +f 893 910 911 +f 912 895 894 +f 894 911 912 +f 913 896 895 +f 895 912 913 +f 914 897 896 +f 896 913 914 +f 915 898 897 +f 897 914 915 +f 916 899 898 +f 898 915 916 +f 917 900 899 +f 899 916 917 +f 918 901 900 +f 900 917 918 +f 919 902 901 +f 901 918 919 +f 920 903 902 +f 902 919 920 +f 921 904 903 +f 903 920 921 +f 922 905 904 +f 904 921 922 +f 929 942 940 +f 940 941 930 +f 929 940 930 +f 930 925 939 +f 939 937 938 +f 930 939 938 +f 929 930 938 +f 938 926 905 +f 929 938 905 +f 929 905 922 +f 924 932 878 +f 878 870 924 +f 932 931 877 +f 877 878 932 +f 931 933 879 +f 879 877 931 +f 933 923 869 +f 869 879 933 +f 923 889 835 +f 835 869 923 +f 906 928 874 +f 874 852 906 +f 928 935 881 +f 881 874 928 +f 935 934 880 +f 880 881 935 +f 934 936 882 +f 882 880 934 +f 936 927 873 +f 873 882 936 +f 927 924 870 +f 870 873 927 +f 907 906 852 +f 852 853 907 +f 889 890 836 +f 836 835 889 +f 908 907 853 +f 853 854 908 +f 890 891 837 +f 837 836 890 +f 909 908 854 +f 854 855 909 +f 891 892 838 +f 838 837 891 +f 910 909 855 +f 855 856 910 +f 892 893 839 +f 839 838 892 +f 911 910 856 +f 856 857 911 +f 893 894 840 +f 840 839 893 +f 912 911 857 +f 857 858 912 +f 894 895 841 +f 841 840 894 +f 913 912 858 +f 858 859 913 +f 895 896 842 +f 842 841 895 +f 914 913 859 +f 859 860 914 +f 896 897 843 +f 843 842 896 +f 915 914 860 +f 860 861 915 +f 897 898 844 +f 844 843 897 +f 916 915 861 +f 861 862 916 +f 898 899 845 +f 845 844 898 +f 917 916 862 +f 862 863 917 +f 899 900 846 +f 846 845 899 +f 918 917 863 +f 863 864 918 +f 900 901 847 +f 847 846 900 +f 919 918 864 +f 864 865 919 +f 901 902 848 +f 848 847 901 +f 920 919 865 +f 865 866 920 +f 902 903 849 +f 849 848 902 +f 921 920 866 +f 866 867 921 +f 903 904 850 +f 850 849 903 +f 922 921 867 +f 867 868 922 +f 904 905 851 +f 851 850 904 +f 905 926 872 +f 872 851 905 +f 926 938 884 +f 884 872 926 +f 938 937 883 +f 883 884 938 +f 937 939 885 +f 885 883 937 +f 939 925 871 +f 871 885 939 +f 925 930 876 +f 876 871 925 +f 930 941 887 +f 887 876 930 +f 941 940 886 +f 886 887 941 +f 940 942 888 +f 888 886 940 +f 942 929 875 +f 875 888 942 +f 929 922 868 +f 868 875 929 +o Test_Tube_AA_01_Test_Tube_Test_Tube_lid________006 +v 0.01494539 0.00000000 0.10958172 +v 0.01712596 0.00207947 0.10727537 +v 0.01675044 0.00412861 0.10727537 +v 0.01613066 0.00611755 0.10727537 +v 0.01527566 0.00801728 0.10727537 +v 0.01419790 0.00980011 0.10727537 +v 0.01291311 0.01144002 0.10727537 +v 0.01144002 0.01291311 0.10727537 +v 0.00980011 0.01419790 0.10727537 +v 0.00801728 0.01527566 0.10727537 +v 0.00611755 0.01613066 0.10727537 +v 0.00412861 0.01675044 0.10727537 +v 0.00207947 0.01712596 0.10727537 +v -0.00000000 0.01725174 0.10727537 +v -0.00207947 0.01712596 0.10727537 +v -0.00412862 0.01675044 0.10727537 +v -0.00611756 0.01613066 0.10727537 +v -0.00801729 0.01527566 0.10727537 +v -0.00980011 0.01419790 0.10727537 +v -0.01144003 0.01291311 0.10727537 +v -0.01291312 0.01144001 0.10727537 +v -0.01419791 0.00980010 0.10727537 +v -0.01527566 0.00801727 0.10727537 +v -0.01613066 0.00611754 0.10727537 +v -0.01675044 0.00412860 0.10727537 +v -0.01712596 0.00207945 0.10727537 +v -0.01725174 -0.00000001 0.10727537 +v -0.01712595 -0.00207948 0.10727537 +v -0.01675043 -0.00412863 0.10727537 +v -0.01613065 -0.00611757 0.10727537 +v -0.01527565 -0.00801730 0.10727537 +v -0.01419789 -0.00980012 0.10727537 +v -0.01291310 -0.01144003 0.10727537 +v -0.01144001 -0.01291313 0.10727537 +v -0.00980009 -0.01419792 0.10727537 +v -0.00801727 -0.01527567 0.10727537 +v -0.00611753 -0.01613067 0.10727537 +v -0.00412859 -0.01675044 0.10727537 +v -0.00207944 -0.01712596 0.10727537 +v 0.00000002 -0.01725174 0.10727537 +v 0.00207949 -0.01712595 0.10727537 +v 0.00412864 -0.01675043 0.10727537 +v 0.00611758 -0.01613065 0.10727537 +v 0.00801731 -0.01527565 0.10727537 +v 0.00980013 -0.01419789 0.10727537 +v 0.01144004 -0.01291309 0.10727537 +v 0.01291313 -0.01144000 0.10727537 +v 0.01419792 -0.00980008 0.10727537 +v 0.01527567 -0.00801725 0.10727537 +v 0.01613067 -0.00611752 0.10727537 +v 0.01675045 -0.00412858 0.10727537 +v 0.01483642 -0.00180144 0.10958172 +v 0.01725174 0.00000000 0.09952368 +v 0.01712596 0.00207947 0.09952368 +v 0.01675044 0.00412861 0.09952368 +v 0.01613066 0.00611755 0.09952368 +v 0.01527566 0.00801728 0.09952368 +v 0.01419790 0.00980011 0.09952368 +v 0.01291311 0.01144002 0.09952368 +v 0.01144002 0.01291311 0.09952368 +v 0.00980011 0.01419790 0.09952368 +v 0.00801728 0.01527566 0.09952368 +v 0.00611755 0.01613066 0.09952368 +v 0.00412861 0.01675044 0.09952368 +v 0.00207947 0.01712596 0.09952368 +v -0.00000000 0.01725174 0.09952368 +v -0.00207947 0.01712596 0.09952368 +v -0.00412862 0.01675044 0.09952368 +v -0.00611756 0.01613066 0.09952368 +v -0.00801729 0.01527566 0.09952368 +v -0.00980011 0.01419790 0.09952368 +v -0.01144003 0.01291311 0.09952368 +v -0.01291312 0.01144001 0.09952368 +v -0.01419791 0.00980010 0.09952368 +v -0.01527566 0.00801727 0.09952368 +v -0.01613066 0.00611754 0.09952368 +v -0.01675044 0.00412860 0.09952368 +v -0.01712596 0.00207945 0.09952368 +v -0.01725174 -0.00000001 0.09952368 +v -0.01712595 -0.00207948 0.09952368 +v -0.01675043 -0.00412863 0.09952368 +v -0.01613065 -0.00611757 0.09952368 +v -0.01527565 -0.00801730 0.09952368 +v -0.01419789 -0.00980012 0.09952368 +v -0.01291310 -0.01144003 0.09952368 +v -0.01144001 -0.01291313 0.09952368 +v -0.00980009 -0.01419792 0.09952368 +v -0.00801727 -0.01527567 0.09952368 +v -0.00611753 -0.01613067 0.09952368 +v -0.00412859 -0.01675044 0.09952368 +v -0.00207944 -0.01712596 0.09952368 +v 0.00000002 -0.01725174 0.09952368 +v 0.00207949 -0.01712595 0.09952368 +v 0.00412864 -0.01675043 0.09952368 +v 0.00611758 -0.01613065 0.09952368 +v 0.00801731 -0.01527565 0.09952368 +v 0.00980013 -0.01419789 0.09952368 +v 0.01144004 -0.01291309 0.09952368 +v 0.01291313 -0.01144000 0.09952368 +v 0.01419792 -0.00980008 0.09952368 +v 0.01527567 -0.00801725 0.09952368 +v 0.01613067 -0.00611752 0.09952368 +v 0.01675045 -0.00412858 0.09952368 +v 0.01712596 -0.00207943 0.09952368 +v 0.00178171 0.00000000 0.10958172 +v 0.01725174 0.00000000 0.10727537 +v 0.01483642 0.00180147 0.10958172 +v 0.01451110 0.00357667 0.10958172 +v 0.01397418 0.00529971 0.10958172 +v 0.01323348 0.00694547 0.10958172 +v 0.01229981 0.00848995 0.10958172 +v 0.01118678 0.00991062 0.10958172 +v 0.00991063 0.01118678 0.10958172 +v 0.00848995 0.01229981 0.10958172 +v 0.00694547 0.01323348 0.10958172 +v 0.00529971 0.01397418 0.10958172 +v 0.00357667 0.01451110 0.10958172 +v 0.00180147 0.01483642 0.10958172 +v -0.00000000 0.01494539 0.10958172 +v -0.00180147 0.01483642 0.10958172 +v -0.00357667 0.01451110 0.10958172 +v -0.00529971 0.01397418 0.10958172 +v -0.00694547 0.01323348 0.10958172 +v -0.00848995 0.01229981 0.10958172 +v -0.00991063 0.01118678 0.10958172 +v -0.01118679 0.00991062 0.10958172 +v -0.01229982 0.00848994 0.10958172 +v -0.01323349 0.00694546 0.10958172 +v -0.01397418 0.00529970 0.10958172 +v -0.01451110 0.00357666 0.10958172 +v -0.01483642 0.00180146 0.10958172 +v -0.01494539 -0.00000001 0.10958172 +v -0.01483642 -0.00180148 0.10958172 +v -0.01451110 -0.00357668 0.10958172 +v -0.01397417 -0.00529972 0.10958172 +v -0.01323348 -0.00694548 0.10958172 +v -0.01229980 -0.00848996 0.10958172 +v -0.01118677 -0.00991064 0.10958172 +v -0.00991061 -0.01118680 0.10958172 +v -0.00848993 -0.01229982 0.10958172 +v -0.00694545 -0.01323349 0.10958172 +v -0.00529969 -0.01397419 0.10958172 +v -0.00357664 -0.01451111 0.10958172 +v -0.00180145 -0.01483642 0.10958172 +v 0.00000002 -0.01494539 0.10958172 +v 0.00180149 -0.01483642 0.10958172 +v 0.00357669 -0.01451110 0.10958172 +v 0.00529973 -0.01397417 0.10958172 +v 0.00694549 -0.01323347 0.10958172 +v 0.00848997 -0.01229980 0.10958172 +v 0.00991065 -0.01118677 0.10958172 +v 0.01118680 -0.00991060 0.10958172 +v 0.01229983 -0.00848993 0.10958172 +v 0.01323350 -0.00694544 0.10958172 +v 0.01397419 -0.00529968 0.10958172 +v 0.01451111 -0.00357664 0.10958172 +v 0.01712596 -0.00207943 0.10727537 +v 0.01577567 0.00000000 0.10948946 +v 0.01642145 0.00000000 0.10921270 +v 0.01688273 0.00000000 0.10875143 +v 0.01715949 0.00000000 0.10810565 +v 0.01703438 0.00206835 0.10810565 +v 0.01675963 0.00203499 0.10875143 +v 0.01630172 0.00197939 0.10921270 +v 0.01566065 0.00190155 0.10948946 +v 0.01666087 0.00410653 0.10810565 +v 0.01639215 0.00404030 0.10875143 +v 0.01594428 0.00392991 0.10921270 +v 0.01531726 0.00377537 0.10948946 +v 0.01604440 0.00608484 0.10810565 +v 0.01578562 0.00598670 0.10875143 +v 0.01535433 0.00582313 0.10921270 +v 0.01475051 0.00559413 0.10948946 +v 0.01519397 0.00797441 0.10810565 +v 0.01494891 0.00784579 0.10875143 +v 0.01454047 0.00763143 0.10921270 +v 0.01396867 0.00733132 0.10948946 +v 0.01412198 0.00974770 0.10810565 +v 0.01389421 0.00959048 0.10875143 +v 0.01351459 0.00932845 0.10921270 +v 0.01298313 0.00896160 0.10948946 +v 0.01284406 0.01137884 0.10810565 +v 0.01263690 0.01119532 0.10875143 +v 0.01229164 0.01088944 0.10921270 +v 0.01180826 0.01046121 0.10948946 +v 0.01137884 0.01284406 0.10810565 +v 0.01119532 0.01263690 0.10875143 +v 0.01088944 0.01229163 0.10921270 +v 0.01046121 0.01180826 0.10948946 +v 0.00974770 0.01412198 0.10810565 +v 0.00959048 0.01389421 0.10875143 +v 0.00932845 0.01351459 0.10921270 +v 0.00896161 0.01298313 0.10948946 +v 0.00797441 0.01519397 0.10810565 +v 0.00784579 0.01494891 0.10875143 +v 0.00763143 0.01454047 0.10921270 +v 0.00733132 0.01396867 0.10948946 +v 0.00608484 0.01604440 0.10810565 +v 0.00598670 0.01578562 0.10875143 +v 0.00582313 0.01535433 0.10921270 +v 0.00559413 0.01475051 0.10948946 +v 0.00410653 0.01666087 0.10810565 +v 0.00404030 0.01639215 0.10875143 +v 0.00392991 0.01594428 0.10921270 +v 0.00377537 0.01531726 0.10948946 +v 0.00206835 0.01703438 0.10810565 +v 0.00203499 0.01675963 0.10875143 +v 0.00197939 0.01630172 0.10921270 +v 0.00190155 0.01566065 0.10948946 +v -0.00000000 0.01715949 0.10810565 +v -0.00000000 0.01688273 0.10875143 +v -0.00000000 0.01642145 0.10921270 +v -0.00000000 0.01577567 0.10948946 +v -0.00206835 0.01703438 0.10810565 +v -0.00203499 0.01675963 0.10875143 +v -0.00197939 0.01630172 0.10921270 +v -0.00190155 0.01566065 0.10948946 +v -0.00410654 0.01666086 0.10810565 +v -0.00404030 0.01639214 0.10875143 +v -0.00392991 0.01594428 0.10921270 +v -0.00377537 0.01531726 0.10948946 +v -0.00608484 0.01604440 0.10810565 +v -0.00598670 0.01578562 0.10875143 +v -0.00582313 0.01535432 0.10921270 +v -0.00559414 0.01475051 0.10948946 +v -0.00797442 0.01519397 0.10810565 +v -0.00784580 0.01494891 0.10875143 +v -0.00763144 0.01454047 0.10921270 +v -0.00733133 0.01396866 0.10948946 +v -0.00974771 0.01412198 0.10810565 +v -0.00959049 0.01389421 0.10875143 +v -0.00932845 0.01351459 0.10921270 +v -0.00896161 0.01298312 0.10948946 +v -0.01137885 0.01284405 0.10810565 +v -0.01119532 0.01263690 0.10875143 +v -0.01088944 0.01229163 0.10921270 +v -0.01046121 0.01180826 0.10948946 +v -0.01284407 0.01137884 0.10810565 +v -0.01263691 0.01119531 0.10875143 +v -0.01229164 0.01088943 0.10921270 +v -0.01180827 0.01046120 0.10948946 +v -0.01412199 0.00974769 0.10810565 +v -0.01389421 0.00959047 0.10875143 +v -0.01351460 0.00932844 0.10921270 +v -0.01298313 0.00896160 0.10948946 +v -0.01519398 0.00797440 0.10810565 +v -0.01494892 0.00784578 0.10875143 +v -0.01454048 0.00763142 0.10921270 +v -0.01396867 0.00733131 0.10948946 +v -0.01604440 0.00608483 0.10810565 +v -0.01578563 0.00598669 0.10875143 +v -0.01535433 0.00582312 0.10921270 +v -0.01475052 0.00559412 0.10948946 +v -0.01666087 0.00410652 0.10810565 +v -0.01639215 0.00404029 0.10875143 +v -0.01594428 0.00392990 0.10921270 +v -0.01531727 0.00377536 0.10948946 +v -0.01703438 0.00206833 0.10810565 +v -0.01675963 0.00203497 0.10875143 +v -0.01630173 0.00197938 0.10921270 +v -0.01566065 0.00190154 0.10948946 +v -0.01715949 -0.00000001 0.10810565 +v -0.01688273 -0.00000001 0.10875143 +v -0.01642145 -0.00000001 0.10921270 +v -0.01577567 -0.00000001 0.10948946 +v -0.01703437 -0.00206836 0.10810565 +v -0.01675963 -0.00203500 0.10875143 +v -0.01630172 -0.00197940 0.10921270 +v -0.01566065 -0.00190156 0.10948946 +v -0.01666086 -0.00410655 0.10810565 +v -0.01639214 -0.00404032 0.10875143 +v -0.01594427 -0.00392993 0.10921270 +v -0.01531726 -0.00377538 0.10948946 +v -0.01604439 -0.00608485 0.10810565 +v -0.01578562 -0.00598671 0.10875143 +v -0.01535432 -0.00582314 0.10921270 +v -0.01475051 -0.00559415 0.10948946 +v -0.01519396 -0.00797443 0.10810565 +v -0.01494890 -0.00784581 0.10875143 +v -0.01454047 -0.00763145 0.10921270 +v -0.01396866 -0.00733134 0.10948946 +v -0.01412197 -0.00974771 0.10810565 +v -0.01389420 -0.00959049 0.10875143 +v -0.01351458 -0.00932846 0.10921270 +v -0.01298312 -0.00896162 0.10948946 +v -0.01284405 -0.01137886 0.10810565 +v -0.01263689 -0.01119533 0.10875143 +v -0.01229162 -0.01088945 0.10921270 +v -0.01180825 -0.01046122 0.10948946 +v -0.01137883 -0.01284407 0.10810565 +v -0.01119530 -0.01263691 0.10875143 +v -0.01088942 -0.01229165 0.10921270 +v -0.01046119 -0.01180827 0.10948946 +v -0.00974768 -0.01412199 0.10810565 +v -0.00959046 -0.01389422 0.10875143 +v -0.00932843 -0.01351460 0.10921270 +v -0.00896159 -0.01298314 0.10948946 +v -0.00797439 -0.01519398 0.10810565 +v -0.00784578 -0.01494892 0.10875143 +v -0.00763141 -0.01454049 0.10921270 +v -0.00733130 -0.01396868 0.10948946 +v -0.00608482 -0.01604441 0.10810565 +v -0.00598668 -0.01578563 0.10875143 +v -0.00582311 -0.01535433 0.10921270 +v -0.00559411 -0.01475052 0.10948946 +v -0.00410651 -0.01666087 0.10810565 +v -0.00404028 -0.01639215 0.10875143 +v -0.00392989 -0.01594428 0.10921270 +v -0.00377534 -0.01531727 0.10948946 +v -0.00206832 -0.01703438 0.10810565 +v -0.00203496 -0.01675963 0.10875143 +v -0.00197936 -0.01630173 0.10921270 +v -0.00190153 -0.01566065 0.10948946 +v 0.00000002 -0.01715949 0.10810565 +v 0.00000002 -0.01688273 0.10875143 +v 0.00000002 -0.01642145 0.10921270 +v 0.00000002 -0.01577567 0.10948946 +v 0.00206837 -0.01703437 0.10810565 +v 0.00203501 -0.01675963 0.10875143 +v 0.00197941 -0.01630172 0.10921270 +v 0.00190157 -0.01566065 0.10948946 +v 0.00410656 -0.01666086 0.10810565 +v 0.00404033 -0.01639214 0.10875143 +v 0.00392994 -0.01594427 0.10921270 +v 0.00377539 -0.01531726 0.10948946 +v 0.00608486 -0.01604439 0.10810565 +v 0.00598672 -0.01578561 0.10875143 +v 0.00582315 -0.01535432 0.10921270 +v 0.00559416 -0.01475050 0.10948946 +v 0.00797444 -0.01519396 0.10810565 +v 0.00784582 -0.01494890 0.10875143 +v 0.00763145 -0.01454046 0.10921270 +v 0.00733135 -0.01396865 0.10948946 +v 0.00974772 -0.01412196 0.10810565 +v 0.00959050 -0.01389419 0.10875143 +v 0.00932847 -0.01351458 0.10921270 +v 0.00896163 -0.01298311 0.10948946 +v 0.01137887 -0.01284404 0.10810565 +v 0.01119534 -0.01263688 0.10875143 +v 0.01088946 -0.01229162 0.10921270 +v 0.01046123 -0.01180824 0.10948946 +v 0.01284408 -0.01137882 0.10810565 +v 0.01263692 -0.01119529 0.10875143 +v 0.01229165 -0.01088942 0.10921270 +v 0.01180828 -0.01046119 0.10948946 +v 0.01412200 -0.00974767 0.10810565 +v 0.01389423 -0.00959046 0.10875143 +v 0.01351461 -0.00932842 0.10921270 +v 0.01298314 -0.00896158 0.10948946 +v 0.01519399 -0.00797438 0.10810565 +v 0.01494893 -0.00784576 0.10875143 +v 0.01454049 -0.00763140 0.10921270 +v 0.01396868 -0.00733129 0.10948946 +v 0.01604441 -0.00608481 0.10810565 +v 0.01578563 -0.00598667 0.10875143 +v 0.01535434 -0.00582310 0.10921270 +v 0.01475052 -0.00559410 0.10948946 +v 0.01666087 -0.00410650 0.10810565 +v 0.01639215 -0.00404027 0.10875143 +v 0.01594429 -0.00392988 0.10921270 +v 0.01531727 -0.00377534 0.10948946 +v 0.01566066 -0.00190152 0.10948946 +v 0.01630173 -0.00197935 0.10921270 +v 0.01675963 -0.00203495 0.10875143 +v 0.01703438 -0.00206831 0.10810565 +v 0.01549937 0.00184157 0.10972436 +v 0.01560833 0.00004010 0.10972436 +v 0.01643862 0.00004010 0.10963210 +v 0.01708440 0.00004010 0.10935534 +v 0.01754567 0.00004010 0.10889407 +v 0.01782243 0.00004010 0.10824829 +v 0.01791469 0.00004010 0.10741801 +v 0.01791469 0.00004010 0.09966632 +v 0.01778890 0.00211957 0.09966632 +v 0.01778890 0.00211957 0.10741801 +v 0.01769732 0.00210845 0.10824829 +v 0.01742258 0.00207509 0.10889407 +v 0.01696467 0.00201949 0.10935534 +v 0.01632360 0.00194165 0.10963210 +v 0.01460827 0.00549730 0.10972436 +v 0.01514519 0.00377426 0.10972436 +v 0.01595135 0.00397296 0.10963210 +v 0.01657836 0.00412750 0.10935534 +v 0.01702623 0.00423789 0.10889407 +v 0.01729495 0.00430412 0.10824829 +v 0.01738452 0.00432620 0.10741801 +v 0.01738452 0.00432620 0.09966632 +v 0.01676474 0.00631514 0.09966632 +v 0.01676474 0.00631514 0.10741801 +v 0.01667848 0.00628243 0.10824829 +v 0.01641971 0.00618429 0.10889407 +v 0.01598841 0.00602072 0.10935534 +v 0.01538460 0.00579172 0.10963210 +v 0.01286819 0.00883354 0.10972436 +v 0.01380186 0.00728906 0.10972436 +v 0.01453704 0.00767492 0.10963210 +v 0.01510885 0.00797503 0.10935534 +v 0.01551729 0.00818939 0.10889407 +v 0.01576235 0.00831801 0.10824829 +v 0.01584403 0.00836088 0.10741801 +v 0.01584403 0.00836088 0.09966632 +v 0.01476628 0.01014370 0.09966632 +v 0.01476628 0.01014370 0.10741801 +v 0.01469036 0.01009129 0.10824829 +v 0.01446258 0.00993408 0.10889407 +v 0.01408297 0.00967204 0.10935534 +v 0.01355150 0.00930520 0.10963210 +v 0.01038026 0.01165641 0.10972436 +v 0.01165642 0.01038026 0.10972436 +v 0.01227789 0.01093084 0.10963210 +v 0.01276127 0.01135907 0.10935534 +v 0.01310653 0.01166495 0.10889407 +v 0.01331369 0.01184848 0.10824829 +v 0.01338275 0.01190965 0.10741801 +v 0.01338275 0.01190965 0.09966632 +v 0.01190965 0.01338274 0.09966632 +v 0.01190965 0.01338274 0.10741801 +v 0.01184848 0.01331369 0.10824829 +v 0.01166495 0.01310653 0.10889407 +v 0.01135907 0.01276127 0.10935534 +v 0.01093084 0.01227789 0.10963210 +v 0.00728906 0.01380186 0.10972436 +v 0.00883354 0.01286819 0.10972436 +v 0.00930520 0.01355150 0.10963210 +v 0.00967204 0.01408297 0.10935534 +v 0.00993408 0.01446258 0.10889407 +v 0.01009129 0.01469036 0.10824829 +v 0.01014370 0.01476628 0.10741801 +v 0.01014370 0.01476628 0.09966632 +v 0.00836088 0.01584403 0.09966632 +v 0.00836088 0.01584403 0.10741801 +v 0.00831801 0.01576235 0.10824829 +v 0.00818939 0.01551729 0.10889407 +v 0.00797503 0.01510885 0.10935534 +v 0.00767492 0.01453704 0.10963210 +v 0.00377425 0.01514519 0.10972436 +v 0.00549730 0.01460827 0.10972436 +v 0.00579172 0.01538460 0.10963210 +v 0.00602072 0.01598841 0.10935534 +v 0.00618429 0.01641971 0.10889407 +v 0.00628243 0.01667848 0.10824829 +v 0.00631514 0.01676474 0.10741801 +v 0.00631514 0.01676474 0.09966632 +v 0.00432620 0.01738452 0.09966632 +v 0.00432620 0.01738452 0.10741801 +v 0.00430412 0.01729495 0.10824829 +v 0.00423789 0.01702623 0.10889407 +v 0.00412750 0.01657836 0.10935534 +v 0.00397296 0.01595135 0.10963210 +v 0.00004010 0.01560833 0.10972436 +v 0.00184157 0.01549937 0.10972436 +v 0.00194165 0.01632360 0.10963210 +v 0.00201949 0.01696467 0.10935534 +v 0.00207509 0.01742258 0.10889407 +v 0.00210845 0.01769732 0.10824829 +v 0.00211957 0.01778890 0.10741801 +v 0.00211957 0.01778890 0.09966632 +v 0.00004010 0.01791469 0.09966632 +v 0.00004010 0.01791469 0.10741801 +v 0.00004010 0.01782243 0.10824829 +v 0.00004010 0.01754567 0.10889407 +v 0.00004010 0.01708440 0.10935534 +v 0.00004010 0.01643862 0.10963210 +v -0.00369639 0.01516438 0.10972436 +v -0.00192119 0.01548970 0.10972436 +v -0.00202127 0.01631393 0.10963210 +v -0.00209911 0.01695500 0.10935534 +v -0.00215471 0.01741291 0.10889407 +v -0.00218807 0.01768766 0.10824829 +v -0.00219919 0.01777924 0.10741801 +v -0.00219919 0.01777924 0.09966632 +v -0.00424833 0.01740372 0.09966632 +v -0.00424833 0.01740372 0.10741801 +v -0.00422626 0.01731414 0.10824829 +v -0.00416002 0.01704542 0.10889407 +v -0.00404963 0.01659756 0.10935534 +v -0.00389509 0.01597054 0.10963210 +v -0.00721805 0.01383913 0.10972436 +v -0.00557229 0.01457983 0.10972436 +v -0.00586671 0.01535616 0.10963210 +v -0.00609571 0.01595997 0.10935534 +v -0.00625928 0.01639127 0.10889407 +v -0.00635742 0.01665004 0.10824829 +v -0.00639014 0.01673630 0.10741801 +v -0.00639014 0.01673630 0.09966632 +v -0.00828987 0.01588130 0.09966632 +v -0.00828987 0.01588130 0.10741801 +v -0.00824700 0.01579961 0.10824829 +v -0.00811838 0.01555455 0.10889407 +v -0.00790401 0.01514612 0.10935534 +v -0.00760391 0.01457431 0.10963210 +v -0.01032023 0.01170959 0.10972436 +v -0.00889955 0.01282262 0.10972436 +v -0.00937121 0.01350594 0.10963210 +v -0.00973805 0.01403740 0.10935534 +v -0.01000009 0.01441702 0.10889407 +v -0.01015730 0.01464479 0.10824829 +v -0.01020971 0.01472071 0.10741801 +v -0.01020971 0.01472071 0.09966632 +v -0.01184963 0.01343592 0.09966632 +v -0.01184963 0.01343592 0.10741801 +v -0.01178845 0.01336687 0.10824829 +v -0.01160492 0.01315971 0.10889407 +v -0.01129904 0.01281444 0.10935534 +v -0.01087081 0.01233107 0.10963210 +v -0.01282263 0.00889954 0.10972436 +v -0.01170960 0.01032022 0.10972436 +v -0.01233108 0.01087080 0.10963210 +v -0.01281446 0.01129903 0.10935534 +v -0.01315972 0.01160491 0.10889407 +v -0.01336688 0.01178844 0.10824829 +v -0.01343593 0.01184961 0.10741801 +v -0.01343593 0.01184961 0.09966632 +v -0.01472072 0.01020970 0.09966632 +v -0.01472072 0.01020970 0.10741801 +v -0.01464480 0.01015729 0.10824829 +v -0.01441703 0.01000007 0.10889407 +v -0.01403741 0.00973804 0.10935534 +v -0.01350595 0.00937120 0.10963210 +v -0.01457983 0.00557228 0.10972436 +v -0.01383913 0.00721804 0.10972436 +v -0.01457432 0.00760389 0.10963210 +v -0.01514613 0.00790400 0.10935534 +v -0.01555456 0.00811836 0.10889407 +v -0.01579962 0.00824698 0.10824829 +v -0.01588131 0.00828985 0.10741801 +v -0.01588131 0.00828985 0.09966632 +v -0.01673631 0.00639012 0.09966632 +v -0.01673631 0.00639012 0.10741801 +v -0.01665005 0.00635741 0.10824829 +v -0.01639127 0.00625927 0.10889407 +v -0.01595998 0.00609570 0.10935534 +v -0.01535616 0.00586670 0.10963210 +v -0.01548970 0.00192117 0.10972436 +v -0.01516438 0.00369637 0.10972436 +v -0.01597054 0.00389507 0.10963210 +v -0.01659756 0.00404962 0.10935534 +v -0.01704543 0.00416001 0.10889407 +v -0.01731415 0.00422624 0.10824829 +v -0.01740372 0.00424832 0.10741801 +v -0.01740372 0.00424832 0.09966632 +v -0.01777924 0.00219917 0.09966632 +v -0.01777924 0.00219917 0.10741801 +v -0.01768766 0.00218805 0.10824829 +v -0.01741291 0.00215469 0.10889407 +v -0.01695501 0.00209909 0.10935534 +v -0.01631393 0.00202125 0.10963210 +v -0.01549936 -0.00184158 0.10972436 +v -0.01560833 -0.00004011 0.10972436 +v -0.01643862 -0.00004011 0.10963210 +v -0.01708440 -0.00004011 0.10935534 +v -0.01754567 -0.00004011 0.10889407 +v -0.01782243 -0.00004012 0.10824829 +v -0.01791469 -0.00004012 0.10741801 +v -0.01791469 -0.00004012 0.09966632 +v -0.01778890 -0.00211958 0.09966632 +v -0.01778890 -0.00211958 0.10741801 +v -0.01769732 -0.00210846 0.10824829 +v -0.01742258 -0.00207510 0.10889407 +v -0.01696467 -0.00201950 0.10935534 +v -0.01632360 -0.00194166 0.10963210 +v -0.01460826 -0.00549731 0.10972436 +v -0.01514518 -0.00377427 0.10972436 +v -0.01595134 -0.00397297 0.10963210 +v -0.01657836 -0.00412752 0.10935534 +v -0.01702623 -0.00423791 0.10889407 +v -0.01729495 -0.00430414 0.10824829 +v -0.01738452 -0.00432622 0.10741801 +v -0.01738452 -0.00432622 0.09966632 +v -0.01676474 -0.00631516 0.09966632 +v -0.01676474 -0.00631516 0.10741801 +v -0.01667848 -0.00628244 0.10824829 +v -0.01641970 -0.00618430 0.10889407 +v -0.01598841 -0.00602073 0.10935534 +v -0.01538459 -0.00579174 0.10963210 +v -0.01286818 -0.00883356 0.10972436 +v -0.01380185 -0.00728908 0.10972436 +v -0.01453703 -0.00767493 0.10963210 +v -0.01510884 -0.00797504 0.10935534 +v -0.01551728 -0.00818940 0.10889407 +v -0.01576234 -0.00831802 0.10824829 +v -0.01584402 -0.00836089 0.10741801 +v -0.01584402 -0.00836089 0.09966632 +v -0.01476627 -0.01014371 0.09966632 +v -0.01476627 -0.01014371 0.10741801 +v -0.01469035 -0.01009131 0.10824829 +v -0.01446257 -0.00993409 0.10889407 +v -0.01408296 -0.00967206 0.10935534 +v -0.01355149 -0.00930521 0.10963210 +v -0.01038024 -0.01165643 0.10972436 +v -0.01165640 -0.01038027 0.10972436 +v -0.01227788 -0.01093085 0.10963210 +v -0.01276125 -0.01135908 0.10935534 +v -0.01310652 -0.01166496 0.10889407 +v -0.01331368 -0.01184849 0.10824829 +v -0.01338273 -0.01190967 0.10741801 +v -0.01338273 -0.01190967 0.09966632 +v -0.01190964 -0.01338276 0.09966632 +v -0.01190964 -0.01338276 0.10741801 +v -0.01184846 -0.01331371 0.10824829 +v -0.01166493 -0.01310655 0.10889407 +v -0.01135905 -0.01276128 0.10935534 +v -0.01093083 -0.01227791 0.10963210 +v -0.00728905 -0.01380187 0.10972436 +v -0.00883353 -0.01286820 0.10972436 +v -0.00930518 -0.01355151 0.10963210 +v -0.00967203 -0.01408298 0.10935534 +v -0.00993406 -0.01446260 0.10889407 +v -0.01009128 -0.01469037 0.10824829 +v -0.01014368 -0.01476629 0.10741801 +v -0.01014368 -0.01476629 0.09966632 +v -0.00836086 -0.01584404 0.09966632 +v -0.00836086 -0.01584404 0.10741801 +v -0.00831799 -0.01576236 0.10824829 +v -0.00818937 -0.01551730 0.10889407 +v -0.00797501 -0.01510886 0.10935534 +v -0.00767490 -0.01453705 0.10963210 +v -0.00377423 -0.01514519 0.10972436 +v -0.00549728 -0.01460827 0.10972436 +v -0.00579170 -0.01538461 0.10963210 +v -0.00602070 -0.01598842 0.10935534 +v -0.00618426 -0.01641972 0.10889407 +v -0.00628241 -0.01667849 0.10824829 +v -0.00631512 -0.01676475 0.10741801 +v -0.00631512 -0.01676475 0.09966632 +v -0.00432618 -0.01738453 0.09966632 +v -0.00432618 -0.01738453 0.10741801 +v -0.00430410 -0.01729496 0.10824829 +v -0.00423787 -0.01702624 0.10889407 +v -0.00412748 -0.01657837 0.10935534 +v -0.00397293 -0.01595135 0.10963210 +v -0.00004008 -0.01560833 0.10972436 +v -0.00184155 -0.01549937 0.10972436 +v -0.00194163 -0.01632360 0.10963210 +v -0.00201946 -0.01696467 0.10935534 +v -0.00207506 -0.01742258 0.10889407 +v -0.00210842 -0.01769732 0.10824829 +v -0.00211954 -0.01778891 0.10741801 +v -0.00211954 -0.01778891 0.09966632 +v -0.00004008 -0.01791469 0.09966632 +v -0.00004008 -0.01791469 0.10741801 +v -0.00004008 -0.01782243 0.10824829 +v -0.00004008 -0.01754567 0.10889407 +v -0.00004008 -0.01708440 0.10935534 +v -0.00004008 -0.01643862 0.10963210 +v 0.00369641 -0.01516438 0.10972436 +v 0.00192121 -0.01548970 0.10972436 +v 0.00202129 -0.01631393 0.10963210 +v 0.00209913 -0.01695500 0.10935534 +v 0.00215473 -0.01741291 0.10889407 +v 0.00218809 -0.01768765 0.10824829 +v 0.00219921 -0.01777923 0.10741801 +v 0.00219921 -0.01777923 0.09966632 +v 0.00424836 -0.01740371 0.09966632 +v 0.00424836 -0.01740371 0.10741801 +v 0.00422628 -0.01731414 0.10824829 +v 0.00416004 -0.01704542 0.10889407 +v 0.00404965 -0.01659755 0.10935534 +v 0.00389511 -0.01597054 0.10963210 +v 0.00721807 -0.01383912 0.10972436 +v 0.00557231 -0.01457982 0.10972436 +v 0.00586674 -0.01535615 0.10963210 +v 0.00609573 -0.01595996 0.10935534 +v 0.00625930 -0.01639126 0.10889407 +v 0.00635744 -0.01665004 0.10824829 +v 0.00639016 -0.01673630 0.10741801 +v 0.00639016 -0.01673630 0.09966632 +v 0.00828989 -0.01588129 0.09966632 +v 0.00828989 -0.01588129 0.10741801 +v 0.00824702 -0.01579960 0.10824829 +v 0.00811840 -0.01555454 0.10889407 +v 0.00790403 -0.01514611 0.10935534 +v 0.00760393 -0.01457430 0.10963210 +v 0.01032025 -0.01170958 0.10972436 +v 0.00889957 -0.01282261 0.10972436 +v 0.00937123 -0.01350592 0.10963210 +v 0.00973807 -0.01403739 0.10935534 +v 0.01000010 -0.01441701 0.10889407 +v 0.01015732 -0.01464478 0.10824829 +v 0.01020973 -0.01472070 0.10741801 +v 0.01020973 -0.01472070 0.09966632 +v 0.01184964 -0.01343591 0.09966632 +v 0.01184964 -0.01343591 0.10741801 +v 0.01178847 -0.01336686 0.10824829 +v 0.01160494 -0.01315970 0.10889407 +v 0.01129906 -0.01281443 0.10935534 +v 0.01087083 -0.01233106 0.10963210 +v 0.01282264 -0.00889952 0.10972436 +v 0.01170962 -0.01032020 0.10972436 +v 0.01233110 -0.01087078 0.10963210 +v 0.01281447 -0.01129902 0.10935534 +v 0.01315974 -0.01160489 0.10889407 +v 0.01336690 -0.01178842 0.10824829 +v 0.01343595 -0.01184960 0.10741801 +v 0.01343595 -0.01184960 0.09966632 +v 0.01472074 -0.01020968 0.09966632 +v 0.01472074 -0.01020968 0.10741801 +v 0.01464482 -0.01015727 0.10824829 +v 0.01441704 -0.01000005 0.10889407 +v 0.01403742 -0.00973802 0.10935534 +v 0.01350596 -0.00937118 0.10963210 +v 0.01457984 -0.00557226 0.10972436 +v 0.01383914 -0.00721802 0.10972436 +v 0.01457433 -0.00760387 0.10963210 +v 0.01514614 -0.00790398 0.10935534 +v 0.01555457 -0.00811834 0.10889407 +v 0.01579963 -0.00824696 0.10824829 +v 0.01588132 -0.00828983 0.10741801 +v 0.01588132 -0.00828983 0.09966632 +v 0.01673632 -0.00639010 0.09966632 +v 0.01673632 -0.00639010 0.10741801 +v 0.01665006 -0.00635738 0.10824829 +v 0.01639128 -0.00625924 0.10889407 +v 0.01595999 -0.00609568 0.10935534 +v 0.01535617 -0.00586668 0.10963210 +v 0.01548970 -0.00192115 0.10972436 +v 0.01516439 -0.00369635 0.10972436 +v 0.01597055 -0.00389505 0.10963210 +v 0.01659757 -0.00404960 0.10935534 +v 0.01704543 -0.00415998 0.10889407 +v 0.01731415 -0.00422622 0.10824829 +v 0.01740373 -0.00424829 0.10741801 +v 0.01740373 -0.00424829 0.09966632 +v 0.01777924 -0.00219915 0.09966632 +v 0.01777924 -0.00219915 0.10741801 +v 0.01768766 -0.00218803 0.10824829 +v 0.01741291 -0.00215467 0.10889407 +v 0.01695501 -0.00209907 0.10935534 +v 0.01631394 -0.00202123 0.10963210 +v 0.00176872 0.00021476 0.10958172 +v 0.00172993 0.00042639 0.10958172 +v 0.00166592 0.00063180 0.10958172 +v 0.00157762 0.00082800 0.10958172 +v 0.00146632 0.00101212 0.10958172 +v 0.00133363 0.00118149 0.10958172 +v 0.00118149 0.00133362 0.10958172 +v 0.00101213 0.00146631 0.10958172 +v 0.00082800 0.00157762 0.10958172 +v 0.00063181 0.00166592 0.10958172 +v 0.00042639 0.00172993 0.10958172 +v 0.00021477 0.00176871 0.10958172 +v 0.00000001 0.00178170 0.10958172 +v -0.00021475 0.00176871 0.10958172 +v -0.00042638 0.00172993 0.10958172 +v -0.00063179 0.00166592 0.10958172 +v -0.00082799 0.00157762 0.10958172 +v -0.00101211 0.00146631 0.10958172 +v -0.00118148 0.00133362 0.10958172 +v -0.00133361 0.00118149 0.10958172 +v -0.00146630 0.00101212 0.10958172 +v -0.00157761 0.00082800 0.10958172 +v -0.00166591 0.00063180 0.10958172 +v -0.00172992 0.00042639 0.10958172 +v -0.00176870 0.00021476 0.10958172 +v -0.00178169 -0.00000000 0.10958172 +v -0.00176870 -0.00021476 0.10958172 +v -0.00172992 -0.00042639 0.10958172 +v -0.00166591 -0.00063180 0.10958172 +v -0.00157761 -0.00082800 0.10958172 +v -0.00146630 -0.00101212 0.10958172 +v -0.00133361 -0.00118149 0.10958172 +v -0.00118148 -0.00133362 0.10958172 +v -0.00101211 -0.00146631 0.10958172 +v -0.00082799 -0.00157762 0.10958172 +v -0.00063179 -0.00166592 0.10958172 +v -0.00042638 -0.00172993 0.10958172 +v -0.00021475 -0.00176871 0.10958172 +v 0.00000001 -0.00178170 0.10958172 +v 0.00021477 -0.00176871 0.10958172 +v 0.00042640 -0.00172993 0.10958172 +v 0.00063181 -0.00166592 0.10958172 +v 0.00082801 -0.00157761 0.10958172 +v 0.00101213 -0.00146631 0.10958172 +v 0.00118149 -0.00133362 0.10958172 +v 0.00133363 -0.00118148 0.10958172 +v 0.00146632 -0.00101212 0.10958172 +v 0.00157762 -0.00082799 0.10958172 +v 0.00166593 -0.00063180 0.10958172 +v 0.00172993 -0.00042638 0.10958172 +v 0.00176872 -0.00021476 0.10958172 +v 0.00059176 0.00000000 0.10933756 +v 0.00058745 0.00007133 0.10933756 +v 0.00057457 0.00014162 0.10933756 +v 0.00055331 0.00020984 0.10933756 +v 0.00052398 0.00027500 0.10933756 +v 0.00048701 0.00033616 0.10933756 +v 0.00044294 0.00039241 0.10933756 +v 0.00039241 0.00044294 0.10933756 +v 0.00033616 0.00048701 0.10933756 +v 0.00027501 0.00052397 0.10933756 +v 0.00020985 0.00055330 0.10933756 +v 0.00014162 0.00057456 0.10933756 +v 0.00007134 0.00058744 0.10933756 +v 0.00000001 0.00059176 0.10933756 +v -0.00007132 0.00058744 0.10933756 +v -0.00014161 0.00057456 0.10933756 +v -0.00020983 0.00055330 0.10933756 +v -0.00027500 0.00052397 0.10933756 +v -0.00033615 0.00048701 0.10933756 +v -0.00039240 0.00044294 0.10933756 +v -0.00044293 0.00039241 0.10933756 +v -0.00048700 0.00033616 0.10933756 +v -0.00052397 0.00027500 0.10933756 +v -0.00055329 0.00020984 0.10933756 +v -0.00057455 0.00014162 0.10933756 +v -0.00058743 0.00007133 0.10933756 +v -0.00059175 0.00000000 0.10933756 +v -0.00058743 -0.00007133 0.10933756 +v -0.00057455 -0.00014162 0.10933756 +v -0.00055329 -0.00020984 0.10933756 +v -0.00052397 -0.00027500 0.10933756 +v -0.00048700 -0.00033616 0.10933756 +v -0.00044293 -0.00039241 0.10933756 +v -0.00039240 -0.00044294 0.10933756 +v -0.00033615 -0.00048701 0.10933756 +v -0.00027499 -0.00052397 0.10933756 +v -0.00020983 -0.00055330 0.10933756 +v -0.00014161 -0.00057456 0.10933756 +v -0.00007132 -0.00058744 0.10933756 +v 0.00000001 -0.00059176 0.10933756 +v 0.00007134 -0.00058744 0.10933756 +v 0.00014162 -0.00057456 0.10933756 +v 0.00020985 -0.00055330 0.10933756 +v 0.00027501 -0.00052397 0.10933756 +v 0.00033616 -0.00048700 0.10933756 +v 0.00039241 -0.00044293 0.10933756 +v 0.00044294 -0.00039241 0.10933756 +v 0.00048701 -0.00033615 0.10933756 +v 0.00052398 -0.00027500 0.10933756 +v 0.00055331 -0.00020984 0.10933756 +v 0.00057457 -0.00014161 0.10933756 +v 0.00058745 -0.00007133 0.10933756 +v 0.00036115 0.00000000 0.10958171 +v 0.00035852 0.00004353 0.10958171 +v 0.00035066 0.00008643 0.10958171 +v 0.00033768 0.00012807 0.10958171 +v 0.00031979 0.00016783 0.10958171 +v 0.00029722 0.00020516 0.10958171 +v 0.00027033 0.00023949 0.10958171 +v 0.00023949 0.00027032 0.10958171 +v 0.00020516 0.00029722 0.10958171 +v 0.00016784 0.00031978 0.10958171 +v 0.00012807 0.00033768 0.10958171 +v 0.00008644 0.00035065 0.10958171 +v 0.00004354 0.00035851 0.10958171 +v 0.00000001 0.00036115 0.10958171 +v -0.00004352 0.00035851 0.10958171 +v -0.00008642 0.00035065 0.10958171 +v -0.00012806 0.00033768 0.10958171 +v -0.00016783 0.00031978 0.10958171 +v -0.00020515 0.00029722 0.10958171 +v -0.00023948 0.00027032 0.10958171 +v -0.00027031 0.00023948 0.10958171 +v -0.00029721 0.00020515 0.10958171 +v -0.00031977 0.00016783 0.10958171 +v -0.00033767 0.00012806 0.10958171 +v -0.00035064 0.00008643 0.10958171 +v -0.00035851 0.00004353 0.10958171 +v -0.00036114 0.00000000 0.10958171 +v -0.00035851 -0.00004353 0.10958171 +v -0.00035064 -0.00008643 0.10958171 +v -0.00033767 -0.00012806 0.10958171 +v -0.00031977 -0.00016783 0.10958171 +v -0.00029721 -0.00020515 0.10958171 +v -0.00027031 -0.00023948 0.10958171 +v -0.00023948 -0.00027032 0.10958171 +v -0.00020515 -0.00029722 0.10958171 +v -0.00016783 -0.00031978 0.10958171 +v -0.00012806 -0.00033768 0.10958171 +v -0.00008642 -0.00035065 0.10958171 +v -0.00004352 -0.00035851 0.10958171 +v 0.00000001 -0.00036115 0.10958171 +v 0.00004354 -0.00035851 0.10958171 +v 0.00008644 -0.00035065 0.10958171 +v 0.00012807 -0.00033768 0.10958171 +v 0.00016784 -0.00031978 0.10958171 +v 0.00020516 -0.00029722 0.10958171 +v 0.00023949 -0.00027032 0.10958171 +v 0.00027033 -0.00023948 0.10958171 +v 0.00029723 -0.00020515 0.10958171 +v 0.00031979 -0.00016783 0.10958171 +v 0.00033769 -0.00012806 0.10958171 +v 0.00035066 -0.00008643 0.10958171 +v 0.00035852 -0.00004353 0.10958171 +v 0.00000001 0.00000000 0.10958172 +v 0.00162893 0.00000000 0.10956516 +v 0.00161705 0.00019635 0.10956516 +v 0.00158160 0.00038983 0.10956516 +v 0.00152308 0.00057763 0.10956516 +v 0.00144235 0.00075700 0.10956516 +v 0.00134058 0.00092533 0.10956516 +v 0.00121927 0.00108018 0.10956516 +v 0.00108018 0.00121927 0.10956516 +v 0.00092534 0.00134058 0.10956516 +v 0.00075701 0.00144234 0.10956516 +v 0.00057763 0.00152307 0.10956516 +v 0.00038983 0.00158159 0.10956516 +v 0.00019635 0.00161705 0.10956516 +v 0.00000001 0.00162892 0.10956516 +v -0.00019634 0.00161705 0.10956516 +v -0.00038982 0.00158159 0.10956516 +v -0.00057762 0.00152307 0.10956516 +v -0.00075699 0.00144234 0.10956516 +v -0.00092533 0.00134058 0.10956516 +v -0.00108017 0.00121927 0.10956516 +v -0.00121926 0.00108018 0.10956516 +v -0.00134057 0.00092533 0.10956516 +v -0.00144233 0.00075700 0.10956516 +v -0.00152306 0.00057762 0.10956516 +v -0.00158158 0.00038983 0.10956516 +v -0.00161704 0.00019634 0.10956516 +v -0.00162892 -0.00000000 0.10956516 +v -0.00161704 -0.00019635 0.10956516 +v -0.00158158 -0.00038983 0.10956516 +v -0.00152306 -0.00057762 0.10956516 +v -0.00144233 -0.00075700 0.10956516 +v -0.00134057 -0.00092533 0.10956516 +v -0.00121926 -0.00108018 0.10956516 +v -0.00108017 -0.00121927 0.10956516 +v -0.00092533 -0.00134058 0.10956516 +v -0.00075699 -0.00144234 0.10956516 +v -0.00057762 -0.00152307 0.10956516 +v -0.00038982 -0.00158159 0.10956516 +v -0.00019634 -0.00161705 0.10956516 +v 0.00000001 -0.00162892 0.10956516 +v 0.00019635 -0.00161705 0.10956516 +v 0.00038984 -0.00158159 0.10956516 +v 0.00057763 -0.00152307 0.10956516 +v 0.00075701 -0.00144234 0.10956516 +v 0.00092534 -0.00134058 0.10956516 +v 0.00108018 -0.00121926 0.10956516 +v 0.00121928 -0.00108017 0.10956516 +v 0.00134059 -0.00092533 0.10956516 +v 0.00144235 -0.00075700 0.10956516 +v 0.00152308 -0.00057762 0.10956516 +v 0.00158160 -0.00038982 0.10956516 +v 0.00161705 -0.00019634 0.10956516 +v 0.00173038 0.00000000 0.10957988 +v 0.00167946 0.00000000 0.10957436 +v 0.00171777 0.00020857 0.10957988 +v 0.00166721 0.00020244 0.10957436 +v 0.00168010 0.00041411 0.10957988 +v 0.00163065 0.00040192 0.10957436 +v 0.00161794 0.00061360 0.10957988 +v 0.00157032 0.00059554 0.10957436 +v 0.00153218 0.00080415 0.10957988 +v 0.00148709 0.00078048 0.10957436 +v 0.00142408 0.00098297 0.10957988 +v 0.00138217 0.00095404 0.10957436 +v 0.00129521 0.00114745 0.10957988 +v 0.00125709 0.00111368 0.10957436 +v 0.00114746 0.00129520 0.10957988 +v 0.00111369 0.00125709 0.10957436 +v 0.00098297 0.00142407 0.10957988 +v 0.00095404 0.00138216 0.10957436 +v 0.00080415 0.00153217 0.10957988 +v 0.00078049 0.00148708 0.10957436 +v 0.00061361 0.00161793 0.10957988 +v 0.00059555 0.00157031 0.10957436 +v 0.00041411 0.00168009 0.10957988 +v 0.00040193 0.00163065 0.10957436 +v 0.00020858 0.00171776 0.10957988 +v 0.00020244 0.00166721 0.10957436 +v 0.00000001 0.00173038 0.10957988 +v 0.00000001 0.00167945 0.10957436 +v -0.00020857 0.00171776 0.10957988 +v -0.00020243 0.00166721 0.10957436 +v -0.00041410 0.00168009 0.10957988 +v -0.00040191 0.00163065 0.10957436 +v -0.00061359 0.00161793 0.10957988 +v -0.00059553 0.00157031 0.10957436 +v -0.00080414 0.00153217 0.10957988 +v -0.00078047 0.00148708 0.10957436 +v -0.00098296 0.00142407 0.10957988 +v -0.00095403 0.00138216 0.10957436 +v -0.00114744 0.00129520 0.10957988 +v -0.00111367 0.00125709 0.10957436 +v -0.00129520 0.00114745 0.10957988 +v -0.00125708 0.00111368 0.10957436 +v -0.00142406 0.00098296 0.10957988 +v -0.00138215 0.00095404 0.10957436 +v -0.00153216 0.00080414 0.10957988 +v -0.00148707 0.00078048 0.10957436 +v -0.00161792 0.00061360 0.10957988 +v -0.00157031 0.00059554 0.10957436 +v -0.00168009 0.00041411 0.10957988 +v -0.00163064 0.00040192 0.10957436 +v -0.00171775 0.00020857 0.10957988 +v -0.00166720 0.00020243 0.10957436 +v -0.00173037 -0.00000000 0.10957988 +v -0.00167944 -0.00000000 0.10957436 +v -0.00171775 -0.00020857 0.10957988 +v -0.00166720 -0.00020244 0.10957436 +v -0.00168009 -0.00041411 0.10957988 +v -0.00163064 -0.00040192 0.10957436 +v -0.00161792 -0.00061360 0.10957988 +v -0.00157030 -0.00059554 0.10957436 +v -0.00153216 -0.00080415 0.10957988 +v -0.00148707 -0.00078048 0.10957436 +v -0.00142406 -0.00098297 0.10957988 +v -0.00138215 -0.00095404 0.10957436 +v -0.00129520 -0.00114745 0.10957988 +v -0.00125708 -0.00111368 0.10957436 +v -0.00114744 -0.00129520 0.10957988 +v -0.00111367 -0.00125709 0.10957436 +v -0.00098296 -0.00142407 0.10957988 +v -0.00095403 -0.00138216 0.10957436 +v -0.00080414 -0.00153217 0.10957988 +v -0.00078047 -0.00148708 0.10957436 +v -0.00061359 -0.00161793 0.10957988 +v -0.00059553 -0.00157031 0.10957436 +v -0.00041410 -0.00168009 0.10957988 +v -0.00040191 -0.00163065 0.10957436 +v -0.00020856 -0.00171776 0.10957988 +v -0.00020243 -0.00166720 0.10957436 +v 0.00000001 -0.00173037 0.10957988 +v 0.00000001 -0.00167945 0.10957436 +v 0.00020858 -0.00171776 0.10957988 +v 0.00020244 -0.00166720 0.10957436 +v 0.00041411 -0.00168009 0.10957988 +v 0.00040193 -0.00163065 0.10957436 +v 0.00061361 -0.00161793 0.10957988 +v 0.00059555 -0.00157031 0.10957436 +v 0.00080415 -0.00153217 0.10957988 +v 0.00078049 -0.00148708 0.10957436 +v 0.00098297 -0.00142407 0.10957988 +v 0.00095404 -0.00138216 0.10957436 +v 0.00114746 -0.00129520 0.10957988 +v 0.00111369 -0.00125708 0.10957436 +v 0.00129521 -0.00114745 0.10957988 +v 0.00125709 -0.00111368 0.10957436 +v 0.00142408 -0.00098296 0.10957988 +v 0.00138217 -0.00095403 0.10957436 +v 0.00153218 -0.00080414 0.10957988 +v 0.00148709 -0.00078048 0.10957436 +v 0.00161794 -0.00061360 0.10957988 +v 0.00157032 -0.00059554 0.10957436 +v 0.00168010 -0.00041410 0.10957988 +v 0.00163066 -0.00040191 0.10957436 +v 0.00171777 -0.00020857 0.10957988 +v 0.00166721 -0.00020243 0.10957436 +v 0.01572988 -0.00000000 0.09952368 +v 0.01561519 0.00189603 0.09952368 +v 0.01527280 0.00376441 0.09952368 +v 0.01470769 0.00557789 0.09952368 +v 0.01392811 0.00731004 0.09952368 +v 0.01294544 0.00893559 0.09952368 +v 0.01177398 0.01043084 0.09952368 +v 0.01043084 0.01177398 0.09952368 +v 0.00893559 0.01294543 0.09952368 +v 0.00731004 0.01392811 0.09952368 +v 0.00557789 0.01470769 0.09952368 +v 0.00376441 0.01527280 0.09952368 +v 0.00189603 0.01561519 0.09952368 +v -0.00000000 0.01572988 0.09952368 +v -0.00189603 0.01561519 0.09952368 +v -0.00376441 0.01527279 0.09952368 +v -0.00557789 0.01470769 0.09952368 +v -0.00731004 0.01392811 0.09952368 +v -0.00893559 0.01294543 0.09952368 +v -0.01043084 0.01177398 0.09952368 +v -0.01177398 0.01043083 0.09952368 +v -0.01294544 0.00893558 0.09952368 +v -0.01392812 0.00731003 0.09952368 +v -0.01470769 0.00557788 0.09952368 +v -0.01527280 0.00376439 0.09952368 +v -0.01561519 0.00189602 0.09952368 +v -0.01572988 -0.00000001 0.09952368 +v -0.01561518 -0.00189604 0.09952368 +v -0.01527279 -0.00376442 0.09952368 +v -0.01470768 -0.00557791 0.09952368 +v -0.01392811 -0.00731005 0.09952368 +v -0.01294542 -0.00893560 0.09952368 +v -0.01177397 -0.01043085 0.09952368 +v -0.01043082 -0.01177399 0.09952368 +v -0.00893557 -0.01294544 0.09952368 +v -0.00731002 -0.01392812 0.09952368 +v -0.00557787 -0.01470770 0.09952368 +v -0.00376438 -0.01527280 0.09952368 +v -0.00189600 -0.01561519 0.09952368 +v 0.00000002 -0.01572988 0.09952368 +v 0.00189605 -0.01561518 0.09952368 +v 0.00376443 -0.01527279 0.09952368 +v 0.00557791 -0.01470768 0.09952368 +v 0.00731006 -0.01392810 0.09952368 +v 0.00893561 -0.01294542 0.09952368 +v 0.01043086 -0.01177396 0.09952368 +v 0.01177400 -0.01043082 0.09952368 +v 0.01294545 -0.00893556 0.09952368 +v 0.01392813 -0.00731001 0.09952368 +v 0.01470770 -0.00557786 0.09952368 +v 0.01527280 -0.00376438 0.09952368 +v 0.01561519 -0.00189600 0.09952368 +v 0.01572988 -0.00000000 0.10565951 +v 0.01561519 0.00189603 0.10565951 +v 0.01527280 0.00376441 0.10565951 +v 0.01470769 0.00557789 0.10565951 +v 0.01392811 0.00731004 0.10565951 +v 0.01294544 0.00893559 0.10565951 +v 0.01177398 0.01043084 0.10565951 +v 0.01043084 0.01177398 0.10565951 +v 0.00893559 0.01294543 0.10565951 +v 0.00731004 0.01392811 0.10565951 +v 0.00557789 0.01470769 0.10565951 +v 0.00376441 0.01527280 0.10565951 +v 0.00189603 0.01561519 0.10565951 +v -0.00000000 0.01572988 0.10565951 +v -0.00189603 0.01561519 0.10565951 +v -0.00376441 0.01527279 0.10565951 +v -0.00557789 0.01470769 0.10565951 +v -0.00731004 0.01392811 0.10565951 +v -0.00893559 0.01294543 0.10565951 +v -0.01043084 0.01177398 0.10565951 +v -0.01177398 0.01043083 0.10565951 +v -0.01294544 0.00893558 0.10565951 +v -0.01392812 0.00731003 0.10565951 +v -0.01470769 0.00557788 0.10565951 +v -0.01527280 0.00376439 0.10565951 +v -0.01561519 0.00189602 0.10565951 +v -0.01572988 -0.00000001 0.10565951 +v -0.01561518 -0.00189604 0.10565951 +v -0.01527279 -0.00376442 0.10565951 +v -0.01470768 -0.00557791 0.10565951 +v -0.01392811 -0.00731005 0.10565951 +v -0.01294542 -0.00893560 0.10565951 +v -0.01177397 -0.01043085 0.10565951 +v -0.01043082 -0.01177399 0.10565951 +v -0.00893557 -0.01294544 0.10565951 +v -0.00731002 -0.01392812 0.10565951 +v -0.00557787 -0.01470770 0.10565951 +v -0.00376438 -0.01527280 0.10565951 +v -0.00189600 -0.01561519 0.10565951 +v 0.00000002 -0.01572988 0.10565951 +v 0.00189605 -0.01561518 0.10565951 +v 0.00376443 -0.01527279 0.10565951 +v 0.00557791 -0.01470768 0.10565951 +v 0.00731006 -0.01392810 0.10565951 +v 0.00893561 -0.01294542 0.10565951 +v 0.01043086 -0.01177396 0.10565951 +v 0.01177400 -0.01043082 0.10565951 +v 0.01294545 -0.00893556 0.10565951 +v 0.01392813 -0.00731001 0.10565951 +v 0.01470770 -0.00557786 0.10565951 +v 0.01527280 -0.00376438 0.10565951 +v 0.01561519 -0.00189600 0.10565951 +v 0.00000001 0.00000000 0.10709216 +v 0.01429727 -0.00000000 0.10709212 +v 0.01419302 0.00172334 0.10709212 +v 0.01388181 0.00342156 0.10709212 +v 0.01336818 0.00506988 0.10709212 +v 0.01265960 0.00664427 0.10709212 +v 0.01176642 0.00812177 0.10709212 +v 0.01070166 0.00948084 0.10709212 +v 0.00948084 0.01070166 0.10709212 +v 0.00812177 0.01176642 0.10709212 +v 0.00664427 0.01265960 0.10709212 +v 0.00506988 0.01336817 0.10709212 +v 0.00342156 0.01388181 0.10709212 +v 0.00172334 0.01419302 0.10709212 +v -0.00000000 0.01429727 0.10709212 +v -0.00172335 0.01419302 0.10709212 +v -0.00342156 0.01388181 0.10709212 +v -0.00506988 0.01336817 0.10709212 +v -0.00664427 0.01265960 0.10709212 +v -0.00812178 0.01176642 0.10709212 +v -0.00948084 0.01070165 0.10709212 +v -0.01070166 0.00948084 0.10709212 +v -0.01176642 0.00812177 0.10709212 +v -0.01265960 0.00664426 0.10709212 +v -0.01336818 0.00506987 0.10709212 +v -0.01388181 0.00342155 0.10709212 +v -0.01419302 0.00172333 0.10709212 +v -0.01429727 -0.00000001 0.10709212 +v -0.01419302 -0.00172336 0.10709212 +v -0.01388181 -0.00342157 0.10709212 +v -0.01336817 -0.00506989 0.10709212 +v -0.01265959 -0.00664428 0.10709212 +v -0.01176641 -0.00812178 0.10709212 +v -0.01070164 -0.00948085 0.10709212 +v -0.00948083 -0.01070167 0.10709212 +v -0.00812176 -0.01176643 0.10709212 +v -0.00664425 -0.01265961 0.10709212 +v -0.00506986 -0.01336818 0.10709212 +v -0.00342154 -0.01388182 0.10709212 +v -0.00172332 -0.01419302 0.10709212 +v 0.00000002 -0.01429727 0.10709212 +v 0.00172337 -0.01419302 0.10709212 +v 0.00342158 -0.01388181 0.10709212 +v 0.00506990 -0.01336817 0.10709212 +v 0.00664429 -0.01265959 0.10709212 +v 0.00812179 -0.01176640 0.10709212 +v 0.00948086 -0.01070164 0.10709212 +v 0.01070167 -0.00948082 0.10709212 +v 0.01176643 -0.00812175 0.10709212 +v 0.01265961 -0.00664425 0.10709212 +v 0.01336819 -0.00506985 0.10709212 +v 0.01388182 -0.00342153 0.10709212 +v 0.01419303 -0.00172332 0.10709212 +v 0.01564034 -0.00000000 0.10628628 +v 0.01537172 -0.00000000 0.10673397 +v 0.01492403 -0.00000000 0.10700259 +v 0.01552630 0.00188523 0.10628628 +v 0.01525965 0.00185286 0.10673397 +v 0.01481522 0.00179889 0.10700259 +v 0.01518586 0.00374298 0.10628628 +v 0.01492505 0.00367869 0.10673397 +v 0.01449037 0.00357155 0.10700259 +v 0.01462397 0.00554614 0.10628628 +v 0.01437281 0.00545089 0.10673397 +v 0.01395421 0.00529213 0.10700259 +v 0.01384883 0.00726843 0.10628628 +v 0.01361099 0.00714360 0.10673397 +v 0.01321458 0.00693554 0.10700259 +v 0.01287175 0.00888473 0.10628628 +v 0.01265068 0.00873213 0.10673397 +v 0.01228224 0.00847782 0.10700259 +v 0.01170696 0.01037146 0.10628628 +v 0.01150590 0.01019334 0.10673397 +v 0.01117080 0.00989646 0.10700259 +v 0.01037146 0.01170696 0.10628628 +v 0.01019334 0.01150590 0.10673397 +v 0.00989646 0.01117080 0.10700259 +v 0.00888473 0.01287175 0.10628628 +v 0.00873214 0.01265068 0.10673397 +v 0.00847782 0.01228224 0.10700259 +v 0.00726843 0.01384883 0.10628628 +v 0.00714360 0.01361099 0.10673397 +v 0.00693554 0.01321457 0.10700259 +v 0.00554614 0.01462397 0.10628628 +v 0.00545089 0.01437281 0.10673397 +v 0.00529214 0.01395421 0.10700259 +v 0.00374298 0.01518586 0.10628628 +v 0.00367869 0.01492505 0.10673397 +v 0.00357155 0.01449037 0.10700259 +v 0.00188523 0.01552630 0.10628628 +v 0.00185286 0.01525965 0.10673397 +v 0.00179889 0.01481522 0.10700259 +v -0.00000000 0.01564034 0.10628628 +v -0.00000000 0.01537172 0.10673397 +v -0.00000000 0.01492403 0.10700259 +v -0.00188524 0.01552630 0.10628628 +v -0.00185286 0.01525965 0.10673397 +v -0.00179890 0.01481522 0.10700259 +v -0.00374298 0.01518586 0.10628628 +v -0.00367870 0.01492505 0.10673397 +v -0.00357156 0.01449037 0.10700259 +v -0.00554614 0.01462397 0.10628628 +v -0.00545089 0.01437281 0.10673397 +v -0.00529214 0.01395421 0.10700259 +v -0.00726843 0.01384883 0.10628628 +v -0.00714360 0.01361098 0.10673397 +v -0.00693555 0.01321457 0.10700259 +v -0.00888473 0.01287174 0.10628628 +v -0.00873214 0.01265068 0.10673397 +v -0.00847782 0.01228223 0.10700259 +v -0.01037147 0.01170696 0.10628628 +v -0.01019334 0.01150589 0.10673397 +v -0.00989647 0.01117079 0.10700259 +v -0.01170696 0.01037146 0.10628628 +v -0.01150590 0.01019333 0.10673397 +v -0.01117080 0.00989646 0.10700259 +v -0.01287175 0.00888472 0.10628628 +v -0.01265068 0.00873213 0.10673397 +v -0.01228224 0.00847781 0.10700259 +v -0.01384883 0.00726842 0.10628628 +v -0.01361099 0.00714359 0.10673397 +v -0.01321458 0.00693554 0.10700259 +v -0.01462397 0.00554613 0.10628628 +v -0.01437281 0.00545088 0.10673397 +v -0.01395421 0.00529213 0.10700259 +v -0.01518586 0.00374297 0.10628628 +v -0.01492505 0.00367868 0.10673397 +v -0.01449037 0.00357154 0.10700259 +v -0.01552630 0.00188522 0.10628628 +v -0.01525965 0.00185284 0.10673397 +v -0.01481522 0.00179888 0.10700259 +v -0.01564034 -0.00000001 0.10628628 +v -0.01537172 -0.00000001 0.10673397 +v -0.01492403 -0.00000001 0.10700259 +v -0.01552630 -0.00188525 0.10628628 +v -0.01525964 -0.00185287 0.10673397 +v -0.01481522 -0.00179891 0.10700259 +v -0.01518585 -0.00374299 0.10628628 +v -0.01492504 -0.00367871 0.10673397 +v -0.01449036 -0.00357157 0.10700259 +v -0.01462396 -0.00554615 0.10628628 +v -0.01437280 -0.00545090 0.10673397 +v -0.01395421 -0.00529215 0.10700259 +v -0.01384882 -0.00726844 0.10628628 +v -0.01361098 -0.00714361 0.10673397 +v -0.01321457 -0.00693556 0.10700259 +v -0.01287174 -0.00888474 0.10628628 +v -0.01265067 -0.00873215 0.10673397 +v -0.01228223 -0.00847783 0.10700259 +v -0.01170695 -0.01037148 0.10628628 +v -0.01150589 -0.01019335 0.10673397 +v -0.01117079 -0.00989648 0.10700259 +v -0.01037145 -0.01170697 0.10628628 +v -0.01019332 -0.01150591 0.10673397 +v -0.00989645 -0.01117081 0.10700259 +v -0.00888471 -0.01287176 0.10628628 +v -0.00873212 -0.01265069 0.10673397 +v -0.00847780 -0.01228225 0.10700259 +v -0.00726841 -0.01384884 0.10628628 +v -0.00714358 -0.01361099 0.10673397 +v -0.00693553 -0.01321458 0.10700259 +v -0.00554612 -0.01462398 0.10628628 +v -0.00545087 -0.01437282 0.10673397 +v -0.00529212 -0.01395422 0.10700259 +v -0.00374296 -0.01518586 0.10628628 +v -0.00367867 -0.01492505 0.10673397 +v -0.00357153 -0.01449037 0.10700259 +v -0.00188521 -0.01552630 0.10628628 +v -0.00185283 -0.01525965 0.10673397 +v -0.00179887 -0.01481522 0.10700259 +v 0.00000002 -0.01564034 0.10628628 +v 0.00000002 -0.01537172 0.10673397 +v 0.00000002 -0.01492403 0.10700259 +v 0.00188526 -0.01552630 0.10628628 +v 0.00185288 -0.01525964 0.10673397 +v 0.00179892 -0.01481522 0.10700259 +v 0.00374300 -0.01518585 0.10628628 +v 0.00367872 -0.01492504 0.10673397 +v 0.00357158 -0.01449036 0.10700259 +v 0.00554616 -0.01462396 0.10628628 +v 0.00545091 -0.01437280 0.10673397 +v 0.00529216 -0.01395420 0.10700259 +v 0.00726845 -0.01384882 0.10628628 +v 0.00714362 -0.01361097 0.10673397 +v 0.00693557 -0.01321456 0.10700259 +v 0.00888475 -0.01287173 0.10628628 +v 0.00873216 -0.01265067 0.10673397 +v 0.00847784 -0.01228222 0.10700259 +v 0.01037148 -0.01170694 0.10628628 +v 0.01019336 -0.01150588 0.10673397 +v 0.00989648 -0.01117078 0.10700259 +v 0.01170698 -0.01037144 0.10628628 +v 0.01150592 -0.01019332 0.10673397 +v 0.01117082 -0.00989644 0.10700259 +v 0.01287176 -0.00888470 0.10628628 +v 0.01265070 -0.00873211 0.10673397 +v 0.01228226 -0.00847779 0.10700259 +v 0.01384885 -0.00726840 0.10628628 +v 0.01361100 -0.00714357 0.10673397 +v 0.01321459 -0.00693552 0.10700259 +v 0.01462398 -0.00554611 0.10628628 +v 0.01437282 -0.00545086 0.10673397 +v 0.01395422 -0.00529211 0.10700259 +v 0.01518587 -0.00374295 0.10628628 +v 0.01492506 -0.00367866 0.10673397 +v 0.01449038 -0.00357153 0.10700259 +v 0.01552631 -0.00188520 0.10628628 +v 0.01525965 -0.00185283 0.10673397 +v 0.01481522 -0.00179886 0.10700259 +f 1317 1314 1315 +f 1317 1315 1316 +f 945 944 996 +f 945 996 997 +f 1331 1328 1329 +f 1331 1329 1330 +f 947 946 998 +f 947 998 999 +f 1345 1342 1343 +f 1345 1343 1344 +f 949 948 1000 +f 949 1000 1001 +f 1359 1356 1357 +f 1359 1357 1358 +f 951 950 1002 +f 951 1002 1003 +f 1373 1370 1371 +f 1373 1371 1372 +f 953 952 1004 +f 953 1004 1005 +f 1387 1384 1385 +f 1387 1385 1386 +f 955 954 1006 +f 955 1006 1007 +f 1401 1398 1399 +f 1401 1399 1400 +f 957 956 1008 +f 957 1008 1009 +f 1415 1412 1413 +f 1415 1413 1414 +f 959 958 1010 +f 959 1010 1011 +f 1429 1426 1427 +f 1429 1427 1428 +f 961 960 1012 +f 961 1012 1013 +f 1443 1440 1441 +f 1443 1441 1442 +f 963 962 1014 +f 963 1014 1015 +f 1457 1454 1455 +f 1457 1455 1456 +f 965 964 1016 +f 965 1016 1017 +f 1471 1468 1469 +f 1471 1469 1470 +f 967 966 1018 +f 967 1018 1019 +f 1485 1482 1483 +f 1485 1483 1484 +f 969 968 1020 +f 969 1020 1021 +f 1499 1496 1497 +f 1499 1497 1498 +f 971 970 1022 +f 971 1022 1023 +f 1513 1510 1511 +f 1513 1511 1512 +f 973 972 1024 +f 973 1024 1025 +f 1527 1524 1525 +f 1527 1525 1526 +f 975 974 1026 +f 975 1026 1027 +f 1541 1538 1539 +f 1541 1539 1540 +f 977 976 1028 +f 977 1028 1029 +f 1555 1552 1553 +f 1555 1553 1554 +f 979 978 1030 +f 979 1030 1031 +f 1569 1566 1567 +f 1569 1567 1568 +f 981 980 1032 +f 981 1032 1033 +f 1583 1580 1581 +f 1583 1581 1582 +f 983 982 1034 +f 983 1034 1035 +f 1597 1594 1595 +f 1597 1595 1596 +f 985 984 1036 +f 985 1036 1037 +f 1611 1608 1609 +f 1611 1609 1610 +f 987 986 1038 +f 987 1038 1039 +f 1625 1622 1623 +f 1625 1623 1624 +f 989 988 1040 +f 989 1040 1041 +f 1639 1636 1637 +f 1639 1637 1638 +f 991 990 1042 +f 991 1042 1043 +f 1653 1650 1651 +f 1653 1651 1652 +f 993 992 1044 +f 993 1044 1045 +f 1667 1664 1665 +f 1667 1665 1666 +f 1048 1099 1046 +f 1048 1046 995 +f 1049 1672 1047 +f 1049 1047 943 +f 1050 1673 1672 +f 1050 1672 1049 +f 1051 1674 1673 +f 1051 1673 1050 +f 1052 1675 1674 +f 1052 1674 1051 +f 1053 1676 1675 +f 1053 1675 1052 +f 1054 1677 1676 +f 1054 1676 1053 +f 1055 1678 1677 +f 1055 1677 1054 +f 1056 1679 1678 +f 1056 1678 1055 +f 1057 1680 1679 +f 1057 1679 1056 +f 1058 1681 1680 +f 1058 1680 1057 +f 1059 1682 1681 +f 1059 1681 1058 +f 1060 1683 1682 +f 1060 1682 1059 +f 1061 1684 1683 +f 1061 1683 1060 +f 1062 1685 1684 +f 1062 1684 1061 +f 1063 1686 1685 +f 1063 1685 1062 +f 1064 1687 1686 +f 1064 1686 1063 +f 1065 1688 1687 +f 1065 1687 1064 +f 1066 1689 1688 +f 1066 1688 1065 +f 1067 1690 1689 +f 1067 1689 1066 +f 1068 1691 1690 +f 1068 1690 1067 +f 1069 1692 1691 +f 1069 1691 1068 +f 1070 1693 1692 +f 1070 1692 1069 +f 1071 1694 1693 +f 1071 1693 1070 +f 1072 1695 1694 +f 1072 1694 1071 +f 1073 1696 1695 +f 1073 1695 1072 +f 1074 1697 1696 +f 1074 1696 1073 +f 1075 1698 1697 +f 1075 1697 1074 +f 1076 1699 1698 +f 1076 1698 1075 +f 1077 1700 1699 +f 1077 1699 1076 +f 1078 1701 1700 +f 1078 1700 1077 +f 1079 1702 1701 +f 1079 1701 1078 +f 1080 1703 1702 +f 1080 1702 1079 +f 1081 1704 1703 +f 1081 1703 1080 +f 1082 1705 1704 +f 1082 1704 1081 +f 1083 1706 1705 +f 1083 1705 1082 +f 1084 1707 1706 +f 1084 1706 1083 +f 1085 1708 1707 +f 1085 1707 1084 +f 1086 1709 1708 +f 1086 1708 1085 +f 1087 1710 1709 +f 1087 1709 1086 +f 1088 1711 1710 +f 1088 1710 1087 +f 1089 1712 1711 +f 1089 1711 1088 +f 1090 1713 1712 +f 1090 1712 1089 +f 1091 1714 1713 +f 1091 1713 1090 +f 1092 1715 1714 +f 1092 1714 1091 +f 1093 1716 1715 +f 1093 1715 1092 +f 1094 1717 1716 +f 1094 1716 1093 +f 1095 1718 1717 +f 1095 1717 1094 +f 1096 1719 1718 +f 1096 1718 1095 +f 1097 1720 1719 +f 1097 1719 1096 +f 1098 1721 1720 +f 1098 1720 1097 +f 994 1722 1721 +f 994 1721 1098 +f 943 1047 1722 +f 943 1722 994 +f 1309 1310 1321 +f 1309 1321 1308 +f 1049 1107 1111 +f 1049 1111 1050 +f 1323 1324 1335 +f 1323 1335 1322 +f 1051 1115 1119 +f 1051 1119 1052 +f 1337 1338 1349 +f 1337 1349 1336 +f 1053 1123 1127 +f 1053 1127 1054 +f 1351 1352 1363 +f 1351 1363 1350 +f 1055 1131 1135 +f 1055 1135 1056 +f 1365 1366 1377 +f 1365 1377 1364 +f 1057 1139 1143 +f 1057 1143 1058 +f 1379 1380 1391 +f 1379 1391 1378 +f 1059 1147 1151 +f 1059 1151 1060 +f 1393 1394 1405 +f 1393 1405 1392 +f 1061 1155 1159 +f 1061 1159 1062 +f 1407 1408 1419 +f 1407 1419 1406 +f 1063 1163 1167 +f 1063 1167 1064 +f 1421 1422 1433 +f 1421 1433 1420 +f 1065 1171 1175 +f 1065 1175 1066 +f 1435 1436 1447 +f 1435 1447 1434 +f 1067 1179 1183 +f 1067 1183 1068 +f 1449 1450 1461 +f 1449 1461 1448 +f 1069 1187 1191 +f 1069 1191 1070 +f 1463 1464 1475 +f 1463 1475 1462 +f 1071 1195 1199 +f 1071 1199 1072 +f 1477 1478 1489 +f 1477 1489 1476 +f 1073 1203 1207 +f 1073 1207 1074 +f 1491 1492 1503 +f 1491 1503 1490 +f 1075 1211 1215 +f 1075 1215 1076 +f 1505 1506 1517 +f 1505 1517 1504 +f 1077 1219 1223 +f 1077 1223 1078 +f 1519 1520 1531 +f 1519 1531 1518 +f 1079 1227 1231 +f 1079 1231 1080 +f 1533 1534 1545 +f 1533 1545 1532 +f 1081 1235 1239 +f 1081 1239 1082 +f 1547 1548 1559 +f 1547 1559 1546 +f 1083 1243 1247 +f 1083 1247 1084 +f 1561 1562 1573 +f 1561 1573 1560 +f 1085 1251 1255 +f 1085 1255 1086 +f 1575 1576 1587 +f 1575 1587 1574 +f 1087 1259 1263 +f 1087 1263 1088 +f 1589 1590 1601 +f 1589 1601 1588 +f 1089 1267 1271 +f 1089 1271 1090 +f 1603 1604 1615 +f 1603 1615 1602 +f 1091 1275 1279 +f 1091 1279 1092 +f 1617 1618 1629 +f 1617 1629 1616 +f 1093 1283 1287 +f 1093 1287 1094 +f 1631 1632 1643 +f 1631 1643 1630 +f 1095 1291 1295 +f 1095 1295 1096 +f 1645 1646 1657 +f 1645 1657 1644 +f 1097 1299 1303 +f 1097 1303 1098 +f 1659 1660 1671 +f 1659 1671 1658 +f 994 1304 1100 +f 994 1100 943 +f 1310 1311 1320 +f 1310 1320 1321 +f 1311 1312 1319 +f 1311 1319 1320 +f 1312 1313 1318 +f 1312 1318 1319 +f 1313 1314 1317 +f 1313 1317 1318 +f 1107 1106 1110 +f 1107 1110 1111 +f 1106 1105 1109 +f 1106 1109 1110 +f 1105 1104 1108 +f 1105 1108 1109 +f 1104 944 945 +f 1104 945 1108 +f 1324 1325 1334 +f 1324 1334 1335 +f 1325 1326 1333 +f 1325 1333 1334 +f 1326 1327 1332 +f 1326 1332 1333 +f 1327 1328 1331 +f 1327 1331 1332 +f 1115 1114 1118 +f 1115 1118 1119 +f 1114 1113 1117 +f 1114 1117 1118 +f 1113 1112 1116 +f 1113 1116 1117 +f 1112 946 947 +f 1112 947 1116 +f 1338 1339 1348 +f 1338 1348 1349 +f 1339 1340 1347 +f 1339 1347 1348 +f 1340 1341 1346 +f 1340 1346 1347 +f 1341 1342 1345 +f 1341 1345 1346 +f 1123 1122 1126 +f 1123 1126 1127 +f 1122 1121 1125 +f 1122 1125 1126 +f 1121 1120 1124 +f 1121 1124 1125 +f 1120 948 949 +f 1120 949 1124 +f 1352 1353 1362 +f 1352 1362 1363 +f 1353 1354 1361 +f 1353 1361 1362 +f 1354 1355 1360 +f 1354 1360 1361 +f 1355 1356 1359 +f 1355 1359 1360 +f 1131 1130 1134 +f 1131 1134 1135 +f 1130 1129 1133 +f 1130 1133 1134 +f 1129 1128 1132 +f 1129 1132 1133 +f 1128 950 951 +f 1128 951 1132 +f 1366 1367 1376 +f 1366 1376 1377 +f 1367 1368 1375 +f 1367 1375 1376 +f 1368 1369 1374 +f 1368 1374 1375 +f 1369 1370 1373 +f 1369 1373 1374 +f 1139 1138 1142 +f 1139 1142 1143 +f 1138 1137 1141 +f 1138 1141 1142 +f 1137 1136 1140 +f 1137 1140 1141 +f 1136 952 953 +f 1136 953 1140 +f 1380 1381 1390 +f 1380 1390 1391 +f 1381 1382 1389 +f 1381 1389 1390 +f 1382 1383 1388 +f 1382 1388 1389 +f 1383 1384 1387 +f 1383 1387 1388 +f 1147 1146 1150 +f 1147 1150 1151 +f 1146 1145 1149 +f 1146 1149 1150 +f 1145 1144 1148 +f 1145 1148 1149 +f 1144 954 955 +f 1144 955 1148 +f 1394 1395 1404 +f 1394 1404 1405 +f 1395 1396 1403 +f 1395 1403 1404 +f 1396 1397 1402 +f 1396 1402 1403 +f 1397 1398 1401 +f 1397 1401 1402 +f 1155 1154 1158 +f 1155 1158 1159 +f 1154 1153 1157 +f 1154 1157 1158 +f 1153 1152 1156 +f 1153 1156 1157 +f 1152 956 957 +f 1152 957 1156 +f 1408 1409 1418 +f 1408 1418 1419 +f 1409 1410 1417 +f 1409 1417 1418 +f 1410 1411 1416 +f 1410 1416 1417 +f 1411 1412 1415 +f 1411 1415 1416 +f 1163 1162 1166 +f 1163 1166 1167 +f 1162 1161 1165 +f 1162 1165 1166 +f 1161 1160 1164 +f 1161 1164 1165 +f 1160 958 959 +f 1160 959 1164 +f 1422 1423 1432 +f 1422 1432 1433 +f 1423 1424 1431 +f 1423 1431 1432 +f 1424 1425 1430 +f 1424 1430 1431 +f 1425 1426 1429 +f 1425 1429 1430 +f 1171 1170 1174 +f 1171 1174 1175 +f 1170 1169 1173 +f 1170 1173 1174 +f 1169 1168 1172 +f 1169 1172 1173 +f 1168 960 961 +f 1168 961 1172 +f 1436 1437 1446 +f 1436 1446 1447 +f 1437 1438 1445 +f 1437 1445 1446 +f 1438 1439 1444 +f 1438 1444 1445 +f 1439 1440 1443 +f 1439 1443 1444 +f 1179 1178 1182 +f 1179 1182 1183 +f 1178 1177 1181 +f 1178 1181 1182 +f 1177 1176 1180 +f 1177 1180 1181 +f 1176 962 963 +f 1176 963 1180 +f 1450 1451 1460 +f 1450 1460 1461 +f 1451 1452 1459 +f 1451 1459 1460 +f 1452 1453 1458 +f 1452 1458 1459 +f 1453 1454 1457 +f 1453 1457 1458 +f 1187 1186 1190 +f 1187 1190 1191 +f 1186 1185 1189 +f 1186 1189 1190 +f 1185 1184 1188 +f 1185 1188 1189 +f 1184 964 965 +f 1184 965 1188 +f 1464 1465 1474 +f 1464 1474 1475 +f 1465 1466 1473 +f 1465 1473 1474 +f 1466 1467 1472 +f 1466 1472 1473 +f 1467 1468 1471 +f 1467 1471 1472 +f 1195 1194 1198 +f 1195 1198 1199 +f 1194 1193 1197 +f 1194 1197 1198 +f 1193 1192 1196 +f 1193 1196 1197 +f 1192 966 967 +f 1192 967 1196 +f 1478 1479 1488 +f 1478 1488 1489 +f 1479 1480 1487 +f 1479 1487 1488 +f 1480 1481 1486 +f 1480 1486 1487 +f 1481 1482 1485 +f 1481 1485 1486 +f 1203 1202 1206 +f 1203 1206 1207 +f 1202 1201 1205 +f 1202 1205 1206 +f 1201 1200 1204 +f 1201 1204 1205 +f 1200 968 969 +f 1200 969 1204 +f 1492 1493 1502 +f 1492 1502 1503 +f 1493 1494 1501 +f 1493 1501 1502 +f 1494 1495 1500 +f 1494 1500 1501 +f 1495 1496 1499 +f 1495 1499 1500 +f 1211 1210 1214 +f 1211 1214 1215 +f 1210 1209 1213 +f 1210 1213 1214 +f 1209 1208 1212 +f 1209 1212 1213 +f 1208 970 971 +f 1208 971 1212 +f 1506 1507 1516 +f 1506 1516 1517 +f 1507 1508 1515 +f 1507 1515 1516 +f 1508 1509 1514 +f 1508 1514 1515 +f 1509 1510 1513 +f 1509 1513 1514 +f 1219 1218 1222 +f 1219 1222 1223 +f 1218 1217 1221 +f 1218 1221 1222 +f 1217 1216 1220 +f 1217 1220 1221 +f 1216 972 973 +f 1216 973 1220 +f 1520 1521 1530 +f 1520 1530 1531 +f 1521 1522 1529 +f 1521 1529 1530 +f 1522 1523 1528 +f 1522 1528 1529 +f 1523 1524 1527 +f 1523 1527 1528 +f 1227 1226 1230 +f 1227 1230 1231 +f 1226 1225 1229 +f 1226 1229 1230 +f 1225 1224 1228 +f 1225 1228 1229 +f 1224 974 975 +f 1224 975 1228 +f 1534 1535 1544 +f 1534 1544 1545 +f 1535 1536 1543 +f 1535 1543 1544 +f 1536 1537 1542 +f 1536 1542 1543 +f 1537 1538 1541 +f 1537 1541 1542 +f 1235 1234 1238 +f 1235 1238 1239 +f 1234 1233 1237 +f 1234 1237 1238 +f 1233 1232 1236 +f 1233 1236 1237 +f 1232 976 977 +f 1232 977 1236 +f 1548 1549 1558 +f 1548 1558 1559 +f 1549 1550 1557 +f 1549 1557 1558 +f 1550 1551 1556 +f 1550 1556 1557 +f 1551 1552 1555 +f 1551 1555 1556 +f 1243 1242 1246 +f 1243 1246 1247 +f 1242 1241 1245 +f 1242 1245 1246 +f 1241 1240 1244 +f 1241 1244 1245 +f 1240 978 979 +f 1240 979 1244 +f 1562 1563 1572 +f 1562 1572 1573 +f 1563 1564 1571 +f 1563 1571 1572 +f 1564 1565 1570 +f 1564 1570 1571 +f 1565 1566 1569 +f 1565 1569 1570 +f 1251 1250 1254 +f 1251 1254 1255 +f 1250 1249 1253 +f 1250 1253 1254 +f 1249 1248 1252 +f 1249 1252 1253 +f 1248 980 981 +f 1248 981 1252 +f 1576 1577 1586 +f 1576 1586 1587 +f 1577 1578 1585 +f 1577 1585 1586 +f 1578 1579 1584 +f 1578 1584 1585 +f 1579 1580 1583 +f 1579 1583 1584 +f 1259 1258 1262 +f 1259 1262 1263 +f 1258 1257 1261 +f 1258 1261 1262 +f 1257 1256 1260 +f 1257 1260 1261 +f 1256 982 983 +f 1256 983 1260 +f 1590 1591 1600 +f 1590 1600 1601 +f 1591 1592 1599 +f 1591 1599 1600 +f 1592 1593 1598 +f 1592 1598 1599 +f 1593 1594 1597 +f 1593 1597 1598 +f 1267 1266 1270 +f 1267 1270 1271 +f 1266 1265 1269 +f 1266 1269 1270 +f 1265 1264 1268 +f 1265 1268 1269 +f 1264 984 985 +f 1264 985 1268 +f 1604 1605 1614 +f 1604 1614 1615 +f 1605 1606 1613 +f 1605 1613 1614 +f 1606 1607 1612 +f 1606 1612 1613 +f 1607 1608 1611 +f 1607 1611 1612 +f 1275 1274 1278 +f 1275 1278 1279 +f 1274 1273 1277 +f 1274 1277 1278 +f 1273 1272 1276 +f 1273 1276 1277 +f 1272 986 987 +f 1272 987 1276 +f 1618 1619 1628 +f 1618 1628 1629 +f 1619 1620 1627 +f 1619 1627 1628 +f 1620 1621 1626 +f 1620 1626 1627 +f 1621 1622 1625 +f 1621 1625 1626 +f 1283 1282 1286 +f 1283 1286 1287 +f 1282 1281 1285 +f 1282 1285 1286 +f 1281 1280 1284 +f 1281 1284 1285 +f 1280 988 989 +f 1280 989 1284 +f 1632 1633 1642 +f 1632 1642 1643 +f 1633 1634 1641 +f 1633 1641 1642 +f 1634 1635 1640 +f 1634 1640 1641 +f 1635 1636 1639 +f 1635 1639 1640 +f 1291 1290 1294 +f 1291 1294 1295 +f 1290 1289 1293 +f 1290 1293 1294 +f 1289 1288 1292 +f 1289 1292 1293 +f 1288 990 991 +f 1288 991 1292 +f 1646 1647 1656 +f 1646 1656 1657 +f 1647 1648 1655 +f 1647 1655 1656 +f 1648 1649 1654 +f 1648 1654 1655 +f 1649 1650 1653 +f 1649 1653 1654 +f 1299 1298 1302 +f 1299 1302 1303 +f 1298 1297 1301 +f 1298 1301 1302 +f 1297 1296 1300 +f 1297 1300 1301 +f 1296 992 993 +f 1296 993 1300 +f 1660 1661 1670 +f 1660 1670 1671 +f 1661 1662 1669 +f 1661 1669 1670 +f 1662 1663 1668 +f 1662 1668 1669 +f 1663 1664 1667 +f 1663 1667 1668 +f 1304 1305 1101 +f 1304 1101 1100 +f 1305 1306 1102 +f 1305 1102 1101 +f 1306 1307 1103 +f 1306 1103 1102 +f 1307 1099 1048 +f 1307 1048 1103 +f 1049 943 1309 +f 1049 1309 1308 +f 943 1100 1310 +f 943 1310 1309 +f 1100 1101 1311 +f 1100 1311 1310 +f 1101 1102 1312 +f 1101 1312 1311 +f 1102 1103 1313 +f 1102 1313 1312 +f 1103 1048 1314 +f 1103 1314 1313 +f 1048 995 1315 +f 1048 1315 1314 +f 995 996 1316 +f 995 1316 1315 +f 996 944 1317 +f 996 1317 1316 +f 944 1104 1318 +f 944 1318 1317 +f 1104 1105 1319 +f 1104 1319 1318 +f 1105 1106 1320 +f 1105 1320 1319 +f 1106 1107 1321 +f 1106 1321 1320 +f 1107 1049 1308 +f 1107 1308 1321 +f 1051 1050 1323 +f 1051 1323 1322 +f 1050 1111 1324 +f 1050 1324 1323 +f 1111 1110 1325 +f 1111 1325 1324 +f 1110 1109 1326 +f 1110 1326 1325 +f 1109 1108 1327 +f 1109 1327 1326 +f 1108 945 1328 +f 1108 1328 1327 +f 945 997 1329 +f 945 1329 1328 +f 997 998 1330 +f 997 1330 1329 +f 998 946 1331 +f 998 1331 1330 +f 946 1112 1332 +f 946 1332 1331 +f 1112 1113 1333 +f 1112 1333 1332 +f 1113 1114 1334 +f 1113 1334 1333 +f 1114 1115 1335 +f 1114 1335 1334 +f 1115 1051 1322 +f 1115 1322 1335 +f 1053 1052 1337 +f 1053 1337 1336 +f 1052 1119 1338 +f 1052 1338 1337 +f 1119 1118 1339 +f 1119 1339 1338 +f 1118 1117 1340 +f 1118 1340 1339 +f 1117 1116 1341 +f 1117 1341 1340 +f 1116 947 1342 +f 1116 1342 1341 +f 947 999 1343 +f 947 1343 1342 +f 999 1000 1344 +f 999 1344 1343 +f 1000 948 1345 +f 1000 1345 1344 +f 948 1120 1346 +f 948 1346 1345 +f 1120 1121 1347 +f 1120 1347 1346 +f 1121 1122 1348 +f 1121 1348 1347 +f 1122 1123 1349 +f 1122 1349 1348 +f 1123 1053 1336 +f 1123 1336 1349 +f 1055 1054 1351 +f 1055 1351 1350 +f 1054 1127 1352 +f 1054 1352 1351 +f 1127 1126 1353 +f 1127 1353 1352 +f 1126 1125 1354 +f 1126 1354 1353 +f 1125 1124 1355 +f 1125 1355 1354 +f 1124 949 1356 +f 1124 1356 1355 +f 949 1001 1357 +f 949 1357 1356 +f 1001 1002 1358 +f 1001 1358 1357 +f 1002 950 1359 +f 1002 1359 1358 +f 950 1128 1360 +f 950 1360 1359 +f 1128 1129 1361 +f 1128 1361 1360 +f 1129 1130 1362 +f 1129 1362 1361 +f 1130 1131 1363 +f 1130 1363 1362 +f 1131 1055 1350 +f 1131 1350 1363 +f 1057 1056 1365 +f 1057 1365 1364 +f 1056 1135 1366 +f 1056 1366 1365 +f 1135 1134 1367 +f 1135 1367 1366 +f 1134 1133 1368 +f 1134 1368 1367 +f 1133 1132 1369 +f 1133 1369 1368 +f 1132 951 1370 +f 1132 1370 1369 +f 951 1003 1371 +f 951 1371 1370 +f 1003 1004 1372 +f 1003 1372 1371 +f 1004 952 1373 +f 1004 1373 1372 +f 952 1136 1374 +f 952 1374 1373 +f 1136 1137 1375 +f 1136 1375 1374 +f 1137 1138 1376 +f 1137 1376 1375 +f 1138 1139 1377 +f 1138 1377 1376 +f 1139 1057 1364 +f 1139 1364 1377 +f 1059 1058 1379 +f 1059 1379 1378 +f 1058 1143 1380 +f 1058 1380 1379 +f 1143 1142 1381 +f 1143 1381 1380 +f 1142 1141 1382 +f 1142 1382 1381 +f 1141 1140 1383 +f 1141 1383 1382 +f 1140 953 1384 +f 1140 1384 1383 +f 953 1005 1385 +f 953 1385 1384 +f 1005 1006 1386 +f 1005 1386 1385 +f 1006 954 1387 +f 1006 1387 1386 +f 954 1144 1388 +f 954 1388 1387 +f 1144 1145 1389 +f 1144 1389 1388 +f 1145 1146 1390 +f 1145 1390 1389 +f 1146 1147 1391 +f 1146 1391 1390 +f 1147 1059 1378 +f 1147 1378 1391 +f 1061 1060 1393 +f 1061 1393 1392 +f 1060 1151 1394 +f 1060 1394 1393 +f 1151 1150 1395 +f 1151 1395 1394 +f 1150 1149 1396 +f 1150 1396 1395 +f 1149 1148 1397 +f 1149 1397 1396 +f 1148 955 1398 +f 1148 1398 1397 +f 955 1007 1399 +f 955 1399 1398 +f 1007 1008 1400 +f 1007 1400 1399 +f 1008 956 1401 +f 1008 1401 1400 +f 956 1152 1402 +f 956 1402 1401 +f 1152 1153 1403 +f 1152 1403 1402 +f 1153 1154 1404 +f 1153 1404 1403 +f 1154 1155 1405 +f 1154 1405 1404 +f 1155 1061 1392 +f 1155 1392 1405 +f 1063 1062 1407 +f 1063 1407 1406 +f 1062 1159 1408 +f 1062 1408 1407 +f 1159 1158 1409 +f 1159 1409 1408 +f 1158 1157 1410 +f 1158 1410 1409 +f 1157 1156 1411 +f 1157 1411 1410 +f 1156 957 1412 +f 1156 1412 1411 +f 957 1009 1413 +f 957 1413 1412 +f 1009 1010 1414 +f 1009 1414 1413 +f 1010 958 1415 +f 1010 1415 1414 +f 958 1160 1416 +f 958 1416 1415 +f 1160 1161 1417 +f 1160 1417 1416 +f 1161 1162 1418 +f 1161 1418 1417 +f 1162 1163 1419 +f 1162 1419 1418 +f 1163 1063 1406 +f 1163 1406 1419 +f 1065 1064 1421 +f 1065 1421 1420 +f 1064 1167 1422 +f 1064 1422 1421 +f 1167 1166 1423 +f 1167 1423 1422 +f 1166 1165 1424 +f 1166 1424 1423 +f 1165 1164 1425 +f 1165 1425 1424 +f 1164 959 1426 +f 1164 1426 1425 +f 959 1011 1427 +f 959 1427 1426 +f 1011 1012 1428 +f 1011 1428 1427 +f 1012 960 1429 +f 1012 1429 1428 +f 960 1168 1430 +f 960 1430 1429 +f 1168 1169 1431 +f 1168 1431 1430 +f 1169 1170 1432 +f 1169 1432 1431 +f 1170 1171 1433 +f 1170 1433 1432 +f 1171 1065 1420 +f 1171 1420 1433 +f 1067 1066 1435 +f 1067 1435 1434 +f 1066 1175 1436 +f 1066 1436 1435 +f 1175 1174 1437 +f 1175 1437 1436 +f 1174 1173 1438 +f 1174 1438 1437 +f 1173 1172 1439 +f 1173 1439 1438 +f 1172 961 1440 +f 1172 1440 1439 +f 961 1013 1441 +f 961 1441 1440 +f 1013 1014 1442 +f 1013 1442 1441 +f 1014 962 1443 +f 1014 1443 1442 +f 962 1176 1444 +f 962 1444 1443 +f 1176 1177 1445 +f 1176 1445 1444 +f 1177 1178 1446 +f 1177 1446 1445 +f 1178 1179 1447 +f 1178 1447 1446 +f 1179 1067 1434 +f 1179 1434 1447 +f 1069 1068 1449 +f 1069 1449 1448 +f 1068 1183 1450 +f 1068 1450 1449 +f 1183 1182 1451 +f 1183 1451 1450 +f 1182 1181 1452 +f 1182 1452 1451 +f 1181 1180 1453 +f 1181 1453 1452 +f 1180 963 1454 +f 1180 1454 1453 +f 963 1015 1455 +f 963 1455 1454 +f 1015 1016 1456 +f 1015 1456 1455 +f 1016 964 1457 +f 1016 1457 1456 +f 964 1184 1458 +f 964 1458 1457 +f 1184 1185 1459 +f 1184 1459 1458 +f 1185 1186 1460 +f 1185 1460 1459 +f 1186 1187 1461 +f 1186 1461 1460 +f 1187 1069 1448 +f 1187 1448 1461 +f 1071 1070 1463 +f 1071 1463 1462 +f 1070 1191 1464 +f 1070 1464 1463 +f 1191 1190 1465 +f 1191 1465 1464 +f 1190 1189 1466 +f 1190 1466 1465 +f 1189 1188 1467 +f 1189 1467 1466 +f 1188 965 1468 +f 1188 1468 1467 +f 965 1017 1469 +f 965 1469 1468 +f 1017 1018 1470 +f 1017 1470 1469 +f 1018 966 1471 +f 1018 1471 1470 +f 966 1192 1472 +f 966 1472 1471 +f 1192 1193 1473 +f 1192 1473 1472 +f 1193 1194 1474 +f 1193 1474 1473 +f 1194 1195 1475 +f 1194 1475 1474 +f 1195 1071 1462 +f 1195 1462 1475 +f 1073 1072 1477 +f 1073 1477 1476 +f 1072 1199 1478 +f 1072 1478 1477 +f 1199 1198 1479 +f 1199 1479 1478 +f 1198 1197 1480 +f 1198 1480 1479 +f 1197 1196 1481 +f 1197 1481 1480 +f 1196 967 1482 +f 1196 1482 1481 +f 967 1019 1483 +f 967 1483 1482 +f 1019 1020 1484 +f 1019 1484 1483 +f 1020 968 1485 +f 1020 1485 1484 +f 968 1200 1486 +f 968 1486 1485 +f 1200 1201 1487 +f 1200 1487 1486 +f 1201 1202 1488 +f 1201 1488 1487 +f 1202 1203 1489 +f 1202 1489 1488 +f 1203 1073 1476 +f 1203 1476 1489 +f 1075 1074 1491 +f 1075 1491 1490 +f 1074 1207 1492 +f 1074 1492 1491 +f 1207 1206 1493 +f 1207 1493 1492 +f 1206 1205 1494 +f 1206 1494 1493 +f 1205 1204 1495 +f 1205 1495 1494 +f 1204 969 1496 +f 1204 1496 1495 +f 969 1021 1497 +f 969 1497 1496 +f 1021 1022 1498 +f 1021 1498 1497 +f 1022 970 1499 +f 1022 1499 1498 +f 970 1208 1500 +f 970 1500 1499 +f 1208 1209 1501 +f 1208 1501 1500 +f 1209 1210 1502 +f 1209 1502 1501 +f 1210 1211 1503 +f 1210 1503 1502 +f 1211 1075 1490 +f 1211 1490 1503 +f 1077 1076 1505 +f 1077 1505 1504 +f 1076 1215 1506 +f 1076 1506 1505 +f 1215 1214 1507 +f 1215 1507 1506 +f 1214 1213 1508 +f 1214 1508 1507 +f 1213 1212 1509 +f 1213 1509 1508 +f 1212 971 1510 +f 1212 1510 1509 +f 971 1023 1511 +f 971 1511 1510 +f 1023 1024 1512 +f 1023 1512 1511 +f 1024 972 1513 +f 1024 1513 1512 +f 972 1216 1514 +f 972 1514 1513 +f 1216 1217 1515 +f 1216 1515 1514 +f 1217 1218 1516 +f 1217 1516 1515 +f 1218 1219 1517 +f 1218 1517 1516 +f 1219 1077 1504 +f 1219 1504 1517 +f 1079 1078 1519 +f 1079 1519 1518 +f 1078 1223 1520 +f 1078 1520 1519 +f 1223 1222 1521 +f 1223 1521 1520 +f 1222 1221 1522 +f 1222 1522 1521 +f 1221 1220 1523 +f 1221 1523 1522 +f 1220 973 1524 +f 1220 1524 1523 +f 973 1025 1525 +f 973 1525 1524 +f 1025 1026 1526 +f 1025 1526 1525 +f 1026 974 1527 +f 1026 1527 1526 +f 974 1224 1528 +f 974 1528 1527 +f 1224 1225 1529 +f 1224 1529 1528 +f 1225 1226 1530 +f 1225 1530 1529 +f 1226 1227 1531 +f 1226 1531 1530 +f 1227 1079 1518 +f 1227 1518 1531 +f 1081 1080 1533 +f 1081 1533 1532 +f 1080 1231 1534 +f 1080 1534 1533 +f 1231 1230 1535 +f 1231 1535 1534 +f 1230 1229 1536 +f 1230 1536 1535 +f 1229 1228 1537 +f 1229 1537 1536 +f 1228 975 1538 +f 1228 1538 1537 +f 975 1027 1539 +f 975 1539 1538 +f 1027 1028 1540 +f 1027 1540 1539 +f 1028 976 1541 +f 1028 1541 1540 +f 976 1232 1542 +f 976 1542 1541 +f 1232 1233 1543 +f 1232 1543 1542 +f 1233 1234 1544 +f 1233 1544 1543 +f 1234 1235 1545 +f 1234 1545 1544 +f 1235 1081 1532 +f 1235 1532 1545 +f 1083 1082 1547 +f 1083 1547 1546 +f 1082 1239 1548 +f 1082 1548 1547 +f 1239 1238 1549 +f 1239 1549 1548 +f 1238 1237 1550 +f 1238 1550 1549 +f 1237 1236 1551 +f 1237 1551 1550 +f 1236 977 1552 +f 1236 1552 1551 +f 977 1029 1553 +f 977 1553 1552 +f 1029 1030 1554 +f 1029 1554 1553 +f 1030 978 1555 +f 1030 1555 1554 +f 978 1240 1556 +f 978 1556 1555 +f 1240 1241 1557 +f 1240 1557 1556 +f 1241 1242 1558 +f 1241 1558 1557 +f 1242 1243 1559 +f 1242 1559 1558 +f 1243 1083 1546 +f 1243 1546 1559 +f 1085 1084 1561 +f 1085 1561 1560 +f 1084 1247 1562 +f 1084 1562 1561 +f 1247 1246 1563 +f 1247 1563 1562 +f 1246 1245 1564 +f 1246 1564 1563 +f 1245 1244 1565 +f 1245 1565 1564 +f 1244 979 1566 +f 1244 1566 1565 +f 979 1031 1567 +f 979 1567 1566 +f 1031 1032 1568 +f 1031 1568 1567 +f 1032 980 1569 +f 1032 1569 1568 +f 980 1248 1570 +f 980 1570 1569 +f 1248 1249 1571 +f 1248 1571 1570 +f 1249 1250 1572 +f 1249 1572 1571 +f 1250 1251 1573 +f 1250 1573 1572 +f 1251 1085 1560 +f 1251 1560 1573 +f 1087 1086 1575 +f 1087 1575 1574 +f 1086 1255 1576 +f 1086 1576 1575 +f 1255 1254 1577 +f 1255 1577 1576 +f 1254 1253 1578 +f 1254 1578 1577 +f 1253 1252 1579 +f 1253 1579 1578 +f 1252 981 1580 +f 1252 1580 1579 +f 981 1033 1581 +f 981 1581 1580 +f 1033 1034 1582 +f 1033 1582 1581 +f 1034 982 1583 +f 1034 1583 1582 +f 982 1256 1584 +f 982 1584 1583 +f 1256 1257 1585 +f 1256 1585 1584 +f 1257 1258 1586 +f 1257 1586 1585 +f 1258 1259 1587 +f 1258 1587 1586 +f 1259 1087 1574 +f 1259 1574 1587 +f 1089 1088 1589 +f 1089 1589 1588 +f 1088 1263 1590 +f 1088 1590 1589 +f 1263 1262 1591 +f 1263 1591 1590 +f 1262 1261 1592 +f 1262 1592 1591 +f 1261 1260 1593 +f 1261 1593 1592 +f 1260 983 1594 +f 1260 1594 1593 +f 983 1035 1595 +f 983 1595 1594 +f 1035 1036 1596 +f 1035 1596 1595 +f 1036 984 1597 +f 1036 1597 1596 +f 984 1264 1598 +f 984 1598 1597 +f 1264 1265 1599 +f 1264 1599 1598 +f 1265 1266 1600 +f 1265 1600 1599 +f 1266 1267 1601 +f 1266 1601 1600 +f 1267 1089 1588 +f 1267 1588 1601 +f 1091 1090 1603 +f 1091 1603 1602 +f 1090 1271 1604 +f 1090 1604 1603 +f 1271 1270 1605 +f 1271 1605 1604 +f 1270 1269 1606 +f 1270 1606 1605 +f 1269 1268 1607 +f 1269 1607 1606 +f 1268 985 1608 +f 1268 1608 1607 +f 985 1037 1609 +f 985 1609 1608 +f 1037 1038 1610 +f 1037 1610 1609 +f 1038 986 1611 +f 1038 1611 1610 +f 986 1272 1612 +f 986 1612 1611 +f 1272 1273 1613 +f 1272 1613 1612 +f 1273 1274 1614 +f 1273 1614 1613 +f 1274 1275 1615 +f 1274 1615 1614 +f 1275 1091 1602 +f 1275 1602 1615 +f 1093 1092 1617 +f 1093 1617 1616 +f 1092 1279 1618 +f 1092 1618 1617 +f 1279 1278 1619 +f 1279 1619 1618 +f 1278 1277 1620 +f 1278 1620 1619 +f 1277 1276 1621 +f 1277 1621 1620 +f 1276 987 1622 +f 1276 1622 1621 +f 987 1039 1623 +f 987 1623 1622 +f 1039 1040 1624 +f 1039 1624 1623 +f 1040 988 1625 +f 1040 1625 1624 +f 988 1280 1626 +f 988 1626 1625 +f 1280 1281 1627 +f 1280 1627 1626 +f 1281 1282 1628 +f 1281 1628 1627 +f 1282 1283 1629 +f 1282 1629 1628 +f 1283 1093 1616 +f 1283 1616 1629 +f 1095 1094 1631 +f 1095 1631 1630 +f 1094 1287 1632 +f 1094 1632 1631 +f 1287 1286 1633 +f 1287 1633 1632 +f 1286 1285 1634 +f 1286 1634 1633 +f 1285 1284 1635 +f 1285 1635 1634 +f 1284 989 1636 +f 1284 1636 1635 +f 989 1041 1637 +f 989 1637 1636 +f 1041 1042 1638 +f 1041 1638 1637 +f 1042 990 1639 +f 1042 1639 1638 +f 990 1288 1640 +f 990 1640 1639 +f 1288 1289 1641 +f 1288 1641 1640 +f 1289 1290 1642 +f 1289 1642 1641 +f 1290 1291 1643 +f 1290 1643 1642 +f 1291 1095 1630 +f 1291 1630 1643 +f 1097 1096 1645 +f 1097 1645 1644 +f 1096 1295 1646 +f 1096 1646 1645 +f 1295 1294 1647 +f 1295 1647 1646 +f 1294 1293 1648 +f 1294 1648 1647 +f 1293 1292 1649 +f 1293 1649 1648 +f 1292 991 1650 +f 1292 1650 1649 +f 991 1043 1651 +f 991 1651 1650 +f 1043 1044 1652 +f 1043 1652 1651 +f 1044 992 1653 +f 1044 1653 1652 +f 992 1296 1654 +f 992 1654 1653 +f 1296 1297 1655 +f 1296 1655 1654 +f 1297 1298 1656 +f 1297 1656 1655 +f 1298 1299 1657 +f 1298 1657 1656 +f 1299 1097 1644 +f 1299 1644 1657 +f 994 1098 1659 +f 994 1659 1658 +f 1098 1303 1660 +f 1098 1660 1659 +f 1303 1302 1661 +f 1303 1661 1660 +f 1302 1301 1662 +f 1302 1662 1661 +f 1301 1300 1663 +f 1301 1663 1662 +f 1300 993 1664 +f 1300 1664 1663 +f 993 1045 1665 +f 993 1665 1664 +f 1045 1046 1666 +f 1045 1666 1665 +f 1046 1099 1667 +f 1046 1667 1666 +f 1099 1307 1668 +f 1099 1668 1667 +f 1307 1306 1669 +f 1307 1669 1668 +f 1306 1305 1670 +f 1306 1670 1669 +f 1305 1304 1671 +f 1305 1671 1670 +f 1304 994 1658 +f 1304 1658 1671 +f 1828 1829 1724 +f 1828 1724 1723 +f 1829 1830 1725 +f 1829 1725 1724 +f 1830 1831 1726 +f 1830 1726 1725 +f 1831 1832 1727 +f 1831 1727 1726 +f 1832 1833 1728 +f 1832 1728 1727 +f 1833 1834 1729 +f 1833 1729 1728 +f 1834 1835 1730 +f 1834 1730 1729 +f 1835 1836 1731 +f 1835 1731 1730 +f 1836 1837 1732 +f 1836 1732 1731 +f 1837 1838 1733 +f 1837 1733 1732 +f 1838 1839 1734 +f 1838 1734 1733 +f 1839 1840 1735 +f 1839 1735 1734 +f 1840 1841 1736 +f 1840 1736 1735 +f 1841 1842 1737 +f 1841 1737 1736 +f 1842 1843 1738 +f 1842 1738 1737 +f 1843 1844 1739 +f 1843 1739 1738 +f 1844 1845 1740 +f 1844 1740 1739 +f 1845 1846 1741 +f 1845 1741 1740 +f 1846 1847 1742 +f 1846 1742 1741 +f 1847 1848 1743 +f 1847 1743 1742 +f 1848 1849 1744 +f 1848 1744 1743 +f 1849 1850 1745 +f 1849 1745 1744 +f 1850 1851 1746 +f 1850 1746 1745 +f 1851 1852 1747 +f 1851 1747 1746 +f 1852 1853 1748 +f 1852 1748 1747 +f 1853 1854 1749 +f 1853 1749 1748 +f 1854 1855 1750 +f 1854 1750 1749 +f 1855 1856 1751 +f 1855 1751 1750 +f 1856 1857 1752 +f 1856 1752 1751 +f 1857 1858 1753 +f 1857 1753 1752 +f 1858 1859 1754 +f 1858 1754 1753 +f 1859 1860 1755 +f 1859 1755 1754 +f 1860 1861 1756 +f 1860 1756 1755 +f 1861 1862 1757 +f 1861 1757 1756 +f 1862 1863 1758 +f 1862 1758 1757 +f 1863 1864 1759 +f 1863 1759 1758 +f 1864 1865 1760 +f 1864 1760 1759 +f 1865 1866 1761 +f 1865 1761 1760 +f 1866 1867 1762 +f 1866 1762 1761 +f 1867 1868 1763 +f 1867 1763 1762 +f 1868 1869 1764 +f 1868 1764 1763 +f 1869 1870 1765 +f 1869 1765 1764 +f 1870 1871 1766 +f 1870 1766 1765 +f 1871 1872 1767 +f 1871 1767 1766 +f 1872 1873 1768 +f 1872 1768 1767 +f 1873 1874 1769 +f 1873 1769 1768 +f 1874 1875 1770 +f 1874 1770 1769 +f 1875 1876 1771 +f 1875 1771 1770 +f 1876 1877 1772 +f 1876 1772 1771 +f 1877 1878 1773 +f 1877 1773 1772 +f 1878 1879 1774 +f 1878 1774 1773 +f 1879 1828 1723 +f 1879 1723 1774 +f 1776 1827 1775 +f 1776 1775 1723 +f 1776 1723 1724 +f 1777 1827 1776 +f 1777 1776 1724 +f 1777 1724 1725 +f 1778 1827 1777 +f 1778 1777 1725 +f 1778 1725 1726 +f 1779 1827 1778 +f 1779 1778 1726 +f 1779 1726 1727 +f 1780 1827 1779 +f 1780 1779 1727 +f 1780 1727 1728 +f 1781 1827 1780 +f 1781 1780 1728 +f 1781 1728 1729 +f 1782 1827 1781 +f 1782 1781 1729 +f 1782 1729 1730 +f 1783 1827 1782 +f 1783 1782 1730 +f 1783 1730 1731 +f 1784 1827 1783 +f 1784 1783 1731 +f 1784 1731 1732 +f 1785 1827 1784 +f 1785 1784 1732 +f 1785 1732 1733 +f 1786 1827 1785 +f 1786 1785 1733 +f 1786 1733 1734 +f 1787 1827 1786 +f 1787 1786 1734 +f 1787 1734 1735 +f 1788 1827 1787 +f 1788 1787 1735 +f 1788 1735 1736 +f 1789 1827 1788 +f 1789 1788 1736 +f 1789 1736 1737 +f 1790 1827 1789 +f 1790 1789 1737 +f 1790 1737 1738 +f 1791 1827 1790 +f 1791 1790 1738 +f 1791 1738 1739 +f 1792 1827 1791 +f 1792 1791 1739 +f 1792 1739 1740 +f 1793 1827 1792 +f 1793 1792 1740 +f 1793 1740 1741 +f 1794 1827 1793 +f 1794 1793 1741 +f 1794 1741 1742 +f 1795 1827 1794 +f 1795 1794 1742 +f 1795 1742 1743 +f 1796 1827 1795 +f 1796 1795 1743 +f 1796 1743 1744 +f 1797 1827 1796 +f 1797 1796 1744 +f 1797 1744 1745 +f 1798 1827 1797 +f 1798 1797 1745 +f 1798 1745 1746 +f 1799 1827 1798 +f 1799 1798 1746 +f 1799 1746 1747 +f 1800 1827 1799 +f 1800 1799 1747 +f 1800 1747 1748 +f 1801 1827 1800 +f 1801 1800 1748 +f 1801 1748 1749 +f 1802 1827 1801 +f 1802 1801 1749 +f 1802 1749 1750 +f 1803 1827 1802 +f 1803 1802 1750 +f 1803 1750 1751 +f 1804 1827 1803 +f 1804 1803 1751 +f 1804 1751 1752 +f 1805 1827 1804 +f 1805 1804 1752 +f 1805 1752 1753 +f 1806 1827 1805 +f 1806 1805 1753 +f 1806 1753 1754 +f 1807 1827 1806 +f 1807 1806 1754 +f 1807 1754 1755 +f 1808 1827 1807 +f 1808 1807 1755 +f 1808 1755 1756 +f 1809 1827 1808 +f 1809 1808 1756 +f 1809 1756 1757 +f 1810 1827 1809 +f 1810 1809 1757 +f 1810 1757 1758 +f 1811 1827 1810 +f 1811 1810 1758 +f 1811 1758 1759 +f 1812 1827 1811 +f 1812 1811 1759 +f 1812 1759 1760 +f 1813 1827 1812 +f 1813 1812 1760 +f 1813 1760 1761 +f 1814 1827 1813 +f 1814 1813 1761 +f 1814 1761 1762 +f 1815 1827 1814 +f 1815 1814 1762 +f 1815 1762 1763 +f 1816 1827 1815 +f 1816 1815 1763 +f 1816 1763 1764 +f 1817 1827 1816 +f 1817 1816 1764 +f 1817 1764 1765 +f 1818 1827 1817 +f 1818 1817 1765 +f 1818 1765 1766 +f 1819 1827 1818 +f 1819 1818 1766 +f 1819 1766 1767 +f 1820 1827 1819 +f 1820 1819 1767 +f 1820 1767 1768 +f 1821 1827 1820 +f 1821 1820 1768 +f 1821 1768 1769 +f 1822 1827 1821 +f 1822 1821 1769 +f 1822 1769 1770 +f 1823 1827 1822 +f 1823 1822 1770 +f 1823 1770 1771 +f 1824 1827 1823 +f 1824 1823 1771 +f 1824 1771 1772 +f 1825 1827 1824 +f 1825 1824 1772 +f 1825 1772 1773 +f 1826 1827 1825 +f 1826 1825 1773 +f 1826 1773 1774 +f 1775 1827 1826 +f 1775 1826 1774 +f 1775 1774 1723 +f 1828 1881 1883 +f 1828 1883 1829 +f 1829 1883 1885 +f 1829 1885 1830 +f 1830 1885 1887 +f 1830 1887 1831 +f 1831 1887 1889 +f 1831 1889 1832 +f 1832 1889 1891 +f 1832 1891 1833 +f 1833 1891 1893 +f 1833 1893 1834 +f 1834 1893 1895 +f 1834 1895 1835 +f 1835 1895 1897 +f 1835 1897 1836 +f 1836 1897 1899 +f 1836 1899 1837 +f 1837 1899 1901 +f 1837 1901 1838 +f 1838 1901 1903 +f 1838 1903 1839 +f 1839 1903 1905 +f 1839 1905 1840 +f 1840 1905 1907 +f 1840 1907 1841 +f 1841 1907 1909 +f 1841 1909 1842 +f 1842 1909 1911 +f 1842 1911 1843 +f 1843 1911 1913 +f 1843 1913 1844 +f 1844 1913 1915 +f 1844 1915 1845 +f 1845 1915 1917 +f 1845 1917 1846 +f 1846 1917 1919 +f 1846 1919 1847 +f 1847 1919 1921 +f 1847 1921 1848 +f 1848 1921 1923 +f 1848 1923 1849 +f 1849 1923 1925 +f 1849 1925 1850 +f 1850 1925 1927 +f 1850 1927 1851 +f 1851 1927 1929 +f 1851 1929 1852 +f 1852 1929 1931 +f 1852 1931 1853 +f 1853 1931 1933 +f 1853 1933 1854 +f 1854 1933 1935 +f 1854 1935 1855 +f 1855 1935 1937 +f 1855 1937 1856 +f 1856 1937 1939 +f 1856 1939 1857 +f 1857 1939 1941 +f 1857 1941 1858 +f 1858 1941 1943 +f 1858 1943 1859 +f 1859 1943 1945 +f 1859 1945 1860 +f 1860 1945 1947 +f 1860 1947 1861 +f 1861 1947 1949 +f 1861 1949 1862 +f 1862 1949 1951 +f 1862 1951 1863 +f 1863 1951 1953 +f 1863 1953 1864 +f 1864 1953 1955 +f 1864 1955 1865 +f 1865 1955 1957 +f 1865 1957 1866 +f 1866 1957 1959 +f 1866 1959 1867 +f 1867 1959 1961 +f 1867 1961 1868 +f 1868 1961 1963 +f 1868 1963 1869 +f 1869 1963 1965 +f 1869 1965 1870 +f 1870 1965 1967 +f 1870 1967 1871 +f 1871 1967 1969 +f 1871 1969 1872 +f 1872 1969 1971 +f 1872 1971 1873 +f 1873 1971 1973 +f 1873 1973 1874 +f 1874 1973 1975 +f 1874 1975 1875 +f 1875 1975 1977 +f 1875 1977 1876 +f 1876 1977 1979 +f 1876 1979 1877 +f 1877 1979 1981 +f 1877 1981 1878 +f 1878 1981 1983 +f 1878 1983 1879 +f 1879 1983 1881 +f 1879 1881 1828 +f 1881 1880 1882 +f 1881 1882 1883 +f 1880 1047 1672 +f 1880 1672 1882 +f 1883 1882 1884 +f 1883 1884 1885 +f 1882 1672 1673 +f 1882 1673 1884 +f 1885 1884 1886 +f 1885 1886 1887 +f 1884 1673 1674 +f 1884 1674 1886 +f 1887 1886 1888 +f 1887 1888 1889 +f 1886 1674 1675 +f 1886 1675 1888 +f 1889 1888 1890 +f 1889 1890 1891 +f 1888 1675 1676 +f 1888 1676 1890 +f 1891 1890 1892 +f 1891 1892 1893 +f 1890 1676 1677 +f 1890 1677 1892 +f 1893 1892 1894 +f 1893 1894 1895 +f 1892 1677 1678 +f 1892 1678 1894 +f 1895 1894 1896 +f 1895 1896 1897 +f 1894 1678 1679 +f 1894 1679 1896 +f 1897 1896 1898 +f 1897 1898 1899 +f 1896 1679 1680 +f 1896 1680 1898 +f 1899 1898 1900 +f 1899 1900 1901 +f 1898 1680 1681 +f 1898 1681 1900 +f 1901 1900 1902 +f 1901 1902 1903 +f 1900 1681 1682 +f 1900 1682 1902 +f 1903 1902 1904 +f 1903 1904 1905 +f 1902 1682 1683 +f 1902 1683 1904 +f 1905 1904 1906 +f 1905 1906 1907 +f 1904 1683 1684 +f 1904 1684 1906 +f 1907 1906 1908 +f 1907 1908 1909 +f 1906 1684 1685 +f 1906 1685 1908 +f 1909 1908 1910 +f 1909 1910 1911 +f 1908 1685 1686 +f 1908 1686 1910 +f 1911 1910 1912 +f 1911 1912 1913 +f 1910 1686 1687 +f 1910 1687 1912 +f 1913 1912 1914 +f 1913 1914 1915 +f 1912 1687 1688 +f 1912 1688 1914 +f 1915 1914 1916 +f 1915 1916 1917 +f 1914 1688 1689 +f 1914 1689 1916 +f 1917 1916 1918 +f 1917 1918 1919 +f 1916 1689 1690 +f 1916 1690 1918 +f 1919 1918 1920 +f 1919 1920 1921 +f 1918 1690 1691 +f 1918 1691 1920 +f 1921 1920 1922 +f 1921 1922 1923 +f 1920 1691 1692 +f 1920 1692 1922 +f 1923 1922 1924 +f 1923 1924 1925 +f 1922 1692 1693 +f 1922 1693 1924 +f 1925 1924 1926 +f 1925 1926 1927 +f 1924 1693 1694 +f 1924 1694 1926 +f 1927 1926 1928 +f 1927 1928 1929 +f 1926 1694 1695 +f 1926 1695 1928 +f 1929 1928 1930 +f 1929 1930 1931 +f 1928 1695 1696 +f 1928 1696 1930 +f 1931 1930 1932 +f 1931 1932 1933 +f 1930 1696 1697 +f 1930 1697 1932 +f 1933 1932 1934 +f 1933 1934 1935 +f 1932 1697 1698 +f 1932 1698 1934 +f 1935 1934 1936 +f 1935 1936 1937 +f 1934 1698 1699 +f 1934 1699 1936 +f 1937 1936 1938 +f 1937 1938 1939 +f 1936 1699 1700 +f 1936 1700 1938 +f 1939 1938 1940 +f 1939 1940 1941 +f 1938 1700 1701 +f 1938 1701 1940 +f 1941 1940 1942 +f 1941 1942 1943 +f 1940 1701 1702 +f 1940 1702 1942 +f 1943 1942 1944 +f 1943 1944 1945 +f 1942 1702 1703 +f 1942 1703 1944 +f 1945 1944 1946 +f 1945 1946 1947 +f 1944 1703 1704 +f 1944 1704 1946 +f 1947 1946 1948 +f 1947 1948 1949 +f 1946 1704 1705 +f 1946 1705 1948 +f 1949 1948 1950 +f 1949 1950 1951 +f 1948 1705 1706 +f 1948 1706 1950 +f 1951 1950 1952 +f 1951 1952 1953 +f 1950 1706 1707 +f 1950 1707 1952 +f 1953 1952 1954 +f 1953 1954 1955 +f 1952 1707 1708 +f 1952 1708 1954 +f 1955 1954 1956 +f 1955 1956 1957 +f 1954 1708 1709 +f 1954 1709 1956 +f 1957 1956 1958 +f 1957 1958 1959 +f 1956 1709 1710 +f 1956 1710 1958 +f 1959 1958 1960 +f 1959 1960 1961 +f 1958 1710 1711 +f 1958 1711 1960 +f 1961 1960 1962 +f 1961 1962 1963 +f 1960 1711 1712 +f 1960 1712 1962 +f 1963 1962 1964 +f 1963 1964 1965 +f 1962 1712 1713 +f 1962 1713 1964 +f 1965 1964 1966 +f 1965 1966 1967 +f 1964 1713 1714 +f 1964 1714 1966 +f 1967 1966 1968 +f 1967 1968 1969 +f 1966 1714 1715 +f 1966 1715 1968 +f 1969 1968 1970 +f 1969 1970 1971 +f 1968 1715 1716 +f 1968 1716 1970 +f 1971 1970 1972 +f 1971 1972 1973 +f 1970 1716 1717 +f 1970 1717 1972 +f 1973 1972 1974 +f 1973 1974 1975 +f 1972 1717 1718 +f 1972 1718 1974 +f 1975 1974 1976 +f 1975 1976 1977 +f 1974 1718 1719 +f 1974 1719 1976 +f 1977 1976 1978 +f 1977 1978 1979 +f 1976 1719 1720 +f 1976 1720 1978 +f 1979 1978 1980 +f 1979 1980 1981 +f 1978 1720 1721 +f 1978 1721 1980 +f 1981 1980 1982 +f 1981 1982 1983 +f 1980 1721 1722 +f 1980 1722 1982 +f 1983 1982 1880 +f 1983 1880 1881 +f 1982 1722 1047 +f 1982 1047 1880 +f 996 995 1984 +f 996 1984 1985 +f 997 996 1985 +f 997 1985 1986 +f 998 997 1986 +f 998 1986 1987 +f 999 998 1987 +f 999 1987 1988 +f 1000 999 1988 +f 1000 1988 1989 +f 1001 1000 1989 +f 1001 1989 1990 +f 1002 1001 1990 +f 1002 1990 1991 +f 1003 1002 1991 +f 1003 1991 1992 +f 1004 1003 1992 +f 1004 1992 1993 +f 1005 1004 1993 +f 1005 1993 1994 +f 1006 1005 1994 +f 1006 1994 1995 +f 1007 1006 1995 +f 1007 1995 1996 +f 1008 1007 1996 +f 1008 1996 1997 +f 1009 1008 1997 +f 1009 1997 1998 +f 1010 1009 1998 +f 1010 1998 1999 +f 1011 1010 1999 +f 1011 1999 2000 +f 1012 1011 2000 +f 1012 2000 2001 +f 1013 1012 2001 +f 1013 2001 2002 +f 1014 1013 2002 +f 1014 2002 2003 +f 1015 1014 2003 +f 1015 2003 2004 +f 1016 1015 2004 +f 1016 2004 2005 +f 1017 1016 2005 +f 1017 2005 2006 +f 1018 1017 2006 +f 1018 2006 2007 +f 1019 1018 2007 +f 1019 2007 2008 +f 1020 1019 2008 +f 1020 2008 2009 +f 1021 1020 2009 +f 1021 2009 2010 +f 1022 1021 2010 +f 1022 2010 2011 +f 1023 1022 2011 +f 1023 2011 2012 +f 1024 1023 2012 +f 1024 2012 2013 +f 1025 1024 2013 +f 1025 2013 2014 +f 1026 1025 2014 +f 1026 2014 2015 +f 1027 1026 2015 +f 1027 2015 2016 +f 1028 1027 2016 +f 1028 2016 2017 +f 1029 1028 2017 +f 1029 2017 2018 +f 1030 1029 2018 +f 1030 2018 2019 +f 1031 1030 2019 +f 1031 2019 2020 +f 1032 1031 2020 +f 1032 2020 2021 +f 1033 1032 2021 +f 1033 2021 2022 +f 1034 1033 2022 +f 1034 2022 2023 +f 1035 1034 2023 +f 1035 2023 2024 +f 1036 1035 2024 +f 1036 2024 2025 +f 1037 1036 2025 +f 1037 2025 2026 +f 1038 1037 2026 +f 1038 2026 2027 +f 1039 1038 2027 +f 1039 2027 2028 +f 1040 1039 2028 +f 1040 2028 2029 +f 1041 1040 2029 +f 1041 2029 2030 +f 1042 1041 2030 +f 1042 2030 2031 +f 1043 1042 2031 +f 1043 2031 2032 +f 1044 1043 2032 +f 1044 2032 2033 +f 1045 1044 2033 +f 1045 2033 2034 +f 1046 1045 2034 +f 1046 2034 2035 +f 995 1046 2035 +f 995 2035 1984 +f 1985 1984 2036 +f 1985 2036 2037 +f 1986 1985 2037 +f 1986 2037 2038 +f 1987 1986 2038 +f 1987 2038 2039 +f 1988 1987 2039 +f 1988 2039 2040 +f 1989 1988 2040 +f 1989 2040 2041 +f 1990 1989 2041 +f 1990 2041 2042 +f 1991 1990 2042 +f 1991 2042 2043 +f 1992 1991 2043 +f 1992 2043 2044 +f 1993 1992 2044 +f 1993 2044 2045 +f 1994 1993 2045 +f 1994 2045 2046 +f 1995 1994 2046 +f 1995 2046 2047 +f 1996 1995 2047 +f 1996 2047 2048 +f 1997 1996 2048 +f 1997 2048 2049 +f 1998 1997 2049 +f 1998 2049 2050 +f 1999 1998 2050 +f 1999 2050 2051 +f 2000 1999 2051 +f 2000 2051 2052 +f 2001 2000 2052 +f 2001 2052 2053 +f 2002 2001 2053 +f 2002 2053 2054 +f 2003 2002 2054 +f 2003 2054 2055 +f 2004 2003 2055 +f 2004 2055 2056 +f 2005 2004 2056 +f 2005 2056 2057 +f 2006 2005 2057 +f 2006 2057 2058 +f 2007 2006 2058 +f 2007 2058 2059 +f 2008 2007 2059 +f 2008 2059 2060 +f 2009 2008 2060 +f 2009 2060 2061 +f 2010 2009 2061 +f 2010 2061 2062 +f 2011 2010 2062 +f 2011 2062 2063 +f 2012 2011 2063 +f 2012 2063 2064 +f 2013 2012 2064 +f 2013 2064 2065 +f 2014 2013 2065 +f 2014 2065 2066 +f 2015 2014 2066 +f 2015 2066 2067 +f 2016 2015 2067 +f 2016 2067 2068 +f 2017 2016 2068 +f 2017 2068 2069 +f 2018 2017 2069 +f 2018 2069 2070 +f 2019 2018 2070 +f 2019 2070 2071 +f 2020 2019 2071 +f 2020 2071 2072 +f 2021 2020 2072 +f 2021 2072 2073 +f 2022 2021 2073 +f 2022 2073 2074 +f 2023 2022 2074 +f 2023 2074 2075 +f 2024 2023 2075 +f 2024 2075 2076 +f 2025 2024 2076 +f 2025 2076 2077 +f 2026 2025 2077 +f 2026 2077 2078 +f 2027 2026 2078 +f 2027 2078 2079 +f 2028 2027 2079 +f 2028 2079 2080 +f 2029 2028 2080 +f 2029 2080 2081 +f 2030 2029 2081 +f 2030 2081 2082 +f 2031 2030 2082 +f 2031 2082 2083 +f 2032 2031 2083 +f 2032 2083 2084 +f 2033 2032 2084 +f 2033 2084 2085 +f 2034 2033 2085 +f 2034 2085 2086 +f 2035 2034 2086 +f 2035 2086 2087 +f 1984 2035 2087 +f 1984 2087 2036 +f 2090 2089 2088 +f 2091 2090 2088 +f 2092 2091 2088 +f 2093 2092 2088 +f 2094 2093 2088 +f 2095 2094 2088 +f 2096 2095 2088 +f 2097 2096 2088 +f 2098 2097 2088 +f 2099 2098 2088 +f 2100 2099 2088 +f 2101 2100 2088 +f 2102 2101 2088 +f 2103 2102 2088 +f 2104 2103 2088 +f 2105 2104 2088 +f 2106 2105 2088 +f 2107 2106 2088 +f 2108 2107 2088 +f 2109 2108 2088 +f 2110 2109 2088 +f 2111 2110 2088 +f 2112 2111 2088 +f 2113 2112 2088 +f 2114 2113 2088 +f 2115 2114 2088 +f 2116 2115 2088 +f 2117 2116 2088 +f 2118 2117 2088 +f 2119 2118 2088 +f 2120 2119 2088 +f 2121 2120 2088 +f 2122 2121 2088 +f 2123 2122 2088 +f 2124 2123 2088 +f 2125 2124 2088 +f 2126 2125 2088 +f 2127 2126 2088 +f 2128 2127 2088 +f 2129 2128 2088 +f 2130 2129 2088 +f 2131 2130 2088 +f 2132 2131 2088 +f 2133 2132 2088 +f 2134 2133 2088 +f 2135 2134 2088 +f 2136 2135 2088 +f 2137 2136 2088 +f 2138 2137 2088 +f 2139 2138 2088 +f 2140 2139 2088 +f 2089 2140 2088 +f 2036 2141 2144 +f 2036 2144 2037 +f 2037 2144 2147 +f 2037 2147 2038 +f 2038 2147 2150 +f 2038 2150 2039 +f 2039 2150 2153 +f 2039 2153 2040 +f 2040 2153 2156 +f 2040 2156 2041 +f 2041 2156 2159 +f 2041 2159 2042 +f 2042 2159 2162 +f 2042 2162 2043 +f 2043 2162 2165 +f 2043 2165 2044 +f 2044 2165 2168 +f 2044 2168 2045 +f 2045 2168 2171 +f 2045 2171 2046 +f 2046 2171 2174 +f 2046 2174 2047 +f 2047 2174 2177 +f 2047 2177 2048 +f 2048 2177 2180 +f 2048 2180 2049 +f 2049 2180 2183 +f 2049 2183 2050 +f 2050 2183 2186 +f 2050 2186 2051 +f 2051 2186 2189 +f 2051 2189 2052 +f 2052 2189 2192 +f 2052 2192 2053 +f 2053 2192 2195 +f 2053 2195 2054 +f 2054 2195 2198 +f 2054 2198 2055 +f 2055 2198 2201 +f 2055 2201 2056 +f 2056 2201 2204 +f 2056 2204 2057 +f 2057 2204 2207 +f 2057 2207 2058 +f 2058 2207 2210 +f 2058 2210 2059 +f 2059 2210 2213 +f 2059 2213 2060 +f 2060 2213 2216 +f 2060 2216 2061 +f 2061 2216 2219 +f 2061 2219 2062 +f 2062 2219 2222 +f 2062 2222 2063 +f 2063 2222 2225 +f 2063 2225 2064 +f 2064 2225 2228 +f 2064 2228 2065 +f 2065 2228 2231 +f 2065 2231 2066 +f 2066 2231 2234 +f 2066 2234 2067 +f 2067 2234 2237 +f 2067 2237 2068 +f 2068 2237 2240 +f 2068 2240 2069 +f 2069 2240 2243 +f 2069 2243 2070 +f 2070 2243 2246 +f 2070 2246 2071 +f 2071 2246 2249 +f 2071 2249 2072 +f 2072 2249 2252 +f 2072 2252 2073 +f 2073 2252 2255 +f 2073 2255 2074 +f 2074 2255 2258 +f 2074 2258 2075 +f 2075 2258 2261 +f 2075 2261 2076 +f 2076 2261 2264 +f 2076 2264 2077 +f 2077 2264 2267 +f 2077 2267 2078 +f 2078 2267 2270 +f 2078 2270 2079 +f 2079 2270 2273 +f 2079 2273 2080 +f 2080 2273 2276 +f 2080 2276 2081 +f 2081 2276 2279 +f 2081 2279 2082 +f 2082 2279 2282 +f 2082 2282 2083 +f 2083 2282 2285 +f 2083 2285 2084 +f 2084 2285 2288 +f 2084 2288 2085 +f 2085 2288 2291 +f 2085 2291 2086 +f 2086 2291 2294 +f 2086 2294 2087 +f 2087 2294 2141 +f 2087 2141 2036 +f 2141 2142 2145 +f 2141 2145 2144 +f 2142 2143 2146 +f 2142 2146 2145 +f 2143 2089 2090 +f 2143 2090 2146 +f 2144 2145 2148 +f 2144 2148 2147 +f 2145 2146 2149 +f 2145 2149 2148 +f 2146 2090 2091 +f 2146 2091 2149 +f 2147 2148 2151 +f 2147 2151 2150 +f 2148 2149 2152 +f 2148 2152 2151 +f 2149 2091 2092 +f 2149 2092 2152 +f 2150 2151 2154 +f 2150 2154 2153 +f 2151 2152 2155 +f 2151 2155 2154 +f 2152 2092 2093 +f 2152 2093 2155 +f 2153 2154 2157 +f 2153 2157 2156 +f 2154 2155 2158 +f 2154 2158 2157 +f 2155 2093 2094 +f 2155 2094 2158 +f 2156 2157 2160 +f 2156 2160 2159 +f 2157 2158 2161 +f 2157 2161 2160 +f 2158 2094 2095 +f 2158 2095 2161 +f 2159 2160 2163 +f 2159 2163 2162 +f 2160 2161 2164 +f 2160 2164 2163 +f 2161 2095 2096 +f 2161 2096 2164 +f 2162 2163 2166 +f 2162 2166 2165 +f 2163 2164 2167 +f 2163 2167 2166 +f 2164 2096 2097 +f 2164 2097 2167 +f 2165 2166 2169 +f 2165 2169 2168 +f 2166 2167 2170 +f 2166 2170 2169 +f 2167 2097 2098 +f 2167 2098 2170 +f 2168 2169 2172 +f 2168 2172 2171 +f 2169 2170 2173 +f 2169 2173 2172 +f 2170 2098 2099 +f 2170 2099 2173 +f 2171 2172 2175 +f 2171 2175 2174 +f 2172 2173 2176 +f 2172 2176 2175 +f 2173 2099 2100 +f 2173 2100 2176 +f 2174 2175 2178 +f 2174 2178 2177 +f 2175 2176 2179 +f 2175 2179 2178 +f 2176 2100 2101 +f 2176 2101 2179 +f 2177 2178 2181 +f 2177 2181 2180 +f 2178 2179 2182 +f 2178 2182 2181 +f 2179 2101 2102 +f 2179 2102 2182 +f 2180 2181 2184 +f 2180 2184 2183 +f 2181 2182 2185 +f 2181 2185 2184 +f 2182 2102 2103 +f 2182 2103 2185 +f 2183 2184 2187 +f 2183 2187 2186 +f 2184 2185 2188 +f 2184 2188 2187 +f 2185 2103 2104 +f 2185 2104 2188 +f 2186 2187 2190 +f 2186 2190 2189 +f 2187 2188 2191 +f 2187 2191 2190 +f 2188 2104 2105 +f 2188 2105 2191 +f 2189 2190 2193 +f 2189 2193 2192 +f 2190 2191 2194 +f 2190 2194 2193 +f 2191 2105 2106 +f 2191 2106 2194 +f 2192 2193 2196 +f 2192 2196 2195 +f 2193 2194 2197 +f 2193 2197 2196 +f 2194 2106 2107 +f 2194 2107 2197 +f 2195 2196 2199 +f 2195 2199 2198 +f 2196 2197 2200 +f 2196 2200 2199 +f 2197 2107 2108 +f 2197 2108 2200 +f 2198 2199 2202 +f 2198 2202 2201 +f 2199 2200 2203 +f 2199 2203 2202 +f 2200 2108 2109 +f 2200 2109 2203 +f 2201 2202 2205 +f 2201 2205 2204 +f 2202 2203 2206 +f 2202 2206 2205 +f 2203 2109 2110 +f 2203 2110 2206 +f 2204 2205 2208 +f 2204 2208 2207 +f 2205 2206 2209 +f 2205 2209 2208 +f 2206 2110 2111 +f 2206 2111 2209 +f 2207 2208 2211 +f 2207 2211 2210 +f 2208 2209 2212 +f 2208 2212 2211 +f 2209 2111 2112 +f 2209 2112 2212 +f 2210 2211 2214 +f 2210 2214 2213 +f 2211 2212 2215 +f 2211 2215 2214 +f 2212 2112 2113 +f 2212 2113 2215 +f 2213 2214 2217 +f 2213 2217 2216 +f 2214 2215 2218 +f 2214 2218 2217 +f 2215 2113 2114 +f 2215 2114 2218 +f 2216 2217 2220 +f 2216 2220 2219 +f 2217 2218 2221 +f 2217 2221 2220 +f 2218 2114 2115 +f 2218 2115 2221 +f 2219 2220 2223 +f 2219 2223 2222 +f 2220 2221 2224 +f 2220 2224 2223 +f 2221 2115 2116 +f 2221 2116 2224 +f 2222 2223 2226 +f 2222 2226 2225 +f 2223 2224 2227 +f 2223 2227 2226 +f 2224 2116 2117 +f 2224 2117 2227 +f 2225 2226 2229 +f 2225 2229 2228 +f 2226 2227 2230 +f 2226 2230 2229 +f 2227 2117 2118 +f 2227 2118 2230 +f 2228 2229 2232 +f 2228 2232 2231 +f 2229 2230 2233 +f 2229 2233 2232 +f 2230 2118 2119 +f 2230 2119 2233 +f 2231 2232 2235 +f 2231 2235 2234 +f 2232 2233 2236 +f 2232 2236 2235 +f 2233 2119 2120 +f 2233 2120 2236 +f 2234 2235 2238 +f 2234 2238 2237 +f 2235 2236 2239 +f 2235 2239 2238 +f 2236 2120 2121 +f 2236 2121 2239 +f 2237 2238 2241 +f 2237 2241 2240 +f 2238 2239 2242 +f 2238 2242 2241 +f 2239 2121 2122 +f 2239 2122 2242 +f 2240 2241 2244 +f 2240 2244 2243 +f 2241 2242 2245 +f 2241 2245 2244 +f 2242 2122 2123 +f 2242 2123 2245 +f 2243 2244 2247 +f 2243 2247 2246 +f 2244 2245 2248 +f 2244 2248 2247 +f 2245 2123 2124 +f 2245 2124 2248 +f 2246 2247 2250 +f 2246 2250 2249 +f 2247 2248 2251 +f 2247 2251 2250 +f 2248 2124 2125 +f 2248 2125 2251 +f 2249 2250 2253 +f 2249 2253 2252 +f 2250 2251 2254 +f 2250 2254 2253 +f 2251 2125 2126 +f 2251 2126 2254 +f 2252 2253 2256 +f 2252 2256 2255 +f 2253 2254 2257 +f 2253 2257 2256 +f 2254 2126 2127 +f 2254 2127 2257 +f 2255 2256 2259 +f 2255 2259 2258 +f 2256 2257 2260 +f 2256 2260 2259 +f 2257 2127 2128 +f 2257 2128 2260 +f 2258 2259 2262 +f 2258 2262 2261 +f 2259 2260 2263 +f 2259 2263 2262 +f 2260 2128 2129 +f 2260 2129 2263 +f 2261 2262 2265 +f 2261 2265 2264 +f 2262 2263 2266 +f 2262 2266 2265 +f 2263 2129 2130 +f 2263 2130 2266 +f 2264 2265 2268 +f 2264 2268 2267 +f 2265 2266 2269 +f 2265 2269 2268 +f 2266 2130 2131 +f 2266 2131 2269 +f 2267 2268 2271 +f 2267 2271 2270 +f 2268 2269 2272 +f 2268 2272 2271 +f 2269 2131 2132 +f 2269 2132 2272 +f 2270 2271 2274 +f 2270 2274 2273 +f 2271 2272 2275 +f 2271 2275 2274 +f 2272 2132 2133 +f 2272 2133 2275 +f 2273 2274 2277 +f 2273 2277 2276 +f 2274 2275 2278 +f 2274 2278 2277 +f 2275 2133 2134 +f 2275 2134 2278 +f 2276 2277 2280 +f 2276 2280 2279 +f 2277 2278 2281 +f 2277 2281 2280 +f 2278 2134 2135 +f 2278 2135 2281 +f 2279 2280 2283 +f 2279 2283 2282 +f 2280 2281 2284 +f 2280 2284 2283 +f 2281 2135 2136 +f 2281 2136 2284 +f 2282 2283 2286 +f 2282 2286 2285 +f 2283 2284 2287 +f 2283 2287 2286 +f 2284 2136 2137 +f 2284 2137 2287 +f 2285 2286 2289 +f 2285 2289 2288 +f 2286 2287 2290 +f 2286 2290 2289 +f 2287 2137 2138 +f 2287 2138 2290 +f 2288 2289 2292 +f 2288 2292 2291 +f 2289 2290 2293 +f 2289 2293 2292 +f 2290 2138 2139 +f 2290 2139 2293 +f 2291 2292 2295 +f 2291 2295 2294 +f 2292 2293 2296 +f 2292 2296 2295 +f 2293 2139 2140 +f 2293 2140 2296 +f 2294 2295 2142 +f 2294 2142 2141 +f 2295 2296 2143 +f 2295 2143 2142 +f 2296 2140 2089 +f 2296 2089 2143 diff --git a/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.usd b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.usd new file mode 100644 index 0000000..656c50c Binary files /dev/null and b/workflows/simbox/example_assets/task/pick_test_tube/test_tube/Aligned_obj.usd differ diff --git a/workflows/simbox/example_assets/task/pick_test_tube/test_tube_rack/Test_Tube_Rack_AA_01.usdc b/workflows/simbox/example_assets/task/pick_test_tube/test_tube_rack/Test_Tube_Rack_AA_01.usdc new file mode 120000 index 0000000..c9bf233 --- /dev/null +++ b/workflows/simbox/example_assets/task/pick_test_tube/test_tube_rack/Test_Tube_Rack_AA_01.usdc @@ -0,0 +1 @@ +/home/tangger/LYT/maic_usd_assets_moudle/laboratory_equipment/Test_Tube_Rack/Test_Tube_Rack_AA_01.usdc \ No newline at end of file