Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- applications/avoiding/avoiding_baselines/DiRecT/.gitignore +28 -0
- applications/avoiding/avoiding_baselines/DiRecT/README.md +155 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/__init__.py +1 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Config/fictive_robot_controller_config.gin +19 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Config/mujoco_controller_config.gin +48 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Config/pybullet_controller_config.gin +34 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Controller.py +253 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/GainsInterface.py +90 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/IKControllers.py +386 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/TrajectoryTracking.py +416 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/__init__.py +3 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/executor.py +48 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Camera.py +205 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Model.py +91 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Robots.py +491 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Scene.py +319 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/__init__.py +4 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/logger.py +867 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/sim_object/__init__.py +2 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/sim_object/sim_obj_repo.py +46 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/sim_object/sim_object.py +49 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/time_keeper.py +13 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_controllers.py +162 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_env_wrapper.py +119 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_utils/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_utils/helpers.py +7 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_utils/rewards.py +13 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/SimFactory.py +81 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/__init__.py +6 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjCamera.py +196 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjFactory.py +50 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjLoadable.py +86 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjPrimLoader.py +38 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjRobot.py +287 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjScene.py +282 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/__init__.py +14 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_helper.py +75 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_render_singleton.py +44 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_renderer.py +593 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_scene_object.py +262 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_scene_parser.py +94 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/universal_sim/PrimitiveObjects.py +144 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/universal_sim/__init__.py +0 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/utils/__init__.py +1 -0
- applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/utils/geometric_transformation.py +319 -0
applications/avoiding/avoiding_baselines/DiRecT/.gitignore
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Caches
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.pyc
|
| 4 |
+
*.pyo
|
| 5 |
+
|
| 6 |
+
# Python packaging
|
| 7 |
+
*.egg-info/
|
| 8 |
+
*.so
|
| 9 |
+
*.o
|
| 10 |
+
|
| 11 |
+
# Experiments, logs, and run outputs
|
| 12 |
+
logs/
|
| 13 |
+
slurm_logs/
|
| 14 |
+
|
| 15 |
+
# Mujoco
|
| 16 |
+
MUJOCO_LOG.TXT
|
| 17 |
+
panda_tmp_*.xml
|
| 18 |
+
|
| 19 |
+
# Notebooks
|
| 20 |
+
.ipynb_checkpoints/
|
| 21 |
+
*.ipynb
|
| 22 |
+
|
| 23 |
+
# Slurm
|
| 24 |
+
*.out
|
| 25 |
+
*.err
|
| 26 |
+
|
| 27 |
+
# Wandb
|
| 28 |
+
wandb/
|
applications/avoiding/avoiding_baselines/DiRecT/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Safe robotic manipulation in D3IL avoiding
|
| 2 |
+
|
| 3 |
+
This folder presents the benchmarks for _DiRecT_ and relevant baselines on the [D3IL](https://github.com/ALRhub/d3il) `avoiding-v0` navigation task with dynamics constraints. The agent must navigate upward through 6 circular pillar obstacles to reach a goal region while satisfying linearized dynamics and obstacle-avoidance constraints.
|
| 4 |
+

|
| 5 |
+
|
| 6 |
+
**Table of Contents**:
|
| 7 |
+
- [Installation](#installation)
|
| 8 |
+
- [Fit the linearized dynamics](#fit-the-linearized-dynamics)
|
| 9 |
+
- [Train the model](#train-the-model)
|
| 10 |
+
- [Evaluation](#evaluation)
|
| 11 |
+
- [Results](#results)
|
| 12 |
+
- [Code structure](#code-structure)
|
| 13 |
+
- [References](#references)
|
| 14 |
+
|
| 15 |
+
## Installation
|
| 16 |
+
> [!NOTE]
|
| 17 |
+
> The `avoiding-v0` environment uses MuJoCo 2.3.x via the `mujoco` Python bindings — no separate system-wide MuJoCo install is required. CasADi / IPOPT and Pinocchio are pulled in through conda-forge.
|
| 18 |
+
|
| 19 |
+
1. Change the CUDA and torch versions in `environment.yml` to ones compatible with your system requirements (default is CUDA 12.6 + torch 2.11).
|
| 20 |
+
2. Create and activate the conda environment. The provided `setup_env.sh` script handles environment creation, the `qpth` install (which needs `--no-build-isolation` due to a broken numpy specifier), and the `LD_LIBRARY_PATH` activation hook:
|
| 21 |
+
```bash
|
| 22 |
+
bash setup_env.sh
|
| 23 |
+
conda activate direct-d3il
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
## Fit the linearized dynamics
|
| 27 |
+
Fit a linear dynamics model `s_{t+1} = A·s_t + B·a_t + c` from the D3IL `avoiding-v0` expert trajectories (96 episodes). The fitted matrices are later imposed as linear constraints during planning.
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
python fit_dynamics.py \
|
| 31 |
+
--env avoiding-v0 # gym env id (defaults to avoiding-v0)
|
| 32 |
+
```
|
| 33 |
+
Outputs (written to `logs/avoiding-v0/dynamics/`):
|
| 34 |
+
- `linear_model.npz` — fitted `A`, `B`, `c`, metadata, and normalizer (consumed by `eval.py`).
|
| 35 |
+
- `linear_model_predictions.png` — per-dimension true-vs-predicted scatter plot.
|
| 36 |
+
- `dynamics_verification.png` — single-step comparison against the real environment.
|
| 37 |
+
- `all_trajectories.png` — overlay of the expert dataset.
|
| 38 |
+
|
| 39 |
+
## Train the model
|
| 40 |
+
Train the unconstrained diffusion planner (Temporal U-Net, x_0-prediction parameterization with a cosine noise schedule) on the same D3IL dataset. Weights & Biases is used for logging (project `direct-d3il`).
|
| 41 |
+
|
| 42 |
+
```bash
|
| 43 |
+
python train.py \
|
| 44 |
+
--exp-name x0pred \ # subfolder under <log-folder>/<env>/diffusion/
|
| 45 |
+
--seed 0 \
|
| 46 |
+
--batch-size 32 \ # overrides default 32
|
| 47 |
+
--n-train-steps 1000001 # default 1M steps
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Resume from the latest checkpoint with `--resume`. Checkpoints (raw + EMA weights), sample previews, and the resolved config are written to `logs/avoiding-v0/diffusion/<exp-name>/`:
|
| 51 |
+
- `model_*.pth`, `model_ema_*.pth` — saved every `--save-freq` steps (default 50k).
|
| 52 |
+
- `samples/step_*.png` — periodic sample previews from the EMA model.
|
| 53 |
+
- `tensorboard_logs/` — TensorBoard event files.
|
| 54 |
+
- `config.yaml` — snapshot of the fully-resolved config.
|
| 55 |
+
|
| 56 |
+
To submit as a SLURM job (edit the `#SBATCH` directives and conda env name first):
|
| 57 |
+
```bash
|
| 58 |
+
sbatch run.sbatch
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
## Evaluation
|
| 62 |
+
Evaluate any of the planning policies on the constrained `avoiding-v0` task. The trained diffusion weights and fitted dynamics are both required.
|
| 63 |
+
|
| 64 |
+
```bash
|
| 65 |
+
python eval.py \
|
| 66 |
+
--diffusion-exp-name x0pred \ # which trained model to load
|
| 67 |
+
--diffusion-cp 10 \ # checkpoint index (model_ema_<cp>.pth)
|
| 68 |
+
--guidance-method direct \ # one of the methods listed below
|
| 69 |
+
--constraints \ # enable the obstacle constraint set
|
| 70 |
+
--random-repeat 100 \ # number of evaluation rollouts
|
| 71 |
+
--exp-name direct \ # subfolder under <log-folder>/<env>/eval/
|
| 72 |
+
--seed 0 \
|
| 73 |
+
--device cuda:0
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
Available planning policies (selected via `--guidance-method`): `no` (unconditional), `gradient`, `classifier`, `projection`, `safediffuser` (with `--cbf-algorithm {RoS,ReS,TVS}`), `primal-dual`, `augmented-lagrangian`, `direct`. The DiRecT method additionally supports `--pred-objective distance` to add a goal-distance objective in clean x_0 space.
|
| 77 |
+
|
| 78 |
+
To run the full sweep over every policy (dynamics constraint always on, 100 episodes each), use the provided SLURM script (edit the `#SBATCH` directives and conda env name first):
|
| 79 |
+
```bash
|
| 80 |
+
sbatch eval_all.sbatch
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
## Results
|
| 84 |
+
For each evaluation, results are written under `logs/avoiding-v0/eval/<exp-name>/`:
|
| 85 |
+
- `config.yaml` — resolved config used for the run.
|
| 86 |
+
- `runs.csv` — one row per rollout with per-run metrics (success, safety, steps, total constraint violations, total reward, average computation time per replan).
|
| 87 |
+
- `summary.json` — aggregated statistics (success rate, safety rate, average steps / violations / rewards / compute time).
|
| 88 |
+
- `all_real.png` — overlay of every closed-loop rollout on the obstacle field.
|
| 89 |
+
- `predictions/ep_*.png` — per-episode plot overlaying the diffused plans and the executed rollout.
|
| 90 |
+
- `trajectories.npz` — raw rollout state arrays (suppressed by `--no-trajectories`).
|
| 91 |
+
|
| 92 |
+
## Code structure
|
| 93 |
+
```
|
| 94 |
+
d3il/
|
| 95 |
+
├── d3il/ # ported D3IL `avoiding-v0` environment + expert data (96 episodes)
|
| 96 |
+
│ └── environments/
|
| 97 |
+
├── datasets/
|
| 98 |
+
│ ├── sequence.py # `SequenceDataset` (H=16 windows, LimitsNormalizer)
|
| 99 |
+
│ ├── buffer.py # replay buffer over expert episodes
|
| 100 |
+
│ ├── normalization.py # observation / action normalizers
|
| 101 |
+
│ └── preprocessing.py
|
| 102 |
+
├── policy/ # planning policies (one file per method)
|
| 103 |
+
│ ├── unconditional.py # baseline DDIM sampling
|
| 104 |
+
│ ├── gradient_guidance.py # DPS-style posterior sampling
|
| 105 |
+
│ ├── classifier_guidance.py # classifier guidance on noisy trajectory
|
| 106 |
+
│ ├── projected_diffusion.py # IPOPT projection per denoising step (noisy space)
|
| 107 |
+
│ ├── safediffuser.py # CBF-guided QP (RoS / ReS / TVS)
|
| 108 |
+
│ ├── augmented_lagrangian.py # primal-dual + augmented Lagrangian
|
| 109 |
+
│ ├── direct.py # DiRecT: IPOPT projection in clean x_0 space
|
| 110 |
+
│ └── value_model.py # differentiable proxy value function (obstacles + dynamics)
|
| 111 |
+
├── model.py # `TemporalUnet` + `CosineScheduleDiffusion` (x_0 parameterization)
|
| 112 |
+
├── projector.py # `IpoptProjector` — CasADi/IPOPT constraint projection
|
| 113 |
+
├── obstacles.py # circular & planar obstacles, constraint sets, violation checks
|
| 114 |
+
├── fit_dynamics.py # fit `s_{t+1} = A s_t + B a_t + c` from offline data
|
| 115 |
+
├── train.py # train the diffusion planner
|
| 116 |
+
├── eval.py # rollout + metric aggregation for a chosen policy
|
| 117 |
+
├── utils/ # rendering, array helpers, training utilities
|
| 118 |
+
├── run.sbatch # SLURM script for training
|
| 119 |
+
├── eval_all.sbatch # SLURM script sweeping all 11 policies
|
| 120 |
+
├── setup_env.sh # conda env bootstrap (incl. qpth + LD_LIBRARY_PATH hook)
|
| 121 |
+
└── environment.yml # conda environment spec
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
## ✏️ Citation
|
| 125 |
+
If you find our code or paper useful for your research, please consider citing our work:
|
| 126 |
+
```
|
| 127 |
+
@misc{giaretta2026directsafediffusionbasedplanning,
|
| 128 |
+
title={DiRecT: Safe Diffusion-Based Planning via Receding-Horizon Denoising},
|
| 129 |
+
author={Paolo Giaretta and Zeyang Li and Navid Azizan},
|
| 130 |
+
year={2026},
|
| 131 |
+
eprint={2606.15359},
|
| 132 |
+
archivePrefix={arXiv},
|
| 133 |
+
primaryClass={cs.LG},
|
| 134 |
+
url={https://arxiv.org/abs/2606.15359},
|
| 135 |
+
}
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
The `avoiding-v0` environment and expert data come from the D3IL benchmark:
|
| 139 |
+
```bibtex
|
| 140 |
+
@inproceedings{jia2024towards,
|
| 141 |
+
title={Towards Diverse Behaviors: A Benchmark for Imitation Learning with Human Demonstrations},
|
| 142 |
+
author={Xiaogang Jia and Denis Blessing and Xinkai Jiang and Moritz Reuss and Atalay Donat and Rudolf Lioutikov and Gerhard Neumann},
|
| 143 |
+
booktitle={The Twelfth International Conference on Learning Representations},
|
| 144 |
+
year={2024},
|
| 145 |
+
url={https://openreview.net/forum?id=6pPYRXKPpw}
|
| 146 |
+
}
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
## References
|
| 150 |
+
- Michael Janner, Yilun Du, Joshua B. Tenenbaum, and Sergey Levine. Planning with diffusion for flexible behavior synthesis. In Proceedings of the 39th International Conference on Machine Learning, volume 162 of Proceedings of Machine Learning Research, pages 9902–9915, 2022.
|
| 151 |
+
- Prafulla Dhariwal and Alexander Nichol. Diffusion models beat gans on image synthesis. In Advances in Neural Information Processing Systems, volume 34, pages 8780–8794, 2021.
|
| 152 |
+
- Jacob K Christopher, Stephen Baek, and Ferdinando Fioretto. Constrained synthesis with projected diffusion models. Advances in Neural Information Processing Systems, 37:89307–89333, 2024.
|
| 153 |
+
- Wei Xiao, Tsun-Hsuan Wang, Chuang Gan, Ramin Hasani, Mathias Lechner, and Daniela Rus. Safediffuser: Safe planning with diffusion probabilistic models. In The Thirteenth International Conference on Learning Representations, 2025. URL https://openreview.net/forum?id=ig2wk7kK9J
|
| 154 |
+
- Jichen Zhang, Liqun Zhao, Antonis Papachristodoulou, and Jack Umenberger. Constrained diffusers for safe planning and control, 2025. URL https://arxiv.org/abs/2506.12544.
|
| 155 |
+
- Xiaogang Jia, Denis Blessing, Xinkai Jiang, Moritz Reuss, Atalay Donat, Rudolf Lioutikov, and Gerhard Neumann. Towards diverse behaviors: A benchmark for imitation learning with human demonstrations. In The Twelfth International Conference on Learning Representations, 2024.
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .environments.d3il.envs.gym_avoiding_env.gym_avoiding import envs
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Config/fictive_robot_controller_config.gin
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Joint PD Controller
|
| 2 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.pgain = [1200.0, 1200.0, 1200.0, 1200.0, 1200.0, 300.0, 100.0]
|
| 3 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.dgain = [25.0, 25.0, 25.0, 25.0, 15.0, 12.5, 7.5]
|
| 4 |
+
|
| 5 |
+
# Damping Controller
|
| 6 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.DampingGains.dgain = [5. , 5. , 5. , 5. , 3. , 2.5, 1.5]
|
| 7 |
+
|
| 8 |
+
# Cartesian Position Impedance Controller
|
| 9 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.pgain = [1000.0, 1000.0, 1000.0]
|
| 10 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.dgain = [31.622776601683793, 31.622776601683793, 31.622776601683793]
|
| 11 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.pgain_null = [1.2, 1.2, 1.2, 1.2, 0.5, 0.3, 0.1]
|
| 12 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.dgain_null = [0.1 , 0.1 , 0.1 , 0.1 , 0.06, 0.05, 0.03]
|
| 13 |
+
|
| 14 |
+
# Cartesian Position and Orientation Impedance Controller
|
| 15 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatPDGains.pgain = [33.13189603604459, 187.9403713446798, 31.0064033017648, 27.69370238555632, 33.98706171459314, 180.9185531893281]
|
| 16 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatPDGains.dgain = [16.329377526884908, 7.672464154315535, 19.307738014214987, 19.369027815549774, 14.709666893271407, 13.529185054450448]
|
| 17 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatPDGains.pgain_null = [7.675519770796831, 2.676935478437176, 8.539040163444975, 1.270446361314313, 8.87752182480855, 2.186782233762969, 4.414432577659688]
|
| 18 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatPDGains.dgain_null = [0.35158682223052035,0.03532768494155088,0.9812676349486628,0.791470915462905,0.00066062504055376,0.9520533614689963,0.04623941774486632]
|
| 19 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatPDGains.dgain_velcontroller = [25.0, 25.0, 25.0, 25.0, 15.0, 12.5, 7.5]
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Config/mujoco_controller_config.gin
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Joint PD Controller
|
| 2 |
+
# ##these gains seem to be too high ...
|
| 3 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.pgain = [600.0, 600.0, 600.0, 600.0, 250.0, 150.0, 50.0]
|
| 4 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.dgain = [50.0, 50.0, 50.0, 50.0, 30.0, 25.0, 15.0]
|
| 5 |
+
|
| 6 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.pgain = [120.0, 120.0, 120.0, 120.0, 50.0, 30.0, 10.0]
|
| 7 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.dgain = [10.0, 10.0, 10.0, 10.0, 6.0, 5.0, 3.0]
|
| 8 |
+
|
| 9 |
+
# Damping Controller
|
| 10 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.DampingGains.dgain = [5. , 5. , 5. , 5. , 3. , 2.5, 1.5]
|
| 11 |
+
|
| 12 |
+
# Cartesian Position Impedance Controller
|
| 13 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.pgain = [80.0, 80.0, 80.0]
|
| 14 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.dgain = [18.973665961010276, 18.973665961010276, 18.973665961010276]
|
| 15 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.pgain_null = [1.2, 1.2, 1.2, 1.2, 0.5, 0.3, 0.1]
|
| 16 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.dgain_null = [0.1 , 0.1 , 0.1 , 0.1 , 0.06, 0.05, 0.03]
|
| 17 |
+
|
| 18 |
+
# Cartesian Position Impedance Controller
|
| 19 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.pgain_pos = [160.0, 160.0, 160.0]
|
| 20 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.pgain_null = [1.2, 1.2, 1.2, 1.2, 0.5, 0.3, 0.1]
|
| 21 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.J_reg = 1e-6
|
| 22 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.W = [1, 1, 1, 1, 1, 1, 1]
|
| 23 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.rest_posture = [0, 0.174, 0, -0.872, 0, 1.222, 0.785]
|
| 24 |
+
|
| 25 |
+
# Cartesian Position Impedance Controller And Orientation
|
| 26 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.pgain_pos = [200.0, 200.0, 800.0]
|
| 27 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.pgain_null = [40, 40, 40, 40, 40, 40, 40]
|
| 28 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.J_reg = 1e-12
|
| 29 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.W = [1, 1, 1, 1, 1, 1, 1]
|
| 30 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.rest_posture = [0, 0.174, 0, -0.872, 0, 1.222, 0.785]
|
| 31 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.pgain_quat = [30.0, 30.0, 30.0]
|
| 32 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.ddgain = [0.4, 0.4, 0.4, 0.4 ,0.4, 0.4, 0.4]
|
| 33 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.joint_filter_coefficient = 1.0
|
| 34 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.min_svd_values = 1e-2
|
| 35 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.max_svd_values = 1e2
|
| 36 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.num_iter = 3
|
| 37 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.learningRate = 0.001
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
# Cartesian Position Jacobian Transpose Impedance Controller And Orientation
|
| 41 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.pgain_pos = [450.0, 450.0, 450.0]
|
| 42 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.dgain = [25.0, 25.0, 25.0, 25.0, 25.0, 25.0]
|
| 43 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.pgain_null = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
|
| 44 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.dgain_null = [0.25 , 0.25 , 0.25 , 0.25 , 0.25, 0.25, 0.25]
|
| 45 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.J_reg = 1e-3
|
| 46 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.W = [1, 1, 1, 10, 1, 1, 1]
|
| 47 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.rest_posture = [0, 0.174, 0, -0.872, 0, 1.222, 0.785]
|
| 48 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatJacTransposeControllerConfig.pgain_quat = [250.0, 250.0, 250.0]
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Config/pybullet_controller_config.gin
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Joint PD Controller
|
| 2 |
+
|
| 3 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.pgain = [1200.0, 1200.0, 1200.0, 1200.0, 1200.0, 300.0, 100.0]
|
| 4 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.JointPDGains.dgain = [25.0, 25.0, 25.0, 25.0, 25.0, 15.0, 10.0]
|
| 5 |
+
|
| 6 |
+
# Damping Controller
|
| 7 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.DampingGains.dgain = [5. , 5. , 5. , 5. , 3. , 2.5, 1.5]
|
| 8 |
+
|
| 9 |
+
# Cartesian Position Impedance Controller
|
| 10 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.pgain = [80.0, 80.0, 80.0]
|
| 11 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.dgain = [18.973665961010276, 18.973665961010276, 18.973665961010276]
|
| 12 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.pgain_null = [1.2, 1.2, 1.2, 1.2, 0.5, 0.3, 0.1]
|
| 13 |
+
#environments.d3il.d3il_sim.controllers.GainsInterface.CartPosPDGains.dgain_null = [0.1 , 0.1 , 0.1 , 0.1 , 0.06, 0.05, 0.03]
|
| 14 |
+
|
| 15 |
+
# Cartesian Position Impedance Controller
|
| 16 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.pgain_pos = [160.0, 160.0, 160.0]
|
| 17 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.pgain_null = [1.2, 1.2, 1.2, 1.2, 0.5, 0.3, 0.1]
|
| 18 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.J_reg = 1e-6
|
| 19 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.W = [1, 1, 1, 1, 1, 1, 1]
|
| 20 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosControllerConfig.rest_posture = [0, 0.174, 0, -0.872, 0, 1.222, 0.785]
|
| 21 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.ddgain = [0.4, 0.4, 0.4, 0.4 ,0.4, 0.4, 0.4]
|
| 22 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.joint_filter_coefficient = 0.99
|
| 23 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.min_svd_values = 1e-2
|
| 24 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.max_svd_values = 1e2
|
| 25 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.num_iter = 3
|
| 26 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.learningRate = 0.001
|
| 27 |
+
|
| 28 |
+
# Cartesian Position Impedance Controller And Orientation
|
| 29 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.pgain_pos = [160.0, 160.0, 160.0]
|
| 30 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.pgain_null = [1.2, 1.2, 1.2, 1.2, 0.5, 0.3, 0.1]
|
| 31 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.J_reg = 1e-6
|
| 32 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.W = [1, 1, 1, 1, 1, 1, 1]
|
| 33 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.rest_posture = [0, 0.174, 0, -0.872, 0, 1.222, 0.785]
|
| 34 |
+
environments.d3il.d3il_sim.controllers.GainsInterface.CartPosQuatControllerConfig.pgain_quat = [800.0, 800.0, 800.0]
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/Controller.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import threading
|
| 2 |
+
from abc import abstractmethod
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
import d3il.environments.d3il.d3il_sim.controllers.GainsInterface as gains
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ControllerBase:
|
| 10 |
+
|
| 11 |
+
def __init__(self):
|
| 12 |
+
self.paramsLock = threading.Lock()
|
| 13 |
+
self.last_control_timestamp = np.nan
|
| 14 |
+
self._max_duration = None
|
| 15 |
+
self._max_timesteps = None
|
| 16 |
+
self._controller_timer = None
|
| 17 |
+
|
| 18 |
+
def isFinished(self, robot):
|
| 19 |
+
|
| 20 |
+
if self._max_duration is not None:
|
| 21 |
+
return robot.time_stamp - self._controller_timer >= self._max_duration
|
| 22 |
+
|
| 23 |
+
if self._max_timesteps is not None:
|
| 24 |
+
return robot.step_count - self._controller_timer >= self._max_timesteps
|
| 25 |
+
return False
|
| 26 |
+
|
| 27 |
+
def initController(self, robot, maxDuration):
|
| 28 |
+
return
|
| 29 |
+
|
| 30 |
+
def getControl(self, robot):
|
| 31 |
+
self.last_control_timestamp = robot.time_stamp
|
| 32 |
+
return 0
|
| 33 |
+
|
| 34 |
+
def is_used(self, robot):
|
| 35 |
+
return (
|
| 36 |
+
not np.isnan(self.last_control_timestamp)
|
| 37 |
+
and robot.time_stamp - self.last_control_timestamp < 0.03
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
def setAction(self, action):
|
| 41 |
+
return 0
|
| 42 |
+
|
| 43 |
+
def run(self, robot, log=True):
|
| 44 |
+
|
| 45 |
+
while not self.isFinished(robot):
|
| 46 |
+
robot.nextStep(log)
|
| 47 |
+
|
| 48 |
+
def executeController(self, robot, maxDuration=10, block=True, log=True):
|
| 49 |
+
|
| 50 |
+
self._max_duration = maxDuration
|
| 51 |
+
self._max_timesteps = None
|
| 52 |
+
self._controller_timer = robot.time_stamp
|
| 53 |
+
|
| 54 |
+
self.initController(robot, maxDuration)
|
| 55 |
+
robot.activeController = self
|
| 56 |
+
|
| 57 |
+
if block:
|
| 58 |
+
self.run(robot, log=log)
|
| 59 |
+
|
| 60 |
+
def executeControllerTimeSteps(self, robot, timeSteps=10, block=True, log=True):
|
| 61 |
+
|
| 62 |
+
self._max_duration = None
|
| 63 |
+
self._max_timesteps = timeSteps
|
| 64 |
+
self._controller_timer = robot.step_count
|
| 65 |
+
|
| 66 |
+
self.initController(robot, timeSteps * robot.dt)
|
| 67 |
+
robot.activeController = self
|
| 68 |
+
|
| 69 |
+
if block:
|
| 70 |
+
self.run(robot, log)
|
| 71 |
+
|
| 72 |
+
@abstractmethod
|
| 73 |
+
def reset(self):
|
| 74 |
+
pass
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class TorqueController(ControllerBase):
|
| 78 |
+
|
| 79 |
+
def __init__(self):
|
| 80 |
+
ControllerBase.__init__(self)
|
| 81 |
+
self.reset()
|
| 82 |
+
|
| 83 |
+
def getControl(self, robot):
|
| 84 |
+
super(TorqueController, self).getControl(robot)
|
| 85 |
+
return self.torque
|
| 86 |
+
|
| 87 |
+
def setAction(self, action):
|
| 88 |
+
self.torque = action.copy()
|
| 89 |
+
|
| 90 |
+
def reset(self):
|
| 91 |
+
self.torque = []
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class TrackingController(ControllerBase):
|
| 95 |
+
|
| 96 |
+
def __init__(self, dimSetPoint):
|
| 97 |
+
ControllerBase.__init__(self)
|
| 98 |
+
self.dimSetPoint = dimSetPoint
|
| 99 |
+
self.tracking_error = False
|
| 100 |
+
|
| 101 |
+
def setSetPoint(self, desired_pos, desired_vel=None, desired_acc=None):
|
| 102 |
+
pass
|
| 103 |
+
|
| 104 |
+
def getCurrentPos(self, robot):
|
| 105 |
+
pass
|
| 106 |
+
|
| 107 |
+
def getDesiredPos(self, robot):
|
| 108 |
+
pass
|
| 109 |
+
|
| 110 |
+
@abstractmethod
|
| 111 |
+
def reset(self):
|
| 112 |
+
pass
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class JointPDController(TrackingController, gains.JointPDGains):
|
| 116 |
+
|
| 117 |
+
def __init__(self):
|
| 118 |
+
TrackingController.__init__(self, dimSetPoint=7)
|
| 119 |
+
gains.JointPDGains.__init__(self)
|
| 120 |
+
|
| 121 |
+
self.reset()
|
| 122 |
+
|
| 123 |
+
def reset(self):
|
| 124 |
+
self.desired_joint_pos = np.array([0, 0, 0, -1.562, 0, 1.914, 0])
|
| 125 |
+
self.desired_joint_vel = np.zeros((7,))
|
| 126 |
+
self.desired_joint_acc = np.zeros((7,))
|
| 127 |
+
|
| 128 |
+
def getControl(self, robot):
|
| 129 |
+
|
| 130 |
+
super(JointPDController, self).getControl(robot)
|
| 131 |
+
self.paramsLock.acquire()
|
| 132 |
+
qd_d = self.desired_joint_pos - robot.current_j_pos
|
| 133 |
+
vd_d = self.desired_joint_vel - robot.current_j_vel
|
| 134 |
+
|
| 135 |
+
target_j_acc = self.pgain * qd_d + self.dgain * vd_d
|
| 136 |
+
|
| 137 |
+
robot.des_joint_pos = self.desired_joint_pos.copy()
|
| 138 |
+
robot.des_joint_vel = self.desired_joint_vel.copy()
|
| 139 |
+
robot.des_joint_acc = self.desired_joint_acc.copy()
|
| 140 |
+
|
| 141 |
+
self.paramsLock.release()
|
| 142 |
+
return target_j_acc
|
| 143 |
+
|
| 144 |
+
def setSetPoint(self, desired_pos, desired_vel=None, desired_acc=None):
|
| 145 |
+
|
| 146 |
+
self.paramsLock.acquire()
|
| 147 |
+
self.desired_joint_pos = desired_pos
|
| 148 |
+
if desired_vel is not None:
|
| 149 |
+
self.desired_joint_vel = desired_vel
|
| 150 |
+
if desired_acc is not None:
|
| 151 |
+
self.desired_joint_acc = desired_acc
|
| 152 |
+
self.paramsLock.release()
|
| 153 |
+
|
| 154 |
+
def getCurrentPos(self, robot):
|
| 155 |
+
|
| 156 |
+
return robot.current_j_pos
|
| 157 |
+
|
| 158 |
+
def getDesiredPos(self, robot):
|
| 159 |
+
|
| 160 |
+
return robot.des_joint_pos
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class ModelBasedFeedforwardController(JointPDController):
|
| 164 |
+
|
| 165 |
+
def __init__(self):
|
| 166 |
+
JointPDController.__init__(self)
|
| 167 |
+
|
| 168 |
+
def getControl(self, robot):
|
| 169 |
+
|
| 170 |
+
super(ModelBasedFeedforwardController, self).getControl(robot)
|
| 171 |
+
self.paramsLock.acquire()
|
| 172 |
+
qd_d = self.desired_joint_pos - robot.current_j_pos
|
| 173 |
+
vd_d = self.desired_joint_vel - robot.current_j_vel
|
| 174 |
+
|
| 175 |
+
target_j_acc = self.pgain * qd_d + self.dgain * vd_d
|
| 176 |
+
uff = robot.get_mass_matrix(self.desired_joint_pos).dot(
|
| 177 |
+
self.desired_joint_acc
|
| 178 |
+
) + robot.get_coriolis(self.desired_joint_pos, self.desired_joint_vel)
|
| 179 |
+
|
| 180 |
+
robot.des_joint_pos = self.desired_joint_pos.copy()
|
| 181 |
+
robot.des_joint_vel = self.desired_joint_vel.copy()
|
| 182 |
+
robot.des_joint_acc = self.desired_joint_acc.copy()
|
| 183 |
+
|
| 184 |
+
self.paramsLock.release()
|
| 185 |
+
return target_j_acc + uff
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class ModelBasedFeedbackController(JointPDController):
|
| 189 |
+
|
| 190 |
+
def __init__(self):
|
| 191 |
+
JointPDController.__init__(self)
|
| 192 |
+
|
| 193 |
+
def getControl(self, robot):
|
| 194 |
+
|
| 195 |
+
super(ModelBasedFeedbackController, self).getControl(robot)
|
| 196 |
+
self.paramsLock.acquire()
|
| 197 |
+
qd_d = self.desired_joint_pos - robot.current_j_pos
|
| 198 |
+
vd_d = self.desired_joint_vel - robot.current_j_vel
|
| 199 |
+
|
| 200 |
+
target_j_acc = self.pgain * qd_d + self.dgain * vd_d + self.desired_joint_acc
|
| 201 |
+
uff = robot.get_mass_matrix(robot.current_j_pos).dot(
|
| 202 |
+
target_j_acc
|
| 203 |
+
) + robot.get_coriolis(robot.current_j_pos, robot.current_j_vel)
|
| 204 |
+
|
| 205 |
+
robot.des_joint_pos = self.desired_joint_pos.copy()
|
| 206 |
+
robot.des_joint_vel = self.desired_joint_vel.copy()
|
| 207 |
+
robot.des_joint_acc = self.desired_joint_acc.copy()
|
| 208 |
+
|
| 209 |
+
self.paramsLock.release()
|
| 210 |
+
return uff
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
class JointPositionController(JointPDController):
|
| 214 |
+
def setAction(self, action):
|
| 215 |
+
self.desired_joint_pos = action
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
class JointVelocityController(JointPDController):
|
| 219 |
+
def __init__(self):
|
| 220 |
+
JointPDController.__init__(self)
|
| 221 |
+
self.pgain = np.zeros((self.dimSetPoint,))
|
| 222 |
+
|
| 223 |
+
def setAction(self, action):
|
| 224 |
+
self.desired_joint_vel = action
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
class ZeroTorqueController(TrackingController):
|
| 228 |
+
|
| 229 |
+
def __init__(self, dimSetPoint=7):
|
| 230 |
+
TrackingController.__init__(self, dimSetPoint=dimSetPoint)
|
| 231 |
+
|
| 232 |
+
def getControl(self, robot):
|
| 233 |
+
super().getControl(robot)
|
| 234 |
+
target_j_acc = np.zeros((self.dimSetPoint,))
|
| 235 |
+
return target_j_acc
|
| 236 |
+
|
| 237 |
+
def reset(self):
|
| 238 |
+
pass
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
class DampingController(ControllerBase, gains.DampingGains):
|
| 242 |
+
|
| 243 |
+
def __init__(self):
|
| 244 |
+
ControllerBase.__init__(self)
|
| 245 |
+
gains.DampingGains.__init__(self)
|
| 246 |
+
|
| 247 |
+
def getControl(self, robot):
|
| 248 |
+
|
| 249 |
+
super(DampingController, self).getControl(robot)
|
| 250 |
+
self.paramsLock.acquire()
|
| 251 |
+
target_j_acc = -self.dgain * robot.current_j_vel
|
| 252 |
+
self.paramsLock.release()
|
| 253 |
+
return target_j_acc
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/GainsInterface.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gin
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class PDGains:
|
| 5 |
+
def __init__(self, pgain, dgain):
|
| 6 |
+
self.pgain = pgain
|
| 7 |
+
self.dgain = dgain
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@gin.configurable
|
| 11 |
+
class JointPDGains(PDGains):
|
| 12 |
+
def __init__(self, pgain, dgain):
|
| 13 |
+
super(JointPDGains, self).__init__(pgain, dgain)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@gin.configurable
|
| 17 |
+
class DampingGains(PDGains):
|
| 18 |
+
def __init__(self, dgain):
|
| 19 |
+
super(DampingGains, self).__init__(None, dgain)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@gin.configurable
|
| 23 |
+
class CartPosControllerConfig:
|
| 24 |
+
def __init__(self, pgain_pos, J_reg, W, pgain_null, rest_posture):
|
| 25 |
+
self.pgain_pos = pgain_pos
|
| 26 |
+
self.J_reg = J_reg
|
| 27 |
+
self.W = W
|
| 28 |
+
|
| 29 |
+
self.rest_posture = rest_posture
|
| 30 |
+
self.pgain_null = pgain_null
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@gin.configurable
|
| 34 |
+
class CartPosQuatControllerConfig:
|
| 35 |
+
def __init__(
|
| 36 |
+
self,
|
| 37 |
+
pgain_pos,
|
| 38 |
+
pgain_quat,
|
| 39 |
+
J_reg,
|
| 40 |
+
W,
|
| 41 |
+
pgain_null,
|
| 42 |
+
rest_posture,
|
| 43 |
+
ddgain,
|
| 44 |
+
joint_filter_coefficient,
|
| 45 |
+
min_svd_values,
|
| 46 |
+
max_svd_values,
|
| 47 |
+
num_iter,
|
| 48 |
+
learningRate,
|
| 49 |
+
):
|
| 50 |
+
|
| 51 |
+
self.pgain_pos = pgain_pos
|
| 52 |
+
self.pgain_quat = pgain_quat
|
| 53 |
+
self.J_reg = J_reg
|
| 54 |
+
self.W = W
|
| 55 |
+
|
| 56 |
+
self.rest_posture = rest_posture
|
| 57 |
+
self.pgain_null = pgain_null
|
| 58 |
+
|
| 59 |
+
self.ddgain = ddgain
|
| 60 |
+
self.joint_filter_coefficient = joint_filter_coefficient
|
| 61 |
+
self.min_svd_values = min_svd_values
|
| 62 |
+
self.max_svd_values = max_svd_values
|
| 63 |
+
|
| 64 |
+
self.num_iter = num_iter
|
| 65 |
+
self.learningRate = learningRate
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
@gin.configurable
|
| 69 |
+
class CartPosQuatJacTransposeControllerConfig:
|
| 70 |
+
def __init__(
|
| 71 |
+
self,
|
| 72 |
+
pgain_pos,
|
| 73 |
+
dgain,
|
| 74 |
+
pgain_quat,
|
| 75 |
+
J_reg,
|
| 76 |
+
W,
|
| 77 |
+
pgain_null,
|
| 78 |
+
dgain_null,
|
| 79 |
+
rest_posture,
|
| 80 |
+
):
|
| 81 |
+
|
| 82 |
+
self.pgain_pos = pgain_pos
|
| 83 |
+
self.dgain = dgain
|
| 84 |
+
self.pgain_quat = pgain_quat
|
| 85 |
+
self.dgain_null = dgain_null
|
| 86 |
+
self.J_reg = J_reg
|
| 87 |
+
self.W = W
|
| 88 |
+
|
| 89 |
+
self.rest_posture = rest_posture
|
| 90 |
+
self.pgain_null = pgain_null
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/IKControllers.py
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import abstractmethod
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
import d3il.environments.d3il.d3il_sim.controllers.GainsInterface as gains
|
| 7 |
+
import d3il.environments.d3il.d3il_sim.utils as utils
|
| 8 |
+
from d3il.environments.d3il.d3il_sim.controllers.Controller import TrackingController
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class CartPosImpedenceController(TrackingController, gains.CartPosControllerConfig):
|
| 12 |
+
|
| 13 |
+
def __init__(self):
|
| 14 |
+
|
| 15 |
+
TrackingController.__init__(self, dimSetPoint=3)
|
| 16 |
+
gains.CartPosControllerConfig.__init__(self)
|
| 17 |
+
|
| 18 |
+
self.J_reg = 1e-6
|
| 19 |
+
self.W = np.diag([1, 1, 1, 1, 1, 1, 1])
|
| 20 |
+
|
| 21 |
+
self.target_th_null = np.array(
|
| 22 |
+
[
|
| 23 |
+
3.57795216e-09,
|
| 24 |
+
1.74532920e-01,
|
| 25 |
+
3.30500960e-08,
|
| 26 |
+
-8.72664630e-01,
|
| 27 |
+
-1.14096181e-07,
|
| 28 |
+
1.22173047e00,
|
| 29 |
+
7.85398126e-01,
|
| 30 |
+
]
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
self.reset()
|
| 34 |
+
|
| 35 |
+
def reset(self):
|
| 36 |
+
self.desired_c_pos = np.array([0.624, 0, 0.55])
|
| 37 |
+
self.desired_c_vel = np.zeros((3,))
|
| 38 |
+
self.desired_c_acc = np.zeros((3,))
|
| 39 |
+
|
| 40 |
+
def getControl(self, robot):
|
| 41 |
+
|
| 42 |
+
self.paramsLock.acquire()
|
| 43 |
+
xd_d = self.desired_c_pos - robot.current_c_pos
|
| 44 |
+
target_c_acc = self.pgain * xd_d
|
| 45 |
+
|
| 46 |
+
J = robot.getJacobian()
|
| 47 |
+
J = J[:3, :]
|
| 48 |
+
Jw = J.dot(self.W)
|
| 49 |
+
|
| 50 |
+
JwJ_reg = Jw.dot(J.T) + self.J_reg * np.eye(3)
|
| 51 |
+
|
| 52 |
+
qd_null = self.pgain_null * (self.target_th_null - robot.current_j_pos)
|
| 53 |
+
|
| 54 |
+
qd_d = np.linalg.solve(JwJ_reg, target_c_acc - J.dot(qd_null))
|
| 55 |
+
qd_d = self.W.dot(J.transpose()).dot(qd_d) + qd_null
|
| 56 |
+
|
| 57 |
+
robot.des_c_pos = self.desired_c_pos
|
| 58 |
+
robot.des_c_vel = self.desired_c_vel
|
| 59 |
+
|
| 60 |
+
robot.jointTrackingController.setSetPoint(robot.current_j_pos, qd_d)
|
| 61 |
+
self.paramsLock.release()
|
| 62 |
+
|
| 63 |
+
return robot.jointTrackingController.getControl(robot)
|
| 64 |
+
|
| 65 |
+
def setGains(self, pGain, dGain):
|
| 66 |
+
|
| 67 |
+
self.paramsLock.acquire()
|
| 68 |
+
self.pgain = pGain
|
| 69 |
+
self.dgain = dGain
|
| 70 |
+
self.paramsLock.release()
|
| 71 |
+
|
| 72 |
+
def setSetPoint(self, desired_pos, desired_vel=None, desired_acc=None):
|
| 73 |
+
|
| 74 |
+
self.paramsLock.acquire()
|
| 75 |
+
self.desired_c_pos = desired_pos
|
| 76 |
+
if desired_vel is not None:
|
| 77 |
+
self.desired_c_vel = desired_vel
|
| 78 |
+
if desired_acc is not None:
|
| 79 |
+
self.desired_c_acc = desired_acc
|
| 80 |
+
self.paramsLock.release()
|
| 81 |
+
|
| 82 |
+
def getCurrentPos(self, robot):
|
| 83 |
+
|
| 84 |
+
return robot.current_c_pos
|
| 85 |
+
|
| 86 |
+
def getDesiredPos(self, robot):
|
| 87 |
+
return robot.des_c_pos
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class CartesianPositionController(CartPosImpedenceController):
|
| 91 |
+
def setAction(self, action):
|
| 92 |
+
self.desired_c_pos = action
|
| 93 |
+
|
| 94 |
+
@abstractmethod
|
| 95 |
+
def reset(self):
|
| 96 |
+
pass
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class CartPosQuatImpedenceController(
|
| 100 |
+
TrackingController, gains.CartPosQuatControllerConfig
|
| 101 |
+
):
|
| 102 |
+
|
| 103 |
+
def __init__(self):
|
| 104 |
+
|
| 105 |
+
TrackingController.__init__(self, dimSetPoint=7)
|
| 106 |
+
gains.CartPosQuatControllerConfig.__init__(self)
|
| 107 |
+
self.W = np.diag(self.W)
|
| 108 |
+
self.dgain_null = np.sqrt(self.pgain_null) * 2
|
| 109 |
+
|
| 110 |
+
self.reset()
|
| 111 |
+
self.neglect_dynamics = False
|
| 112 |
+
|
| 113 |
+
def reset(self):
|
| 114 |
+
self.desired_c_pos = np.array([0.624, 0, 0.55])
|
| 115 |
+
self.desired_quat = np.array([0.0, 0.984, 0, 0.177])
|
| 116 |
+
self.desired_quat = self.desired_quat / np.linalg.norm(self.desired_quat)
|
| 117 |
+
self.desired_c_vel = np.zeros((3,))
|
| 118 |
+
self.desired_quat_vel = np.zeros((4,))
|
| 119 |
+
|
| 120 |
+
self.old_des_joint_vel = np.zeros((7,))
|
| 121 |
+
self.old_q = np.zeros((7,))
|
| 122 |
+
self.old_q[:] = np.nan
|
| 123 |
+
|
| 124 |
+
def getControl(self, robot):
|
| 125 |
+
|
| 126 |
+
self.paramsLock.acquire()
|
| 127 |
+
super(CartPosQuatImpedenceController, self).getControl(robot)
|
| 128 |
+
|
| 129 |
+
if any(np.isnan(self.old_q)):
|
| 130 |
+
self.old_q = robot.current_j_pos.copy()
|
| 131 |
+
|
| 132 |
+
q = self.old_q.copy()
|
| 133 |
+
|
| 134 |
+
q = (
|
| 135 |
+
self.joint_filter_coefficient * q
|
| 136 |
+
+ (1 - self.joint_filter_coefficient) * robot.current_j_pos
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
qd_dsum = np.zeros(q.shape)
|
| 140 |
+
|
| 141 |
+
oldErrorNorm = np.inf
|
| 142 |
+
qd_d = np.zeros(q.shape)
|
| 143 |
+
|
| 144 |
+
[current_c_pos, current_c_quat] = robot.getForwardKinematics(q)
|
| 145 |
+
target_cpos_acc = self.desired_c_pos - current_c_pos
|
| 146 |
+
|
| 147 |
+
curr_quat = current_c_quat
|
| 148 |
+
|
| 149 |
+
if np.linalg.norm(curr_quat - self.desired_quat) > np.linalg.norm(
|
| 150 |
+
curr_quat + self.desired_quat
|
| 151 |
+
):
|
| 152 |
+
curr_quat = -curr_quat
|
| 153 |
+
oldErrorNorm = np.sum(target_cpos_acc**2) + np.sum(
|
| 154 |
+
(curr_quat - self.desired_quat) ** 2
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
des_quat = self.desired_quat
|
| 158 |
+
for i in range(self.num_iter):
|
| 159 |
+
|
| 160 |
+
[current_c_pos, current_c_quat] = robot.getForwardKinematics(q)
|
| 161 |
+
target_cpos_acc = self.desired_c_pos - current_c_pos
|
| 162 |
+
|
| 163 |
+
curr_quat = current_c_quat
|
| 164 |
+
|
| 165 |
+
if np.linalg.norm(curr_quat - des_quat) > np.linalg.norm(
|
| 166 |
+
curr_quat + des_quat
|
| 167 |
+
):
|
| 168 |
+
des_quat = -des_quat
|
| 169 |
+
errNorm = np.sum(target_cpos_acc**2) + np.sum((curr_quat - des_quat) ** 2)
|
| 170 |
+
|
| 171 |
+
target_cquat = utils.get_quaternion_error(curr_quat, des_quat)
|
| 172 |
+
|
| 173 |
+
target_cpos_acc = np.clip(target_cpos_acc, -0.01, 0.01)
|
| 174 |
+
target_cquat = np.clip(target_cquat, -0.1, 0.1)
|
| 175 |
+
|
| 176 |
+
target_c_acc = np.hstack(
|
| 177 |
+
(self.pgain_pos * target_cpos_acc, self.pgain_quat * target_cquat)
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
J = robot.getJacobian(q)
|
| 181 |
+
|
| 182 |
+
Jw = J.dot(self.W)
|
| 183 |
+
|
| 184 |
+
condNumber = np.linalg.cond(Jw.dot(J.T))
|
| 185 |
+
JwJ_reg = Jw.dot(J.T) + self.J_reg * np.eye(J.shape[0])
|
| 186 |
+
|
| 187 |
+
u, s, v = np.linalg.svd(JwJ_reg, full_matrices=False)
|
| 188 |
+
s_orig = s
|
| 189 |
+
s = np.clip(s, self.min_svd_values, self.max_svd_values)
|
| 190 |
+
JwJ_reg = u @ np.diag(s) @ v
|
| 191 |
+
condNumber2 = np.linalg.cond(JwJ_reg)
|
| 192 |
+
largestSV = np.max(s_orig)
|
| 193 |
+
|
| 194 |
+
qdev_rest = np.clip(self.rest_posture - q, -0.2, 0.2)
|
| 195 |
+
|
| 196 |
+
qd_null = np.array(self.pgain_null * (qdev_rest))
|
| 197 |
+
|
| 198 |
+
margin_to_limit = 0.01
|
| 199 |
+
pgain_limit = 20
|
| 200 |
+
|
| 201 |
+
qd_null_limit = np.zeros(qd_null.shape)
|
| 202 |
+
qd_null_limit_max = pgain_limit * (
|
| 203 |
+
robot.joint_pos_max - margin_to_limit - q
|
| 204 |
+
)
|
| 205 |
+
qd_null_limit_min = pgain_limit * (
|
| 206 |
+
robot.joint_pos_min + margin_to_limit - q
|
| 207 |
+
)
|
| 208 |
+
qd_null_limit[
|
| 209 |
+
q > robot.joint_pos_max - margin_to_limit
|
| 210 |
+
] += qd_null_limit_max[q > robot.joint_pos_max - margin_to_limit]
|
| 211 |
+
qd_null_limit[
|
| 212 |
+
q < robot.joint_pos_min + margin_to_limit
|
| 213 |
+
] += qd_null_limit_min[q < robot.joint_pos_min + margin_to_limit]
|
| 214 |
+
|
| 215 |
+
qd_d = np.linalg.solve(JwJ_reg, target_c_acc - J.dot(qd_null))
|
| 216 |
+
qd_d = self.W.dot(J.transpose()).dot(qd_d) + qd_null
|
| 217 |
+
|
| 218 |
+
if np.linalg.norm(qd_d) > 3:
|
| 219 |
+
qd_d = qd_d * 3 / np.linalg.norm(qd_d)
|
| 220 |
+
|
| 221 |
+
qd_dsum = qd_dsum + qd_d
|
| 222 |
+
|
| 223 |
+
q = q + self.learningRate * qd_d
|
| 224 |
+
q = np.clip(q, robot.joint_pos_min, robot.joint_pos_max)
|
| 225 |
+
|
| 226 |
+
self.tracking_error = np.sum(np.abs(self.desired_c_pos - current_c_pos)) > 0.01
|
| 227 |
+
|
| 228 |
+
qd_dsum = (q - self.old_q) / robot.dt
|
| 229 |
+
des_acc = self.ddgain * (qd_dsum - self.old_des_joint_vel) / robot.dt
|
| 230 |
+
|
| 231 |
+
if np.sum(np.abs(self.desired_c_pos - current_c_pos)) > 0.1:
|
| 232 |
+
target_cquat = utils.get_quaternion_error(curr_quat, self.desired_quat)
|
| 233 |
+
|
| 234 |
+
logging.getLogger(__name__).debug(
|
| 235 |
+
"i: %d, Time: %f, Pos_error: %f, Quat_error: %f, qd_d: %f"
|
| 236 |
+
% (
|
| 237 |
+
i,
|
| 238 |
+
robot.time_stamp,
|
| 239 |
+
np.linalg.norm(self.desired_c_pos - current_c_pos),
|
| 240 |
+
np.linalg.norm(target_cquat),
|
| 241 |
+
np.linalg.norm(qd_d),
|
| 242 |
+
),
|
| 243 |
+
qd_d,
|
| 244 |
+
self.desired_c_pos,
|
| 245 |
+
des_acc,
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
if np.linalg.norm(des_acc) > 10000:
|
| 249 |
+
des_acc = des_acc * 10000 / np.linalg.norm(des_acc)
|
| 250 |
+
|
| 251 |
+
robot.jointTrackingController.setSetPoint(q, qd_dsum, des_acc)
|
| 252 |
+
|
| 253 |
+
self.old_q = q.copy()
|
| 254 |
+
self.old_des_joint_vel = qd_dsum
|
| 255 |
+
|
| 256 |
+
robot.des_c_pos = self.desired_c_pos
|
| 257 |
+
robot.des_c_vel = self.desired_c_vel
|
| 258 |
+
robot.des_quat = self.desired_quat
|
| 259 |
+
robot.des_quat_vel = self.desired_quat_vel
|
| 260 |
+
robot.misc_data = np.array([errNorm, condNumber, condNumber2, largestSV])
|
| 261 |
+
|
| 262 |
+
self.paramsLock.release()
|
| 263 |
+
|
| 264 |
+
if self.neglect_dynamics:
|
| 265 |
+
robot.beam_to_joint_pos(q, resetDesired=False)
|
| 266 |
+
return np.zeros((7,))
|
| 267 |
+
else:
|
| 268 |
+
control = robot.jointTrackingController.getControl(robot)
|
| 269 |
+
|
| 270 |
+
return control
|
| 271 |
+
|
| 272 |
+
def setSetPoint(self, desired_pos, desired_vel=None, desired_acc=None):
|
| 273 |
+
|
| 274 |
+
self.paramsLock.acquire()
|
| 275 |
+
self.desired_c_pos = desired_pos[:3].copy()
|
| 276 |
+
self.desired_quat = desired_pos[3:] / np.linalg.norm(desired_pos[3:])
|
| 277 |
+
if desired_vel is not None:
|
| 278 |
+
self.desired_c_vel = desired_vel[:3]
|
| 279 |
+
self.desired_quat_vel = desired_vel[3:]
|
| 280 |
+
|
| 281 |
+
self.paramsLock.release()
|
| 282 |
+
|
| 283 |
+
def getCurrentPos(self, robot):
|
| 284 |
+
|
| 285 |
+
return np.hstack((robot.current_c_pos, robot.current_c_quat))
|
| 286 |
+
|
| 287 |
+
def getDesiredPos(self, robot):
|
| 288 |
+
return np.concatenate((robot.des_c_pos, robot.des_quat))
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
class CartVelocityImpedenceController(CartPosQuatImpedenceController):
|
| 292 |
+
|
| 293 |
+
def __init__(self, fixed_orientation=None, max_cart_vel=0.5):
|
| 294 |
+
|
| 295 |
+
super(CartVelocityImpedenceController, self).__init__()
|
| 296 |
+
self.fixed_orientation = fixed_orientation
|
| 297 |
+
self.max_cart_vel = max_cart_vel
|
| 298 |
+
|
| 299 |
+
self.max_cart_pos = np.array([0.7, 0.4, 0.75])
|
| 300 |
+
self.min_cart_pos = np.array([0.2, -0.4, 0.0])
|
| 301 |
+
|
| 302 |
+
def getControl(self, robot):
|
| 303 |
+
|
| 304 |
+
self.desired_c_pos = np.array(robot.current_c_pos) + robot.dt * np.array(
|
| 305 |
+
self.desired_c_vel
|
| 306 |
+
)
|
| 307 |
+
self.desired_c_pos = np.clip(
|
| 308 |
+
self.desired_c_pos, self.min_cart_pos, self.max_cart_pos
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
if self.fixed_orientation is None:
|
| 312 |
+
self.desired_quat = self.fixed_orientation
|
| 313 |
+
else:
|
| 314 |
+
self.desired_quat = self.fixed_orientation
|
| 315 |
+
|
| 316 |
+
return super(CartVelocityImpedenceController, self).getControl(robot)
|
| 317 |
+
|
| 318 |
+
def setSetPoint(self, desired_pos, desired_vel, desired_acc=None):
|
| 319 |
+
|
| 320 |
+
self.paramsLock.acquire()
|
| 321 |
+
self.desired_c_vel = desired_vel[:3]
|
| 322 |
+
if self.fixed_orientation is None:
|
| 323 |
+
self.desired_quat_vel = desired_vel[3:]
|
| 324 |
+
|
| 325 |
+
self.paramsLock.release()
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
class CartPosQuatImpedenceJacTransposeController(
|
| 329 |
+
CartPosQuatImpedenceController, gains.CartPosQuatJacTransposeControllerConfig
|
| 330 |
+
):
|
| 331 |
+
def __init__(self):
|
| 332 |
+
super(CartPosQuatImpedenceJacTransposeController, self).__init__()
|
| 333 |
+
gains.CartPosQuatJacTransposeControllerConfig.__init__(self)
|
| 334 |
+
|
| 335 |
+
def getControl(self, robot):
|
| 336 |
+
self.paramsLock.acquire()
|
| 337 |
+
qd_d = self.desired_c_pos - robot.current_c_pos
|
| 338 |
+
target_cpos_acc = self.pgain_pos * qd_d
|
| 339 |
+
curr_quat = (
|
| 340 |
+
-robot.current_c_quat
|
| 341 |
+
if (robot.current_c_quat @ self.desired_quat) < 0
|
| 342 |
+
else robot.current_c_quat
|
| 343 |
+
)
|
| 344 |
+
target_cquat = self.pgain_quat * utils.get_quaternion_error(
|
| 345 |
+
robot.current_c_quat, self.desired_quat
|
| 346 |
+
)
|
| 347 |
+
J = robot.getJacobian()
|
| 348 |
+
|
| 349 |
+
qd_null = self.pgain_null * (
|
| 350 |
+
self.rest_posture - robot.current_j_pos
|
| 351 |
+
) + self.dgain_null * (-robot.current_j_vel)
|
| 352 |
+
target_acc = np.concatenate((target_cpos_acc, target_cquat))
|
| 353 |
+
qdd = J.T @ (target_acc - self.dgain * (J @ robot.current_j_vel)) + qd_null
|
| 354 |
+
robot.des_c_pos = self.desired_c_pos
|
| 355 |
+
robot.des_c_vel = self.desired_c_vel
|
| 356 |
+
robot.des_quat = self.desired_quat
|
| 357 |
+
robot.des_quat_vel = self.desired_quat_vel
|
| 358 |
+
self.paramsLock.release()
|
| 359 |
+
return qdd
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
class CartPosQuatCartesianRobotController(TrackingController):
|
| 363 |
+
def __init__(self):
|
| 364 |
+
TrackingController.__init__(self, dimSetPoint=7)
|
| 365 |
+
self.reset()
|
| 366 |
+
|
| 367 |
+
def reset(self):
|
| 368 |
+
self.desired_pos = np.zeros((self.dimSetPoint,))
|
| 369 |
+
|
| 370 |
+
def setAction(self, action):
|
| 371 |
+
self.desired_pos = action
|
| 372 |
+
|
| 373 |
+
def setSetPoint(self, desired_pos, desired_vel=None, desired_acc=None):
|
| 374 |
+
self.desired_pos = desired_pos
|
| 375 |
+
|
| 376 |
+
def getControl(self, robot):
|
| 377 |
+
robot.des_c_pos = self.desired_pos[:3]
|
| 378 |
+
robot.des_c_vel = np.zeros((3,))
|
| 379 |
+
if self.desired_pos.shape[0] > 3:
|
| 380 |
+
robot.des_quat = self.desired_pos[3:]
|
| 381 |
+
robot.des_quat_vel = np.zeros((4,))
|
| 382 |
+
|
| 383 |
+
return self.desired_pos
|
| 384 |
+
|
| 385 |
+
def getCurrentPos(self, robot):
|
| 386 |
+
return np.hstack((robot.current_c_pos, robot.current_c_quat))
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/TrajectoryTracking.py
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
from scipy.interpolate import make_interp_spline
|
| 5 |
+
|
| 6 |
+
import d3il.environments.d3il.d3il_sim.utils as utils
|
| 7 |
+
from d3il.environments.d3il.d3il_sim.controllers import ControllerBase
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class TrajectoryGenerator:
|
| 11 |
+
def __init__(self):
|
| 12 |
+
pass
|
| 13 |
+
|
| 14 |
+
def generate_trajectory(self, trackingController, robot, duration):
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
def setDesiredPos(self, desiredPos):
|
| 18 |
+
pass
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class TrajectoryTracker(ControllerBase):
|
| 22 |
+
|
| 23 |
+
def __init__(self, dt, trajectory_generator=None):
|
| 24 |
+
ControllerBase.__init__(self)
|
| 25 |
+
|
| 26 |
+
self.trajectory_generator = trajectory_generator
|
| 27 |
+
self.startingTime = None
|
| 28 |
+
self.trajectory = None
|
| 29 |
+
self.trajectoryVel = None
|
| 30 |
+
self.trajectoryAcc = None
|
| 31 |
+
self.dt = dt
|
| 32 |
+
self.additionalDuration = 0
|
| 33 |
+
self.delay_trajectory = 0
|
| 34 |
+
self.old_time_stamp = None
|
| 35 |
+
self.delay_trajectory_on_error = False
|
| 36 |
+
|
| 37 |
+
def initController(self, robot, maxDuration):
|
| 38 |
+
|
| 39 |
+
self.get_tracking_controller(robot).initController(robot, maxDuration)
|
| 40 |
+
self.startingTime = robot.time_stamp
|
| 41 |
+
self.duration = maxDuration
|
| 42 |
+
self.delay_trajectory = 0
|
| 43 |
+
self.old_time_stamp = robot.time_stamp
|
| 44 |
+
if self.trajectory_generator is not None:
|
| 45 |
+
self.setTrajectory(
|
| 46 |
+
self.trajectory_generator.generate_trajectory(
|
| 47 |
+
self.get_tracking_controller(robot), robot, maxDuration
|
| 48 |
+
)
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
self.set_point_for_time_step(robot, 0)
|
| 52 |
+
|
| 53 |
+
def get_tracking_controller(self, robot):
|
| 54 |
+
pass
|
| 55 |
+
|
| 56 |
+
def setAction(self, action):
|
| 57 |
+
if action.ndim == 1:
|
| 58 |
+
self.setDesiredPos(action)
|
| 59 |
+
else:
|
| 60 |
+
self.setTrajectory(action)
|
| 61 |
+
|
| 62 |
+
def setDesiredPos(self, desiredPosition):
|
| 63 |
+
self.trajectory_generator.setDesiredPos(desiredPosition)
|
| 64 |
+
|
| 65 |
+
def set_point_for_time_step(self, robot, timeStep):
|
| 66 |
+
desired_pos = self.trajectory[timeStep, :]
|
| 67 |
+
|
| 68 |
+
if timeStep < self.trajectory.shape[0] - 1:
|
| 69 |
+
desired_vel = self.trajectoryVel[timeStep, :]
|
| 70 |
+
else:
|
| 71 |
+
desired_vel = np.zeros((self.trajectory.shape[1],))
|
| 72 |
+
|
| 73 |
+
if timeStep < self.trajectory.shape[0] - 2:
|
| 74 |
+
desired_acc = self.trajectoryAcc[timeStep, :]
|
| 75 |
+
else:
|
| 76 |
+
desired_acc = np.zeros((self.trajectory.shape[1],))
|
| 77 |
+
|
| 78 |
+
self.get_tracking_controller(robot).setSetPoint(
|
| 79 |
+
desired_pos, desired_vel, desired_acc
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
def getControl(self, robot):
|
| 83 |
+
if self.trajectory is None:
|
| 84 |
+
logging.getLogger(__name__).warning("Error: Trajectory is empty")
|
| 85 |
+
|
| 86 |
+
self.paramsLock.acquire()
|
| 87 |
+
|
| 88 |
+
if (
|
| 89 |
+
self.delay_trajectory_on_error
|
| 90 |
+
and self.get_tracking_controller(robot).tracking_error
|
| 91 |
+
):
|
| 92 |
+
self.delay_trajectory += robot.time_stamp - self.old_time_stamp
|
| 93 |
+
self.old_time_stamp = robot.time_stamp
|
| 94 |
+
|
| 95 |
+
timeStep = np.round(
|
| 96 |
+
(robot.time_stamp - self.startingTime - self.delay_trajectory) / self.dt
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
timeStep = int(np.min([timeStep, self.trajectory.shape[0] - 1]))
|
| 100 |
+
|
| 101 |
+
self.set_point_for_time_step(robot, timeStep)
|
| 102 |
+
|
| 103 |
+
self.paramsLock.release()
|
| 104 |
+
|
| 105 |
+
return self.get_tracking_controller(robot).getControl(robot)
|
| 106 |
+
|
| 107 |
+
def setTrajectory(self, trajectory):
|
| 108 |
+
|
| 109 |
+
self.paramsLock.acquire()
|
| 110 |
+
|
| 111 |
+
self.trajectory = trajectory
|
| 112 |
+
self.trajectoryVel = np.diff(trajectory, 1, axis=0) / self.dt
|
| 113 |
+
self.trajectoryAcc = np.diff(trajectory, 2, axis=0) / (self.dt**2)
|
| 114 |
+
|
| 115 |
+
self.paramsLock.release()
|
| 116 |
+
|
| 117 |
+
def resetTrajectory(self):
|
| 118 |
+
|
| 119 |
+
self.trajectory = None
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class JointTrajectoryTracker(TrajectoryTracker):
|
| 123 |
+
|
| 124 |
+
def __init__(self, dt, trajectory_generator=None):
|
| 125 |
+
TrajectoryTracker.__init__(self, dt, trajectory_generator)
|
| 126 |
+
|
| 127 |
+
def get_tracking_controller(self, robot):
|
| 128 |
+
return robot.jointTrackingController
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
class CartPosTrajectoryTracker(TrajectoryTracker):
|
| 132 |
+
|
| 133 |
+
def __init__(self, dt, trajectory_generator=None):
|
| 134 |
+
TrajectoryTracker.__init__(self, dt, trajectory_generator)
|
| 135 |
+
|
| 136 |
+
def get_tracking_controller(self, robot):
|
| 137 |
+
return robot.cartesianPosTrackingController
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class CartPosQuatTrajectoryTracker(TrajectoryTracker):
|
| 141 |
+
|
| 142 |
+
def __init__(self, dt, trajectory_generator=None):
|
| 143 |
+
TrajectoryTracker.__init__(self, dt, trajectory_generator)
|
| 144 |
+
|
| 145 |
+
def get_tracking_controller(self, robot):
|
| 146 |
+
return robot.cartesianPosQuatTrackingController
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
class GotoTrajectoryBase(TrajectoryGenerator):
|
| 150 |
+
def __init__(self):
|
| 151 |
+
|
| 152 |
+
TrajectoryGenerator.__init__(self)
|
| 153 |
+
|
| 154 |
+
self.desiredPosition = np.array([0, 0, 0, -1.562, 0, 1.914, 0])
|
| 155 |
+
|
| 156 |
+
def get_init_pos(self, trackingController, robot):
|
| 157 |
+
|
| 158 |
+
cur_state = trackingController.getCurrentPos(robot)
|
| 159 |
+
des_state = trackingController.getDesiredPos(robot)
|
| 160 |
+
if (
|
| 161 |
+
robot.smooth_spline
|
| 162 |
+
and not any(np.isnan(des_state))
|
| 163 |
+
and not trackingController.tracking_error
|
| 164 |
+
and trackingController.is_used(robot)
|
| 165 |
+
):
|
| 166 |
+
cur_state = des_state
|
| 167 |
+
return cur_state
|
| 168 |
+
|
| 169 |
+
def setDesiredPos(self, desiredPosition):
|
| 170 |
+
|
| 171 |
+
self.desiredPosition = desiredPosition
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class GotoTrajectorySpline(GotoTrajectoryBase):
|
| 175 |
+
|
| 176 |
+
def __init__(self):
|
| 177 |
+
|
| 178 |
+
GotoTrajectoryBase.__init__(self)
|
| 179 |
+
|
| 180 |
+
def generate_trajectory(self, trackingController, robot, duration):
|
| 181 |
+
|
| 182 |
+
super().generate_trajectory(trackingController, robot, duration)
|
| 183 |
+
|
| 184 |
+
cur_state = self.get_init_pos(trackingController, robot)
|
| 185 |
+
|
| 186 |
+
time = np.linspace(0, duration, int(duration / robot.dt) + 1)
|
| 187 |
+
trajectory = np.zeros((time.shape[0], trackingController.dimSetPoint))
|
| 188 |
+
|
| 189 |
+
for i in range(trackingController.dimSetPoint):
|
| 190 |
+
try:
|
| 191 |
+
l, r = [(1, 0.0), (2, 0.0)], [(1, 0.0), (2, 0.0)]
|
| 192 |
+
bsplinef = make_interp_spline(
|
| 193 |
+
x=[0, duration],
|
| 194 |
+
y=[cur_state[i], self.desiredPosition[i]],
|
| 195 |
+
bc_type=(l, r),
|
| 196 |
+
k=5,
|
| 197 |
+
)
|
| 198 |
+
trajectory[:, i] = bsplinef(time)
|
| 199 |
+
except ValueError:
|
| 200 |
+
raise ValueError(
|
| 201 |
+
"Robot might be already be at this configuration.\nDesired Pos: {}\nCurrent Pos: {}".format(
|
| 202 |
+
self.desiredPosition[i], cur_state[i]
|
| 203 |
+
)
|
| 204 |
+
)
|
| 205 |
+
return trajectory
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
class GotoTrajectoryLinear(GotoTrajectoryBase):
|
| 209 |
+
|
| 210 |
+
def __init__(self):
|
| 211 |
+
|
| 212 |
+
GotoTrajectoryBase.__init__(self)
|
| 213 |
+
|
| 214 |
+
def generate_trajectory(self, trackingController, robot, duration):
|
| 215 |
+
|
| 216 |
+
super().generate_trajectory(trackingController, robot, duration)
|
| 217 |
+
|
| 218 |
+
cur_state = self.get_init_pos(trackingController, robot)
|
| 219 |
+
|
| 220 |
+
time = np.linspace(0, duration, int(duration / robot.dt) + 1)
|
| 221 |
+
trajectory = np.zeros((time.shape[0], trackingController.dimSetPoint))
|
| 222 |
+
|
| 223 |
+
for i in range(trackingController.dimSetPoint):
|
| 224 |
+
trajectory[:, i] = np.linspace(
|
| 225 |
+
cur_state[i], self.desiredPosition[i], int(duration / robot.dt) + 1
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
trajectory = trajectory[1:, :]
|
| 229 |
+
return trajectory
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
class OfflineIKTrajectoryGenerator(TrajectoryGenerator):
|
| 233 |
+
def __init__(self, interpolation_trajectory=None):
|
| 234 |
+
TrajectoryGenerator.__init__(self)
|
| 235 |
+
|
| 236 |
+
self.desiredTaskPosition = np.zeros(
|
| 237 |
+
7,
|
| 238 |
+
)
|
| 239 |
+
self.J_reg = 1e-6
|
| 240 |
+
self.W = np.diag([1, 1, 1, 1, 1, 1, 1])
|
| 241 |
+
self.target_th_null = np.array(
|
| 242 |
+
[
|
| 243 |
+
3.57795216e-09,
|
| 244 |
+
1.74532920e-01,
|
| 245 |
+
3.30500960e-08,
|
| 246 |
+
-8.72664630e-01,
|
| 247 |
+
-1.14096181e-07,
|
| 248 |
+
1.22173047e00,
|
| 249 |
+
7.85398126e-01,
|
| 250 |
+
]
|
| 251 |
+
)
|
| 252 |
+
if interpolation_trajectory is None:
|
| 253 |
+
self.interpolation_trajectory = GotoTrajectorySpline()
|
| 254 |
+
else:
|
| 255 |
+
self.interpolation_trajectory = interpolation_trajectory
|
| 256 |
+
|
| 257 |
+
def generate_trajectory(self, trackingController, robot, duration):
|
| 258 |
+
|
| 259 |
+
eps = 1e-5
|
| 260 |
+
IT_MAX = 1000
|
| 261 |
+
DT = 1e-3
|
| 262 |
+
|
| 263 |
+
i = 0
|
| 264 |
+
self.pgain = [
|
| 265 |
+
33.9403713446798,
|
| 266 |
+
30.9403713446798,
|
| 267 |
+
33.9403713446798,
|
| 268 |
+
27.69370238555632,
|
| 269 |
+
33.98706171459314,
|
| 270 |
+
30.9185531893281,
|
| 271 |
+
]
|
| 272 |
+
self.pgain_null = 5 * np.array(
|
| 273 |
+
[
|
| 274 |
+
7.675519770796831,
|
| 275 |
+
2.676935478437176,
|
| 276 |
+
8.539040163444975,
|
| 277 |
+
1.270446361314313,
|
| 278 |
+
8.87752182480855,
|
| 279 |
+
2.186782233762969,
|
| 280 |
+
4.414432577659688,
|
| 281 |
+
]
|
| 282 |
+
)
|
| 283 |
+
self.pgain_limit = 20
|
| 284 |
+
|
| 285 |
+
q = robot.current_j_pos.copy()
|
| 286 |
+
qd_d = np.zeros(q.shape)
|
| 287 |
+
oldErrNorm = np.inf
|
| 288 |
+
while True:
|
| 289 |
+
oldQ = q
|
| 290 |
+
q = q + DT * qd_d
|
| 291 |
+
|
| 292 |
+
q = np.clip(q, robot.joint_pos_min, robot.joint_pos_max)
|
| 293 |
+
|
| 294 |
+
cartPos, orient = robot.getForwardKinematics(q)
|
| 295 |
+
cpos_err = self.desiredTaskPosition[:3] - cartPos
|
| 296 |
+
|
| 297 |
+
if np.linalg.norm(orient - self.desiredTaskPosition[3:]) > np.linalg.norm(
|
| 298 |
+
orient + self.desiredTaskPosition[3:]
|
| 299 |
+
):
|
| 300 |
+
orient = -orient
|
| 301 |
+
|
| 302 |
+
cpos_err = np.clip(cpos_err, -0.1, 0.1)
|
| 303 |
+
cquat_err = np.clip(
|
| 304 |
+
utils.get_quaternion_error(orient, self.desiredTaskPosition[3:]),
|
| 305 |
+
-0.5,
|
| 306 |
+
0.5,
|
| 307 |
+
)
|
| 308 |
+
err = np.hstack((cpos_err, cquat_err))
|
| 309 |
+
|
| 310 |
+
errNorm = np.sum(cpos_err**2) + np.sum(
|
| 311 |
+
(orient - self.desiredTaskPosition[3:]) ** 2
|
| 312 |
+
)
|
| 313 |
+
if errNorm > oldErrNorm:
|
| 314 |
+
q = oldQ
|
| 315 |
+
DT = DT * 0.7
|
| 316 |
+
continue
|
| 317 |
+
else:
|
| 318 |
+
DT = DT * 1.025
|
| 319 |
+
|
| 320 |
+
if errNorm < eps:
|
| 321 |
+
success = True
|
| 322 |
+
break
|
| 323 |
+
if i >= IT_MAX:
|
| 324 |
+
success = False
|
| 325 |
+
break
|
| 326 |
+
|
| 327 |
+
oldErrNorm = errNorm
|
| 328 |
+
|
| 329 |
+
J = robot.getJacobian(q)
|
| 330 |
+
|
| 331 |
+
Jw = J.dot(self.W)
|
| 332 |
+
|
| 333 |
+
JwJ_reg = Jw.dot(J.T) + self.J_reg * np.eye(J.shape[0])
|
| 334 |
+
|
| 335 |
+
qd_null = self.pgain_null * (self.target_th_null - q)
|
| 336 |
+
|
| 337 |
+
margin_to_limit = 0.1
|
| 338 |
+
qd_null_limit = np.zeros(qd_null.shape)
|
| 339 |
+
qd_null_limit_max = self.pgain_limit * (
|
| 340 |
+
robot.joint_pos_max - margin_to_limit - q
|
| 341 |
+
)
|
| 342 |
+
qd_null_limit_min = self.pgain_limit * (
|
| 343 |
+
robot.joint_pos_min + margin_to_limit - q
|
| 344 |
+
)
|
| 345 |
+
qd_null_limit[
|
| 346 |
+
q > robot.joint_pos_max - margin_to_limit
|
| 347 |
+
] += qd_null_limit_max[q > robot.joint_pos_max - margin_to_limit]
|
| 348 |
+
qd_null_limit[
|
| 349 |
+
q < robot.joint_pos_min + margin_to_limit
|
| 350 |
+
] += qd_null_limit_min[q < robot.joint_pos_min + margin_to_limit]
|
| 351 |
+
|
| 352 |
+
qd_null += qd_null_limit
|
| 353 |
+
qd_d = np.linalg.solve(JwJ_reg, self.pgain * err - J.dot(qd_null))
|
| 354 |
+
qd_d = self.W.dot(J.transpose()).dot(qd_d) + qd_null
|
| 355 |
+
|
| 356 |
+
i += 1
|
| 357 |
+
|
| 358 |
+
print("Final IK error (%d iterations): %s" % (i, errNorm))
|
| 359 |
+
|
| 360 |
+
logging.getLogger(__name__).debug(
|
| 361 |
+
"Final IK error (%d iterations): %s" % (i, errNorm)
|
| 362 |
+
)
|
| 363 |
+
self.interpolation_trajectory.setDesiredPos(q)
|
| 364 |
+
return self.interpolation_trajectory.generate_trajectory(
|
| 365 |
+
trackingController, robot, duration
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
def setDesiredPos(self, desiredTaskPosition):
|
| 369 |
+
|
| 370 |
+
self.desiredTaskPosition = desiredTaskPosition
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
class GotoCartPosQuatOfflineIKController(JointTrajectoryTracker):
|
| 374 |
+
def __init__(self, dt):
|
| 375 |
+
super(JointTrajectoryTracker, self).__init__(
|
| 376 |
+
dt, trajectory_generator=OfflineIKTrajectoryGenerator()
|
| 377 |
+
)
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
class GotoJointController(JointTrajectoryTracker):
|
| 381 |
+
def __init__(self, dt):
|
| 382 |
+
super(JointTrajectoryTracker, self).__init__(
|
| 383 |
+
dt, trajectory_generator=GotoTrajectorySpline()
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
class GotoCartPosImpedanceController(CartPosTrajectoryTracker):
|
| 388 |
+
def __init__(self, dt):
|
| 389 |
+
super(CartPosTrajectoryTracker, self).__init__(
|
| 390 |
+
dt, trajectory_generator=GotoTrajectorySpline()
|
| 391 |
+
)
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
class GotoCartPosQuatImpedanceController(CartPosQuatTrajectoryTracker):
|
| 395 |
+
def __init__(self, dt):
|
| 396 |
+
super(CartPosQuatTrajectoryTracker, self).__init__(
|
| 397 |
+
dt, trajectory_generator=GotoTrajectorySpline()
|
| 398 |
+
)
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
class GotoCartPosCartesianRobotController(JointTrajectoryTracker):
|
| 402 |
+
|
| 403 |
+
def __init__(self, dt):
|
| 404 |
+
assert False
|
| 405 |
+
super(JointTrajectoryTracker, self).__init__(
|
| 406 |
+
dt, trajectory_generator=GotoTrajectory()
|
| 407 |
+
)
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
class GotoCartPosQuatCartesianRobotController(JointTrajectoryTracker):
|
| 411 |
+
|
| 412 |
+
def __init__(self, dt):
|
| 413 |
+
assert False
|
| 414 |
+
super(JointTrajectoryTracker, self).__init__(
|
| 415 |
+
dt, trajectory_generator=GotoTrajectory()
|
| 416 |
+
)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .Controller import *
|
| 2 |
+
from .IKControllers import *
|
| 3 |
+
from .TrajectoryTracking import *
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/controllers/executor.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def execute_controller(
|
| 5 |
+
robots: list,
|
| 6 |
+
controllers: list,
|
| 7 |
+
max_duration: float = 4.0,
|
| 8 |
+
):
|
| 9 |
+
start_times = [rb.time_stamp for rb in robots]
|
| 10 |
+
robot_ctrl_matches = list(zip(robots, controllers, start_times))
|
| 11 |
+
|
| 12 |
+
for rb, ctrl, _ in robot_ctrl_matches:
|
| 13 |
+
ctrl.initController(rb, max_duration)
|
| 14 |
+
|
| 15 |
+
while (
|
| 16 |
+
not np.all([ctrl.isFinished(rb) for rb, ctrl, _ in robot_ctrl_matches])
|
| 17 |
+
) and (
|
| 18 |
+
np.all([rb.time_stamp - st < max_duration for rb, _, st in robot_ctrl_matches])
|
| 19 |
+
):
|
| 20 |
+
for rb, ctrl, _ in robot_ctrl_matches:
|
| 21 |
+
ctrl_action = ctrl.getControl(rb)
|
| 22 |
+
rb.command = ctrl_action
|
| 23 |
+
|
| 24 |
+
robots[0].nextStep()
|
| 25 |
+
|
| 26 |
+
for rb in robots:
|
| 27 |
+
rb.hold_joint_position()
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def execute_controller_timesteps(
|
| 31 |
+
robots: list,
|
| 32 |
+
controllers: list,
|
| 33 |
+
time_steps: int = 10,
|
| 34 |
+
):
|
| 35 |
+
robot_ctrl_matches = list(zip(robots, controllers))
|
| 36 |
+
|
| 37 |
+
for rb, ctrl in robot_ctrl_matches:
|
| 38 |
+
ctrl.initController(rb, time_steps * rb.dt)
|
| 39 |
+
|
| 40 |
+
for i in range(time_steps):
|
| 41 |
+
for rb, ctrl in robot_ctrl_matches:
|
| 42 |
+
ctrl_action = ctrl.getControl(rb)
|
| 43 |
+
rb.command = ctrl_action
|
| 44 |
+
|
| 45 |
+
robots[0].nextStep()
|
| 46 |
+
|
| 47 |
+
for rb in robots:
|
| 48 |
+
rb.hold_joint_position()
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Camera.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
from typing import Optional, Tuple
|
| 3 |
+
|
| 4 |
+
import cv2
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
from d3il.environments.d3il.d3il_sim.core.sim_object.sim_object import (
|
| 8 |
+
IntelligentSimObject,
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Camera(IntelligentSimObject, ABC):
|
| 13 |
+
|
| 14 |
+
def __init__(
|
| 15 |
+
self,
|
| 16 |
+
name: str,
|
| 17 |
+
width: int = 1000,
|
| 18 |
+
height: int = 1000,
|
| 19 |
+
init_pos=None,
|
| 20 |
+
init_quat=None,
|
| 21 |
+
near: float = 0.01,
|
| 22 |
+
far: float = 10,
|
| 23 |
+
fovy: int = 45,
|
| 24 |
+
*args,
|
| 25 |
+
**kwargs
|
| 26 |
+
):
|
| 27 |
+
|
| 28 |
+
if init_pos is None:
|
| 29 |
+
init_pos = [0, 0, 0]
|
| 30 |
+
if init_quat is None:
|
| 31 |
+
init_quat = [0, 1, 0, 0]
|
| 32 |
+
|
| 33 |
+
super(Camera, self).__init__(name, init_pos, init_quat)
|
| 34 |
+
self.width = width
|
| 35 |
+
self.height = height
|
| 36 |
+
|
| 37 |
+
self.near = near
|
| 38 |
+
self.far = far
|
| 39 |
+
self.fovy = fovy
|
| 40 |
+
self.fovx = (
|
| 41 |
+
2
|
| 42 |
+
* np.arctan(
|
| 43 |
+
self.width
|
| 44 |
+
* 0.5
|
| 45 |
+
/ (self.height * 0.5 / np.tan(self.fovy * np.pi / 360 / 2))
|
| 46 |
+
)
|
| 47 |
+
/ np.pi
|
| 48 |
+
* 360
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
self.fx = (self.width / 2) / (np.tan(self.fovx * np.pi / 180 / 2))
|
| 52 |
+
self.fy = (self.height / 2) / (np.tan(self.fovy * np.pi / 180 / 2))
|
| 53 |
+
self.cx = self.width / 2
|
| 54 |
+
self.cy = self.height / 2
|
| 55 |
+
|
| 56 |
+
self.intrinsics = np.array(
|
| 57 |
+
[[self.fx, 0, self.cx], [0, self.fy, self.cy], [0, 0, 1]]
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
def set_cam_params(
|
| 61 |
+
self,
|
| 62 |
+
width: Optional[int] = None,
|
| 63 |
+
height: Optional[int] = None,
|
| 64 |
+
near: Optional[float] = None,
|
| 65 |
+
far: Optional[float] = None,
|
| 66 |
+
fovy: Optional[int] = None,
|
| 67 |
+
):
|
| 68 |
+
|
| 69 |
+
self.width = width or self.width
|
| 70 |
+
self.height = height or self.height
|
| 71 |
+
|
| 72 |
+
self.near = near or self.near
|
| 73 |
+
self.far = far or self.far
|
| 74 |
+
self.fovy = fovy or self.fovy
|
| 75 |
+
self.fovx = (
|
| 76 |
+
2
|
| 77 |
+
* np.arctan(
|
| 78 |
+
self.width
|
| 79 |
+
* 0.5
|
| 80 |
+
/ (self.height * 0.5 / np.tan(self.fovy * np.pi / 360 / 2))
|
| 81 |
+
)
|
| 82 |
+
/ np.pi
|
| 83 |
+
* 360
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
self.fx = (self.width / 2) / (np.tan(self.fovx * np.pi / 180 / 2))
|
| 87 |
+
self.fy = (self.height / 2) / (np.tan(self.fovy * np.pi / 180 / 2))
|
| 88 |
+
self.cx = self.width / 2
|
| 89 |
+
self.cy = self.height / 2
|
| 90 |
+
|
| 91 |
+
self.intrinsics = np.array(
|
| 92 |
+
[[self.fx, 0, self.cx], [0, self.fy, self.cy], [0, 0, 1]]
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
def get_segmentation(
|
| 96 |
+
self, width: int = None, height: int = None, depth: bool = True
|
| 97 |
+
) -> np.ndarray:
|
| 98 |
+
|
| 99 |
+
return self._get_img_data(
|
| 100 |
+
width=width, height=height, depth=depth, segmentation=True
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
def get_image(
|
| 104 |
+
self,
|
| 105 |
+
width: int = None,
|
| 106 |
+
height: int = None,
|
| 107 |
+
depth: bool = True,
|
| 108 |
+
denormalize_depth: bool = True,
|
| 109 |
+
) -> np.ndarray:
|
| 110 |
+
|
| 111 |
+
return self._get_img_data(width, height, depth, denormalize_depth, False)
|
| 112 |
+
|
| 113 |
+
def calc_point_cloud(
|
| 114 |
+
self, width: int = None, height: int = None, denormalize: bool = True
|
| 115 |
+
) -> Tuple[np.ndarray, np.ndarray]:
|
| 116 |
+
|
| 117 |
+
rgb_img, depth_img = self.get_image(
|
| 118 |
+
width, height, denormalize_depth=denormalize
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
return self.calc_point_cloud_from_images(rgb_img=rgb_img, depth_img=depth_img)
|
| 122 |
+
|
| 123 |
+
def calc_point_cloud_from_images(
|
| 124 |
+
self, rgb_img: np.ndarray, depth_img: np.ndarray
|
| 125 |
+
) -> Tuple[np.ndarray, np.ndarray]:
|
| 126 |
+
|
| 127 |
+
true_width = rgb_img.shape[1]
|
| 128 |
+
true_height = rgb_img.shape[0]
|
| 129 |
+
if self.height == true_height and self.width == true_width:
|
| 130 |
+
fx = self.fx
|
| 131 |
+
fy = self.fy
|
| 132 |
+
cx = self.cx
|
| 133 |
+
cy = self.cy
|
| 134 |
+
else:
|
| 135 |
+
fx = (true_width / 2) / (np.tan(self.fovx * np.pi / 180 / 2))
|
| 136 |
+
fy = (true_height / 2) / (np.tan(self.fovy * np.pi / 180 / 2))
|
| 137 |
+
cx = true_width / 2
|
| 138 |
+
cy = true_height / 2
|
| 139 |
+
|
| 140 |
+
z = depth_img
|
| 141 |
+
u = np.arange(true_width) - cx
|
| 142 |
+
v = np.arange(true_height) - cy
|
| 143 |
+
|
| 144 |
+
x = (z * u) / fx
|
| 145 |
+
y = (z.T * v).T / fy
|
| 146 |
+
|
| 147 |
+
points = np.stack((x, y, z), axis=-1).reshape((true_width * true_height, 3))
|
| 148 |
+
colors = rgb_img.reshape((true_width * true_height, 3)) / 255.0
|
| 149 |
+
|
| 150 |
+
valid_points = ~np.isnan(points).any(axis=1)
|
| 151 |
+
points = points[valid_points]
|
| 152 |
+
colors = colors[valid_points]
|
| 153 |
+
|
| 154 |
+
return points, colors
|
| 155 |
+
|
| 156 |
+
def denormalize_depth(self, depth_img: np.ndarray) -> np.ndarray:
|
| 157 |
+
|
| 158 |
+
z = self.near / (1 - depth_img * (1 - self.near / self.far))
|
| 159 |
+
return z
|
| 160 |
+
|
| 161 |
+
def apply_noise(self, depth_img: np.ndarray) -> np.ndarray:
|
| 162 |
+
|
| 163 |
+
z = self.denormalize_depth(depth_img)
|
| 164 |
+
|
| 165 |
+
depth_img = depth_img + (
|
| 166 |
+
0.0001 * np.power(z - 0.5, 2) + 0.0004
|
| 167 |
+
) * np.random.rand(self.height, self.width)
|
| 168 |
+
|
| 169 |
+
depth_img = ((40000 * depth_img).astype(int) / 40000.0).astype(np.float32)
|
| 170 |
+
|
| 171 |
+
z = (
|
| 172 |
+
2
|
| 173 |
+
* self.far
|
| 174 |
+
* self.near
|
| 175 |
+
/ (self.far + self.near - (self.far - self.near) * (2 * depth_img - 1))
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
z = cv2.bilateralFilter(z, 5, 0.1, 5)
|
| 179 |
+
|
| 180 |
+
return z
|
| 181 |
+
|
| 182 |
+
def get_poi(self) -> list:
|
| 183 |
+
|
| 184 |
+
return [self.name]
|
| 185 |
+
|
| 186 |
+
@abstractmethod
|
| 187 |
+
def _get_img_data(
|
| 188 |
+
self,
|
| 189 |
+
width: int = None,
|
| 190 |
+
height: int = None,
|
| 191 |
+
depth: bool = True,
|
| 192 |
+
denormalize_depth: bool = True,
|
| 193 |
+
segmentation: bool = False,
|
| 194 |
+
) -> np.ndarray:
|
| 195 |
+
|
| 196 |
+
pass
|
| 197 |
+
|
| 198 |
+
@abstractmethod
|
| 199 |
+
def get_cart_pos_quat(self) -> Tuple[np.ndarray, np.ndarray]:
|
| 200 |
+
|
| 201 |
+
pass
|
| 202 |
+
|
| 203 |
+
@property
|
| 204 |
+
def fov(self):
|
| 205 |
+
return self.fovy
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Model.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pinocchio
|
| 3 |
+
|
| 4 |
+
from d3il.environments.d3il.d3il_sim.utils.sim_path import d3il_path
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class RobotKinematicsInterface:
|
| 8 |
+
def getForwardKinematics(self, q=None):
|
| 9 |
+
pass
|
| 10 |
+
|
| 11 |
+
def getJacobian(self, q=None):
|
| 12 |
+
pass
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class RobotDynamicsInterface:
|
| 16 |
+
def get_gravity(self, q=None):
|
| 17 |
+
pass
|
| 18 |
+
|
| 19 |
+
def get_coriolis(self, q=None, qd=None):
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
def get_mass_matrix(self, q=None):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class RobotModelFromPinochio(RobotKinematicsInterface, RobotDynamicsInterface):
|
| 27 |
+
def __init__(self, obj_urdf="./models/common/robots/panda_arm_hand_pinocchio.urdf"):
|
| 28 |
+
obj_urdf = d3il_path(obj_urdf)
|
| 29 |
+
self.pin_model = pinocchio.buildModelFromUrdf(obj_urdf)
|
| 30 |
+
self.pin_data = self.pin_model.createData()
|
| 31 |
+
|
| 32 |
+
self.pin_end_effector_frame_id = self.pin_model.getFrameId("panda_grasptarget")
|
| 33 |
+
|
| 34 |
+
self.pin_q = np.zeros(self.pin_model.nv)
|
| 35 |
+
self.pin_qd = np.zeros(self.pin_model.nv)
|
| 36 |
+
|
| 37 |
+
def getForwardKinematics(self, q):
|
| 38 |
+
|
| 39 |
+
self.pin_q[:7] = q
|
| 40 |
+
pinocchio.framesForwardKinematics(self.pin_model, self.pin_data, self.pin_q)
|
| 41 |
+
|
| 42 |
+
current_c_pos = np.array(
|
| 43 |
+
self.pin_data.oMf[self.pin_end_effector_frame_id].translation
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
quat_pin = pinocchio.Quaternion(
|
| 47 |
+
self.pin_data.oMf[self.pin_end_effector_frame_id].rotation
|
| 48 |
+
).coeffs()
|
| 49 |
+
current_c_quat = np.zeros(4)
|
| 50 |
+
current_c_quat[1:] = quat_pin[:3]
|
| 51 |
+
current_c_quat[0] = quat_pin[-1]
|
| 52 |
+
|
| 53 |
+
return current_c_pos, current_c_quat
|
| 54 |
+
|
| 55 |
+
def getJacobian(self, q):
|
| 56 |
+
self.pin_q[:7] = q
|
| 57 |
+
|
| 58 |
+
pinocchio.computeJointJacobians(self.pin_model, self.pin_data, self.pin_q)
|
| 59 |
+
pinocchio.framesForwardKinematics(self.pin_model, self.pin_data, self.pin_q)
|
| 60 |
+
return pinocchio.getFrameJacobian(
|
| 61 |
+
self.pin_model,
|
| 62 |
+
self.pin_data,
|
| 63 |
+
self.pin_end_effector_frame_id,
|
| 64 |
+
pinocchio.LOCAL_WORLD_ALIGNED,
|
| 65 |
+
)[:, :7]
|
| 66 |
+
|
| 67 |
+
def get_gravity(self, q=None):
|
| 68 |
+
self.pin_q[:7] = q
|
| 69 |
+
|
| 70 |
+
vq = np.zeros(self.pin_q.shape)
|
| 71 |
+
aq0 = np.zeros(self.pin_q.shape)
|
| 72 |
+
|
| 73 |
+
b = pinocchio.rnea(self.pin_model, self.pin_data, self.pin_q, vq, aq0)
|
| 74 |
+
return b[:7]
|
| 75 |
+
|
| 76 |
+
def get_coriolis(self, q, qd):
|
| 77 |
+
self.pin_q[:7] = q
|
| 78 |
+
self.pin_qd[:7] = qd
|
| 79 |
+
|
| 80 |
+
aq0 = np.zeros(self.pin_q.shape)
|
| 81 |
+
|
| 82 |
+
b = pinocchio.rnea(self.pin_model, self.pin_data, self.pin_q, self.pin_qd, aq0)
|
| 83 |
+
return b[:7] - self.get_gravity(q)
|
| 84 |
+
|
| 85 |
+
def get_mass_matrix(self, q):
|
| 86 |
+
if q.shape[0] == 7:
|
| 87 |
+
q_tmp = q.copy()
|
| 88 |
+
q = np.zeros(9)
|
| 89 |
+
q[:7] = q_tmp
|
| 90 |
+
M = pinocchio.crba(self.pin_model, self.pin_data, q)
|
| 91 |
+
return M[:7, :7]
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Robots.py
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum, auto
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
import d3il.environments.d3il.d3il_sim.controllers as ctrl
|
| 6 |
+
import d3il.environments.d3il.d3il_sim.core.logger as logger
|
| 7 |
+
import d3il.environments.d3il.d3il_sim.core.Model as model
|
| 8 |
+
import d3il.environments.d3il.d3il_sim.core.time_keeper as time_keeper
|
| 9 |
+
import d3il.environments.d3il.d3il_sim.utils.geometric_transformation as geom_trans
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class RobotControlInterface(Enum):
|
| 13 |
+
TorqueInterface = (auto(),)
|
| 14 |
+
CartesianInterface = auto()
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class RobotBase:
|
| 18 |
+
|
| 19 |
+
def __init__(self, scene, dt, num_DoF=7, base_position=None, base_orientation=None):
|
| 20 |
+
|
| 21 |
+
self.scene = scene
|
| 22 |
+
self.dt = dt
|
| 23 |
+
self.num_DoF = num_DoF
|
| 24 |
+
|
| 25 |
+
self.inhand_cam = None
|
| 26 |
+
|
| 27 |
+
if base_position is None:
|
| 28 |
+
base_position = [0.0, 0.0, 0.0]
|
| 29 |
+
self.base_position = np.array(base_position)
|
| 30 |
+
|
| 31 |
+
if base_orientation is None:
|
| 32 |
+
base_orientation = [1.0, 0.0, 0.0, 0.0]
|
| 33 |
+
self.base_orientation = np.array(base_orientation)
|
| 34 |
+
|
| 35 |
+
self.misc_data = None
|
| 36 |
+
|
| 37 |
+
self.use_inv_dyn = False
|
| 38 |
+
self.gravity_comp = True
|
| 39 |
+
self.clip_rate = False
|
| 40 |
+
|
| 41 |
+
self.kinModel = model.RobotModelFromPinochio()
|
| 42 |
+
self.dynModel = self.kinModel
|
| 43 |
+
|
| 44 |
+
self.smooth_spline = True
|
| 45 |
+
|
| 46 |
+
self.torque_limit = np.array([80, 80, 80, 80, 10, 10, 10], dtype=np.float64)
|
| 47 |
+
|
| 48 |
+
self.joint_vel_limit = np.array([2.00, 2.00, 2.00, 2.00, 2.50, 2.50, 2.50])
|
| 49 |
+
self.joint_pos_min = np.array(
|
| 50 |
+
[-2.8973, -1.7628, -2.8973, -3.0718, -2.8973, -0.0175, -2.8973]
|
| 51 |
+
)
|
| 52 |
+
self.joint_pos_max = np.array(
|
| 53 |
+
[2.8973, 1.7628, 2.0, -0.0698, 2.8973, 3.7525, 2.8973]
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
self.clip_actions = True
|
| 57 |
+
self.rate_limit = 0.8
|
| 58 |
+
self.end_effector = None
|
| 59 |
+
|
| 60 |
+
self.jointTrackingController = ctrl.JointPDController()
|
| 61 |
+
self.cartesianPosTrackingController = ctrl.CartPosImpedenceController()
|
| 62 |
+
self.cartesianPosQuatTrackingController = ctrl.CartPosQuatImpedenceController()
|
| 63 |
+
self.activeController = self.jointTrackingController
|
| 64 |
+
|
| 65 |
+
self.jointPosController = ctrl.JointPositionController()
|
| 66 |
+
self.jointVelController = ctrl.JointVelocityController()
|
| 67 |
+
self.torqueController = ctrl.TorqueController()
|
| 68 |
+
|
| 69 |
+
self.gotoJointController = ctrl.GotoJointController(self.dt)
|
| 70 |
+
|
| 71 |
+
self.gotoCartPosController = ctrl.GotoCartPosQuatOfflineIKController(self.dt)
|
| 72 |
+
self.gotoCartPosQuatController = ctrl.GotoCartPosQuatOfflineIKController(
|
| 73 |
+
self.dt
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
self.gotoCartPosImpedanceController = ctrl.GotoCartPosImpedanceController(
|
| 77 |
+
self.dt
|
| 78 |
+
)
|
| 79 |
+
self.gotoCartPosQuatImpedanceController = (
|
| 80 |
+
ctrl.GotoCartPosQuatImpedanceController(self.dt)
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
self.jointTrajectoryTracker = ctrl.JointTrajectoryTracker(self.dt)
|
| 84 |
+
self.cartPosQuatTrajectoryTracker = ctrl.CartPosQuatTrajectoryTracker(self.dt)
|
| 85 |
+
|
| 86 |
+
self.controlInterface = RobotControlInterface.TorqueInterface
|
| 87 |
+
|
| 88 |
+
self.robot_logger = logger.RobotLogger(self)
|
| 89 |
+
self.time_keeper = time_keeper.TimeKeeper(self.dt)
|
| 90 |
+
self.additional_loggers = []
|
| 91 |
+
|
| 92 |
+
self.scene.add_robot(self)
|
| 93 |
+
self.reset()
|
| 94 |
+
|
| 95 |
+
def reset(self):
|
| 96 |
+
|
| 97 |
+
self.current_j_pos = np.zeros((self.num_DoF,)) * np.nan
|
| 98 |
+
self.current_j_vel = np.zeros((self.num_DoF,)) * np.nan
|
| 99 |
+
self.des_joint_pos = np.zeros((self.num_DoF,)) * np.nan
|
| 100 |
+
self.des_joint_vel = np.zeros((self.num_DoF,)) * np.nan
|
| 101 |
+
self.des_joint_acc = np.zeros((self.num_DoF,)) * np.nan
|
| 102 |
+
|
| 103 |
+
self.current_fing_pos = np.zeros((2,)) * np.nan
|
| 104 |
+
self.current_fing_vel = np.zeros((2,)) * np.nan
|
| 105 |
+
self.des_fing_pos = np.zeros(2) * np.nan
|
| 106 |
+
self.gripper_width = 0
|
| 107 |
+
self.set_gripper_width = 0.001
|
| 108 |
+
self.finger_commands = np.zeros((2,))
|
| 109 |
+
self.grasp_flag = False
|
| 110 |
+
|
| 111 |
+
self.current_c_pos = np.zeros((3,)) * np.nan
|
| 112 |
+
self.current_c_vel = np.zeros((3,)) * np.nan
|
| 113 |
+
self.current_c_quat = np.zeros((4,)) * np.nan
|
| 114 |
+
self.current_c_quat_vel = np.zeros((4,)) * np.nan
|
| 115 |
+
self.des_c_pos = np.zeros((3,)) * np.nan
|
| 116 |
+
self.des_c_vel = np.zeros((3,)) * np.nan
|
| 117 |
+
self.des_quat = np.zeros((4,)) * np.nan
|
| 118 |
+
self.des_quat_vel = np.zeros((4,)) * np.nan
|
| 119 |
+
|
| 120 |
+
self.current_c_pos_global = np.zeros((3,)) * np.nan
|
| 121 |
+
self.current_c_vel_global = np.zeros((3,)) * np.nan
|
| 122 |
+
self.current_c_quat_global = np.zeros((4,)) * np.nan
|
| 123 |
+
self.current_c_quat_vel_global = np.zeros((4,)) * np.nan
|
| 124 |
+
self.des_c_pos_global = np.zeros((3,)) * np.nan
|
| 125 |
+
self.des_c_vel_global = np.zeros((3,)) * np.nan
|
| 126 |
+
self.des_quat_global = np.zeros((4,)) * np.nan
|
| 127 |
+
self.des_quat_vel_global = np.zeros((4,)) * np.nan
|
| 128 |
+
|
| 129 |
+
self.uff = np.zeros((self.num_DoF + 2,))
|
| 130 |
+
self.uff_last = np.zeros((self.num_DoF + 2,))
|
| 131 |
+
self.last_cmd = np.zeros((self.num_DoF,))
|
| 132 |
+
self.command = np.zeros((self.num_DoF,))
|
| 133 |
+
self.grav_terms = np.zeros(9) * np.nan
|
| 134 |
+
self.current_load = np.zeros((self.num_DoF,))
|
| 135 |
+
|
| 136 |
+
self.robot_logger = logger.RobotLogger(self)
|
| 137 |
+
self.time_keeper = time_keeper.TimeKeeper(self.dt)
|
| 138 |
+
|
| 139 |
+
if self.gotoJointController is not None:
|
| 140 |
+
self.gotoJointController.resetTrajectory()
|
| 141 |
+
|
| 142 |
+
if self.gotoCartPosController is not None:
|
| 143 |
+
self.gotoCartPosController.resetTrajectory()
|
| 144 |
+
if self.gotoCartPosQuatController is not None:
|
| 145 |
+
self.gotoCartPosQuatController.resetTrajectory()
|
| 146 |
+
if self.gotoCartPosImpedanceController is not None:
|
| 147 |
+
self.gotoCartPosImpedanceController.resetTrajectory()
|
| 148 |
+
if self.gotoCartPosQuatImpedanceController is not None:
|
| 149 |
+
self.gotoCartPosQuatImpedanceController.resetTrajectory()
|
| 150 |
+
|
| 151 |
+
self.jointTrackingController = ctrl.JointPDController()
|
| 152 |
+
|
| 153 |
+
self.cartesianPosTrackingController.reset()
|
| 154 |
+
self.cartesianPosQuatTrackingController.reset()
|
| 155 |
+
self.activeController.reset()
|
| 156 |
+
|
| 157 |
+
def getForwardKinematics(self, q=None):
|
| 158 |
+
if q is None:
|
| 159 |
+
q = self.current_j_pos
|
| 160 |
+
|
| 161 |
+
return self.kinModel.getForwardKinematics(q)
|
| 162 |
+
|
| 163 |
+
def getJacobian(self, q=None):
|
| 164 |
+
if q is None:
|
| 165 |
+
q = self.current_j_pos
|
| 166 |
+
|
| 167 |
+
return self.kinModel.getJacobian(q)
|
| 168 |
+
|
| 169 |
+
def get_gravity(self, q=None):
|
| 170 |
+
if q is None:
|
| 171 |
+
q = self.current_j_pos
|
| 172 |
+
|
| 173 |
+
return self.dynModel.get_gravity(q)
|
| 174 |
+
|
| 175 |
+
def get_coriolis(self, q=None, qd=None):
|
| 176 |
+
if q is None:
|
| 177 |
+
q = self.current_j_pos
|
| 178 |
+
|
| 179 |
+
if qd is None:
|
| 180 |
+
qd = self.current_j_vel
|
| 181 |
+
|
| 182 |
+
return self.dynModel.get_coriolis(q, qd)
|
| 183 |
+
|
| 184 |
+
def get_mass_matrix(self, q=None):
|
| 185 |
+
if q is None:
|
| 186 |
+
q = self.current_j_pos
|
| 187 |
+
return self.dynModel.get_mass_matrix(q)
|
| 188 |
+
|
| 189 |
+
def start_logging(self, duration: float = 300.0, **kwargs):
|
| 190 |
+
self.robot_logger.start_logging(duration, **kwargs)
|
| 191 |
+
|
| 192 |
+
def stop_logging(self):
|
| 193 |
+
self.robot_logger.stop_logging()
|
| 194 |
+
|
| 195 |
+
def log_data(self):
|
| 196 |
+
self.robot_logger.log_data()
|
| 197 |
+
|
| 198 |
+
def hold_joint_position(self):
|
| 199 |
+
self.des_joint_pos = self.current_j_pos
|
| 200 |
+
self.command = None
|
| 201 |
+
self.activeController = self.jointTrackingController
|
| 202 |
+
self.jointTrackingController.setSetPoint(self.current_j_pos)
|
| 203 |
+
|
| 204 |
+
def gotoJointPosition(self, desiredPos, duration=4.0, block=True):
|
| 205 |
+
|
| 206 |
+
self.gotoJointController.setDesiredPos(desiredPos)
|
| 207 |
+
self.gotoJointController.executeController(self, duration, block=block)
|
| 208 |
+
|
| 209 |
+
def executeJointPosCtrlTimeStep(self, action, timeSteps=1, block=True):
|
| 210 |
+
self.jointPosController.setAction(action)
|
| 211 |
+
self.jointPosController.executeControllerTimeSteps(self, timeSteps, block=block)
|
| 212 |
+
|
| 213 |
+
def executeJointVelCtrlTimeStep(self, action, timeSteps=1, block=True):
|
| 214 |
+
self.jointVelController.setAction(action)
|
| 215 |
+
self.jointVelController.executeControllerTimeSteps(self, timeSteps, block=block)
|
| 216 |
+
|
| 217 |
+
def executeTorqueCtrlTimeStep(self, action, timeSteps=1, block=True):
|
| 218 |
+
self.torqueController.setAction(action)
|
| 219 |
+
self.torqueController.executeControllerTimeSteps(self, timeSteps, block=block)
|
| 220 |
+
|
| 221 |
+
def follow_JointTraj(self, desiredTraj, goto_start=True, block=True):
|
| 222 |
+
|
| 223 |
+
if goto_start:
|
| 224 |
+
if np.linalg.norm(self.current_j_pos - desiredTraj[0, :]) > 0.05:
|
| 225 |
+
self.gotoJointPosition(desiredTraj[0, :], 4.0)
|
| 226 |
+
|
| 227 |
+
self.jointTrajectoryTracker.setTrajectory(trajectory=desiredTraj)
|
| 228 |
+
self.jointTrajectoryTracker.executeController(
|
| 229 |
+
self, maxDuration=desiredTraj.shape[0] * self.dt, block=block
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
def gotoCartPosition_ImpedanceCtrl(
|
| 233 |
+
self, desiredPos, duration=4.0, global_coord=True, block=True
|
| 234 |
+
):
|
| 235 |
+
if global_coord:
|
| 236 |
+
desiredPos = self._localize_cart_pos(desiredPos)
|
| 237 |
+
|
| 238 |
+
self.gotoCartPosImpedanceController.setDesiredPos(desiredPos)
|
| 239 |
+
self.gotoCartPosImpedanceController.executeController(
|
| 240 |
+
self, duration, block=block
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
def gotoCartPositionAndQuat_ImpedanceCtrl(
|
| 244 |
+
self, desiredPos, desiredQuat, duration=4.0, global_coord=True, block=True
|
| 245 |
+
):
|
| 246 |
+
if global_coord:
|
| 247 |
+
desiredPos = self._localize_cart_pos(desiredPos)
|
| 248 |
+
desiredQuat = self._localize_cart_quat(desiredQuat)
|
| 249 |
+
|
| 250 |
+
self.gotoCartPosQuatImpedanceController.setDesiredPos(
|
| 251 |
+
np.hstack((desiredPos, desiredQuat))
|
| 252 |
+
)
|
| 253 |
+
self.gotoCartPosQuatImpedanceController.executeController(
|
| 254 |
+
self, duration, block=block
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
def gotoCartPosition(self, desiredPos, duration=4.0, global_coord=True, block=True):
|
| 258 |
+
|
| 259 |
+
if global_coord:
|
| 260 |
+
desiredPos = self._localize_cart_pos(desiredPos)
|
| 261 |
+
self.gotoCartPosController.setDesiredPos(desiredPos)
|
| 262 |
+
self.gotoCartPosController.executeController(self, duration, block=block)
|
| 263 |
+
|
| 264 |
+
def gotoCartPositionAndQuat(
|
| 265 |
+
self,
|
| 266 |
+
desiredPos,
|
| 267 |
+
desiredQuat,
|
| 268 |
+
duration=4.0,
|
| 269 |
+
global_coord=True,
|
| 270 |
+
block=True,
|
| 271 |
+
log=True,
|
| 272 |
+
):
|
| 273 |
+
|
| 274 |
+
if global_coord:
|
| 275 |
+
desiredPos = self._localize_cart_pos(desiredPos)
|
| 276 |
+
desiredQuat = self._localize_cart_quat(desiredQuat)
|
| 277 |
+
|
| 278 |
+
self.gotoCartPosQuatController.setDesiredPos(
|
| 279 |
+
np.hstack((desiredPos, desiredQuat))
|
| 280 |
+
)
|
| 281 |
+
self.gotoCartPosQuatController.executeController(
|
| 282 |
+
self, duration, block=block, log=log
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
def follow_CartPositionAndQuatTraj(
|
| 286 |
+
self, desiredPos, desiredQuat, goto_start=True, global_coord=True, block=True
|
| 287 |
+
):
|
| 288 |
+
|
| 289 |
+
if global_coord:
|
| 290 |
+
desiredPos = np.array([self._localize_cart_pos(p) for p in desiredPos])
|
| 291 |
+
desiredQuat = np.array([self._localize_cart_quat(q) for q in desiredQuat])
|
| 292 |
+
|
| 293 |
+
desiredTraj = np.hstack((desiredPos, desiredQuat))
|
| 294 |
+
duration = desiredTraj.shape[0] * self.dt
|
| 295 |
+
|
| 296 |
+
if goto_start:
|
| 297 |
+
is_position_far = (
|
| 298 |
+
np.linalg.norm(self.current_c_pos - desiredPos[0, :]) > 0.005
|
| 299 |
+
)
|
| 300 |
+
is_rotation_far = (
|
| 301 |
+
np.linalg.norm(self.current_c_quat - desiredQuat[0, :]) > 0.005
|
| 302 |
+
) and (np.linalg.norm(self.current_c_quat + desiredQuat[0, :]) > 0.005)
|
| 303 |
+
|
| 304 |
+
if is_position_far or is_rotation_far:
|
| 305 |
+
self.gotoCartPositionAndQuat(
|
| 306 |
+
desiredPos=desiredPos[0, :],
|
| 307 |
+
desiredQuat=desiredQuat[0, :],
|
| 308 |
+
duration=4.0,
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
self.cartPosQuatTrajectoryTracker.setTrajectory(trajectory=desiredTraj)
|
| 312 |
+
self.cartPosQuatTrajectoryTracker.executeController(
|
| 313 |
+
self, maxDuration=duration, block=block
|
| 314 |
+
)
|
| 315 |
+
|
| 316 |
+
def _localize_cart_pos(self, pos):
|
| 317 |
+
if not np.any(self.base_position) and np.array_equal(
|
| 318 |
+
self.base_orientation, [1, 0, 0, 0]
|
| 319 |
+
):
|
| 320 |
+
return pos
|
| 321 |
+
|
| 322 |
+
pos_vec = np.array(pos)
|
| 323 |
+
|
| 324 |
+
local_pos = pos_vec - self.base_position
|
| 325 |
+
local_pos = geom_trans.quat_rot_vec(
|
| 326 |
+
geom_trans.quat_conjugate(self.base_orientation), local_pos
|
| 327 |
+
)
|
| 328 |
+
return local_pos
|
| 329 |
+
|
| 330 |
+
def _localize_cart_quat(self, quat):
|
| 331 |
+
if np.array_equal(self.base_orientation, [1, 0, 0, 0]):
|
| 332 |
+
return quat
|
| 333 |
+
|
| 334 |
+
local_quat = quat
|
| 335 |
+
if local_quat is not None:
|
| 336 |
+
quat_vec = np.array(local_quat)
|
| 337 |
+
local_quat = geom_trans.quat_mul(
|
| 338 |
+
geom_trans.quat_conjugate(self.base_orientation), quat_vec
|
| 339 |
+
)
|
| 340 |
+
return local_quat
|
| 341 |
+
|
| 342 |
+
def _localize_cart_coords(self, pos, quat=None):
|
| 343 |
+
if quat is not None:
|
| 344 |
+
quat = self._localize_cart_quat(quat)
|
| 345 |
+
return self._localize_cart_pos(pos), quat
|
| 346 |
+
|
| 347 |
+
def wait(self, duration=1.0, block=True):
|
| 348 |
+
self.activeController.executeController(self, duration, block=block)
|
| 349 |
+
|
| 350 |
+
def open_fingers(self):
|
| 351 |
+
self.set_desired_gripper_width(0.04)
|
| 352 |
+
|
| 353 |
+
def close_fingers(self, duration=0.5):
|
| 354 |
+
self.set_gripper_width = 0.0
|
| 355 |
+
self.grasp_flag = False
|
| 356 |
+
if duration > 0:
|
| 357 |
+
self.wait(duration)
|
| 358 |
+
self.grasp_flag = True
|
| 359 |
+
|
| 360 |
+
def set_desired_gripper_width(self, desired_width):
|
| 361 |
+
self.set_gripper_width = desired_width
|
| 362 |
+
self.grasp_flag = False
|
| 363 |
+
|
| 364 |
+
def fing_ctrl_step(self):
|
| 365 |
+
|
| 366 |
+
pgain = 1 * np.array([500, 500], dtype=np.float64)
|
| 367 |
+
dgain = 1 * np.array([10, 10], dtype=np.float64)
|
| 368 |
+
|
| 369 |
+
gripper_width = self.current_fing_pos
|
| 370 |
+
gripper_width_vel = self.current_fing_vel
|
| 371 |
+
|
| 372 |
+
des_fing_pos = np.array(
|
| 373 |
+
[self.set_gripper_width, self.set_gripper_width], dtype=np.float64
|
| 374 |
+
)
|
| 375 |
+
|
| 376 |
+
mean_finger_pos = np.mean(gripper_width)
|
| 377 |
+
force = pgain * (mean_finger_pos - gripper_width)
|
| 378 |
+
|
| 379 |
+
if (np.mean(gripper_width) - self.set_gripper_width) > 0.005:
|
| 380 |
+
if self.grasp_flag:
|
| 381 |
+
force2 = np.array([-20, -20])
|
| 382 |
+
else:
|
| 383 |
+
force2 = dgain * (np.array([-0.2, -0.2]) - gripper_width_vel)
|
| 384 |
+
else:
|
| 385 |
+
force2 = pgain * (des_fing_pos - gripper_width) - dgain * gripper_width_vel
|
| 386 |
+
force2 = np.clip(force2, -5, 5)
|
| 387 |
+
|
| 388 |
+
force = force + force2
|
| 389 |
+
|
| 390 |
+
return force
|
| 391 |
+
|
| 392 |
+
def receiveState(self):
|
| 393 |
+
|
| 394 |
+
raise NotImplementedError
|
| 395 |
+
|
| 396 |
+
def tick(self):
|
| 397 |
+
self.time_keeper.tick()
|
| 398 |
+
|
| 399 |
+
def nextStep(self, log=True):
|
| 400 |
+
|
| 401 |
+
self.scene.next_step(log)
|
| 402 |
+
|
| 403 |
+
def prepare_step(self):
|
| 404 |
+
|
| 405 |
+
self.des_joint_pos[:] = np.nan
|
| 406 |
+
self.des_joint_vel[:] = np.nan
|
| 407 |
+
self.des_joint_acc[:] = np.nan
|
| 408 |
+
|
| 409 |
+
self.des_c_pos[:] = np.nan
|
| 410 |
+
self.des_c_vel[:] = np.nan
|
| 411 |
+
self.des_quat[:] = np.nan
|
| 412 |
+
self.des_quat_vel[:] = np.nan
|
| 413 |
+
|
| 414 |
+
def get_command_from_inverse_dynamics(
|
| 415 |
+
self, target_j_acc, mj_calc_inv=False, robot_id=None, client_id=None
|
| 416 |
+
):
|
| 417 |
+
raise NotImplementedError
|
| 418 |
+
|
| 419 |
+
def preprocessCommand(self, target_j_acc):
|
| 420 |
+
if len(target_j_acc.shape) == 2:
|
| 421 |
+
target_j_acc = target_j_acc.reshape((-1))
|
| 422 |
+
if target_j_acc.shape[0] != self.num_DoF:
|
| 423 |
+
raise ValueError(
|
| 424 |
+
"Specified motor command vector needs to be of size {} is {}\n".format(
|
| 425 |
+
self.num_DoF, target_j_acc.shape[0]
|
| 426 |
+
)
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
self.finger_commands = self.fing_ctrl_step()
|
| 430 |
+
if self.use_inv_dyn:
|
| 431 |
+
target_j_acc = np.append(target_j_acc, self.finger_commands[0])
|
| 432 |
+
target_j_acc = np.append(target_j_acc, self.finger_commands[1])
|
| 433 |
+
self.des_joint_acc = target_j_acc.copy()
|
| 434 |
+
self.uff = self.get_command_from_inverse_dynamics(
|
| 435 |
+
target_j_acc, mj_calc_inv=True
|
| 436 |
+
)
|
| 437 |
+
self.uff[7:9] = self.finger_commands
|
| 438 |
+
|
| 439 |
+
else:
|
| 440 |
+
target_j_acc = np.append(target_j_acc, self.finger_commands[0])
|
| 441 |
+
target_j_acc = np.append(target_j_acc, self.finger_commands[1])
|
| 442 |
+
self.uff = target_j_acc
|
| 443 |
+
if self.gravity_comp:
|
| 444 |
+
comp_forces = self.get_command_from_inverse_dynamics(
|
| 445 |
+
target_j_acc=np.zeros(9), mj_calc_inv=False
|
| 446 |
+
)
|
| 447 |
+
self.grav_terms = comp_forces.copy()
|
| 448 |
+
self.uff += comp_forces
|
| 449 |
+
|
| 450 |
+
if self.step_count == 0:
|
| 451 |
+
self.uff_last = 0
|
| 452 |
+
|
| 453 |
+
if self.clip_rate:
|
| 454 |
+
uff_diff = self.uff - self.uff_last
|
| 455 |
+
uff_diff = np.clip(uff_diff, -self.rate_limit, self.rate_limit)
|
| 456 |
+
self.uff = self.uff_last + uff_diff
|
| 457 |
+
|
| 458 |
+
if self.clip_actions:
|
| 459 |
+
self.uff[:7] = np.clip(self.uff[:7], -self.torque_limit, self.torque_limit)
|
| 460 |
+
|
| 461 |
+
def beam_to_cart_pos_and_quat(self, desiredPos, desiredQuat):
|
| 462 |
+
q = self.gotoCartPosQuatController.trajectory_generator.findJointPosition(
|
| 463 |
+
self, desiredPos=desiredPos, desiredQuat=desiredQuat
|
| 464 |
+
)
|
| 465 |
+
self.beam_to_joint_pos(q)
|
| 466 |
+
|
| 467 |
+
def beam_to_joint_pos(self, desiredJoints, run=True, log=False):
|
| 468 |
+
self.set_q(desiredJoints)
|
| 469 |
+
self.receiveState()
|
| 470 |
+
self.gotoJointController.resetTrajectory()
|
| 471 |
+
self.gotoCartPosQuatController.resetTrajectory()
|
| 472 |
+
self.gotoCartPosImpedanceController.resetTrajectory()
|
| 473 |
+
self.jointTrackingController.setSetPoint(desiredJoints)
|
| 474 |
+
if run:
|
| 475 |
+
self.jointTrackingController.executeControllerTimeSteps(self, 1, log)
|
| 476 |
+
|
| 477 |
+
def set_q(self, joints, robot_id=None, physicsClientId=None):
|
| 478 |
+
|
| 479 |
+
raise NotImplementedError
|
| 480 |
+
|
| 481 |
+
@property
|
| 482 |
+
def step_count(self):
|
| 483 |
+
return self.time_keeper.step_count
|
| 484 |
+
|
| 485 |
+
@property
|
| 486 |
+
def time_stamp(self):
|
| 487 |
+
return self.time_keeper.time_stamp
|
| 488 |
+
|
| 489 |
+
@property
|
| 490 |
+
def step_count(self):
|
| 491 |
+
return self.time_keeper.step_count
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/Scene.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import enum
|
| 2 |
+
from abc import ABC, abstractmethod
|
| 3 |
+
from typing import List
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
import d3il.environments.d3il.d3il_sim.core.sim_object as sim_object
|
| 8 |
+
import d3il.environments.d3il.d3il_sim.core.time_keeper as time_keeper
|
| 9 |
+
import d3il.environments.d3il.d3il_sim.utils.geometric_transformation as geom_trans
|
| 10 |
+
from d3il.environments.d3il.d3il_sim.core import Camera, Robots
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class Scene(ABC):
|
| 14 |
+
|
| 15 |
+
class RenderMode(enum.Enum):
|
| 16 |
+
BLIND = "blind"
|
| 17 |
+
OFFSCREEN = "offscreen"
|
| 18 |
+
HUMAN = "human"
|
| 19 |
+
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
object_list=None,
|
| 23 |
+
dt=0.001,
|
| 24 |
+
render: RenderMode = RenderMode.HUMAN,
|
| 25 |
+
*args,
|
| 26 |
+
**kwargs
|
| 27 |
+
):
|
| 28 |
+
if object_list is None:
|
| 29 |
+
object_list = []
|
| 30 |
+
|
| 31 |
+
self.obj_repo = sim_object.SimObjectRepository(object_list)
|
| 32 |
+
|
| 33 |
+
self.dt = dt
|
| 34 |
+
self.render_mode = render
|
| 35 |
+
|
| 36 |
+
self.setup_done = False
|
| 37 |
+
|
| 38 |
+
self._robots: List[Robots.RobotBase] = []
|
| 39 |
+
self.inhand_cam = None
|
| 40 |
+
self.cage_cam = None
|
| 41 |
+
|
| 42 |
+
self.step_callbacks = []
|
| 43 |
+
self.additional_loggers = []
|
| 44 |
+
self.time_keeper = time_keeper.TimeKeeper(self.dt)
|
| 45 |
+
|
| 46 |
+
def add_robot(self, robot):
|
| 47 |
+
self._robots.append(robot)
|
| 48 |
+
|
| 49 |
+
@property
|
| 50 |
+
def robots(self):
|
| 51 |
+
return self._robots
|
| 52 |
+
|
| 53 |
+
def register_callback(self, fn, **kwargs):
|
| 54 |
+
|
| 55 |
+
self.step_callbacks.append((fn, kwargs))
|
| 56 |
+
|
| 57 |
+
@property
|
| 58 |
+
@abstractmethod
|
| 59 |
+
def sim_name(self) -> str:
|
| 60 |
+
|
| 61 |
+
return "NONE"
|
| 62 |
+
|
| 63 |
+
@abstractmethod
|
| 64 |
+
def _setup_scene(self):
|
| 65 |
+
|
| 66 |
+
self._setup_objects(self.object_list)
|
| 67 |
+
|
| 68 |
+
@abstractmethod
|
| 69 |
+
def reset(self, obj_pos=None):
|
| 70 |
+
|
| 71 |
+
raise NotImplementedError
|
| 72 |
+
|
| 73 |
+
def start(self, robot_init_qpos: np.ndarray = None):
|
| 74 |
+
|
| 75 |
+
self._setup_scene()
|
| 76 |
+
self.load_robot_to_scene(robot_init_qpos=robot_init_qpos)
|
| 77 |
+
self.setup_done = True
|
| 78 |
+
self.reset()
|
| 79 |
+
|
| 80 |
+
@abstractmethod
|
| 81 |
+
def render(self):
|
| 82 |
+
|
| 83 |
+
raise NotImplementedError
|
| 84 |
+
|
| 85 |
+
def next_step(self, log=True):
|
| 86 |
+
for rb in self.robots:
|
| 87 |
+
rb.prepare_step()
|
| 88 |
+
|
| 89 |
+
self._sim_step()
|
| 90 |
+
|
| 91 |
+
self.time_keeper.tick()
|
| 92 |
+
|
| 93 |
+
for rb in self.robots:
|
| 94 |
+
rb.tick()
|
| 95 |
+
rb.receiveState()
|
| 96 |
+
|
| 97 |
+
for call_back, kwargs in self.step_callbacks:
|
| 98 |
+
call_back(**kwargs)
|
| 99 |
+
if log:
|
| 100 |
+
self.log_data()
|
| 101 |
+
|
| 102 |
+
self.render()
|
| 103 |
+
|
| 104 |
+
@abstractmethod
|
| 105 |
+
def _sim_step(self):
|
| 106 |
+
raise NotImplementedError
|
| 107 |
+
|
| 108 |
+
@abstractmethod
|
| 109 |
+
def load_robot_to_scene(self, robot_init_qpos: np.ndarray = None):
|
| 110 |
+
|
| 111 |
+
raise NotImplementedError
|
| 112 |
+
|
| 113 |
+
@abstractmethod
|
| 114 |
+
def _setup_objects(self, sim_objs: List[sim_object.SimObject]):
|
| 115 |
+
|
| 116 |
+
raise NotImplementedError
|
| 117 |
+
|
| 118 |
+
@abstractmethod
|
| 119 |
+
def _rt_add_object(self, sim_obj: sim_object.SimObject):
|
| 120 |
+
|
| 121 |
+
raise NotImplementedError
|
| 122 |
+
|
| 123 |
+
def add_object(self, sim_obj: sim_object.SimObject):
|
| 124 |
+
|
| 125 |
+
if not self.setup_done:
|
| 126 |
+
self.obj_repo.add_object(sim_obj)
|
| 127 |
+
else:
|
| 128 |
+
self._rt_add_object(sim_obj)
|
| 129 |
+
|
| 130 |
+
def list_objects(self):
|
| 131 |
+
return list(self._objects.keys())
|
| 132 |
+
|
| 133 |
+
def get_object(self, name: str = None, obj_id: int = None) -> sim_object.SimObject:
|
| 134 |
+
|
| 135 |
+
return self.obj_repo.get_object(name, obj_id)
|
| 136 |
+
|
| 137 |
+
def _query_pois(self, sim_obj: sim_object.SimObject, fn) -> np.ndarray:
|
| 138 |
+
|
| 139 |
+
if len(sim_obj.get_poi()) > 1:
|
| 140 |
+
return np.array([fn(poi, sim_obj) for poi in sim_obj.get_poi()])
|
| 141 |
+
else:
|
| 142 |
+
return fn(sim_obj.get_poi()[0], sim_obj)
|
| 143 |
+
|
| 144 |
+
@abstractmethod
|
| 145 |
+
def _get_obj_seg_id(self, obj_name: str) -> int:
|
| 146 |
+
|
| 147 |
+
raise NotImplementedError
|
| 148 |
+
|
| 149 |
+
def get_obj_seg_id(
|
| 150 |
+
self,
|
| 151 |
+
sim_obj: sim_object.SimObject = None,
|
| 152 |
+
obj_name: str = None,
|
| 153 |
+
obj_id: int = None,
|
| 154 |
+
) -> int:
|
| 155 |
+
|
| 156 |
+
if obj_name is None:
|
| 157 |
+
if sim_obj is None:
|
| 158 |
+
sim_obj = self.get_object(obj_id=obj_id)
|
| 159 |
+
obj_name = sim_obj.name
|
| 160 |
+
|
| 161 |
+
return self._get_obj_seg_id(obj_name=obj_name)
|
| 162 |
+
|
| 163 |
+
@abstractmethod
|
| 164 |
+
def _get_obj_pos(self, poi, sim_obj: sim_object.SimObject) -> np.ndarray:
|
| 165 |
+
|
| 166 |
+
raise NotImplementedError
|
| 167 |
+
|
| 168 |
+
def get_obj_pos(
|
| 169 |
+
self,
|
| 170 |
+
sim_obj: sim_object.SimObject = None,
|
| 171 |
+
obj_name: str = None,
|
| 172 |
+
obj_id: int = None,
|
| 173 |
+
) -> np.ndarray:
|
| 174 |
+
|
| 175 |
+
if sim_obj is None:
|
| 176 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 177 |
+
return self._query_pois(sim_obj, self._get_obj_pos)
|
| 178 |
+
|
| 179 |
+
@abstractmethod
|
| 180 |
+
def _get_obj_quat(self, poi, sim_obj: sim_object.SimObject) -> np.ndarray:
|
| 181 |
+
|
| 182 |
+
raise NotImplementedError
|
| 183 |
+
|
| 184 |
+
def get_obj_quat(
|
| 185 |
+
self,
|
| 186 |
+
sim_obj: sim_object.SimObject = None,
|
| 187 |
+
obj_name: str = None,
|
| 188 |
+
obj_id: int = None,
|
| 189 |
+
) -> np.ndarray:
|
| 190 |
+
|
| 191 |
+
if sim_obj is None:
|
| 192 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 193 |
+
return self._query_pois(sim_obj, self._get_obj_quat)
|
| 194 |
+
|
| 195 |
+
def _get_obj_rot_mat(self, poi, sim_obj: sim_object.SimObject) -> np.ndarray:
|
| 196 |
+
|
| 197 |
+
quat = self._get_obj_quat(poi, sim_obj)
|
| 198 |
+
return geom_trans.mat2quat(quat)
|
| 199 |
+
|
| 200 |
+
def get_obj_rot_mat(
|
| 201 |
+
self,
|
| 202 |
+
sim_obj: sim_object.SimObject = None,
|
| 203 |
+
obj_name: str = None,
|
| 204 |
+
obj_id: int = None,
|
| 205 |
+
) -> np.ndarray:
|
| 206 |
+
|
| 207 |
+
if sim_obj is None:
|
| 208 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 209 |
+
return self._query_pois(sim_obj, self._get_obj_rot_mat)
|
| 210 |
+
|
| 211 |
+
@abstractmethod
|
| 212 |
+
def _set_obj_pos(self, new_pos, sim_obj: sim_object.SimObject):
|
| 213 |
+
|
| 214 |
+
raise NotImplementedError
|
| 215 |
+
|
| 216 |
+
def set_obj_pos(
|
| 217 |
+
self,
|
| 218 |
+
new_pos,
|
| 219 |
+
sim_obj: sim_object.SimObject = None,
|
| 220 |
+
obj_name: str = None,
|
| 221 |
+
obj_id: int = None,
|
| 222 |
+
):
|
| 223 |
+
|
| 224 |
+
if sim_obj is None:
|
| 225 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 226 |
+
|
| 227 |
+
self._set_obj_pos(new_pos, sim_obj)
|
| 228 |
+
|
| 229 |
+
@abstractmethod
|
| 230 |
+
def _set_obj_quat(self, new_quat, sim_obj: sim_object.SimObject) -> np.ndarray:
|
| 231 |
+
|
| 232 |
+
raise NotImplementedError
|
| 233 |
+
|
| 234 |
+
def set_obj_quat(
|
| 235 |
+
self,
|
| 236 |
+
new_quat,
|
| 237 |
+
sim_obj: sim_object.SimObject = None,
|
| 238 |
+
obj_name: str = None,
|
| 239 |
+
obj_id: int = None,
|
| 240 |
+
):
|
| 241 |
+
|
| 242 |
+
if sim_obj is None:
|
| 243 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 244 |
+
|
| 245 |
+
self._set_obj_quat(new_quat, sim_obj)
|
| 246 |
+
|
| 247 |
+
@abstractmethod
|
| 248 |
+
def _set_obj_pos_and_quat(
|
| 249 |
+
self, new_pos, new_quat, sim_obj: sim_object.SimObject
|
| 250 |
+
) -> np.ndarray:
|
| 251 |
+
|
| 252 |
+
raise NotImplementedError
|
| 253 |
+
|
| 254 |
+
def set_obj_pos_and_quat(
|
| 255 |
+
self,
|
| 256 |
+
new_pos,
|
| 257 |
+
new_quat,
|
| 258 |
+
sim_obj: sim_object.SimObject = None,
|
| 259 |
+
obj_name: str = None,
|
| 260 |
+
obj_id: int = None,
|
| 261 |
+
):
|
| 262 |
+
|
| 263 |
+
if sim_obj is None:
|
| 264 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 265 |
+
|
| 266 |
+
self._set_obj_pos_and_quat(new_pos, new_quat, sim_obj)
|
| 267 |
+
|
| 268 |
+
@abstractmethod
|
| 269 |
+
def _remove_object(self, sim_obj: sim_object.SimObject):
|
| 270 |
+
raise NotImplementedError
|
| 271 |
+
|
| 272 |
+
def remove_object(
|
| 273 |
+
self,
|
| 274 |
+
sim_obj: sim_object.SimObject = None,
|
| 275 |
+
obj_name: str = None,
|
| 276 |
+
obj_id: int = None,
|
| 277 |
+
):
|
| 278 |
+
if sim_obj is None:
|
| 279 |
+
sim_obj = self.get_object(obj_name, obj_id)
|
| 280 |
+
|
| 281 |
+
self._remove_object(sim_obj)
|
| 282 |
+
self.obj_repo.remove_object(sim_obj)
|
| 283 |
+
|
| 284 |
+
def get_cage_cam(self) -> Camera.Camera:
|
| 285 |
+
return self.cage_cam
|
| 286 |
+
|
| 287 |
+
def start_logging(self, duration: float = 300.0, **kwargs):
|
| 288 |
+
|
| 289 |
+
for rb in self.robots:
|
| 290 |
+
rb.start_logging(duration, **kwargs)
|
| 291 |
+
|
| 292 |
+
for logger in self.additional_loggers:
|
| 293 |
+
logger.start_logging(duration, **kwargs)
|
| 294 |
+
|
| 295 |
+
def stop_logging(self):
|
| 296 |
+
|
| 297 |
+
for rb in self.robots:
|
| 298 |
+
rb.stop_logging()
|
| 299 |
+
|
| 300 |
+
for logger in self.additional_loggers:
|
| 301 |
+
logger.stop_logging()
|
| 302 |
+
|
| 303 |
+
def add_logger(self, logger):
|
| 304 |
+
|
| 305 |
+
self.additional_loggers.append(logger)
|
| 306 |
+
|
| 307 |
+
def log_data(self):
|
| 308 |
+
for rb in self.robots:
|
| 309 |
+
rb.log_data()
|
| 310 |
+
for logger in self.additional_loggers:
|
| 311 |
+
logger.log_data()
|
| 312 |
+
|
| 313 |
+
@property
|
| 314 |
+
def step_count(self):
|
| 315 |
+
return self.time_keeper.step_count
|
| 316 |
+
|
| 317 |
+
@property
|
| 318 |
+
def time_stamp(self):
|
| 319 |
+
return self.time_keeper.time_stamp
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .logger import *
|
| 2 |
+
from .Model import *
|
| 3 |
+
from .Robots import *
|
| 4 |
+
from .Scene import *
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/logger.py
ADDED
|
@@ -0,0 +1,867 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
import datetime
|
| 3 |
+
import os
|
| 4 |
+
from enum import Flag, auto
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
import wandb
|
| 10 |
+
except ImportError:
|
| 11 |
+
pass
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def reset_wandb_env():
|
| 15 |
+
exclude = {
|
| 16 |
+
"WANDB_PROJECT",
|
| 17 |
+
"WANDB_ENTITY",
|
| 18 |
+
"WANDB_API_KEY",
|
| 19 |
+
"WANDB_START_METHOD",
|
| 20 |
+
}
|
| 21 |
+
for k, v in os.environ.items():
|
| 22 |
+
if k.startswith("WANDB_") and k not in exclude:
|
| 23 |
+
del os.environ[k]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class RobotPlotFlags(Flag):
|
| 27 |
+
JOINT_POS = auto()
|
| 28 |
+
JOINT_VEL = auto()
|
| 29 |
+
JOINT_ACC = auto()
|
| 30 |
+
|
| 31 |
+
CART_POS = auto()
|
| 32 |
+
CART_VEL = auto()
|
| 33 |
+
ORIENTATION = auto()
|
| 34 |
+
ORIENTATION_VEL = auto()
|
| 35 |
+
|
| 36 |
+
CART_POS_GLOBAL = auto()
|
| 37 |
+
CART_VEL_GLOBAL = auto()
|
| 38 |
+
ORIENTATION_GLOBAL = auto()
|
| 39 |
+
ORIENTATION_VEL_GLOBAL = auto()
|
| 40 |
+
|
| 41 |
+
COMMAND = auto()
|
| 42 |
+
TORQUES = auto()
|
| 43 |
+
GRAVITY = auto()
|
| 44 |
+
LOADS = auto()
|
| 45 |
+
TIME_STAMPS = auto()
|
| 46 |
+
|
| 47 |
+
GRIPPER_POS = auto()
|
| 48 |
+
GRIPPER_WIDTH = auto()
|
| 49 |
+
GRIPPER_FORCE = auto()
|
| 50 |
+
GRIPPER_VEL = auto()
|
| 51 |
+
|
| 52 |
+
MISC = auto()
|
| 53 |
+
|
| 54 |
+
GRIPPER = GRIPPER_WIDTH | GRIPPER_POS | GRIPPER_FORCE | GRIPPER_VEL
|
| 55 |
+
JOINTS = JOINT_POS | JOINT_ACC | JOINT_VEL
|
| 56 |
+
END_EFFECTOR = CART_POS | CART_VEL | ORIENTATION | ORIENTATION_VEL
|
| 57 |
+
END_EFFECTOR_GLOBAL = (
|
| 58 |
+
CART_POS_GLOBAL | CART_VEL_GLOBAL | ORIENTATION_GLOBAL | ORIENTATION_VEL_GLOBAL
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
FULL = GRIPPER | JOINTS | END_EFFECTOR | END_EFFECTOR_GLOBAL
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class ObjectPlotFlags(Flag):
|
| 65 |
+
POSITION = auto()
|
| 66 |
+
ORIENTATION = auto()
|
| 67 |
+
FULL = POSITION | ORIENTATION
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class CamPlotFlags(Flag):
|
| 71 |
+
IMAGE = auto()
|
| 72 |
+
FULL = IMAGE
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class LoggerBase(abc.ABC):
|
| 76 |
+
def __init__(self):
|
| 77 |
+
|
| 78 |
+
self.logged_time = 0
|
| 79 |
+
self.log_interval = None
|
| 80 |
+
self.log_duration = None
|
| 81 |
+
self.logged_time_steps = 0
|
| 82 |
+
self.max_time_steps = None
|
| 83 |
+
self.last_log_time = None
|
| 84 |
+
self.is_logging = False
|
| 85 |
+
|
| 86 |
+
def start_logging(self, duration: float = 300.0, **kwargs):
|
| 87 |
+
|
| 88 |
+
self.logged_time = 0
|
| 89 |
+
self.logged_time_steps = 0
|
| 90 |
+
self.log_duration = duration
|
| 91 |
+
self._start(duration, **kwargs)
|
| 92 |
+
self.is_logging = True
|
| 93 |
+
|
| 94 |
+
def log_data(self):
|
| 95 |
+
|
| 96 |
+
if self.is_logging and self._check_log_interval():
|
| 97 |
+
self._log()
|
| 98 |
+
self.logged_time_steps += 1
|
| 99 |
+
if (
|
| 100 |
+
self.max_time_steps is not None
|
| 101 |
+
and self.logged_time_steps >= self.max_time_steps
|
| 102 |
+
) or self.logged_time >= self.log_duration:
|
| 103 |
+
self.stop_logging()
|
| 104 |
+
|
| 105 |
+
def stop_logging(self):
|
| 106 |
+
|
| 107 |
+
if self.is_logging:
|
| 108 |
+
self.is_logging = False
|
| 109 |
+
if self.logged_time_steps > 0:
|
| 110 |
+
self._stop()
|
| 111 |
+
|
| 112 |
+
@abc.abstractmethod
|
| 113 |
+
def _start(self, duration=600, **kwargs):
|
| 114 |
+
pass
|
| 115 |
+
|
| 116 |
+
@abc.abstractmethod
|
| 117 |
+
def _log(self):
|
| 118 |
+
pass
|
| 119 |
+
|
| 120 |
+
@abc.abstractmethod
|
| 121 |
+
def _stop(self):
|
| 122 |
+
pass
|
| 123 |
+
|
| 124 |
+
@abc.abstractmethod
|
| 125 |
+
def _check_log_interval(self) -> bool:
|
| 126 |
+
return True
|
| 127 |
+
|
| 128 |
+
@abc.abstractmethod
|
| 129 |
+
def reset(self):
|
| 130 |
+
pass
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
class RobotLogger(LoggerBase):
|
| 134 |
+
def __init__(self, robot):
|
| 135 |
+
super().__init__()
|
| 136 |
+
|
| 137 |
+
self.robot = robot
|
| 138 |
+
self.plot_selection = RobotPlotFlags.FULL
|
| 139 |
+
self.use_wandb = False
|
| 140 |
+
|
| 141 |
+
self.reset()
|
| 142 |
+
|
| 143 |
+
def reset(self):
|
| 144 |
+
self.log_dict_full = None
|
| 145 |
+
|
| 146 |
+
@property
|
| 147 |
+
def log_dict(self):
|
| 148 |
+
log_dict = dict()
|
| 149 |
+
|
| 150 |
+
if RobotPlotFlags.JOINTS & self.plot_selection:
|
| 151 |
+
log_dict.update(
|
| 152 |
+
{
|
| 153 |
+
"joint_pos": self.robot.current_j_pos,
|
| 154 |
+
"joint_vel": self.robot.current_j_vel,
|
| 155 |
+
"des_joint_pos": self.robot.des_joint_pos,
|
| 156 |
+
"des_joint_vel": self.robot.des_joint_vel,
|
| 157 |
+
"des_joint_acc": self.robot.des_joint_acc,
|
| 158 |
+
}
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
if RobotPlotFlags.GRIPPER & self.plot_selection:
|
| 162 |
+
log_dict.update(
|
| 163 |
+
{
|
| 164 |
+
"finger_pos": self.robot.current_fing_pos,
|
| 165 |
+
"finger_vel": self.robot.current_fing_vel,
|
| 166 |
+
"des_finger_pos": self.robot.set_gripper_width,
|
| 167 |
+
"gripper_width": self.robot.gripper_width,
|
| 168 |
+
"uff": self.robot.uff,
|
| 169 |
+
}
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
if RobotPlotFlags.END_EFFECTOR & self.plot_selection:
|
| 173 |
+
log_dict.update(
|
| 174 |
+
{
|
| 175 |
+
"cart_pos": self.robot.current_c_pos,
|
| 176 |
+
"cart_vel": self.robot.current_c_vel,
|
| 177 |
+
"cart_quat": self.robot.current_c_quat,
|
| 178 |
+
"cart_quat_vel": self.robot.current_c_quat_vel,
|
| 179 |
+
"des_c_pos": self.robot.des_c_pos,
|
| 180 |
+
"des_c_vel": self.robot.des_c_vel,
|
| 181 |
+
"des_quat": self.robot.des_quat,
|
| 182 |
+
"des_quat_vel": self.robot.des_quat_vel,
|
| 183 |
+
}
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
if RobotPlotFlags.END_EFFECTOR_GLOBAL & self.plot_selection:
|
| 187 |
+
log_dict.update(
|
| 188 |
+
{
|
| 189 |
+
"cart_pos_global": self.robot.current_c_pos_global,
|
| 190 |
+
"cart_vel_global": self.robot.current_c_vel_global,
|
| 191 |
+
"cart_quat_global": self.robot.current_c_quat_global,
|
| 192 |
+
"cart_quat_vel_global": self.robot.current_c_quat_vel_global,
|
| 193 |
+
}
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
if RobotPlotFlags.COMMAND & self.plot_selection:
|
| 197 |
+
log_dict.update(
|
| 198 |
+
{
|
| 199 |
+
"uff": self.robot.uff,
|
| 200 |
+
"last_cmd": self.robot.last_cmd,
|
| 201 |
+
"command": self.robot.command,
|
| 202 |
+
"grav_terms": self.robot.grav_terms,
|
| 203 |
+
"load": self.robot.current_load,
|
| 204 |
+
}
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
if RobotPlotFlags.MISC & self.plot_selection:
|
| 208 |
+
log_dict.update({"misc": self.robot.misc_data})
|
| 209 |
+
|
| 210 |
+
log_dict.update(
|
| 211 |
+
{
|
| 212 |
+
"time_stamp": self.robot.time_stamp,
|
| 213 |
+
"step": self.robot.step_count,
|
| 214 |
+
"wall_clock": self.robot.time_keeper.wall_clock,
|
| 215 |
+
}
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
return log_dict
|
| 219 |
+
|
| 220 |
+
def _start(
|
| 221 |
+
self,
|
| 222 |
+
duration=600,
|
| 223 |
+
log_interval=None,
|
| 224 |
+
plot_selection: RobotPlotFlags = RobotPlotFlags.FULL,
|
| 225 |
+
use_wandb=False,
|
| 226 |
+
**kwargs
|
| 227 |
+
):
|
| 228 |
+
|
| 229 |
+
self.plot_selection = plot_selection
|
| 230 |
+
self.use_wandb = use_wandb
|
| 231 |
+
|
| 232 |
+
if log_interval is None:
|
| 233 |
+
self.log_interval = 1
|
| 234 |
+
else:
|
| 235 |
+
self.log_interval = log_interval
|
| 236 |
+
|
| 237 |
+
assert self.log_interval >= 1
|
| 238 |
+
|
| 239 |
+
self.max_time_steps = int(duration / self.log_interval)
|
| 240 |
+
|
| 241 |
+
if self.max_time_steps > 1800000:
|
| 242 |
+
self.max_time_steps = 1800000
|
| 243 |
+
|
| 244 |
+
self.log_dict_full = {
|
| 245 |
+
k: [None] * self.max_time_steps for k, v in self.log_dict.items()
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
self.initial_time_stamp = self.robot.step_count
|
| 249 |
+
|
| 250 |
+
if self.use_wandb:
|
| 251 |
+
reset_wandb_env()
|
| 252 |
+
now = datetime.datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
|
| 253 |
+
job_name = "robot_log_" + now
|
| 254 |
+
self.run = wandb.init(
|
| 255 |
+
project="sf",
|
| 256 |
+
group="test_log",
|
| 257 |
+
name=job_name,
|
| 258 |
+
settings=wandb.Settings(_disable_stats=True),
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
def _log(self):
|
| 262 |
+
log_properties = self.log_dict
|
| 263 |
+
|
| 264 |
+
for k, v in log_properties.items():
|
| 265 |
+
self.log_dict_full[k][self.logged_time_steps] = v
|
| 266 |
+
|
| 267 |
+
self.last_log_time = log_properties["step"]
|
| 268 |
+
|
| 269 |
+
self.logged_time = self.last_log_time - self.initial_time_stamp
|
| 270 |
+
|
| 271 |
+
if self.use_wandb:
|
| 272 |
+
flat_log_dict = {
|
| 273 |
+
k + "_" + str(i): vv
|
| 274 |
+
for k, v in log_properties.items()
|
| 275 |
+
for i, vv in enumerate(np.atleast_1d(v))
|
| 276 |
+
}
|
| 277 |
+
self.run.log(flat_log_dict, step=self.logged_time_steps)
|
| 278 |
+
|
| 279 |
+
def _stop(self):
|
| 280 |
+
|
| 281 |
+
if self.logged_time_steps < self.max_time_steps:
|
| 282 |
+
for v in self.log_dict_full.values():
|
| 283 |
+
del v[self.logged_time_steps :]
|
| 284 |
+
|
| 285 |
+
if self.use_wandb:
|
| 286 |
+
if self.run is not None:
|
| 287 |
+
self.run.finish()
|
| 288 |
+
|
| 289 |
+
self.__dict__.update({k: np.array(v) for k, v in self.log_dict_full.items()})
|
| 290 |
+
|
| 291 |
+
def _check_log_interval(self) -> bool:
|
| 292 |
+
|
| 293 |
+
if (
|
| 294 |
+
self.last_log_time is None
|
| 295 |
+
or self.robot.step_count >= self.last_log_time + self.log_interval
|
| 296 |
+
):
|
| 297 |
+
return True
|
| 298 |
+
return False
|
| 299 |
+
|
| 300 |
+
def plot(self, plot_selection=RobotPlotFlags.FULL, block=True):
|
| 301 |
+
|
| 302 |
+
import matplotlib.pyplot as plt
|
| 303 |
+
|
| 304 |
+
valid_plot_selections = plot_selection & self.plot_selection
|
| 305 |
+
|
| 306 |
+
if self.is_logging:
|
| 307 |
+
self.stop_logging()
|
| 308 |
+
|
| 309 |
+
self.time_stamp = self.time_stamp - self.time_stamp[0]
|
| 310 |
+
|
| 311 |
+
j1_limit_lower = -2.9671
|
| 312 |
+
j1_limit_upper = 2.9671
|
| 313 |
+
j2_limit_lower = -1.8326
|
| 314 |
+
j2_limit_upper = 1.8326
|
| 315 |
+
j3_limit_lower = -2.9671
|
| 316 |
+
j3_limit_upper = 2.9671
|
| 317 |
+
j4_limit_lower = -3.1416
|
| 318 |
+
j4_limit_upper = 0.0873
|
| 319 |
+
j5_limit_lower = -2.9671
|
| 320 |
+
j5_limit_upper = 2.9671
|
| 321 |
+
j6_limit_lower = -0.0873
|
| 322 |
+
j6_limit_upper = 3.8223
|
| 323 |
+
j7_limit_lower = -2.9671
|
| 324 |
+
j7_limit_upper = 2.9671
|
| 325 |
+
|
| 326 |
+
j1_limit_lower_safety = -2.8973
|
| 327 |
+
j1_limit_upper_safety = 2.8973
|
| 328 |
+
j2_limit_lower_safety = -1.7628
|
| 329 |
+
j2_limit_upper_safety = 1.7628
|
| 330 |
+
j3_limit_lower_safety = -2.8973
|
| 331 |
+
j3_limit_upper_safety = 2.8973
|
| 332 |
+
j4_limit_lower_safety = -3.0718
|
| 333 |
+
j4_limit_upper_safety = 0.0175
|
| 334 |
+
j5_limit_lower_safety = -2.8973
|
| 335 |
+
j5_limit_upper_safety = 2.8973
|
| 336 |
+
j6_limit_lower_safety = -0.0175
|
| 337 |
+
j6_limit_upper_safety = 3.7525
|
| 338 |
+
j7_limit_lower_safety = -2.8973
|
| 339 |
+
j7_limit_upper_safety = 2.8973
|
| 340 |
+
|
| 341 |
+
j1_vel_limit_lower = -2.3925
|
| 342 |
+
j1_vel_limit_upper = 2.3925
|
| 343 |
+
j2_vel_limit_lower = -2.3925
|
| 344 |
+
j2_vel_limit_upper = 2.3925
|
| 345 |
+
j3_vel_limit_lower = -2.3925
|
| 346 |
+
j3_vel_limit_upper = 2.3925
|
| 347 |
+
j4_vel_limit_lower = -2.3925
|
| 348 |
+
j4_vel_limit_upper = 2.3925
|
| 349 |
+
j5_vel_limit_lower = -2.8710
|
| 350 |
+
j5_vel_limit_upper = 2.8710
|
| 351 |
+
j6_vel_limit_lower = -2.8710
|
| 352 |
+
j6_vel_limit_upper = 2.8710
|
| 353 |
+
j7_vel_limit_lower = -2.8710
|
| 354 |
+
j7_vel_limit_upper = 2.8710
|
| 355 |
+
|
| 356 |
+
upper_limits = [
|
| 357 |
+
j1_limit_upper,
|
| 358 |
+
j2_limit_upper,
|
| 359 |
+
j3_limit_upper,
|
| 360 |
+
j4_limit_upper,
|
| 361 |
+
j5_limit_upper,
|
| 362 |
+
j6_limit_upper,
|
| 363 |
+
j7_limit_upper,
|
| 364 |
+
]
|
| 365 |
+
lower_limits = [
|
| 366 |
+
j1_limit_lower,
|
| 367 |
+
j2_limit_lower,
|
| 368 |
+
j3_limit_lower,
|
| 369 |
+
j4_limit_lower,
|
| 370 |
+
j5_limit_lower,
|
| 371 |
+
j6_limit_lower,
|
| 372 |
+
j7_limit_lower,
|
| 373 |
+
]
|
| 374 |
+
|
| 375 |
+
soft_upper_limits = [
|
| 376 |
+
j1_limit_upper_safety,
|
| 377 |
+
j2_limit_upper_safety,
|
| 378 |
+
j3_limit_upper_safety,
|
| 379 |
+
j4_limit_upper_safety,
|
| 380 |
+
j5_limit_upper_safety,
|
| 381 |
+
j6_limit_upper_safety,
|
| 382 |
+
j7_limit_upper_safety,
|
| 383 |
+
]
|
| 384 |
+
|
| 385 |
+
soft_lower_limits = [
|
| 386 |
+
j1_limit_lower_safety,
|
| 387 |
+
j2_limit_lower_safety,
|
| 388 |
+
j3_limit_lower_safety,
|
| 389 |
+
j4_limit_lower_safety,
|
| 390 |
+
j5_limit_lower_safety,
|
| 391 |
+
j6_limit_lower_safety,
|
| 392 |
+
j7_limit_lower_safety,
|
| 393 |
+
]
|
| 394 |
+
|
| 395 |
+
upper_limits_vel = [
|
| 396 |
+
j1_vel_limit_upper,
|
| 397 |
+
j2_vel_limit_upper,
|
| 398 |
+
j3_vel_limit_upper,
|
| 399 |
+
j4_vel_limit_upper,
|
| 400 |
+
j5_vel_limit_upper,
|
| 401 |
+
j6_vel_limit_upper,
|
| 402 |
+
j7_vel_limit_upper,
|
| 403 |
+
]
|
| 404 |
+
|
| 405 |
+
lower_limits_vel = [
|
| 406 |
+
j1_vel_limit_lower,
|
| 407 |
+
j2_vel_limit_lower,
|
| 408 |
+
j3_vel_limit_lower,
|
| 409 |
+
j4_vel_limit_lower,
|
| 410 |
+
j5_vel_limit_lower,
|
| 411 |
+
j6_vel_limit_lower,
|
| 412 |
+
j7_vel_limit_lower,
|
| 413 |
+
]
|
| 414 |
+
|
| 415 |
+
if RobotPlotFlags.JOINT_POS in valid_plot_selections:
|
| 416 |
+
joint_pos_fig = plt.figure()
|
| 417 |
+
for k in range(7):
|
| 418 |
+
plt.figure(joint_pos_fig.number)
|
| 419 |
+
plt.subplot(7, 1, k + 1)
|
| 420 |
+
plt.plot(self.time_stamp, self.joint_pos[:, k])
|
| 421 |
+
plt.plot(self.time_stamp, self.des_joint_pos[:, k], "r")
|
| 422 |
+
plt.subplot(7, 1, 1)
|
| 423 |
+
plt.title("joint positions ")
|
| 424 |
+
|
| 425 |
+
if RobotPlotFlags.JOINT_VEL in valid_plot_selections:
|
| 426 |
+
joint_vel_fig = plt.figure()
|
| 427 |
+
for k in range(7):
|
| 428 |
+
plt.figure(joint_vel_fig.number)
|
| 429 |
+
plt.subplot(7, 1, k + 1)
|
| 430 |
+
plt.plot(self.time_stamp, self.joint_vel[:, k])
|
| 431 |
+
plt.plot(self.time_stamp, self.des_joint_vel[:, k], "r")
|
| 432 |
+
plt.subplot(7, 1, 1)
|
| 433 |
+
plt.title("joint velocities ")
|
| 434 |
+
|
| 435 |
+
if RobotPlotFlags.JOINT_ACC in valid_plot_selections:
|
| 436 |
+
robot_acceleration = np.diff(self.joint_vel, 1, axis=0) / self.robot.dt
|
| 437 |
+
acceleration_fig = plt.figure()
|
| 438 |
+
for k in range(7):
|
| 439 |
+
plt.figure(acceleration_fig.number)
|
| 440 |
+
plt.subplot(7, 1, k + 1)
|
| 441 |
+
plt.plot(self.time_stamp[1:], robot_acceleration[:, k])
|
| 442 |
+
plt.plot(self.time_stamp, self.des_joint_acc[:, k], "r")
|
| 443 |
+
plt.subplot(7, 1, 1)
|
| 444 |
+
plt.title("joint accelerations ")
|
| 445 |
+
|
| 446 |
+
if RobotPlotFlags.COMMAND in valid_plot_selections:
|
| 447 |
+
ctrl_torques_fig = plt.figure()
|
| 448 |
+
for k in range(7):
|
| 449 |
+
plt.figure(ctrl_torques_fig.number)
|
| 450 |
+
plt.subplot(7, 1, k + 1)
|
| 451 |
+
plt.plot(self.time_stamp, self.command[:, k])
|
| 452 |
+
plt.subplot(7, 1, 1)
|
| 453 |
+
plt.title(" control torques (without gravity comp.)")
|
| 454 |
+
|
| 455 |
+
if RobotPlotFlags.TORQUES in valid_plot_selections:
|
| 456 |
+
clipped_torques_fig = plt.figure()
|
| 457 |
+
for k in range(7):
|
| 458 |
+
plt.figure(clipped_torques_fig.number)
|
| 459 |
+
plt.subplot(7, 1, k + 1)
|
| 460 |
+
plt.plot(self.time_stamp, self.uff[:, k])
|
| 461 |
+
plt.subplot(7, 1, 1)
|
| 462 |
+
plt.title(" clipped torques (with gravity comp.)")
|
| 463 |
+
|
| 464 |
+
if RobotPlotFlags.GRAVITY in valid_plot_selections:
|
| 465 |
+
grav_terms_fig = plt.figure()
|
| 466 |
+
for k in range(7):
|
| 467 |
+
plt.figure(grav_terms_fig.number)
|
| 468 |
+
plt.subplot(9, 1, k + 1)
|
| 469 |
+
plt.plot(self.time_stamp, self.grav_terms[:, k])
|
| 470 |
+
plt.subplot(7, 1, 1)
|
| 471 |
+
plt.title(" gravity (+coriolis) ")
|
| 472 |
+
|
| 473 |
+
if RobotPlotFlags.LOADS in valid_plot_selections:
|
| 474 |
+
loads_fig = plt.figure()
|
| 475 |
+
for k in range(7):
|
| 476 |
+
plt.figure(loads_fig.number)
|
| 477 |
+
plt.subplot(7, 1, k + 1)
|
| 478 |
+
plt.plot(self.time_stamp, self.load[:, k])
|
| 479 |
+
plt.subplot(7, 1, 1)
|
| 480 |
+
plt.title(" loads - forces in joints")
|
| 481 |
+
|
| 482 |
+
if RobotPlotFlags.GRIPPER_WIDTH in valid_plot_selections:
|
| 483 |
+
gripper_fig = plt.figure()
|
| 484 |
+
plt.figure(gripper_fig.number)
|
| 485 |
+
plt.plot(self.time_stamp, self.gripper_width)
|
| 486 |
+
plt.title(" gripper width ")
|
| 487 |
+
|
| 488 |
+
if RobotPlotFlags.GRIPPER_FORCE in valid_plot_selections:
|
| 489 |
+
gripper_force_fig = plt.figure()
|
| 490 |
+
plt.figure(gripper_force_fig.number)
|
| 491 |
+
plt.plot(self.time_stamp, self.uff[:, -2:])
|
| 492 |
+
|
| 493 |
+
plt.title(" gripper force (without contact forces)")
|
| 494 |
+
|
| 495 |
+
if RobotPlotFlags.GRIPPER_POS in valid_plot_selections:
|
| 496 |
+
gripper_pos_fig = plt.figure()
|
| 497 |
+
plt.figure(gripper_pos_fig.number)
|
| 498 |
+
plt.plot(self.time_stamp, self.finger_pos[:, -2:], label="actual")
|
| 499 |
+
plt.plot(self.time_stamp, self.des_finger_pos, label="desired")
|
| 500 |
+
plt.legend()
|
| 501 |
+
plt.title(" gripper pos ")
|
| 502 |
+
|
| 503 |
+
if RobotPlotFlags.GRIPPER_VEL in valid_plot_selections:
|
| 504 |
+
gripper_vel_fig = plt.figure()
|
| 505 |
+
plt.figure(gripper_vel_fig.number)
|
| 506 |
+
plt.plot(self.time_stamp, self.finger_vel[:, -2:])
|
| 507 |
+
plt.title(" gripper vel ")
|
| 508 |
+
|
| 509 |
+
if RobotPlotFlags.CART_POS in valid_plot_selections:
|
| 510 |
+
cart_pos_fig = plt.figure()
|
| 511 |
+
for j in range(3):
|
| 512 |
+
plt.figure(cart_pos_fig.number)
|
| 513 |
+
plt.subplot(3, 1, j + 1)
|
| 514 |
+
plt.plot(self.time_stamp, self.cart_pos[:, j])
|
| 515 |
+
plt.plot(self.time_stamp, self.des_c_pos[:, j], "r")
|
| 516 |
+
plt.subplot(3, 1, 1)
|
| 517 |
+
plt.title(" endeffector pos ")
|
| 518 |
+
|
| 519 |
+
if RobotPlotFlags.CART_VEL in valid_plot_selections:
|
| 520 |
+
cart_vel_fig = plt.figure()
|
| 521 |
+
for j in range(3):
|
| 522 |
+
plt.figure(cart_vel_fig.number)
|
| 523 |
+
plt.subplot(3, 1, j + 1)
|
| 524 |
+
plt.plot(self.time_stamp, self.cart_vel[:, j])
|
| 525 |
+
plt.plot(self.time_stamp, self.des_c_vel[:, j], "r")
|
| 526 |
+
plt.subplot(3, 1, 1)
|
| 527 |
+
plt.title(" endeffector vel ")
|
| 528 |
+
|
| 529 |
+
if RobotPlotFlags.ORIENTATION in valid_plot_selections:
|
| 530 |
+
orientation_fig = plt.figure()
|
| 531 |
+
for j in range(4):
|
| 532 |
+
plt.figure(orientation_fig.number)
|
| 533 |
+
plt.subplot(4, 1, j + 1)
|
| 534 |
+
plt.plot(self.time_stamp, self.cart_quat[:, j])
|
| 535 |
+
plt.plot(self.time_stamp, self.des_quat[:, j], "r")
|
| 536 |
+
plt.subplot(4, 1, 1)
|
| 537 |
+
plt.title(" endeffector orientation ")
|
| 538 |
+
|
| 539 |
+
if RobotPlotFlags.ORIENTATION_VEL in valid_plot_selections:
|
| 540 |
+
orientation_vel_fig = plt.figure()
|
| 541 |
+
for j in range(4):
|
| 542 |
+
plt.figure(orientation_vel_fig.number)
|
| 543 |
+
plt.subplot(4, 1, j + 1)
|
| 544 |
+
plt.plot(self.time_stamp, self.cart_quat_vel[:, j])
|
| 545 |
+
plt.plot(self.time_stamp, self.des_quat_vel[:, j], "r")
|
| 546 |
+
plt.subplot(4, 1, 1)
|
| 547 |
+
plt.title(" endeffector angular velocity ")
|
| 548 |
+
|
| 549 |
+
if RobotPlotFlags.CART_POS_GLOBAL in valid_plot_selections:
|
| 550 |
+
cart_pos_fig = plt.figure()
|
| 551 |
+
for j in range(3):
|
| 552 |
+
plt.figure(cart_pos_fig.number)
|
| 553 |
+
plt.subplot(3, 1, j + 1)
|
| 554 |
+
plt.plot(self.time_stamp, self.cart_pos_global[:, j])
|
| 555 |
+
plt.subplot(3, 1, 1)
|
| 556 |
+
plt.title(" endeffector pos global")
|
| 557 |
+
|
| 558 |
+
if RobotPlotFlags.CART_VEL_GLOBAL in valid_plot_selections:
|
| 559 |
+
cart_vel_fig = plt.figure()
|
| 560 |
+
for j in range(3):
|
| 561 |
+
plt.figure(cart_vel_fig.number)
|
| 562 |
+
plt.subplot(3, 1, j + 1)
|
| 563 |
+
plt.plot(self.time_stamp, self.cart_vel_global[:, j])
|
| 564 |
+
plt.subplot(3, 1, 1)
|
| 565 |
+
plt.title(" endeffector vel global")
|
| 566 |
+
|
| 567 |
+
if RobotPlotFlags.ORIENTATION_GLOBAL in valid_plot_selections:
|
| 568 |
+
orientation_fig = plt.figure()
|
| 569 |
+
for j in range(4):
|
| 570 |
+
plt.figure(orientation_fig.number)
|
| 571 |
+
plt.subplot(4, 1, j + 1)
|
| 572 |
+
plt.plot(self.time_stamp, self.cart_quat_global[:, j])
|
| 573 |
+
plt.subplot(4, 1, 1)
|
| 574 |
+
plt.title(" endeffector orientation global")
|
| 575 |
+
|
| 576 |
+
if RobotPlotFlags.ORIENTATION_VEL_GLOBAL in valid_plot_selections:
|
| 577 |
+
orientation_vel_fig = plt.figure()
|
| 578 |
+
for j in range(4):
|
| 579 |
+
plt.figure(orientation_vel_fig.number)
|
| 580 |
+
plt.subplot(4, 1, j + 1)
|
| 581 |
+
plt.plot(self.time_stamp, self.cart_quat_vel_global[:, j])
|
| 582 |
+
plt.subplot(4, 1, 1)
|
| 583 |
+
plt.title(" endeffector angular velocity global")
|
| 584 |
+
|
| 585 |
+
if RobotPlotFlags.TIME_STAMPS in valid_plot_selections:
|
| 586 |
+
timeStamp_fig = plt.figure()
|
| 587 |
+
plt.figure(timeStamp_fig.number)
|
| 588 |
+
plt.plot(np.diff(self.time_stamp))
|
| 589 |
+
plt.title("time stamp difference ")
|
| 590 |
+
|
| 591 |
+
if RobotPlotFlags.MISC in valid_plot_selections:
|
| 592 |
+
misc_fig = plt.figure()
|
| 593 |
+
numplots = self.misc.shape[1]
|
| 594 |
+
for j in range(numplots):
|
| 595 |
+
plt.figure(misc_fig.number)
|
| 596 |
+
plt.subplot(numplots, 1, j + 1)
|
| 597 |
+
plt.plot(self.misc[:, j])
|
| 598 |
+
|
| 599 |
+
plt.subplot(numplots, 1, 1)
|
| 600 |
+
plt.title("misc debug data ")
|
| 601 |
+
plt.show(block=block)
|
| 602 |
+
|
| 603 |
+
|
| 604 |
+
class ObjectLogger(LoggerBase):
|
| 605 |
+
|
| 606 |
+
def __init__(self, scene, sim_object=None, obj_name=None):
|
| 607 |
+
super().__init__()
|
| 608 |
+
self.scene = scene
|
| 609 |
+
|
| 610 |
+
if obj_name is not None:
|
| 611 |
+
sim_object = scene.get_object(name=obj_name)
|
| 612 |
+
|
| 613 |
+
if sim_object is None:
|
| 614 |
+
raise ValueError("No Simobject defined.")
|
| 615 |
+
self.object = sim_object
|
| 616 |
+
self.plot_selection = ObjectPlotFlags.FULL
|
| 617 |
+
|
| 618 |
+
def reset(self):
|
| 619 |
+
self.log_dict_full = None
|
| 620 |
+
self.last_log_time = None
|
| 621 |
+
|
| 622 |
+
@property
|
| 623 |
+
def log_dict(self):
|
| 624 |
+
log_dict = dict()
|
| 625 |
+
|
| 626 |
+
if ObjectPlotFlags.POSITION in self.plot_selection:
|
| 627 |
+
log_dict.update({"pos": self.scene.get_obj_pos(self.object).flatten()})
|
| 628 |
+
|
| 629 |
+
if ObjectPlotFlags.ORIENTATION in self.plot_selection:
|
| 630 |
+
log_dict.update({"orientation": self.scene.get_obj_quat(self.object)})
|
| 631 |
+
|
| 632 |
+
log_dict.update({"time_stamp": self.scene.robots[0].time_stamp})
|
| 633 |
+
log_dict.update({"step": self.scene.robots[0].step_count})
|
| 634 |
+
|
| 635 |
+
return log_dict
|
| 636 |
+
|
| 637 |
+
def _start(
|
| 638 |
+
self,
|
| 639 |
+
duration=600,
|
| 640 |
+
log_interval=None,
|
| 641 |
+
obj_plot_selection: ObjectPlotFlags = ObjectPlotFlags.FULL,
|
| 642 |
+
use_wandb=False,
|
| 643 |
+
**kwargs
|
| 644 |
+
):
|
| 645 |
+
|
| 646 |
+
self.plot_selection = obj_plot_selection
|
| 647 |
+
self.use_wandb = use_wandb
|
| 648 |
+
|
| 649 |
+
if log_interval is None:
|
| 650 |
+
self.log_interval = 1
|
| 651 |
+
else:
|
| 652 |
+
self.log_interval = log_interval
|
| 653 |
+
|
| 654 |
+
assert self.log_interval >= 1
|
| 655 |
+
|
| 656 |
+
self.max_time_steps = int(duration / self.log_interval)
|
| 657 |
+
|
| 658 |
+
if self.max_time_steps > 1800000:
|
| 659 |
+
self.max_time_steps = 1800000
|
| 660 |
+
|
| 661 |
+
self.log_dict_full = {
|
| 662 |
+
k: [None] * self.max_time_steps for k, v in self.log_dict.items()
|
| 663 |
+
}
|
| 664 |
+
|
| 665 |
+
self.initial_time_stamp = self.scene.robots[0].step_count
|
| 666 |
+
|
| 667 |
+
if self.use_wandb:
|
| 668 |
+
reset_wandb_env()
|
| 669 |
+
now = datetime.datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
|
| 670 |
+
job_name = "robot_log_" + now
|
| 671 |
+
self.run = wandb.init(
|
| 672 |
+
project="sf",
|
| 673 |
+
group="test_log",
|
| 674 |
+
name=job_name,
|
| 675 |
+
settings=wandb.Settings(_disable_stats=True),
|
| 676 |
+
)
|
| 677 |
+
|
| 678 |
+
def _log(self):
|
| 679 |
+
log_properties = self.log_dict
|
| 680 |
+
|
| 681 |
+
for k, v in log_properties.items():
|
| 682 |
+
self.log_dict_full[k][self.logged_time_steps] = v
|
| 683 |
+
|
| 684 |
+
self.last_log_time = log_properties["step"]
|
| 685 |
+
|
| 686 |
+
self.logged_time = self.last_log_time - self.initial_time_stamp
|
| 687 |
+
|
| 688 |
+
if self.use_wandb:
|
| 689 |
+
flat_log_dict = {
|
| 690 |
+
k + "_" + str(i): vv
|
| 691 |
+
for k, v in log_properties.items()
|
| 692 |
+
for i, vv in enumerate(np.atleast_1d(v))
|
| 693 |
+
}
|
| 694 |
+
self.run.log(flat_log_dict, step=self.logged_time_steps)
|
| 695 |
+
|
| 696 |
+
def _stop(self):
|
| 697 |
+
|
| 698 |
+
if self.logged_time_steps < self.max_time_steps:
|
| 699 |
+
for v in self.log_dict_full.values():
|
| 700 |
+
del v[self.logged_time_steps :]
|
| 701 |
+
|
| 702 |
+
if self.use_wandb:
|
| 703 |
+
if self.run is not None:
|
| 704 |
+
self.run.finish()
|
| 705 |
+
|
| 706 |
+
self.__dict__.update({k: np.array(v) for k, v in self.log_dict_full.items()})
|
| 707 |
+
|
| 708 |
+
def _check_log_interval(self) -> bool:
|
| 709 |
+
if (
|
| 710 |
+
self.last_log_time is None
|
| 711 |
+
or self.scene.robots[0].step_count >= self.last_log_time + self.log_interval
|
| 712 |
+
):
|
| 713 |
+
return True
|
| 714 |
+
return False
|
| 715 |
+
|
| 716 |
+
def plot(self, plot_selection=ObjectPlotFlags.FULL, block=True):
|
| 717 |
+
valid_plot_selections = plot_selection & self.plot_selection
|
| 718 |
+
|
| 719 |
+
import matplotlib.pyplot as plt
|
| 720 |
+
|
| 721 |
+
self.time_stamp = self.time_stamp - self.time_stamp[0]
|
| 722 |
+
|
| 723 |
+
if ObjectPlotFlags.POSITION in valid_plot_selections:
|
| 724 |
+
pos_fig = plt.figure()
|
| 725 |
+
for k in range(3):
|
| 726 |
+
plt.figure(pos_fig.number)
|
| 727 |
+
plt.subplot(3, 1, k + 1)
|
| 728 |
+
plt.plot(self.time_stamp, self.pos[:, k])
|
| 729 |
+
plt.subplot(3, 1, 1)
|
| 730 |
+
plt.title("object positions ")
|
| 731 |
+
|
| 732 |
+
if ObjectPlotFlags.ORIENTATION in valid_plot_selections:
|
| 733 |
+
orientation_fig = plt.figure()
|
| 734 |
+
for k in range(4):
|
| 735 |
+
plt.figure(orientation_fig.number)
|
| 736 |
+
plt.subplot(4, 1, k + 1)
|
| 737 |
+
plt.plot(self.time_stamp, self.orientation[:, k])
|
| 738 |
+
plt.title(" object orientation ")
|
| 739 |
+
plt.show(block=block)
|
| 740 |
+
|
| 741 |
+
|
| 742 |
+
class CamLogger(LoggerBase):
|
| 743 |
+
|
| 744 |
+
def __init__(self, scene, camera):
|
| 745 |
+
super().__init__()
|
| 746 |
+
|
| 747 |
+
self.scene = scene
|
| 748 |
+
self.cam = camera
|
| 749 |
+
|
| 750 |
+
self.plot_selection = CamPlotFlags.FULL
|
| 751 |
+
|
| 752 |
+
def reset(self):
|
| 753 |
+
self.log_dict_full = None
|
| 754 |
+
self.last_log_time = None
|
| 755 |
+
self.cam.get_image(depth=False)
|
| 756 |
+
|
| 757 |
+
@property
|
| 758 |
+
def log_dict(self):
|
| 759 |
+
log_dict = dict()
|
| 760 |
+
|
| 761 |
+
if CamPlotFlags.IMAGE in self.plot_selection:
|
| 762 |
+
|
| 763 |
+
log_dict.update({"color_image": self.cam.get_image(depth=False)})
|
| 764 |
+
|
| 765 |
+
log_dict.update({"time_stamp": self.scene.robots[0].step_count})
|
| 766 |
+
log_dict.update({"step": self.scene.robots[0].step_count})
|
| 767 |
+
|
| 768 |
+
return log_dict
|
| 769 |
+
|
| 770 |
+
def _start(
|
| 771 |
+
self,
|
| 772 |
+
duration=600,
|
| 773 |
+
log_interval=None,
|
| 774 |
+
obj_plot_selection: CamPlotFlags = CamPlotFlags.FULL,
|
| 775 |
+
use_wandb=False,
|
| 776 |
+
**kwargs
|
| 777 |
+
):
|
| 778 |
+
|
| 779 |
+
self.plot_selection = obj_plot_selection
|
| 780 |
+
self.use_wandb = use_wandb
|
| 781 |
+
|
| 782 |
+
if log_interval is None:
|
| 783 |
+
self.log_interval = 1
|
| 784 |
+
else:
|
| 785 |
+
self.log_interval = log_interval
|
| 786 |
+
|
| 787 |
+
assert self.log_interval >= 1
|
| 788 |
+
|
| 789 |
+
self.max_time_steps = int(duration / self.log_interval)
|
| 790 |
+
|
| 791 |
+
if self.max_time_steps > 1800000:
|
| 792 |
+
self.max_time_steps = 1800000
|
| 793 |
+
|
| 794 |
+
self.log_dict_full = {
|
| 795 |
+
k: [None] * self.max_time_steps for k, v in self.log_dict.items()
|
| 796 |
+
}
|
| 797 |
+
|
| 798 |
+
self.initial_time_stamp = self.scene.robots[0].step_count
|
| 799 |
+
|
| 800 |
+
if self.use_wandb:
|
| 801 |
+
reset_wandb_env()
|
| 802 |
+
now = datetime.datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
|
| 803 |
+
job_name = "robot_log_" + now
|
| 804 |
+
self.run = wandb.init(
|
| 805 |
+
project="sf",
|
| 806 |
+
group="test_log",
|
| 807 |
+
name=job_name,
|
| 808 |
+
settings=wandb.Settings(_disable_stats=True),
|
| 809 |
+
)
|
| 810 |
+
|
| 811 |
+
def _log(self):
|
| 812 |
+
|
| 813 |
+
log_properties = self.log_dict
|
| 814 |
+
|
| 815 |
+
for k, v in log_properties.items():
|
| 816 |
+
self.log_dict_full[k][self.logged_time_steps] = v
|
| 817 |
+
|
| 818 |
+
self.last_log_time = log_properties["step"]
|
| 819 |
+
|
| 820 |
+
self.logged_time = self.last_log_time - self.initial_time_stamp
|
| 821 |
+
|
| 822 |
+
if self.use_wandb:
|
| 823 |
+
flat_log_dict = {
|
| 824 |
+
k + "_" + str(i): vv
|
| 825 |
+
for k, v in log_properties.items()
|
| 826 |
+
for i, vv in enumerate(np.atleast_1d(v))
|
| 827 |
+
}
|
| 828 |
+
self.run.log(flat_log_dict, step=self.logged_time_steps)
|
| 829 |
+
|
| 830 |
+
def _stop(self):
|
| 831 |
+
|
| 832 |
+
if self.logged_time_steps < self.max_time_steps:
|
| 833 |
+
for v in self.log_dict_full.values():
|
| 834 |
+
del v[self.logged_time_steps :]
|
| 835 |
+
|
| 836 |
+
if self.use_wandb:
|
| 837 |
+
if self.run is not None:
|
| 838 |
+
self.run.finish()
|
| 839 |
+
|
| 840 |
+
self.__dict__.update({k: np.array(v) for k, v in self.log_dict_full.items()})
|
| 841 |
+
|
| 842 |
+
def _check_log_interval(self) -> bool:
|
| 843 |
+
|
| 844 |
+
if (
|
| 845 |
+
self.last_log_time is None
|
| 846 |
+
or self.scene.robots[0].step_count >= self.last_log_time + self.log_interval
|
| 847 |
+
):
|
| 848 |
+
return True
|
| 849 |
+
return False
|
| 850 |
+
|
| 851 |
+
def plot(self, plot_selection=CamPlotFlags.FULL, block=True):
|
| 852 |
+
valid_plot_selections = plot_selection & self.plot_selection
|
| 853 |
+
|
| 854 |
+
import matplotlib.pyplot as plt
|
| 855 |
+
|
| 856 |
+
self.time_stamp = self.time_stamp - self.time_stamp[0]
|
| 857 |
+
|
| 858 |
+
if CamPlotFlags.IMAGE in valid_plot_selections:
|
| 859 |
+
pos_fig = plt.figure()
|
| 860 |
+
for k in range(3):
|
| 861 |
+
plt.figure(pos_fig.number)
|
| 862 |
+
plt.subplot(3, 1, k + 1)
|
| 863 |
+
plt.plot(self.time_stamp, self.pos[:, k])
|
| 864 |
+
plt.subplot(3, 1, 1)
|
| 865 |
+
plt.title("camera images ")
|
| 866 |
+
|
| 867 |
+
plt.show(block=block)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/sim_object/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .sim_obj_repo import SimObjectRepository
|
| 2 |
+
from .sim_object import IntelligentSimObject, SimObject
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/sim_object/sim_obj_repo.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List
|
| 2 |
+
|
| 3 |
+
from d3il.environments.d3il.d3il_sim.utils.unique_dict import UniqueDict
|
| 4 |
+
|
| 5 |
+
from .sim_object import SimObject
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class SimObjectRepository:
|
| 9 |
+
|
| 10 |
+
def __init__(self, obj_list=None) -> None:
|
| 11 |
+
self._name2id_map = UniqueDict(err_msg="Duplicate object name:")
|
| 12 |
+
self._id2name_map = UniqueDict(err_msg="Duplicate object id:")
|
| 13 |
+
self._objects = UniqueDict(err_msg="Duplicate object name:")
|
| 14 |
+
|
| 15 |
+
if obj_list is not None:
|
| 16 |
+
for obj in obj_list:
|
| 17 |
+
self.add_object(obj)
|
| 18 |
+
|
| 19 |
+
def add_object(self, sim_obj: SimObject) -> None:
|
| 20 |
+
self._objects[sim_obj.name] = sim_obj
|
| 21 |
+
|
| 22 |
+
def remove_object(self, sim_obj: SimObject) -> None:
|
| 23 |
+
del self._name2id_map[sim_obj.name]
|
| 24 |
+
|
| 25 |
+
del self._objects[sim_obj.name]
|
| 26 |
+
|
| 27 |
+
def register_obj_id(self, sim_obj: SimObject, obj_id: int):
|
| 28 |
+
sim_obj.obj_id = obj_id
|
| 29 |
+
self._name2id_map[sim_obj.name] = obj_id
|
| 30 |
+
|
| 31 |
+
def get_obj_list(self) -> List[SimObject]:
|
| 32 |
+
return list(self._objects.values())
|
| 33 |
+
|
| 34 |
+
def get_object(self, name: str = None, obj_id: int = None) -> SimObject:
|
| 35 |
+
|
| 36 |
+
if obj_id is not None:
|
| 37 |
+
name = self.get_name_from_id(obj_id)
|
| 38 |
+
return self._objects[name]
|
| 39 |
+
|
| 40 |
+
def get_id_from_name(self, name: str) -> int:
|
| 41 |
+
|
| 42 |
+
return self._name2id_map[name]
|
| 43 |
+
|
| 44 |
+
def get_name_from_id(self, obj_id: int) -> str:
|
| 45 |
+
|
| 46 |
+
return self._id2name_map[obj_id]
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/sim_object/sim_object.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
from typing import List
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class SimObject(abc.ABC):
|
| 6 |
+
|
| 7 |
+
GLOBAL_NAME_COUNTER = 0
|
| 8 |
+
|
| 9 |
+
def __init__(self, name: str = None, init_pos=None, init_quat=None):
|
| 10 |
+
|
| 11 |
+
if name is None:
|
| 12 |
+
name = "SIM_OBJ_{}".format(SimObject.GLOBAL_NAME_COUNTER)
|
| 13 |
+
|
| 14 |
+
self.name = name
|
| 15 |
+
|
| 16 |
+
self.init_pos = init_pos
|
| 17 |
+
self.init_quat = init_quat
|
| 18 |
+
|
| 19 |
+
self.obj_id = None
|
| 20 |
+
|
| 21 |
+
SimObject.GLOBAL_NAME_COUNTER += 1
|
| 22 |
+
|
| 23 |
+
@abc.abstractmethod
|
| 24 |
+
def get_poi(self) -> list:
|
| 25 |
+
|
| 26 |
+
return [self.name]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class IntelligentSimObject(SimObject, abc.ABC):
|
| 30 |
+
|
| 31 |
+
def __init__(self, name: str = None, init_pos=None, init_quat=None):
|
| 32 |
+
|
| 33 |
+
super(IntelligentSimObject, self).__init__(name, init_pos, init_quat)
|
| 34 |
+
|
| 35 |
+
self.sim = None
|
| 36 |
+
self.sim_name = None
|
| 37 |
+
|
| 38 |
+
def register_sim(self, sim, sim_name):
|
| 39 |
+
|
| 40 |
+
self.sim = sim
|
| 41 |
+
self.sim_name = sim_name
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class DummyObject(SimObject):
|
| 45 |
+
def __init__(self, pois: List[str]):
|
| 46 |
+
self.pois = pois
|
| 47 |
+
|
| 48 |
+
def get_poi(self) -> list:
|
| 49 |
+
return self.pois
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/core/time_keeper.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class TimeKeeper:
|
| 5 |
+
def __init__(self, dt) -> None:
|
| 6 |
+
self.dt = dt
|
| 7 |
+
self.step_count = 0
|
| 8 |
+
self.time_stamp = 0
|
| 9 |
+
|
| 10 |
+
def tick(self):
|
| 11 |
+
self.step_count += 1
|
| 12 |
+
self.time_stamp += self.dt
|
| 13 |
+
self.wall_clock = time.time_ns()
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_controllers.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
|
| 3 |
+
import gym
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
import d3il.environments.d3il.d3il_sim.controllers.Controller as ctrl
|
| 7 |
+
from d3il.environments.d3il.d3il_sim.controllers import CartPosQuatImpedenceController
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class GymController(abc.ABC):
|
| 11 |
+
def __init__(self, robot, controller):
|
| 12 |
+
self.controller = controller
|
| 13 |
+
self.robot = robot
|
| 14 |
+
|
| 15 |
+
@abc.abstractmethod
|
| 16 |
+
def action_space(self):
|
| 17 |
+
raise NotImplementedError
|
| 18 |
+
|
| 19 |
+
def set_action(self, action):
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
def reset(self):
|
| 23 |
+
self.controller.reset()
|
| 24 |
+
|
| 25 |
+
def execute_action(self, n_time_steps):
|
| 26 |
+
self.controller.executeControllerTimeSteps(self.robot, timeSteps=n_time_steps)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class GymJointPosController(GymController):
|
| 30 |
+
def __init__(self, robot):
|
| 31 |
+
|
| 32 |
+
super().__init__(robot, ctrl.JointPositionController())
|
| 33 |
+
|
| 34 |
+
def action_space(self):
|
| 35 |
+
low = [-2.8973, -1.7628, -2.8973, -3.0718, -2.8973, -0.0175, -2.8973]
|
| 36 |
+
high = [2.8973, 1.7628, 2.8973, -0.0698, 2.8973, 3.7525, 2.8973]
|
| 37 |
+
|
| 38 |
+
action_space = gym.spaces.Box(low=np.array(low), high=np.array(high))
|
| 39 |
+
|
| 40 |
+
return action_space
|
| 41 |
+
|
| 42 |
+
def set_action(self, action):
|
| 43 |
+
self.controller.setSetPoint(desired_pos=action)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class GymJointVelController(GymController):
|
| 47 |
+
def __init__(self, robot):
|
| 48 |
+
|
| 49 |
+
super().__init__(robot, ctrl.JointVelocityController())
|
| 50 |
+
|
| 51 |
+
def action_space(self):
|
| 52 |
+
low = [-2.175, -2.1750, -2.1750, -2.1750, -2.6100, -2.6100, -2.6100]
|
| 53 |
+
high = [2.175, 2.1750, 2.1750, 2.1750, 2.6100, 2.6100, 2.6100]
|
| 54 |
+
|
| 55 |
+
action_space = gym.spaces.Box(low=np.array(low), high=np.array(high))
|
| 56 |
+
|
| 57 |
+
return action_space
|
| 58 |
+
|
| 59 |
+
def set_action(self, action):
|
| 60 |
+
self.controller.setSetPoint(desired_vel=action)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class GymTorqueController(GymController):
|
| 64 |
+
def __init__(self, robot):
|
| 65 |
+
super().__init__(robot, ctrl.TorqueController())
|
| 66 |
+
|
| 67 |
+
def action_space(self):
|
| 68 |
+
low = [-2.175, -2.1750, -2.1750, -2.1750, -2.6100, -2.6100, -2.6100]
|
| 69 |
+
high = [2.175, 2.1750, 2.1750, 2.1750, 2.6100, 2.6100, 2.6100]
|
| 70 |
+
|
| 71 |
+
action_space = gym.spaces.Box(low=np.array(low), high=np.array(high))
|
| 72 |
+
|
| 73 |
+
return action_space
|
| 74 |
+
|
| 75 |
+
def set_action(self, action):
|
| 76 |
+
self.controller.setAction(action)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class GymCartesianVelController(GymController):
|
| 80 |
+
def __init__(self, robot, fixed_orientation=None, max_cart_vel=0.05):
|
| 81 |
+
controller = CartPosQuatImpedenceController()
|
| 82 |
+
super(GymCartesianVelController, self).__init__(robot, controller)
|
| 83 |
+
self.fixed_orientation = fixed_orientation
|
| 84 |
+
self.max_cart_vel = max_cart_vel
|
| 85 |
+
self.max_cart_pos = np.array([0.7, 0.4, 0.6])
|
| 86 |
+
self.min_cart_pos = np.array([0.2, -0.4, 0.0])
|
| 87 |
+
self.desired_c_pos = robot.des_c_pos
|
| 88 |
+
|
| 89 |
+
def action_space(self):
|
| 90 |
+
low_xyz_gripper = np.array([-1.0, -1.0, -1.0]) * self.max_cart_vel
|
| 91 |
+
high_xyz_gripper = np.array([1.0, 1.0, 1.0]) * self.max_cart_vel
|
| 92 |
+
|
| 93 |
+
quat_low_wxyz = np.array([-1, -1, -1, -1])
|
| 94 |
+
quat_high_wxyz = np.array([1, 1, 1, 1])
|
| 95 |
+
|
| 96 |
+
low_limits = low_xyz_gripper
|
| 97 |
+
high_limits = high_xyz_gripper
|
| 98 |
+
|
| 99 |
+
if self.fixed_orientation is None:
|
| 100 |
+
low_limits.append(quat_low_wxyz)
|
| 101 |
+
high_limits.append(quat_high_wxyz)
|
| 102 |
+
|
| 103 |
+
action_space = gym.spaces.Box(low=low_limits, high=high_limits)
|
| 104 |
+
return action_space
|
| 105 |
+
|
| 106 |
+
def set_action(self, action):
|
| 107 |
+
self.desired_c_pos = np.array(action[:3])
|
| 108 |
+
|
| 109 |
+
if self.fixed_orientation is None:
|
| 110 |
+
desired_quat = np.array(action[3:])
|
| 111 |
+
else:
|
| 112 |
+
desired_quat = self.fixed_orientation
|
| 113 |
+
|
| 114 |
+
self.controller.setDesiredPos(
|
| 115 |
+
np.concatenate([self.desired_c_pos, desired_quat])
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class GymXYVelController(GymController):
|
| 120 |
+
def __init__(
|
| 121 |
+
self, robot, fixed_orientation=None, fixed_z_pos=0.4, max_cart_vel=0.05
|
| 122 |
+
):
|
| 123 |
+
controller = CartPosQuatImpedenceController()
|
| 124 |
+
super(GymCartesianVelController, self).__init__(robot, controller)
|
| 125 |
+
self.fixed_orientation = fixed_orientation
|
| 126 |
+
self.fixed_z_pos = fixed_z_pos
|
| 127 |
+
self.max_cart_vel = max_cart_vel
|
| 128 |
+
self.max_cart_pos = np.array([0.7, 0.4])
|
| 129 |
+
self.min_cart_pos = np.array([0.2, -0.4])
|
| 130 |
+
self.desired_c_pos = robot.des_c_pos
|
| 131 |
+
|
| 132 |
+
def action_space(self):
|
| 133 |
+
low_xyz_gripper = np.array([-1.0, -1.0]) * self.max_cart_vel
|
| 134 |
+
high_xyz_gripper = np.array([1.0, 1.0]) * self.max_cart_vel
|
| 135 |
+
|
| 136 |
+
quat_low_wxyz = np.array([-1, -1, -1, -1])
|
| 137 |
+
quat_high_wxyz = np.array([1, 1, 1, 1])
|
| 138 |
+
|
| 139 |
+
low_limits = low_xyz_gripper
|
| 140 |
+
high_limits = high_xyz_gripper
|
| 141 |
+
|
| 142 |
+
if self.fixed_orientation is None:
|
| 143 |
+
low_limits.append(quat_low_wxyz)
|
| 144 |
+
high_limits.append(quat_high_wxyz)
|
| 145 |
+
|
| 146 |
+
action_space = gym.spaces.Box(low=low_limits, high=high_limits)
|
| 147 |
+
return action_space
|
| 148 |
+
|
| 149 |
+
def set_action(self, action):
|
| 150 |
+
self.desired_c_pos = np.array(self.robot.des_c_pos) + np.array(action[:2])
|
| 151 |
+
self.desired_c_pos = np.clip(
|
| 152 |
+
self.desired_c_pos, self.min_cart_pos, self.max_cart_pos
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
if self.fixed_orientation is None:
|
| 156 |
+
desired_quat = np.array(action[2:])
|
| 157 |
+
else:
|
| 158 |
+
desired_quat = self.fixed_orientation
|
| 159 |
+
|
| 160 |
+
self.controller.setDesiredPos(
|
| 161 |
+
np.concatenate([self.desired_c_pos, desired_quat])
|
| 162 |
+
)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_env_wrapper.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
|
| 3 |
+
import gym
|
| 4 |
+
from gym.utils import seeding
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
from d3il.environments.d3il.d3il_sim.controllers.Controller import ControllerBase
|
| 8 |
+
from d3il.environments.d3il.d3il_sim.core import Scene
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class GymEnvWrapper(gym.Env, ABC):
|
| 12 |
+
|
| 13 |
+
metadata = {"render.modes": ["human", "rgb_array"], "video.frames_per_second": 50}
|
| 14 |
+
|
| 15 |
+
def __init__(
|
| 16 |
+
self,
|
| 17 |
+
scene: Scene,
|
| 18 |
+
controller: ControllerBase,
|
| 19 |
+
max_steps_per_episode,
|
| 20 |
+
n_substeps,
|
| 21 |
+
debug: bool = False,
|
| 22 |
+
):
|
| 23 |
+
self.scene = scene
|
| 24 |
+
self.robot = scene.robots[0]
|
| 25 |
+
|
| 26 |
+
self.controller = controller
|
| 27 |
+
|
| 28 |
+
self.max_steps_per_episode = max_steps_per_episode
|
| 29 |
+
self.n_substeps = n_substeps
|
| 30 |
+
self.env_step_counter = 0
|
| 31 |
+
|
| 32 |
+
self.episode = 0
|
| 33 |
+
self.terminated = False
|
| 34 |
+
self.debug = debug
|
| 35 |
+
|
| 36 |
+
def start(self):
|
| 37 |
+
self.scene.start()
|
| 38 |
+
self.controller.reset()
|
| 39 |
+
|
| 40 |
+
def seed(self, seed=None):
|
| 41 |
+
self.np_random, seed = seeding.np_random(seed)
|
| 42 |
+
return [seed]
|
| 43 |
+
|
| 44 |
+
def step(self, action, gripper_width=None, desired_vel=None, desired_acc=None):
|
| 45 |
+
|
| 46 |
+
if gripper_width is not None:
|
| 47 |
+
self.robot.set_gripper_width = gripper_width
|
| 48 |
+
|
| 49 |
+
self.robot.open_fingers()
|
| 50 |
+
|
| 51 |
+
self.controller.setSetPoint(action)
|
| 52 |
+
self.controller.executeControllerTimeSteps(
|
| 53 |
+
self.robot, self.n_substeps, block=False
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
for i in range(self.n_substeps):
|
| 57 |
+
self.scene.next_step()
|
| 58 |
+
|
| 59 |
+
observation = self.get_observation()
|
| 60 |
+
reward = self.get_reward()
|
| 61 |
+
done = self.is_finished()
|
| 62 |
+
|
| 63 |
+
debug_info = {}
|
| 64 |
+
if self.debug:
|
| 65 |
+
debug_info = self.debug_msg()
|
| 66 |
+
|
| 67 |
+
self.env_step_counter += 1
|
| 68 |
+
return observation, reward, done, debug_info
|
| 69 |
+
|
| 70 |
+
@abstractmethod
|
| 71 |
+
def get_observation(self) -> np.ndarray:
|
| 72 |
+
raise NotImplementedError
|
| 73 |
+
|
| 74 |
+
@abstractmethod
|
| 75 |
+
def get_reward(self):
|
| 76 |
+
raise NotImplementedError
|
| 77 |
+
|
| 78 |
+
@abstractmethod
|
| 79 |
+
def _check_early_termination(self) -> bool:
|
| 80 |
+
return self.terminated
|
| 81 |
+
|
| 82 |
+
def is_finished(self):
|
| 83 |
+
if (
|
| 84 |
+
self.terminated
|
| 85 |
+
or self._check_early_termination()
|
| 86 |
+
or self.env_step_counter >= self.max_steps_per_episode - 1
|
| 87 |
+
):
|
| 88 |
+
return True
|
| 89 |
+
return False
|
| 90 |
+
|
| 91 |
+
def debug_msg(self) -> dict:
|
| 92 |
+
return {}
|
| 93 |
+
|
| 94 |
+
@abstractmethod
|
| 95 |
+
def _reset_env(self):
|
| 96 |
+
raise NotImplementedError
|
| 97 |
+
|
| 98 |
+
def reset(self):
|
| 99 |
+
self.terminated = False
|
| 100 |
+
self.env_step_counter = 0
|
| 101 |
+
self.episode += 1
|
| 102 |
+
obs = self._reset_env()
|
| 103 |
+
|
| 104 |
+
return obs
|
| 105 |
+
|
| 106 |
+
def robot_state(self):
|
| 107 |
+
self.robot.receiveState()
|
| 108 |
+
|
| 109 |
+
joint_pos = self.robot.current_j_pos
|
| 110 |
+
joint_vel = self.robot.current_j_vel
|
| 111 |
+
|
| 112 |
+
gripper_vel = self.robot.current_fing_vel
|
| 113 |
+
gripper_width = [self.robot.gripper_width]
|
| 114 |
+
|
| 115 |
+
tcp_pos = self.robot.current_c_pos
|
| 116 |
+
tcp_vel = self.robot.current_c_vel
|
| 117 |
+
tcp_quad = self.robot.current_c_quat
|
| 118 |
+
|
| 119 |
+
return tcp_pos
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_utils/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_utils/helpers.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def obj_distance(obj1_pos, obj2_pos):
|
| 5 |
+
dist = np.linalg.norm(obj1_pos - obj2_pos)
|
| 6 |
+
rel_dist = (obj1_pos - obj2_pos) / dist + 1e-8
|
| 7 |
+
return dist, rel_dist
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/gyms/gym_utils/rewards.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class Distance:
|
| 5 |
+
@staticmethod
|
| 6 |
+
def linear(start, goal):
|
| 7 |
+
return np.linalg.norm(start - goal, axis=-1)
|
| 8 |
+
|
| 9 |
+
@staticmethod
|
| 10 |
+
def quadratic(start, goal): ...
|
| 11 |
+
|
| 12 |
+
@staticmethod
|
| 13 |
+
def tanh(start, goal): ...
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/SimFactory.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
from functools import wraps
|
| 3 |
+
|
| 4 |
+
from d3il.environments.d3il.d3il_sim.core import Camera, RobotBase, Scene
|
| 5 |
+
from d3il.environments.d3il.d3il_sim.sims.universal_sim.PrimitiveObjects import (
|
| 6 |
+
PrimitiveObject,
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def _register_function_on_class(cls, fn):
|
| 11 |
+
|
| 12 |
+
@wraps(fn)
|
| 13 |
+
def self_wrapper(self, *args, **kwargs):
|
| 14 |
+
return fn(self, *args, **kwargs)
|
| 15 |
+
|
| 16 |
+
setattr(cls, fn.__name__, self_wrapper)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class SimFactory(abc.ABC):
|
| 20 |
+
|
| 21 |
+
def __init__(self) -> None:
|
| 22 |
+
super().__init__()
|
| 23 |
+
fn = self.prim_loading()
|
| 24 |
+
if fn is not None:
|
| 25 |
+
_register_function_on_class(PrimitiveObject, fn)
|
| 26 |
+
|
| 27 |
+
@abc.abstractmethod
|
| 28 |
+
def create_scene(
|
| 29 |
+
self,
|
| 30 |
+
gin_config=None,
|
| 31 |
+
object_list: list = None,
|
| 32 |
+
dt: float = 0.001,
|
| 33 |
+
render: Scene.RenderMode = Scene.RenderMode.HUMAN,
|
| 34 |
+
*args,
|
| 35 |
+
**kwargs
|
| 36 |
+
) -> Scene:
|
| 37 |
+
pass
|
| 38 |
+
|
| 39 |
+
@abc.abstractmethod
|
| 40 |
+
def create_robot(self, scene, *args, **kwargs) -> RobotBase:
|
| 41 |
+
pass
|
| 42 |
+
|
| 43 |
+
@abc.abstractmethod
|
| 44 |
+
def create_camera(
|
| 45 |
+
self,
|
| 46 |
+
name: str,
|
| 47 |
+
width: int = 1000,
|
| 48 |
+
height: int = 1000,
|
| 49 |
+
init_pos=None,
|
| 50 |
+
init_quat=None,
|
| 51 |
+
*args,
|
| 52 |
+
**kwargs
|
| 53 |
+
) -> Camera.Camera:
|
| 54 |
+
pass
|
| 55 |
+
|
| 56 |
+
@abc.abstractmethod
|
| 57 |
+
def prim_loading(self):
|
| 58 |
+
return None
|
| 59 |
+
|
| 60 |
+
@property
|
| 61 |
+
def RenderMode(self):
|
| 62 |
+
return Scene.RenderMode
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class SimRepository:
|
| 66 |
+
|
| 67 |
+
_repository = {}
|
| 68 |
+
|
| 69 |
+
@classmethod
|
| 70 |
+
def register(cls, factory: SimFactory, sim_name: str):
|
| 71 |
+
if sim_name in cls._repository:
|
| 72 |
+
return
|
| 73 |
+
cls._repository[sim_name] = factory
|
| 74 |
+
|
| 75 |
+
@classmethod
|
| 76 |
+
def get_factory(cls, sim_name: str) -> SimFactory:
|
| 77 |
+
return cls._repository[sim_name]
|
| 78 |
+
|
| 79 |
+
@classmethod
|
| 80 |
+
def list_all_sims(cls):
|
| 81 |
+
return cls._repository.keys()
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .mj_beta import *
|
| 2 |
+
|
| 3 |
+
try:
|
| 4 |
+
from .mj_beta import MjCamera
|
| 5 |
+
except ImportError:
|
| 6 |
+
pass
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjCamera.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Tuple
|
| 2 |
+
from xml.etree import ElementTree as Et
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
from mujoco import MjData, MjModel, mj_name2id, mjtObj
|
| 6 |
+
|
| 7 |
+
from d3il.environments.d3il.d3il_sim.core.Camera import Camera
|
| 8 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_helper import IncludeType
|
| 9 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_render_singleton import (
|
| 10 |
+
render,
|
| 11 |
+
)
|
| 12 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.MjLoadable import MjLoadable
|
| 13 |
+
from d3il.environments.d3il.d3il_sim.utils.geometric_transformation import (
|
| 14 |
+
mat2posQuat,
|
| 15 |
+
posRotMat2TFMat,
|
| 16 |
+
quat2mat,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class MjCamera(Camera, MjLoadable):
|
| 21 |
+
def __init__(
|
| 22 |
+
self,
|
| 23 |
+
name: str,
|
| 24 |
+
width: int = 1000,
|
| 25 |
+
height: int = 1000,
|
| 26 |
+
init_pos=None,
|
| 27 |
+
init_quat=None,
|
| 28 |
+
near=0.01,
|
| 29 |
+
far=10,
|
| 30 |
+
*args,
|
| 31 |
+
**kwargs,
|
| 32 |
+
):
|
| 33 |
+
super(MjCamera, self).__init__(
|
| 34 |
+
name, width, height, init_pos, init_quat, near=near, far=far
|
| 35 |
+
)
|
| 36 |
+
self.viewer = None
|
| 37 |
+
self.data = None
|
| 38 |
+
self.model = None
|
| 39 |
+
|
| 40 |
+
def register_sim(self, sim: Tuple[MjModel, MjData], sim_name: str):
|
| 41 |
+
self.model = sim[0]
|
| 42 |
+
self.data = sim[1]
|
| 43 |
+
super().register_sim(sim, sim_name)
|
| 44 |
+
|
| 45 |
+
extent = self.model.stat.extent
|
| 46 |
+
self.near = self.model.vis.map.znear * extent
|
| 47 |
+
self.far = self.model.vis.map.zfar * extent
|
| 48 |
+
|
| 49 |
+
cam_id = mj_name2id(m=self.model, name=self.name, type=mjtObj.mjOBJ_CAMERA)
|
| 50 |
+
self.fovy = self.model.cam_fovy[cam_id]
|
| 51 |
+
self.fovx = (
|
| 52 |
+
2
|
| 53 |
+
* np.arctan(
|
| 54 |
+
self.width
|
| 55 |
+
* 0.5
|
| 56 |
+
/ (self.height * 0.5 / np.tan(self.fovy * np.pi / 360 / 2))
|
| 57 |
+
)
|
| 58 |
+
/ np.pi
|
| 59 |
+
* 360
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
self.fx = (self.width / 2) / (np.tan(self.fovx * np.pi / 180 / 2))
|
| 63 |
+
self.fy = (self.height / 2) / (np.tan(self.fovy * np.pi / 180 / 2))
|
| 64 |
+
self.cx = self.width / 2
|
| 65 |
+
self.cy = self.height / 2
|
| 66 |
+
|
| 67 |
+
self.intrinsics = np.array(
|
| 68 |
+
[[self.fx, 0, self.cx], [0, self.fy, self.cy], [0, 0, 1]]
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
return
|
| 72 |
+
|
| 73 |
+
def _get_img_data(
|
| 74 |
+
self,
|
| 75 |
+
width: int = None,
|
| 76 |
+
height: int = None,
|
| 77 |
+
depth: bool = True,
|
| 78 |
+
denormalize_depth: bool = True,
|
| 79 |
+
segmentation: bool = False,
|
| 80 |
+
):
|
| 81 |
+
if width is None:
|
| 82 |
+
width = self.width
|
| 83 |
+
if height is None:
|
| 84 |
+
height = self.height
|
| 85 |
+
|
| 86 |
+
rendered_images = render(
|
| 87 |
+
cam_name=self.name,
|
| 88 |
+
width=width,
|
| 89 |
+
height=height,
|
| 90 |
+
depth=depth,
|
| 91 |
+
segmentation=segmentation,
|
| 92 |
+
model=self.model,
|
| 93 |
+
data=self.data,
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
if depth:
|
| 97 |
+
color_img, depth_img = rendered_images
|
| 98 |
+
color_img = self.vertical_flip(color_img)
|
| 99 |
+
depth_img = self.vertical_flip(depth_img)
|
| 100 |
+
if denormalize_depth:
|
| 101 |
+
depth_img = self.denormalize_depth(depth_img)
|
| 102 |
+
return color_img, depth_img
|
| 103 |
+
elif segmentation:
|
| 104 |
+
seg_img = rendered_images
|
| 105 |
+
seg_img = self.vertical_flip(seg_img)
|
| 106 |
+
return seg_img
|
| 107 |
+
else:
|
| 108 |
+
color_img = rendered_images
|
| 109 |
+
color_img = self.vertical_flip(color_img)
|
| 110 |
+
return color_img
|
| 111 |
+
|
| 112 |
+
def get_cart_pos_quat(self):
|
| 113 |
+
cam_id = mj_name2id(m=self.model, name=self.name, type=mjtObj.mjOBJ_CAMERA)
|
| 114 |
+
|
| 115 |
+
cam_pos = self.data.cam_xpos[cam_id]
|
| 116 |
+
cam_mat = self.data.cam_xmat[cam_id]
|
| 117 |
+
c2b_r = cam_mat.reshape((3, 3))
|
| 118 |
+
b2w_r = quat2mat([0, 1, 0, 0])
|
| 119 |
+
c2w_r = np.matmul(c2b_r, b2w_r)
|
| 120 |
+
c2w = posRotMat2TFMat(cam_pos, c2w_r)
|
| 121 |
+
pos, quat = mat2posQuat(c2w)
|
| 122 |
+
|
| 123 |
+
return pos, quat
|
| 124 |
+
|
| 125 |
+
def mj_load(self):
|
| 126 |
+
pos_str = " ".join(map(str, self.init_pos))
|
| 127 |
+
quat_str = " ".join(map(str, self.init_quat))
|
| 128 |
+
|
| 129 |
+
object_body = Et.Element("body")
|
| 130 |
+
object_body.set("pos", pos_str)
|
| 131 |
+
object_body.set("quat", quat_str)
|
| 132 |
+
object_body.set("name", self.name)
|
| 133 |
+
|
| 134 |
+
cam = Et.SubElement(object_body, "camera")
|
| 135 |
+
cam.set("name", self.name)
|
| 136 |
+
cam.set("fovy", str(self.fovy))
|
| 137 |
+
|
| 138 |
+
return object_body, {}, IncludeType.WORLD_BODY
|
| 139 |
+
|
| 140 |
+
def get_segmentation(self, width=None, height=None, depth=True):
|
| 141 |
+
data = super(MjCamera, self).get_segmentation(width, height, depth)
|
| 142 |
+
|
| 143 |
+
if depth:
|
| 144 |
+
data, d = data
|
| 145 |
+
|
| 146 |
+
obj_types = data[..., 0]
|
| 147 |
+
obj_ids = self.model.geom_bodyid[data[..., 1]]
|
| 148 |
+
|
| 149 |
+
if depth:
|
| 150 |
+
return obj_ids, d
|
| 151 |
+
return obj_ids
|
| 152 |
+
|
| 153 |
+
def vertical_flip(self, img):
|
| 154 |
+
return np.flipud(img)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
class MjInhandCamera(MjCamera):
|
| 158 |
+
|
| 159 |
+
def __init__(
|
| 160 |
+
self,
|
| 161 |
+
robot_cam_id: str,
|
| 162 |
+
width: int = 96,
|
| 163 |
+
height: int = 96,
|
| 164 |
+
*args,
|
| 165 |
+
**kwargs,
|
| 166 |
+
):
|
| 167 |
+
super().__init__(
|
| 168 |
+
robot_cam_id,
|
| 169 |
+
width,
|
| 170 |
+
height,
|
| 171 |
+
*args,
|
| 172 |
+
**kwargs,
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
def mj_load(self):
|
| 176 |
+
|
| 177 |
+
return None, None, IncludeType.VIRTUAL_INCLUDE
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class MjCageCam(MjCamera):
|
| 181 |
+
|
| 182 |
+
def __init__(self, width: int = 1000, height: int = 1000, *args, **kwargs):
|
| 183 |
+
super().__init__(
|
| 184 |
+
"rgbd_cage",
|
| 185 |
+
width,
|
| 186 |
+
height,
|
| 187 |
+
init_pos=[0.7, 0.1, 0.9],
|
| 188 |
+
init_quat=[
|
| 189 |
+
0.6830127,
|
| 190 |
+
0.1830127,
|
| 191 |
+
0.1830127,
|
| 192 |
+
0.683012,
|
| 193 |
+
],
|
| 194 |
+
*args,
|
| 195 |
+
**kwargs,
|
| 196 |
+
)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjFactory.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gin
|
| 2 |
+
|
| 3 |
+
import d3il.environments.d3il.d3il_sim.sims.SimFactory as Sims
|
| 4 |
+
from d3il.environments.d3il.d3il_sim.core import Camera, RobotBase, Scene
|
| 5 |
+
from d3il.environments.d3il.d3il_sim.utils.sim_path import d3il_path
|
| 6 |
+
|
| 7 |
+
from .MjCamera import MjCamera
|
| 8 |
+
from .MjPrimLoader import mj_load
|
| 9 |
+
from .MjRobot import MjRobot
|
| 10 |
+
from .MjScene import MjScene
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class MjFactory(Sims.SimFactory):
|
| 14 |
+
def create_scene(
|
| 15 |
+
self,
|
| 16 |
+
gin_path=None,
|
| 17 |
+
object_list: list = None,
|
| 18 |
+
dt: float = 0.001,
|
| 19 |
+
render: Scene.RenderMode = Scene.RenderMode.HUMAN,
|
| 20 |
+
*args,
|
| 21 |
+
**kwargs
|
| 22 |
+
) -> Scene:
|
| 23 |
+
|
| 24 |
+
if gin_path is None:
|
| 25 |
+
gin_path = d3il_path(
|
| 26 |
+
"d3il_sim/controllers/Config/mujoco_controller_config.gin"
|
| 27 |
+
)
|
| 28 |
+
gin.parse_config_file(gin_path)
|
| 29 |
+
return MjScene(object_list, dt, render, *args, **kwargs)
|
| 30 |
+
|
| 31 |
+
def create_robot(self, scene, *args, **kwargs) -> RobotBase:
|
| 32 |
+
return MjRobot(scene, *args, **kwargs)
|
| 33 |
+
|
| 34 |
+
def create_camera(
|
| 35 |
+
self,
|
| 36 |
+
name: str,
|
| 37 |
+
width: int = 1000,
|
| 38 |
+
height: int = 1000,
|
| 39 |
+
init_pos=None,
|
| 40 |
+
init_quat=None,
|
| 41 |
+
*args,
|
| 42 |
+
**kwargs
|
| 43 |
+
) -> Camera:
|
| 44 |
+
return MjCamera(name, width, height, init_pos, init_quat)
|
| 45 |
+
|
| 46 |
+
def prim_loading(self):
|
| 47 |
+
return mj_load
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
Sims.SimRepository.register(MjFactory(), "mj_beta")
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjLoadable.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import abc
|
| 2 |
+
import os
|
| 3 |
+
import xml.etree.ElementTree as Et
|
| 4 |
+
from typing import Tuple
|
| 5 |
+
|
| 6 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_helper import IncludeType
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class MjLoadable(abc.ABC):
|
| 10 |
+
|
| 11 |
+
@abc.abstractmethod
|
| 12 |
+
def mj_load(self) -> Tuple[Et.Element, list, IncludeType]:
|
| 13 |
+
raise NotImplementedError
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class MjXmlLoadable(MjLoadable):
|
| 17 |
+
|
| 18 |
+
def __init__(self, full_xml_path, asset_path=None) -> None:
|
| 19 |
+
super().__init__()
|
| 20 |
+
self._full_xml_path = full_xml_path
|
| 21 |
+
self._asset_path = asset_path
|
| 22 |
+
|
| 23 |
+
@property
|
| 24 |
+
def loadable_dir(self):
|
| 25 |
+
return os.path.split(self._full_xml_path)[0]
|
| 26 |
+
|
| 27 |
+
@property
|
| 28 |
+
def asset_path(self):
|
| 29 |
+
if self._asset_path is not None:
|
| 30 |
+
return self._asset_path
|
| 31 |
+
return os.path.join(self.loadable_dir, "assets")
|
| 32 |
+
|
| 33 |
+
@property
|
| 34 |
+
def file_name(self):
|
| 35 |
+
return os.path.split(self._full_xml_path)[1]
|
| 36 |
+
|
| 37 |
+
def mj_load(self) -> Tuple[Et.Element, list, IncludeType]:
|
| 38 |
+
et_include = Et.Element("include")
|
| 39 |
+
et_include.set("file", self.file_name)
|
| 40 |
+
assets = {}
|
| 41 |
+
|
| 42 |
+
if os.path.isdir(self.asset_path):
|
| 43 |
+
for dirpath, dirnames, files in os.walk(self.asset_path):
|
| 44 |
+
for f in files:
|
| 45 |
+
with open(os.path.join(dirpath, f), "rb") as file:
|
| 46 |
+
assets[f] = file.read()
|
| 47 |
+
|
| 48 |
+
with open(os.path.join(self._full_xml_path), "rb") as file:
|
| 49 |
+
assets[self.file_name] = file.read()
|
| 50 |
+
return et_include, assets, IncludeType.FILE_INCLUDE
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class MjIncludeTemplate(MjXmlLoadable):
|
| 54 |
+
def __init__(self, full_xml_path, asset_path=None) -> None:
|
| 55 |
+
super().__init__(full_xml_path, asset_path)
|
| 56 |
+
self._tmp_filled_xml = None
|
| 57 |
+
|
| 58 |
+
@abc.abstractmethod
|
| 59 |
+
def modify_template(self, et: Et.ElementTree) -> Et.ElementTree:
|
| 60 |
+
pass
|
| 61 |
+
|
| 62 |
+
def mj_load(self) -> Tuple[Et.Element, list, IncludeType]:
|
| 63 |
+
inc, assets, include_type = super().mj_load()
|
| 64 |
+
|
| 65 |
+
obj = Et.parse(self._full_xml_path)
|
| 66 |
+
new_xml = self.modify_template(obj)
|
| 67 |
+
|
| 68 |
+
self._tmp_filled_xml = new_xml
|
| 69 |
+
|
| 70 |
+
et_include = Et.Element("include")
|
| 71 |
+
et_include.set("file", new_xml)
|
| 72 |
+
return et_include, assets, include_type
|
| 73 |
+
|
| 74 |
+
def cleanup(self):
|
| 75 |
+
if self._tmp_filled_xml is not None:
|
| 76 |
+
os.remove(self._tmp_filled_xml)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class MjFreezable:
|
| 80 |
+
@abc.abstractmethod
|
| 81 |
+
def freeze(self, data, model):
|
| 82 |
+
pass
|
| 83 |
+
|
| 84 |
+
@abc.abstractmethod
|
| 85 |
+
def unfreeze(self, data, model):
|
| 86 |
+
pass
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjPrimLoader.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import xml.etree.ElementTree as Et
|
| 2 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_helper import IncludeType
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def mj_load(prim_obj, *args, **kwargs):
|
| 6 |
+
pos_str = " ".join(map(str, prim_obj.init_pos))
|
| 7 |
+
orientation_str = " ".join(map(str, prim_obj.init_quat))
|
| 8 |
+
mass_str = str(prim_obj.mass)
|
| 9 |
+
size_str = " ".join(map(str, prim_obj.size))
|
| 10 |
+
rgba_str = " ".join(map(str, prim_obj.rgba))
|
| 11 |
+
|
| 12 |
+
object_body = Et.Element("body")
|
| 13 |
+
object_body.set("name", prim_obj.name)
|
| 14 |
+
object_body.set("pos", pos_str)
|
| 15 |
+
object_body.set("quat", orientation_str)
|
| 16 |
+
|
| 17 |
+
geom = Et.SubElement(object_body, "geom")
|
| 18 |
+
geom.set("type", prim_obj.type.value)
|
| 19 |
+
geom.set("name", "{}:geom".format(prim_obj.name))
|
| 20 |
+
if prim_obj.mass:
|
| 21 |
+
geom.set("mass", mass_str)
|
| 22 |
+
geom.set("size", size_str)
|
| 23 |
+
geom.set("rgba", rgba_str)
|
| 24 |
+
|
| 25 |
+
if prim_obj.visual_only:
|
| 26 |
+
geom.set("contype", "0")
|
| 27 |
+
geom.set("conaffinity", "0")
|
| 28 |
+
|
| 29 |
+
if not prim_obj.static:
|
| 30 |
+
Et.SubElement(object_body, "freejoint")
|
| 31 |
+
|
| 32 |
+
if prim_obj.solimp is not None:
|
| 33 |
+
geom.set("solimp", " ".join(prim_obj.solimp))
|
| 34 |
+
if prim_obj.solref is not None:
|
| 35 |
+
geom.set("solref", " ".join(prim_obj.solref))
|
| 36 |
+
|
| 37 |
+
inc_type = IncludeType.WORLD_BODY
|
| 38 |
+
return object_body, {}, inc_type
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjRobot.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import xml.etree.ElementTree as Et
|
| 2 |
+
|
| 3 |
+
import mujoco
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
from d3il.environments.d3il.d3il_sim.controllers.Controller import (
|
| 7 |
+
ModelBasedFeedforwardController,
|
| 8 |
+
)
|
| 9 |
+
from d3il.environments.d3il.d3il_sim.core import RobotBase
|
| 10 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_helper import (
|
| 11 |
+
IncludeType,
|
| 12 |
+
get_body_xvelp,
|
| 13 |
+
get_body_xvelr,
|
| 14 |
+
)
|
| 15 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.MjCamera import MjInhandCamera
|
| 16 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.MjLoadable import (
|
| 17 |
+
MjIncludeTemplate,
|
| 18 |
+
MjXmlLoadable,
|
| 19 |
+
)
|
| 20 |
+
from d3il.environments.d3il.d3il_sim.utils import sim_path
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class MjRobot(RobotBase, MjIncludeTemplate):
|
| 24 |
+
GLOBAL_MJ_ROBOT_COUNTER = 0
|
| 25 |
+
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
scene,
|
| 29 |
+
dt=1e-3,
|
| 30 |
+
num_DoF=7,
|
| 31 |
+
base_position=None,
|
| 32 |
+
base_orientation=None,
|
| 33 |
+
gravity_comp=True,
|
| 34 |
+
clip_actions=False,
|
| 35 |
+
root=sim_path.D3IL_DIR,
|
| 36 |
+
xml_path=None,
|
| 37 |
+
):
|
| 38 |
+
RobotBase.__init__(self, scene, dt, num_DoF, base_position, base_orientation)
|
| 39 |
+
|
| 40 |
+
if xml_path is None:
|
| 41 |
+
xml_path = sim_path.d3il_path("models/mj/robot/panda.xml")
|
| 42 |
+
MjXmlLoadable.__init__(self, xml_path)
|
| 43 |
+
|
| 44 |
+
self.clip_actions = clip_actions
|
| 45 |
+
self.gravity_comp = gravity_comp
|
| 46 |
+
|
| 47 |
+
self.joint_names = None
|
| 48 |
+
self.joint_indices = None
|
| 49 |
+
self.joint_act_indices = None
|
| 50 |
+
|
| 51 |
+
self.gripper_names = None
|
| 52 |
+
self.gripper_indices = None
|
| 53 |
+
self.gripper_act_indices = None
|
| 54 |
+
|
| 55 |
+
self.jointTrackingController = ModelBasedFeedforwardController()
|
| 56 |
+
|
| 57 |
+
self._mj_robot_id = MjRobot.GLOBAL_MJ_ROBOT_COUNTER
|
| 58 |
+
MjRobot.GLOBAL_MJ_ROBOT_COUNTER += 1
|
| 59 |
+
self.root = root
|
| 60 |
+
|
| 61 |
+
self.inhand_cam = MjInhandCamera(self.add_id2model_key("rgbd"))
|
| 62 |
+
|
| 63 |
+
self.qpos_freeze = None
|
| 64 |
+
self.qvel_freeze = None
|
| 65 |
+
self.ctrl_freeze = None
|
| 66 |
+
|
| 67 |
+
def _getJacobian_internal(self, q=None):
|
| 68 |
+
jac = np.zeros((6, 7))
|
| 69 |
+
if q is not None:
|
| 70 |
+
|
| 71 |
+
cur_sim_state = self.scene.sim.get_state()
|
| 72 |
+
qpos_idx = self.joint_indices
|
| 73 |
+
self.scene.sim.data.qpos[qpos_idx] = q
|
| 74 |
+
mujoco.mj_kinematics(self.scene.model, self.scene.sim.data)
|
| 75 |
+
mujoco.mj_comPos(self.scene.model, self.scene.sim.data)
|
| 76 |
+
|
| 77 |
+
tcp_id = self.add_id2model_key("tcp")
|
| 78 |
+
jac[:3, :] = self.scene.sim.data.get_body_jacp(tcp_id).reshape((3, -1))[
|
| 79 |
+
:, -9:-2
|
| 80 |
+
]
|
| 81 |
+
jac[3:, :] = self.scene.sim.data.get_body_jacr(tcp_id).reshape((3, -1))[
|
| 82 |
+
:, -9:-2
|
| 83 |
+
]
|
| 84 |
+
|
| 85 |
+
if q is not None:
|
| 86 |
+
self.scene.sim.set_state(cur_sim_state)
|
| 87 |
+
return jac
|
| 88 |
+
|
| 89 |
+
def _getForwardKinematics_internal(self, q=None):
|
| 90 |
+
if q is not None:
|
| 91 |
+
cur_sim_state = self.scene.sim.get_state()
|
| 92 |
+
qpos_idx = self.joint_indices
|
| 93 |
+
self.scene.data.qpos[qpos_idx] = q
|
| 94 |
+
mujoco.mj_kinematics(self.scene.model, self.scene.data)
|
| 95 |
+
|
| 96 |
+
tcp_id = self.add_id2model_key("tcp")
|
| 97 |
+
cart_pos = self.scene.data.get_body_xpos(tcp_id)
|
| 98 |
+
cart_or = self.scene.data.get_body_xquat(tcp_id)
|
| 99 |
+
if q is not None:
|
| 100 |
+
self.scene.sim.set_state(cur_sim_state)
|
| 101 |
+
return cart_pos, cart_or
|
| 102 |
+
|
| 103 |
+
def prepare_step(self):
|
| 104 |
+
self.command = self.activeController.getControl(self)
|
| 105 |
+
|
| 106 |
+
self.preprocessCommand(self.command)
|
| 107 |
+
self.scene.data.ctrl[self.joint_act_indices] = self.uff.copy()[: self.num_DoF]
|
| 108 |
+
self.scene.data.ctrl[self.gripper_act_indices] = self.finger_commands
|
| 109 |
+
self.receiveState()
|
| 110 |
+
|
| 111 |
+
def receiveState(self):
|
| 112 |
+
if self.joint_indices is None:
|
| 113 |
+
self._init_jnt_indices()
|
| 114 |
+
|
| 115 |
+
tcp_name = self.add_id2model_key("tcp")
|
| 116 |
+
|
| 117 |
+
self.current_j_pos = np.array(
|
| 118 |
+
[self.scene.data.joint(name).qpos.copy() for name in self.joint_names]
|
| 119 |
+
).squeeze()
|
| 120 |
+
self.current_j_vel = np.array(
|
| 121 |
+
[self.scene.data.joint(name).qvel.copy() for name in self.joint_names]
|
| 122 |
+
).squeeze()
|
| 123 |
+
|
| 124 |
+
test = self.scene.data.body(tcp_name)
|
| 125 |
+
self.current_c_pos_global = self.scene.data.body(tcp_name).xpos.copy()
|
| 126 |
+
self.current_c_vel_global = get_body_xvelp(
|
| 127 |
+
self.scene.model,
|
| 128 |
+
self.scene.data,
|
| 129 |
+
tcp_name,
|
| 130 |
+
)
|
| 131 |
+
self.current_c_quat_global = self.scene.data.body(tcp_name).xquat.copy()
|
| 132 |
+
self.current_c_quat_vel_global = np.zeros(4)
|
| 133 |
+
|
| 134 |
+
self.current_c_quat_vel_global[1:] = get_body_xvelr(
|
| 135 |
+
self.scene.model, self.scene.data, tcp_name
|
| 136 |
+
)
|
| 137 |
+
self.current_c_quat_vel_global *= 0.5 * self.current_c_quat_global
|
| 138 |
+
|
| 139 |
+
self.current_c_pos, self.current_c_quat = self._localize_cart_coords(
|
| 140 |
+
self.current_c_pos_global, self.current_c_quat_global
|
| 141 |
+
)
|
| 142 |
+
self.current_c_vel, _ = self._localize_cart_coords(
|
| 143 |
+
self.current_c_vel_global + self.base_position
|
| 144 |
+
)
|
| 145 |
+
_, self.current_c_quat_vel = self._localize_cart_coords(
|
| 146 |
+
self.base_position, self.current_c_quat_vel_global
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
self.current_fing_pos = np.array(
|
| 150 |
+
[self.scene.data.joint(name).qpos.copy() for name in self.gripper_names]
|
| 151 |
+
).squeeze()
|
| 152 |
+
self.current_fing_vel = np.array(
|
| 153 |
+
[self.scene.data.joint(name).qvel.copy() for name in self.gripper_names]
|
| 154 |
+
).squeeze()
|
| 155 |
+
self.gripper_width = self.current_fing_pos[-2] + self.current_fing_pos[-1]
|
| 156 |
+
|
| 157 |
+
def get_command_from_inverse_dynamics(self, target_j_acc, mj_calc_inv=False):
|
| 158 |
+
if mj_calc_inv:
|
| 159 |
+
self.scene.data.qacc[self.joint_vel_indices + self.gripper_vel_indices] = (
|
| 160 |
+
target_j_acc
|
| 161 |
+
)
|
| 162 |
+
mujoco.mj_inverse(self.scene.model, self.scene.sim.data)
|
| 163 |
+
return self.scene.data.qfrc_inverse[
|
| 164 |
+
self.joint_vel_indices + self.gripper_vel_indices
|
| 165 |
+
]
|
| 166 |
+
else:
|
| 167 |
+
return self.scene.data.qfrc_bias[
|
| 168 |
+
self.joint_vel_indices + self.gripper_vel_indices
|
| 169 |
+
]
|
| 170 |
+
|
| 171 |
+
def get_init_qpos(self):
|
| 172 |
+
return np.array(
|
| 173 |
+
[
|
| 174 |
+
3.57795216e-09,
|
| 175 |
+
1.74532920e-01,
|
| 176 |
+
3.30500960e-08,
|
| 177 |
+
-8.72664630e-01,
|
| 178 |
+
-1.14096181e-07,
|
| 179 |
+
1.22173047e00,
|
| 180 |
+
7.85398126e-01,
|
| 181 |
+
]
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
def _init_jnt_indices(self):
|
| 185 |
+
n_joints = len(self.joint_names)
|
| 186 |
+
assert (
|
| 187 |
+
n_joints == self.num_DoF
|
| 188 |
+
), "Error, found {} joints, but expected {}".format(n_joints, self.num_DoF)
|
| 189 |
+
assert (
|
| 190 |
+
len(self.gripper_names) == 2
|
| 191 |
+
), "Error, found more gripper joints than expected."
|
| 192 |
+
|
| 193 |
+
self.joint_indices = []
|
| 194 |
+
self.joint_vel_indices = []
|
| 195 |
+
self.gripper_indices = []
|
| 196 |
+
self.gripper_vel_indices = []
|
| 197 |
+
for jnt_name in self.joint_names:
|
| 198 |
+
jnt_id = mujoco.mj_name2id(
|
| 199 |
+
self.scene.model, type=mujoco.mjtObj.mjOBJ_JOINT, name=jnt_name
|
| 200 |
+
)
|
| 201 |
+
self.joint_indices.append(self.scene.model.jnt_qposadr[jnt_id])
|
| 202 |
+
self.joint_vel_indices.append(self.scene.model.jnt_dofadr[jnt_id])
|
| 203 |
+
for grp_name in self.gripper_names:
|
| 204 |
+
grp_id = mujoco.mj_name2id(
|
| 205 |
+
self.scene.model, type=mujoco.mjtObj.mjOBJ_JOINT, name=grp_name
|
| 206 |
+
)
|
| 207 |
+
self.gripper_indices.append(self.scene.model.jnt_qposadr[grp_id])
|
| 208 |
+
self.gripper_vel_indices.append(self.scene.model.jnt_dofadr[grp_id])
|
| 209 |
+
self.joint_act_indices = [
|
| 210 |
+
mujoco.mj_name2id(
|
| 211 |
+
self.scene.model, type=mujoco.mjtObj.mjOBJ_ACTUATOR, name=name + "_act"
|
| 212 |
+
)
|
| 213 |
+
for name in self.joint_names
|
| 214 |
+
]
|
| 215 |
+
self.gripper_act_indices = [
|
| 216 |
+
mujoco.mj_name2id(
|
| 217 |
+
self.scene.model, type=mujoco.mjtObj.mjOBJ_ACTUATOR, name=name + "_act"
|
| 218 |
+
)
|
| 219 |
+
for name in self.gripper_names
|
| 220 |
+
]
|
| 221 |
+
|
| 222 |
+
def set_q(self, joint_pos):
|
| 223 |
+
if self.joint_indices is None:
|
| 224 |
+
self._init_jnt_indices()
|
| 225 |
+
|
| 226 |
+
qpos = self.scene.data.qpos.copy()
|
| 227 |
+
qpos[self.joint_indices] = joint_pos
|
| 228 |
+
|
| 229 |
+
qvel = self.scene.data.qvel.copy()
|
| 230 |
+
qvel[self.joint_vel_indices] = np.zeros(len(self.joint_vel_indices))
|
| 231 |
+
self.scene.set_state(qpos, qvel)
|
| 232 |
+
|
| 233 |
+
def add_id2model_key(self, model_key_id: str) -> str:
|
| 234 |
+
attrib_split = model_key_id.split("_")
|
| 235 |
+
attrib_split.insert(1, "rb{}".format(self._mj_robot_id))
|
| 236 |
+
attrib_id = "_".join(attrib_split)
|
| 237 |
+
return attrib_id
|
| 238 |
+
|
| 239 |
+
def modify_template(self, et: Et.ElementTree) -> str:
|
| 240 |
+
self.joint_names = None
|
| 241 |
+
self.joint_indices = None
|
| 242 |
+
self.joint_act_indices = None
|
| 243 |
+
|
| 244 |
+
self.gripper_names = None
|
| 245 |
+
self.gripper_indices = None
|
| 246 |
+
self.gripper_act_indices = None
|
| 247 |
+
|
| 248 |
+
for node in et.iter():
|
| 249 |
+
for attrib in [
|
| 250 |
+
"body1",
|
| 251 |
+
"body2",
|
| 252 |
+
"name",
|
| 253 |
+
"class",
|
| 254 |
+
"childclass",
|
| 255 |
+
"mesh",
|
| 256 |
+
"site",
|
| 257 |
+
"joint",
|
| 258 |
+
]:
|
| 259 |
+
if attrib in node.attrib:
|
| 260 |
+
model_key_id = node.get(attrib)
|
| 261 |
+
attrib_id = self.add_id2model_key(model_key_id)
|
| 262 |
+
node.set(attrib, attrib_id)
|
| 263 |
+
|
| 264 |
+
wb = et.find("worldbody")
|
| 265 |
+
body_root = wb.find("body")
|
| 266 |
+
body_root.set("pos", " ".join(map(str, self.base_position)))
|
| 267 |
+
body_root.set("quat", " ".join(map(str, self.base_orientation)))
|
| 268 |
+
|
| 269 |
+
self.joint_names = []
|
| 270 |
+
self.gripper_names = []
|
| 271 |
+
for jnt in wb.iter("joint"):
|
| 272 |
+
if jnt.get("name") is not None and jnt.get("name").startswith(
|
| 273 |
+
self.add_id2model_key("panda_joint")
|
| 274 |
+
):
|
| 275 |
+
self.joint_names.append(jnt.get("name"))
|
| 276 |
+
if jnt.get("name") is not None and jnt.get("name").startswith(
|
| 277 |
+
self.add_id2model_key("panda_finger_joint")
|
| 278 |
+
):
|
| 279 |
+
self.gripper_names.append(jnt.get("name"))
|
| 280 |
+
|
| 281 |
+
import uuid
|
| 282 |
+
|
| 283 |
+
new_path = sim_path.d3il_path(
|
| 284 |
+
f"models/mj/robot/panda_tmp_rb{self._mj_robot_id}_{uuid.uuid1()}.xml"
|
| 285 |
+
)
|
| 286 |
+
et.write(new_path)
|
| 287 |
+
return new_path
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/MjScene.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools
|
| 2 |
+
import logging
|
| 3 |
+
import os
|
| 4 |
+
from typing import List, Tuple
|
| 5 |
+
|
| 6 |
+
import mujoco
|
| 7 |
+
import numpy as np
|
| 8 |
+
from mujoco import MjData, MjModel, mj_name2id, mjtObj
|
| 9 |
+
|
| 10 |
+
from d3il.environments.d3il.d3il_sim.core.Scene import Scene
|
| 11 |
+
from d3il.environments.d3il.d3il_sim.core.sim_object.sim_object import (
|
| 12 |
+
IntelligentSimObject,
|
| 13 |
+
SimObject,
|
| 14 |
+
)
|
| 15 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_render_singleton import (
|
| 16 |
+
reset_singleton as reset_render_singleton,
|
| 17 |
+
)
|
| 18 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_renderer import Viewer
|
| 19 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_scene_object import (
|
| 20 |
+
MjSurrounding,
|
| 21 |
+
)
|
| 22 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_scene_parser import (
|
| 23 |
+
MjSceneParser,
|
| 24 |
+
)
|
| 25 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.MjCamera import MjCageCam, MjCamera
|
| 26 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.MjLoadable import (
|
| 27 |
+
MjFreezable,
|
| 28 |
+
MjXmlLoadable,
|
| 29 |
+
)
|
| 30 |
+
from d3il.environments.d3il.d3il_sim.utils import sim_path
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class MjScene(Scene):
|
| 34 |
+
def __init__(
|
| 35 |
+
self,
|
| 36 |
+
object_list=None,
|
| 37 |
+
dt=0.001,
|
| 38 |
+
render=Scene.RenderMode.HUMAN,
|
| 39 |
+
surrounding=None,
|
| 40 |
+
random_env=False,
|
| 41 |
+
main_xml_path=None,
|
| 42 |
+
):
|
| 43 |
+
|
| 44 |
+
super(MjScene, self).__init__(object_list=object_list, dt=dt, render=render)
|
| 45 |
+
|
| 46 |
+
self.data: MjData = None
|
| 47 |
+
self.model: MjModel = None
|
| 48 |
+
self.viewer: Viewer = None
|
| 49 |
+
self.rt_obj = []
|
| 50 |
+
|
| 51 |
+
self.init_qpos, self.init_qvel = None, None
|
| 52 |
+
self.random_env = random_env
|
| 53 |
+
|
| 54 |
+
if surrounding is None:
|
| 55 |
+
surrounding = sim_path.d3il_path(
|
| 56 |
+
"models/mujoco/surroundings/lab_surrounding.xml"
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
self.surrounding = MjSurrounding(
|
| 60 |
+
surrounding_name=os.path.split(os.path.splitext(surrounding)[0])[-1],
|
| 61 |
+
root=os.path.dirname(surrounding),
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
self.mj_scene_parser = MjSceneParser(main_xml_path=main_xml_path)
|
| 65 |
+
|
| 66 |
+
self.cage_cam = MjCageCam()
|
| 67 |
+
self.add_object(self.cage_cam)
|
| 68 |
+
|
| 69 |
+
@property
|
| 70 |
+
def sim_name(self) -> str:
|
| 71 |
+
return "mj"
|
| 72 |
+
|
| 73 |
+
def _setup_scene(self):
|
| 74 |
+
for rb in self.robots:
|
| 75 |
+
self.add_object(rb.inhand_cam)
|
| 76 |
+
|
| 77 |
+
self.model, self.data = self.mj_scene_parser.create_scene(
|
| 78 |
+
self.robots, self.surrounding, self.obj_repo.get_obj_list(), self.dt
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
self.viewer = None
|
| 82 |
+
if self.render_mode == self.RenderMode.HUMAN:
|
| 83 |
+
self.viewer = Viewer(self.model, self.data)
|
| 84 |
+
self.init_qpos = self.data.qpos.copy()
|
| 85 |
+
self.init_qvel = np.zeros(self.data.qvel.shape)
|
| 86 |
+
|
| 87 |
+
self._setup_objects(self.obj_repo.get_obj_list())
|
| 88 |
+
|
| 89 |
+
def set_views(self, views):
|
| 90 |
+
if self.render_mode == self.RenderMode.HUMAN:
|
| 91 |
+
cam_ids = []
|
| 92 |
+
for v in views:
|
| 93 |
+
cam_ids.append(
|
| 94 |
+
mujoco.mj_name2id(self.model, mujoco.mjtObj.mjOBJ_CAMERA, v.name)
|
| 95 |
+
)
|
| 96 |
+
self.viewer.set_cameras(cam_ids)
|
| 97 |
+
|
| 98 |
+
def load_robot_to_scene(self, robot_init_qpos: np.ndarray = None):
|
| 99 |
+
if robot_init_qpos is None:
|
| 100 |
+
robot_init_qpos = np.stack([robot.get_init_qpos() for robot in self.robots])
|
| 101 |
+
else:
|
| 102 |
+
robot_init_qpos = np.asarray(robot_init_qpos)
|
| 103 |
+
if robot_init_qpos.ndim == 1:
|
| 104 |
+
robot_init_qpos = np.expand_dims(robot_init_qpos, 0)
|
| 105 |
+
|
| 106 |
+
for i in range(len(self.robots)):
|
| 107 |
+
self.robots[i].beam_to_joint_pos(robot_init_qpos[i], run=False)
|
| 108 |
+
self.init_qpos = self.data.qpos.copy()
|
| 109 |
+
|
| 110 |
+
def _sim_step(self):
|
| 111 |
+
mujoco.mj_step(self.model, self.data)
|
| 112 |
+
|
| 113 |
+
def sim_steps(self, n_steps):
|
| 114 |
+
mujoco.mj_step(self.model, self.data, n_steps)
|
| 115 |
+
|
| 116 |
+
def render(self):
|
| 117 |
+
if self.render_mode == self.RenderMode.HUMAN:
|
| 118 |
+
self.viewer.render()
|
| 119 |
+
|
| 120 |
+
def reset(self, obj_pos=None):
|
| 121 |
+
if obj_pos is None:
|
| 122 |
+
obj_pos = []
|
| 123 |
+
|
| 124 |
+
for rb in self.robots:
|
| 125 |
+
rb.reset()
|
| 126 |
+
|
| 127 |
+
mujoco.mj_resetData(self.model, self.data)
|
| 128 |
+
|
| 129 |
+
qpos = self.data.qpos.copy()
|
| 130 |
+
qpos[:] = self.init_qpos
|
| 131 |
+
|
| 132 |
+
qvel = np.zeros(self.data.qvel.shape)
|
| 133 |
+
self.set_state(qpos, qvel)
|
| 134 |
+
|
| 135 |
+
for obj, new_pos in obj_pos:
|
| 136 |
+
self.set_obj_pos(new_pos, obj)
|
| 137 |
+
|
| 138 |
+
mujoco.mj_forward(self.model, self.data)
|
| 139 |
+
|
| 140 |
+
for rb in self.robots:
|
| 141 |
+
rb.receiveState()
|
| 142 |
+
|
| 143 |
+
def add_object_rt(self, new_obj: MjXmlLoadable):
|
| 144 |
+
for obj in itertools.chain(self.obj_repo.get_obj_list(), self.robots):
|
| 145 |
+
if isinstance(obj, MjFreezable):
|
| 146 |
+
obj.freeze(self.model, self.data)
|
| 147 |
+
|
| 148 |
+
self.obj_repo.add_object(new_obj)
|
| 149 |
+
self.obj_repo.register_obj_id(new_obj, len(self.obj_repo.get_obj_list()))
|
| 150 |
+
if isinstance(new_obj, IntelligentSimObject):
|
| 151 |
+
new_obj.register_sim((self.model, self.data), self.sim_name)
|
| 152 |
+
|
| 153 |
+
self.mj_scene_parser = MjSceneParser()
|
| 154 |
+
self.model, self.data = self.mj_scene_parser.create_scene(
|
| 155 |
+
self.robots, self.surrounding, self.obj_repo.get_obj_list(), self.dt
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
reset_render_singleton()
|
| 159 |
+
if self.render_mode == self.RenderMode.HUMAN:
|
| 160 |
+
self.viewer.reinit(self.model, self.data)
|
| 161 |
+
|
| 162 |
+
self.init_qpos = self.data.qpos.copy()
|
| 163 |
+
self.init_qvel = np.zeros(self.data.qvel.shape)
|
| 164 |
+
self.load_robot_to_scene()
|
| 165 |
+
|
| 166 |
+
for obj in itertools.chain(self.obj_repo.get_obj_list(), self.robots):
|
| 167 |
+
if isinstance(obj, MjFreezable):
|
| 168 |
+
obj.unfreeze(self.model, self.data)
|
| 169 |
+
self.rt_obj.append(new_obj)
|
| 170 |
+
|
| 171 |
+
def obj_reset(self):
|
| 172 |
+
for obj in self.rt_obj:
|
| 173 |
+
self.obj_repo.remove_object(obj)
|
| 174 |
+
|
| 175 |
+
for obj in itertools.chain(self.obj_repo.get_obj_list(), self.robots):
|
| 176 |
+
if isinstance(obj, MjFreezable):
|
| 177 |
+
obj.freeze(self.model, self.data)
|
| 178 |
+
|
| 179 |
+
self.mj_scene_parser = MjSceneParser()
|
| 180 |
+
self.model, self.data = self.mj_scene_parser.create_scene(
|
| 181 |
+
self.robots, self.surrounding, self.obj_repo.get_obj_list(), self.dt
|
| 182 |
+
)
|
| 183 |
+
if self.render_mode == self.RenderMode.HUMAN:
|
| 184 |
+
self.viewer.reinit(self.model, self.data)
|
| 185 |
+
|
| 186 |
+
self.init_qpos = self.data.qpos.copy()
|
| 187 |
+
self.init_qvel = np.zeros(self.data.qvel.shape)
|
| 188 |
+
self.load_robot_to_scene()
|
| 189 |
+
|
| 190 |
+
for obj in itertools.chain(self.obj_repo.get_obj_list(), self.robots):
|
| 191 |
+
if isinstance(obj, MjFreezable):
|
| 192 |
+
obj.unfreeze(self.model, self.data)
|
| 193 |
+
self.rt_objects = []
|
| 194 |
+
|
| 195 |
+
def start_recording(self, nframes):
|
| 196 |
+
if self.render_mode == self.RenderMode.HUMAN:
|
| 197 |
+
self.viewer.start_recording(nframes=nframes)
|
| 198 |
+
|
| 199 |
+
def _setup_objects(self, sim_objs: List[SimObject]):
|
| 200 |
+
for i, obj in enumerate(sim_objs):
|
| 201 |
+
self.obj_repo.register_obj_id(obj, i)
|
| 202 |
+
|
| 203 |
+
if isinstance(obj, IntelligentSimObject):
|
| 204 |
+
obj.register_sim((self.model, self.data), self.sim_name)
|
| 205 |
+
|
| 206 |
+
def _rt_add_object(self, sim_obj: SimObject):
|
| 207 |
+
raise RuntimeError(
|
| 208 |
+
"Adding objects in MuJoCo only possible prior to scene setup."
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
def _get_obj_seg_id(self, obj_name: str):
|
| 212 |
+
return mj_name2id(self.model, 1, obj_name)
|
| 213 |
+
|
| 214 |
+
def _get_obj_pos(self, poi, sim_obj: SimObject):
|
| 215 |
+
pos, quat = self._get_obj_pos_and_quat(poi=poi, sim_obj=sim_obj)
|
| 216 |
+
return pos
|
| 217 |
+
|
| 218 |
+
def _get_obj_quat(self, poi, sim_obj: SimObject):
|
| 219 |
+
pos, quat = self._get_obj_pos_and_quat(poi=poi, sim_obj=sim_obj)
|
| 220 |
+
return quat
|
| 221 |
+
|
| 222 |
+
def _get_obj_pos_and_quat(self, poi, sim_obj: SimObject):
|
| 223 |
+
body_id = mj_name2id(self.model, mjtObj.mjOBJ_BODY, sim_obj.name)
|
| 224 |
+
body_jnt_addr = self.model.body_jntadr[body_id]
|
| 225 |
+
qposadr = self.model.jnt_qposadr[body_jnt_addr]
|
| 226 |
+
|
| 227 |
+
if body_jnt_addr == -1:
|
| 228 |
+
pos = self.model.body_pos[body_id]
|
| 229 |
+
quat = self.model.body_quat[body_id]
|
| 230 |
+
else:
|
| 231 |
+
pos = self.data.qpos[qposadr : qposadr + 3]
|
| 232 |
+
quat = self.data.qpos[qposadr + 3 : qposadr + 7]
|
| 233 |
+
|
| 234 |
+
return pos.copy(), quat.copy()
|
| 235 |
+
|
| 236 |
+
def _set_obj_pos(self, new_pos, sim_obj: SimObject):
|
| 237 |
+
self._set_obj_pos_and_quat(new_pos, None, sim_obj=sim_obj)
|
| 238 |
+
|
| 239 |
+
def _set_obj_quat(self, new_quat, sim_obj: SimObject):
|
| 240 |
+
self._set_obj_pos_and_quat(None, new_quat, sim_obj=sim_obj)
|
| 241 |
+
|
| 242 |
+
def _set_obj_pos_and_quat(self, new_pos, new_quat, sim_obj: SimObject):
|
| 243 |
+
if new_pos is None and new_quat is None:
|
| 244 |
+
logging.getLogger(__name__).warning(
|
| 245 |
+
"Expected at least either a new position or quaternion for set_obj_pos_and_quat"
|
| 246 |
+
)
|
| 247 |
+
return
|
| 248 |
+
|
| 249 |
+
body_id = mj_name2id(self.model, mjtObj.mjOBJ_BODY, sim_obj.name)
|
| 250 |
+
body_jnt_addr = self.model.body_jntadr[body_id]
|
| 251 |
+
qposadr = self.model.jnt_qposadr[body_jnt_addr]
|
| 252 |
+
|
| 253 |
+
if new_pos is not None:
|
| 254 |
+
assert len(new_pos) == 3, print(
|
| 255 |
+
f"Expected a positions list of 3 values, got {len(new_pos)}"
|
| 256 |
+
)
|
| 257 |
+
|
| 258 |
+
if body_jnt_addr == -1:
|
| 259 |
+
self.model.body_pos[body_id] = new_pos
|
| 260 |
+
else:
|
| 261 |
+
self.data.qpos[qposadr : qposadr + 3] = new_pos
|
| 262 |
+
|
| 263 |
+
if new_quat is not None:
|
| 264 |
+
assert len(new_quat) == 4, print(
|
| 265 |
+
f"Expected a quaternions list of 4 values, got {len(new_quat)}"
|
| 266 |
+
)
|
| 267 |
+
if body_jnt_addr == -1:
|
| 268 |
+
self.model.body_quat[body_id] = new_quat
|
| 269 |
+
else:
|
| 270 |
+
self.data.qpos[qposadr + 3 : qposadr + 7] = new_quat
|
| 271 |
+
|
| 272 |
+
def _remove_object(self, sim_obj: SimObject):
|
| 273 |
+
raise RuntimeError(
|
| 274 |
+
"Removing objects in MuJoCo only possible prior to scene setup."
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
def set_state(self, qpos, qvel):
|
| 278 |
+
self.data.qpos[:] = np.copy(qpos)
|
| 279 |
+
self.data.qvel[:] = np.copy(qvel)
|
| 280 |
+
if self.model.na == 0:
|
| 281 |
+
self.data.act[:] = None
|
| 282 |
+
mujoco.mj_forward(self.model, self.data)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
try:
|
| 4 |
+
import mujoco
|
| 5 |
+
|
| 6 |
+
from .MjFactory import *
|
| 7 |
+
from .MjCamera import MjCamera
|
| 8 |
+
except ImportError as e:
|
| 9 |
+
logging.getLogger(__name__).info(e)
|
| 10 |
+
logging.getLogger(__name__).info(
|
| 11 |
+
"No MuJoCo bindings are(new bindings from DeepMind) installed.",
|
| 12 |
+
"Install MuJoCo >= 2.1.5 and load bindings via pip, conda or mamba",
|
| 13 |
+
)
|
| 14 |
+
pass
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_helper.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
|
| 3 |
+
import mujoco
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class IncludeType(Enum):
|
| 8 |
+
FILE_INCLUDE = 0
|
| 9 |
+
MJ_INCLUDE = 1
|
| 10 |
+
WORLD_BODY = 2
|
| 11 |
+
VIRTUAL_INCLUDE = 3
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def change_domain(value, in_low, in_high, out_low, out_high):
|
| 15 |
+
return (out_high - out_low) * ((value - in_low) / (in_high - in_low)) + out_low
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def obj_position(data, obj_id: str):
|
| 19 |
+
return data.get_body_xpos(obj_id)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def obj_distance(data, obj1: str, obj2: str):
|
| 23 |
+
obj1_pos = obj_position(data, obj1)
|
| 24 |
+
obj2_pos = obj_position(data, obj2)
|
| 25 |
+
dist = np.linalg.norm(obj1_pos - obj2_pos)
|
| 26 |
+
rel_dist = (obj1_pos - obj2_pos) / dist + 1e-8
|
| 27 |
+
return dist, rel_dist
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def reset_mocap2body_xpos(model, data):
|
| 31 |
+
|
| 32 |
+
if model.eq_type is None or model.eq_obj1id is None or model.eq_obj2id is None:
|
| 33 |
+
return
|
| 34 |
+
for eq_type, obj1_id, obj2_id in zip(
|
| 35 |
+
model.eq_type, model.eq_obj1id, model.eq_obj2id
|
| 36 |
+
):
|
| 37 |
+
if eq_type != mujoco.const.EQ_WELD:
|
| 38 |
+
continue
|
| 39 |
+
|
| 40 |
+
mocap_id = model.body_mocapid[obj1_id]
|
| 41 |
+
if mocap_id != -1:
|
| 42 |
+
body_idx = obj2_id
|
| 43 |
+
else:
|
| 44 |
+
mocap_id = model.body_mocapid[obj2_id]
|
| 45 |
+
body_idx = obj1_id
|
| 46 |
+
|
| 47 |
+
assert mocap_id != -1
|
| 48 |
+
data.mocap_pos[mocap_id][:] = data.body_xpos[body_idx]
|
| 49 |
+
data.mocap_quat[mocap_id][:] = data.body_xquat[body_idx]
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def get_body_jacr(model, data, name):
|
| 53 |
+
id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, name)
|
| 54 |
+
jacr = np.zeros((3, model.nv))
|
| 55 |
+
mujoco.mj_jacBody(model, data, None, jacr, body=id)
|
| 56 |
+
return jacr
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def get_body_jacp(model, data, name):
|
| 60 |
+
id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, name)
|
| 61 |
+
jacp = np.zeros((3, model.nv))
|
| 62 |
+
mujoco.mj_jacBody(model, data, jacp, None, body=id)
|
| 63 |
+
return jacp
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def get_body_xvelp(model, data, name):
|
| 67 |
+
jacp = get_body_jacp(model, data, name).reshape((3, model.nv))
|
| 68 |
+
xvelp = np.dot(jacp, data.qvel.copy())
|
| 69 |
+
return xvelp
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def get_body_xvelr(model, data, name):
|
| 73 |
+
jacr = get_body_jacr(model, data, name).reshape((3, model.nv))
|
| 74 |
+
xvelr = np.dot(jacr, data.qvel.copy())
|
| 75 |
+
return xvelr
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_render_singleton.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from mujoco import MjData, MjModel, mj_name2id, mjtObj
|
| 2 |
+
|
| 3 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_renderer import (
|
| 4 |
+
RenderContext,
|
| 5 |
+
RenderContextOffscreen,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
__RENDER_CTX_MAP = {}
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def reset_singleton():
|
| 12 |
+
global __RENDER_CTX_MAP
|
| 13 |
+
keys = list(__RENDER_CTX_MAP.keys())
|
| 14 |
+
for key in keys:
|
| 15 |
+
del __RENDER_CTX_MAP[key]
|
| 16 |
+
__RENDER_CTX_MAP = {}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def get_renderer(
|
| 20 |
+
name: str, width: int, height: int, model: MjModel, data: MjData
|
| 21 |
+
) -> RenderContextOffscreen:
|
| 22 |
+
global __RENDER_CTX_MAP
|
| 23 |
+
if name not in __RENDER_CTX_MAP:
|
| 24 |
+
ctx = RenderContextOffscreen(width, height, model, data)
|
| 25 |
+
__RENDER_CTX_MAP[name] = ctx
|
| 26 |
+
ctx = __RENDER_CTX_MAP[name]
|
| 27 |
+
return ctx
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def render(
|
| 31 |
+
cam_name: str,
|
| 32 |
+
width: int,
|
| 33 |
+
height: int,
|
| 34 |
+
model: MjModel,
|
| 35 |
+
data: MjData,
|
| 36 |
+
depth: bool,
|
| 37 |
+
segmentation: bool,
|
| 38 |
+
):
|
| 39 |
+
|
| 40 |
+
ctx = get_renderer(cam_name, width, height, model, data)
|
| 41 |
+
ctx.opengl_context.make_current()
|
| 42 |
+
cam_id = mj_name2id(m=model, name=cam_name, type=mjtObj.mjOBJ_CAMERA)
|
| 43 |
+
ctx.render(width, height, cam_id, segmentation=segmentation)
|
| 44 |
+
return ctx.read_pixels(width, height, depth=depth, segmentation=segmentation)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_renderer.py
ADDED
|
@@ -0,0 +1,593 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import collections
|
| 2 |
+
import os
|
| 3 |
+
import time
|
| 4 |
+
from threading import Lock
|
| 5 |
+
from typing import List
|
| 6 |
+
|
| 7 |
+
import glfw
|
| 8 |
+
import imageio
|
| 9 |
+
import mujoco
|
| 10 |
+
import numpy as np
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def _import_egl(width, height):
|
| 14 |
+
from mujoco.egl import GLContext
|
| 15 |
+
|
| 16 |
+
return GLContext(width, height)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _import_glfw(width, height):
|
| 20 |
+
from mujoco.glfw import GLContext
|
| 21 |
+
|
| 22 |
+
return GLContext(width, height)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _import_osmesa(width, height):
|
| 26 |
+
from mujoco.osmesa import GLContext
|
| 27 |
+
|
| 28 |
+
return GLContext(width, height)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
_ALL_RENDERERS = collections.OrderedDict(
|
| 32 |
+
[
|
| 33 |
+
("glfw", _import_glfw),
|
| 34 |
+
("egl", _import_egl),
|
| 35 |
+
("osmesa", _import_osmesa),
|
| 36 |
+
]
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class RenderContext:
|
| 41 |
+
|
| 42 |
+
def __init__(self, model, data, offscreen=True):
|
| 43 |
+
|
| 44 |
+
self.model = model
|
| 45 |
+
self.data = data
|
| 46 |
+
self.offscreen = offscreen
|
| 47 |
+
max_geom = 1000
|
| 48 |
+
|
| 49 |
+
mujoco.mj_forward(self.model, self.data)
|
| 50 |
+
|
| 51 |
+
self.scn = mujoco.MjvScene(self.model, max_geom)
|
| 52 |
+
self.cam = mujoco.MjvCamera()
|
| 53 |
+
self.vopt = mujoco.MjvOption()
|
| 54 |
+
self.pert = mujoco.MjvPerturb()
|
| 55 |
+
self.con = mujoco.MjrContext(self.model, mujoco.mjtFontScale.mjFONTSCALE_150)
|
| 56 |
+
|
| 57 |
+
self._markers = []
|
| 58 |
+
self._overlays = {}
|
| 59 |
+
|
| 60 |
+
self._init_camera()
|
| 61 |
+
self._set_mujoco_buffers()
|
| 62 |
+
|
| 63 |
+
def _set_mujoco_buffers(self):
|
| 64 |
+
if self.offscreen:
|
| 65 |
+
mujoco.mjr_setBuffer(mujoco.mjtFramebuffer.mjFB_OFFSCREEN, self.con)
|
| 66 |
+
if self.con.currentBuffer != mujoco.mjtFramebuffer.mjFB_OFFSCREEN:
|
| 67 |
+
raise RuntimeError("Offscreen rendering not supported")
|
| 68 |
+
else:
|
| 69 |
+
mujoco.mjr_setBuffer(mujoco.mjtFramebuffer.mjFB_WINDOW, self.con)
|
| 70 |
+
if self.con.currentBuffer != mujoco.mjtFramebuffer.mjFB_WINDOW:
|
| 71 |
+
raise RuntimeError("Window rendering not supported")
|
| 72 |
+
|
| 73 |
+
def update_offscreen_size(self, width, height):
|
| 74 |
+
if width != self.con.offWidth or height != self.con.offHeight:
|
| 75 |
+
self.model.vis.global_.offwidth = width
|
| 76 |
+
self.model.vis.global_.offheight = height
|
| 77 |
+
self.con.free()
|
| 78 |
+
self.con = mujoco.MjrContext(
|
| 79 |
+
self.model, mujoco.mjtFontScale.mjFONTSCALE_150
|
| 80 |
+
)
|
| 81 |
+
self._set_mujoco_buffers()
|
| 82 |
+
|
| 83 |
+
def render(self, width, height, camera_id=None, segmentation=False):
|
| 84 |
+
rect = mujoco.MjrRect(left=0, bottom=0, width=width, height=height)
|
| 85 |
+
|
| 86 |
+
if width != self.con.offWidth or height != self.con.offHeight:
|
| 87 |
+
new_width = max(width, self.model.vis.global_.offwidth)
|
| 88 |
+
new_height = max(height, self.model.vis.global_.offheight)
|
| 89 |
+
self.update_offscreen_size(new_width, new_height)
|
| 90 |
+
|
| 91 |
+
if camera_id is not None:
|
| 92 |
+
if camera_id == -1:
|
| 93 |
+
self.cam.type = mujoco.mjtCamera.mjCAMERA_FREE
|
| 94 |
+
else:
|
| 95 |
+
self.cam.type = mujoco.mjtCamera.mjCAMERA_FIXED
|
| 96 |
+
self.cam.fixedcamid = camera_id
|
| 97 |
+
|
| 98 |
+
mujoco.mjv_updateScene(
|
| 99 |
+
self.model,
|
| 100 |
+
self.data,
|
| 101 |
+
self.vopt,
|
| 102 |
+
self.pert,
|
| 103 |
+
self.cam,
|
| 104 |
+
mujoco.mjtCatBit.mjCAT_ALL,
|
| 105 |
+
self.scn,
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
if segmentation:
|
| 109 |
+
self.scn.flags[mujoco.mjtRndFlag.mjRND_SEGMENT] = 1
|
| 110 |
+
self.scn.flags[mujoco.mjtRndFlag.mjRND_IDCOLOR] = 1
|
| 111 |
+
|
| 112 |
+
for marker_params in self._markers:
|
| 113 |
+
self._add_marker_to_scene(marker_params)
|
| 114 |
+
|
| 115 |
+
mujoco.mjr_render(rect, self.scn, self.con)
|
| 116 |
+
|
| 117 |
+
for gridpos, (text1, text2) in self._overlays.items():
|
| 118 |
+
mujoco.mjr_overlay(
|
| 119 |
+
mujoco.mjtFontScale.mjFONTSCALE_150,
|
| 120 |
+
gridpos,
|
| 121 |
+
rect,
|
| 122 |
+
text1.encode(),
|
| 123 |
+
text2.encode(),
|
| 124 |
+
self.con,
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
if segmentation:
|
| 128 |
+
self.scn.flags[mujoco.mjtRndFlag.mjRND_SEGMENT] = 0
|
| 129 |
+
self.scn.flags[mujoco.mjtRndFlag.mjRND_IDCOLOR] = 0
|
| 130 |
+
|
| 131 |
+
def read_pixels(self, width, height, depth=True, segmentation=False):
|
| 132 |
+
rect = mujoco.MjrRect(left=0, bottom=0, width=width, height=height)
|
| 133 |
+
|
| 134 |
+
rgb_arr = np.zeros(3 * rect.width * rect.height, dtype=np.uint8)
|
| 135 |
+
depth_arr = np.zeros(rect.width * rect.height, dtype=np.float32)
|
| 136 |
+
|
| 137 |
+
mujoco.mjr_readPixels(rgb_arr, depth_arr, rect, self.con)
|
| 138 |
+
rgb_img = rgb_arr.reshape(rect.height, rect.width, 3)
|
| 139 |
+
|
| 140 |
+
ret_img = rgb_img
|
| 141 |
+
if segmentation:
|
| 142 |
+
seg_img = (
|
| 143 |
+
rgb_img[:, :, 0]
|
| 144 |
+
+ rgb_img[:, :, 1] * (2**8)
|
| 145 |
+
+ rgb_img[:, :, 2] * (2**16)
|
| 146 |
+
)
|
| 147 |
+
seg_img[seg_img >= (self.scn.ngeom + 1)] = 0
|
| 148 |
+
seg_ids = np.full((self.scn.ngeom + 1, 2), fill_value=-1, dtype=np.int32)
|
| 149 |
+
|
| 150 |
+
for i in range(self.scn.ngeom):
|
| 151 |
+
geom = self.scn.geoms[i]
|
| 152 |
+
if geom.segid != -1:
|
| 153 |
+
seg_ids[geom.segid + 1, 0] = geom.objtype
|
| 154 |
+
seg_ids[geom.segid + 1, 1] = geom.objid
|
| 155 |
+
ret_img = seg_ids[seg_img]
|
| 156 |
+
|
| 157 |
+
if depth:
|
| 158 |
+
depth_img = depth_arr.reshape(rect.height, rect.width)
|
| 159 |
+
return (ret_img, depth_img)
|
| 160 |
+
else:
|
| 161 |
+
return ret_img
|
| 162 |
+
|
| 163 |
+
def _init_camera(self):
|
| 164 |
+
self.cam.type = mujoco.mjtCamera.mjCAMERA_FREE
|
| 165 |
+
self.cam.fixedcamid = -1
|
| 166 |
+
for i in range(3):
|
| 167 |
+
self.cam.lookat[i] = np.median(self.data.geom_xpos[:, i])
|
| 168 |
+
|
| 169 |
+
self.cam.azimuth = 180
|
| 170 |
+
self.cam.elevation = -30
|
| 171 |
+
self.cam.distance = self.model.stat.extent * 0.75
|
| 172 |
+
|
| 173 |
+
def add_overlay(self, gridpos: int, text1: str, text2: str):
|
| 174 |
+
if gridpos not in self._overlays:
|
| 175 |
+
self._overlays[gridpos] = ["", ""]
|
| 176 |
+
self._overlays[gridpos][0] += text1 + "\n"
|
| 177 |
+
self._overlays[gridpos][1] += text2 + "\n"
|
| 178 |
+
|
| 179 |
+
def add_marker(self, **marker_params):
|
| 180 |
+
self._markers.append(marker_params)
|
| 181 |
+
|
| 182 |
+
def _add_marker_to_scene(self, marker):
|
| 183 |
+
if self.scn.ngeom >= self.scn.maxgeom:
|
| 184 |
+
raise RuntimeError("Ran out of geoms. maxgeom: %d" % self.scn.maxgeom)
|
| 185 |
+
|
| 186 |
+
g = self.scn.geoms[self.scn.ngeom]
|
| 187 |
+
g.dataid = -1
|
| 188 |
+
g.objtype = mujoco.mjtObj.mjOBJ_UNKNOWN
|
| 189 |
+
g.objid = -1
|
| 190 |
+
g.category = mujoco.mjtCatBit.mjCAT_DECOR
|
| 191 |
+
g.texid = -1
|
| 192 |
+
g.texuniform = 0
|
| 193 |
+
g.texrepeat[0] = 1
|
| 194 |
+
g.texrepeat[1] = 1
|
| 195 |
+
g.emission = 0
|
| 196 |
+
g.specular = 0.5
|
| 197 |
+
g.shininess = 0.5
|
| 198 |
+
g.reflectance = 0
|
| 199 |
+
g.type = mujoco.mjtGeom.mjGEOM_BOX
|
| 200 |
+
g.size[:] = np.ones(3) * 0.1
|
| 201 |
+
g.mat[:] = np.eye(3)
|
| 202 |
+
g.rgba[:] = np.ones(4)
|
| 203 |
+
|
| 204 |
+
for key, value in marker.items():
|
| 205 |
+
if isinstance(value, (int, float, mujoco._enums.mjtGeom)):
|
| 206 |
+
setattr(g, key, value)
|
| 207 |
+
elif isinstance(value, (tuple, list, np.ndarray)):
|
| 208 |
+
attr = getattr(g, key)
|
| 209 |
+
attr[:] = np.asarray(value).reshape(attr.shape)
|
| 210 |
+
elif isinstance(value, str):
|
| 211 |
+
assert key == "label", "Only label is a string in mjtGeom."
|
| 212 |
+
if value is None:
|
| 213 |
+
g.label[0] = 0
|
| 214 |
+
else:
|
| 215 |
+
g.label = value
|
| 216 |
+
elif hasattr(g, key):
|
| 217 |
+
raise ValueError(
|
| 218 |
+
"mjtGeom has attr {} but type {} is invalid".format(
|
| 219 |
+
key, type(value)
|
| 220 |
+
)
|
| 221 |
+
)
|
| 222 |
+
else:
|
| 223 |
+
raise ValueError("mjtGeom doesn't have field %s" % key)
|
| 224 |
+
|
| 225 |
+
self.scn.ngeom += 1
|
| 226 |
+
|
| 227 |
+
def close(self):
|
| 228 |
+
pass
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
class RenderContextOffscreen(RenderContext):
|
| 232 |
+
|
| 233 |
+
def __init__(self, width, height, model, data):
|
| 234 |
+
|
| 235 |
+
self._get_opengl_backend(width, height)
|
| 236 |
+
self.opengl_context.make_current()
|
| 237 |
+
|
| 238 |
+
super().__init__(model, data, offscreen=True)
|
| 239 |
+
|
| 240 |
+
def _get_opengl_backend(self, width, height):
|
| 241 |
+
|
| 242 |
+
backend = os.environ.get("MUJOCO_GL")
|
| 243 |
+
if backend is not None:
|
| 244 |
+
try:
|
| 245 |
+
self.opengl_context = _ALL_RENDERERS[backend](width, height)
|
| 246 |
+
except KeyError:
|
| 247 |
+
raise RuntimeError(
|
| 248 |
+
"Environment variable {} must be one of {!r}: got {!r}.".format(
|
| 249 |
+
"MUJOCO_GL", _ALL_RENDERERS.keys(), backend
|
| 250 |
+
)
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
else:
|
| 254 |
+
for name, _ in _ALL_RENDERERS.items():
|
| 255 |
+
try:
|
| 256 |
+
self.opengl_context = _ALL_RENDERERS[name](width, height)
|
| 257 |
+
backend = name
|
| 258 |
+
break
|
| 259 |
+
except:
|
| 260 |
+
pass
|
| 261 |
+
if backend is None:
|
| 262 |
+
raise RuntimeError(
|
| 263 |
+
"No OpenGL backend could be imported. Attempting to create a "
|
| 264 |
+
"rendering context will result in a RuntimeError."
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
class Viewer(RenderContext):
|
| 269 |
+
|
| 270 |
+
def __init__(self, model, data):
|
| 271 |
+
self._gui_lock = Lock()
|
| 272 |
+
self._button_left_pressed = False
|
| 273 |
+
self._button_right_pressed = False
|
| 274 |
+
self._last_mouse_x = 0
|
| 275 |
+
self._last_mouse_y = 0
|
| 276 |
+
self._paused = False
|
| 277 |
+
self._transparent = False
|
| 278 |
+
self._contacts = False
|
| 279 |
+
self._render_every_frame = False
|
| 280 |
+
self._image_idx = 0
|
| 281 |
+
self._image_path = "/tmp/frame_%07d.png"
|
| 282 |
+
self._run_speed = 1.0
|
| 283 |
+
self.last_time_rendered = 0.0
|
| 284 |
+
self._advance_by_one_step = False
|
| 285 |
+
self._hide_menu = False
|
| 286 |
+
self.windows = []
|
| 287 |
+
self.cams = [-1]
|
| 288 |
+
|
| 289 |
+
glfw.init()
|
| 290 |
+
self.width, self.height = glfw.get_video_mode(glfw.get_primary_monitor()).size
|
| 291 |
+
self.windows.append(
|
| 292 |
+
glfw.create_window(self.width // 2, self.height // 2, "mujoco", None, None)
|
| 293 |
+
)
|
| 294 |
+
glfw.make_context_current(self.windows[0])
|
| 295 |
+
glfw.swap_interval(1)
|
| 296 |
+
|
| 297 |
+
framebuffer_width, framebuffer_height = glfw.get_framebuffer_size(
|
| 298 |
+
self.windows[0]
|
| 299 |
+
)
|
| 300 |
+
window_width, _ = glfw.get_window_size(self.windows[0])
|
| 301 |
+
self._scale = framebuffer_width * 1.0 / window_width
|
| 302 |
+
|
| 303 |
+
glfw.set_cursor_pos_callback(self.windows[0], self._cursor_pos_callback)
|
| 304 |
+
glfw.set_mouse_button_callback(self.windows[0], self._mouse_button_callback)
|
| 305 |
+
glfw.set_scroll_callback(self.windows[0], self._scroll_callback)
|
| 306 |
+
glfw.set_key_callback(self.windows[0], self._key_callback)
|
| 307 |
+
|
| 308 |
+
self.viewport = mujoco.MjrRect(0, 0, framebuffer_width, framebuffer_height)
|
| 309 |
+
super().__init__(model, data, offscreen=False)
|
| 310 |
+
|
| 311 |
+
def set_cameras(self, cameras: List[int]):
|
| 312 |
+
for cam in cameras:
|
| 313 |
+
self.windows.append(
|
| 314 |
+
glfw.create_window(
|
| 315 |
+
self.width // 2,
|
| 316 |
+
self.height // 2,
|
| 317 |
+
"Camera: {}".format(str(cam)),
|
| 318 |
+
None,
|
| 319 |
+
self.windows[0],
|
| 320 |
+
)
|
| 321 |
+
)
|
| 322 |
+
glfw.make_context_current(self.windows[-1])
|
| 323 |
+
self.cams.append(cam)
|
| 324 |
+
|
| 325 |
+
def _key_callback(self, window, key, scancode, action, mods):
|
| 326 |
+
if action != glfw.RELEASE:
|
| 327 |
+
return
|
| 328 |
+
elif key == glfw.KEY_TAB:
|
| 329 |
+
self.cams[0] += 1
|
| 330 |
+
if self.cams[0] >= self.model.ncam:
|
| 331 |
+
self.cams[0] = -1
|
| 332 |
+
elif key == glfw.KEY_A:
|
| 333 |
+
self.windows.append(
|
| 334 |
+
glfw.create_window(
|
| 335 |
+
self.width // 2,
|
| 336 |
+
self.height // 2,
|
| 337 |
+
"Camera: {}".format(str(self.cams[0])),
|
| 338 |
+
None,
|
| 339 |
+
self.windows[0],
|
| 340 |
+
)
|
| 341 |
+
)
|
| 342 |
+
glfw.make_context_current(self.windows[-1])
|
| 343 |
+
self.cams.append(self.cams[0])
|
| 344 |
+
elif key == glfw.KEY_P:
|
| 345 |
+
print("lookat: ", self.cam.lookat)
|
| 346 |
+
print("elevation: ", self.cam.elevation)
|
| 347 |
+
print("distance: ", self.cam.distance)
|
| 348 |
+
print("azimuth: ", self.cam.azimuth)
|
| 349 |
+
elif key == glfw.KEY_SPACE and self._paused is not None:
|
| 350 |
+
self._paused = not self._paused
|
| 351 |
+
elif key == glfw.KEY_RIGHT and self._paused is not None:
|
| 352 |
+
self._advance_by_one_step = True
|
| 353 |
+
self._paused = True
|
| 354 |
+
elif key == glfw.KEY_S:
|
| 355 |
+
self._run_speed -= 0.25
|
| 356 |
+
if self._run_speed <= 0.0:
|
| 357 |
+
self._run_speed = 0.25
|
| 358 |
+
elif key == glfw.KEY_F:
|
| 359 |
+
self._run_speed += 0.25
|
| 360 |
+
elif key == glfw.KEY_D:
|
| 361 |
+
self._render_every_frame = not self._render_every_frame
|
| 362 |
+
elif key == glfw.KEY_T:
|
| 363 |
+
img = np.zeros(
|
| 364 |
+
(
|
| 365 |
+
glfw.get_framebuffer_size(window)[1],
|
| 366 |
+
glfw.get_framebuffer_size(window)[0],
|
| 367 |
+
3,
|
| 368 |
+
),
|
| 369 |
+
dtype=np.uint8,
|
| 370 |
+
)
|
| 371 |
+
mujoco.mjr_readPixels(img, None, self.viewport, self.con)
|
| 372 |
+
imageio.imwrite(self._image_path % self._image_idx, np.flipud(img))
|
| 373 |
+
self._image_idx += 1
|
| 374 |
+
elif key == glfw.KEY_C:
|
| 375 |
+
self._contacts = not self._contacts
|
| 376 |
+
self.vopt.flags[mujoco.mjtVisFlag.mjVIS_CONTACTPOINT] = self._contacts
|
| 377 |
+
self.vopt.flags[mujoco.mjtVisFlag.mjVIS_CONTACTFORCE] = self._contacts
|
| 378 |
+
elif key == glfw.KEY_E:
|
| 379 |
+
self.vopt.frame = 1 - self.vopt.frame
|
| 380 |
+
elif key == glfw.KEY_H:
|
| 381 |
+
self._hide_menu = not self._hide_menu
|
| 382 |
+
elif key == glfw.KEY_R:
|
| 383 |
+
self._transparent = not self._transparent
|
| 384 |
+
if self._transparent:
|
| 385 |
+
self.model.geom_rgba[:, 3] /= 5.0
|
| 386 |
+
else:
|
| 387 |
+
self.model.geom_rgba[:, 3] *= 5.0
|
| 388 |
+
elif key in (glfw.KEY_0, glfw.KEY_1, glfw.KEY_2, glfw.KEY_3, glfw.KEY_4):
|
| 389 |
+
self.vopt.geomgroup[key - glfw.KEY_0] ^= 1
|
| 390 |
+
if key == glfw.KEY_ESCAPE:
|
| 391 |
+
print("Pressed ESC")
|
| 392 |
+
print("Quitting.")
|
| 393 |
+
glfw.destroy_window(window)
|
| 394 |
+
glfw.terminate()
|
| 395 |
+
|
| 396 |
+
def _cursor_pos_callback(self, window, xpos, ypos):
|
| 397 |
+
if not (self._button_left_pressed or self._button_right_pressed):
|
| 398 |
+
return
|
| 399 |
+
|
| 400 |
+
mod_shift = (
|
| 401 |
+
glfw.get_key(window, glfw.KEY_LEFT_SHIFT) == glfw.PRESS
|
| 402 |
+
or glfw.get_key(window, glfw.KEY_RIGHT_SHIFT) == glfw.PRESS
|
| 403 |
+
)
|
| 404 |
+
if self._button_right_pressed:
|
| 405 |
+
action = (
|
| 406 |
+
mujoco.mjtMouse.mjMOUSE_MOVE_H
|
| 407 |
+
if mod_shift
|
| 408 |
+
else mujoco.mjtMouse.mjMOUSE_MOVE_V
|
| 409 |
+
)
|
| 410 |
+
elif self._button_left_pressed:
|
| 411 |
+
action = (
|
| 412 |
+
mujoco.mjtMouse.mjMOUSE_ROTATE_H
|
| 413 |
+
if mod_shift
|
| 414 |
+
else mujoco.mjtMouse.mjMOUSE_ROTATE_V
|
| 415 |
+
)
|
| 416 |
+
else:
|
| 417 |
+
action = mujoco.mjtMouse.mjMOUSE_ZOOM
|
| 418 |
+
|
| 419 |
+
dx = int(self._scale * xpos) - self._last_mouse_x
|
| 420 |
+
dy = int(self._scale * ypos) - self._last_mouse_y
|
| 421 |
+
width, height = glfw.get_framebuffer_size(window)
|
| 422 |
+
|
| 423 |
+
with self._gui_lock:
|
| 424 |
+
mujoco.mjv_moveCamera(
|
| 425 |
+
self.model, action, dx / height, dy / height, self.scn, self.cam
|
| 426 |
+
)
|
| 427 |
+
|
| 428 |
+
self._last_mouse_x = int(self._scale * xpos)
|
| 429 |
+
self._last_mouse_y = int(self._scale * ypos)
|
| 430 |
+
|
| 431 |
+
def _mouse_button_callback(self, window, button, act, mods):
|
| 432 |
+
self._button_left_pressed = (
|
| 433 |
+
glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_LEFT) == glfw.PRESS
|
| 434 |
+
)
|
| 435 |
+
self._button_right_pressed = (
|
| 436 |
+
glfw.get_mouse_button(window, glfw.MOUSE_BUTTON_RIGHT) == glfw.PRESS
|
| 437 |
+
)
|
| 438 |
+
|
| 439 |
+
x, y = glfw.get_cursor_pos(window)
|
| 440 |
+
self._last_mouse_x = int(self._scale * x)
|
| 441 |
+
self._last_mouse_y = int(self._scale * y)
|
| 442 |
+
|
| 443 |
+
def _scroll_callback(self, window, x_offset, y_offset):
|
| 444 |
+
with self._gui_lock:
|
| 445 |
+
mujoco.mjv_moveCamera(
|
| 446 |
+
self.model,
|
| 447 |
+
mujoco.mjtMouse.mjMOUSE_ZOOM,
|
| 448 |
+
0,
|
| 449 |
+
-0.05 * y_offset,
|
| 450 |
+
self.scn,
|
| 451 |
+
self.cam,
|
| 452 |
+
)
|
| 453 |
+
|
| 454 |
+
def _create_overlay(self):
|
| 455 |
+
topleft = mujoco.mjtGridPos.mjGRID_TOPLEFT
|
| 456 |
+
bottomleft = mujoco.mjtGridPos.mjGRID_BOTTOMLEFT
|
| 457 |
+
|
| 458 |
+
if self._render_every_frame:
|
| 459 |
+
self.add_overlay(topleft, "", "")
|
| 460 |
+
else:
|
| 461 |
+
self.add_overlay(
|
| 462 |
+
topleft,
|
| 463 |
+
"Run speed = %.3f x real time" % self._run_speed,
|
| 464 |
+
"[S]lower, [F]aster",
|
| 465 |
+
)
|
| 466 |
+
self.add_overlay(
|
| 467 |
+
topleft, "Ren[d]er every frame", "On" if self._render_every_frame else "Off"
|
| 468 |
+
)
|
| 469 |
+
self.add_overlay(
|
| 470 |
+
topleft,
|
| 471 |
+
"Switch camera (#cams = %d)" % (self.model.ncam + 1),
|
| 472 |
+
"[Tab] (camera ID = %d)" % self.cam.fixedcamid,
|
| 473 |
+
)
|
| 474 |
+
self.add_overlay(topleft, "[C]ontact forces", "On" if self._contacts else "Off")
|
| 475 |
+
self.add_overlay(topleft, "T[r]ansparent", "On" if self._transparent else "Off")
|
| 476 |
+
self.add_overlay(topleft, "Camera detach", "[A]")
|
| 477 |
+
self.add_overlay(topleft, "Print camera position", "[p]")
|
| 478 |
+
if self._paused is not None:
|
| 479 |
+
if not self._paused:
|
| 480 |
+
self.add_overlay(topleft, "Stop", "[Space]")
|
| 481 |
+
else:
|
| 482 |
+
self.add_overlay(topleft, "Start", "[Space]")
|
| 483 |
+
self.add_overlay(
|
| 484 |
+
topleft, "Advance simulation by one step", "[right arrow]"
|
| 485 |
+
)
|
| 486 |
+
self.add_overlay(
|
| 487 |
+
topleft, "Referenc[e] frames", "On" if self.vopt.frame == 1 else "Off"
|
| 488 |
+
)
|
| 489 |
+
self.add_overlay(topleft, "[H]ide Menu", "")
|
| 490 |
+
if self._image_idx > 0:
|
| 491 |
+
fname = self._image_path % (self._image_idx - 1)
|
| 492 |
+
self.add_overlay(topleft, "Cap[t]ure frame", "Saved as %s" % fname)
|
| 493 |
+
else:
|
| 494 |
+
self.add_overlay(topleft, "Cap[t]ure frame", "")
|
| 495 |
+
self.add_overlay(topleft, "Toggle geomgroup visibility", "0-4")
|
| 496 |
+
|
| 497 |
+
self.add_overlay(
|
| 498 |
+
bottomleft, "Solver iterations", str(self.data.solver_iter + 1)
|
| 499 |
+
)
|
| 500 |
+
self.add_overlay(
|
| 501 |
+
bottomleft, "Step", str(round(self.data.time / self.model.opt.timestep))
|
| 502 |
+
)
|
| 503 |
+
self.add_overlay(bottomleft, "timestep", "%.5f" % self.model.opt.timestep)
|
| 504 |
+
|
| 505 |
+
def render(self):
|
| 506 |
+
def get_scaled_time():
|
| 507 |
+
return (1.0 / self._run_speed) * self.data.time
|
| 508 |
+
|
| 509 |
+
def time_for_next_frame(next_frame_after=1.0 / 30.0):
|
| 510 |
+
if (
|
| 511 |
+
get_scaled_time() < self.last_time_rendered
|
| 512 |
+
or self.last_time_rendered == 0.0
|
| 513 |
+
):
|
| 514 |
+
return True
|
| 515 |
+
return (get_scaled_time() - self.last_time_rendered) >= next_frame_after
|
| 516 |
+
|
| 517 |
+
def update():
|
| 518 |
+
for i, wc in enumerate(zip(self.windows, self.cams)):
|
| 519 |
+
window, cam_id = wc
|
| 520 |
+
if i == 0:
|
| 521 |
+
self._create_overlay()
|
| 522 |
+
self.cam.fixedcamid = cam_id
|
| 523 |
+
self.cam.type = mujoco.mjtCamera.mjCAMERA_FIXED
|
| 524 |
+
if self.cam.fixedcamid == -1:
|
| 525 |
+
self.cam.type = mujoco.mjtCamera.mjCAMERA_FREE
|
| 526 |
+
|
| 527 |
+
if window is None:
|
| 528 |
+
continue
|
| 529 |
+
elif glfw.window_should_close(window):
|
| 530 |
+
if i == 0:
|
| 531 |
+
glfw.terminate()
|
| 532 |
+
glfw.destroy_window(window)
|
| 533 |
+
self.windows.remove(window)
|
| 534 |
+
self.cams.pop(i)
|
| 535 |
+
continue
|
| 536 |
+
self.viewport.width, self.viewport.height = glfw.get_framebuffer_size(
|
| 537 |
+
window
|
| 538 |
+
)
|
| 539 |
+
with self._gui_lock:
|
| 540 |
+
mujoco.mjv_updateScene(
|
| 541 |
+
self.model,
|
| 542 |
+
self.data,
|
| 543 |
+
self.vopt,
|
| 544 |
+
mujoco.MjvPerturb(),
|
| 545 |
+
self.cam,
|
| 546 |
+
mujoco.mjtCatBit.mjCAT_ALL.value,
|
| 547 |
+
self.scn,
|
| 548 |
+
)
|
| 549 |
+
for marker in self._markers:
|
| 550 |
+
self._add_marker_to_scene(marker)
|
| 551 |
+
glfw.make_context_current(window)
|
| 552 |
+
mujoco.mjr_render(self.viewport, self.scn, self.con)
|
| 553 |
+
if not self._hide_menu:
|
| 554 |
+
for gridpos, [t1, t2] in self._overlays.items():
|
| 555 |
+
mujoco.mjr_overlay(
|
| 556 |
+
mujoco.mjtFontScale.mjFONTSCALE_150,
|
| 557 |
+
gridpos,
|
| 558 |
+
self.viewport,
|
| 559 |
+
t1,
|
| 560 |
+
t2,
|
| 561 |
+
self.con,
|
| 562 |
+
)
|
| 563 |
+
glfw.swap_buffers(window)
|
| 564 |
+
glfw.poll_events()
|
| 565 |
+
if i == 0:
|
| 566 |
+
self._overlays.clear()
|
| 567 |
+
|
| 568 |
+
if self._paused:
|
| 569 |
+
while self._paused:
|
| 570 |
+
update()
|
| 571 |
+
if self._advance_by_one_step:
|
| 572 |
+
self._advance_by_one_step = False
|
| 573 |
+
break
|
| 574 |
+
else:
|
| 575 |
+
if self._render_every_frame or time_for_next_frame():
|
| 576 |
+
update()
|
| 577 |
+
self.last_time_rendered = get_scaled_time()
|
| 578 |
+
|
| 579 |
+
self._markers[:] = []
|
| 580 |
+
|
| 581 |
+
def close(self):
|
| 582 |
+
for window in self.windows:
|
| 583 |
+
glfw.destroy_window(window)
|
| 584 |
+
glfw.terminate()
|
| 585 |
+
|
| 586 |
+
def reinit(self, model, data):
|
| 587 |
+
self.model = model
|
| 588 |
+
self.data = data
|
| 589 |
+
max_geom = 1000
|
| 590 |
+
mujoco.mj_forward(self.model, self.data)
|
| 591 |
+
self.scn = mujoco.MjvScene(self.model, max_geom)
|
| 592 |
+
glfw.make_context_current(self.windows[-1])
|
| 593 |
+
self.con = mujoco.MjrContext(self.model, mujoco.mjtFontScale.mjFONTSCALE_150)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_scene_object.py
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import xml.etree.ElementTree as Et
|
| 3 |
+
from typing import Tuple
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import yaml
|
| 7 |
+
|
| 8 |
+
import d3il.environments.d3il.d3il_sim.sims.mj_beta.MjLoadable as mj_load
|
| 9 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_helper import IncludeType
|
| 10 |
+
from d3il.environments.d3il.d3il_sim.utils import sim_path
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class MujocoObject(mj_load.MjXmlLoadable):
|
| 14 |
+
def __init__(self, object_name, pos, quat, root=sim_path.D3IL_DIR):
|
| 15 |
+
if pos is None:
|
| 16 |
+
pos = [0, 0, 0]
|
| 17 |
+
else:
|
| 18 |
+
assert len(pos) == 3, "Error, parameter pos has to be three dimensional."
|
| 19 |
+
|
| 20 |
+
if quat is None:
|
| 21 |
+
quat = [0, 0, 0, 0]
|
| 22 |
+
else:
|
| 23 |
+
assert len(quat) == 4, "Error, parameter quat has to be four dimensional."
|
| 24 |
+
|
| 25 |
+
self.obj_dir_path = "models/mj/common-objects/" + object_name
|
| 26 |
+
self.obj_file_name = object_name + ".xml"
|
| 27 |
+
self.root = root
|
| 28 |
+
self.pos = pos
|
| 29 |
+
self.quat = quat
|
| 30 |
+
self.name = object_name
|
| 31 |
+
super().__init__(os.path.join(root, self.obj_dir_path, self.obj_file_name))
|
| 32 |
+
|
| 33 |
+
def mj_load(self) -> Tuple[Et.Element, list, IncludeType]:
|
| 34 |
+
include_et, xml, is_include = super(MujocoObject, self).mj_load()
|
| 35 |
+
xml_file = xml[0]
|
| 36 |
+
obj = Et.ElementTree(Et.fromstring(xml_file))
|
| 37 |
+
worldbody = obj.find("worldbody")
|
| 38 |
+
body = worldbody.find("body")
|
| 39 |
+
|
| 40 |
+
obj_pos_str = " ".join(map(str, self.pos))
|
| 41 |
+
obj_quat_str = " ".join(map(str, self.quat))
|
| 42 |
+
|
| 43 |
+
body.set("pos", obj_pos_str)
|
| 44 |
+
body.set("quat", obj_quat_str)
|
| 45 |
+
|
| 46 |
+
return include_et, Et.tostring(obj), IncludeType.FILE_INCLUDE
|
| 47 |
+
|
| 48 |
+
def get_poi(self) -> list:
|
| 49 |
+
return [self.name]
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class CompositeMujocoObject(mj_load.MjLoadable, mj_load.MjFreezable):
|
| 53 |
+
def __init__(self, object_folder, object_name, pos, quat):
|
| 54 |
+
if pos is None:
|
| 55 |
+
pos = [0, 0, 0]
|
| 56 |
+
else:
|
| 57 |
+
assert len(pos) == 3, "Error, parameter pos has to be three dimensional."
|
| 58 |
+
|
| 59 |
+
if quat is None:
|
| 60 |
+
quat = [0, 0, 0, 0]
|
| 61 |
+
else:
|
| 62 |
+
assert len(quat) == 4, "Error, parameter quat has to be four dimensional."
|
| 63 |
+
|
| 64 |
+
self.object_folder = object_folder
|
| 65 |
+
self.pos = pos
|
| 66 |
+
self.quat = quat
|
| 67 |
+
self.name = object_name
|
| 68 |
+
self.file_name = f"{self.name}.xml"
|
| 69 |
+
|
| 70 |
+
def mj_load(self) -> Tuple[Et.Element, list, bool]:
|
| 71 |
+
et_include = Et.Element("include")
|
| 72 |
+
et_include.set("file", self.file_name)
|
| 73 |
+
|
| 74 |
+
xml_string = self.generate_xml()
|
| 75 |
+
return (
|
| 76 |
+
et_include,
|
| 77 |
+
{os.path.join(self.object_folder, self.file_name): xml_string},
|
| 78 |
+
IncludeType.FILE_INCLUDE,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
def generate_xml(self) -> str:
|
| 82 |
+
info_file = os.path.join(self.object_folder, "info.yml")
|
| 83 |
+
assert os.path.isfile(
|
| 84 |
+
info_file
|
| 85 |
+
), f"The file {info_file} was not found. Did you specify the path to the object folder correctly?"
|
| 86 |
+
|
| 87 |
+
with open(info_file, "r") as f:
|
| 88 |
+
info_dict = yaml.safe_load(f)
|
| 89 |
+
|
| 90 |
+
original_file = info_dict["original_file"]
|
| 91 |
+
submesh_files = info_dict["submesh_files"]
|
| 92 |
+
submesh_props = info_dict["submesh_props"]
|
| 93 |
+
weight = info_dict["weight"]
|
| 94 |
+
material_map = info_dict["material_map"]
|
| 95 |
+
|
| 96 |
+
root = Et.Element("mujoco", attrib={"model": self.name})
|
| 97 |
+
|
| 98 |
+
assets = Et.SubElement(root, "asset")
|
| 99 |
+
worldbody = Et.SubElement(root, "worldbody")
|
| 100 |
+
body_attributes = {
|
| 101 |
+
"name": f"{self.name}",
|
| 102 |
+
"pos": " ".join(map(str, self.pos)),
|
| 103 |
+
"quat": " ".join(map(str, self.quat)),
|
| 104 |
+
}
|
| 105 |
+
body = Et.SubElement(worldbody, "body", attrib=body_attributes)
|
| 106 |
+
|
| 107 |
+
texture_attributes = {
|
| 108 |
+
"type": "2d",
|
| 109 |
+
"name": f"{self.name}_tex",
|
| 110 |
+
"file": os.path.join(self.object_folder, material_map),
|
| 111 |
+
}
|
| 112 |
+
texture = Et.SubElement(assets, "texture", attrib=texture_attributes)
|
| 113 |
+
|
| 114 |
+
material_attributes = {
|
| 115 |
+
"name": f"{self.name}_mat",
|
| 116 |
+
"texture": texture_attributes["name"],
|
| 117 |
+
"specular": "0.5",
|
| 118 |
+
"shininess": "0.5",
|
| 119 |
+
}
|
| 120 |
+
material = Et.SubElement(assets, "material", attrib=material_attributes)
|
| 121 |
+
|
| 122 |
+
orig_mesh_attributes = {
|
| 123 |
+
"name": f"{self.name}_orig",
|
| 124 |
+
"file": os.path.join(self.object_folder, original_file),
|
| 125 |
+
}
|
| 126 |
+
orig_mesh = Et.SubElement(assets, "mesh", attrib=orig_mesh_attributes)
|
| 127 |
+
|
| 128 |
+
orig_geom_attributes = {
|
| 129 |
+
"material": material_attributes["name"],
|
| 130 |
+
"mesh": orig_mesh_attributes["name"],
|
| 131 |
+
"group": "2",
|
| 132 |
+
"type": "mesh",
|
| 133 |
+
"contype": "0",
|
| 134 |
+
"conaffinity": "0",
|
| 135 |
+
}
|
| 136 |
+
orig_geom = Et.SubElement(body, "geom", attrib=orig_geom_attributes)
|
| 137 |
+
|
| 138 |
+
for i, (submesh_file, submesh_prop) in enumerate(
|
| 139 |
+
zip(submesh_files, submesh_props)
|
| 140 |
+
):
|
| 141 |
+
collision_mesh_attributes = {
|
| 142 |
+
"name": f"{self.name}_coll_{i}",
|
| 143 |
+
"file": os.path.join(self.object_folder, submesh_file),
|
| 144 |
+
}
|
| 145 |
+
collision_mesh = Et.SubElement(
|
| 146 |
+
assets, "mesh", attrib=collision_mesh_attributes
|
| 147 |
+
)
|
| 148 |
+
collision_geom_attributes = {
|
| 149 |
+
"mesh": collision_mesh_attributes["name"],
|
| 150 |
+
"mass": str(weight * submesh_prop),
|
| 151 |
+
"group": "3",
|
| 152 |
+
"type": "mesh",
|
| 153 |
+
"conaffinity": "1",
|
| 154 |
+
"contype": "1",
|
| 155 |
+
"condim": "4",
|
| 156 |
+
"friction": "0.95 0.3 0.1",
|
| 157 |
+
"rgba": "1 1 1 1",
|
| 158 |
+
"solimp": "0.998 0.998 0.001",
|
| 159 |
+
"solref": "0.001 1",
|
| 160 |
+
}
|
| 161 |
+
collision_geom = Et.SubElement(
|
| 162 |
+
body, "geom", attrib=collision_geom_attributes
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
joint_attributes = {
|
| 166 |
+
"damping": "0.0001",
|
| 167 |
+
"name": f"{self.name}:joint",
|
| 168 |
+
"type": "free",
|
| 169 |
+
}
|
| 170 |
+
joint = Et.SubElement(body, "joint", attrib=joint_attributes)
|
| 171 |
+
|
| 172 |
+
return Et.tostring(root)
|
| 173 |
+
|
| 174 |
+
def get_poi(self) -> list:
|
| 175 |
+
return [self.name]
|
| 176 |
+
|
| 177 |
+
def freeze(self, model, data):
|
| 178 |
+
pos: np.ndarray = data.jnt(self.name + ":joint").qpos
|
| 179 |
+
self.pos = pos[0:3].tolist()
|
| 180 |
+
self.quat = pos[3:].tolist()
|
| 181 |
+
|
| 182 |
+
def unfreeze(self, data, model):
|
| 183 |
+
pass
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
class YCBMujocoObject(CompositeMujocoObject):
|
| 187 |
+
def __init__(self, ycb_base_folder, object_id, object_name, pos, quat):
|
| 188 |
+
self.ycb_base_folder = ycb_base_folder
|
| 189 |
+
self.ycb_object_folder = os.path.join(ycb_base_folder, object_id)
|
| 190 |
+
|
| 191 |
+
super().__init__(self.ycb_object_folder, object_name, pos, quat)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
class CustomMujocoObject(mj_load.MjXmlLoadable):
|
| 195 |
+
def __init__(self, object_name, object_dir_path, pos, quat, root=sim_path.D3IL_DIR):
|
| 196 |
+
if pos is None:
|
| 197 |
+
pos = [0, 0, 0]
|
| 198 |
+
else:
|
| 199 |
+
assert len(pos) == 3, "Error, parameter pos has to be three dimensional."
|
| 200 |
+
|
| 201 |
+
if quat is None:
|
| 202 |
+
quat = [0, 0, 0, 0]
|
| 203 |
+
else:
|
| 204 |
+
assert len(quat) == 4, "Error, parameter quat has to be four dimensional."
|
| 205 |
+
|
| 206 |
+
self.obj_dir_path = object_dir_path
|
| 207 |
+
self.obj_file_name = object_name + ".xml"
|
| 208 |
+
self.root = root
|
| 209 |
+
self.pos = pos
|
| 210 |
+
self.quat = quat
|
| 211 |
+
self.name = object_name
|
| 212 |
+
|
| 213 |
+
super().__init__(os.path.join(root, self.obj_dir_path, self.obj_file_name))
|
| 214 |
+
|
| 215 |
+
def mj_load(self) -> Tuple[Et.Element, list, bool]:
|
| 216 |
+
include_et, xml, is_include = super(CustomMujocoObject, self).mj_load()
|
| 217 |
+
xml_file = list(xml.values())[0]
|
| 218 |
+
obj = Et.ElementTree(Et.fromstring(xml_file))
|
| 219 |
+
worldbody = obj.find("worldbody")
|
| 220 |
+
body = worldbody.find("body")
|
| 221 |
+
|
| 222 |
+
obj_pos_str = " ".join(map(str, self.pos))
|
| 223 |
+
obj_quat_str = " ".join(map(str, self.quat))
|
| 224 |
+
|
| 225 |
+
body.set("pos", obj_pos_str)
|
| 226 |
+
body.set("quat", obj_quat_str)
|
| 227 |
+
|
| 228 |
+
return (
|
| 229 |
+
include_et,
|
| 230 |
+
{
|
| 231 |
+
os.path.join(self.loadable_dir, self.file_name): Et.tostring(
|
| 232 |
+
obj.getroot()
|
| 233 |
+
)
|
| 234 |
+
},
|
| 235 |
+
is_include,
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
def get_poi(self) -> list:
|
| 239 |
+
return [self.name]
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
class MjWorkspace(mj_load.MjXmlLoadable):
|
| 243 |
+
def __init__(self, size: str, root=sim_path.D3IL_DIR):
|
| 244 |
+
if size not in ["small", "medium", "large"]:
|
| 245 |
+
raise ValueError(
|
| 246 |
+
"Error, please choose a size between <small>, <medium> or <large> ."
|
| 247 |
+
)
|
| 248 |
+
self.size = size
|
| 249 |
+
self.root = root
|
| 250 |
+
super().__init__(
|
| 251 |
+
os.path.join(root, "models/mj/workspace/{}".format(self.size), "ws.xml")
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
class MjSurrounding(mj_load.MjXmlLoadable):
|
| 256 |
+
def __init__(self, surrounding_name, root=None):
|
| 257 |
+
self.surrounding_name = surrounding_name
|
| 258 |
+
if root is None:
|
| 259 |
+
root = os.path.join(sim_path.D3IL_DIR, "models/mj/surroundings")
|
| 260 |
+
self.root = root
|
| 261 |
+
|
| 262 |
+
super().__init__(os.path.join(root, "{}.xml".format(self.surrounding_name)))
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/mj_beta/mj_utils/mj_scene_parser.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import xml.etree.ElementTree as Et
|
| 2 |
+
from itertools import chain
|
| 3 |
+
from xml.etree.ElementTree import Element
|
| 4 |
+
|
| 5 |
+
import mujoco
|
| 6 |
+
|
| 7 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.mj_utils.mj_helper import IncludeType
|
| 8 |
+
from d3il.environments.d3il.d3il_sim.sims.mj_beta.MjLoadable import (
|
| 9 |
+
MjLoadable,
|
| 10 |
+
MjIncludeTemplate,
|
| 11 |
+
)
|
| 12 |
+
from d3il.environments.d3il.d3il_sim.utils.sim_path import d3il_path
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class MjSceneParser:
|
| 16 |
+
|
| 17 |
+
def __init__(self, main_xml_path=None):
|
| 18 |
+
if main_xml_path is None:
|
| 19 |
+
main_xml_path = "./models/mj/surroundings/base.xml"
|
| 20 |
+
|
| 21 |
+
with open(d3il_path(main_xml_path), "r") as file:
|
| 22 |
+
self.scene_xml = file.read()
|
| 23 |
+
|
| 24 |
+
self.assets = {}
|
| 25 |
+
self.mj_xml_string = ""
|
| 26 |
+
|
| 27 |
+
self._tree = Et.ElementTree(Et.fromstring(self.scene_xml))
|
| 28 |
+
self._root = self._tree.getroot()
|
| 29 |
+
self._worldbody = self._root.find("worldbody")
|
| 30 |
+
assert self._worldbody, "Error, xml file does contain a world body."
|
| 31 |
+
|
| 32 |
+
def create_scene(self, mj_robots, mj_surrounding, object_list: list, dt):
|
| 33 |
+
if object_list is None:
|
| 34 |
+
object_list = []
|
| 35 |
+
if not isinstance(object_list, list):
|
| 36 |
+
object_list = [object_list]
|
| 37 |
+
|
| 38 |
+
all_objects = chain([mj_surrounding], object_list, mj_robots)
|
| 39 |
+
|
| 40 |
+
for obj in all_objects:
|
| 41 |
+
self.load_mj_loadable(obj)
|
| 42 |
+
|
| 43 |
+
self.set_dt(dt)
|
| 44 |
+
|
| 45 |
+
model = mujoco.MjModel.from_xml_string(self.mj_xml_string, self.assets)
|
| 46 |
+
data = mujoco.MjData(model)
|
| 47 |
+
|
| 48 |
+
self.cleanup(all_objects)
|
| 49 |
+
return model, data
|
| 50 |
+
|
| 51 |
+
def load_mj_loadable(self, mj_loadable: MjLoadable):
|
| 52 |
+
xml_element, assets, include_type = mj_loadable.mj_load()
|
| 53 |
+
if include_type == IncludeType.FILE_INCLUDE:
|
| 54 |
+
self._root.append(xml_element)
|
| 55 |
+
elif include_type == IncludeType.WORLD_BODY:
|
| 56 |
+
self._worldbody.append(xml_element)
|
| 57 |
+
elif include_type == IncludeType.MJ_INCLUDE:
|
| 58 |
+
for child in xml_element:
|
| 59 |
+
self._root.append(child)
|
| 60 |
+
elif include_type == IncludeType.VIRTUAL_INCLUDE:
|
| 61 |
+
return
|
| 62 |
+
else:
|
| 63 |
+
raise ValueError("Unknown IncludeType")
|
| 64 |
+
|
| 65 |
+
self.indent(self._root)
|
| 66 |
+
|
| 67 |
+
self.mj_xml_string = Et.tostring(self._root, encoding="unicode", method="xml")
|
| 68 |
+
self.assets.update(assets)
|
| 69 |
+
|
| 70 |
+
def set_dt(self, dt=0.001):
|
| 71 |
+
options = self._root.find("option")
|
| 72 |
+
options.set("timestep", str(dt))
|
| 73 |
+
self.indent(self._root)
|
| 74 |
+
self.mj_xml_string = Et.tostring(self._root, encoding="unicode", method="xml")
|
| 75 |
+
|
| 76 |
+
def indent(self, elem: Element, level=0):
|
| 77 |
+
i = "\n" + level * " "
|
| 78 |
+
if len(elem):
|
| 79 |
+
if not elem.text or not elem.text.strip():
|
| 80 |
+
elem.text = i + " "
|
| 81 |
+
if not elem.tail or not elem.tail.strip():
|
| 82 |
+
elem.tail = i
|
| 83 |
+
for elem in elem:
|
| 84 |
+
self.indent(elem, level + 1)
|
| 85 |
+
if not elem.tail or not elem.tail.strip():
|
| 86 |
+
elem.tail = i
|
| 87 |
+
else:
|
| 88 |
+
if level and (not elem.tail or not elem.tail.strip()):
|
| 89 |
+
elem.tail = i
|
| 90 |
+
|
| 91 |
+
def cleanup(self, objects: list):
|
| 92 |
+
for obj in objects:
|
| 93 |
+
if isinstance(obj, MjIncludeTemplate):
|
| 94 |
+
obj.cleanup()
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/universal_sim/PrimitiveObjects.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
from typing import List, Tuple
|
| 3 |
+
|
| 4 |
+
from d3il.environments.d3il.d3il_sim.core.sim_object.sim_object import SimObject
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class PrimitiveObject(SimObject):
|
| 8 |
+
class Shape(Enum):
|
| 9 |
+
BOX = "box"
|
| 10 |
+
SPHERE = "sphere"
|
| 11 |
+
CYLINDER = "cylinder"
|
| 12 |
+
|
| 13 |
+
def __init__(
|
| 14 |
+
self,
|
| 15 |
+
name: str,
|
| 16 |
+
init_pos,
|
| 17 |
+
init_quat,
|
| 18 |
+
mass: float,
|
| 19 |
+
size,
|
| 20 |
+
rgba,
|
| 21 |
+
static: bool,
|
| 22 |
+
visual_only: bool,
|
| 23 |
+
obj_type: Shape,
|
| 24 |
+
solimp: List[float] = None,
|
| 25 |
+
solref: List[float] = None,
|
| 26 |
+
):
|
| 27 |
+
super(PrimitiveObject, self).__init__(name, init_pos, init_quat)
|
| 28 |
+
self.type = obj_type
|
| 29 |
+
self.mass = mass
|
| 30 |
+
self.size = size
|
| 31 |
+
self.static = static
|
| 32 |
+
self.visual_only = visual_only
|
| 33 |
+
|
| 34 |
+
if rgba is None:
|
| 35 |
+
rgba = [0.5, 0.5, 0.5, 1.0]
|
| 36 |
+
self.rgba = rgba
|
| 37 |
+
|
| 38 |
+
self.solimp = solimp
|
| 39 |
+
self.solref = solref
|
| 40 |
+
|
| 41 |
+
def get_poi(self) -> list:
|
| 42 |
+
return [self.name]
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class Box(PrimitiveObject):
|
| 46 |
+
def __init__(
|
| 47 |
+
self,
|
| 48 |
+
name: str,
|
| 49 |
+
init_pos,
|
| 50 |
+
init_quat,
|
| 51 |
+
mass: float = 0.1,
|
| 52 |
+
size=None,
|
| 53 |
+
rgba=None,
|
| 54 |
+
static: bool = False,
|
| 55 |
+
visual_only: bool = False,
|
| 56 |
+
solimp: List[float] = None,
|
| 57 |
+
solref: List[float] = None,
|
| 58 |
+
):
|
| 59 |
+
if size is None:
|
| 60 |
+
size = [0.02, 0.02, 0.02]
|
| 61 |
+
|
| 62 |
+
if len(size) != 3:
|
| 63 |
+
raise ValueError("Expected list of size three for size attribute.")
|
| 64 |
+
super(Box, self).__init__(
|
| 65 |
+
name,
|
| 66 |
+
init_pos,
|
| 67 |
+
init_quat,
|
| 68 |
+
mass,
|
| 69 |
+
size,
|
| 70 |
+
rgba,
|
| 71 |
+
static,
|
| 72 |
+
visual_only,
|
| 73 |
+
self.Shape.BOX,
|
| 74 |
+
solimp,
|
| 75 |
+
solref,
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class Sphere(PrimitiveObject):
|
| 80 |
+
def __init__(
|
| 81 |
+
self,
|
| 82 |
+
name: str,
|
| 83 |
+
init_pos,
|
| 84 |
+
init_quat,
|
| 85 |
+
mass: float = 0.1,
|
| 86 |
+
size=None,
|
| 87 |
+
rgba=None,
|
| 88 |
+
static: bool = False,
|
| 89 |
+
visual_only: bool = False,
|
| 90 |
+
solimp: List[float] = None,
|
| 91 |
+
solref: List[float] = None,
|
| 92 |
+
):
|
| 93 |
+
if size is None:
|
| 94 |
+
size = [0.02]
|
| 95 |
+
|
| 96 |
+
if len(size) != 1:
|
| 97 |
+
raise ValueError("Expected list of size one for size attribute")
|
| 98 |
+
super(Sphere, self).__init__(
|
| 99 |
+
name,
|
| 100 |
+
init_pos,
|
| 101 |
+
init_quat,
|
| 102 |
+
mass,
|
| 103 |
+
size,
|
| 104 |
+
rgba,
|
| 105 |
+
static,
|
| 106 |
+
visual_only,
|
| 107 |
+
self.Shape.SPHERE,
|
| 108 |
+
solimp,
|
| 109 |
+
solref,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
class Cylinder(PrimitiveObject):
|
| 114 |
+
def __init__(
|
| 115 |
+
self,
|
| 116 |
+
name: str,
|
| 117 |
+
init_pos,
|
| 118 |
+
init_quat,
|
| 119 |
+
mass: float = 0.1,
|
| 120 |
+
size=None,
|
| 121 |
+
rgba=None,
|
| 122 |
+
static: bool = False,
|
| 123 |
+
visual_only: bool = False,
|
| 124 |
+
solimp: List[float] = None,
|
| 125 |
+
solref: List[float] = None,
|
| 126 |
+
):
|
| 127 |
+
if size is None:
|
| 128 |
+
size = [0.02, 0.02]
|
| 129 |
+
|
| 130 |
+
if len(size) != 2:
|
| 131 |
+
raise ValueError("Expected list of size two for size attribute.")
|
| 132 |
+
super(Cylinder, self).__init__(
|
| 133 |
+
name,
|
| 134 |
+
init_pos,
|
| 135 |
+
init_quat,
|
| 136 |
+
mass,
|
| 137 |
+
size,
|
| 138 |
+
rgba,
|
| 139 |
+
static,
|
| 140 |
+
visual_only,
|
| 141 |
+
self.Shape.CYLINDER,
|
| 142 |
+
solimp,
|
| 143 |
+
solref,
|
| 144 |
+
)
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/sims/universal_sim/__init__.py
ADDED
|
File without changes
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/utils/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .geometric_transformation import *
|
applications/avoiding/avoiding_baselines/DiRecT/d3il/environments/d3il/d3il_sim/utils/geometric_transformation.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import itertools
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
_FLOAT_EPS = np.finfo(np.float64).eps
|
| 6 |
+
_EPS4 = _FLOAT_EPS * 4.0
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def get_quaternion_error(curr_quat, des_quat):
|
| 10 |
+
|
| 11 |
+
quatError = np.zeros((3,))
|
| 12 |
+
|
| 13 |
+
quatError[0] = (
|
| 14 |
+
curr_quat[0] * des_quat[1]
|
| 15 |
+
- des_quat[0] * curr_quat[1]
|
| 16 |
+
- curr_quat[3] * des_quat[2]
|
| 17 |
+
+ curr_quat[2] * des_quat[3]
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
quatError[1] = (
|
| 21 |
+
curr_quat[0] * des_quat[2]
|
| 22 |
+
- des_quat[0] * curr_quat[2]
|
| 23 |
+
+ curr_quat[3] * des_quat[1]
|
| 24 |
+
- curr_quat[1] * des_quat[3]
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
quatError[2] = (
|
| 28 |
+
curr_quat[0] * des_quat[3]
|
| 29 |
+
- des_quat[0] * curr_quat[3]
|
| 30 |
+
- curr_quat[2] * des_quat[1]
|
| 31 |
+
+ curr_quat[1] * des_quat[2]
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
return quatError
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def euler2mat(euler):
|
| 38 |
+
euler = np.asarray(euler, dtype=np.float64)
|
| 39 |
+
assert euler.shape[-1] == 3, "Invalid shaped euler {}".format(euler)
|
| 40 |
+
|
| 41 |
+
ai, aj, ak = -euler[..., 2], -euler[..., 1], -euler[..., 0]
|
| 42 |
+
si, sj, sk = np.sin(ai), np.sin(aj), np.sin(ak)
|
| 43 |
+
ci, cj, ck = np.cos(ai), np.cos(aj), np.cos(ak)
|
| 44 |
+
cc, cs = ci * ck, ci * sk
|
| 45 |
+
sc, ss = si * ck, si * sk
|
| 46 |
+
|
| 47 |
+
mat = np.empty(euler.shape[:-1] + (3, 3), dtype=np.float64)
|
| 48 |
+
mat[..., 2, 2] = cj * ck
|
| 49 |
+
mat[..., 2, 1] = sj * sc - cs
|
| 50 |
+
mat[..., 2, 0] = sj * cc + ss
|
| 51 |
+
mat[..., 1, 2] = cj * sk
|
| 52 |
+
mat[..., 1, 1] = sj * ss + cc
|
| 53 |
+
mat[..., 1, 0] = sj * cs - sc
|
| 54 |
+
mat[..., 0, 2] = -sj
|
| 55 |
+
mat[..., 0, 1] = cj * si
|
| 56 |
+
mat[..., 0, 0] = cj * ci
|
| 57 |
+
return mat
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def euler2quat(euler):
|
| 61 |
+
euler = np.asarray(euler, dtype=np.float64)
|
| 62 |
+
assert euler.shape[-1] == 3, "Invalid shape euler {}".format(euler)
|
| 63 |
+
|
| 64 |
+
ai, aj, ak = euler[..., 2] / 2, -euler[..., 1] / 2, euler[..., 0] / 2
|
| 65 |
+
si, sj, sk = np.sin(ai), np.sin(aj), np.sin(ak)
|
| 66 |
+
ci, cj, ck = np.cos(ai), np.cos(aj), np.cos(ak)
|
| 67 |
+
cc, cs = ci * ck, ci * sk
|
| 68 |
+
sc, ss = si * ck, si * sk
|
| 69 |
+
|
| 70 |
+
quat = np.empty(euler.shape[:-1] + (4,), dtype=np.float64)
|
| 71 |
+
quat[..., 0] = cj * cc + sj * ss
|
| 72 |
+
quat[..., 3] = cj * sc - sj * cs
|
| 73 |
+
quat[..., 2] = -(cj * ss + sj * cc)
|
| 74 |
+
quat[..., 1] = cj * cs - sj * sc
|
| 75 |
+
return quat
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def mat2euler(mat):
|
| 79 |
+
mat = np.asarray(mat, dtype=np.float64)
|
| 80 |
+
assert mat.shape[-2:] == (3, 3), "Invalid shape matrix {}".format(mat)
|
| 81 |
+
|
| 82 |
+
cy = np.sqrt(mat[..., 2, 2] * mat[..., 2, 2] + mat[..., 1, 2] * mat[..., 1, 2])
|
| 83 |
+
condition = cy > _EPS4
|
| 84 |
+
euler = np.empty(mat.shape[:-1], dtype=np.float64)
|
| 85 |
+
euler[..., 2] = np.where(
|
| 86 |
+
condition,
|
| 87 |
+
-np.arctan2(mat[..., 0, 1], mat[..., 0, 0]),
|
| 88 |
+
-np.arctan2(-mat[..., 1, 0], mat[..., 1, 1]),
|
| 89 |
+
)
|
| 90 |
+
euler[..., 1] = np.where(
|
| 91 |
+
condition, -np.arctan2(-mat[..., 0, 2], cy), -np.arctan2(-mat[..., 0, 2], cy)
|
| 92 |
+
)
|
| 93 |
+
euler[..., 0] = np.where(
|
| 94 |
+
condition, -np.arctan2(mat[..., 1, 2], mat[..., 2, 2]), 0.0
|
| 95 |
+
)
|
| 96 |
+
return euler
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def mat2quat(mat):
|
| 100 |
+
mat = np.asarray(mat, dtype=np.float64)
|
| 101 |
+
assert mat.shape[-2:] == (3, 3), "Invalid shape matrix {}".format(mat)
|
| 102 |
+
|
| 103 |
+
Qxx, Qyx, Qzx = mat[..., 0, 0], mat[..., 0, 1], mat[..., 0, 2]
|
| 104 |
+
Qxy, Qyy, Qzy = mat[..., 1, 0], mat[..., 1, 1], mat[..., 1, 2]
|
| 105 |
+
Qxz, Qyz, Qzz = mat[..., 2, 0], mat[..., 2, 1], mat[..., 2, 2]
|
| 106 |
+
K = np.zeros(mat.shape[:-2] + (4, 4), dtype=np.float64)
|
| 107 |
+
K[..., 0, 0] = Qxx - Qyy - Qzz
|
| 108 |
+
K[..., 1, 0] = Qyx + Qxy
|
| 109 |
+
K[..., 1, 1] = Qyy - Qxx - Qzz
|
| 110 |
+
K[..., 2, 0] = Qzx + Qxz
|
| 111 |
+
K[..., 2, 1] = Qzy + Qyz
|
| 112 |
+
K[..., 2, 2] = Qzz - Qxx - Qyy
|
| 113 |
+
K[..., 3, 0] = Qyz - Qzy
|
| 114 |
+
K[..., 3, 1] = Qzx - Qxz
|
| 115 |
+
K[..., 3, 2] = Qxy - Qyx
|
| 116 |
+
K[..., 3, 3] = Qxx + Qyy + Qzz
|
| 117 |
+
K /= 3.0
|
| 118 |
+
q = np.empty(K.shape[:-2] + (4,))
|
| 119 |
+
it = np.nditer(q[..., 0], flags=["multi_index"])
|
| 120 |
+
while not it.finished:
|
| 121 |
+
vals, vecs = np.linalg.eigh(K[it.multi_index])
|
| 122 |
+
q[it.multi_index] = vecs[[3, 0, 1, 2], np.argmax(vals)]
|
| 123 |
+
if q[it.multi_index][0] < 0:
|
| 124 |
+
q[it.multi_index] *= -1
|
| 125 |
+
it.iternext()
|
| 126 |
+
return q
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def quat2euler(quat):
|
| 130 |
+
return mat2euler(quat2mat(quat))
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def subtract_euler(e1, e2):
|
| 134 |
+
assert e1.shape == e2.shape
|
| 135 |
+
assert e1.shape[-1] == 3
|
| 136 |
+
q1 = euler2quat(e1)
|
| 137 |
+
q2 = euler2quat(e2)
|
| 138 |
+
q_diff = quat_mul(q1, quat_conjugate(q2))
|
| 139 |
+
return quat2euler(q_diff)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def quat2mat(quat):
|
| 143 |
+
quat = np.asarray(quat, dtype=np.float64)
|
| 144 |
+
assert quat.shape[-1] == 4, "Invalid shape quat {}".format(quat)
|
| 145 |
+
|
| 146 |
+
w, x, y, z = quat[..., 0], quat[..., 1], quat[..., 2], quat[..., 3]
|
| 147 |
+
Nq = np.sum(quat * quat, axis=-1)
|
| 148 |
+
s = 2.0 / Nq
|
| 149 |
+
X, Y, Z = x * s, y * s, z * s
|
| 150 |
+
wX, wY, wZ = w * X, w * Y, w * Z
|
| 151 |
+
xX, xY, xZ = x * X, x * Y, x * Z
|
| 152 |
+
yY, yZ, zZ = y * Y, y * Z, z * Z
|
| 153 |
+
|
| 154 |
+
mat = np.empty(quat.shape[:-1] + (3, 3), dtype=np.float64)
|
| 155 |
+
mat[..., 0, 0] = 1.0 - (yY + zZ)
|
| 156 |
+
mat[..., 0, 1] = xY - wZ
|
| 157 |
+
mat[..., 0, 2] = xZ + wY
|
| 158 |
+
mat[..., 1, 0] = xY + wZ
|
| 159 |
+
mat[..., 1, 1] = 1.0 - (xX + zZ)
|
| 160 |
+
mat[..., 1, 2] = yZ - wX
|
| 161 |
+
mat[..., 2, 0] = xZ - wY
|
| 162 |
+
mat[..., 2, 1] = yZ + wX
|
| 163 |
+
mat[..., 2, 2] = 1.0 - (xX + yY)
|
| 164 |
+
return np.where((Nq > _FLOAT_EPS)[..., np.newaxis, np.newaxis], mat, np.eye(3))
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def quat_conjugate(q):
|
| 168 |
+
inv_q = -q
|
| 169 |
+
inv_q[..., 0] *= -1
|
| 170 |
+
return inv_q
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def quat_mul(q0, q1):
|
| 174 |
+
assert q0.shape == q1.shape
|
| 175 |
+
assert q0.shape[-1] == 4
|
| 176 |
+
assert q1.shape[-1] == 4
|
| 177 |
+
|
| 178 |
+
w0 = q0[..., 0]
|
| 179 |
+
x0 = q0[..., 1]
|
| 180 |
+
y0 = q0[..., 2]
|
| 181 |
+
z0 = q0[..., 3]
|
| 182 |
+
|
| 183 |
+
w1 = q1[..., 0]
|
| 184 |
+
x1 = q1[..., 1]
|
| 185 |
+
y1 = q1[..., 2]
|
| 186 |
+
z1 = q1[..., 3]
|
| 187 |
+
|
| 188 |
+
w = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1
|
| 189 |
+
x = w0 * x1 + x0 * w1 + y0 * z1 - z0 * y1
|
| 190 |
+
y = w0 * y1 + y0 * w1 + z0 * x1 - x0 * z1
|
| 191 |
+
z = w0 * z1 + z0 * w1 + x0 * y1 - y0 * x1
|
| 192 |
+
q = np.array([w, x, y, z])
|
| 193 |
+
if q.ndim == 2:
|
| 194 |
+
q = q.swapaxes(0, 1)
|
| 195 |
+
assert q.shape == q0.shape
|
| 196 |
+
return q
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def quat_rot_vec(q, v0):
|
| 200 |
+
q_v0 = np.array([0, v0[0], v0[1], v0[2]])
|
| 201 |
+
q_v = quat_mul(q, quat_mul(q_v0, quat_conjugate(q)))
|
| 202 |
+
v = q_v[1:]
|
| 203 |
+
return v
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def quat_identity():
|
| 207 |
+
return np.array([1, 0, 0, 0])
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def quat2axisangle(quat):
|
| 211 |
+
theta = 0
|
| 212 |
+
axis = np.array([0, 0, 1])
|
| 213 |
+
sin_theta = np.linalg.norm(quat[1:])
|
| 214 |
+
|
| 215 |
+
if sin_theta > 0.0001:
|
| 216 |
+
theta = 2 * np.arcsin(sin_theta)
|
| 217 |
+
theta *= 1 if quat[0] >= 0 else -1
|
| 218 |
+
axis = quat[1:] / sin_theta
|
| 219 |
+
|
| 220 |
+
return axis, theta
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def euler2point_euler(euler):
|
| 224 |
+
_euler = euler.copy()
|
| 225 |
+
if len(_euler.shape) < 2:
|
| 226 |
+
_euler = np.expand_dims(_euler, 0)
|
| 227 |
+
assert _euler.shape[1] == 3
|
| 228 |
+
_euler_sin = np.sin(_euler)
|
| 229 |
+
_euler_cos = np.cos(_euler)
|
| 230 |
+
return np.concatenate([_euler_sin, _euler_cos], axis=-1)
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def point_euler2euler(euler):
|
| 234 |
+
_euler = euler.copy()
|
| 235 |
+
if len(_euler.shape) < 2:
|
| 236 |
+
_euler = np.expand_dims(_euler, 0)
|
| 237 |
+
assert _euler.shape[1] == 6
|
| 238 |
+
angle = np.arctan(_euler[..., :3] / _euler[..., 3:])
|
| 239 |
+
angle[_euler[..., 3:] < 0] += np.pi
|
| 240 |
+
return angle
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def quat2point_quat(quat):
|
| 244 |
+
_quat = quat.copy()
|
| 245 |
+
if len(_quat.shape) < 2:
|
| 246 |
+
_quat = np.expand_dims(_quat, 0)
|
| 247 |
+
assert _quat.shape[1] == 4
|
| 248 |
+
angle = np.arccos(_quat[:, [0]]) * 2
|
| 249 |
+
xyz = _quat[:, 1:]
|
| 250 |
+
xyz[np.squeeze(np.abs(np.sin(angle / 2))) >= 1e-5] = (xyz / np.sin(angle / 2))[
|
| 251 |
+
np.squeeze(np.abs(np.sin(angle / 2))) >= 1e-5
|
| 252 |
+
]
|
| 253 |
+
return np.concatenate([np.sin(angle), np.cos(angle), xyz], axis=-1)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def point_quat2quat(quat):
|
| 257 |
+
_quat = quat.copy()
|
| 258 |
+
if len(_quat.shape) < 2:
|
| 259 |
+
_quat = np.expand_dims(_quat, 0)
|
| 260 |
+
assert _quat.shape[1] == 5
|
| 261 |
+
angle = np.arctan(_quat[:, [0]] / _quat[:, [1]])
|
| 262 |
+
qw = np.cos(angle / 2)
|
| 263 |
+
|
| 264 |
+
qxyz = _quat[:, 2:]
|
| 265 |
+
qxyz[np.squeeze(np.abs(np.sin(angle / 2))) >= 1e-5] = (qxyz * np.sin(angle / 2))[
|
| 266 |
+
np.squeeze(np.abs(np.sin(angle / 2))) >= 1e-5
|
| 267 |
+
]
|
| 268 |
+
return np.concatenate([qw, qxyz], axis=-1)
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def normalize_angles(angles):
|
| 272 |
+
angles = angles.copy()
|
| 273 |
+
if angles.size > 0:
|
| 274 |
+
angles = (angles + np.pi) % (2 * np.pi) - np.pi
|
| 275 |
+
assert -np.pi - 1e-6 <= angles.min() and angles.max() <= np.pi + 1e-6
|
| 276 |
+
return angles
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def round_to_straight_angles(angles):
|
| 280 |
+
angles = np.round(angles / (np.pi / 2)) * (np.pi / 2)
|
| 281 |
+
return normalize_angles(angles)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def get_parallel_rotations():
|
| 285 |
+
mult90 = [0, np.pi / 2, -np.pi / 2, np.pi]
|
| 286 |
+
parallel_rotations = []
|
| 287 |
+
for euler in itertools.product(mult90, repeat=3):
|
| 288 |
+
canonical = mat2euler(euler2mat(euler))
|
| 289 |
+
canonical = np.round(canonical / (np.pi / 2))
|
| 290 |
+
if canonical[0] == -2:
|
| 291 |
+
canonical[0] = 2
|
| 292 |
+
if canonical[2] == -2:
|
| 293 |
+
canonical[2] = 2
|
| 294 |
+
canonical *= np.pi / 2
|
| 295 |
+
if all([(canonical != rot).any() for rot in parallel_rotations]):
|
| 296 |
+
parallel_rotations += [canonical]
|
| 297 |
+
assert len(parallel_rotations) == 24
|
| 298 |
+
return parallel_rotations
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def posRotMat2TFMat(pos, rot_mat):
|
| 302 |
+
t_mat = np.eye(4)
|
| 303 |
+
t_mat[:3, :3] = rot_mat
|
| 304 |
+
t_mat[:3, 3] = np.array(pos)
|
| 305 |
+
return t_mat
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def mat2posQuat(mat):
|
| 309 |
+
pos = mat[:3, 3]
|
| 310 |
+
quat = mat2quat(mat[:3, :3])
|
| 311 |
+
return pos, quat
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def wxyz_to_xyzw(quat):
|
| 315 |
+
return np.roll(quat, -1)
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def xyzw_to_wxyz(quat):
|
| 319 |
+
return np.roll(quat, 1)
|