initial commit
This commit is contained in:
Executable
+70
@@ -0,0 +1,70 @@
|
||||
runs
|
||||
# Jaynes secret files
|
||||
.secret*
|
||||
# ml_logger cache files
|
||||
.cache
|
||||
# These are some examples of commonly ignored file patterns.
|
||||
# You should customize this list as applicable to your project.
|
||||
# Learn more about .gitignore:
|
||||
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
|
||||
|
||||
# Node artifact files
|
||||
node_modules/
|
||||
dist/
|
||||
|
||||
# Compiled Java class files
|
||||
*.class
|
||||
|
||||
# Compiled Python bytecode
|
||||
*.py[cod]
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# Package files
|
||||
*.jar
|
||||
|
||||
# Maven
|
||||
target/
|
||||
dist/
|
||||
|
||||
# JetBrains IDE
|
||||
.idea/
|
||||
|
||||
# Unit test reports
|
||||
TEST*.xml
|
||||
|
||||
# Generated by MacOS
|
||||
.DS_Store
|
||||
|
||||
# Generated by Windows
|
||||
Thumbs.db
|
||||
|
||||
# Applications
|
||||
*.app
|
||||
*.exe
|
||||
*.war
|
||||
|
||||
# Large media files
|
||||
*.mp4
|
||||
*.tiff
|
||||
*.avi
|
||||
*.flv
|
||||
*.mov
|
||||
*.wmv
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
# logs
|
||||
logs
|
||||
runs
|
||||
|
||||
# other
|
||||
*.egg-info
|
||||
__pycache__
|
||||
|
||||
docker/rsc
|
||||
*.swp
|
||||
*.jit
|
||||
tmp
|
||||
*.tar
|
||||
@@ -0,0 +1,25 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 MIT Improbable AI Lab
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
See licenses/legged_gym and licenses/rsl_rl for additional license information for some files in this package.
|
||||
Files associated with these additional licenses indicate so in the header.
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
Copyright (c) 2021, ETH Zurich, Nikita Rudin
|
||||
Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
See licenses/assets for license information for assets included in this repository.
|
||||
See licenses/dependencies for license information of dependencies of this package.
|
||||
@@ -0,0 +1,30 @@
|
||||
Copyright (c) 2021, ETH Zurich, Nikita Rudin
|
||||
Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
See licenses/dependencies for license information of dependencies of this package.
|
||||
@@ -0,0 +1,232 @@
|
||||
# Go1 Sim-to-Real Locomotion Starter Kit
|
||||
|
||||
|
||||
# Table of contents
|
||||
1. [Overview](#overview)
|
||||
2. [System Requirements](#requirements)
|
||||
3. [Training a Model](#simulation)
|
||||
1. [Installation](#installation)
|
||||
2. [Environment and Model Configuration](#configuration)
|
||||
3. [Training and Logging](#training)
|
||||
4. [Analyzing the Policy](#analysis)
|
||||
4. [Deploying a Model](#realworld)
|
||||
1. [Installing the Deployment Utility](#robotconfig)
|
||||
2. [Running the Controller](#runcontroller)
|
||||
3. [RC Configuration](#rcconfig)
|
||||
2. [Deploying a Custom Model](#configuration)
|
||||
4. [Deployment and Logging](#deployment)
|
||||
5. [Analyzing Real-world Performance](#plotting)
|
||||
5. [Acknowledgements](#acknowledgements)
|
||||
|
||||
## Overview <a name="introduction"></a>
|
||||
|
||||
This repository provides an implementation of the paper:
|
||||
|
||||
|
||||
<td style="padding:20px;width:75%;vertical-align:middle">
|
||||
<a href="">
|
||||
<b> Walk these Ways: Learning with Parametric Auxiliary Rewards for an Uncertain Future </b>
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://gmargo11.github.io/" target="_blank">Gabriel B. Margolis</a> and <a href="https://people.csail.mit.edu/pulkitag" target="_blank">Pulkit Agrawal</a>
|
||||
<br>
|
||||
<em>Conference on Robot Learning</em>, 2022
|
||||
<br>
|
||||
<a href="">paper</a> /
|
||||
<a href="">bibtex</a> /
|
||||
<a href="https://sites.google.com/view/gait-conditioned-rl/" target="_blank">project page</a>
|
||||
<br>
|
||||
</td>
|
||||
|
||||
<br>
|
||||
|
||||
This environment builds on the [legged gym environment](https://leggedrobotics.github.io/legged_gym/) by Nikita
|
||||
Rudin, Robotic Systems Lab, ETH Zurich (Paper: https://arxiv.org/abs/2109.11978) and the Isaac Gym simulator from
|
||||
NVIDIA (Paper: https://arxiv.org/abs/2108.10470). Training code builds on the
|
||||
[rsl_rl](https://github.com/leggedrobotics/rsl_rl) repository, also by Nikita
|
||||
Rudin, Robotic Systems Lab, ETH Zurich. All redistributed code retains its
|
||||
original [license](LICENSES/legged_gym/LICENSE).
|
||||
|
||||
Our initial release provides the following features:
|
||||
* Train reinforcement learning policies for the Go1 robot using PPO, IsaacGym, Domain Randomization and Parametric Auxiliary Rewards.
|
||||
* Evaluate a pretrained parametric auxiliary reward policy in simulation.
|
||||
* Deploy learned policies on the Go1 using the `unitree_legged_sdk`.
|
||||
|
||||
## System Requirements <a name="requirements"></a>
|
||||
|
||||
**Simulated Training and Evaluation**: Isaac Gym requires an NVIDIA GPU. To train in the default configuration, we recommend a GPU with at least 10GB of VRAM. The code can run on a smaller GPU if you decrease the number of parallel environments (`Cfg.env.num_envs`). However, training will be slower with fewer environments.
|
||||
|
||||
**Hardware Deployment**: We provide deployment code for the Unitree Go1 Edu robot. This relatively low-cost, commercially available quadruped can be purchased here: https://shop.unitree.com/. You will need the Edu version of the robot to run and customize your locomotion controller.
|
||||
|
||||
<b>Users are advised to follow Unitree's recommendations for safety while using the Go1 in low-level control mode. This means hanging up the robot and keeping it away from people and obstacles. Use our code at your own risk; we do not take responsibility for any damage.</b>
|
||||
|
||||
## Training a Model <a name="simulation"></a>
|
||||
|
||||
### Installation <a name="installation"></a>
|
||||
|
||||
#### Install pytorch 1.10 with cuda-11.3:
|
||||
|
||||
```bash
|
||||
pip3 install torch==1.10.0+cu113 torchvision==0.11.1+cu113 torchaudio==0.10.0+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html
|
||||
```
|
||||
|
||||
#### Install Isaac Gym
|
||||
|
||||
1. Download and install Isaac Gym Preview 4 from https://developer.nvidia.com/isaac-gym
|
||||
2. unzip the file via:
|
||||
```bash
|
||||
tar -xf IsaacGym_Preview_4_Package.tar.gz
|
||||
```
|
||||
|
||||
3. now install the python package
|
||||
```bash
|
||||
cd isaacgym/python && pip install -e .
|
||||
```
|
||||
4. Verify the installation by try running an example
|
||||
|
||||
```bash
|
||||
python examples/1080_balls_of_solitude.py
|
||||
```
|
||||
5. For troubleshooting check docs `isaacgym/docs/index.html`
|
||||
|
||||
#### Install the `go1_gym` package
|
||||
|
||||
In this repository, run `pip install -e .`
|
||||
|
||||
### Verifying the Installation
|
||||
|
||||
If everything is installed correctly, you should be able to run the test script with:
|
||||
|
||||
```bash
|
||||
python scripts/test.py
|
||||
```
|
||||
|
||||
The script should print `Simulating step {i}`.
|
||||
The GUI is off by default. To turn it on, set `headless=False` in `test.py`'s main function call.
|
||||
|
||||
### Environment and Model Configuration <a name="configuration"></a>
|
||||
|
||||
|
||||
**CODE STRUCTURE** The main environment for simulating a legged robot is
|
||||
in [legged_robot.py](go1_gym/envs/base/legged_robot.py). The default configuration parameters including reward
|
||||
weightings are defined in [legged_robot_config.py::Cfg](go1_gym/envs/base/legged_robot_config.py).
|
||||
|
||||
There are three scripts in the [scripts](scripts/) directory:
|
||||
|
||||
```bash
|
||||
scripts
|
||||
├── __init__.py
|
||||
├── play.py
|
||||
├── test.py
|
||||
└── train.py
|
||||
```
|
||||
|
||||
You can run the `test.py` script to verify your environment setup. If it runs then you have installed the gym
|
||||
environments correctly. To train an agent, run `train.py`. To evaluate a pretrained agent, run `play.py`. We provie a
|
||||
pretrained agent checkpoint in the [./runs/pretrain-v0](runs/pretrain-v0) directory.
|
||||
|
||||
|
||||
|
||||
### Training and Logging <a name="training"></a>
|
||||
|
||||
To train the Go1 controller from [Walk these Ways](https://sites.google.com/view/gait-conditioned-rl/), run:
|
||||
|
||||
```bash
|
||||
python scripts/train.py
|
||||
```
|
||||
|
||||
After initializing the simulator, the script will print out a list of metrics every ten training iterations.
|
||||
|
||||
Training with the default configuration requires about 12GB of GPU memory. If you have less memory available, you can
|
||||
still train by reducing the number of parallel environments used in simulation (the default is `Cfg.env.num_envs = 4000`).
|
||||
|
||||
To visualize training progress, first start the ml_dash frontend app:
|
||||
```bash
|
||||
python -m ml_dash.app
|
||||
```
|
||||
then start the ml_dash backend server by running this command in the parent directory of the `runs` folder:
|
||||
```bash
|
||||
python -m ml_dash.server .
|
||||
```
|
||||
|
||||
Finally, use a web browser to go to the app IP (defaults to `localhost:3001`)
|
||||
and create a new profile with the credentials:
|
||||
|
||||
Username: `runs`
|
||||
API: [server IP] (defaults to `localhost:8081`)
|
||||
Access Token: [blank]
|
||||
|
||||
Now, clicking on the profile should yield a
|
||||
|
||||
### Analyzing the Policy <a name="analysis"></a>
|
||||
|
||||
To evaluate the most recently trained model, run:
|
||||
|
||||
```bash
|
||||
python scripts/play.py
|
||||
```
|
||||
|
||||
The robot is commanded to run forward at 3m/s for 5 seconds. After completing the simulation,
|
||||
the script plots the robot's velocity and joint angles.
|
||||
|
||||
The GUI is on by default.
|
||||
If it does not appear, and you're working in docker, make sure you haven't forgotten to run `bash docker/visualize_access.bash`.
|
||||
|
||||
|
||||
|
||||
## Deploying a Model <a name="realworld"></a>
|
||||
### Installing the Deployment Utility <a name="robotconfig"></a>
|
||||
|
||||
The first step is to connect your development machine to the robot using ethernet. You should ping the robot to verify the connection: `ping 192.168.123.15` should return `x packets transmitted, x received, 0% packet loss`.
|
||||
|
||||
Once you have confirmed the robot is connected, run the following command on your computer to transfer files to the robot. The first time you run it, the script will download and transfer the zipped docker image for development on the robot (`deployment_image.tar`). This file is quite large (3.5GB), but it only needs to be downloaded and transferred once.
|
||||
|
||||
```
|
||||
cd go1_gym_deploy/scripts && ./send_to_unitree.sh
|
||||
```
|
||||
|
||||
Next, you will log onto the robot's onboard computer and install the docker environment. To enter the onboard computer, the command is:
|
||||
|
||||
```
|
||||
ssh unitree@192.168.123.15
|
||||
```
|
||||
|
||||
Now, run the following commands on the robot's onboard computer:
|
||||
|
||||
```
|
||||
cd ~/go1_gym/go1_gym_deploy/installer
|
||||
./install_deployment_code.sh
|
||||
```
|
||||
|
||||
The installer will automatically unzip and install the docker image containing the deployment environment.
|
||||
|
||||
|
||||
### Running the Controller <a name="runcontroller"></a>
|
||||
|
||||
Place the robot into damping mode. The control sequence is: [L1+B], [L1+A], [L1+L2+START]. After this, the robot should sit on the ground and the joints should move freely.
|
||||
|
||||
Now, ssh to `unitree@192.168.123.15` and run the following two commands to start the controller. <b>This will operate the robot in low-level control mode. Make sure your Go1 is hung up.</b>
|
||||
|
||||
First:
|
||||
```
|
||||
cd ~/go1_gym/go1_gym_deploy/autostart
|
||||
./start_unitree_sdk.sh
|
||||
```
|
||||
|
||||
Second:
|
||||
```
|
||||
cd ~/go1_gym/go1_gym_deploy/docker
|
||||
sudo make autostart
|
||||
```
|
||||
|
||||
The robot will wait for you to press [R2], then calibrate, then wait for a second press of [R2] before running the control loop.
|
||||
|
||||
### The RC Mapping <a name="rcconfig"></a>
|
||||

|
||||
The RC mapping is depicted above.
|
||||
### Deploying a Custom Model <a name="configuration"></a>
|
||||
<i>Coming soon</i>
|
||||
### Logging and Debugging <a name="deployment"></a>
|
||||
<i>Coming soon</i>
|
||||
### Analyzing Real-world Performance <a name="plotting"></a>
|
||||
<i>Coming soon</i>
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
import os
|
||||
|
||||
MINI_GYM_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
MINI_GYM_ENVS_DIR = os.path.join(MINI_GYM_ROOT_DIR, 'go1_gym', 'envs')
|
||||
Executable
Executable
+137
@@ -0,0 +1,137 @@
|
||||
# License: see [LICENSE, LICENSES/legged_gym/LICENSE]
|
||||
|
||||
import sys
|
||||
|
||||
import gym
|
||||
import torch
|
||||
from isaacgym import gymapi, gymutil
|
||||
|
||||
from gym import spaces
|
||||
import numpy as np
|
||||
|
||||
|
||||
# Base class for RL tasks
|
||||
class BaseTask(gym.Env):
|
||||
|
||||
def __init__(self, cfg, sim_params, physics_engine, sim_device, headless, eval_cfg=None):
|
||||
self.gym = gymapi.acquire_gym()
|
||||
|
||||
if isinstance(physics_engine, str) and physics_engine == "SIM_PHYSX":
|
||||
physics_engine = gymapi.SIM_PHYSX
|
||||
|
||||
self.sim_params = sim_params
|
||||
self.physics_engine = physics_engine
|
||||
self.sim_device = sim_device
|
||||
sim_device_type, self.sim_device_id = gymutil.parse_device_str(self.sim_device)
|
||||
self.headless = headless
|
||||
|
||||
# env device is GPU only if sim is on GPU and use_gpu_pipeline=True, otherwise returned tensors are copied to CPU by physX.
|
||||
if sim_device_type == 'cuda' and sim_params.use_gpu_pipeline:
|
||||
self.device = self.sim_device
|
||||
else:
|
||||
self.device = 'cpu'
|
||||
|
||||
# graphics device for rendering, -1 for no rendering
|
||||
self.graphics_device_id = self.sim_device_id
|
||||
if self.headless == True:
|
||||
self.graphics_device_id = self.sim_device_id
|
||||
|
||||
self.num_obs = cfg.env.num_observations
|
||||
self.num_privileged_obs = cfg.env.num_privileged_obs
|
||||
self.num_actions = cfg.env.num_actions
|
||||
|
||||
if eval_cfg is not None:
|
||||
self.num_eval_envs = eval_cfg.env.num_envs
|
||||
self.num_train_envs = cfg.env.num_envs
|
||||
self.num_envs = self.num_eval_envs + self.num_train_envs
|
||||
else:
|
||||
self.num_eval_envs = 0
|
||||
self.num_train_envs = cfg.env.num_envs
|
||||
self.num_envs = cfg.env.num_envs
|
||||
|
||||
# optimization flags for pytorch JIT
|
||||
torch._C._jit_set_profiling_mode(False)
|
||||
torch._C._jit_set_profiling_executor(False)
|
||||
|
||||
# allocate buffers
|
||||
self.obs_buf = torch.zeros(self.num_envs, self.num_obs, device=self.device, dtype=torch.float)
|
||||
self.rew_buf = torch.zeros(self.num_envs, device=self.device, dtype=torch.float)
|
||||
self.rew_buf_pos = torch.zeros(self.num_envs, device=self.device, dtype=torch.float)
|
||||
self.rew_buf_neg = torch.zeros(self.num_envs, device=self.device, dtype=torch.float)
|
||||
self.reset_buf = torch.ones(self.num_envs, device=self.device, dtype=torch.long)
|
||||
self.episode_length_buf = torch.zeros(self.num_envs, device=self.device, dtype=torch.long)
|
||||
self.time_out_buf = torch.zeros(self.num_envs, device=self.device, dtype=torch.bool)
|
||||
self.privileged_obs_buf = torch.zeros(self.num_envs, self.num_privileged_obs, device=self.device,
|
||||
dtype=torch.float)
|
||||
# self.num_privileged_obs = self.num_obs
|
||||
|
||||
self.extras = {}
|
||||
|
||||
# create envs, sim and viewer
|
||||
self.create_sim()
|
||||
self.gym.prepare_sim(self.sim)
|
||||
|
||||
# todo: read from config
|
||||
self.enable_viewer_sync = True
|
||||
self.viewer = None
|
||||
|
||||
# if running with a viewer, set up keyboard shortcuts and camera
|
||||
if self.headless == False:
|
||||
# subscribe to keyboard shortcuts
|
||||
self.viewer = self.gym.create_viewer(
|
||||
self.sim, gymapi.CameraProperties())
|
||||
self.gym.subscribe_viewer_keyboard_event(
|
||||
self.viewer, gymapi.KEY_ESCAPE, "QUIT")
|
||||
self.gym.subscribe_viewer_keyboard_event(
|
||||
self.viewer, gymapi.KEY_V, "toggle_viewer_sync")
|
||||
|
||||
def get_observations(self):
|
||||
return self.obs_buf
|
||||
|
||||
def get_privileged_observations(self):
|
||||
return self.privileged_obs_buf
|
||||
|
||||
def reset_idx(self, env_ids):
|
||||
"""Reset selected robots"""
|
||||
raise NotImplementedError
|
||||
|
||||
def reset(self):
|
||||
""" Reset all robots"""
|
||||
self.reset_idx(torch.arange(self.num_envs, device=self.device))
|
||||
obs, privileged_obs, _, _, _ = self.step(
|
||||
torch.zeros(self.num_envs, self.num_actions, device=self.device, requires_grad=False))
|
||||
return obs, privileged_obs
|
||||
|
||||
def step(self, actions):
|
||||
raise NotImplementedError
|
||||
|
||||
def render_gui(self, sync_frame_time=True):
|
||||
if self.viewer:
|
||||
# check for window closed
|
||||
if self.gym.query_viewer_has_closed(self.viewer):
|
||||
sys.exit()
|
||||
|
||||
# check for keyboard events
|
||||
for evt in self.gym.query_viewer_action_events(self.viewer):
|
||||
if evt.action == "QUIT" and evt.value > 0:
|
||||
sys.exit()
|
||||
elif evt.action == "toggle_viewer_sync" and evt.value > 0:
|
||||
self.enable_viewer_sync = not self.enable_viewer_sync
|
||||
|
||||
# fetch results
|
||||
if self.device != 'cpu':
|
||||
self.gym.fetch_results(self.sim, True)
|
||||
|
||||
# step graphics
|
||||
if self.enable_viewer_sync:
|
||||
self.gym.step_graphics(self.sim)
|
||||
self.gym.draw_viewer(self.viewer, self.sim, True)
|
||||
if sync_frame_time:
|
||||
self.gym.sync_frame_time(self.sim)
|
||||
else:
|
||||
self.gym.poll_viewer_events(self.viewer)
|
||||
|
||||
def close(self):
|
||||
if self.headless == False:
|
||||
self.gym.destroy_viewer(self.viewer)
|
||||
self.gym.destroy_sim(self.sim)
|
||||
@@ -0,0 +1,181 @@
|
||||
import numpy as np
|
||||
import torch
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
|
||||
def is_met(scale, l2_err, threshold):
|
||||
return (l2_err / scale) < threshold
|
||||
|
||||
|
||||
def key_is_met(metric_cache, config, ep_len, target_key, env_id, threshold):
|
||||
# metric_cache[target_key][env_id] / ep_len
|
||||
scale = 1
|
||||
l2_err = 0
|
||||
return is_met(scale, l2_err, threshold)
|
||||
|
||||
|
||||
class Curriculum:
|
||||
def set_to(self, low, high, value=1.0):
|
||||
inds = np.logical_and(
|
||||
self.grid >= low[:, None],
|
||||
self.grid <= high[:, None]
|
||||
).all(axis=0)
|
||||
|
||||
assert len(inds) != 0, "You are intializing your distribution with an empty domain!"
|
||||
|
||||
self.weights[inds] = value
|
||||
|
||||
def __init__(self, seed, **key_ranges):
|
||||
self.rng = np.random.RandomState(seed)
|
||||
|
||||
self.cfg = cfg = {}
|
||||
self.indices = indices = {}
|
||||
for key, v_range in key_ranges.items():
|
||||
bin_size = (v_range[1] - v_range[0]) / v_range[2]
|
||||
cfg[key] = np.linspace(v_range[0] + bin_size / 2, v_range[1] - bin_size / 2, v_range[2])
|
||||
indices[key] = np.linspace(0, v_range[2]-1, v_range[2])
|
||||
|
||||
self.lows = np.array([range[0] for range in key_ranges.values()])
|
||||
self.highs = np.array([range[1] for range in key_ranges.values()])
|
||||
|
||||
# self.bin_sizes = {key: arr[1] - arr[0] for key, arr in cfg.items()}
|
||||
self.bin_sizes = {key: (v_range[1] - v_range[0]) / v_range[2] for key, v_range in key_ranges.items()}
|
||||
|
||||
self._raw_grid = np.stack(np.meshgrid(*cfg.values(), indexing='ij'))
|
||||
self._idx_grid = np.stack(np.meshgrid(*indices.values(), indexing='ij'))
|
||||
self.keys = [*key_ranges.keys()]
|
||||
self.grid = self._raw_grid.reshape([len(self.keys), -1])
|
||||
self.idx_grid = self._idx_grid.reshape([len(self.keys), -1])
|
||||
# self.grid = np.stack([params.flatten() for params in raw_grid])
|
||||
|
||||
self._l = l = len(self.grid[0])
|
||||
self.ls = {key: len(self.cfg[key]) for key in self.cfg.keys()}
|
||||
|
||||
self.weights = np.zeros(l)
|
||||
self.indices = np.arange(l)
|
||||
|
||||
def __len__(self):
|
||||
return self._l
|
||||
|
||||
def __getitem__(self, *keys):
|
||||
pass
|
||||
|
||||
def update(self, **kwargs):
|
||||
# bump the envelop if
|
||||
pass
|
||||
|
||||
def sample_bins(self, batch_size, low=None, high=None):
|
||||
"""default to uniform"""
|
||||
if low is not None and high is not None: # if bounds given
|
||||
valid_inds = np.logical_and(
|
||||
self.grid >= low[:, None],
|
||||
self.grid <= high[:, None]
|
||||
).all(axis=0)
|
||||
temp_weights = np.zeros_like(self.weights)
|
||||
temp_weights[valid_inds] = self.weights[valid_inds]
|
||||
inds = self.rng.choice(self.indices, batch_size, p=temp_weights / temp_weights.sum())
|
||||
else: # if no bounds given
|
||||
inds = self.rng.choice(self.indices, batch_size, p=self.weights / self.weights.sum())
|
||||
|
||||
return self.grid.T[inds], inds
|
||||
|
||||
def sample_uniform_from_cell(self, centroids):
|
||||
bin_sizes = np.array([*self.bin_sizes.values()])
|
||||
low, high = centroids + bin_sizes / 2, centroids - bin_sizes / 2
|
||||
return self.rng.uniform(low, high)#.clip(self.lows, self.highs)
|
||||
|
||||
def sample(self, batch_size, low=None, high=None):
|
||||
cgf_centroid, inds = self.sample_bins(batch_size, low=low, high=high)
|
||||
return np.stack([self.sample_uniform_from_cell(v_range) for v_range in cgf_centroid]), inds
|
||||
|
||||
|
||||
class SumCurriculum(Curriculum):
|
||||
def __init__(self, seed, **kwargs):
|
||||
super().__init__(seed, **kwargs)
|
||||
|
||||
self.success = np.zeros(len(self))
|
||||
self.trials = np.zeros(len(self))
|
||||
|
||||
def update(self, bin_inds, l1_error, threshold):
|
||||
is_success = l1_error < threshold
|
||||
self.success[bin_inds[is_success]] += 1
|
||||
self.trials[bin_inds] += 1
|
||||
|
||||
def success_rates(self, *keys):
|
||||
s_rate = self.success / (self.trials + 1e-6)
|
||||
s_rate = s_rate.reshape(list(self.ls.values()))
|
||||
marginals = tuple(i for i, key in enumerate(self.keys) if key not in keys)
|
||||
if marginals:
|
||||
return s_rate.mean(axis=marginals)
|
||||
return s_rate
|
||||
|
||||
|
||||
class RewardThresholdCurriculum(Curriculum):
|
||||
def __init__(self, seed, **kwargs):
|
||||
super().__init__(seed, **kwargs)
|
||||
|
||||
self.episode_reward_lin = np.zeros(len(self))
|
||||
self.episode_reward_ang = np.zeros(len(self))
|
||||
self.episode_lin_vel_raw = np.zeros(len(self))
|
||||
self.episode_ang_vel_raw = np.zeros(len(self))
|
||||
self.episode_duration = np.zeros(len(self))
|
||||
|
||||
def get_local_bins(self, bin_inds, ranges=0.1):
|
||||
if isinstance(ranges, float):
|
||||
ranges = np.ones(self.grid.shape[0]) * ranges
|
||||
bin_inds = bin_inds.reshape(-1)
|
||||
|
||||
adjacent_inds = np.logical_and(
|
||||
self.grid[:, None, :].repeat(bin_inds.shape[0], axis=1) >= self.grid[:, bin_inds, None] - ranges.reshape(-1, 1, 1),
|
||||
self.grid[:, None, :].repeat(bin_inds.shape[0], axis=1) <= self.grid[:, bin_inds, None] + ranges.reshape(-1, 1, 1)
|
||||
).all(axis=0)
|
||||
|
||||
return adjacent_inds
|
||||
|
||||
def update(self, bin_inds, task_rewards, success_thresholds, local_range=0.5):
|
||||
|
||||
is_success = 1.
|
||||
for task_reward, success_threshold in zip(task_rewards, success_thresholds):
|
||||
is_success = is_success * (task_reward > success_threshold).cpu()
|
||||
if len(success_thresholds) == 0:
|
||||
is_success = np.array([False] * len(bin_inds))
|
||||
else:
|
||||
is_success = np.array(is_success.bool())
|
||||
|
||||
# if len(is_success) > 0 and is_success.any():
|
||||
# print("successes")
|
||||
|
||||
self.weights[bin_inds[is_success]] = np.clip(self.weights[bin_inds[is_success]] + 0.2, 0, 1)
|
||||
adjacents = self.get_local_bins(bin_inds[is_success], ranges=local_range)
|
||||
for adjacent in adjacents:
|
||||
#print(adjacent)
|
||||
#print(self.grid[:, adjacent])
|
||||
adjacent_inds = np.array(adjacent.nonzero()[0])
|
||||
self.weights[adjacent_inds] = np.clip(self.weights[adjacent_inds] + 0.2, 0, 1)
|
||||
|
||||
def log(self, bin_inds, lin_vel_raw=None, ang_vel_raw=None, episode_duration=None):
|
||||
self.episode_lin_vel_raw[bin_inds] = lin_vel_raw.cpu().numpy()
|
||||
self.episode_ang_vel_raw[bin_inds] = ang_vel_raw.cpu().numpy()
|
||||
self.episode_duration[bin_inds] = episode_duration.cpu().numpy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
r = RewardThresholdCurriculum(100, x=(-1, 1, 5), y=(-1, 1, 2), z=(-1, 1, 11))
|
||||
|
||||
assert r._raw_grid.shape == (3, 5, 2, 11), "grid shape is wrong: {}".format(r.grid.shape)
|
||||
|
||||
low, high = np.array([-1.0, -0.6, -1.0]), np.array([1.0, 0.6, 1.0])
|
||||
|
||||
# r.set_to(low, high, value=1.0)
|
||||
|
||||
adjacents = r.get_local_bins(np.array([10, ]), range=0.5)
|
||||
for adjacent in adjacents:
|
||||
adjacent_inds = np.array(adjacent.nonzero()[0])
|
||||
print(adjacent_inds)
|
||||
r.update(bin_inds=adjacent_inds, lin_vel_rewards=np.ones_like(adjacent_inds),
|
||||
ang_vel_rewards=np.ones_like(adjacent_inds), lin_vel_threshold=0.0, ang_vel_threshold=0.0,
|
||||
local_range=0.5)
|
||||
|
||||
samples, bins = r.sample(10_000)
|
||||
|
||||
plt.scatter(*samples.T[:2])
|
||||
plt.show()
|
||||
Executable
+1806
File diff suppressed because it is too large
Load Diff
Executable
+421
@@ -0,0 +1,421 @@
|
||||
# License: see [LICENSE, LICENSES/legged_gym/LICENSE]
|
||||
|
||||
from params_proto import PrefixProto, ParamsProto
|
||||
|
||||
|
||||
class Cfg(PrefixProto, cli=False):
|
||||
class env(PrefixProto, cli=False):
|
||||
num_envs = 4096
|
||||
num_observations = 235
|
||||
num_scalar_observations = 42
|
||||
# if not None a privilige_obs_buf will be returned by step() (critic obs for assymetric training). None is returned otherwise
|
||||
num_privileged_obs = 18
|
||||
privileged_future_horizon = 1
|
||||
num_actions = 12
|
||||
num_observation_history = 15
|
||||
env_spacing = 3. # not used with heightfields/trimeshes
|
||||
send_timeouts = True # send time out information to the algorithm
|
||||
episode_length_s = 20 # episode length in seconds
|
||||
observe_vel = True
|
||||
observe_only_ang_vel = False
|
||||
observe_only_lin_vel = False
|
||||
observe_yaw = False
|
||||
observe_contact_states = False
|
||||
observe_command = True
|
||||
observe_height_command = False
|
||||
observe_gait_commands = False
|
||||
observe_timing_parameter = False
|
||||
observe_clock_inputs = False
|
||||
observe_two_prev_actions = False
|
||||
observe_imu = False
|
||||
record_video = True
|
||||
recording_width_px = 360
|
||||
recording_height_px = 240
|
||||
recording_mode = "COLOR"
|
||||
num_recording_envs = 1
|
||||
debug_viz = False
|
||||
all_agents_share = False
|
||||
|
||||
priv_observe_friction = True
|
||||
priv_observe_friction_indep = True
|
||||
priv_observe_ground_friction = False
|
||||
priv_observe_ground_friction_per_foot = False
|
||||
priv_observe_restitution = True
|
||||
priv_observe_base_mass = True
|
||||
priv_observe_com_displacement = True
|
||||
priv_observe_motor_strength = False
|
||||
priv_observe_motor_offset = False
|
||||
priv_observe_joint_friction = True
|
||||
priv_observe_Kp_factor = True
|
||||
priv_observe_Kd_factor = True
|
||||
priv_observe_contact_forces = False
|
||||
priv_observe_contact_states = False
|
||||
priv_observe_body_velocity = False
|
||||
priv_observe_foot_height = False
|
||||
priv_observe_body_height = False
|
||||
priv_observe_gravity = False
|
||||
priv_observe_terrain_type = False
|
||||
priv_observe_clock_inputs = False
|
||||
priv_observe_doubletime_clock_inputs = False
|
||||
priv_observe_halftime_clock_inputs = False
|
||||
priv_observe_desired_contact_states = False
|
||||
priv_observe_dummy_variable = False
|
||||
|
||||
class terrain(PrefixProto, cli=False):
|
||||
mesh_type = 'trimesh' # "heightfield" # none, plane, heightfield or trimesh
|
||||
horizontal_scale = 0.1 # [m]
|
||||
vertical_scale = 0.005 # [m]
|
||||
border_size = 0 # 25 # [m]
|
||||
curriculum = True
|
||||
static_friction = 1.0
|
||||
dynamic_friction = 1.0
|
||||
restitution = 0.0
|
||||
terrain_noise_magnitude = 0.1
|
||||
# rough terrain only:
|
||||
terrain_smoothness = 0.005
|
||||
measure_heights = True
|
||||
# 1mx1.6m rectangle (without center line)
|
||||
measured_points_x = [-0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]
|
||||
measured_points_y = [-0.5, -0.4, -0.3, -0.2, -0.1, 0., 0.1, 0.2, 0.3, 0.4, 0.5]
|
||||
selected = False # select a unique terrain type and pass all arguments
|
||||
terrain_kwargs = None # Dict of arguments for selected terrain
|
||||
min_init_terrain_level = 0
|
||||
max_init_terrain_level = 5 # starting curriculum state
|
||||
terrain_length = 8.
|
||||
terrain_width = 8.
|
||||
num_rows = 10 # number of terrain rows (levels)
|
||||
num_cols = 20 # number of terrain cols (types)
|
||||
# terrain types: [smooth slope, rough slope, stairs up, stairs down, discrete]
|
||||
terrain_proportions = [0.1, 0.1, 0.35, 0.25, 0.2]
|
||||
# trimesh only:
|
||||
slope_treshold = 0.75 # slopes above this threshold will be corrected to vertical surfaces
|
||||
difficulty_scale = 1.
|
||||
x_init_range = 1.
|
||||
y_init_range = 1.
|
||||
yaw_init_range = 0.
|
||||
x_init_offset = 0.
|
||||
y_init_offset = 0.
|
||||
teleport_robots = True
|
||||
teleport_thresh = 2.0
|
||||
max_platform_height = 0.2
|
||||
center_robots = False
|
||||
center_span = 5
|
||||
|
||||
class commands(PrefixProto, cli=False):
|
||||
command_curriculum = False
|
||||
max_reverse_curriculum = 1.
|
||||
max_forward_curriculum = 1.
|
||||
yaw_command_curriculum = False
|
||||
max_yaw_curriculum = 1.
|
||||
exclusive_command_sampling = False
|
||||
num_commands = 3
|
||||
resampling_time = 10. # time before command are changed[s]
|
||||
subsample_gait = False
|
||||
gait_interval_s = 10. # time between resampling gait params
|
||||
vel_interval_s = 10.
|
||||
jump_interval_s = 20. # time between jumps
|
||||
jump_duration_s = 0.1 # duration of jump
|
||||
jump_height = 0.3
|
||||
heading_command = True # if true: compute ang vel command from heading error
|
||||
global_reference = False
|
||||
observe_accel = False
|
||||
distributional_commands = False
|
||||
curriculum_type = "RewardThresholdCurriculum"
|
||||
lipschitz_threshold = 0.9
|
||||
|
||||
num_lin_vel_bins = 20
|
||||
lin_vel_step = 0.3
|
||||
num_ang_vel_bins = 20
|
||||
ang_vel_step = 0.3
|
||||
distribution_update_extension_distance = 1
|
||||
curriculum_seed = 100
|
||||
|
||||
lin_vel_x = [-1.0, 1.0] # min max [m/s]
|
||||
lin_vel_y = [-1.0, 1.0] # min max [m/s]
|
||||
ang_vel_yaw = [-1, 1] # min max [rad/s]
|
||||
body_height_cmd = [-0.05, 0.05]
|
||||
impulse_height_commands = False
|
||||
|
||||
limit_vel_x = [-10.0, 10.0]
|
||||
limit_vel_y = [-0.6, 0.6]
|
||||
limit_vel_yaw = [-10.0, 10.0]
|
||||
limit_body_height = [-0.05, 0.05]
|
||||
limit_gait_phase = [0, 0.01]
|
||||
limit_gait_offset = [0, 0.01]
|
||||
limit_gait_bound = [0, 0.01]
|
||||
limit_gait_frequency = [2.0, 2.01]
|
||||
limit_gait_duration = [0.49, 0.5]
|
||||
limit_footswing_height = [0.06, 0.061]
|
||||
limit_body_pitch = [0.0, 0.01]
|
||||
limit_body_roll = [0.0, 0.01]
|
||||
limit_aux_reward_coef = [0.0, 0.01]
|
||||
limit_compliance = [0.0, 0.01]
|
||||
limit_stance_width = [0.0, 0.01]
|
||||
limit_stance_length = [0.0, 0.01]
|
||||
|
||||
num_bins_vel_x = 25
|
||||
num_bins_vel_y = 3
|
||||
num_bins_vel_yaw = 25
|
||||
num_bins_body_height = 1
|
||||
num_bins_gait_frequency = 11
|
||||
num_bins_gait_phase = 11
|
||||
num_bins_gait_offset = 2
|
||||
num_bins_gait_bound = 2
|
||||
num_bins_gait_duration = 3
|
||||
num_bins_footswing_height = 1
|
||||
num_bins_body_pitch = 1
|
||||
num_bins_body_roll = 1
|
||||
num_bins_aux_reward_coef = 1
|
||||
num_bins_compliance = 1
|
||||
num_bins_compliance = 1
|
||||
num_bins_stance_width = 1
|
||||
num_bins_stance_length = 1
|
||||
|
||||
heading = [-3.14, 3.14]
|
||||
|
||||
gait_phase_cmd_range = [0.0, 0.01]
|
||||
gait_offset_cmd_range = [0.0, 0.01]
|
||||
gait_bound_cmd_range = [0.0, 0.01]
|
||||
gait_frequency_cmd_range = [2.0, 2.01]
|
||||
gait_duration_cmd_range = [0.49, 0.5]
|
||||
footswing_height_range = [0.06, 0.061]
|
||||
body_pitch_range = [0.0, 0.01]
|
||||
body_roll_range = [0.0, 0.01]
|
||||
aux_reward_coef_range = [0.0, 0.01]
|
||||
compliance_range = [0.0, 0.01]
|
||||
stance_width_range = [0.0, 0.01]
|
||||
stance_length_range = [0.0, 0.01]
|
||||
|
||||
exclusive_phase_offset = True
|
||||
binary_phases = False
|
||||
pacing_offset = False
|
||||
balance_gait_distribution = True
|
||||
gaitwise_curricula = True
|
||||
|
||||
class curriculum_thresholds(PrefixProto, cli=False):
|
||||
tracking_lin_vel = 0.8 # closer to 1 is tighter
|
||||
tracking_ang_vel = 0.5
|
||||
tracking_contacts_shaped_force = 0.8 # closer to 1 is tighter
|
||||
tracking_contacts_shaped_vel = 0.8
|
||||
|
||||
class init_state(PrefixProto, cli=False):
|
||||
pos = [0.0, 0.0, 1.] # x,y,z [m]
|
||||
rot = [0.0, 0.0, 0.0, 1.0] # x,y,z,w [quat]
|
||||
lin_vel = [0.0, 0.0, 0.0] # x,y,z [m/s]
|
||||
ang_vel = [0.0, 0.0, 0.0] # x,y,z [rad/s]
|
||||
# target angles when action = 0.0
|
||||
default_joint_angles = {"joint_a": 0., "joint_b": 0.}
|
||||
|
||||
class control(PrefixProto, cli=False):
|
||||
control_type = 'actuator_net' #'P' # P: position, V: velocity, T: torques
|
||||
# PD Drive parameters:
|
||||
stiffness = {'joint_a': 10.0, 'joint_b': 15.} # [N*m/rad]
|
||||
damping = {'joint_a': 1.0, 'joint_b': 1.5} # [N*m*s/rad]
|
||||
# action scale: target angle = actionScale * action + defaultAngle
|
||||
action_scale = 0.5
|
||||
hip_scale_reduction = 1.0
|
||||
# decimation: Number of control action updates @ sim DT per policy DT
|
||||
decimation = 4
|
||||
|
||||
class asset(PrefixProto, cli=False):
|
||||
file = ""
|
||||
foot_name = "None" # name of the feet bodies, used to index body state and contact force tensors
|
||||
penalize_contacts_on = []
|
||||
terminate_after_contacts_on = []
|
||||
disable_gravity = False
|
||||
# merge bodies connected by fixed joints. Specific fixed joints can be kept by adding " <... dont_collapse="true">
|
||||
collapse_fixed_joints = True
|
||||
fix_base_link = False # fixe the base of the robot
|
||||
default_dof_drive_mode = 3 # see GymDofDriveModeFlags (0 is none, 1 is pos tgt, 2 is vel tgt, 3 effort)
|
||||
self_collisions = 0 # 1 to disable, 0 to enable...bitwise filter
|
||||
# replace collision cylinders with capsules, leads to faster/more stable simulation
|
||||
replace_cylinder_with_capsule = True
|
||||
flip_visual_attachments = True # Some .obj meshes must be flipped from y-up to z-up
|
||||
|
||||
density = 0.001
|
||||
angular_damping = 0.
|
||||
linear_damping = 0.
|
||||
max_angular_velocity = 1000.
|
||||
max_linear_velocity = 1000.
|
||||
armature = 0.
|
||||
thickness = 0.01
|
||||
|
||||
class domain_rand(PrefixProto, cli=False):
|
||||
rand_interval_s = 10
|
||||
randomize_rigids_after_start = True
|
||||
randomize_friction = True
|
||||
friction_range = [0.5, 1.25] # increase range
|
||||
randomize_restitution = False
|
||||
restitution_range = [0, 1.0]
|
||||
randomize_base_mass = False
|
||||
# add link masses, increase range, randomize inertia, randomize joint properties
|
||||
added_mass_range = [-1., 1.]
|
||||
randomize_com_displacement = False
|
||||
# add link masses, increase range, randomize inertia, randomize joint properties
|
||||
com_displacement_range = [-0.15, 0.15]
|
||||
randomize_motor_strength = False
|
||||
motor_strength_range = [0.9, 1.1]
|
||||
randomize_Kp_factor = False
|
||||
Kp_factor_range = [0.8, 1.3]
|
||||
randomize_Kd_factor = False
|
||||
Kd_factor_range = [0.5, 1.5]
|
||||
gravity_rand_interval_s = 7
|
||||
gravity_impulse_duration = 1.0
|
||||
randomize_gravity = False
|
||||
gravity_range = [-1.0, 1.0]
|
||||
push_robots = True
|
||||
push_interval_s = 15
|
||||
max_push_vel_xy = 1.
|
||||
randomize_lag_timesteps = True
|
||||
lag_timesteps = 6
|
||||
|
||||
class rewards(PrefixProto, cli=False):
|
||||
only_positive_rewards = True # if true negative total rewards are clipped at zero (avoids early termination problems)
|
||||
only_positive_rewards_ji22_style = False
|
||||
sigma_rew_neg = 5
|
||||
reward_container_name = "CoRLRewards"
|
||||
tracking_sigma = 0.25 # tracking reward = exp(-error^2/sigma)
|
||||
tracking_sigma_lat = 0.25 # tracking reward = exp(-error^2/sigma)
|
||||
tracking_sigma_long = 0.25 # tracking reward = exp(-error^2/sigma)
|
||||
tracking_sigma_yaw = 0.25 # tracking reward = exp(-error^2/sigma)
|
||||
soft_dof_pos_limit = 1. # percentage of urdf limits, values above this limit are penalized
|
||||
soft_dof_vel_limit = 1.
|
||||
soft_torque_limit = 1.
|
||||
base_height_target = 1.
|
||||
max_contact_force = 100. # forces above this value are penalized
|
||||
use_terminal_body_height = False
|
||||
terminal_body_height = 0.20
|
||||
use_terminal_foot_height = False
|
||||
terminal_foot_height = -0.005
|
||||
use_terminal_roll_pitch = False
|
||||
terminal_body_ori = 0.5
|
||||
kappa_gait_probs = 0.07
|
||||
gait_force_sigma = 50.
|
||||
gait_vel_sigma = 0.5
|
||||
footswing_height = 0.09
|
||||
|
||||
class reward_scales(ParamsProto, cli=False):
|
||||
termination = -0.0
|
||||
tracking_lin_vel = 1.0
|
||||
tracking_ang_vel = 0.5
|
||||
lin_vel_z = -2.0
|
||||
ang_vel_xy = -0.05
|
||||
orientation = -0.
|
||||
torques = -0.00001
|
||||
dof_vel = -0.
|
||||
dof_acc = -2.5e-7
|
||||
base_height = -0.
|
||||
feet_air_time = 1.0
|
||||
collision = -1.
|
||||
feet_stumble = -0.0
|
||||
action_rate = -0.01
|
||||
stand_still = -0.
|
||||
tracking_lin_vel_lat = 0.
|
||||
tracking_lin_vel_long = 0.
|
||||
tracking_contacts = 0.
|
||||
tracking_contacts_shaped = 0.
|
||||
tracking_contacts_shaped_force = 0.
|
||||
tracking_contacts_shaped_vel = 0.
|
||||
jump = 0.0
|
||||
energy = 0.0
|
||||
energy_expenditure = 0.0
|
||||
survival = 0.0
|
||||
dof_pos_limits = 0.0
|
||||
feet_contact_forces = 0.
|
||||
feet_slip = 0.
|
||||
feet_clearance_cmd_linear = 0.
|
||||
dof_pos = 0.
|
||||
action_smoothness_1 = 0.
|
||||
action_smoothness_2 = 0.
|
||||
base_motion = 0.
|
||||
feet_impact_vel = 0.0
|
||||
raibert_heuristic = 0.0
|
||||
|
||||
class normalization(PrefixProto, cli=False):
|
||||
clip_observations = 100.
|
||||
clip_actions = 100.
|
||||
|
||||
friction_range = [0.05, 4.5]
|
||||
ground_friction_range = [0.05, 4.5]
|
||||
restitution_range = [0, 1.0]
|
||||
added_mass_range = [-1., 3.]
|
||||
com_displacement_range = [-0.1, 0.1]
|
||||
motor_strength_range = [0.9, 1.1]
|
||||
motor_offset_range = [-0.05, 0.05]
|
||||
Kp_factor_range = [0.8, 1.3]
|
||||
Kd_factor_range = [0.5, 1.5]
|
||||
joint_friction_range = [0.0, 0.7]
|
||||
contact_force_range = [0.0, 50.0]
|
||||
contact_state_range = [0.0, 1.0]
|
||||
body_velocity_range = [-6.0, 6.0]
|
||||
foot_height_range = [0.0, 0.15]
|
||||
body_height_range = [0.0, 0.60]
|
||||
gravity_range = [-1.0, 1.0]
|
||||
motion = [-0.01, 0.01]
|
||||
|
||||
class obs_scales(PrefixProto, cli=False):
|
||||
lin_vel = 2.0
|
||||
ang_vel = 0.25
|
||||
dof_pos = 1.0
|
||||
dof_vel = 0.05
|
||||
imu = 0.1
|
||||
height_measurements = 5.0
|
||||
friction_measurements = 1.0
|
||||
body_height_cmd = 2.0
|
||||
gait_phase_cmd = 1.0
|
||||
gait_freq_cmd = 1.0
|
||||
footswing_height_cmd = 0.15
|
||||
body_pitch_cmd = 0.3
|
||||
body_roll_cmd = 0.3
|
||||
aux_reward_cmd = 1.0
|
||||
compliance_cmd = 1.0
|
||||
stance_width_cmd = 1.0
|
||||
stance_length_cmd = 1.0
|
||||
segmentation_image = 1.0
|
||||
rgb_image = 1.0
|
||||
depth_image = 1.0
|
||||
|
||||
class noise(PrefixProto, cli=False):
|
||||
add_noise = True
|
||||
noise_level = 1.0 # scales other values
|
||||
|
||||
class noise_scales(PrefixProto, cli=False):
|
||||
dof_pos = 0.01
|
||||
dof_vel = 1.5
|
||||
lin_vel = 0.1
|
||||
ang_vel = 0.2
|
||||
imu = 0.1
|
||||
gravity = 0.05
|
||||
contact_states = 0.05
|
||||
height_measurements = 0.1
|
||||
friction_measurements = 0.0
|
||||
segmentation_image = 0.0
|
||||
rgb_image = 0.0
|
||||
depth_image = 0.0
|
||||
|
||||
# viewer camera:
|
||||
class viewer(PrefixProto, cli=False):
|
||||
ref_env = 0
|
||||
pos = [10, 0, 6] # [m]
|
||||
lookat = [11., 5, 3.] # [m]
|
||||
|
||||
class sim(PrefixProto, cli=False):
|
||||
dt = 0.005
|
||||
substeps = 1
|
||||
gravity = [0., 0., -9.81] # [m/s^2]
|
||||
up_axis = 1 # 0 is y, 1 is z
|
||||
|
||||
use_gpu_pipeline = True
|
||||
|
||||
class physx(PrefixProto, cli=False):
|
||||
num_threads = 10
|
||||
solver_type = 1 # 0: pgs, 1: tgs
|
||||
num_position_iterations = 4
|
||||
num_velocity_iterations = 0
|
||||
contact_offset = 0.01 # [m]
|
||||
rest_offset = 0.0 # [m]
|
||||
bounce_threshold_velocity = 0.5 # 0.5 [m/s]
|
||||
max_depenetration_velocity = 1.0
|
||||
max_gpu_contact_pairs = 2 ** 23 # 2**24 -> needed for 8000 envs and more
|
||||
default_buffer_size_multiplier = 5
|
||||
contact_collection = 2 # 0: never, 1: last sub-step, 2: all sub-steps (default=2)
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
from typing import Union
|
||||
|
||||
from params_proto import Meta
|
||||
|
||||
from go1_gym.envs.base.legged_robot_config import Cfg
|
||||
|
||||
|
||||
def config_go1(Cnfg: Union[Cfg, Meta]):
|
||||
_ = Cnfg.init_state
|
||||
|
||||
_.pos = [0.0, 0.0, 0.34] # x,y,z [m]
|
||||
_.default_joint_angles = { # = target angles [rad] when action = 0.0
|
||||
'FL_hip_joint': 0.1, # [rad]
|
||||
'RL_hip_joint': 0.1, # [rad]
|
||||
'FR_hip_joint': -0.1, # [rad]
|
||||
'RR_hip_joint': -0.1, # [rad]
|
||||
|
||||
'FL_thigh_joint': 0.8, # [rad]
|
||||
'RL_thigh_joint': 1., # [rad]
|
||||
'FR_thigh_joint': 0.8, # [rad]
|
||||
'RR_thigh_joint': 1., # [rad]
|
||||
|
||||
'FL_calf_joint': -1.5, # [rad]
|
||||
'RL_calf_joint': -1.5, # [rad]
|
||||
'FR_calf_joint': -1.5, # [rad]
|
||||
'RR_calf_joint': -1.5 # [rad]
|
||||
}
|
||||
|
||||
_ = Cnfg.control
|
||||
_.control_type = 'P'
|
||||
_.stiffness = {'joint': 20.} # [N*m/rad]
|
||||
_.damping = {'joint': 0.5} # [N*m*s/rad]
|
||||
# action scale: target angle = actionScale * action + defaultAngle
|
||||
_.action_scale = 0.25
|
||||
_.hip_scale_reduction = 0.5
|
||||
# decimation: Number of control action updates @ sim DT per policy DT
|
||||
_.decimation = 4
|
||||
|
||||
_ = Cnfg.asset
|
||||
_.file = '{MINI_GYM_ROOT_DIR}/resources/robots/go1/urdf/go1.urdf'
|
||||
_.foot_name = "foot"
|
||||
_.penalize_contacts_on = ["thigh", "calf"]
|
||||
_.terminate_after_contacts_on = ["base"]
|
||||
_.self_collisions = 0 # 1 to disable, 0 to enable...bitwise filter
|
||||
_.flip_visual_attachments = False
|
||||
_.fix_base_link = False
|
||||
|
||||
_ = Cnfg.rewards
|
||||
_.soft_dof_pos_limit = 0.9
|
||||
_.base_height_target = 0.34
|
||||
|
||||
_ = Cnfg.reward_scales
|
||||
_.torques = -0.0001
|
||||
_.action_rate = -0.01
|
||||
_.dof_pos_limits = -10.0
|
||||
_.orientation = -5.
|
||||
_.base_height = -30.
|
||||
|
||||
_ = Cnfg.terrain
|
||||
_.mesh_type = 'trimesh'
|
||||
_.measure_heights = False
|
||||
_.terrain_noise_magnitude = 0.0
|
||||
_.teleport_robots = True
|
||||
_.border_size = 50
|
||||
|
||||
_.terrain_proportions = [0, 0, 0, 0, 0, 0, 0, 0, 1.0]
|
||||
_.curriculum = False
|
||||
|
||||
_ = Cnfg.env
|
||||
_.num_observations = 42
|
||||
_.observe_vel = False
|
||||
_.num_envs = 4000
|
||||
|
||||
_ = Cnfg.commands
|
||||
_.lin_vel_x = [-1.0, 1.0]
|
||||
_.lin_vel_y = [-1.0, 1.0]
|
||||
|
||||
_ = Cnfg.commands
|
||||
_.heading_command = False
|
||||
_.resampling_time = 10.0
|
||||
_.command_curriculum = True
|
||||
_.num_lin_vel_bins = 30
|
||||
_.num_ang_vel_bins = 30
|
||||
_.lin_vel_x = [-0.6, 0.6]
|
||||
_.lin_vel_y = [-0.6, 0.6]
|
||||
_.ang_vel_yaw = [-1, 1]
|
||||
|
||||
_ = Cnfg.domain_rand
|
||||
_.randomize_base_mass = True
|
||||
_.added_mass_range = [-1, 3]
|
||||
_.push_robots = False
|
||||
_.max_push_vel_xy = 0.5
|
||||
_.randomize_friction = True
|
||||
_.friction_range = [0.05, 4.5]
|
||||
_.randomize_restitution = True
|
||||
_.restitution_range = [0.0, 1.0]
|
||||
_.restitution = 0.5 # default terrain restitution
|
||||
_.randomize_com_displacement = True
|
||||
_.com_displacement_range = [-0.1, 0.1]
|
||||
_.randomize_motor_strength = True
|
||||
_.motor_strength_range = [0.9, 1.1]
|
||||
_.randomize_Kp_factor = False
|
||||
_.Kp_factor_range = [0.8, 1.3]
|
||||
_.randomize_Kd_factor = False
|
||||
_.Kd_factor_range = [0.5, 1.5]
|
||||
_.rand_interval_s = 6
|
||||
@@ -0,0 +1,50 @@
|
||||
from isaacgym import gymutil, gymapi
|
||||
import torch
|
||||
from params_proto import Meta
|
||||
from typing import Union
|
||||
|
||||
from go1_gym.envs.base.legged_robot import LeggedRobot
|
||||
from go1_gym.envs.base.legged_robot_config import Cfg
|
||||
|
||||
|
||||
class VelocityTrackingEasyEnv(LeggedRobot):
|
||||
def __init__(self, sim_device, headless, num_envs=None, prone=False, deploy=False,
|
||||
cfg: Cfg = None, eval_cfg: Cfg = None, initial_dynamics_dict=None, physics_engine="SIM_PHYSX"):
|
||||
|
||||
if num_envs is not None:
|
||||
cfg.env.num_envs = num_envs
|
||||
|
||||
sim_params = gymapi.SimParams()
|
||||
gymutil.parse_sim_config(vars(cfg.sim), sim_params)
|
||||
super().__init__(cfg, sim_params, physics_engine, sim_device, headless, eval_cfg, initial_dynamics_dict)
|
||||
|
||||
|
||||
def step(self, actions):
|
||||
self.obs_buf, self.privileged_obs_buf, self.rew_buf, self.reset_buf, self.extras = super().step(actions)
|
||||
|
||||
self.foot_positions = self.rigid_body_state.view(self.num_envs, self.num_bodies, 13)[:, self.feet_indices,
|
||||
0:3]
|
||||
|
||||
self.extras.update({
|
||||
"privileged_obs": self.privileged_obs_buf,
|
||||
"joint_pos": self.dof_pos.cpu().numpy(),
|
||||
"joint_vel": self.dof_vel.cpu().numpy(),
|
||||
"joint_pos_target": self.joint_pos_target.cpu().detach().numpy(),
|
||||
"joint_vel_target": torch.zeros(12),
|
||||
"body_linear_vel": self.base_lin_vel.cpu().detach().numpy(),
|
||||
"body_angular_vel": self.base_ang_vel.cpu().detach().numpy(),
|
||||
"body_linear_vel_cmd": self.commands.cpu().numpy()[:, 0:2],
|
||||
"body_angular_vel_cmd": self.commands.cpu().numpy()[:, 2:],
|
||||
"contact_states": (self.contact_forces[:, self.feet_indices, 2] > 1.).detach().cpu().numpy().copy(),
|
||||
"foot_positions": (self.foot_positions).detach().cpu().numpy().copy(),
|
||||
"body_pos": self.root_states[:, 0:3].detach().cpu().numpy(),
|
||||
"torques": self.torques.detach().cpu().numpy()
|
||||
})
|
||||
|
||||
return self.obs_buf, self.rew_buf, self.reset_buf, self.extras
|
||||
|
||||
def reset(self):
|
||||
self.reset_idx(torch.arange(self.num_envs, device=self.device))
|
||||
obs, _, _, _ = self.step(torch.zeros(self.num_envs, self.num_actions, device=self.device, requires_grad=False))
|
||||
return obs
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import torch
|
||||
import numpy as np
|
||||
from go1_gym.utils.math_utils import quat_apply_yaw, wrap_to_pi, get_scale_shift
|
||||
from isaacgym.torch_utils import *
|
||||
from isaacgym import gymapi
|
||||
|
||||
class CoRLRewards:
|
||||
def __init__(self, env):
|
||||
self.env = env
|
||||
|
||||
def load_env(self, env):
|
||||
self.env = env
|
||||
|
||||
# ------------ reward functions----------------
|
||||
def _reward_tracking_lin_vel(self):
|
||||
# Tracking of linear velocity commands (xy axes)
|
||||
lin_vel_error = torch.sum(torch.square(self.env.commands[:, :2] - self.env.base_lin_vel[:, :2]), dim=1)
|
||||
return torch.exp(-lin_vel_error / self.env.cfg.rewards.tracking_sigma)
|
||||
|
||||
def _reward_tracking_ang_vel(self):
|
||||
# Tracking of angular velocity commands (yaw)
|
||||
ang_vel_error = torch.square(self.env.commands[:, 2] - self.env.base_ang_vel[:, 2])
|
||||
return torch.exp(-ang_vel_error / self.env.cfg.rewards.tracking_sigma_yaw)
|
||||
|
||||
def _reward_lin_vel_z(self):
|
||||
# Penalize z axis base linear velocity
|
||||
return torch.square(self.env.base_lin_vel[:, 2])
|
||||
|
||||
def _reward_ang_vel_xy(self):
|
||||
# Penalize xy axes base angular velocity
|
||||
return torch.sum(torch.square(self.env.base_ang_vel[:, :2]), dim=1)
|
||||
|
||||
def _reward_orientation(self):
|
||||
# Penalize non flat base orientation
|
||||
return torch.sum(torch.square(self.env.projected_gravity[:, :2]), dim=1)
|
||||
|
||||
def _reward_torques(self):
|
||||
# Penalize torques
|
||||
return torch.sum(torch.square(self.env.torques), dim=1)
|
||||
|
||||
def _reward_dof_acc(self):
|
||||
# Penalize dof accelerations
|
||||
return torch.sum(torch.square((self.env.last_dof_vel - self.env.dof_vel) / self.env.dt), dim=1)
|
||||
|
||||
def _reward_action_rate(self):
|
||||
# Penalize changes in actions
|
||||
return torch.sum(torch.square(self.env.last_actions - self.env.actions), dim=1)
|
||||
|
||||
def _reward_collision(self):
|
||||
# Penalize collisions on selected bodies
|
||||
return torch.sum(1. * (torch.norm(self.env.contact_forces[:, self.env.penalised_contact_indices, :], dim=-1) > 0.1),
|
||||
dim=1)
|
||||
|
||||
def _reward_dof_pos_limits(self):
|
||||
# Penalize dof positions too close to the limit
|
||||
out_of_limits = -(self.env.dof_pos - self.env.dof_pos_limits[:, 0]).clip(max=0.) # lower limit
|
||||
out_of_limits += (self.env.dof_pos - self.env.dof_pos_limits[:, 1]).clip(min=0.)
|
||||
return torch.sum(out_of_limits, dim=1)
|
||||
|
||||
def _reward_jump(self):
|
||||
reference_heights = 0
|
||||
body_height = self.env.base_pos[:, 2] - reference_heights
|
||||
jump_height_target = self.env.commands[:, 3] + self.env.cfg.rewards.base_height_target
|
||||
reward = - torch.square(body_height - jump_height_target)
|
||||
return reward
|
||||
|
||||
def _reward_tracking_contacts_shaped_force(self):
|
||||
foot_forces = torch.norm(self.env.contact_forces[:, self.env.feet_indices, :], dim=-1)
|
||||
desired_contact = self.env.desired_contact_states
|
||||
|
||||
reward = 0
|
||||
for i in range(4):
|
||||
reward += - (1 - desired_contact[:, i]) * (
|
||||
1 - torch.exp(-1 * foot_forces[:, i] ** 2 / self.env.cfg.rewards.gait_force_sigma))
|
||||
return reward / 4
|
||||
|
||||
def _reward_tracking_contacts_shaped_vel(self):
|
||||
foot_velocities = torch.norm(self.env.foot_velocities, dim=2).view(self.env.num_envs, -1)
|
||||
desired_contact = self.env.desired_contact_states
|
||||
reward = 0
|
||||
for i in range(4):
|
||||
reward += - (desired_contact[:, i] * (
|
||||
1 - torch.exp(-1 * foot_velocities[:, i] ** 2 / self.env.cfg.rewards.gait_vel_sigma)))
|
||||
return reward / 4
|
||||
|
||||
def _reward_dof_pos(self):
|
||||
# Penalize dof positions
|
||||
return torch.sum(torch.square(self.env.dof_pos - self.env.default_dof_pos), dim=1)
|
||||
|
||||
def _reward_dof_vel(self):
|
||||
# Penalize dof velocities
|
||||
return torch.sum(torch.square(self.env.dof_vel), dim=1)
|
||||
|
||||
def _reward_action_smoothness_1(self):
|
||||
# Penalize changes in actions
|
||||
diff = torch.square(self.env.joint_pos_target[:, :self.env.num_actuated_dof] - self.env.last_joint_pos_target[:, :self.env.num_actuated_dof])
|
||||
diff = diff * (self.env.last_actions[:, :self.env.num_dof] != 0) # ignore first step
|
||||
return torch.sum(diff, dim=1)
|
||||
|
||||
def _reward_action_smoothness_2(self):
|
||||
# Penalize changes in actions
|
||||
diff = torch.square(self.env.joint_pos_target[:, :self.env.num_actuated_dof] - 2 * self.env.last_joint_pos_target[:, :self.env.num_actuated_dof] + self.env.last_last_joint_pos_target[:, :self.env.num_actuated_dof])
|
||||
diff = diff * (self.env.last_actions[:, :self.env.num_dof] != 0) # ignore first step
|
||||
diff = diff * (self.env.last_last_actions[:, :self.env.num_dof] != 0) # ignore second step
|
||||
return torch.sum(diff, dim=1)
|
||||
|
||||
def _reward_feet_slip(self):
|
||||
contact = self.env.contact_forces[:, self.env.feet_indices, 2] > 1.
|
||||
contact_filt = torch.logical_or(contact, self.env.last_contacts)
|
||||
self.env.last_contacts = contact
|
||||
foot_velocities = torch.square(torch.norm(self.env.foot_velocities[:, :, 0:2], dim=2).view(self.env.num_envs, -1))
|
||||
rew_slip = torch.sum(contact_filt * foot_velocities, dim=1)
|
||||
return rew_slip
|
||||
|
||||
def _reward_feet_contact_vel(self):
|
||||
reference_heights = 0
|
||||
near_ground = self.env.foot_positions[:, :, 2] - reference_heights < 0.03
|
||||
foot_velocities = torch.square(torch.norm(self.env.foot_velocities[:, :, 0:3], dim=2).view(self.env.num_envs, -1))
|
||||
rew_contact_vel = torch.sum(near_ground * foot_velocities, dim=1)
|
||||
return rew_contact_vel
|
||||
|
||||
def _reward_feet_contact_forces(self):
|
||||
# penalize high contact forces
|
||||
return torch.sum((torch.norm(self.env.contact_forces[:, self.env.feet_indices, :],
|
||||
dim=-1) - self.env.cfg.rewards.max_contact_force).clip(min=0.), dim=1)
|
||||
|
||||
def _reward_feet_clearance_cmd_linear(self):
|
||||
phases = 1 - torch.abs(1.0 - torch.clip((self.env.foot_indices * 2.0) - 1.0, 0.0, 1.0) * 2.0)
|
||||
foot_height = (self.env.foot_positions[:, :, 2]).view(self.env.num_envs, -1)# - reference_heights
|
||||
target_height = self.env.commands[:, 9].unsqueeze(1) * phases + 0.02 # offset for foot radius 2cm
|
||||
rew_foot_clearance = torch.square(target_height - foot_height) * (1 - self.env.desired_contact_states)
|
||||
return torch.sum(rew_foot_clearance, dim=1)
|
||||
|
||||
def _reward_feet_impact_vel(self):
|
||||
prev_foot_velocities = self.env.prev_foot_velocities[:, :, 2].view(self.env.num_envs, -1)
|
||||
contact_states = torch.norm(self.env.contact_forces[:, self.env.feet_indices, :], dim=-1) > 1.0
|
||||
|
||||
rew_foot_impact_vel = contact_states * torch.square(torch.clip(prev_foot_velocities, -100, 0))
|
||||
|
||||
return torch.sum(rew_foot_impact_vel, dim=1)
|
||||
|
||||
|
||||
def _reward_collision(self):
|
||||
# Penalize collisions on selected bodies
|
||||
return torch.sum(1. * (torch.norm(self.env.contact_forces[:, self.env.penalised_contact_indices, :], dim=-1) > 0.1),
|
||||
dim=1)
|
||||
|
||||
def _reward_orientation_control(self):
|
||||
# Penalize non flat base orientation
|
||||
roll_pitch_commands = self.env.commands[:, 10:12]
|
||||
quat_roll = quat_from_angle_axis(-roll_pitch_commands[:, 1],
|
||||
torch.tensor([1, 0, 0], device=self.env.device, dtype=torch.float))
|
||||
quat_pitch = quat_from_angle_axis(-roll_pitch_commands[:, 0],
|
||||
torch.tensor([0, 1, 0], device=self.env.device, dtype=torch.float))
|
||||
|
||||
desired_base_quat = quat_mul(quat_roll, quat_pitch)
|
||||
desired_projected_gravity = quat_rotate_inverse(desired_base_quat, self.env.gravity_vec)
|
||||
|
||||
return torch.sum(torch.square(self.env.projected_gravity[:, :2] - desired_projected_gravity[:, :2]), dim=1)
|
||||
|
||||
def _reward_raibert_heuristic(self):
|
||||
cur_footsteps_translated = self.env.foot_positions - self.env.base_pos.unsqueeze(1)
|
||||
footsteps_in_body_frame = torch.zeros(self.env.num_envs, 4, 3, device=self.env.device)
|
||||
for i in range(4):
|
||||
footsteps_in_body_frame[:, i, :] = quat_apply_yaw(quat_conjugate(self.env.base_quat),
|
||||
cur_footsteps_translated[:, i, :])
|
||||
|
||||
# nominal positions: [FR, FL, RR, RL]
|
||||
if self.env.cfg.commands.num_commands >= 13:
|
||||
desired_stance_width = self.env.commands[:, 12:13]
|
||||
desired_ys_nom = torch.cat([desired_stance_width / 2, -desired_stance_width / 2, desired_stance_width / 2, -desired_stance_width / 2], dim=1)
|
||||
else:
|
||||
desired_stance_width = 0.3
|
||||
desired_ys_nom = torch.tensor([desired_stance_width / 2, -desired_stance_width / 2, desired_stance_width / 2, -desired_stance_width / 2], device=self.env.device).unsqueeze(0)
|
||||
|
||||
if self.env.cfg.commands.num_commands >= 14:
|
||||
desired_stance_length = self.env.commands[:, 13:14]
|
||||
desired_xs_nom = torch.cat([desired_stance_length / 2, desired_stance_length / 2, -desired_stance_length / 2, -desired_stance_length / 2], dim=1)
|
||||
else:
|
||||
desired_stance_length = 0.45
|
||||
desired_xs_nom = torch.tensor([desired_stance_length / 2, desired_stance_length / 2, -desired_stance_length / 2, -desired_stance_length / 2], device=self.env.device).unsqueeze(0)
|
||||
|
||||
# raibert offsets
|
||||
phases = torch.abs(1.0 - (self.env.foot_indices * 2.0)) * 1.0 - 0.5
|
||||
frequencies = self.env.commands[:, 4]
|
||||
x_vel_des = self.env.commands[:, 0:1]
|
||||
yaw_vel_des = self.env.commands[:, 2:3]
|
||||
y_vel_des = yaw_vel_des * desired_stance_length / 2
|
||||
desired_ys_offset = phases * y_vel_des * (0.5 / frequencies.unsqueeze(1))
|
||||
desired_ys_offset[:, 2:4] *= -1
|
||||
desired_xs_offset = phases * x_vel_des * (0.5 / frequencies.unsqueeze(1))
|
||||
|
||||
desired_ys_nom = desired_ys_nom + desired_ys_offset
|
||||
desired_xs_nom = desired_xs_nom + desired_xs_offset
|
||||
|
||||
desired_footsteps_body_frame = torch.cat((desired_xs_nom.unsqueeze(2), desired_ys_nom.unsqueeze(2)), dim=2)
|
||||
|
||||
err_raibert_heuristic = torch.abs(desired_footsteps_body_frame - footsteps_in_body_frame[:, :, 0:2])
|
||||
|
||||
reward = torch.sum(torch.square(err_raibert_heuristic), dim=(1, 2))
|
||||
|
||||
return reward
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
import isaacgym
|
||||
assert isaacgym
|
||||
import torch
|
||||
import gym
|
||||
|
||||
class HistoryWrapper(gym.Wrapper):
|
||||
def __init__(self, env):
|
||||
super().__init__(env)
|
||||
self.env = env
|
||||
|
||||
self.obs_history_length = self.env.cfg.env.num_observation_history
|
||||
|
||||
self.num_obs_history = self.obs_history_length * self.num_obs
|
||||
self.obs_history = torch.zeros(self.env.num_envs, self.num_obs_history, dtype=torch.float,
|
||||
device=self.env.device, requires_grad=False)
|
||||
self.num_privileged_obs = self.num_privileged_obs
|
||||
|
||||
def step(self, action):
|
||||
# privileged information and observation history are stored in info
|
||||
obs, rew, done, info = self.env.step(action)
|
||||
privileged_obs = info["privileged_obs"]
|
||||
|
||||
self.obs_history = torch.cat((self.obs_history[:, self.env.num_obs:], obs), dim=-1)
|
||||
return {'obs': obs, 'privileged_obs': privileged_obs, 'obs_history': self.obs_history}, rew, done, info
|
||||
|
||||
def get_observations(self):
|
||||
obs = self.env.get_observations()
|
||||
privileged_obs = self.env.get_privileged_observations()
|
||||
self.obs_history = torch.cat((self.obs_history[:, self.env.num_obs:], obs), dim=-1)
|
||||
return {'obs': obs, 'privileged_obs': privileged_obs, 'obs_history': self.obs_history}
|
||||
|
||||
def reset_idx(self, env_ids): # it might be a problem that this isn't getting called!!
|
||||
ret = super().reset_idx(env_ids)
|
||||
self.obs_history[env_ids, :] = 0
|
||||
return ret
|
||||
|
||||
def reset(self):
|
||||
ret = super().reset()
|
||||
privileged_obs = self.env.get_privileged_observations()
|
||||
self.obs_history[:, :] = 0
|
||||
return {"obs": ret, "privileged_obs": privileged_obs, "obs_history": self.obs_history}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from tqdm import trange
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import ml_logger as logger
|
||||
|
||||
from go1_gym_learn.ppo import Runner
|
||||
from go1_gym.envs.wrappers.history_wrapper import HistoryWrapper
|
||||
from go1_gym_learn.ppo.actor_critic import AC_Args
|
||||
|
||||
from go1_gym.envs.base.legged_robot_config import Cfg
|
||||
from go1_gym.envs.mini_cheetah.mini_cheetah_config import config_mini_cheetah
|
||||
config_mini_cheetah(Cfg)
|
||||
|
||||
test_env = gym.make("VelocityTrackingEasyEnv-v0", cfg=Cfg)
|
||||
env = HistoryWrapper(test_env)
|
||||
|
||||
env.reset()
|
||||
action = torch.zeros(test_env.num_envs, 12)
|
||||
for i in trange(3):
|
||||
obs, rew, done, info = env.step(action)
|
||||
print(obs.keys())
|
||||
print(f"obs: {obs['obs']}")
|
||||
print(f"privileged obs: {obs['privileged_obs']}")
|
||||
print(f"obs_history: {obs['obs_history']}")
|
||||
|
||||
img = env.render('rgb_array')
|
||||
plt.imshow(img)
|
||||
plt.show()
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
from .math_utils import *
|
||||
from .terrain import Terrain
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
# License: see [LICENSE, LICENSES/legged_gym/LICENSE]
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from isaacgym.torch_utils import quat_apply, normalize
|
||||
from torch import Tensor
|
||||
|
||||
|
||||
# @ torch.jit.script
|
||||
def quat_apply_yaw(quat, vec):
|
||||
quat_yaw = quat.clone().view(-1, 4)
|
||||
quat_yaw[:, :2] = 0.
|
||||
quat_yaw = normalize(quat_yaw)
|
||||
return quat_apply(quat_yaw, vec)
|
||||
|
||||
|
||||
# @ torch.jit.script
|
||||
def wrap_to_pi(angles):
|
||||
angles %= 2 * np.pi
|
||||
angles -= 2 * np.pi * (angles > np.pi)
|
||||
return angles
|
||||
|
||||
|
||||
# @ torch.jit.script
|
||||
def torch_rand_sqrt_float(lower, upper, shape, device):
|
||||
# type: (float, float, Tuple[int, int], str) -> Tensor
|
||||
r = 2 * torch.rand(*shape, device=device) - 1
|
||||
r = torch.where(r < 0., -torch.sqrt(-r), torch.sqrt(r))
|
||||
r = (r + 1.) / 2.
|
||||
return (upper - lower) * r + lower
|
||||
|
||||
|
||||
def get_scale_shift(range):
|
||||
scale = 2. / (range[1] - range[0])
|
||||
shift = (range[1] + range[0]) / 2.
|
||||
return scale, shift
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
# License: see [LICENSE, LICENSES/legged_gym/LICENSE]
|
||||
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
from isaacgym import terrain_utils
|
||||
from numpy.random import choice
|
||||
|
||||
from go1_gym.envs.base.legged_robot_config import Cfg
|
||||
|
||||
|
||||
class Terrain:
|
||||
def __init__(self, cfg: Cfg.terrain, num_robots, eval_cfg=None, num_eval_robots=0) -> None:
|
||||
|
||||
self.cfg = cfg
|
||||
self.eval_cfg = eval_cfg
|
||||
self.num_robots = num_robots
|
||||
self.type = cfg.mesh_type
|
||||
if self.type in ["none", 'plane']:
|
||||
return
|
||||
self.train_rows, self.train_cols, self.eval_rows, self.eval_cols = self.load_cfgs()
|
||||
self.tot_rows = len(self.train_rows) + len(self.eval_rows)
|
||||
self.tot_cols = max(len(self.train_cols), len(self.eval_cols))
|
||||
self.cfg.env_length = cfg.terrain_length
|
||||
self.cfg.env_width = cfg.terrain_width
|
||||
|
||||
self.height_field_raw = np.zeros((self.tot_rows, self.tot_cols), dtype=np.int16)
|
||||
|
||||
self.initialize_terrains()
|
||||
|
||||
self.heightsamples = self.height_field_raw
|
||||
if self.type == "trimesh":
|
||||
self.vertices, self.triangles = terrain_utils.convert_heightfield_to_trimesh(self.height_field_raw,
|
||||
self.cfg.horizontal_scale,
|
||||
self.cfg.vertical_scale,
|
||||
self.cfg.slope_treshold)
|
||||
|
||||
def load_cfgs(self):
|
||||
self._load_cfg(self.cfg)
|
||||
self.cfg.row_indices = np.arange(0, self.cfg.tot_rows)
|
||||
self.cfg.col_indices = np.arange(0, self.cfg.tot_cols)
|
||||
self.cfg.x_offset = 0
|
||||
self.cfg.rows_offset = 0
|
||||
if self.eval_cfg is None:
|
||||
return self.cfg.row_indices, self.cfg.col_indices, [], []
|
||||
else:
|
||||
self._load_cfg(self.eval_cfg)
|
||||
self.eval_cfg.row_indices = np.arange(self.cfg.tot_rows, self.cfg.tot_rows + self.eval_cfg.tot_rows)
|
||||
self.eval_cfg.col_indices = np.arange(0, self.eval_cfg.tot_cols)
|
||||
self.eval_cfg.x_offset = self.cfg.tot_rows
|
||||
self.eval_cfg.rows_offset = self.cfg.num_rows
|
||||
return self.cfg.row_indices, self.cfg.col_indices, self.eval_cfg.row_indices, self.eval_cfg.col_indices
|
||||
|
||||
def _load_cfg(self, cfg):
|
||||
cfg.proportions = [np.sum(cfg.terrain_proportions[:i + 1]) for i in range(len(cfg.terrain_proportions))]
|
||||
|
||||
cfg.num_sub_terrains = cfg.num_rows * cfg.num_cols
|
||||
cfg.env_origins = np.zeros((cfg.num_rows, cfg.num_cols, 3))
|
||||
|
||||
cfg.width_per_env_pixels = int(cfg.terrain_length / cfg.horizontal_scale)
|
||||
cfg.length_per_env_pixels = int(cfg.terrain_width / cfg.horizontal_scale)
|
||||
|
||||
cfg.border = int(cfg.border_size / cfg.horizontal_scale)
|
||||
cfg.tot_cols = int(cfg.num_cols * cfg.width_per_env_pixels) + 2 * cfg.border
|
||||
cfg.tot_rows = int(cfg.num_rows * cfg.length_per_env_pixels) + 2 * cfg.border
|
||||
|
||||
def initialize_terrains(self):
|
||||
self._initialize_terrain(self.cfg)
|
||||
if self.eval_cfg is not None:
|
||||
self._initialize_terrain(self.eval_cfg)
|
||||
|
||||
def _initialize_terrain(self, cfg):
|
||||
if cfg.curriculum:
|
||||
self.curriculum(cfg)
|
||||
elif cfg.selected:
|
||||
self.selected_terrain(cfg)
|
||||
else:
|
||||
self.randomized_terrain(cfg)
|
||||
|
||||
def randomized_terrain(self, cfg):
|
||||
for k in range(cfg.num_sub_terrains):
|
||||
# Env coordinates in the world
|
||||
(i, j) = np.unravel_index(k, (cfg.num_rows, cfg.num_cols))
|
||||
|
||||
choice = np.random.uniform(0, 1)
|
||||
difficulty = np.random.choice([0.5, 0.75, 0.9])
|
||||
terrain = self.make_terrain(cfg, choice, difficulty, cfg.proportions)
|
||||
self.add_terrain_to_map(cfg, terrain, i, j)
|
||||
|
||||
def curriculum(self, cfg):
|
||||
for j in range(cfg.num_cols):
|
||||
for i in range(cfg.num_rows):
|
||||
difficulty = i / cfg.num_rows * cfg.difficulty_scale
|
||||
choice = j / cfg.num_cols + 0.001
|
||||
|
||||
terrain = self.make_terrain(cfg, choice, difficulty, cfg.proportions)
|
||||
self.add_terrain_to_map(cfg, terrain, i, j)
|
||||
|
||||
def selected_terrain(self, cfg):
|
||||
terrain_type = cfg.terrain_kwargs.pop('type')
|
||||
for k in range(cfg.num_sub_terrains):
|
||||
# Env coordinates in the world
|
||||
(i, j) = np.unravel_index(k, (cfg.num_rows, cfg.num_cols))
|
||||
|
||||
terrain = terrain_utils.SubTerrain("terrain",
|
||||
width=cfg.width_per_env_pixels,
|
||||
length=cfg.width_per_env_pixels,
|
||||
vertical_scale=cfg.vertical_scale,
|
||||
horizontal_scale=cfg.horizontal_scale)
|
||||
|
||||
eval(terrain_type)(terrain, **cfg.terrain_kwargs.terrain_kwargs)
|
||||
self.add_terrain_to_map(cfg, terrain, i, j)
|
||||
|
||||
def make_terrain(self, cfg, choice, difficulty, proportions):
|
||||
terrain = terrain_utils.SubTerrain("terrain",
|
||||
width=cfg.width_per_env_pixels,
|
||||
length=cfg.width_per_env_pixels,
|
||||
vertical_scale=cfg.vertical_scale,
|
||||
horizontal_scale=cfg.horizontal_scale)
|
||||
slope = difficulty * 0.4
|
||||
step_height = 0.05 + 0.18 * difficulty
|
||||
discrete_obstacles_height = 0.05 + difficulty * (cfg.max_platform_height - 0.05)
|
||||
stepping_stones_size = 1.5 * (1.05 - difficulty)
|
||||
stone_distance = 0.05 if difficulty == 0 else 0.1
|
||||
if choice < proportions[0]:
|
||||
if choice < proportions[0] / 2:
|
||||
slope *= -1
|
||||
terrain_utils.pyramid_sloped_terrain(terrain, slope=slope, platform_size=3.)
|
||||
elif choice < proportions[1]:
|
||||
terrain_utils.pyramid_sloped_terrain(terrain, slope=slope, platform_size=3.)
|
||||
terrain_utils.random_uniform_terrain(terrain, min_height=-0.05, max_height=0.05,
|
||||
step=self.cfg.terrain_smoothness, downsampled_scale=0.2)
|
||||
elif choice < proportions[3]:
|
||||
if choice < proportions[2]:
|
||||
step_height *= -1
|
||||
terrain_utils.pyramid_stairs_terrain(terrain, step_width=0.31, step_height=step_height, platform_size=3.)
|
||||
elif choice < proportions[4]:
|
||||
num_rectangles = 20
|
||||
rectangle_min_size = 1.
|
||||
rectangle_max_size = 2.
|
||||
terrain_utils.discrete_obstacles_terrain(terrain, discrete_obstacles_height, rectangle_min_size,
|
||||
rectangle_max_size, num_rectangles, platform_size=3.)
|
||||
elif choice < proportions[5]:
|
||||
terrain_utils.stepping_stones_terrain(terrain, stone_size=stepping_stones_size,
|
||||
stone_distance=stone_distance, max_height=0., platform_size=4.)
|
||||
elif choice < proportions[6]:
|
||||
pass
|
||||
elif choice < proportions[7]:
|
||||
pass
|
||||
elif choice < proportions[8]:
|
||||
terrain_utils.random_uniform_terrain(terrain, min_height=-cfg.terrain_noise_magnitude,
|
||||
max_height=cfg.terrain_noise_magnitude, step=0.005,
|
||||
downsampled_scale=0.2)
|
||||
elif choice < proportions[9]:
|
||||
terrain_utils.random_uniform_terrain(terrain, min_height=-0.05, max_height=0.05,
|
||||
step=self.cfg.terrain_smoothness, downsampled_scale=0.2)
|
||||
terrain.height_field_raw[0:terrain.length // 2, :] = 0
|
||||
|
||||
return terrain
|
||||
|
||||
def add_terrain_to_map(self, cfg, terrain, row, col):
|
||||
i = row
|
||||
j = col
|
||||
# map coordinate system
|
||||
start_x = cfg.border + i * cfg.length_per_env_pixels + cfg.x_offset
|
||||
end_x = cfg.border + (i + 1) * cfg.length_per_env_pixels + cfg.x_offset
|
||||
start_y = cfg.border + j * cfg.width_per_env_pixels
|
||||
end_y = cfg.border + (j + 1) * cfg.width_per_env_pixels
|
||||
self.height_field_raw[start_x: end_x, start_y:end_y] = terrain.height_field_raw
|
||||
|
||||
env_origin_x = (i + 0.5) * cfg.terrain_length + cfg.x_offset * terrain.horizontal_scale
|
||||
env_origin_y = (j + 0.5) * cfg.terrain_width
|
||||
x1 = int((cfg.terrain_length / 2. - 1) / terrain.horizontal_scale) + cfg.x_offset
|
||||
x2 = int((cfg.terrain_length / 2. + 1) / terrain.horizontal_scale) + cfg.x_offset
|
||||
y1 = int((cfg.terrain_width / 2. - 1) / terrain.horizontal_scale)
|
||||
y2 = int((cfg.terrain_width / 2. + 1) / terrain.horizontal_scale)
|
||||
env_origin_z = np.max(self.height_field_raw[start_x: end_x, start_y:end_y]) * terrain.vertical_scale
|
||||
|
||||
cfg.env_origins[i, j] = [env_origin_x, env_origin_y, env_origin_z]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
sudo docker stop foxy_controller || true
|
||||
sudo docker rm foxy_controller || true
|
||||
cd ~/go1_gym/go1_gym_deploy/docker/
|
||||
sudo make autostart
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
sudo docker stop foxy_controller || true
|
||||
sudo docker rm foxy_controller || true
|
||||
sudo kill $(ps aux |grep lcm_position | awk '{print $2}')
|
||||
cd ~/go1_gym/go1_gym_deploy/unitree_legged_sdk_bin/
|
||||
yes "" | sudo ./lcm_position &
|
||||
@@ -0,0 +1,190 @@
|
||||
# syntax=docker/dockerfile:experimental
|
||||
|
||||
FROM nvcr.io/nvidia/l4t-pytorch:r32.6.1-pth1.9-py3
|
||||
|
||||
#ENV NVIDIA_VISIBLE_DEVICES ${NVIDIA_VISIBLE_DEVICES:-all}
|
||||
#ENV NVIDIA_DRIVER_CAPABILITIES ${NVIDIA_DRIVER_CAPABILITIES:+$NVIDIA_DRIVER_CAPABILITIES,}graphics
|
||||
|
||||
# add new sudo user
|
||||
ENV USERNAME improbable
|
||||
ENV HOME /home/$USERNAME
|
||||
RUN useradd -m $USERNAME && \
|
||||
echo "$USERNAME:$USERNAME" | chpasswd && \
|
||||
usermod --shell /bin/bash $USERNAME && \
|
||||
usermod -aG sudo $USERNAME && \
|
||||
mkdir /etc/sudoers.d && \
|
||||
echo "$USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/$USERNAME && \
|
||||
chmod 0440 /etc/sudoers.d/$USERNAME && \
|
||||
# Replace 1000 with your user/group id
|
||||
usermod --uid 1000 $USERNAME && \
|
||||
groupmod --gid 1000 $USERNAME
|
||||
|
||||
|
||||
# install package
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
curl \
|
||||
sudo \
|
||||
less \
|
||||
emacs \
|
||||
apt-utils \
|
||||
tzdata \
|
||||
git \
|
||||
tmux \
|
||||
bash-completion \
|
||||
command-not-found \
|
||||
libglib2.0-0 \
|
||||
gstreamer1.0-plugins-* \
|
||||
libgstreamer1.0-* \
|
||||
libgstreamer-plugins-*1.0-* \
|
||||
&& \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
|
||||
#COPY config/nvidia_icd.json /usr/share/vulkan/icd.d/
|
||||
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
#RUN apt-get update && apt-get install -y python3-pip && pip3 install torch==1.9.0+cu111 torchvision==0.10.0+cu111 torchaudio==0.9.0 -f https://download.pytorch.org/whl/torch_stable.html
|
||||
|
||||
|
||||
# ==================================================================
|
||||
# Useful Libraries for Development
|
||||
# ------------------------------------------------------------------
|
||||
#RUN apt update && apt install -y apt-transport-https ca-certificates curl software-properties-common
|
||||
#RUN curl -fsSL https://download.sublimetext.com/sublimehq-pub.gpg | apt-key add - && add-apt-repository "deb https://download.sublimetext.com/ apt/stable/" && apt update && apt install sublime-text
|
||||
|
||||
|
||||
# ==================================================================
|
||||
# Python dependencies defined in requirements.txt
|
||||
# ------------------------------------------------------------------
|
||||
#RUN pip3 install --upgrade pip
|
||||
# copy local requirements file for pip install python deps
|
||||
#COPY ./requirements.txt /home/$USERNAME
|
||||
#WORKDIR /home/$USERNAME
|
||||
#RUN pip3 install -r requirements.txt
|
||||
|
||||
# LCM
|
||||
RUN apt-get -y update && apt-get install -y make gcc-8 g++-8
|
||||
RUN cd /home/$USERNAME && git clone https://github.com/lcm-proj/lcm.git && cd lcm && mkdir build && cd build && cmake .. && make -j && make install
|
||||
RUN cd /home/$USERNAME/lcm/lcm-python && pip3 install -e .
|
||||
|
||||
|
||||
RUN apt-get install -y vim
|
||||
#RUN pip3 install pandas
|
||||
|
||||
# ROS
|
||||
ENV ROS_DISTRO melodic
|
||||
|
||||
RUN apt-get install -y gnupg
|
||||
|
||||
COPY install_scripts/install_ros.sh /tmp/install_ros.sh
|
||||
RUN chmod +x /tmp/install_ros.sh
|
||||
RUN /tmp/install_ros.sh
|
||||
|
||||
# bootstrap rosdep
|
||||
RUN rosdep init \
|
||||
&& rosdep update
|
||||
|
||||
# create catkin workspace
|
||||
ENV CATKIN_WS=/root/catkin_ws
|
||||
RUN bash /opt/ros/melodic/setup.bash
|
||||
RUN mkdir -p $CATKIN_WS/src
|
||||
WORKDIR ${CATKIN_WS}
|
||||
RUN catkin init
|
||||
RUN catkin config --extend /opt/ros/$ROS_DISTRO \
|
||||
--cmake-args -DCMAKE_BUILD_TYPE=Release -DCATKIN_ENABLE_TESTING=False
|
||||
WORKDIR $CATKIN_WS/src
|
||||
|
||||
|
||||
RUN apt-get update && apt-get install -y freeglut3-dev libudev-dev
|
||||
#COPY ./install_scripts/install_vision_opencv.sh /tmp/install_vision_opencv.sh
|
||||
#RUN chmod +x /tmp/install_vision_opencv.sh
|
||||
#RUN /tmp/install_vision_opencv.sh
|
||||
|
||||
|
||||
RUN apt-get install -y libgl1-mesa-dev libudev1 libudev-dev
|
||||
|
||||
|
||||
#RUN apt-get install unzip
|
||||
#
|
||||
#RUN cd ~ && \
|
||||
# wget -O opencv.zip https://github.com/opencv/opencv/archive/4.5.1.zip && \
|
||||
# wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.5.1.zip && \
|
||||
# unzip opencv.zip && \
|
||||
# unzip opencv_contrib.zip && \
|
||||
# mv opencv-4.5.1 opencv && \
|
||||
# mv opencv_contrib-4.5.1 opencv_contrib && \
|
||||
# rm opencv.zip && \
|
||||
# rm opencv_contrib.zip
|
||||
#
|
||||
#RUN cd ~/opencv && \
|
||||
# mkdir build && \
|
||||
# cd build && \
|
||||
# cmake -D CMAKE_BUILD_TYPE=RELEASE \
|
||||
# -D CMAKE_INSTALL_PREFIX=/usr \
|
||||
# -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
|
||||
# -D EIGEN_INCLUDE_PATH=/usr/include/eigen3 \
|
||||
# -D WITH_OPENCL=OFF \
|
||||
# -D WITH_CUDA=OFF \
|
||||
# -D CUDA_ARCH_BIN=5.3 \
|
||||
# -D CUDA_ARCH_PTX="" \
|
||||
# -D WITH_CUDNN=OFF \
|
||||
# -D WITH_CUBLAS=OFF \
|
||||
# -D ENABLE_FAST_MATH=ON \
|
||||
# -D CUDA_FAST_MATH=OFF \
|
||||
# -D OPENCV_DNN_CUDA=OFF \
|
||||
# -D ENABLE_NEON=ON \
|
||||
# -D WITH_QT=OFF \
|
||||
# -D WITH_OPENMP=ON \
|
||||
# -D WITH_OPENGL=ON \
|
||||
# -D BUILD_TIFF=ON \
|
||||
# -D WITH_FFMPEG=ON \
|
||||
# -D WITH_GSTREAMER=ON \
|
||||
# -D WITH_TBB=ON \
|
||||
# -D BUILD_TBB=ON \
|
||||
# -D BUILD_TESTS=OFF \
|
||||
# -D WITH_EIGEN=ON \
|
||||
# -D WITH_V4L=ON \
|
||||
# -D WITH_LIBV4L=ON \
|
||||
# -D OPENCV_ENABLE_NONFREE=ON \
|
||||
# -D INSTALL_C_EXAMPLES=OFF \
|
||||
# -D INSTALL_PYTHON_EXAMPLES=OFF \
|
||||
# -D BUILD_NEW_PYTHON_SUPPORT=ON \
|
||||
# -D BUILD_opencv_python3=TRUE \
|
||||
# -D OPENCV_GENERATE_PKGCONFIG=ON \
|
||||
# -D BUILD_EXAMPLES=OFF .. && \
|
||||
# make -j4 && cd ~ && \
|
||||
# # sudo rm -r /usr/include/opencv4/opencv2 && \
|
||||
# cd ~/opencv/build && \
|
||||
# sudo make install && \
|
||||
# sudo ldconfig && \
|
||||
# make clean && \
|
||||
# sudo apt-get update
|
||||
|
||||
RUN apt-get install -y libgtk2.0-dev pkg-config
|
||||
RUN pip3 install opencv-python opencv-contrib-python
|
||||
|
||||
####################################################################################
|
||||
###### START HERE -- Install whatever dependencies you need specific to this project!
|
||||
####################################################################################
|
||||
|
||||
|
||||
#COPY ./rsc/IsaacGym_Preview_2_Package.tar.gz /home/$USERNAME/
|
||||
#RUN cd /home/$USERNAME && tar -xvzf IsaacGym_Preview_2_Package.tar.gz
|
||||
#COPY ./rsc/learning_to_walk_in_minutes.zip /home/$USERNAME/
|
||||
#RUN apt-get install unzip && cd /home/$USERNAME/ && unzip learning_to_walk_in_minutes.zip && cd ./code/rl-pytorch && pip3 install -e .
|
||||
#RUN cd /home/$USERNAME/isaacgym/python && pip3 install -e .
|
||||
#RUN cd /home/$USERNAME/code/isaacgym_anymal && pip3 install -e .
|
||||
#COPY ./src/isaacgym_anymal/ /home/$USERNAME/code/isaacgym_anymal/
|
||||
|
||||
# setup entrypoint
|
||||
COPY entrypoint.sh /
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["bash"]
|
||||
@@ -0,0 +1,37 @@
|
||||
default: build
|
||||
build:
|
||||
docker build -t jetson-model-deployment .
|
||||
clean-build:
|
||||
docker build -t jetson-model-deployment . --no-cache=true
|
||||
run:
|
||||
docker run -it \
|
||||
--env="DISPLAY" \
|
||||
--env="QT_X11_NO_MITSHM=1" \
|
||||
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
|
||||
--env="XAUTHORITY=${XAUTH}" \
|
||||
--volume="${XAUTH}:${XAUTH}" \
|
||||
--volume="/home/unitree/go1_gym:/home/isaac/go1_gym" \
|
||||
--privileged \
|
||||
--runtime=nvidia \
|
||||
--net=host \
|
||||
--workdir="/home/isaac/go1_gym" \
|
||||
--name="foxy_controller" \
|
||||
jetson-model-deployment bash
|
||||
autostart:
|
||||
docker stop foxy_controller || true
|
||||
docker rm foxy_controller || true
|
||||
docker run -d\
|
||||
--env="DISPLAY" \
|
||||
--env="QT_X11_NO_MITSHM=1" \
|
||||
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
|
||||
--env="XAUTHORITY=${XAUTH}" \
|
||||
--volume="${XAUTH}:${XAUTH}" \
|
||||
--volume="/home/unitree/go1_gym:/home/isaac/go1_gym" \
|
||||
--privileged \
|
||||
--runtime=nvidia \
|
||||
--net=host \
|
||||
--workdir="/home/isaac/go1_gym" \
|
||||
--name="foxy_controller" \
|
||||
jetson-model-deployment tail -f /dev/null
|
||||
docker start foxy_controller
|
||||
docker exec foxy_controller bash -c 'cd /home/isaac/go1_gym/ && python3 setup.py install && cd go1_gym_deploy/scripts && ls && python3 deploy_policy.py'
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker load -i deployment_image.tar
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker save -o deployment_image.tar jetson-model-deployment:latest
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
# import isaacgym
|
||||
|
||||
# assert isaacgym, "import isaacgym before pytorch"
|
||||
import torch
|
||||
|
||||
|
||||
class HistoryWrapper:
|
||||
def __init__(self, env):
|
||||
self.env = env
|
||||
|
||||
if isinstance(self.env.cfg, dict):
|
||||
self.obs_history_length = self.env.cfg["env"]["num_observation_history"]
|
||||
else:
|
||||
self.obs_history_length = self.env.cfg.env.num_observation_history
|
||||
self.num_obs_history = self.obs_history_length * self.env.num_obs
|
||||
self.obs_history = torch.zeros(self.env.num_envs, self.num_obs_history, dtype=torch.float,
|
||||
device=self.env.device, requires_grad=False)
|
||||
self.num_privileged_obs = self.env.num_privileged_obs
|
||||
|
||||
def step(self, action):
|
||||
obs, rew, done, info = self.env.step(action)
|
||||
privileged_obs = info["privileged_obs"]
|
||||
|
||||
self.obs_history = torch.cat((self.obs_history[:, self.env.num_obs:], obs), dim=-1)
|
||||
return {'obs': obs, 'privileged_obs': privileged_obs, 'obs_history': self.obs_history}, rew, done, info
|
||||
|
||||
def get_observations(self):
|
||||
obs = self.env.get_observations()
|
||||
privileged_obs = self.env.get_privileged_observations()
|
||||
self.obs_history = torch.cat((self.obs_history[:, self.env.num_obs:], obs), dim=-1)
|
||||
return {'obs': obs, 'privileged_obs': privileged_obs, 'obs_history': self.obs_history}
|
||||
|
||||
def get_obs(self):
|
||||
obs = self.env.get_obs()
|
||||
privileged_obs = self.env.get_privileged_observations()
|
||||
self.obs_history = torch.cat((self.obs_history[:, self.env.num_obs:], obs), dim=-1)
|
||||
return {'obs': obs, 'privileged_obs': privileged_obs, 'obs_history': self.obs_history}
|
||||
|
||||
def reset_idx(self, env_ids): # it might be a problem that this isn't getting called!!
|
||||
ret = self.env.reset_idx(env_ids)
|
||||
self.obs_history[env_ids, :] = 0
|
||||
return ret
|
||||
|
||||
def reset(self):
|
||||
ret = self.env.reset()
|
||||
privileged_obs = self.env.get_privileged_observations()
|
||||
self.obs_history[:, :] = 0
|
||||
return {"obs": ret, "privileged_obs": privileged_obs, "obs_history": self.obs_history}
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.env, name)
|
||||
Executable
+301
@@ -0,0 +1,301 @@
|
||||
import time
|
||||
|
||||
import lcm
|
||||
import numpy as np
|
||||
import torch
|
||||
import cv2
|
||||
|
||||
from go1_gym_deploy.lcm_types.pd_tau_targets_lcmt import pd_tau_targets_lcmt
|
||||
|
||||
lc = lcm.LCM("udpm://239.255.76.67:7667?ttl=255")
|
||||
|
||||
|
||||
def class_to_dict(obj) -> dict:
|
||||
if not hasattr(obj, "__dict__"):
|
||||
return obj
|
||||
result = {}
|
||||
for key in dir(obj):
|
||||
if key.startswith("_") or key == "terrain":
|
||||
continue
|
||||
element = []
|
||||
val = getattr(obj, key)
|
||||
if isinstance(val, list):
|
||||
for item in val:
|
||||
element.append(class_to_dict(item))
|
||||
else:
|
||||
element = class_to_dict(val)
|
||||
result[key] = element
|
||||
return result
|
||||
|
||||
|
||||
class LCMAgent():
|
||||
def __init__(self, cfg, se, command_profile):
|
||||
if not isinstance(cfg, dict):
|
||||
cfg = class_to_dict(cfg)
|
||||
self.cfg = cfg
|
||||
self.se = se
|
||||
self.command_profile = command_profile
|
||||
|
||||
self.dt = self.cfg["control"]["decimation"] * self.cfg["sim"]["dt"]
|
||||
self.timestep = 0
|
||||
|
||||
self.num_obs = self.cfg["env"]["num_observations"]
|
||||
self.num_envs = 1
|
||||
self.num_privileged_obs = self.cfg["env"]["num_privileged_obs"]
|
||||
self.num_actions = self.cfg["env"]["num_actions"]
|
||||
self.num_commands = self.cfg["commands"]["num_commands"]
|
||||
self.device = 'cpu'
|
||||
|
||||
if "obs_scales" in self.cfg.keys():
|
||||
self.obs_scales = self.cfg["obs_scales"]
|
||||
else:
|
||||
self.obs_scales = self.cfg["normalization"]["obs_scales"]
|
||||
|
||||
self.commands_scale = np.array(
|
||||
[self.obs_scales["lin_vel"], self.obs_scales["lin_vel"],
|
||||
self.obs_scales["ang_vel"], self.obs_scales["body_height_cmd"], 1, 1, 1, 1, 1,
|
||||
self.obs_scales["footswing_height_cmd"], self.obs_scales["body_pitch_cmd"],
|
||||
# 0, self.obs_scales["body_pitch_cmd"],
|
||||
self.obs_scales["body_roll_cmd"], self.obs_scales["stance_width_cmd"],
|
||||
self.obs_scales["stance_length_cmd"], self.obs_scales["aux_reward_cmd"], 1, 1, 1, 1, 1, 1
|
||||
])[:self.num_commands]
|
||||
|
||||
|
||||
joint_names = [
|
||||
"FL_hip_joint", "FL_thigh_joint", "FL_calf_joint",
|
||||
"FR_hip_joint", "FR_thigh_joint", "FR_calf_joint",
|
||||
"RL_hip_joint", "RL_thigh_joint", "RL_calf_joint",
|
||||
"RR_hip_joint", "RR_thigh_joint", "RR_calf_joint", ]
|
||||
self.default_dof_pos = np.array([self.cfg["init_state"]["default_joint_angles"][name] for name in joint_names])
|
||||
try:
|
||||
self.default_dof_pos_scale = np.array([self.cfg["init_state"]["default_hip_scales"], self.cfg["init_state"]["default_thigh_scales"], self.cfg["init_state"]["default_calf_scales"],
|
||||
self.cfg["init_state"]["default_hip_scales"], self.cfg["init_state"]["default_thigh_scales"], self.cfg["init_state"]["default_calf_scales"],
|
||||
self.cfg["init_state"]["default_hip_scales"], self.cfg["init_state"]["default_thigh_scales"], self.cfg["init_state"]["default_calf_scales"],
|
||||
self.cfg["init_state"]["default_hip_scales"], self.cfg["init_state"]["default_thigh_scales"], self.cfg["init_state"]["default_calf_scales"]])
|
||||
except KeyError:
|
||||
self.default_dof_pos_scale = np.ones(12)
|
||||
self.default_dof_pos = self.default_dof_pos * self.default_dof_pos_scale
|
||||
|
||||
self.p_gains = np.zeros(12)
|
||||
self.d_gains = np.zeros(12)
|
||||
for i in range(12):
|
||||
joint_name = joint_names[i]
|
||||
found = False
|
||||
for dof_name in self.cfg["control"]["stiffness"].keys():
|
||||
if dof_name in joint_name:
|
||||
self.p_gains[i] = self.cfg["control"]["stiffness"][dof_name]
|
||||
self.d_gains[i] = self.cfg["control"]["damping"][dof_name]
|
||||
found = True
|
||||
if not found:
|
||||
self.p_gains[i] = 0.
|
||||
self.d_gains[i] = 0.
|
||||
if self.cfg["control"]["control_type"] in ["P", "V"]:
|
||||
print(f"PD gain of joint {joint_name} were not defined, setting them to zero")
|
||||
|
||||
print(f"p_gains: {self.p_gains}")
|
||||
|
||||
self.commands = np.zeros((1, self.num_commands))
|
||||
self.actions = torch.zeros(12)
|
||||
self.last_actions = torch.zeros(12)
|
||||
self.gravity_vector = np.zeros(3)
|
||||
self.dof_pos = np.zeros(12)
|
||||
self.dof_vel = np.zeros(12)
|
||||
self.body_linear_vel = np.zeros(3)
|
||||
self.body_angular_vel = np.zeros(3)
|
||||
self.joint_pos_target = np.zeros(12)
|
||||
self.joint_vel_target = np.zeros(12)
|
||||
self.torques = np.zeros(12)
|
||||
self.contact_state = np.ones(4)
|
||||
|
||||
self.joint_idxs = self.se.joint_idxs
|
||||
|
||||
self.gait_indices = torch.zeros(self.num_envs, dtype=torch.float)
|
||||
self.clock_inputs = torch.zeros(self.num_envs, 4, dtype=torch.float)
|
||||
|
||||
if "obs_scales" in self.cfg.keys():
|
||||
self.obs_scales = self.cfg["obs_scales"]
|
||||
else:
|
||||
self.obs_scales = self.cfg["normalization"]["obs_scales"]
|
||||
|
||||
self.is_currently_probing = False
|
||||
|
||||
def set_probing(self, is_currently_probing):
|
||||
self.is_currently_probing = is_currently_probing
|
||||
|
||||
def get_obs(self):
|
||||
|
||||
self.gravity_vector = self.se.get_gravity_vector()
|
||||
cmds, reset_timer = self.command_profile.get_command(self.timestep * self.dt, probe=self.is_currently_probing)
|
||||
self.commands[:, :] = cmds[:self.num_commands]
|
||||
if reset_timer:
|
||||
self.reset_gait_indices()
|
||||
#else:
|
||||
# self.commands[:, 0:3] = self.command_profile.get_command(self.timestep * self.dt)[0:3]
|
||||
self.dof_pos = self.se.get_dof_pos()
|
||||
self.dof_vel = self.se.get_dof_vel()
|
||||
self.body_linear_vel = self.se.get_body_linear_vel()
|
||||
self.body_angular_vel = self.se.get_body_angular_vel()
|
||||
|
||||
ob = np.concatenate((self.gravity_vector.reshape(1, -1),
|
||||
self.commands * self.commands_scale,
|
||||
(self.dof_pos - self.default_dof_pos).reshape(1, -1) * self.obs_scales["dof_pos"],
|
||||
self.dof_vel.reshape(1, -1) * self.obs_scales["dof_vel"],
|
||||
torch.clip(self.actions, -self.cfg["normalization"]["clip_actions"],
|
||||
self.cfg["normalization"]["clip_actions"]).cpu().detach().numpy().reshape(1, -1)
|
||||
), axis=1)
|
||||
|
||||
if self.cfg["env"]["observe_two_prev_actions"]:
|
||||
ob = np.concatenate((ob,
|
||||
self.last_actions.cpu().detach().numpy().reshape(1, -1)), axis=1)
|
||||
|
||||
if self.cfg["env"]["observe_clock_inputs"]:
|
||||
ob = np.concatenate((ob,
|
||||
self.clock_inputs), axis=1)
|
||||
# print(self.clock_inputs)
|
||||
|
||||
if self.cfg["env"]["observe_vel"]:
|
||||
ob = np.concatenate(
|
||||
(self.body_linear_vel.reshape(1, -1) * self.obs_scales["lin_vel"],
|
||||
self.body_angular_vel.reshape(1, -1) * self.obs_scales["ang_vel"],
|
||||
ob), axis=1)
|
||||
|
||||
if self.cfg["env"]["observe_only_lin_vel"]:
|
||||
ob = np.concatenate(
|
||||
(self.body_linear_vel.reshape(1, -1) * self.obs_scales["lin_vel"],
|
||||
ob), axis=1)
|
||||
|
||||
if self.cfg["env"]["observe_yaw"]:
|
||||
heading = self.se.get_yaw()
|
||||
ob = np.concatenate((ob, heading.reshape(1, -1)), axis=-1)
|
||||
|
||||
self.contact_state = self.se.get_contact_state()
|
||||
if "observe_contact_states" in self.cfg["env"].keys() and self.cfg["env"]["observe_contact_states"]:
|
||||
ob = np.concatenate((ob, self.contact_state.reshape(1, -1)), axis=-1)
|
||||
|
||||
if "terrain" in self.cfg.keys() and self.cfg["terrain"]["measure_heights"]:
|
||||
robot_height = 0.25
|
||||
self.measured_heights = np.zeros(
|
||||
(len(self.cfg["terrain"]["measured_points_x"]), len(self.cfg["terrain"]["measured_points_y"]))).reshape(
|
||||
1, -1)
|
||||
heights = np.clip(robot_height - 0.5 - self.measured_heights, -1, 1.) * self.obs_scales["height_measurements"]
|
||||
ob = np.concatenate((ob, heights), axis=1)
|
||||
|
||||
|
||||
return torch.tensor(ob, device=self.device).float()
|
||||
|
||||
def get_privileged_observations(self):
|
||||
return None
|
||||
|
||||
def publish_action(self, action, hard_reset=False):
|
||||
|
||||
command_for_robot = pd_tau_targets_lcmt()
|
||||
self.joint_pos_target = \
|
||||
(action[0, :12].detach().cpu().numpy() * self.cfg["control"]["action_scale"]).flatten()
|
||||
self.joint_pos_target[[0, 3, 6, 9]] *= self.cfg["control"]["hip_scale_reduction"]
|
||||
# self.joint_pos_target[[0, 3, 6, 9]] *= -1
|
||||
self.joint_pos_target = self.joint_pos_target
|
||||
self.joint_pos_target += self.default_dof_pos
|
||||
joint_pos_target = self.joint_pos_target[self.joint_idxs]
|
||||
self.joint_vel_target = np.zeros(12)
|
||||
# print(f'cjp {self.joint_pos_target}')
|
||||
|
||||
command_for_robot.q_des = joint_pos_target
|
||||
command_for_robot.qd_des = self.joint_vel_target
|
||||
command_for_robot.kp = self.p_gains
|
||||
command_for_robot.kd = self.d_gains
|
||||
command_for_robot.tau_ff = np.zeros(12)
|
||||
command_for_robot.se_contactState = np.zeros(4)
|
||||
command_for_robot.timestamp_us = int(time.time() * 10 ** 6)
|
||||
command_for_robot.id = 0
|
||||
|
||||
if hard_reset:
|
||||
command_for_robot.id = -1
|
||||
|
||||
|
||||
self.torques = (self.joint_pos_target - self.dof_pos) * self.p_gains + (self.joint_vel_target - self.dof_vel) * self.d_gains
|
||||
|
||||
lc.publish("pd_plustau_targets", command_for_robot.encode())
|
||||
|
||||
def reset(self):
|
||||
self.actions = torch.zeros(12)
|
||||
self.time = time.time()
|
||||
self.timestep = 0
|
||||
return self.get_obs()
|
||||
|
||||
def reset_gait_indices(self):
|
||||
self.gait_indices = torch.zeros(self.num_envs, dtype=torch.float)
|
||||
|
||||
def step(self, actions, hard_reset=False):
|
||||
clip_actions = self.cfg["normalization"]["clip_actions"]
|
||||
self.last_actions = self.actions[:]
|
||||
self.actions = torch.clip(actions[0:1, :], -clip_actions, clip_actions)
|
||||
self.publish_action(self.actions, hard_reset=hard_reset)
|
||||
time.sleep(max(self.dt - (time.time() - self.time), 0))
|
||||
if self.timestep % 100 == 0: print(f'frq: {1 / (time.time() - self.time)} Hz');
|
||||
self.time = time.time()
|
||||
obs = self.get_obs()
|
||||
|
||||
# clock accounting
|
||||
frequencies = self.commands[:, 4]
|
||||
phases = self.commands[:, 5]
|
||||
offsets = self.commands[:, 6]
|
||||
if self.num_commands == 8:
|
||||
bounds = 0
|
||||
durations = self.commands[:, 7]
|
||||
else:
|
||||
bounds = self.commands[:, 7]
|
||||
durations = self.commands[:, 8]
|
||||
self.gait_indices = torch.remainder(self.gait_indices + self.dt * frequencies, 1.0)
|
||||
|
||||
if "pacing_offset" in self.cfg["commands"] and self.cfg["commands"]["pacing_offset"]:
|
||||
self.foot_indices = [self.gait_indices + phases + offsets + bounds,
|
||||
self.gait_indices + bounds,
|
||||
self.gait_indices + offsets,
|
||||
self.gait_indices + phases]
|
||||
else:
|
||||
self.foot_indices = [self.gait_indices + phases + offsets + bounds,
|
||||
self.gait_indices + offsets,
|
||||
self.gait_indices + bounds,
|
||||
self.gait_indices + phases]
|
||||
self.clock_inputs[:, 0] = torch.sin(2 * np.pi * self.foot_indices[0])
|
||||
self.clock_inputs[:, 1] = torch.sin(2 * np.pi * self.foot_indices[1])
|
||||
self.clock_inputs[:, 2] = torch.sin(2 * np.pi * self.foot_indices[2])
|
||||
self.clock_inputs[:, 3] = torch.sin(2 * np.pi * self.foot_indices[3])
|
||||
|
||||
|
||||
images = {'front': self.se.get_camera_front(),
|
||||
'bottom': self.se.get_camera_bottom(),
|
||||
'rear': self.se.get_camera_rear(),
|
||||
'left': self.se.get_camera_left(),
|
||||
'right': self.se.get_camera_right()
|
||||
}
|
||||
downscale_factor = 2
|
||||
temporal_downscale = 3
|
||||
|
||||
for k, v in images.items():
|
||||
if images[k] is not None:
|
||||
images[k] = cv2.resize(images[k], dsize=(images[k].shape[0]//downscale_factor, images[k].shape[1]//downscale_factor), interpolation=cv2.INTER_CUBIC)
|
||||
if self.timestep % temporal_downscale != 0:
|
||||
images[k] = None
|
||||
#print(self.commands)
|
||||
|
||||
infos = {"joint_pos": self.dof_pos[np.newaxis, :],
|
||||
"joint_vel": self.dof_vel[np.newaxis, :],
|
||||
"joint_pos_target": self.joint_pos_target[np.newaxis, :],
|
||||
"joint_vel_target": self.joint_vel_target[np.newaxis, :],
|
||||
"body_linear_vel": self.body_linear_vel[np.newaxis, :],
|
||||
"body_angular_vel": self.body_angular_vel[np.newaxis, :],
|
||||
"contact_state": self.contact_state[np.newaxis, :],
|
||||
"clock_inputs": self.clock_inputs[np.newaxis, :],
|
||||
"body_linear_vel_cmd": self.commands[:, 0:2],
|
||||
"body_angular_vel_cmd": self.commands[:, 2:],
|
||||
"privileged_obs": None,
|
||||
"camera_image_front": images['front'],
|
||||
"camera_image_bottom": images['bottom'],
|
||||
"camera_image_rear": images['rear'],
|
||||
"camera_image_left": images['left'],
|
||||
"camera_image_right": images['right'],
|
||||
}
|
||||
|
||||
self.timestep += 1
|
||||
return obs, None, None, infos
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "======================================"
|
||||
echo "== Go1 Sim-to-Real Installation Kit =="
|
||||
echo "======================================"
|
||||
echo ""
|
||||
echo "Author: Gabriel Margolis, Improbable AI Lab, MIT"
|
||||
echo "This software is intended to support controls research. It includes safety features but may still damage your Go1. The user assumes all risk."
|
||||
echo ""
|
||||
|
||||
read -r -p "Extract docker container? [y/N] " response
|
||||
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
|
||||
then
|
||||
# load docker image
|
||||
echo "[Step 1] Extracting docker image..."
|
||||
docker load -i deployment_image.tar
|
||||
printf "\nDone!\n"
|
||||
else
|
||||
echo "Quitting"
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""LCM type definitions
|
||||
This file automatically generated by lcm.
|
||||
DO NOT MODIFY BY HAND!!!!
|
||||
"""
|
||||
|
||||
try:
|
||||
import cStringIO.StringIO as BytesIO
|
||||
except ImportError:
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
class camera_message_lcmt(object):
|
||||
__slots__ = ["data"]
|
||||
|
||||
__typenames__ = ["byte"]
|
||||
|
||||
__dimensions__ = [[278400]]
|
||||
|
||||
def __init__(self):
|
||||
self.data = ""
|
||||
|
||||
def encode(self):
|
||||
buf = BytesIO()
|
||||
buf.write(camera_message_lcmt._get_packed_fingerprint())
|
||||
self._encode_one(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
def _encode_one(self, buf):
|
||||
buf.write(bytearray(self.data[:278400]))
|
||||
|
||||
def decode(data):
|
||||
if hasattr(data, 'read'):
|
||||
buf = data
|
||||
else:
|
||||
buf = BytesIO(data)
|
||||
if buf.read(8) != camera_message_lcmt._get_packed_fingerprint():
|
||||
raise ValueError("Decode error")
|
||||
return camera_message_lcmt._decode_one(buf)
|
||||
decode = staticmethod(decode)
|
||||
|
||||
def _decode_one(buf):
|
||||
self = camera_message_lcmt()
|
||||
self.data = buf.read(278400)
|
||||
return self
|
||||
_decode_one = staticmethod(_decode_one)
|
||||
|
||||
_hash = None
|
||||
def _get_hash_recursive(parents):
|
||||
if camera_message_lcmt in parents: return 0
|
||||
tmphash = (0x1610a8a9f4d174b7) & 0xffffffffffffffff
|
||||
tmphash = (((tmphash<<1)&0xffffffffffffffff) + (tmphash>>63)) & 0xffffffffffffffff
|
||||
return tmphash
|
||||
_get_hash_recursive = staticmethod(_get_hash_recursive)
|
||||
_packed_fingerprint = None
|
||||
|
||||
def _get_packed_fingerprint():
|
||||
if camera_message_lcmt._packed_fingerprint is None:
|
||||
camera_message_lcmt._packed_fingerprint = struct.pack(">Q", camera_message_lcmt._get_hash_recursive([]))
|
||||
return camera_message_lcmt._packed_fingerprint
|
||||
_get_packed_fingerprint = staticmethod(_get_packed_fingerprint)
|
||||
@@ -0,0 +1,57 @@
|
||||
"""LCM type definitions
|
||||
This file automatically generated by lcm.
|
||||
DO NOT MODIFY BY HAND!!!!
|
||||
"""
|
||||
|
||||
try:
|
||||
import cStringIO.StringIO as BytesIO
|
||||
except ImportError:
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
class camera_message_rect_wide(object):
|
||||
__slots__ = ["data"]
|
||||
|
||||
def __init__(self):
|
||||
self.data = ""
|
||||
|
||||
def encode(self):
|
||||
buf = BytesIO()
|
||||
buf.write(camera_message_rect_wide._get_packed_fingerprint())
|
||||
self._encode_one(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
def _encode_one(self, buf):
|
||||
buf.write(bytearray(self.data[:34800]))
|
||||
|
||||
def decode(data):
|
||||
if hasattr(data, 'read'):
|
||||
buf = data
|
||||
else:
|
||||
buf = BytesIO(data)
|
||||
if buf.read(8) != camera_message_rect_wide._get_packed_fingerprint():
|
||||
raise ValueError("Decode error")
|
||||
return camera_message_rect_wide._decode_one(buf)
|
||||
decode = staticmethod(decode)
|
||||
|
||||
def _decode_one(buf):
|
||||
self = camera_message_rect_wide()
|
||||
self.data = buf.read(34800)
|
||||
return self
|
||||
_decode_one = staticmethod(_decode_one)
|
||||
|
||||
_hash = None
|
||||
def _get_hash_recursive(parents):
|
||||
if camera_message_rect_wide in parents: return 0
|
||||
tmphash = (0xc3e9f058530b2a8b) & 0xffffffffffffffff
|
||||
tmphash = (((tmphash<<1)&0xffffffffffffffff) + (tmphash>>63)) & 0xffffffffffffffff
|
||||
return tmphash
|
||||
_get_hash_recursive = staticmethod(_get_hash_recursive)
|
||||
_packed_fingerprint = None
|
||||
|
||||
def _get_packed_fingerprint():
|
||||
if camera_message_rect_wide._packed_fingerprint is None:
|
||||
camera_message_rect_wide._packed_fingerprint = struct.pack(">Q", camera_message_rect_wide._get_hash_recursive([]))
|
||||
return camera_message_rect_wide._packed_fingerprint
|
||||
_get_packed_fingerprint = staticmethod(_get_packed_fingerprint)
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
struct leg_control_data_lcmt
|
||||
{
|
||||
float q[12];
|
||||
float qd[12];
|
||||
float p[12];
|
||||
float v[12];
|
||||
float tau_est[12];
|
||||
int64_t timestamp_us;
|
||||
int64_t id;
|
||||
int64_t robot_id;
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
"""LCM type definitions
|
||||
This file automatically generated by lcm.
|
||||
DO NOT MODIFY BY HAND!!!!
|
||||
"""
|
||||
|
||||
try:
|
||||
import cStringIO.StringIO as BytesIO
|
||||
except ImportError:
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
|
||||
class leg_control_data_lcmt(object):
|
||||
__slots__ = ["q", "qd", "p", "v", "tau_est", "timestamp_us", "id", "robot_id"]
|
||||
|
||||
__typenames__ = ["float", "float", "float", "float", "float", "int64_t", "int64_t", "int64_t"]
|
||||
|
||||
__dimensions__ = [[12], [12], [12], [12], [12], None, None, None]
|
||||
|
||||
def __init__(self):
|
||||
self.q = [0.0 for dim0 in range(12)]
|
||||
self.qd = [0.0 for dim0 in range(12)]
|
||||
self.p = [0.0 for dim0 in range(12)]
|
||||
self.v = [0.0 for dim0 in range(12)]
|
||||
self.tau_est = [0.0 for dim0 in range(12)]
|
||||
self.timestamp_us = 0
|
||||
self.id = 0
|
||||
self.robot_id = 0
|
||||
|
||||
def encode(self):
|
||||
buf = BytesIO()
|
||||
buf.write(leg_control_data_lcmt._get_packed_fingerprint())
|
||||
self._encode_one(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
def _encode_one(self, buf):
|
||||
buf.write(struct.pack('>12f', *self.q[:12]))
|
||||
buf.write(struct.pack('>12f', *self.qd[:12]))
|
||||
buf.write(struct.pack('>12f', *self.p[:12]))
|
||||
buf.write(struct.pack('>12f', *self.v[:12]))
|
||||
buf.write(struct.pack('>12f', *self.tau_est[:12]))
|
||||
buf.write(struct.pack(">qqq", self.timestamp_us, self.id, self.robot_id))
|
||||
|
||||
def decode(data):
|
||||
if hasattr(data, 'read'):
|
||||
buf = data
|
||||
else:
|
||||
buf = BytesIO(data)
|
||||
if buf.read(8) != leg_control_data_lcmt._get_packed_fingerprint():
|
||||
raise ValueError("Decode error")
|
||||
return leg_control_data_lcmt._decode_one(buf)
|
||||
|
||||
decode = staticmethod(decode)
|
||||
|
||||
def _decode_one(buf):
|
||||
self = leg_control_data_lcmt()
|
||||
self.q = struct.unpack('>12f', buf.read(48))
|
||||
self.qd = struct.unpack('>12f', buf.read(48))
|
||||
self.p = struct.unpack('>12f', buf.read(48))
|
||||
self.v = struct.unpack('>12f', buf.read(48))
|
||||
self.tau_est = struct.unpack('>12f', buf.read(48))
|
||||
self.timestamp_us, self.id, self.robot_id = struct.unpack(">qqq", buf.read(24))
|
||||
return self
|
||||
|
||||
_decode_one = staticmethod(_decode_one)
|
||||
|
||||
def _get_hash_recursive(parents):
|
||||
if leg_control_data_lcmt in parents: return 0
|
||||
tmphash = (0xa9a928b534bfc487) & 0xffffffffffffffff
|
||||
tmphash = (((tmphash << 1) & 0xffffffffffffffff) + (tmphash >> 63)) & 0xffffffffffffffff
|
||||
return tmphash
|
||||
|
||||
_get_hash_recursive = staticmethod(_get_hash_recursive)
|
||||
_packed_fingerprint = None
|
||||
|
||||
def _get_packed_fingerprint():
|
||||
if leg_control_data_lcmt._packed_fingerprint is None:
|
||||
leg_control_data_lcmt._packed_fingerprint = struct.pack(">Q", leg_control_data_lcmt._get_hash_recursive([]))
|
||||
return leg_control_data_lcmt._packed_fingerprint
|
||||
|
||||
_get_packed_fingerprint = staticmethod(_get_packed_fingerprint)
|
||||
|
||||
def get_hash(self):
|
||||
"""Get the LCM hash of the struct"""
|
||||
return struct.unpack(">Q", leg_control_data_lcmt._get_packed_fingerprint())[0]
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
struct pd_tau_targets_lcmt
|
||||
{
|
||||
double q_des[12];
|
||||
double qd_des[12];
|
||||
double tau_ff[12];
|
||||
double kp[12];
|
||||
double kd[12];
|
||||
int64_t timestamp_us;
|
||||
int64_t id;
|
||||
int64_t robot_id;
|
||||
double se_contactState[4];
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
"""LCM type definitions
|
||||
This file automatically generated by lcm.
|
||||
DO NOT MODIFY BY HAND!!!!
|
||||
"""
|
||||
|
||||
try:
|
||||
import cStringIO.StringIO as BytesIO
|
||||
except ImportError:
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
|
||||
class pd_tau_targets_lcmt(object):
|
||||
__slots__ = ["q_des", "qd_des", "tau_ff", "kp", "kd", "timestamp_us", "id", "robot_id", "se_contactState"]
|
||||
|
||||
__typenames__ = ["double", "double", "double", "double", "double", "int64_t", "int64_t", "int64_t", "double"]
|
||||
|
||||
__dimensions__ = [[12], [12], [12], [12], [12], None, None, None, [4]]
|
||||
|
||||
def __init__(self):
|
||||
self.q_des = [0.0 for dim0 in range(12)]
|
||||
self.qd_des = [0.0 for dim0 in range(12)]
|
||||
self.tau_ff = [0.0 for dim0 in range(12)]
|
||||
self.kp = [0.0 for dim0 in range(12)]
|
||||
self.kd = [0.0 for dim0 in range(12)]
|
||||
self.timestamp_us = 0
|
||||
self.id = 0
|
||||
self.robot_id = 0
|
||||
self.se_contactState = [0.0 for dim0 in range(4)]
|
||||
|
||||
def encode(self):
|
||||
buf = BytesIO()
|
||||
buf.write(pd_tau_targets_lcmt._get_packed_fingerprint())
|
||||
self._encode_one(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
def _encode_one(self, buf):
|
||||
buf.write(struct.pack('>12d', *self.q_des[:12]))
|
||||
buf.write(struct.pack('>12d', *self.qd_des[:12]))
|
||||
buf.write(struct.pack('>12d', *self.tau_ff[:12]))
|
||||
buf.write(struct.pack('>12d', *self.kp[:12]))
|
||||
buf.write(struct.pack('>12d', *self.kd[:12]))
|
||||
buf.write(struct.pack(">qqq", self.timestamp_us, self.id, self.robot_id))
|
||||
buf.write(struct.pack('>4d', *self.se_contactState[:4]))
|
||||
|
||||
def decode(data):
|
||||
if hasattr(data, 'read'):
|
||||
buf = data
|
||||
else:
|
||||
buf = BytesIO(data)
|
||||
if buf.read(8) != pd_tau_targets_lcmt._get_packed_fingerprint():
|
||||
raise ValueError("Decode error")
|
||||
return pd_tau_targets_lcmt._decode_one(buf)
|
||||
|
||||
decode = staticmethod(decode)
|
||||
|
||||
def _decode_one(buf):
|
||||
self = pd_tau_targets_lcmt()
|
||||
self.q_des = struct.unpack('>12d', buf.read(96))
|
||||
self.qd_des = struct.unpack('>12d', buf.read(96))
|
||||
self.tau_ff = struct.unpack('>12d', buf.read(96))
|
||||
self.kp = struct.unpack('>12d', buf.read(96))
|
||||
self.kd = struct.unpack('>12d', buf.read(96))
|
||||
self.timestamp_us, self.id, self.robot_id = struct.unpack(">qqq", buf.read(24))
|
||||
self.se_contactState = struct.unpack('>4d', buf.read(32))
|
||||
return self
|
||||
|
||||
_decode_one = staticmethod(_decode_one)
|
||||
|
||||
def _get_hash_recursive(parents):
|
||||
if pd_tau_targets_lcmt in parents: return 0
|
||||
tmphash = (0x6d88128ef1291cc1) & 0xffffffffffffffff
|
||||
tmphash = (((tmphash << 1) & 0xffffffffffffffff) + (tmphash >> 63)) & 0xffffffffffffffff
|
||||
return tmphash
|
||||
|
||||
_get_hash_recursive = staticmethod(_get_hash_recursive)
|
||||
_packed_fingerprint = None
|
||||
|
||||
def _get_packed_fingerprint():
|
||||
if pd_tau_targets_lcmt._packed_fingerprint is None:
|
||||
pd_tau_targets_lcmt._packed_fingerprint = struct.pack(">Q", pd_tau_targets_lcmt._get_hash_recursive([]))
|
||||
return pd_tau_targets_lcmt._packed_fingerprint
|
||||
|
||||
_get_packed_fingerprint = staticmethod(_get_packed_fingerprint)
|
||||
|
||||
def get_hash(self):
|
||||
"""Get the LCM hash of the struct"""
|
||||
return struct.unpack(">Q", pd_tau_targets_lcmt._get_packed_fingerprint())[0]
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
struct rc_command_lcmt
|
||||
{
|
||||
int16_t mode;
|
||||
float left_stick[2];
|
||||
float right_stick[2];
|
||||
float knobs[2];
|
||||
int16_t left_upper_switch;
|
||||
int16_t left_lower_left_switch;
|
||||
int16_t left_lower_right_switch;
|
||||
int16_t right_upper_switch;
|
||||
int16_t right_lower_left_switch;
|
||||
int16_t right_lower_right_switch;
|
||||
}
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
"""LCM type definitions
|
||||
This file automatically generated by lcm.
|
||||
DO NOT MODIFY BY HAND!!!!
|
||||
"""
|
||||
|
||||
try:
|
||||
import cStringIO.StringIO as BytesIO
|
||||
except ImportError:
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
|
||||
class rc_command_lcmt(object):
|
||||
__slots__ = ["mode", "left_stick", "right_stick", "knobs", "left_upper_switch", "left_lower_left_switch",
|
||||
"left_lower_right_switch", "right_upper_switch", "right_lower_left_switch", "right_lower_right_switch"]
|
||||
|
||||
__typenames__ = ["int16_t", "float", "float", "float", "int16_t", "int16_t", "int16_t", "int16_t", "int16_t",
|
||||
"int16_t"]
|
||||
|
||||
__dimensions__ = [None, [2], [2], [2], None, None, None, None, None, None]
|
||||
|
||||
def __init__(self):
|
||||
self.mode = 0
|
||||
self.left_stick = [0.0 for dim0 in range(2)]
|
||||
self.right_stick = [0.0 for dim0 in range(2)]
|
||||
self.knobs = [0.0 for dim0 in range(2)]
|
||||
self.left_upper_switch = 0
|
||||
self.left_lower_left_switch = 0
|
||||
self.left_lower_right_switch = 0
|
||||
self.right_upper_switch = 0
|
||||
self.right_lower_left_switch = 0
|
||||
self.right_lower_right_switch = 0
|
||||
|
||||
def encode(self):
|
||||
buf = BytesIO()
|
||||
buf.write(rc_command_lcmt._get_packed_fingerprint())
|
||||
self._encode_one(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
def _encode_one(self, buf):
|
||||
buf.write(struct.pack(">h", self.mode))
|
||||
buf.write(struct.pack('>2f', *self.left_stick[:2]))
|
||||
buf.write(struct.pack('>2f', *self.right_stick[:2]))
|
||||
buf.write(struct.pack('>2f', *self.knobs[:2]))
|
||||
buf.write(
|
||||
struct.pack(">hhhhhh", self.left_upper_switch, self.left_lower_left_switch, self.left_lower_right_switch,
|
||||
self.right_upper_switch, self.right_lower_left_switch, self.right_lower_right_switch))
|
||||
|
||||
def decode(data):
|
||||
if hasattr(data, 'read'):
|
||||
buf = data
|
||||
else:
|
||||
buf = BytesIO(data)
|
||||
if buf.read(8) != rc_command_lcmt._get_packed_fingerprint():
|
||||
raise ValueError("Decode error")
|
||||
return rc_command_lcmt._decode_one(buf)
|
||||
|
||||
decode = staticmethod(decode)
|
||||
|
||||
def _decode_one(buf):
|
||||
self = rc_command_lcmt()
|
||||
self.mode = struct.unpack(">h", buf.read(2))[0]
|
||||
self.left_stick = struct.unpack('>2f', buf.read(8))
|
||||
self.right_stick = struct.unpack('>2f', buf.read(8))
|
||||
self.knobs = struct.unpack('>2f', buf.read(8))
|
||||
self.left_upper_switch, self.left_lower_left_switch, self.left_lower_right_switch, self.right_upper_switch, self.right_lower_left_switch, self.right_lower_right_switch = struct.unpack(
|
||||
">hhhhhh", buf.read(12))
|
||||
return self
|
||||
|
||||
_decode_one = staticmethod(_decode_one)
|
||||
|
||||
def _get_hash_recursive(parents):
|
||||
if rc_command_lcmt in parents: return 0
|
||||
tmphash = (0xc7726b02ec3e7de2) & 0xffffffffffffffff
|
||||
tmphash = (((tmphash << 1) & 0xffffffffffffffff) + (tmphash >> 63)) & 0xffffffffffffffff
|
||||
return tmphash
|
||||
|
||||
_get_hash_recursive = staticmethod(_get_hash_recursive)
|
||||
_packed_fingerprint = None
|
||||
|
||||
def _get_packed_fingerprint():
|
||||
if rc_command_lcmt._packed_fingerprint is None:
|
||||
rc_command_lcmt._packed_fingerprint = struct.pack(">Q", rc_command_lcmt._get_hash_recursive([]))
|
||||
return rc_command_lcmt._packed_fingerprint
|
||||
|
||||
_get_packed_fingerprint = staticmethod(_get_packed_fingerprint)
|
||||
|
||||
def get_hash(self):
|
||||
"""Get the LCM hash of the struct"""
|
||||
return struct.unpack(">Q", rc_command_lcmt._get_packed_fingerprint())[0]
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
struct state_estimator_lcmt
|
||||
{
|
||||
float p[3];
|
||||
float vWorld[3];
|
||||
float vBody[3];
|
||||
float rpy[3];
|
||||
float omegaBody[3];
|
||||
float omegaWorld[3];
|
||||
float quat[4];
|
||||
float contact_estimate[4];
|
||||
float aBody[3];
|
||||
float aWorld[3];
|
||||
int64_t timestamp_us;
|
||||
int64_t id;
|
||||
int64_t robot_id;
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
"""LCM type definitions
|
||||
This file automatically generated by lcm.
|
||||
DO NOT MODIFY BY HAND!!!!
|
||||
"""
|
||||
|
||||
try:
|
||||
import cStringIO.StringIO as BytesIO
|
||||
except ImportError:
|
||||
from io import BytesIO
|
||||
import struct
|
||||
|
||||
|
||||
class state_estimator_lcmt(object):
|
||||
__slots__ = ["p", "vWorld", "vBody", "rpy", "omegaBody", "omegaWorld", "quat", "contact_estimate", "aBody",
|
||||
"aWorld", "timestamp_us", "id", "robot_id"]
|
||||
|
||||
__typenames__ = ["float", "float", "float", "float", "float", "float", "float", "float", "float", "float",
|
||||
"int64_t", "int64_t", "int64_t"]
|
||||
|
||||
__dimensions__ = [[3], [3], [3], [3], [3], [3], [4], [4], [3], [3], None, None, None]
|
||||
|
||||
def __init__(self):
|
||||
self.p = [0.0 for dim0 in range(3)]
|
||||
self.vWorld = [0.0 for dim0 in range(3)]
|
||||
self.vBody = [0.0 for dim0 in range(3)]
|
||||
self.rpy = [0.0 for dim0 in range(3)]
|
||||
self.omegaBody = [0.0 for dim0 in range(3)]
|
||||
self.omegaWorld = [0.0 for dim0 in range(3)]
|
||||
self.quat = [0.0 for dim0 in range(4)]
|
||||
self.contact_estimate = [0.0 for dim0 in range(4)]
|
||||
self.aBody = [0.0 for dim0 in range(3)]
|
||||
self.aWorld = [0.0 for dim0 in range(3)]
|
||||
self.timestamp_us = 0
|
||||
self.id = 0
|
||||
self.robot_id = 0
|
||||
|
||||
def encode(self):
|
||||
buf = BytesIO()
|
||||
buf.write(state_estimator_lcmt._get_packed_fingerprint())
|
||||
self._encode_one(buf)
|
||||
return buf.getvalue()
|
||||
|
||||
def _encode_one(self, buf):
|
||||
buf.write(struct.pack('>3f', *self.p[:3]))
|
||||
buf.write(struct.pack('>3f', *self.vWorld[:3]))
|
||||
buf.write(struct.pack('>3f', *self.vBody[:3]))
|
||||
buf.write(struct.pack('>3f', *self.rpy[:3]))
|
||||
buf.write(struct.pack('>3f', *self.omegaBody[:3]))
|
||||
buf.write(struct.pack('>3f', *self.omegaWorld[:3]))
|
||||
buf.write(struct.pack('>4f', *self.quat[:4]))
|
||||
buf.write(struct.pack('>4f', *self.contact_estimate[:4]))
|
||||
buf.write(struct.pack('>3f', *self.aBody[:3]))
|
||||
buf.write(struct.pack('>3f', *self.aWorld[:3]))
|
||||
buf.write(struct.pack(">qqq", self.timestamp_us, self.id, self.robot_id))
|
||||
|
||||
def decode(data):
|
||||
if hasattr(data, 'read'):
|
||||
buf = data
|
||||
else:
|
||||
buf = BytesIO(data)
|
||||
if buf.read(8) != state_estimator_lcmt._get_packed_fingerprint():
|
||||
raise ValueError("Decode error")
|
||||
return state_estimator_lcmt._decode_one(buf)
|
||||
|
||||
decode = staticmethod(decode)
|
||||
|
||||
def _decode_one(buf):
|
||||
self = state_estimator_lcmt()
|
||||
self.p = struct.unpack('>3f', buf.read(12))
|
||||
self.vWorld = struct.unpack('>3f', buf.read(12))
|
||||
self.vBody = struct.unpack('>3f', buf.read(12))
|
||||
self.rpy = struct.unpack('>3f', buf.read(12))
|
||||
self.omegaBody = struct.unpack('>3f', buf.read(12))
|
||||
self.omegaWorld = struct.unpack('>3f', buf.read(12))
|
||||
self.quat = struct.unpack('>4f', buf.read(16))
|
||||
self.contact_estimate = struct.unpack('>4f', buf.read(16))
|
||||
self.aBody = struct.unpack('>3f', buf.read(12))
|
||||
self.aWorld = struct.unpack('>3f', buf.read(12))
|
||||
self.timestamp_us, self.id, self.robot_id = struct.unpack(">qqq", buf.read(24))
|
||||
return self
|
||||
|
||||
_decode_one = staticmethod(_decode_one)
|
||||
|
||||
def _get_hash_recursive(parents):
|
||||
if state_estimator_lcmt in parents: return 0
|
||||
tmphash = (0xea87c8282effe5b6) & 0xffffffffffffffff
|
||||
tmphash = (((tmphash << 1) & 0xffffffffffffffff) + (tmphash >> 63)) & 0xffffffffffffffff
|
||||
return tmphash
|
||||
|
||||
_get_hash_recursive = staticmethod(_get_hash_recursive)
|
||||
_packed_fingerprint = None
|
||||
|
||||
def _get_packed_fingerprint():
|
||||
if state_estimator_lcmt._packed_fingerprint is None:
|
||||
state_estimator_lcmt._packed_fingerprint = struct.pack(">Q", state_estimator_lcmt._get_hash_recursive([]))
|
||||
return state_estimator_lcmt._packed_fingerprint
|
||||
|
||||
_get_packed_fingerprint = staticmethod(_get_packed_fingerprint)
|
||||
|
||||
def get_hash(self):
|
||||
"""Get the LCM hash of the struct"""
|
||||
return struct.unpack(">Q", state_estimator_lcmt._get_packed_fingerprint())[0]
|
||||
@@ -0,0 +1,90 @@
|
||||
import glob
|
||||
import pickle as pkl
|
||||
import lcm
|
||||
import sys
|
||||
|
||||
from go1_gym_deploy.utils.deployment_runner import DeploymentRunner
|
||||
from go1_gym_deploy.envs.lcm_agent import LCMAgent
|
||||
from go1_gym_deploy.utils.cheetah_state_estimator import StateEstimator
|
||||
from go1_gym_deploy.utils.command_profile import *
|
||||
|
||||
import pathlib
|
||||
|
||||
lc = lcm.LCM("udpm://239.255.76.67:7667?ttl=255")
|
||||
|
||||
def load_and_run_policy(label, experiment_name, probe_policy_label=None, max_vel=1.0, max_yaw_vel=1.0, max_vel_probe=1.0):
|
||||
# load agent
|
||||
dirs = glob.glob(f"../../runs/{label}/*")
|
||||
logdir = sorted(dirs)[0]
|
||||
|
||||
with open(logdir+"/parameters.pkl", 'rb') as file:
|
||||
pkl_cfg = pkl.load(file)
|
||||
print(pkl_cfg.keys())
|
||||
cfg = pkl_cfg["Cfg"]
|
||||
print(cfg.keys())
|
||||
|
||||
|
||||
se = StateEstimator(lc)
|
||||
|
||||
control_dt = 0.02
|
||||
command_profile = RCControllerProfile(dt=control_dt, state_estimator=se, x_scale=max_vel, y_scale=0.6, yaw_scale=max_yaw_vel, probe_vel_multiplier=(max_vel_probe / max_vel))
|
||||
|
||||
hardware_agent = LCMAgent(cfg, se, command_profile)
|
||||
se.spin()
|
||||
|
||||
from go1_gym_deploy.envs.history_wrapper import HistoryWrapper
|
||||
hardware_agent = HistoryWrapper(hardware_agent)
|
||||
|
||||
policy = load_policy(logdir)
|
||||
|
||||
if probe_policy_label is not None:
|
||||
# load agent
|
||||
dirs = glob.glob(f"../runs/{probe_policy_label}_*")
|
||||
probe_policy_logdir = sorted(dirs)[0]
|
||||
with open(probe_policy_logdir + "/parameters.pkl", 'rb') as file:
|
||||
probe_cfg = pkl.load(file)
|
||||
probe_cfg = probe_cfg["Cfg"]
|
||||
probe_policy = load_policy(probe_policy_logdir)
|
||||
|
||||
# load runner
|
||||
root = f"{pathlib.Path(__file__).parent.resolve()}/../../logs/"
|
||||
pathlib.Path(root).mkdir(parents=True, exist_ok=True)
|
||||
deployment_runner = DeploymentRunner(experiment_name=experiment_name, se=None,
|
||||
log_root=f"{root}/{experiment_name}")
|
||||
deployment_runner.add_control_agent(hardware_agent, "hardware_closed_loop")
|
||||
deployment_runner.add_policy(policy)
|
||||
if probe_policy_label is not None:
|
||||
deployment_runner.add_probe_policy(probe_policy, probe_cfg)
|
||||
deployment_runner.add_command_profile(command_profile)
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
max_steps = int(sys.argv[1])
|
||||
else:
|
||||
max_steps = 10000000
|
||||
print(f'max steps {max_steps}')
|
||||
|
||||
deployment_runner.run(max_steps=max_steps, logging=True)
|
||||
|
||||
def load_policy(logdir):
|
||||
body = torch.jit.load(logdir + '/checkpoints/body_latest.jit')
|
||||
import os
|
||||
adaptation_module = torch.jit.load(logdir + '/checkpoints/adaptation_module_latest.jit')
|
||||
|
||||
def policy(obs, info):
|
||||
i = 0
|
||||
latent = adaptation_module.forward(obs["obs_history"].to('cpu'))
|
||||
action = body.forward(torch.cat((obs["obs_history"].to('cpu'), latent), dim=-1))
|
||||
info['latent'] = latent
|
||||
return action
|
||||
|
||||
return policy
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
label = "gait-conditioned-agility/pretrain-v0/train"
|
||||
|
||||
probe_policy_label = None
|
||||
|
||||
experiment_name = "example_experiment"
|
||||
|
||||
load_and_run_policy(label, experiment_name=experiment_name, probe_policy_label=probe_policy_label, max_vel=3.0, max_yaw_vel=5.0, max_vel_probe=1.0)
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# download docker image if it doesn't exist yet
|
||||
wget --directory-prefix=../docker -nc --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=1XkVpyYyYqQQ4FcgLIDUxg-GR1WI89-XC' -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=1XkVpyYyYqQQ4FcgLIDUxg-GR1WI89-XC" -O deployment_image.tar && rm -rf /tmp/cookies.txt
|
||||
|
||||
#rsync -av -e ssh --exclude=*.pt --exclude=*.mp4 $PWD/../../go1_gym_deploy $PWD/../../runs $PWD/../../setup.py pi@192.168.12.1:/home/pi/go1_gym
|
||||
rsync -av -e ssh --exclude=*.pt --exclude=*.mp4 $PWD/../../go1_gym_deploy $PWD/../../runs $PWD/../../setup.py unitree@192.168.123.15:/home/unitree/go1_gym
|
||||
#scp -r $PWD/../../runs pi@192.168.12.1:/home/pi/go1_gym
|
||||
#scp -r $PWD/../../setup.py pi@192.168.12.1:/home/pi/go1_gym
|
||||
@@ -0,0 +1,154 @@
|
||||
import lcm
|
||||
import threading
|
||||
import time
|
||||
import select
|
||||
|
||||
import numpy as np
|
||||
|
||||
from go1_gym_deploy.lcm_types.leg_control_data_lcmt import leg_control_data_lcmt
|
||||
from go1_gym_deploy.lcm_types.rc_command_lcmt import rc_command_lcmt
|
||||
from go1_gym_deploy.lcm_types.state_estimator_lcmt import state_estimator_lcmt
|
||||
from go1_gym_deploy.lcm_types.vicon_pose_lcmt import vicon_pose_lcmt
|
||||
from go1_gym_deploy.lcm_types.camera_message_lcmt import camera_message_lcmt
|
||||
from go1_gym_deploy.lcm_types.camera_message_rect_wide import camera_message_rect_wide
|
||||
from go1_gym_deploy.lcm_types.camera_message_rect_wide_mask import camera_message_rect_wide_mask
|
||||
|
||||
|
||||
class UnitreeLCMInspector:
|
||||
def __init__(self, lc):
|
||||
self.lc = lc
|
||||
|
||||
self.camera_names = ["front", "bottom", "left", "right", "rear"]
|
||||
for cam_name in self.camera_names:
|
||||
self.camera_subscription = self.lc.subscribe(f"rect_image_{cam_name}", self._rect_camera_cb)
|
||||
for cam_name in self.camera_names:
|
||||
self.camera_subscription = self.lc.subscribe(f"rect_image_{cam_name}_mask", self._mask_camera_cb)
|
||||
|
||||
self.camera_image_left = None
|
||||
self.camera_image_right = None
|
||||
self.camera_image_front = None
|
||||
self.camera_image_bottom = None
|
||||
self.camera_image_rear = None
|
||||
|
||||
self.ts = [time.time(), time.time(), time.time(), time.time(), time.time(),]
|
||||
|
||||
self.num_low_states = 0
|
||||
|
||||
def _rect_camera_cb(self, channel, data):
|
||||
|
||||
# message_types = [camera_message_rect_front, camera_message_rect_front_chin, camera_message_rect_left,
|
||||
# camera_message_rect_right, camera_message_rect_rear_down]
|
||||
# image_shapes = [(200, 200, 3), (100, 100, 3), (100, 232, 3), (100, 232, 3), (200, 200, 3)]
|
||||
|
||||
message_types = [camera_message_rect_wide, camera_message_rect_wide, camera_message_rect_wide, camera_message_rect_wide, camera_message_rect_wide]
|
||||
image_shapes = [(116, 100, 3), (116, 100, 3), (116, 100, 3), (116, 100, 3), (116, 100, 3)]
|
||||
|
||||
cam_name = channel.split("_")[-1]
|
||||
|
||||
cam_id = self.camera_names.index(cam_name) + 1
|
||||
|
||||
print(channel, message_types[cam_id - 1])
|
||||
msg = message_types[cam_id - 1].decode(data)
|
||||
|
||||
img = np.fromstring(msg.data, dtype=np.uint8)
|
||||
img = np.flip(np.flip(
|
||||
img.reshape((image_shapes[cam_id - 1][2], image_shapes[cam_id - 1][1], image_shapes[cam_id - 1][0])),
|
||||
axis=0), axis=1).transpose(1, 2, 0)
|
||||
|
||||
if cam_id == 1:
|
||||
self.camera_image_front = img
|
||||
elif cam_id == 2:
|
||||
self.camera_image_bottom = img
|
||||
elif cam_id == 3:
|
||||
self.camera_image_left = img
|
||||
elif cam_id == 4:
|
||||
self.camera_image_right = img
|
||||
elif cam_id == 5:
|
||||
self.camera_image_rear = img
|
||||
else:
|
||||
print("Image received from camera with unknown ID#!")
|
||||
|
||||
print(f"f{1. / (time.time() - self.ts[cam_id - 1])}: received py from {cam_name}!")
|
||||
self.ts[cam_id-1] = time.time()
|
||||
|
||||
from PIL import Image
|
||||
im = Image.fromarray(img)
|
||||
im.save(f"{cam_name}_image.jpeg")
|
||||
|
||||
def _mask_camera_cb(self, channel, data):
|
||||
|
||||
message_types = [camera_message_rect_wide_mask for i in range(5)]
|
||||
image_shapes = [(116, 100, 1) for i in range(5)]
|
||||
|
||||
cam_name = channel.split("_")[-2]
|
||||
|
||||
cam_id = self.camera_names.index(cam_name) + 1
|
||||
|
||||
print(channel, message_types[cam_id - 1])
|
||||
msg = message_types[cam_id - 1].decode(data)
|
||||
|
||||
img = np.array(list(msg.data)).astype(np.uint8)
|
||||
img = np.flip(np.flip(
|
||||
img.reshape((image_shapes[cam_id - 1][2], image_shapes[cam_id - 1][1], image_shapes[cam_id - 1][0])),
|
||||
axis=0), axis=1)
|
||||
|
||||
if cam_id == 1:
|
||||
self.camera_image_front = img
|
||||
elif cam_id == 2:
|
||||
self.camera_image_bottom = img
|
||||
elif cam_id == 3:
|
||||
self.camera_image_left = img
|
||||
elif cam_id == 4:
|
||||
self.camera_image_right = img
|
||||
elif cam_id == 5:
|
||||
self.camera_image_rear = img
|
||||
else:
|
||||
print("Image received from camera with unknown ID#!")
|
||||
|
||||
print(f"f{1. / (time.time() - self.ts[cam_id - 1])}: received py from {cam_name}!")
|
||||
self.ts[cam_id-1] = time.time()
|
||||
|
||||
from PIL import Image
|
||||
# print(img[0])
|
||||
im = Image.fromarray(img[0])
|
||||
im.save(f"{cam_name}_mask_image.jpeg")
|
||||
|
||||
def publish_30Hz(self):
|
||||
msg = camera_message_rect_wide()
|
||||
msg.data = [0] * 34800
|
||||
self.lc.publish("rect_image_rear", msg.encode())
|
||||
time.sleep(1./30.)
|
||||
|
||||
def poll(self, cb=None):
|
||||
t = time.time()
|
||||
try:
|
||||
while True:
|
||||
timeout = 0.01
|
||||
rfds, wfds, efds = select.select([self.lc.fileno()], [], [], timeout)
|
||||
if rfds:
|
||||
# print("message received!")
|
||||
self.lc.handle()
|
||||
# print(f'Freq {1. / (time.time() - t)} Hz'); t = time.time()
|
||||
else:
|
||||
continue
|
||||
# print(f'waiting for message... Freq {1. / (time.time() - t)} Hz'); t = time.time()
|
||||
# if cb is not None:
|
||||
# cb()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
def spin(self):
|
||||
self.run_thread = threading.Thread(target=self.poll, daemon=False)
|
||||
self.run_thread.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import lcm
|
||||
|
||||
lc = lcm.LCM("udpm://239.255.76.67:7667?ttl=255")
|
||||
#check_lcm_msgs()
|
||||
print("init")
|
||||
insp = UnitreeLCMInspector(lc)
|
||||
print("polling")
|
||||
# insp.poll()
|
||||
while True:
|
||||
insp.publish_30Hz()
|
||||
BIN
Binary file not shown.
+451
@@ -0,0 +1,451 @@
|
||||
import math
|
||||
import select
|
||||
import threading
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
from go1_gym_deploy.lcm_types.leg_control_data_lcmt import leg_control_data_lcmt
|
||||
from go1_gym_deploy.lcm_types.rc_command_lcmt import rc_command_lcmt
|
||||
from go1_gym_deploy.lcm_types.state_estimator_lcmt import state_estimator_lcmt
|
||||
from go1_gym_deploy.lcm_types.camera_message_lcmt import camera_message_lcmt
|
||||
from go1_gym_deploy.lcm_types.camera_message_rect_wide import camera_message_rect_wide
|
||||
|
||||
|
||||
def get_rpy_from_quaternion(q):
|
||||
w, x, y, z = q
|
||||
r = np.arctan2(2 * (w * x + y * z), 1 - 2 * (x ** 2 + y ** 2))
|
||||
p = np.arcsin(2 * (w * y - z * x))
|
||||
y = np.arctan2(2 * (w * z + x * y), 1 - 2 * (y ** 2 + z ** 2))
|
||||
return np.array([r, p, y])
|
||||
|
||||
|
||||
def get_rotation_matrix_from_rpy(rpy):
|
||||
"""
|
||||
Get rotation matrix from the given quaternion.
|
||||
Args:
|
||||
q (np.array[float[4]]): quaternion [w,x,y,z]
|
||||
Returns:
|
||||
np.array[float[3,3]]: rotation matrix.
|
||||
"""
|
||||
r, p, y = rpy
|
||||
R_x = np.array([[1, 0, 0],
|
||||
[0, math.cos(r), -math.sin(r)],
|
||||
[0, math.sin(r), math.cos(r)]
|
||||
])
|
||||
|
||||
R_y = np.array([[math.cos(p), 0, math.sin(p)],
|
||||
[0, 1, 0],
|
||||
[-math.sin(p), 0, math.cos(p)]
|
||||
])
|
||||
|
||||
R_z = np.array([[math.cos(y), -math.sin(y), 0],
|
||||
[math.sin(y), math.cos(y), 0],
|
||||
[0, 0, 1]
|
||||
])
|
||||
|
||||
rot = np.dot(R_z, np.dot(R_y, R_x))
|
||||
return rot
|
||||
|
||||
|
||||
class StateEstimator:
|
||||
def __init__(self, lc, use_cameras=True):
|
||||
|
||||
# reverse legs
|
||||
self.joint_idxs = [3, 4, 5, 0, 1, 2, 9, 10, 11, 6, 7, 8]
|
||||
self.contact_idxs = [1, 0, 3, 2]
|
||||
# self.joint_idxs = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
self.lc = lc
|
||||
|
||||
self.joint_pos = np.zeros(12)
|
||||
self.joint_vel = np.zeros(12)
|
||||
self.tau_est = np.zeros(12)
|
||||
self.world_lin_vel = np.zeros(3)
|
||||
self.world_ang_vel = np.zeros(3)
|
||||
self.euler = np.zeros(3)
|
||||
self.R = np.eye(3)
|
||||
self.buf_idx = 0
|
||||
|
||||
self.smoothing_length = 12
|
||||
self.deuler_history = np.zeros((self.smoothing_length, 3))
|
||||
self.dt_history = np.zeros((self.smoothing_length, 1))
|
||||
self.euler_prev = np.zeros(3)
|
||||
self.timuprev = time.time()
|
||||
|
||||
self.body_lin_vel = np.zeros(3)
|
||||
self.body_ang_vel = np.zeros(3)
|
||||
self.smoothing_ratio = 0.2
|
||||
|
||||
self.contact_state = np.ones(4)
|
||||
|
||||
self.mode = 0
|
||||
self.ctrlmode = 0
|
||||
self.left_stick = [0, 0]
|
||||
self.right_stick = [0, 0]
|
||||
self.left_upper_switch = 0
|
||||
self.left_lower_left_switch = 0
|
||||
self.left_lower_right_switch = 0
|
||||
self.right_upper_switch = 0
|
||||
self.right_lower_left_switch = 0
|
||||
self.right_lower_right_switch = 0
|
||||
self.left_upper_switch_pressed = 0
|
||||
self.left_lower_left_switch_pressed = 0
|
||||
self.left_lower_right_switch_pressed = 0
|
||||
self.right_upper_switch_pressed = 0
|
||||
self.right_lower_left_switch_pressed = 0
|
||||
self.right_lower_right_switch_pressed = 0
|
||||
|
||||
# default trotting gait
|
||||
self.cmd_freq = 3.0
|
||||
self.cmd_phase = 0.5
|
||||
self.cmd_offset = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
|
||||
|
||||
self.init_time = time.time()
|
||||
self.received_first_legdata = False
|
||||
|
||||
self.imu_subscription = self.lc.subscribe("state_estimator_data", self._imu_cb)
|
||||
self.legdata_state_subscription = self.lc.subscribe("leg_control_data", self._legdata_cb)
|
||||
self.rc_command_subscription = self.lc.subscribe("rc_command", self._rc_command_cb)
|
||||
|
||||
if use_cameras:
|
||||
for cam_id in [1, 2, 3, 4, 5]:
|
||||
self.camera_subscription = self.lc.subscribe(f"camera{cam_id}", self._camera_cb)
|
||||
self.camera_names = ["front", "bottom", "left", "right", "rear"]
|
||||
for cam_name in self.camera_names:
|
||||
self.camera_subscription = self.lc.subscribe(f"rect_image_{cam_name}", self._rect_camera_cb)
|
||||
self.camera_image_left = None
|
||||
self.camera_image_right = None
|
||||
self.camera_image_front = None
|
||||
self.camera_image_bottom = None
|
||||
self.camera_image_rear = None
|
||||
|
||||
self.body_loc = np.array([0, 0, 0])
|
||||
self.body_quat = np.array([0, 0, 0, 1])
|
||||
|
||||
def get_body_linear_vel(self):
|
||||
self.body_lin_vel = np.dot(self.R.T, self.world_lin_vel)
|
||||
return self.body_lin_vel
|
||||
|
||||
def get_body_angular_vel(self):
|
||||
self.body_ang_vel = self.smoothing_ratio * np.mean(self.deuler_history / self.dt_history, axis=0) + (
|
||||
1 - self.smoothing_ratio) * self.body_ang_vel
|
||||
return self.body_ang_vel
|
||||
|
||||
def get_gravity_vector(self):
|
||||
grav = np.dot(self.R.T, np.array([0, 0, -1]))
|
||||
return grav
|
||||
|
||||
def get_contact_state(self):
|
||||
return self.contact_state[self.contact_idxs]
|
||||
|
||||
def get_rpy(self):
|
||||
return self.euler
|
||||
|
||||
def get_command(self):
|
||||
MODES = ["mobility", "body_control", "footswing"]
|
||||
|
||||
if self.left_upper_switch_pressed:
|
||||
self.ctrlmode = (self.ctrlmode + 1) % 3
|
||||
self.left_upper_switch_pressed = False
|
||||
print(f"PRESSED{self.ctrlmode}")
|
||||
# elif self.left_lower_left_switch_pressed:
|
||||
# self.ctrlmode = 0
|
||||
# self.left_lower_left_switch_pressed = False
|
||||
# print("PRESSED0")
|
||||
|
||||
MODE = MODES[self.ctrlmode]
|
||||
|
||||
if MODE == "mobility":
|
||||
cmd_x = 1 * self.left_stick[1]
|
||||
cmd_y = 0 # -1 * self.left_stick[0]
|
||||
cmd_yaw = -1 * self.right_stick[0]
|
||||
# cmd_height = 0.3 * self.right_stick[1]
|
||||
cmd_height = 0.3 * self.left_stick[0]
|
||||
cmd_footswing = 0.08 # max(0, self.left_stick[0]) * 0.27 + 0.03
|
||||
|
||||
cmd_stance_width = 0.33
|
||||
cmd_stance_length = 0.45
|
||||
cmd_ori_pitch = 0.15
|
||||
cmd_ori_roll = 0
|
||||
|
||||
min_freq = 2.0
|
||||
max_freq = 4.0
|
||||
cmd_freq = (1 + self.right_stick[1]) / 2 * (max_freq - min_freq) + min_freq
|
||||
elif MODE == "body_control":
|
||||
cmd_x = 1 * self.left_stick[1]
|
||||
cmd_y = 0 # -1 * self.left_stick[0]
|
||||
cmd_yaw = -1 * self.right_stick[0]
|
||||
cmd_height = 0.0
|
||||
cmd_footswing = 0.10 # max(0, self.left_stick[0]) * 0.27 + 0.03
|
||||
|
||||
cmd_stance_width = 0.275 + 0.175 * self.left_stick[0]
|
||||
cmd_stance_length = 0.45 # 0.45 + 0.25 * self.left_stick[1]
|
||||
cmd_ori_pitch = -0.4 * self.right_stick[1]
|
||||
cmd_ori_roll = 0.0 # 0.5 * self.right_stick[1]
|
||||
cmd_freq = 3.2
|
||||
else:
|
||||
cmd_x = 1 * self.left_stick[1]
|
||||
cmd_y = 0 # -1 * self.left_stick[0]
|
||||
cmd_yaw = -1 * self.right_stick[0]
|
||||
cmd_height = 0.0
|
||||
cmd_footswing = max(0, self.left_stick[0]) * 0.32 + 0.03
|
||||
# cmd_footswing = max(0, self.left_stick[0]) * 0.29 + 0.03
|
||||
|
||||
cmd_stance_width = 0.3 # + 0.175 * self.left_stick[0]
|
||||
cmd_stance_length = 0.40 # + 0.15 * self.left_stick[1]
|
||||
cmd_ori_pitch = 0.0 # -0.3 * self.left_stick[0]
|
||||
cmd_ori_roll = 0.0 # 0.5 * self.right_stick[1]
|
||||
|
||||
min_freq = 2.0
|
||||
max_freq = 4.0
|
||||
cmd_freq = (1 + self.right_stick[1]) / 2 * (max_freq - min_freq) + min_freq
|
||||
|
||||
# cmd_freq = (1 + self.left_stick[0]) / 2 * (max_freq-min_freq) + min_freq
|
||||
|
||||
# if self.left_upper_switch_pressed:
|
||||
# if self.cmd_phase == 0.5:
|
||||
# self.cmd_phase = 0.0
|
||||
# elif self.cmd_phase == 0.0:
|
||||
# self.cmd_phase = 0.25
|
||||
# else:
|
||||
# self.cmd_phase = 0.5
|
||||
# self.left_upper_switch_pressed = False
|
||||
if self.mode == 0:
|
||||
self.cmd_phase = 0.5
|
||||
self.cmd_offset = 0.0
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 1:
|
||||
self.cmd_phase = 0.0
|
||||
self.cmd_offset = 0.0
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 2:
|
||||
self.cmd_phase = 0.0
|
||||
self.cmd_offset = 0.5
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 3:
|
||||
self.cmd_phase = 0.0
|
||||
self.cmd_offset = 0.0
|
||||
self.cmd_bound = 0.5
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 4:
|
||||
self.cmd_phase = 0.7
|
||||
self.cmd_offset = 0.0
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 5:
|
||||
self.cmd_phase = 0.3
|
||||
self.cmd_offset = 0.0
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 6:
|
||||
self.cmd_phase = 0.0
|
||||
self.cmd_offset = 0.7
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
elif self.mode == 7:
|
||||
self.cmd_phase = 0.0
|
||||
self.cmd_offset = 0.3
|
||||
self.cmd_bound = 0.0
|
||||
self.cmd_duration = 0.5
|
||||
|
||||
# cmd_freq = (3.0 if not self.left_upper_switch else 2.0)
|
||||
# cmd_phase = (0.5 if not self.left_lower_left_switch else 0.0)
|
||||
# cmd_offset = 0.0
|
||||
# cmd_duration = 0.5
|
||||
|
||||
# print(cmd_x, cmd_y)
|
||||
|
||||
return np.array([cmd_x, cmd_y, cmd_yaw, cmd_height, cmd_freq, self.cmd_phase, self.cmd_offset, self.cmd_bound,
|
||||
self.cmd_duration, cmd_footswing, cmd_ori_pitch, cmd_ori_roll, cmd_stance_width,
|
||||
cmd_stance_length, 0, 0, 0, 0, 0])
|
||||
|
||||
def get_buttons(self):
|
||||
return np.array([self.left_lower_left_switch, self.left_upper_switch, self.right_lower_right_switch, self.right_upper_switch])
|
||||
|
||||
def get_dof_pos(self):
|
||||
# print("dofposquery", self.joint_pos[self.joint_idxs])
|
||||
return self.joint_pos[self.joint_idxs]
|
||||
|
||||
def get_dof_vel(self):
|
||||
return self.joint_vel[self.joint_idxs]
|
||||
|
||||
def get_tau_est(self):
|
||||
return self.tau_est[self.joint_idxs]
|
||||
|
||||
def get_yaw(self):
|
||||
return self.euler[2]
|
||||
|
||||
def get_body_loc(self):
|
||||
return np.array(self.body_loc)
|
||||
|
||||
def get_body_quat(self):
|
||||
return np.array(self.body_quat)
|
||||
|
||||
def get_camera_front(self):
|
||||
return self.camera_image_front
|
||||
|
||||
def get_camera_bottom(self):
|
||||
return self.camera_image_bottom
|
||||
|
||||
def get_camera_rear(self):
|
||||
return self.camera_image_rear
|
||||
|
||||
def get_camera_left(self):
|
||||
return self.camera_image_left
|
||||
|
||||
def get_camera_right(self):
|
||||
return self.camera_image_right
|
||||
|
||||
def _legdata_cb(self, channel, data):
|
||||
# print("update legdata")
|
||||
if not self.received_first_legdata:
|
||||
self.received_first_legdata = True
|
||||
print(f"First legdata: {time.time() - self.init_time}")
|
||||
|
||||
msg = leg_control_data_lcmt.decode(data)
|
||||
# print(msg.q)
|
||||
self.joint_pos = np.array(msg.q)
|
||||
self.joint_vel = np.array(msg.qd)
|
||||
self.tau_est = np.array(msg.tau_est)
|
||||
# print(f"update legdata {msg.id}")
|
||||
|
||||
def _imu_cb(self, channel, data):
|
||||
# print("update imu")
|
||||
msg = state_estimator_lcmt.decode(data)
|
||||
|
||||
self.euler = np.array(msg.rpy)
|
||||
|
||||
self.R = get_rotation_matrix_from_rpy(self.euler)
|
||||
|
||||
self.contact_state = 1.0 * (np.array(msg.contact_estimate) > 200)
|
||||
|
||||
self.deuler_history[self.buf_idx % self.smoothing_length, :] = msg.rpy - self.euler_prev
|
||||
self.dt_history[self.buf_idx % self.smoothing_length] = time.time() - self.timuprev
|
||||
|
||||
self.timuprev = time.time()
|
||||
|
||||
self.buf_idx += 1
|
||||
self.euler_prev = np.array(msg.rpy)
|
||||
|
||||
def _sensor_cb(self, channel, data):
|
||||
pass
|
||||
|
||||
def _rc_command_cb(self, channel, data):
|
||||
|
||||
msg = rc_command_lcmt.decode(data)
|
||||
|
||||
|
||||
self.left_upper_switch_pressed = ((msg.left_upper_switch and not self.left_upper_switch) or self.left_upper_switch_pressed)
|
||||
self.left_lower_left_switch_pressed = ((msg.left_lower_left_switch and not self.left_lower_left_switch) or self.left_lower_left_switch_pressed)
|
||||
self.left_lower_right_switch_pressed = ((msg.left_lower_right_switch and not self.left_lower_right_switch) or self.left_lower_right_switch_pressed)
|
||||
self.right_upper_switch_pressed = ((msg.right_upper_switch and not self.right_upper_switch) or self.right_upper_switch_pressed)
|
||||
self.right_lower_left_switch_pressed = ((msg.right_lower_left_switch and not self.right_lower_left_switch) or self.right_lower_left_switch_pressed)
|
||||
self.right_lower_right_switch_pressed = ((msg.right_lower_right_switch and not self.right_lower_right_switch) or self.right_lower_right_switch_pressed)
|
||||
|
||||
self.mode = msg.mode
|
||||
self.right_stick = msg.right_stick
|
||||
self.left_stick = msg.left_stick
|
||||
self.left_upper_switch = msg.left_upper_switch
|
||||
self.left_lower_left_switch = msg.left_lower_left_switch
|
||||
self.left_lower_right_switch = msg.left_lower_right_switch
|
||||
self.right_upper_switch = msg.right_upper_switch
|
||||
self.right_lower_left_switch = msg.right_lower_left_switch
|
||||
self.right_lower_right_switch = msg.right_lower_right_switch
|
||||
|
||||
# print(self.right_stick, self.left_stick)
|
||||
|
||||
def _camera_cb(self, channel, data):
|
||||
msg = camera_message_lcmt.decode(data)
|
||||
|
||||
img = np.fromstring(msg.data, dtype=np.uint8)
|
||||
img = img.reshape((3, 200, 464)).transpose(1, 2, 0)
|
||||
|
||||
cam_id = int(channel[-1])
|
||||
if cam_id == 1:
|
||||
self.camera_image_front = img
|
||||
elif cam_id == 2:
|
||||
self.camera_image_bottom = img
|
||||
elif cam_id == 3:
|
||||
self.camera_image_left = img
|
||||
elif cam_id == 4:
|
||||
self.camera_image_right = img
|
||||
elif cam_id == 5:
|
||||
self.camera_image_rear = img
|
||||
else:
|
||||
print("Image received from camera with unknown ID#!")
|
||||
|
||||
#im = Image.fromarray(img).convert('RGB')
|
||||
|
||||
#im.save("test_image_" + channel + ".jpg")
|
||||
#print(channel)
|
||||
|
||||
def _rect_camera_cb(self, channel, data):
|
||||
message_types = [camera_message_rect_wide, camera_message_rect_wide, camera_message_rect_wide,
|
||||
camera_message_rect_wide, camera_message_rect_wide]
|
||||
image_shapes = [(116, 100, 3), (116, 100, 3), (116, 100, 3), (116, 100, 3), (116, 100, 3)]
|
||||
|
||||
cam_name = channel.split("_")[-1]
|
||||
# print(f"received py from {cam_name}")
|
||||
cam_id = self.camera_names.index(cam_name) + 1
|
||||
|
||||
msg = message_types[cam_id - 1].decode(data)
|
||||
|
||||
img = np.fromstring(msg.data, dtype=np.uint8)
|
||||
img = np.flip(np.flip(
|
||||
img.reshape((image_shapes[cam_id - 1][2], image_shapes[cam_id - 1][1], image_shapes[cam_id - 1][0])),
|
||||
axis=0), axis=1).transpose(1, 2, 0)
|
||||
# print(img.shape)
|
||||
# img = np.flip(np.flip(img.reshape(image_shapes[cam_id - 1]), axis=0), axis=1)[:, :,
|
||||
# [2, 1, 0]] # .transpose(1, 2, 0)
|
||||
|
||||
if cam_id == 1:
|
||||
self.camera_image_front = img
|
||||
elif cam_id == 2:
|
||||
self.camera_image_bottom = img
|
||||
elif cam_id == 3:
|
||||
self.camera_image_left = img
|
||||
elif cam_id == 4:
|
||||
self.camera_image_right = img
|
||||
elif cam_id == 5:
|
||||
self.camera_image_rear = img
|
||||
else:
|
||||
print("Image received from camera with unknown ID#!")
|
||||
|
||||
def poll(self, cb=None):
|
||||
t = time.time()
|
||||
try:
|
||||
while True:
|
||||
timeout = 0.01
|
||||
rfds, wfds, efds = select.select([self.lc.fileno()], [], [], timeout)
|
||||
if rfds:
|
||||
# print("message received!")
|
||||
self.lc.handle()
|
||||
# print(f'Freq {1. / (time.time() - t)} Hz'); t = time.time()
|
||||
else:
|
||||
continue
|
||||
# print(f'waiting for message... Freq {1. / (time.time() - t)} Hz'); t = time.time()
|
||||
# if cb is not None:
|
||||
# cb()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
def spin(self):
|
||||
self.run_thread = threading.Thread(target=self.poll, daemon=False)
|
||||
self.run_thread.start()
|
||||
|
||||
def close(self):
|
||||
self.lc.unsubscribe(self.legdata_state_subscription)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import lcm
|
||||
|
||||
lc = lcm.LCM("udpm://239.255.76.67:7667?ttl=255")
|
||||
se = StateEstimator(lc)
|
||||
se.poll()
|
||||
@@ -0,0 +1,231 @@
|
||||
import torch
|
||||
|
||||
|
||||
class CommandProfile:
|
||||
def __init__(self, dt, max_time_s=10.):
|
||||
self.dt = dt
|
||||
self.max_timestep = int(max_time_s / self.dt)
|
||||
self.commands = torch.zeros((self.max_timestep, 9))
|
||||
self.start_time = 0
|
||||
|
||||
def get_command(self, t):
|
||||
timestep = int((t - self.start_time) / self.dt)
|
||||
timestep = min(timestep, self.max_timestep - 1)
|
||||
return self.commands[timestep, :]
|
||||
|
||||
def get_buttons(self):
|
||||
return [0, 0, 0, 0]
|
||||
|
||||
def reset(self, reset_time):
|
||||
self.start_time = reset_time
|
||||
|
||||
|
||||
class ConstantAccelerationProfile(CommandProfile):
|
||||
def __init__(self, dt, max_speed, accel_time, zero_buf_time=0):
|
||||
super().__init__(dt)
|
||||
zero_buf_timesteps = int(zero_buf_time / self.dt)
|
||||
accel_timesteps = int(accel_time / self.dt)
|
||||
self.commands[:zero_buf_timesteps] = 0
|
||||
self.commands[zero_buf_timesteps:zero_buf_timesteps + accel_timesteps, 0] = torch.arange(0, max_speed,
|
||||
step=max_speed / accel_timesteps)
|
||||
self.commands[zero_buf_timesteps + accel_timesteps:, 0] = max_speed
|
||||
|
||||
|
||||
class ElegantForwardProfile(CommandProfile):
|
||||
def __init__(self, dt, max_speed, accel_time, duration, deaccel_time, zero_buf_time=0):
|
||||
import numpy as np
|
||||
|
||||
zero_buf_timesteps = int(zero_buf_time / dt)
|
||||
accel_timesteps = int(accel_time / dt)
|
||||
duration_timesteps = int(duration / dt)
|
||||
deaccel_timesteps = int(deaccel_time / dt)
|
||||
|
||||
total_time_s = zero_buf_time + accel_time + duration + deaccel_time
|
||||
|
||||
super().__init__(dt, total_time_s)
|
||||
|
||||
x_vel_cmds = [0] * zero_buf_timesteps + [*np.linspace(0, max_speed, accel_timesteps)] + \
|
||||
[max_speed] * duration_timesteps + [*np.linspace(max_speed, 0, deaccel_timesteps)]
|
||||
|
||||
self.commands[:len(x_vel_cmds), 0] = torch.Tensor(x_vel_cmds)
|
||||
|
||||
|
||||
class ElegantYawProfile(CommandProfile):
|
||||
def __init__(self, dt, max_speed, zero_buf_time, accel_time, duration, deaccel_time, yaw_rate):
|
||||
import numpy as np
|
||||
|
||||
zero_buf_timesteps = int(zero_buf_time / dt)
|
||||
accel_timesteps = int(accel_time / dt)
|
||||
duration_timesteps = int(duration / dt)
|
||||
deaccel_timesteps = int(deaccel_time / dt)
|
||||
|
||||
total_time_s = zero_buf_time + accel_time + duration + deaccel_time
|
||||
|
||||
super().__init__(dt, total_time_s)
|
||||
|
||||
x_vel_cmds = [0] * zero_buf_timesteps + [*np.linspace(0, max_speed, accel_timesteps)] + \
|
||||
[max_speed] * duration_timesteps + [*np.linspace(max_speed, 0, deaccel_timesteps)]
|
||||
|
||||
yaw_vel_cmds = [0] * zero_buf_timesteps + [0] * accel_timesteps + \
|
||||
[yaw_rate] * duration_timesteps + [0] * deaccel_timesteps
|
||||
|
||||
self.commands[:len(x_vel_cmds), 0] = torch.Tensor(x_vel_cmds)
|
||||
self.commands[:len(yaw_vel_cmds), 2] = torch.Tensor(yaw_vel_cmds)
|
||||
|
||||
|
||||
class ElegantGaitProfile(CommandProfile):
|
||||
def __init__(self, dt, filename):
|
||||
import numpy as np
|
||||
import json
|
||||
|
||||
with open(f'../command_profiles/{filename}', 'r') as file:
|
||||
command_sequence = json.load(file)
|
||||
|
||||
len_command_sequence = len(command_sequence["x_vel_cmd"])
|
||||
total_time_s = int(len_command_sequence / dt)
|
||||
|
||||
super().__init__(dt, total_time_s)
|
||||
|
||||
self.commands[:len_command_sequence, 0] = torch.Tensor(command_sequence["x_vel_cmd"])
|
||||
self.commands[:len_command_sequence, 2] = torch.Tensor(command_sequence["yaw_vel_cmd"])
|
||||
self.commands[:len_command_sequence, 3] = torch.Tensor(command_sequence["height_cmd"])
|
||||
self.commands[:len_command_sequence, 4] = torch.Tensor(command_sequence["frequency_cmd"])
|
||||
self.commands[:len_command_sequence, 5] = torch.Tensor(command_sequence["offset_cmd"])
|
||||
self.commands[:len_command_sequence, 6] = torch.Tensor(command_sequence["phase_cmd"])
|
||||
self.commands[:len_command_sequence, 7] = torch.Tensor(command_sequence["bound_cmd"])
|
||||
self.commands[:len_command_sequence, 8] = torch.Tensor(command_sequence["duration_cmd"])
|
||||
|
||||
class RCControllerProfile(CommandProfile):
|
||||
def __init__(self, dt, state_estimator, x_scale=1.0, y_scale=1.0, yaw_scale=1.0, probe_vel_multiplier=1.0):
|
||||
super().__init__(dt)
|
||||
self.state_estimator = state_estimator
|
||||
self.x_scale = x_scale
|
||||
self.y_scale = y_scale
|
||||
self.yaw_scale = yaw_scale
|
||||
|
||||
self.probe_vel_multiplier = probe_vel_multiplier
|
||||
|
||||
self.triggered_commands = {i: None for i in range(4)} # command profiles for each action button on the controller
|
||||
self.currently_triggered = [0, 0, 0, 0]
|
||||
self.button_states = [0, 0, 0, 0]
|
||||
|
||||
def get_command(self, t, probe=False):
|
||||
|
||||
command = self.state_estimator.get_command()
|
||||
command[0] = command[0] * self.x_scale
|
||||
command[1] = command[1] * self.y_scale
|
||||
command[2] = command[2] * self.yaw_scale
|
||||
|
||||
reset_timer = False
|
||||
|
||||
if probe:
|
||||
command[0] = command[0] * self.probe_vel_multiplier
|
||||
command[2] = command[2] * self.probe_vel_multiplier
|
||||
|
||||
# check for action buttons
|
||||
prev_button_states = self.button_states[:]
|
||||
self.button_states = self.state_estimator.get_buttons()
|
||||
for button in range(4):
|
||||
if self.triggered_commands[button] is not None:
|
||||
if self.button_states[button] == 1 and prev_button_states[button] == 0:
|
||||
if not self.currently_triggered[button]:
|
||||
# reset the triggered action
|
||||
self.triggered_commands[button].reset(t)
|
||||
# reset the internal timing variable
|
||||
reset_timer = True
|
||||
self.currently_triggered[button] = True
|
||||
else:
|
||||
self.currently_triggered[button] = False
|
||||
# execute the triggered action
|
||||
if self.currently_triggered[button] and t < self.triggered_commands[button].max_timestep:
|
||||
command = self.triggered_commands[button].get_command(t)
|
||||
|
||||
|
||||
return command, reset_timer
|
||||
|
||||
def add_triggered_command(self, button_idx, command_profile):
|
||||
self.triggered_commands[button_idx] = command_profile
|
||||
|
||||
def get_buttons(self):
|
||||
return self.state_estimator.get_buttons()
|
||||
|
||||
class RCControllerProfileAccel(RCControllerProfile):
|
||||
def __init__(self, dt, state_estimator, x_scale=1.0, y_scale=1.0, yaw_scale=1.0):
|
||||
super().__init__(dt, state_estimator, x_scale=x_scale, y_scale=y_scale, yaw_scale=yaw_scale)
|
||||
self.x_scale, self.y_scale, self.yaw_scale = self.x_scale / 100., self.y_scale / 100., self.yaw_scale / 100.
|
||||
self.velocity_command = torch.zeros(3)
|
||||
|
||||
def get_command(self, t):
|
||||
|
||||
accel_command = self.state_estimator.get_command()
|
||||
self.velocity_command[0] = self.velocity_command[0] + accel_command[0] * self.x_scale
|
||||
self.velocity_command[1] = self.velocity_command[1] + accel_command[1] * self.y_scale
|
||||
self.velocity_command[2] = self.velocity_command[2] + accel_command[2] * self.yaw_scale
|
||||
|
||||
# check for action buttons
|
||||
prev_button_states = self.button_states[:]
|
||||
self.button_states = self.state_estimator.get_buttons()
|
||||
for button in range(4):
|
||||
if self.button_states[button] == 1 and self.triggered_commands[button] is not None:
|
||||
if prev_button_states[button] == 0:
|
||||
# reset the triggered action
|
||||
self.triggered_commands[button].reset(t)
|
||||
# execute the triggered action
|
||||
return self.triggered_commands[button].get_command(t)
|
||||
|
||||
return self.velocity_command[:]
|
||||
|
||||
def add_triggered_command(self, button_idx, command_profile):
|
||||
self.triggered_commands[button_idx] = command_profile
|
||||
|
||||
def get_buttons(self):
|
||||
return self.state_estimator.get_buttons()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class KeyboardProfile(CommandProfile):
|
||||
# for control via keyboard inputs to isaac gym visualizer
|
||||
def __init__(self, dt, isaac_env, x_scale=1.0, y_scale=1.0, yaw_scale=1.0):
|
||||
super().__init__(dt)
|
||||
from isaacgym.gymapi import KeyboardInput
|
||||
self.gym = isaac_env.gym
|
||||
self.viewer = isaac_env.viewer
|
||||
self.x_scale = x_scale
|
||||
self.y_scale = y_scale
|
||||
self.yaw_scale = yaw_scale
|
||||
self.gym.subscribe_viewer_keyboard_event(self.viewer, KeyboardInput.KEY_UP, "FORWARD")
|
||||
self.gym.subscribe_viewer_keyboard_event(self.viewer, KeyboardInput.KEY_DOWN, "REVERSE")
|
||||
self.gym.subscribe_viewer_keyboard_event(self.viewer, KeyboardInput.KEY_LEFT, "LEFT")
|
||||
self.gym.subscribe_viewer_keyboard_event(self.viewer, KeyboardInput.KEY_RIGHT, "RIGHT")
|
||||
|
||||
self.keyb_command = [0, 0, 0]
|
||||
self.command = [0, 0, 0]
|
||||
|
||||
def get_command(self, t):
|
||||
events = self.gym.query_viewer_action_events(self.viewer)
|
||||
events_dict = {event.action: event.value for event in events}
|
||||
print(events_dict)
|
||||
if "FORWARD" in events_dict and events_dict["FORWARD"] == 1.0: self.keyb_command[0] = 1.0
|
||||
if "FORWARD" in events_dict and events_dict["FORWARD"] == 0.0: self.keyb_command[0] = 0.0
|
||||
if "REVERSE" in events_dict and events_dict["REVERSE"] == 1.0: self.keyb_command[0] = -1.0
|
||||
if "REVERSE" in events_dict and events_dict["REVERSE"] == 0.0: self.keyb_command[0] = 0.0
|
||||
if "LEFT" in events_dict and events_dict["LEFT"] == 1.0: self.keyb_command[1] = 1.0
|
||||
if "LEFT" in events_dict and events_dict["LEFT"] == 0.0: self.keyb_command[1] = 0.0
|
||||
if "RIGHT" in events_dict and events_dict["RIGHT"] == 1.0: self.keyb_command[1] = -1.0
|
||||
if "RIGHT" in events_dict and events_dict["RIGHT"] == 0.0: self.keyb_command[1] = 0.0
|
||||
|
||||
self.command[0] = self.keyb_command[0] * self.x_scale
|
||||
self.command[1] = self.keyb_command[2] * self.y_scale
|
||||
self.command[2] = self.keyb_command[1] * self.yaw_scale
|
||||
|
||||
print(self.command)
|
||||
|
||||
return self.command
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cmdprof = ConstantAccelerationProfile(dt=0.2, max_speed=4, accel_time=3)
|
||||
print(cmdprof.commands)
|
||||
print(cmdprof.get_command(2))
|
||||
Executable
+229
@@ -0,0 +1,229 @@
|
||||
import copy
|
||||
import time
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from go1_gym_deploy.utils.logger import MultiLogger
|
||||
|
||||
|
||||
class DeploymentRunner:
|
||||
def __init__(self, experiment_name="unnamed", se=None, log_root="."):
|
||||
self.agents = {}
|
||||
self.policy = None
|
||||
self.command_profile = None
|
||||
self.logger = MultiLogger()
|
||||
self.se = se
|
||||
self.vision_server = None
|
||||
|
||||
self.log_root = log_root
|
||||
self.init_log_filename()
|
||||
self.control_agent_name = None
|
||||
self.command_agent_name = None
|
||||
|
||||
self.triggered_commands = {i: None for i in range(4)} # command profiles for each action button on the controller
|
||||
self.button_states = np.zeros(4)
|
||||
|
||||
self.is_currently_probing = False
|
||||
self.is_currently_logging = [False, False, False, False]
|
||||
|
||||
def init_log_filename(self):
|
||||
datetime = time.strftime("%Y/%m_%d/%H_%M_%S")
|
||||
|
||||
for i in range(100):
|
||||
try:
|
||||
os.makedirs(f"{self.log_root}/{datetime}_{i}")
|
||||
self.log_filename = f"{self.log_root}/{datetime}_{i}/log.pkl"
|
||||
return
|
||||
except FileExistsError:
|
||||
continue
|
||||
|
||||
|
||||
def add_open_loop_agent(self, agent, name):
|
||||
self.agents[name] = agent
|
||||
self.logger.add_robot(name, agent.env.cfg)
|
||||
|
||||
def add_control_agent(self, agent, name):
|
||||
self.control_agent_name = name
|
||||
self.agents[name] = agent
|
||||
self.logger.add_robot(name, agent.env.cfg)
|
||||
|
||||
def add_vision_server(self, vision_server):
|
||||
self.vision_server = vision_server
|
||||
|
||||
def set_command_agents(self, name):
|
||||
self.command_agent = name
|
||||
|
||||
def add_policy(self, policy):
|
||||
self.policy = policy
|
||||
|
||||
def add_probe_policy(self, probe_policy, probe_cfg):
|
||||
self.probe_policy = probe_policy
|
||||
self.probe_cfg = probe_cfg
|
||||
|
||||
def add_command_profile(self, command_profile):
|
||||
self.command_profile = command_profile
|
||||
|
||||
|
||||
def calibrate(self, wait=True, low=False):
|
||||
# first, if the robot is not in nominal pose, move slowly to the nominal pose
|
||||
for agent_name in self.agents.keys():
|
||||
if hasattr(self.agents[agent_name], "get_obs"):
|
||||
agent = self.agents[agent_name]
|
||||
agent.get_obs()
|
||||
joint_pos = agent.dof_pos
|
||||
if low:
|
||||
final_goal = np.array([0., 0.3, -0.7,
|
||||
0., 0.3, -0.7,
|
||||
0., 0.3, -0.7,
|
||||
0., 0.3, -0.7,])
|
||||
else:
|
||||
final_goal = np.zeros(12)
|
||||
nominal_joint_pos = agent.default_dof_pos
|
||||
|
||||
print(f"About to calibrate; the robot will stand [Press R2 to calibrate]")
|
||||
while wait:
|
||||
self.button_states = self.command_profile.get_buttons()
|
||||
if self.command_profile.state_estimator.right_lower_right_switch_pressed:
|
||||
self.command_profile.state_estimator.right_lower_right_switch_pressed = False
|
||||
break
|
||||
|
||||
cal_action = np.zeros((agent.num_envs, agent.num_actions))
|
||||
target_sequence = []
|
||||
target = joint_pos - nominal_joint_pos
|
||||
while np.max(np.abs(target - final_goal)) > 0.01:
|
||||
target -= np.clip((target - final_goal), -0.05, 0.05)
|
||||
target_sequence += [copy.deepcopy(target)]
|
||||
for target in target_sequence:
|
||||
next_target = target
|
||||
if isinstance(agent.cfg, dict):
|
||||
hip_reduction = agent.cfg["control"]["hip_scale_reduction"]
|
||||
action_scale = agent.cfg["control"]["action_scale"]
|
||||
else:
|
||||
hip_reduction = agent.cfg.control.hip_scale_reduction
|
||||
action_scale = agent.cfg.control.action_scale
|
||||
|
||||
next_target[[0, 3, 6, 9]] /= hip_reduction
|
||||
next_target = next_target / action_scale
|
||||
cal_action[:, 0:12] = next_target
|
||||
agent.step(torch.from_numpy(cal_action))
|
||||
agent.get_obs()
|
||||
time.sleep(0.05)
|
||||
|
||||
print("Starting pose calibrated [Press R2 to start controller]")
|
||||
while True:
|
||||
self.button_states = self.command_profile.get_buttons()
|
||||
if self.command_profile.state_estimator.right_lower_right_switch_pressed:
|
||||
self.command_profile.state_estimator.right_lower_right_switch_pressed = False
|
||||
break
|
||||
|
||||
for agent_name in self.agents.keys():
|
||||
obs = self.agents[agent_name].reset()
|
||||
if agent_name == self.control_agent_name:
|
||||
control_obs = obs
|
||||
|
||||
return control_obs
|
||||
|
||||
|
||||
def run(self, num_log_steps=1000000000, max_steps=100000000, logging=True):
|
||||
assert self.control_agent_name is not None, "cannot deploy, runner has no control agent!"
|
||||
assert self.policy is not None, "cannot deploy, runner has no policy!"
|
||||
assert self.command_profile is not None, "cannot deploy, runner has no command profile!"
|
||||
|
||||
# TODO: add basic test for comms
|
||||
|
||||
for agent_name in self.agents.keys():
|
||||
obs = self.agents[agent_name].reset()
|
||||
if agent_name == self.control_agent_name:
|
||||
control_obs = obs
|
||||
|
||||
control_obs = self.calibrate(wait=True)
|
||||
|
||||
# now, run control loop
|
||||
|
||||
try:
|
||||
for i in range(max_steps):
|
||||
|
||||
policy_info = {}
|
||||
if self.is_currently_probing:
|
||||
action = self.probe_policy(control_obs, policy_info)
|
||||
else:
|
||||
action = self.policy(control_obs, policy_info)
|
||||
|
||||
for agent_name in self.agents.keys():
|
||||
obs, ret, done, info = self.agents[agent_name].step(action)
|
||||
|
||||
info.update(policy_info)
|
||||
info.update({"observation": obs, "reward": ret, "done": done, "timestep": i,
|
||||
"time": i * self.agents[self.control_agent_name].dt, "action": action, "rpy": self.agents[self.control_agent_name].se.get_rpy(), "torques": self.agents[self.control_agent_name].torques})
|
||||
|
||||
if logging: self.logger.log(agent_name, info)
|
||||
|
||||
if agent_name == self.control_agent_name:
|
||||
control_obs, control_ret, control_done, control_info = obs, ret, done, info
|
||||
|
||||
# bad orientation emergency stop
|
||||
rpy = self.agents[self.control_agent_name].se.get_rpy()
|
||||
if abs(rpy[0]) > 1.6 or abs(rpy[1]) > 1.6:
|
||||
self.calibrate(wait=False, low=True)
|
||||
|
||||
# check for logging command
|
||||
prev_button_states = self.button_states[:]
|
||||
self.button_states = self.command_profile.get_buttons()
|
||||
|
||||
if self.command_profile.state_estimator.right_upper_switch_pressed:
|
||||
if not self.is_currently_probing:
|
||||
print("START LOGGING")
|
||||
self.is_currently_probing = True
|
||||
self.agents[self.control_agent_name].set_probing(True)
|
||||
self.init_log_filename()
|
||||
self.logger.reset()
|
||||
else:
|
||||
print("SAVE LOG")
|
||||
self.is_currently_probing = False
|
||||
self.agents[self.control_agent_name].set_probing(False)
|
||||
# calibrate, log, and then resume control
|
||||
control_obs = self.calibrate(wait=False)
|
||||
self.logger.save(self.log_filename)
|
||||
self.init_log_filename()
|
||||
self.logger.reset()
|
||||
time.sleep(1)
|
||||
control_obs = self.agents[self.control_agent_name].reset()
|
||||
self.command_profile.state_estimator.right_upper_switch_pressed = False
|
||||
|
||||
for button in range(4):
|
||||
if self.command_profile.currently_triggered[button]:
|
||||
if not self.is_currently_logging[button]:
|
||||
print("START LOGGING")
|
||||
self.is_currently_logging[button] = True
|
||||
self.init_log_filename()
|
||||
self.logger.reset()
|
||||
else:
|
||||
if self.is_currently_logging[button]:
|
||||
print("SAVE LOG")
|
||||
self.is_currently_logging[button] = False
|
||||
# calibrate, log, and then resume control
|
||||
control_obs = self.calibrate(wait=False)
|
||||
self.logger.save(self.log_filename)
|
||||
self.init_log_filename()
|
||||
self.logger.reset()
|
||||
time.sleep(1)
|
||||
control_obs = self.agents[self.control_agent_name].reset()
|
||||
|
||||
if self.command_profile.state_estimator.right_lower_right_switch_pressed:
|
||||
control_obs = self.calibrate(wait=False)
|
||||
time.sleep(1)
|
||||
self.command_profile.state_estimator.right_lower_right_switch_pressed = False
|
||||
# self.button_states = self.command_profile.get_buttons()
|
||||
while not self.command_profile.state_estimator.right_lower_right_switch_pressed:
|
||||
time.sleep(0.01)
|
||||
# self.button_states = self.command_profile.get_buttons()
|
||||
self.command_profile.state_estimator.right_lower_right_switch_pressed = False
|
||||
|
||||
# finally, return to the nominal pose
|
||||
control_obs = self.calibrate(wait=False)
|
||||
self.logger.save(self.log_filename)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
self.logger.save(self.log_filename)
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
import copy
|
||||
import pickle as pkl
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
def class_to_dict(obj) -> dict:
|
||||
if not hasattr(obj, "__dict__"):
|
||||
return obj
|
||||
result = {}
|
||||
for key in dir(obj):
|
||||
if key.startswith("_") or key == "terrain":
|
||||
continue
|
||||
element = []
|
||||
val = getattr(obj, key)
|
||||
if isinstance(val, list):
|
||||
for item in val:
|
||||
element.append(class_to_dict(item))
|
||||
else:
|
||||
print(key)
|
||||
element = class_to_dict(val)
|
||||
result[key] = element
|
||||
return result
|
||||
|
||||
|
||||
class MultiLogger:
|
||||
def __init__(self):
|
||||
self.loggers = {}
|
||||
|
||||
def add_robot(self, name, cfg):
|
||||
print(name, cfg)
|
||||
self.loggers[name] = EpisodeLogger(cfg)
|
||||
|
||||
def log(self, name, info):
|
||||
self.loggers[name].log(info)
|
||||
|
||||
def save(self, filename):
|
||||
with open(filename, 'wb') as file:
|
||||
logdict = {}
|
||||
for key in self.loggers.keys():
|
||||
logdict[key] = [class_to_dict(self.loggers[key].cfg), self.loggers[key].infos]
|
||||
pkl.dump(logdict, file)
|
||||
print(f"Saved log! Number of timesteps: {[len(self.loggers[key].infos) for key in self.loggers.keys()]}; Path: {filename}")
|
||||
|
||||
def read_metric(self, metric, robot_name=None):
|
||||
if robot_name is None:
|
||||
robot_name = list(self.loggers.keys())[0]
|
||||
logger = self.loggers[robot_name]
|
||||
|
||||
metric_arr = []
|
||||
for info in logger.infos:
|
||||
metric_arr += [info[metric]]
|
||||
return np.array(metric_arr)
|
||||
|
||||
def reset(self):
|
||||
for key, log in self.loggers.items():
|
||||
log.reset()
|
||||
|
||||
|
||||
class EpisodeLogger:
|
||||
def __init__(self, cfg):
|
||||
self.infos = []
|
||||
self.cfg = cfg
|
||||
|
||||
def log(self, info):
|
||||
for key in info.keys():
|
||||
if isinstance(info[key], torch.Tensor):
|
||||
info[key] = info[key].detach().cpu().numpy()
|
||||
|
||||
if isinstance(info[key], dict):
|
||||
continue
|
||||
elif "image" not in key:
|
||||
info[key] = copy.deepcopy(info[key])
|
||||
|
||||
self.infos += [dict(info)]
|
||||
|
||||
def reset(self):
|
||||
self.infos = []
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from os.path import expanduser
|
||||
import netifaces
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
def get_saved_interface_name():
|
||||
home = expanduser("~")
|
||||
name = ""
|
||||
try:
|
||||
with open(home + "/.cheetah_network.txt"):
|
||||
name = f.read().split()[0]
|
||||
except:
|
||||
name = ""
|
||||
return name
|
||||
|
||||
def get_likely_iface():
|
||||
ifs = netifaces.interfaces()
|
||||
print("Found {} interfaces:".format(len(ifs)))
|
||||
|
||||
if_to_addrs = {}
|
||||
|
||||
for i in ifs:
|
||||
if_to_addrs[i] = []
|
||||
if netifaces.AF_INET in netifaces.ifaddresses(i).keys():
|
||||
for ad in netifaces.ifaddresses(i)[netifaces.AF_INET]:
|
||||
if_to_addrs[i].append(ad['addr'])
|
||||
|
||||
for i in range(len(ifs)):
|
||||
print(" [{}] : {} : {}".format(i, ifs[i], if_to_addrs[ifs[i]]))
|
||||
|
||||
found_10_ip = 0
|
||||
selected_if = ""
|
||||
|
||||
for i in ifs:
|
||||
match_string = "192.168.123."
|
||||
if len(if_to_addrs[i]) > 0 and if_to_addrs[i][0][:len(match_string)] == match_string:
|
||||
found_10_ip = found_10_ip + 1
|
||||
selected_if = i
|
||||
|
||||
if found_10_ip == 0:
|
||||
print("None of the network adapters look correct. Make sure you have set a 10.0.0.x static ip!")
|
||||
return ""
|
||||
|
||||
elif found_10_ip == 1:
|
||||
print("The adapter {} seems correct".format(selected_if))
|
||||
return selected_if
|
||||
|
||||
else:
|
||||
print("Found {} possible adapters, giving up".format(found_10_ip))
|
||||
return ""
|
||||
|
||||
def main():
|
||||
name = get_saved_interface_name()
|
||||
if not name:
|
||||
print("Didn't find saved interface, searching...")
|
||||
name = get_likely_iface()
|
||||
if not name:
|
||||
sys.exit("Failed to find network adapter name")
|
||||
else:
|
||||
print("Found saved interface {}".format(name))
|
||||
|
||||
print("Setup for interface {}".format(name))
|
||||
subprocess.call(['sudo', 'ifconfig', name, 'multicast'])
|
||||
subprocess.call(['sudo', 'route', 'add', '-net', '224.0.0.0', 'netmask', '240.0.0.0', 'dev', name])
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
from .vec_env import VecEnv
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple, Union
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
# minimal interface of the environment
|
||||
class VecEnv(ABC):
|
||||
num_envs: int
|
||||
num_obs: int
|
||||
num_privileged_obs: int
|
||||
num_actions: int
|
||||
max_episode_length: int
|
||||
privileged_obs_buf: torch.Tensor
|
||||
obs_buf: torch.Tensor
|
||||
rew_buf: torch.Tensor
|
||||
reset_buf: torch.Tensor
|
||||
episode_length_buf: torch.Tensor # current episode duration
|
||||
extras: dict
|
||||
device: torch.device
|
||||
|
||||
@abstractmethod
|
||||
def step(self, actions: torch.Tensor) -> Tuple[
|
||||
torch.Tensor, Union[torch.Tensor, None], torch.Tensor, torch.Tensor, dict]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def reset(self, env_ids: Union[list, torch.Tensor]):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_observations(self) -> torch.Tensor:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_privileged_observations(self) -> Union[torch.Tensor, None]:
|
||||
pass
|
||||
@@ -0,0 +1,148 @@
|
||||
from go1_gym.envs.base.legged_robot_config import Cfg
|
||||
|
||||
|
||||
def base_set():
|
||||
# set basics
|
||||
Cfg.terrain.teleport_robots = True
|
||||
Cfg.terrain.border_size = 50
|
||||
Cfg.terrain.num_rows = 10
|
||||
Cfg.terrain.num_cols = 10
|
||||
Cfg.commands.resampling_time = 1e9
|
||||
Cfg.env.episode_length_s = 500
|
||||
Cfg.rewards.terminal_body_height = 0.0
|
||||
Cfg.rewards.use_terminal_body_height = True
|
||||
|
||||
|
||||
def rand_regular():
|
||||
Cfg.domain_rand.randomize_friction = True
|
||||
Cfg.domain_rand.friction_range = [0.05, 4.5]
|
||||
Cfg.domain_rand.randomize_restitution = True
|
||||
Cfg.domain_rand.restitution_range = [0, 1.0]
|
||||
Cfg.domain_rand.restitution = 0.5
|
||||
Cfg.domain_rand.randomize_base_mass = True
|
||||
Cfg.domain_rand.added_mass_range = [-1., 3.]
|
||||
Cfg.domain_rand.randomize_com_displacement = True
|
||||
Cfg.domain_rand.com_displacement_range = [-0.1, 0.1]
|
||||
Cfg.domain_rand.randomize_motor_strength = True
|
||||
Cfg.domain_rand.motor_strength_range = [0.9, 1.1]
|
||||
Cfg.domain_rand.randomize_Kp_factor = False
|
||||
Cfg.domain_rand.Kp_factor_range = [0.8, 1.3]
|
||||
Cfg.domain_rand.randomize_Kd_factor = False
|
||||
Cfg.domain_rand.Kd_factor_range = [0.5, 1.5]
|
||||
Cfg.domain_rand.push_robots = False
|
||||
Cfg.domain_rand.push_interval_s = 15
|
||||
Cfg.domain_rand.max_push_vel_xy = 1.
|
||||
|
||||
|
||||
def rand_large():
|
||||
Cfg.domain_rand.randomize_friction = True
|
||||
Cfg.domain_rand.friction_range = [0.04, 6.0]
|
||||
Cfg.domain_rand.randomize_restitution = True
|
||||
Cfg.domain_rand.restitution_range = [0, 1.0]
|
||||
Cfg.domain_rand.restitution = 0.5
|
||||
Cfg.domain_rand.randomize_base_mass = True
|
||||
Cfg.domain_rand.added_mass_range = [-1.5, 4.]
|
||||
Cfg.domain_rand.randomize_com_displacement = True
|
||||
Cfg.domain_rand.com_displacement_range = [-0.13, 0.13]
|
||||
Cfg.domain_rand.randomize_motor_strength = True
|
||||
Cfg.domain_rand.motor_strength_range = [0.88, 1.12] # table 1 in RMA may have a typo
|
||||
Cfg.domain_rand.randomize_Kp_factor = False
|
||||
Cfg.domain_rand.Kp_factor_range = [0.8, 1.3]
|
||||
Cfg.domain_rand.randomize_Kd_factor = False
|
||||
Cfg.domain_rand.Kd_factor_range = [0.5, 1.5]
|
||||
Cfg.domain_rand.push_robots = False
|
||||
Cfg.domain_rand.push_interval_s = 15
|
||||
Cfg.domain_rand.max_push_vel_xy = 1.
|
||||
|
||||
|
||||
def static_low():
|
||||
Cfg.domain_rand.randomize_friction = True
|
||||
Cfg.domain_rand.friction_range = [0.05, 0.06]
|
||||
Cfg.domain_rand.randomize_restitution = True
|
||||
Cfg.domain_rand.restitution_range = [0, 0.01]
|
||||
Cfg.domain_rand.restitution = 0.5
|
||||
Cfg.domain_rand.randomize_base_mass = True
|
||||
Cfg.domain_rand.added_mass_range = [-1., -0.99]
|
||||
Cfg.domain_rand.randomize_com_displacement = True
|
||||
Cfg.domain_rand.com_displacement_range = [-0.1, -0.09]
|
||||
Cfg.domain_rand.randomize_motor_strength = True
|
||||
Cfg.domain_rand.motor_strength_range = [0.9, -0.99]
|
||||
Cfg.domain_rand.randomize_Kp_factor = False
|
||||
Cfg.domain_rand.Kp_factor_range = [0.8, 1.3]
|
||||
Cfg.domain_rand.randomize_Kd_factor = False
|
||||
Cfg.domain_rand.Kd_factor_range = [0.5, 1.5]
|
||||
Cfg.domain_rand.push_robots = False
|
||||
Cfg.domain_rand.push_interval_s = 15
|
||||
Cfg.domain_rand.max_push_vel_xy = 1.
|
||||
|
||||
|
||||
def static_medium():
|
||||
Cfg.domain_rand.randomize_friction = True
|
||||
Cfg.domain_rand.friction_range = [1.0, 1.01]
|
||||
Cfg.domain_rand.randomize_restitution = True
|
||||
Cfg.domain_rand.restitution_range = [0.5, 0.51]
|
||||
Cfg.domain_rand.restitution = 0.5
|
||||
Cfg.domain_rand.randomize_base_mass = True
|
||||
Cfg.domain_rand.added_mass_range = [0.0, 0.01]
|
||||
Cfg.domain_rand.randomize_com_displacement = True
|
||||
Cfg.domain_rand.com_displacement_range = [0.0, 0.01]
|
||||
Cfg.domain_rand.randomize_motor_strength = True
|
||||
Cfg.domain_rand.motor_strength_range = [1.0, 1.01]
|
||||
Cfg.domain_rand.randomize_Kp_factor = False
|
||||
Cfg.domain_rand.Kp_factor_range = [0.8, 1.3]
|
||||
Cfg.domain_rand.randomize_Kd_factor = False
|
||||
Cfg.domain_rand.Kd_factor_range = [0.5, 1.5]
|
||||
Cfg.domain_rand.push_robots = False
|
||||
Cfg.domain_rand.push_interval_s = 15
|
||||
Cfg.domain_rand.max_push_vel_xy = 1.
|
||||
|
||||
|
||||
def static_high():
|
||||
Cfg.domain_rand.randomize_friction = True
|
||||
Cfg.domain_rand.friction_range = [4.49, 4.5]
|
||||
Cfg.domain_rand.randomize_restitution = True
|
||||
Cfg.domain_rand.restitution_range = [0.99, 1.0]
|
||||
Cfg.domain_rand.restitution = 0.5
|
||||
Cfg.domain_rand.randomize_base_mass = True
|
||||
Cfg.domain_rand.added_mass_range = [2.99, 3.]
|
||||
Cfg.domain_rand.randomize_com_displacement = True
|
||||
Cfg.domain_rand.com_displacement_range = [0.09, 0.1]
|
||||
Cfg.domain_rand.randomize_motor_strength = True
|
||||
Cfg.domain_rand.motor_strength_range = [1.09, 1.1]
|
||||
Cfg.domain_rand.randomize_Kp_factor = False
|
||||
Cfg.domain_rand.Kp_factor_range = [0.8, 1.3]
|
||||
Cfg.domain_rand.randomize_Kd_factor = False
|
||||
Cfg.domain_rand.Kd_factor_range = [0.5, 1.5]
|
||||
Cfg.domain_rand.push_robots = False
|
||||
Cfg.domain_rand.push_interval_s = 15
|
||||
Cfg.domain_rand.max_push_vel_xy = 1.
|
||||
|
||||
def only_base_mass():
|
||||
Cfg.domain_rand.randomize_friction = True
|
||||
Cfg.domain_rand.friction_range = [1.0, 1.01]
|
||||
Cfg.domain_rand.randomize_restitution = True
|
||||
Cfg.domain_rand.restitution_range = [0.5, 0.51]
|
||||
Cfg.domain_rand.restitution = 0.5
|
||||
Cfg.domain_rand.randomize_base_mass = True
|
||||
Cfg.domain_rand.added_mass_range = [-1, 3]
|
||||
Cfg.domain_rand.randomize_com_displacement = True
|
||||
Cfg.domain_rand.com_displacement_range = [0.0, 0.01]
|
||||
Cfg.domain_rand.randomize_motor_strength = True
|
||||
Cfg.domain_rand.motor_strength_range = [1.0, 1.01]
|
||||
Cfg.domain_rand.randomize_Kp_factor = False
|
||||
Cfg.domain_rand.Kp_factor_range = [0.8, 1.3]
|
||||
Cfg.domain_rand.randomize_Kd_factor = False
|
||||
Cfg.domain_rand.Kd_factor_range = [0.5, 1.5]
|
||||
Cfg.domain_rand.push_robots = False
|
||||
Cfg.domain_rand.push_interval_s = 15
|
||||
Cfg.domain_rand.max_push_vel_xy = 1.
|
||||
|
||||
|
||||
DR_SETTINGS = dict(
|
||||
rand_regular=rand_regular,
|
||||
rand_large=rand_large,
|
||||
static_low=static_low,
|
||||
static_medium=static_medium,
|
||||
static_high=static_high,
|
||||
only_base_mass=only_base_mass,
|
||||
)
|
||||
@@ -0,0 +1,99 @@
|
||||
def to_numpy(fn):
|
||||
def thunk(*args, **kwargs):
|
||||
return fn(*args, **kwargs).cpu().numpy()
|
||||
|
||||
return thunk
|
||||
|
||||
|
||||
def lin_vel_rmsd(env, actor_critic, obs):
|
||||
return ((env.base_lin_vel[:, 0] - env.commands[:, 0]) ** 2).cpu() ** 0.5
|
||||
|
||||
|
||||
def ang_vel_rmsd(env, actor_critic, obs):
|
||||
return ((env.base_ang_vel[:, 2] - env.commands[:, 2]) ** 2).cpu() ** 0.5
|
||||
|
||||
|
||||
def lin_vel_x(env, actor_critic, obs):
|
||||
return env.base_lin_vel[:, 0].cpu()
|
||||
|
||||
|
||||
def ang_vel_yaw(env, actor_critic, obs):
|
||||
return env.base_ang_vel[:, 2].cpu()
|
||||
|
||||
|
||||
def base_height(env, actor_critic, obs):
|
||||
import torch
|
||||
return torch.mean(env.root_states[:, 2].unsqueeze(1) - env.measured_heights, dim=1).cpu()
|
||||
|
||||
|
||||
def max_torques(env, actor_critic, obs):
|
||||
import torch
|
||||
max_torque, max_torque_indices = torch.max(torch.abs(env.torques), dim=1)
|
||||
return max_torque.cpu()
|
||||
|
||||
|
||||
def power_consumption(env, actor_critic, obs):
|
||||
import torch
|
||||
return torch.sum(torch.multiply(env.torques, env.dof_vel), dim=1).cpu()
|
||||
|
||||
|
||||
def CoT(env, actor_critic, obs):
|
||||
# P / (mgv)
|
||||
import torch
|
||||
P = power_consumption(env, actor_critic, obs)
|
||||
m = (env.default_body_mass + env.payloads).cpu()
|
||||
g = 9.8 # m/s^2
|
||||
v = torch.norm(env.base_lin_vel[:, 0:2], dim=1).cpu()
|
||||
return P / (m * g * v)
|
||||
|
||||
|
||||
def froude_number(env, actor_critic, obs):
|
||||
# v^2 / (gh)
|
||||
v = lin_vel_x(env, actor_critic, obs)
|
||||
g = 9.8
|
||||
h = 0.30
|
||||
return v ** 2 / (g * h)
|
||||
|
||||
|
||||
def adaptation_loss(env, actor_critic, obs):
|
||||
import torch
|
||||
if hasattr(actor_critic, "adaptation_module"):
|
||||
pred = actor_critic.adaptation_module(obs["obs_history"])
|
||||
target = actor_critic.env_factor_encoder(obs["privileged_obs"])
|
||||
return torch.mean((pred.cpu().detach() - target.cpu().detach()) ** 2, dim=1)
|
||||
|
||||
|
||||
def auxiliary_rewards(env, actor_critic, obs):
|
||||
rewards = {}
|
||||
for i in range(len(env.reward_functions)):
|
||||
name = env.reward_names[i]
|
||||
rew = env.reward_functions[i]() * env.reward_scales[name]
|
||||
rewards[name] = rew.cpu().detach()
|
||||
return rewards
|
||||
|
||||
|
||||
def termination(env, actor_critic, obs):
|
||||
return env.reset_buf.cpu().detach()
|
||||
|
||||
|
||||
def privileged_obs(env, actor_critic, obs):
|
||||
return obs["privileged_obs"].cpu().numpy()
|
||||
|
||||
|
||||
def latents(env, actor_critic, obs):
|
||||
return actor_critic.env_factor_encoder(obs["privileged_obs"]).cpu().numpy()
|
||||
|
||||
|
||||
METRICS_FNS = {name: fn for name, fn in locals().items() if name not in ['to_numpy'] and "__" not in name}
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(*METRICS_FNS.items(), sep="\n")
|
||||
|
||||
import torch
|
||||
|
||||
env = lambda: None
|
||||
env.base_lin_vel = torch.rand(10, 3)
|
||||
env.commands = torch.rand(10, 3)
|
||||
|
||||
metric = lin_vel_rmsd(env, None, None)
|
||||
print(metric)
|
||||
Executable
+298
@@ -0,0 +1,298 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
import torch
|
||||
from ml_logger import logger
|
||||
from params_proto import PrefixProto
|
||||
import os
|
||||
import copy
|
||||
|
||||
from .actor_critic import ActorCritic
|
||||
from .rollout_storage import RolloutStorage
|
||||
|
||||
from go1_gym import MINI_GYM_ROOT_DIR
|
||||
|
||||
|
||||
def class_to_dict(obj) -> dict:
|
||||
if not hasattr(obj, "__dict__"):
|
||||
return obj
|
||||
result = {}
|
||||
for key in dir(obj):
|
||||
if key.startswith("_") or key == "terrain":
|
||||
continue
|
||||
element = []
|
||||
val = getattr(obj, key)
|
||||
if isinstance(val, list):
|
||||
for item in val:
|
||||
element.append(class_to_dict(item))
|
||||
else:
|
||||
element = class_to_dict(val)
|
||||
result[key] = element
|
||||
return result
|
||||
|
||||
|
||||
class DataCaches:
|
||||
def __init__(self, curriculum_bins):
|
||||
from go1_gym_learn.ppo.metrics_caches import DistCache, SlotCache
|
||||
|
||||
self.slot_cache = SlotCache(curriculum_bins)
|
||||
self.dist_cache = DistCache()
|
||||
|
||||
|
||||
caches = DataCaches(1)
|
||||
|
||||
|
||||
class RunnerArgs(PrefixProto, cli=False):
|
||||
# runner
|
||||
algorithm_class_name = 'PPO'
|
||||
num_steps_per_env = 24 # per iteration
|
||||
max_iterations = 1500 # number of policy updates
|
||||
|
||||
# logging
|
||||
save_interval = 400 # check for potential saves every this many iterations
|
||||
save_video_interval = 100
|
||||
log_freq = 10
|
||||
|
||||
# load and resume
|
||||
resume = False
|
||||
load_run = -1 # -1 = last run
|
||||
checkpoint = -1 # -1 = last saved model
|
||||
resume_path = None # updated from load_run and chkpt
|
||||
|
||||
|
||||
class Runner:
|
||||
|
||||
def __init__(self, env, device='cpu'):
|
||||
from .ppo import PPO
|
||||
|
||||
self.device = device
|
||||
self.env = env
|
||||
|
||||
actor_critic = ActorCritic(self.env.num_obs,
|
||||
self.env.num_privileged_obs,
|
||||
self.env.num_obs_history,
|
||||
self.env.num_actions,
|
||||
).to(self.device)
|
||||
self.alg = PPO(actor_critic, device=self.device)
|
||||
self.num_steps_per_env = RunnerArgs.num_steps_per_env
|
||||
|
||||
# init storage and model
|
||||
self.alg.init_storage(self.env.num_train_envs, self.num_steps_per_env, [self.env.num_obs],
|
||||
[self.env.num_privileged_obs], [self.env.num_obs_history], [self.env.num_actions])
|
||||
|
||||
self.tot_timesteps = 0
|
||||
self.tot_time = 0
|
||||
self.current_learning_iteration = 0
|
||||
self.last_recording_it = 0
|
||||
|
||||
self.env.reset()
|
||||
|
||||
def learn(self, num_learning_iterations, init_at_random_ep_len=False, eval_freq=100, eval_expert=False):
|
||||
from ml_logger import logger
|
||||
# initialize writer
|
||||
assert logger.prefix, "you will overwrite the entire instrument server"
|
||||
|
||||
logger.start('start', 'epoch', 'episode', 'run', 'step')
|
||||
|
||||
if init_at_random_ep_len:
|
||||
self.env.episode_length_buf = torch.randint_like(self.env.episode_length_buf,
|
||||
high=int(self.env.max_episode_length))
|
||||
|
||||
# split train and test envs
|
||||
num_train_envs = self.env.num_train_envs
|
||||
|
||||
obs_dict = self.env.get_observations()
|
||||
obs, privileged_obs, obs_history = obs_dict["obs"], obs_dict["privileged_obs"], obs_dict["obs_history"]
|
||||
obs, privileged_obs, obs_history = obs.to(self.device), privileged_obs.to(self.device), obs_history.to(
|
||||
self.device)
|
||||
self.alg.actor_critic.train()
|
||||
|
||||
rewbuffer = deque(maxlen=100)
|
||||
lenbuffer = deque(maxlen=100)
|
||||
rewbuffer_eval = deque(maxlen=100)
|
||||
lenbuffer_eval = deque(maxlen=100)
|
||||
cur_reward_sum = torch.zeros(self.env.num_envs, dtype=torch.float, device=self.device)
|
||||
cur_episode_length = torch.zeros(self.env.num_envs, dtype=torch.float, device=self.device)
|
||||
|
||||
if hasattr(self.env, "curriculum"):
|
||||
caches.__init__(curriculum_bins=len(self.env.curriculum))
|
||||
|
||||
tot_iter = self.current_learning_iteration + num_learning_iterations
|
||||
for it in range(self.current_learning_iteration, tot_iter):
|
||||
start = time.time()
|
||||
# Rollout
|
||||
with torch.inference_mode():
|
||||
for i in range(self.num_steps_per_env):
|
||||
actions_train = self.alg.act(obs[:num_train_envs], privileged_obs[:num_train_envs],
|
||||
obs_history[:num_train_envs])
|
||||
if eval_expert:
|
||||
actions_eval = self.alg.actor_critic.act_teacher(obs[num_train_envs:],
|
||||
privileged_obs[num_train_envs:])
|
||||
else:
|
||||
actions_eval = self.alg.actor_critic.act_student(obs[num_train_envs:],
|
||||
obs_history[num_train_envs:])
|
||||
ret = self.env.step(torch.cat((actions_train, actions_eval), dim=0))
|
||||
obs_dict, rewards, dones, infos = ret
|
||||
obs, privileged_obs, obs_history = obs_dict["obs"], obs_dict["privileged_obs"], obs_dict[
|
||||
"obs_history"]
|
||||
|
||||
obs, privileged_obs, obs_history, rewards, dones = obs.to(self.device), privileged_obs.to(
|
||||
self.device), obs_history.to(self.device), rewards.to(self.device), dones.to(self.device)
|
||||
self.alg.process_env_step(rewards[:num_train_envs], dones[:num_train_envs], infos)
|
||||
|
||||
if 'train/episode' in infos:
|
||||
with logger.Prefix(metrics="train/episode"):
|
||||
logger.store_metrics(**infos['train/episode'])
|
||||
|
||||
if 'eval/episode' in infos:
|
||||
with logger.Prefix(metrics="eval/episode"):
|
||||
logger.store_metrics(**infos['eval/episode'])
|
||||
|
||||
if 'curriculum' in infos:
|
||||
curr_bins_train = infos['curriculum']['reset_train_env_bins']
|
||||
curr_bins_eval = infos['curriculum']['reset_eval_env_bins']
|
||||
|
||||
caches.slot_cache.log(curr_bins_train, **{
|
||||
k.split("/", 1)[-1]: v for k, v in infos['curriculum'].items()
|
||||
if k.startswith('slot/train')
|
||||
})
|
||||
caches.slot_cache.log(curr_bins_eval, **{
|
||||
k.split("/", 1)[-1]: v for k, v in infos['curriculum'].items()
|
||||
if k.startswith('slot/eval')
|
||||
})
|
||||
caches.dist_cache.log(**{
|
||||
k.split("/", 1)[-1]: v for k, v in infos['curriculum'].items()
|
||||
if k.startswith('dist/train')
|
||||
})
|
||||
caches.dist_cache.log(**{
|
||||
k.split("/", 1)[-1]: v for k, v in infos['curriculum'].items()
|
||||
if k.startswith('dist/eval')
|
||||
})
|
||||
|
||||
cur_reward_sum += rewards
|
||||
cur_episode_length += 1
|
||||
|
||||
new_ids = (dones > 0).nonzero(as_tuple=False)
|
||||
|
||||
new_ids_train = new_ids[new_ids < num_train_envs]
|
||||
rewbuffer.extend(cur_reward_sum[new_ids_train].cpu().numpy().tolist())
|
||||
lenbuffer.extend(cur_episode_length[new_ids_train].cpu().numpy().tolist())
|
||||
cur_reward_sum[new_ids_train] = 0
|
||||
cur_episode_length[new_ids_train] = 0
|
||||
|
||||
new_ids_eval = new_ids[new_ids >= num_train_envs]
|
||||
rewbuffer_eval.extend(cur_reward_sum[new_ids_eval].cpu().numpy().tolist())
|
||||
lenbuffer_eval.extend(cur_episode_length[new_ids_eval].cpu().numpy().tolist())
|
||||
cur_reward_sum[new_ids_eval] = 0
|
||||
cur_episode_length[new_ids_eval] = 0
|
||||
|
||||
# Learning step
|
||||
self.alg.compute_returns(obs[:num_train_envs], privileged_obs[:num_train_envs])
|
||||
|
||||
if it % eval_freq == 0:
|
||||
self.env.reset_evaluation_envs()
|
||||
|
||||
if it % eval_freq == 0:
|
||||
logger.save_pkl({"iteration": it,
|
||||
**caches.slot_cache.get_summary(),
|
||||
**caches.dist_cache.get_summary()},
|
||||
path=f"curriculum/info.pkl", append=True)
|
||||
|
||||
mean_value_loss, mean_surrogate_loss, mean_adaptation_module_loss = self.alg.update()
|
||||
|
||||
logger.store_metrics(
|
||||
time_elapsed=logger.since('start'),
|
||||
time_iter=logger.split('epoch'),
|
||||
adaptation_loss=mean_adaptation_module_loss,
|
||||
mean_value_loss=mean_value_loss,
|
||||
mean_surrogate_loss=mean_surrogate_loss
|
||||
)
|
||||
|
||||
if RunnerArgs.save_video_interval:
|
||||
self.log_video(it)
|
||||
|
||||
self.tot_timesteps += self.num_steps_per_env * self.env.num_envs
|
||||
if logger.every(RunnerArgs.log_freq, "iteration", start_on=1):
|
||||
# if it % Config.log_freq == 0:
|
||||
logger.log_metrics_summary(key_values={"timesteps": self.tot_timesteps, "iterations": it})
|
||||
logger.job_running()
|
||||
|
||||
if it % RunnerArgs.save_interval == 0:
|
||||
with logger.Sync():
|
||||
logger.torch_save(self.alg.actor_critic.state_dict(), f"checkpoints/ac_weights_{it:06d}.pt")
|
||||
logger.duplicate(f"checkpoints/ac_weights_{it:06d}.pt", f"checkpoints/ac_weights_last.pt")
|
||||
|
||||
path = f'{MINI_GYM_ROOT_DIR}/tmp/legged_data'
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
adaptation_module_path = f'{path}/adaptation_module_latest.jit'
|
||||
adaptation_module = copy.deepcopy(self.alg.actor_critic.adaptation_module).to('cpu')
|
||||
traced_script_adaptation_module = torch.jit.script(adaptation_module)
|
||||
traced_script_adaptation_module.save(adaptation_module_path)
|
||||
|
||||
body_path = f'{path}/body_latest.jit'
|
||||
body_model = copy.deepcopy(self.alg.actor_critic.actor_body).to('cpu')
|
||||
traced_script_body_module = torch.jit.script(body_model)
|
||||
traced_script_body_module.save(body_path)
|
||||
|
||||
logger.upload_file(file_path=adaptation_module_path, target_path=f"checkpoints/", once=False)
|
||||
logger.upload_file(file_path=body_path, target_path=f"checkpoints/", once=False)
|
||||
|
||||
self.current_learning_iteration += num_learning_iterations
|
||||
|
||||
with logger.Sync():
|
||||
logger.torch_save(self.alg.actor_critic.state_dict(), f"checkpoints/ac_weights_{it:06d}.pt")
|
||||
logger.duplicate(f"checkpoints/ac_weights_{it:06d}.pt", f"checkpoints/ac_weights_last.pt")
|
||||
|
||||
path = f'{MINI_GYM_ROOT_DIR}/tmp/legged_data'
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
adaptation_module_path = f'{path}/adaptation_module_latest.jit'
|
||||
adaptation_module = copy.deepcopy(self.alg.actor_critic.adaptation_module).to('cpu')
|
||||
traced_script_adaptation_module = torch.jit.script(adaptation_module)
|
||||
traced_script_adaptation_module.save(adaptation_module_path)
|
||||
|
||||
body_path = f'{path}/body_latest.jit'
|
||||
body_model = copy.deepcopy(self.alg.actor_critic.actor_body).to('cpu')
|
||||
traced_script_body_module = torch.jit.script(body_model)
|
||||
traced_script_body_module.save(body_path)
|
||||
|
||||
logger.upload_file(file_path=adaptation_module_path, target_path=f"checkpoints/", once=False)
|
||||
logger.upload_file(file_path=body_path, target_path=f"checkpoints/", once=False)
|
||||
|
||||
def log_video(self, it):
|
||||
if it - self.last_recording_it >= RunnerArgs.save_video_interval:
|
||||
self.env.start_recording()
|
||||
if self.env.num_eval_envs > 0:
|
||||
self.env.start_recording_eval()
|
||||
print("START RECORDING")
|
||||
self.last_recording_it = it
|
||||
|
||||
frames = self.env.get_complete_frames()
|
||||
if len(frames) > 0:
|
||||
self.env.pause_recording()
|
||||
print("LOGGING VIDEO")
|
||||
logger.save_video(frames, f"videos/{it:05d}.mp4", fps=1 / self.env.dt)
|
||||
|
||||
if self.env.num_eval_envs > 0:
|
||||
frames = self.env.get_complete_frames_eval()
|
||||
if len(frames) > 0:
|
||||
self.env.pause_recording_eval()
|
||||
print("LOGGING EVAL VIDEO")
|
||||
logger.save_video(frames, f"videos/{it:05d}_eval.mp4", fps=1 / self.env.dt)
|
||||
|
||||
def get_inference_policy(self, device=None):
|
||||
self.alg.actor_critic.eval()
|
||||
if device is not None:
|
||||
self.alg.actor_critic.to(device)
|
||||
return self.alg.actor_critic.act_inference
|
||||
|
||||
def get_expert_policy(self, device=None):
|
||||
self.alg.actor_critic.eval()
|
||||
if device is not None:
|
||||
self.alg.actor_critic.to(device)
|
||||
return self.alg.actor_critic.act_expert
|
||||
Executable
+193
@@ -0,0 +1,193 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from params_proto import PrefixProto
|
||||
from torch.distributions import Normal
|
||||
|
||||
|
||||
class AC_Args(PrefixProto, cli=False):
|
||||
# policy
|
||||
init_noise_std = 1.0
|
||||
actor_hidden_dims = [512, 256, 128]
|
||||
critic_hidden_dims = [512, 256, 128]
|
||||
activation = 'elu' # can be elu, relu, selu, crelu, lrelu, tanh, sigmoid
|
||||
|
||||
adaptation_module_branch_hidden_dims = [[256, 32]]
|
||||
|
||||
env_factor_encoder_branch_input_dims = [18]
|
||||
env_factor_encoder_branch_latent_dims = [18]
|
||||
env_factor_encoder_branch_hidden_dims = [[256, 128]]
|
||||
|
||||
|
||||
class ActorCritic(nn.Module):
|
||||
is_recurrent = False
|
||||
|
||||
def __init__(self, num_obs,
|
||||
num_privileged_obs,
|
||||
num_obs_history,
|
||||
num_actions,
|
||||
**kwargs):
|
||||
if kwargs:
|
||||
print("ActorCritic.__init__ got unexpected arguments, which will be ignored: " + str(
|
||||
[key for key in kwargs.keys()]))
|
||||
super().__init__()
|
||||
|
||||
activation = get_activation(AC_Args.activation)
|
||||
|
||||
for i, (branch_input_dim, branch_hidden_dims, branch_latent_dim) in enumerate(
|
||||
zip(AC_Args.env_factor_encoder_branch_input_dims,
|
||||
AC_Args.env_factor_encoder_branch_hidden_dims,
|
||||
AC_Args.env_factor_encoder_branch_latent_dims)):
|
||||
# Env factor encoder
|
||||
env_factor_encoder_layers = []
|
||||
env_factor_encoder_layers.append(nn.Linear(branch_input_dim, branch_hidden_dims[0]))
|
||||
env_factor_encoder_layers.append(activation)
|
||||
for l in range(len(branch_hidden_dims)):
|
||||
if l == len(branch_hidden_dims) - 1:
|
||||
env_factor_encoder_layers.append(
|
||||
nn.Linear(branch_hidden_dims[l], branch_latent_dim))
|
||||
else:
|
||||
env_factor_encoder_layers.append(
|
||||
nn.Linear(branch_hidden_dims[l],
|
||||
branch_hidden_dims[l + 1]))
|
||||
env_factor_encoder_layers.append(activation)
|
||||
self.env_factor_encoder = nn.Sequential(*env_factor_encoder_layers)
|
||||
self.add_module(f"encoder", self.env_factor_encoder)
|
||||
|
||||
# Adaptation module
|
||||
for i, (branch_hidden_dims, branch_latent_dim) in enumerate(zip(AC_Args.adaptation_module_branch_hidden_dims,
|
||||
AC_Args.env_factor_encoder_branch_latent_dims)):
|
||||
adaptation_module_layers = []
|
||||
adaptation_module_layers.append(nn.Linear(num_obs_history, branch_hidden_dims[0]))
|
||||
adaptation_module_layers.append(activation)
|
||||
for l in range(len(branch_hidden_dims)):
|
||||
if l == len(branch_hidden_dims) - 1:
|
||||
adaptation_module_layers.append(
|
||||
nn.Linear(branch_hidden_dims[l], branch_latent_dim))
|
||||
else:
|
||||
adaptation_module_layers.append(
|
||||
nn.Linear(branch_hidden_dims[l],
|
||||
branch_hidden_dims[l + 1]))
|
||||
adaptation_module_layers.append(activation)
|
||||
self.adaptation_module = nn.Sequential(*adaptation_module_layers)
|
||||
self.add_module(f"adaptation_module", self.adaptation_module)
|
||||
|
||||
total_latent_dim = int(torch.sum(torch.Tensor(AC_Args.env_factor_encoder_branch_latent_dims)))
|
||||
|
||||
# Policy
|
||||
actor_layers = []
|
||||
actor_layers.append(nn.Linear(total_latent_dim + num_obs, AC_Args.actor_hidden_dims[0]))
|
||||
actor_layers.append(activation)
|
||||
for l in range(len(AC_Args.actor_hidden_dims)):
|
||||
if l == len(AC_Args.actor_hidden_dims) - 1:
|
||||
actor_layers.append(nn.Linear(AC_Args.actor_hidden_dims[l], num_actions))
|
||||
else:
|
||||
actor_layers.append(nn.Linear(AC_Args.actor_hidden_dims[l], AC_Args.actor_hidden_dims[l + 1]))
|
||||
actor_layers.append(activation)
|
||||
self.actor_body = nn.Sequential(*actor_layers)
|
||||
|
||||
# Value function
|
||||
critic_layers = []
|
||||
critic_layers.append(nn.Linear(total_latent_dim + num_obs, AC_Args.critic_hidden_dims[0]))
|
||||
critic_layers.append(activation)
|
||||
for l in range(len(AC_Args.critic_hidden_dims)):
|
||||
if l == len(AC_Args.critic_hidden_dims) - 1:
|
||||
critic_layers.append(nn.Linear(AC_Args.critic_hidden_dims[l], 1))
|
||||
else:
|
||||
critic_layers.append(nn.Linear(AC_Args.critic_hidden_dims[l], AC_Args.critic_hidden_dims[l + 1]))
|
||||
critic_layers.append(activation)
|
||||
self.critic_body = nn.Sequential(*critic_layers)
|
||||
|
||||
print(f"Environment Factor Encoder: {self.env_factor_encoder}")
|
||||
print(f"Adaptation Module: {self.adaptation_module}")
|
||||
print(f"Actor MLP: {self.actor_body}")
|
||||
print(f"Critic MLP: {self.critic_body}")
|
||||
|
||||
# Action noise
|
||||
self.std = nn.Parameter(AC_Args.init_noise_std * torch.ones(num_actions))
|
||||
self.distribution = None
|
||||
# disable args validation for speedup
|
||||
Normal.set_default_validate_args = False
|
||||
|
||||
@staticmethod
|
||||
# not used at the moment
|
||||
def init_weights(sequential, scales):
|
||||
[torch.nn.init.orthogonal_(module.weight, gain=scales[idx]) for idx, module in
|
||||
enumerate(mod for mod in sequential if isinstance(mod, nn.Linear))]
|
||||
|
||||
def reset(self, dones=None):
|
||||
pass
|
||||
|
||||
def forward(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def action_mean(self):
|
||||
return self.distribution.mean
|
||||
|
||||
@property
|
||||
def action_std(self):
|
||||
return self.distribution.stddev
|
||||
|
||||
@property
|
||||
def entropy(self):
|
||||
return self.distribution.entropy().sum(dim=-1)
|
||||
|
||||
def update_distribution(self, observations, privileged_observations):
|
||||
latent = self.env_factor_encoder(privileged_observations)
|
||||
mean = self.actor_body(torch.cat((observations, latent), dim=-1))
|
||||
self.distribution = Normal(mean, mean * 0. + self.std)
|
||||
|
||||
def act(self, observations, privileged_observations, **kwargs):
|
||||
self.update_distribution(observations, privileged_observations)
|
||||
return self.distribution.sample()
|
||||
|
||||
def get_actions_log_prob(self, actions):
|
||||
return self.distribution.log_prob(actions).sum(dim=-1)
|
||||
|
||||
def act_expert(self, ob, policy_info={}):
|
||||
return self.act_teacher(ob["obs"], ob["privileged_obs"])
|
||||
|
||||
def act_inference(self, ob, policy_info={}):
|
||||
if ob["privileged_obs"] is not None:
|
||||
gt_latent = self.env_factor_encoder(ob["privileged_obs"])
|
||||
policy_info["gt_latents"] = gt_latent.detach().cpu().numpy()
|
||||
return self.act_student(ob["obs"], ob["obs_history"])
|
||||
|
||||
def act_student(self, observations, observation_history, policy_info={}):
|
||||
latent = self.adaptation_module(observation_history)
|
||||
actions_mean = self.actor_body(torch.cat((observations, latent), dim=-1))
|
||||
policy_info["latents"] = latent.detach().cpu().numpy()
|
||||
return actions_mean
|
||||
|
||||
def act_teacher(self, observations, privileged_info, policy_info={}):
|
||||
latent = self.env_factor_encoder(privileged_info)
|
||||
actions_mean = self.actor_body(torch.cat((observations, latent), dim=-1))
|
||||
policy_info["latents"] = latent.detach().cpu().numpy()
|
||||
return actions_mean
|
||||
|
||||
def evaluate(self, critic_observations, privileged_observations, **kwargs):
|
||||
latent = self.env_factor_encoder(privileged_observations)
|
||||
value = self.critic_body(torch.cat((critic_observations, latent), dim=-1))
|
||||
return value
|
||||
|
||||
|
||||
def get_activation(act_name):
|
||||
if act_name == "elu":
|
||||
return nn.ELU()
|
||||
elif act_name == "selu":
|
||||
return nn.SELU()
|
||||
elif act_name == "relu":
|
||||
return nn.ReLU()
|
||||
elif act_name == "crelu":
|
||||
return nn.ReLU()
|
||||
elif act_name == "lrelu":
|
||||
return nn.LeakyReLU()
|
||||
elif act_name == "tanh":
|
||||
return nn.Tanh()
|
||||
elif act_name == "sigmoid":
|
||||
return nn.Sigmoid()
|
||||
else:
|
||||
print("invalid activation function!")
|
||||
return None
|
||||
@@ -0,0 +1,88 @@
|
||||
from collections import defaultdict
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class DistCache:
|
||||
def __init__(self):
|
||||
"""
|
||||
Args:
|
||||
n: Number of slots for the cache
|
||||
"""
|
||||
self.cache = defaultdict(lambda: 0)
|
||||
|
||||
def log(self, **key_vals):
|
||||
"""
|
||||
Args:
|
||||
slots: ids for the array
|
||||
**key_vals:
|
||||
"""
|
||||
for k, v in key_vals.items():
|
||||
count = self.cache[k + '@counts'] + 1
|
||||
self.cache[k + '@counts'] = count
|
||||
self.cache[k] = v + (count - 1) * self.cache[k]
|
||||
self.cache[k] /= count
|
||||
|
||||
def get_summary(self):
|
||||
ret = {
|
||||
k: v
|
||||
for k, v in self.cache.items()
|
||||
if not k.endswith("@counts")
|
||||
}
|
||||
self.cache.clear()
|
||||
return ret
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cl = DistCache()
|
||||
lin_vel = np.ones((11, 11))
|
||||
ang_vel = np.zeros((5, 5))
|
||||
cl.log(lin_vel=lin_vel, ang_vel=ang_vel)
|
||||
lin_vel = np.zeros((11, 11))
|
||||
ang_vel = np.zeros((5, 5))
|
||||
cl.log(lin_vel=lin_vel, ang_vel=ang_vel)
|
||||
print(cl.get_summary())
|
||||
|
||||
|
||||
class SlotCache:
|
||||
def __init__(self, n):
|
||||
"""
|
||||
Args:
|
||||
n: Number of slots for the cache
|
||||
"""
|
||||
self.n = n
|
||||
self.cache = defaultdict(lambda: np.zeros([n]))
|
||||
|
||||
def log(self, slots=None, **key_vals):
|
||||
"""
|
||||
Args:
|
||||
slots: ids for the array
|
||||
**key_vals:
|
||||
"""
|
||||
if slots is None:
|
||||
slots = range(self.n)
|
||||
|
||||
for k, v in key_vals.items():
|
||||
counts = self.cache[k + '@counts'][slots] + 1
|
||||
self.cache[k + '@counts'][slots] = counts
|
||||
self.cache[k][slots] = v + (counts - 1) * self.cache[k][slots]
|
||||
self.cache[k][slots] /= counts
|
||||
|
||||
def get_summary(self):
|
||||
ret = {
|
||||
k: v
|
||||
for k, v in self.cache.items()
|
||||
if not k.endswith("@counts")
|
||||
}
|
||||
self.cache.clear()
|
||||
return ret
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cl = SlotCache(100)
|
||||
reset_env_ids = [2, 5, 6]
|
||||
lin_vel = [0.1, 0.5, 0.8]
|
||||
ang_vel = [0.4, -0.4, 0.2]
|
||||
cl.log(reset_env_ids, lin_vel=lin_vel, ang_vel=ang_vel)
|
||||
|
||||
cl.log(lin_vel=np.ones(100))
|
||||
Executable
+178
@@ -0,0 +1,178 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.optim as optim
|
||||
import torch.nn.functional as F
|
||||
import numpy as np
|
||||
from params_proto import PrefixProto
|
||||
|
||||
from go1_gym_learn.ppo import ActorCritic
|
||||
from go1_gym_learn.ppo import RolloutStorage
|
||||
from go1_gym_learn.ppo import caches
|
||||
|
||||
|
||||
class PPO_Args(PrefixProto):
|
||||
# algorithm
|
||||
value_loss_coef = 1.0
|
||||
use_clipped_value_loss = True
|
||||
clip_param = 0.2
|
||||
entropy_coef = 0.01
|
||||
num_learning_epochs = 5
|
||||
num_mini_batches = 4 # mini batch size = num_envs*nsteps / nminibatches
|
||||
learning_rate = 1.e-3 # 5.e-4
|
||||
adaptation_module_learning_rate = 1.e-3
|
||||
num_adaptation_module_substeps = 1
|
||||
schedule = 'adaptive' # could be adaptive, fixed
|
||||
gamma = 0.99
|
||||
lam = 0.95
|
||||
desired_kl = 0.01
|
||||
max_grad_norm = 1.
|
||||
|
||||
|
||||
class PPO:
|
||||
actor_critic: ActorCritic
|
||||
|
||||
def __init__(self, actor_critic, device='cpu'):
|
||||
|
||||
self.device = device
|
||||
|
||||
# PPO components
|
||||
self.actor_critic = actor_critic
|
||||
self.actor_critic.to(device)
|
||||
self.storage = None # initialized later
|
||||
self.optimizer = optim.Adam(self.actor_critic.parameters(), lr=PPO_Args.learning_rate)
|
||||
self.adaptation_module_optimizer = optim.Adam(self.actor_critic.parameters(),
|
||||
lr=PPO_Args.adaptation_module_learning_rate)
|
||||
self.transition = RolloutStorage.Transition()
|
||||
|
||||
self.learning_rate = PPO_Args.learning_rate
|
||||
|
||||
def init_storage(self, num_envs, num_transitions_per_env, actor_obs_shape, privileged_obs_shape, obs_history_shape,
|
||||
action_shape):
|
||||
self.storage = RolloutStorage(num_envs, num_transitions_per_env, actor_obs_shape, privileged_obs_shape,
|
||||
obs_history_shape, action_shape, self.device)
|
||||
|
||||
def test_mode(self):
|
||||
self.actor_critic.test()
|
||||
|
||||
def train_mode(self):
|
||||
self.actor_critic.train()
|
||||
|
||||
def act(self, obs, privileged_obs, obs_history):
|
||||
# Compute the actions and values
|
||||
self.transition.actions = self.actor_critic.act(obs, privileged_obs).detach()
|
||||
self.transition.values = self.actor_critic.evaluate(obs, privileged_obs).detach()
|
||||
self.transition.actions_log_prob = self.actor_critic.get_actions_log_prob(self.transition.actions).detach()
|
||||
self.transition.action_mean = self.actor_critic.action_mean.detach()
|
||||
self.transition.action_sigma = self.actor_critic.action_std.detach()
|
||||
# need to record obs and critic_obs before env.step()
|
||||
self.transition.observations = obs
|
||||
self.transition.critic_observations = obs
|
||||
self.transition.privileged_observations = privileged_obs
|
||||
self.transition.observation_histories = obs_history
|
||||
return self.transition.actions
|
||||
|
||||
def process_env_step(self, rewards, dones, infos):
|
||||
self.transition.rewards = rewards.clone()
|
||||
self.transition.dones = dones
|
||||
self.transition.env_bins = infos["env_bins"]
|
||||
# Bootstrapping on time outs
|
||||
if 'time_outs' in infos:
|
||||
self.transition.rewards += PPO_Args.gamma * torch.squeeze(
|
||||
self.transition.values * infos['time_outs'].unsqueeze(1).to(self.device), 1)
|
||||
|
||||
# Record the transition
|
||||
self.storage.add_transitions(self.transition)
|
||||
self.transition.clear()
|
||||
self.actor_critic.reset(dones)
|
||||
|
||||
def compute_returns(self, last_critic_obs, last_critic_privileged_obs):
|
||||
last_values = self.actor_critic.evaluate(last_critic_obs, last_critic_privileged_obs).detach()
|
||||
self.storage.compute_returns(last_values, PPO_Args.gamma, PPO_Args.lam)
|
||||
|
||||
def update(self):
|
||||
mean_value_loss = 0
|
||||
mean_surrogate_loss = 0
|
||||
mean_adaptation_module_loss = 0
|
||||
generator = self.storage.mini_batch_generator(PPO_Args.num_mini_batches, PPO_Args.num_learning_epochs)
|
||||
for obs_batch, critic_obs_batch, privileged_obs_batch, obs_history_batch, actions_batch, target_values_batch, advantages_batch, returns_batch, old_actions_log_prob_batch, \
|
||||
old_mu_batch, old_sigma_batch, masks_batch, env_bins_batch in generator:
|
||||
|
||||
self.actor_critic.act(obs_batch, privileged_obs_batch, masks=masks_batch)
|
||||
actions_log_prob_batch = self.actor_critic.get_actions_log_prob(actions_batch)
|
||||
value_batch = self.actor_critic.evaluate(critic_obs_batch, privileged_obs_batch, masks=masks_batch)
|
||||
mu_batch = self.actor_critic.action_mean
|
||||
sigma_batch = self.actor_critic.action_std
|
||||
entropy_batch = self.actor_critic.entropy
|
||||
|
||||
# KL
|
||||
if PPO_Args.desired_kl != None and PPO_Args.schedule == 'adaptive':
|
||||
with torch.inference_mode():
|
||||
kl = torch.sum(
|
||||
torch.log(sigma_batch / old_sigma_batch + 1.e-5) + (
|
||||
torch.square(old_sigma_batch) + torch.square(old_mu_batch - mu_batch)) / (
|
||||
2.0 * torch.square(sigma_batch)) - 0.5, axis=-1)
|
||||
kl_mean = torch.mean(kl)
|
||||
|
||||
if kl_mean > PPO_Args.desired_kl * 2.0:
|
||||
self.learning_rate = max(1e-5, self.learning_rate / 1.5)
|
||||
elif kl_mean < PPO_Args.desired_kl / 2.0 and kl_mean > 0.0:
|
||||
self.learning_rate = min(1e-2, self.learning_rate * 1.5)
|
||||
|
||||
for param_group in self.optimizer.param_groups:
|
||||
param_group['lr'] = self.learning_rate
|
||||
|
||||
# Surrogate loss
|
||||
ratio = torch.exp(actions_log_prob_batch - torch.squeeze(old_actions_log_prob_batch))
|
||||
surrogate = -torch.squeeze(advantages_batch) * ratio
|
||||
surrogate_clipped = -torch.squeeze(advantages_batch) * torch.clamp(ratio, 1.0 - PPO_Args.clip_param,
|
||||
1.0 + PPO_Args.clip_param)
|
||||
surrogate_loss = torch.max(surrogate, surrogate_clipped).mean()
|
||||
|
||||
# Value function loss
|
||||
if PPO_Args.use_clipped_value_loss:
|
||||
value_clipped = target_values_batch + \
|
||||
(value_batch - target_values_batch).clamp(-PPO_Args.clip_param,
|
||||
PPO_Args.clip_param)
|
||||
value_losses = (value_batch - returns_batch).pow(2)
|
||||
value_losses_clipped = (value_clipped - returns_batch).pow(2)
|
||||
value_loss = torch.max(value_losses, value_losses_clipped).mean()
|
||||
else:
|
||||
value_loss = (returns_batch - value_batch).pow(2).mean()
|
||||
|
||||
loss = surrogate_loss + PPO_Args.value_loss_coef * value_loss - PPO_Args.entropy_coef * entropy_batch.mean()
|
||||
|
||||
# Gradient step
|
||||
self.optimizer.zero_grad()
|
||||
loss.backward()
|
||||
nn.utils.clip_grad_norm_(self.actor_critic.parameters(), PPO_Args.max_grad_norm)
|
||||
self.optimizer.step()
|
||||
|
||||
mean_value_loss += value_loss.item()
|
||||
mean_surrogate_loss += surrogate_loss.item()
|
||||
|
||||
# Adaptation module gradient step
|
||||
for epoch in range(PPO_Args.num_adaptation_module_substeps):
|
||||
adaptation_pred = self.actor_critic.adaptation_module(obs_history_batch)
|
||||
with torch.no_grad():
|
||||
adaptation_target = self.actor_critic.env_factor_encoder(privileged_obs_batch)
|
||||
residual = (adaptation_target - adaptation_pred).norm(dim=1)
|
||||
caches.slot_cache.log(env_bins_batch[:, 0].cpu().numpy().astype(np.uint8),
|
||||
sysid_residual=residual.cpu().numpy())
|
||||
|
||||
adaptation_loss = F.mse_loss(adaptation_pred, adaptation_target)
|
||||
|
||||
self.adaptation_module_optimizer.zero_grad()
|
||||
adaptation_loss.backward()
|
||||
self.adaptation_module_optimizer.step()
|
||||
|
||||
mean_adaptation_module_loss += adaptation_loss.item()
|
||||
|
||||
num_updates = PPO_Args.num_learning_epochs * PPO_Args.num_mini_batches
|
||||
mean_value_loss /= num_updates
|
||||
mean_surrogate_loss /= num_updates
|
||||
mean_adaptation_module_loss /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
self.storage.clear()
|
||||
|
||||
return mean_value_loss, mean_surrogate_loss, mean_adaptation_module_loss
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
import torch
|
||||
|
||||
from go1_gym_learn.utils import split_and_pad_trajectories
|
||||
|
||||
class RolloutStorage:
|
||||
class Transition:
|
||||
def __init__(self):
|
||||
self.observations = None
|
||||
self.privileged_observations = None
|
||||
self.observation_histories = None
|
||||
self.critic_observations = None
|
||||
self.actions = None
|
||||
self.rewards = None
|
||||
self.dones = None
|
||||
self.values = None
|
||||
self.actions_log_prob = None
|
||||
self.action_mean = None
|
||||
self.action_sigma = None
|
||||
self.env_bins = None
|
||||
|
||||
def clear(self):
|
||||
self.__init__()
|
||||
|
||||
def __init__(self, num_envs, num_transitions_per_env, obs_shape, privileged_obs_shape, obs_history_shape, actions_shape, device='cpu'):
|
||||
|
||||
self.device = device
|
||||
|
||||
self.obs_shape = obs_shape
|
||||
self.privileged_obs_shape = privileged_obs_shape
|
||||
self.obs_history_shape = obs_history_shape
|
||||
self.actions_shape = actions_shape
|
||||
|
||||
# Core
|
||||
self.observations = torch.zeros(num_transitions_per_env, num_envs, *obs_shape, device=self.device)
|
||||
self.privileged_observations = torch.zeros(num_transitions_per_env, num_envs, *privileged_obs_shape, device=self.device)
|
||||
self.observation_histories = torch.zeros(num_transitions_per_env, num_envs, *obs_history_shape, device=self.device)
|
||||
self.rewards = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.actions = torch.zeros(num_transitions_per_env, num_envs, *actions_shape, device=self.device)
|
||||
self.dones = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device).byte()
|
||||
|
||||
# For PPO
|
||||
self.actions_log_prob = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.values = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.returns = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.advantages = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.mu = torch.zeros(num_transitions_per_env, num_envs, *actions_shape, device=self.device)
|
||||
self.sigma = torch.zeros(num_transitions_per_env, num_envs, *actions_shape, device=self.device)
|
||||
self.env_bins = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
|
||||
self.num_transitions_per_env = num_transitions_per_env
|
||||
self.num_envs = num_envs
|
||||
|
||||
self.step = 0
|
||||
|
||||
def add_transitions(self, transition: Transition):
|
||||
if self.step >= self.num_transitions_per_env:
|
||||
raise AssertionError("Rollout buffer overflow")
|
||||
self.observations[self.step].copy_(transition.observations)
|
||||
self.privileged_observations[self.step].copy_(transition.privileged_observations)
|
||||
self.observation_histories[self.step].copy_(transition.observation_histories)
|
||||
self.actions[self.step].copy_(transition.actions)
|
||||
self.rewards[self.step].copy_(transition.rewards.view(-1, 1))
|
||||
self.dones[self.step].copy_(transition.dones.view(-1, 1))
|
||||
self.values[self.step].copy_(transition.values)
|
||||
self.actions_log_prob[self.step].copy_(transition.actions_log_prob.view(-1, 1))
|
||||
self.mu[self.step].copy_(transition.action_mean)
|
||||
self.sigma[self.step].copy_(transition.action_sigma)
|
||||
self.env_bins[self.step].copy_(transition.env_bins.view(-1, 1))
|
||||
self.step += 1
|
||||
|
||||
def clear(self):
|
||||
self.step = 0
|
||||
|
||||
def compute_returns(self, last_values, gamma, lam):
|
||||
advantage = 0
|
||||
for step in reversed(range(self.num_transitions_per_env)):
|
||||
if step == self.num_transitions_per_env - 1:
|
||||
next_values = last_values
|
||||
else:
|
||||
next_values = self.values[step + 1]
|
||||
next_is_not_terminal = 1.0 - self.dones[step].float()
|
||||
delta = self.rewards[step] + next_is_not_terminal * gamma * next_values - self.values[step]
|
||||
advantage = delta + next_is_not_terminal * gamma * lam * advantage
|
||||
self.returns[step] = advantage + self.values[step]
|
||||
|
||||
# Compute and normalize the advantages
|
||||
self.advantages = self.returns - self.values
|
||||
self.advantages = (self.advantages - self.advantages.mean()) / (self.advantages.std() + 1e-8)
|
||||
|
||||
def get_statistics(self):
|
||||
done = self.dones
|
||||
done[-1] = 1
|
||||
flat_dones = done.permute(1, 0, 2).reshape(-1, 1)
|
||||
done_indices = torch.cat((flat_dones.new_tensor([-1], dtype=torch.int64), flat_dones.nonzero(as_tuple=False)[:, 0]))
|
||||
trajectory_lengths = (done_indices[1:] - done_indices[:-1])
|
||||
return trajectory_lengths.float().mean(), self.rewards.mean()
|
||||
|
||||
def mini_batch_generator(self, num_mini_batches, num_epochs=8):
|
||||
batch_size = self.num_envs * self.num_transitions_per_env
|
||||
mini_batch_size = batch_size // num_mini_batches
|
||||
indices = torch.randperm(num_mini_batches*mini_batch_size, requires_grad=False, device=self.device)
|
||||
|
||||
observations = self.observations.flatten(0, 1)
|
||||
privileged_obs = self.privileged_observations.flatten(0, 1)
|
||||
obs_history = self.observation_histories.flatten(0, 1)
|
||||
critic_observations = observations
|
||||
|
||||
actions = self.actions.flatten(0, 1)
|
||||
values = self.values.flatten(0, 1)
|
||||
returns = self.returns.flatten(0, 1)
|
||||
old_actions_log_prob = self.actions_log_prob.flatten(0, 1)
|
||||
advantages = self.advantages.flatten(0, 1)
|
||||
old_mu = self.mu.flatten(0, 1)
|
||||
old_sigma = self.sigma.flatten(0, 1)
|
||||
old_env_bins = self.env_bins.flatten(0, 1)
|
||||
|
||||
for epoch in range(num_epochs):
|
||||
for i in range(num_mini_batches):
|
||||
|
||||
start = i*mini_batch_size
|
||||
end = (i+1)*mini_batch_size
|
||||
batch_idx = indices[start:end]
|
||||
|
||||
obs_batch = observations[batch_idx]
|
||||
critic_observations_batch = critic_observations[batch_idx]
|
||||
privileged_obs_batch = privileged_obs[batch_idx]
|
||||
obs_history_batch = obs_history[batch_idx]
|
||||
actions_batch = actions[batch_idx]
|
||||
target_values_batch = values[batch_idx]
|
||||
returns_batch = returns[batch_idx]
|
||||
old_actions_log_prob_batch = old_actions_log_prob[batch_idx]
|
||||
advantages_batch = advantages[batch_idx]
|
||||
old_mu_batch = old_mu[batch_idx]
|
||||
old_sigma_batch = old_sigma[batch_idx]
|
||||
env_bins_batch = old_env_bins[batch_idx]
|
||||
yield obs_batch, critic_observations_batch, privileged_obs_batch, obs_history_batch, actions_batch, target_values_batch, advantages_batch, returns_batch, \
|
||||
old_actions_log_prob_batch, old_mu_batch, old_sigma_batch, None, env_bins_batch
|
||||
|
||||
# for RNNs only
|
||||
def reccurent_mini_batch_generator(self, num_mini_batches, num_epochs=8):
|
||||
|
||||
padded_obs_trajectories, trajectory_masks = split_and_pad_trajectories(self.observations, self.dones)
|
||||
padded_privileged_obs_trajectories, trajectory_masks = split_and_pad_trajectories(self.privileged_observations, self.dones)
|
||||
padded_obs_history_trajectories, trajectory_masks = split_and_pad_trajectories(self.observation_histories, self.dones)
|
||||
padded_critic_obs_trajectories = padded_obs_trajectories
|
||||
|
||||
mini_batch_size = self.num_envs // num_mini_batches
|
||||
for ep in range(num_epochs):
|
||||
first_traj = 0
|
||||
for i in range(num_mini_batches):
|
||||
start = i*mini_batch_size
|
||||
stop = (i+1)*mini_batch_size
|
||||
|
||||
dones = self.dones.squeeze(-1)
|
||||
last_was_done = torch.zeros_like(dones, dtype=torch.bool)
|
||||
last_was_done[1:] = dones[:-1]
|
||||
last_was_done[0] = True
|
||||
trajectories_batch_size = torch.sum(last_was_done[:, start:stop])
|
||||
last_traj = first_traj + trajectories_batch_size
|
||||
|
||||
masks_batch = trajectory_masks[:, first_traj:last_traj]
|
||||
obs_batch = padded_obs_trajectories[:, first_traj:last_traj]
|
||||
critic_obs_batch = padded_critic_obs_trajectories[:, first_traj:last_traj]
|
||||
privileged_obs_batch = padded_privileged_obs_trajectories[:, first_traj:last_traj]
|
||||
obs_history_batch = padded_obs_history_trajectories[:, first_traj:last_traj]
|
||||
|
||||
actions_batch = self.actions[:, start:stop]
|
||||
old_mu_batch = self.mu[:, start:stop]
|
||||
old_sigma_batch = self.sigma[:, start:stop]
|
||||
returns_batch = self.returns[:, start:stop]
|
||||
advantages_batch = self.advantages[:, start:stop]
|
||||
values_batch = self.values[:, start:stop]
|
||||
old_actions_log_prob_batch = self.actions_log_prob[:, start:stop]
|
||||
|
||||
yield obs_batch, critic_obs_batch, privileged_obs_batch, obs_history_batch, actions_batch, values_batch, advantages_batch, returns_batch, \
|
||||
old_actions_log_prob_batch, old_mu_batch, old_sigma_batch, masks_batch
|
||||
|
||||
first_traj = last_traj
|
||||
Executable
+308
@@ -0,0 +1,308 @@
|
||||
import time
|
||||
from collections import deque
|
||||
import copy
|
||||
import os
|
||||
|
||||
import torch
|
||||
from ml_logger import logger
|
||||
from params_proto import PrefixProto
|
||||
|
||||
from .actor_critic import ActorCritic
|
||||
from .rollout_storage import RolloutStorage
|
||||
|
||||
|
||||
def class_to_dict(obj) -> dict:
|
||||
if not hasattr(obj, "__dict__"):
|
||||
return obj
|
||||
result = {}
|
||||
for key in dir(obj):
|
||||
if key.startswith("_") or key == "terrain":
|
||||
continue
|
||||
element = []
|
||||
val = getattr(obj, key)
|
||||
if isinstance(val, list):
|
||||
for item in val:
|
||||
element.append(class_to_dict(item))
|
||||
else:
|
||||
element = class_to_dict(val)
|
||||
result[key] = element
|
||||
return result
|
||||
|
||||
|
||||
class DataCaches:
|
||||
def __init__(self, curriculum_bins):
|
||||
from go1_gym_learn.ppo.metrics_caches import SlotCache, DistCache
|
||||
|
||||
self.slot_cache = SlotCache(curriculum_bins)
|
||||
self.dist_cache = DistCache()
|
||||
|
||||
|
||||
caches = DataCaches(1)
|
||||
|
||||
|
||||
class RunnerArgs(PrefixProto, cli=False):
|
||||
# runner
|
||||
algorithm_class_name = 'RMA'
|
||||
num_steps_per_env = 24 # per iteration
|
||||
max_iterations = 1500 # number of policy updates
|
||||
|
||||
# logging
|
||||
save_interval = 400 # check for potential saves every this many iterations
|
||||
save_video_interval = 100
|
||||
log_freq = 10
|
||||
|
||||
# load and resume
|
||||
resume = False
|
||||
load_run = -1 # -1 = last run
|
||||
checkpoint = -1 # -1 = last saved model
|
||||
resume_path = None # updated from load_run and chkpt
|
||||
resume_curriculum = True
|
||||
|
||||
|
||||
class Runner:
|
||||
|
||||
def __init__(self, env, device='cpu'):
|
||||
from .ppo import PPO
|
||||
|
||||
self.device = device
|
||||
self.env = env
|
||||
|
||||
actor_critic = ActorCritic(self.env.num_obs,
|
||||
self.env.num_privileged_obs,
|
||||
self.env.num_obs_history,
|
||||
self.env.num_actions,
|
||||
).to(self.device)
|
||||
|
||||
if RunnerArgs.resume:
|
||||
# load pretrained weights from resume_path
|
||||
from ml_logger import ML_Logger
|
||||
loader = ML_Logger(root="http://escher.csail.mit.edu:8080",
|
||||
prefix=RunnerArgs.resume_path)
|
||||
weights = loader.load_torch("checkpoints/ac_weights_last.pt")
|
||||
actor_critic.load_state_dict(state_dict=weights)
|
||||
|
||||
if hasattr(self.env, "curricula") and RunnerArgs.resume_curriculum:
|
||||
# load curriculum state
|
||||
distributions = loader.load_pkl("curriculum/distribution.pkl")
|
||||
distribution_last = distributions[-1]["distribution"]
|
||||
gait_names = [key[8:] if key.startswith("weights_") else None for key in distribution_last.keys()]
|
||||
for gait_id, gait_name in enumerate(self.env.category_names):
|
||||
self.env.curricula[gait_id].weights = distribution_last[f"weights_{gait_name}"]
|
||||
print(gait_name)
|
||||
|
||||
self.alg = PPO(actor_critic, device=self.device)
|
||||
self.num_steps_per_env = RunnerArgs.num_steps_per_env
|
||||
|
||||
# init storage and model
|
||||
self.alg.init_storage(self.env.num_train_envs, self.num_steps_per_env, [self.env.num_obs],
|
||||
[self.env.num_privileged_obs], [self.env.num_obs_history], [self.env.num_actions])
|
||||
|
||||
self.tot_timesteps = 0
|
||||
self.tot_time = 0
|
||||
self.current_learning_iteration = 0
|
||||
self.last_recording_it = 0
|
||||
|
||||
self.env.reset()
|
||||
|
||||
def learn(self, num_learning_iterations, init_at_random_ep_len=False, eval_freq=100, curriculum_dump_freq=500, eval_expert=False):
|
||||
from ml_logger import logger
|
||||
# initialize writer
|
||||
assert logger.prefix, "you will overwrite the entire instrument server"
|
||||
|
||||
logger.start('start', 'epoch', 'episode', 'run', 'step')
|
||||
|
||||
if init_at_random_ep_len:
|
||||
self.env.episode_length_buf = torch.randint_like(self.env.episode_length_buf,
|
||||
high=int(self.env.max_episode_length))
|
||||
|
||||
# split train and test envs
|
||||
num_train_envs = self.env.num_train_envs
|
||||
|
||||
obs_dict = self.env.get_observations() # TODO: check, is this correct on the first step?
|
||||
obs, privileged_obs, obs_history = obs_dict["obs"], obs_dict["privileged_obs"], obs_dict["obs_history"]
|
||||
obs, privileged_obs, obs_history = obs.to(self.device), privileged_obs.to(self.device), obs_history.to(
|
||||
self.device)
|
||||
self.alg.actor_critic.train() # switch to train mode (for dropout for example)
|
||||
|
||||
rewbuffer = deque(maxlen=100)
|
||||
lenbuffer = deque(maxlen=100)
|
||||
rewbuffer_eval = deque(maxlen=100)
|
||||
lenbuffer_eval = deque(maxlen=100)
|
||||
cur_reward_sum = torch.zeros(self.env.num_envs, dtype=torch.float, device=self.device)
|
||||
cur_episode_length = torch.zeros(self.env.num_envs, dtype=torch.float, device=self.device)
|
||||
|
||||
tot_iter = self.current_learning_iteration + num_learning_iterations
|
||||
for it in range(self.current_learning_iteration, tot_iter):
|
||||
start = time.time()
|
||||
# Rollout
|
||||
with torch.inference_mode():
|
||||
for i in range(self.num_steps_per_env):
|
||||
actions_train = self.alg.act(obs[:num_train_envs], privileged_obs[:num_train_envs],
|
||||
obs_history[:num_train_envs])
|
||||
if eval_expert:
|
||||
actions_eval = self.alg.actor_critic.act_teacher(obs_history[num_train_envs:],
|
||||
privileged_obs[num_train_envs:])
|
||||
else:
|
||||
actions_eval = self.alg.actor_critic.act_student(obs_history[num_train_envs:])
|
||||
ret = self.env.step(torch.cat((actions_train, actions_eval), dim=0))
|
||||
obs_dict, rewards, dones, infos = ret
|
||||
obs, privileged_obs, obs_history = obs_dict["obs"], obs_dict["privileged_obs"], obs_dict[
|
||||
"obs_history"]
|
||||
|
||||
obs, privileged_obs, obs_history, rewards, dones = obs.to(self.device), privileged_obs.to(
|
||||
self.device), obs_history.to(self.device), rewards.to(self.device), dones.to(self.device)
|
||||
self.alg.process_env_step(rewards[:num_train_envs], dones[:num_train_envs], infos)
|
||||
|
||||
if 'train/episode' in infos:
|
||||
with logger.Prefix(metrics="train/episode"):
|
||||
logger.store_metrics(**infos['train/episode'])
|
||||
|
||||
if 'eval/episode' in infos:
|
||||
with logger.Prefix(metrics="eval/episode"):
|
||||
logger.store_metrics(**infos['eval/episode'])
|
||||
|
||||
if 'curriculum' in infos:
|
||||
|
||||
cur_reward_sum += rewards
|
||||
cur_episode_length += 1
|
||||
|
||||
new_ids = (dones > 0).nonzero(as_tuple=False)
|
||||
|
||||
new_ids_train = new_ids[new_ids < num_train_envs]
|
||||
rewbuffer.extend(cur_reward_sum[new_ids_train].cpu().numpy().tolist())
|
||||
lenbuffer.extend(cur_episode_length[new_ids_train].cpu().numpy().tolist())
|
||||
cur_reward_sum[new_ids_train] = 0
|
||||
cur_episode_length[new_ids_train] = 0
|
||||
|
||||
new_ids_eval = new_ids[new_ids >= num_train_envs]
|
||||
rewbuffer_eval.extend(cur_reward_sum[new_ids_eval].cpu().numpy().tolist())
|
||||
lenbuffer_eval.extend(cur_episode_length[new_ids_eval].cpu().numpy().tolist())
|
||||
cur_reward_sum[new_ids_eval] = 0
|
||||
cur_episode_length[new_ids_eval] = 0
|
||||
|
||||
if 'curriculum/distribution' in infos:
|
||||
distribution = infos['curriculum/distribution']
|
||||
|
||||
stop = time.time()
|
||||
collection_time = stop - start
|
||||
|
||||
# Learning step
|
||||
start = stop
|
||||
self.alg.compute_returns(obs_history[:num_train_envs], privileged_obs[:num_train_envs])
|
||||
|
||||
if it % curriculum_dump_freq == 0:
|
||||
logger.save_pkl({"iteration": it,
|
||||
**caches.slot_cache.get_summary(),
|
||||
**caches.dist_cache.get_summary()},
|
||||
path=f"curriculum/info.pkl", append=True)
|
||||
|
||||
if 'curriculum/distribution' in infos:
|
||||
logger.save_pkl({"iteration": it,
|
||||
"distribution": distribution},
|
||||
path=f"curriculum/distribution.pkl", append=True)
|
||||
|
||||
mean_value_loss, mean_surrogate_loss, mean_adaptation_module_loss, mean_decoder_loss, mean_decoder_loss_student, mean_adaptation_module_test_loss, mean_decoder_test_loss, mean_decoder_test_loss_student = self.alg.update()
|
||||
stop = time.time()
|
||||
learn_time = stop - start
|
||||
|
||||
logger.store_metrics(
|
||||
# total_time=learn_time - collection_time,
|
||||
time_elapsed=logger.since('start'),
|
||||
time_iter=logger.split('epoch'),
|
||||
adaptation_loss=mean_adaptation_module_loss,
|
||||
mean_value_loss=mean_value_loss,
|
||||
mean_surrogate_loss=mean_surrogate_loss,
|
||||
mean_decoder_loss=mean_decoder_loss,
|
||||
mean_decoder_loss_student=mean_decoder_loss_student,
|
||||
mean_decoder_test_loss=mean_decoder_test_loss,
|
||||
mean_decoder_test_loss_student=mean_decoder_test_loss_student,
|
||||
mean_adaptation_module_test_loss=mean_adaptation_module_test_loss
|
||||
)
|
||||
|
||||
if RunnerArgs.save_video_interval:
|
||||
self.log_video(it)
|
||||
|
||||
self.tot_timesteps += self.num_steps_per_env * self.env.num_envs
|
||||
if logger.every(RunnerArgs.log_freq, "iteration", start_on=1):
|
||||
# if it % Config.log_freq == 0:
|
||||
logger.log_metrics_summary(key_values={"timesteps": self.tot_timesteps, "iterations": it})
|
||||
logger.job_running()
|
||||
|
||||
if it % RunnerArgs.save_interval == 0:
|
||||
with logger.Sync():
|
||||
logger.torch_save(self.alg.actor_critic.state_dict(), f"checkpoints/ac_weights_{it:06d}.pt")
|
||||
logger.duplicate(f"checkpoints/ac_weights_{it:06d}.pt", f"checkpoints/ac_weights_last.pt")
|
||||
|
||||
path = './tmp/legged_data'
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
adaptation_module_path = f'{path}/adaptation_module_latest.jit'
|
||||
adaptation_module = copy.deepcopy(self.alg.actor_critic.adaptation_module).to('cpu')
|
||||
traced_script_adaptation_module = torch.jit.script(adaptation_module)
|
||||
traced_script_adaptation_module.save(adaptation_module_path)
|
||||
|
||||
body_path = f'{path}/body_latest.jit'
|
||||
body_model = copy.deepcopy(self.alg.actor_critic.actor_body).to('cpu')
|
||||
traced_script_body_module = torch.jit.script(body_model)
|
||||
traced_script_body_module.save(body_path)
|
||||
|
||||
logger.upload_file(file_path=adaptation_module_path, target_path=f"checkpoints/", once=False)
|
||||
logger.upload_file(file_path=body_path, target_path=f"checkpoints/", once=False)
|
||||
|
||||
self.current_learning_iteration += num_learning_iterations
|
||||
|
||||
with logger.Sync():
|
||||
logger.torch_save(self.alg.actor_critic.state_dict(), f"checkpoints/ac_weights_{it:06d}.pt")
|
||||
logger.duplicate(f"checkpoints/ac_weights_{it:06d}.pt", f"checkpoints/ac_weights_last.pt")
|
||||
|
||||
path = './tmp/legged_data'
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
adaptation_module_path = f'{path}/adaptation_module_latest.jit'
|
||||
adaptation_module = copy.deepcopy(self.alg.actor_critic.adaptation_module).to('cpu')
|
||||
traced_script_adaptation_module = torch.jit.script(adaptation_module)
|
||||
traced_script_adaptation_module.save(adaptation_module_path)
|
||||
|
||||
body_path = f'{path}/body_latest.jit'
|
||||
body_model = copy.deepcopy(self.alg.actor_critic.actor_body).to('cpu')
|
||||
traced_script_body_module = torch.jit.script(body_model)
|
||||
traced_script_body_module.save(body_path)
|
||||
|
||||
logger.upload_file(file_path=adaptation_module_path, target_path=f"checkpoints/", once=False)
|
||||
logger.upload_file(file_path=body_path, target_path=f"checkpoints/", once=False)
|
||||
|
||||
|
||||
def log_video(self, it):
|
||||
if it - self.last_recording_it >= RunnerArgs.save_video_interval:
|
||||
self.env.start_recording()
|
||||
if self.env.num_eval_envs > 0:
|
||||
self.env.start_recording_eval()
|
||||
print("START RECORDING")
|
||||
self.last_recording_it = it
|
||||
|
||||
frames = self.env.get_complete_frames()
|
||||
if len(frames) > 0:
|
||||
self.env.pause_recording()
|
||||
print("LOGGING VIDEO")
|
||||
logger.save_video(frames, f"videos/{it:05d}.mp4", fps=1 / self.env.dt)
|
||||
|
||||
if self.env.num_eval_envs > 0:
|
||||
frames = self.env.get_complete_frames_eval()
|
||||
if len(frames) > 0:
|
||||
self.env.pause_recording_eval()
|
||||
print("LOGGING EVAL VIDEO")
|
||||
logger.save_video(frames, f"videos/{it:05d}_eval.mp4", fps=1 / self.env.dt)
|
||||
|
||||
def get_inference_policy(self, device=None):
|
||||
self.alg.actor_critic.eval() # switch to evaluation mode (dropout for example)
|
||||
if device is not None:
|
||||
self.alg.actor_critic.to(device)
|
||||
return self.alg.actor_critic.act_inference
|
||||
|
||||
def get_expert_policy(self, device=None):
|
||||
self.alg.actor_critic.eval() # switch to evaluation mode (dropout for example)
|
||||
if device is not None:
|
||||
self.alg.actor_critic.to(device)
|
||||
return self.alg.actor_critic.act_expert
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from params_proto import PrefixProto
|
||||
from torch.distributions import Normal
|
||||
|
||||
|
||||
class AC_Args(PrefixProto, cli=False):
|
||||
# policy
|
||||
init_noise_std = 1.0
|
||||
actor_hidden_dims = [512, 256, 128]
|
||||
critic_hidden_dims = [512, 256, 128]
|
||||
activation = 'elu' # can be elu, relu, selu, crelu, lrelu, tanh, sigmoid
|
||||
|
||||
adaptation_module_branch_hidden_dims = [256, 128]
|
||||
|
||||
use_decoder = False
|
||||
|
||||
|
||||
class ActorCritic(nn.Module):
|
||||
is_recurrent = False
|
||||
|
||||
def __init__(self, num_obs,
|
||||
num_privileged_obs,
|
||||
num_obs_history,
|
||||
num_actions,
|
||||
**kwargs):
|
||||
if kwargs:
|
||||
print("ActorCritic.__init__ got unexpected arguments, which will be ignored: " + str(
|
||||
[key for key in kwargs.keys()]))
|
||||
self.decoder = AC_Args.use_decoder
|
||||
super().__init__()
|
||||
|
||||
self.num_obs_history = num_obs_history
|
||||
self.num_privileged_obs = num_privileged_obs
|
||||
|
||||
activation = get_activation(AC_Args.activation)
|
||||
|
||||
# Adaptation module
|
||||
adaptation_module_layers = []
|
||||
adaptation_module_layers.append(nn.Linear(self.num_obs_history, AC_Args.adaptation_module_branch_hidden_dims[0]))
|
||||
adaptation_module_layers.append(activation)
|
||||
for l in range(len(AC_Args.adaptation_module_branch_hidden_dims)):
|
||||
if l == len(AC_Args.adaptation_module_branch_hidden_dims) - 1:
|
||||
adaptation_module_layers.append(
|
||||
nn.Linear(AC_Args.adaptation_module_branch_hidden_dims[l], self.num_privileged_obs))
|
||||
else:
|
||||
adaptation_module_layers.append(
|
||||
nn.Linear(AC_Args.adaptation_module_branch_hidden_dims[l],
|
||||
AC_Args.adaptation_module_branch_hidden_dims[l + 1]))
|
||||
adaptation_module_layers.append(activation)
|
||||
self.adaptation_module = nn.Sequential(*adaptation_module_layers)
|
||||
|
||||
|
||||
|
||||
# Policy
|
||||
actor_layers = []
|
||||
actor_layers.append(nn.Linear(self.num_privileged_obs + self.num_obs_history, AC_Args.actor_hidden_dims[0]))
|
||||
actor_layers.append(activation)
|
||||
for l in range(len(AC_Args.actor_hidden_dims)):
|
||||
if l == len(AC_Args.actor_hidden_dims) - 1:
|
||||
actor_layers.append(nn.Linear(AC_Args.actor_hidden_dims[l], num_actions))
|
||||
else:
|
||||
actor_layers.append(nn.Linear(AC_Args.actor_hidden_dims[l], AC_Args.actor_hidden_dims[l + 1]))
|
||||
actor_layers.append(activation)
|
||||
self.actor_body = nn.Sequential(*actor_layers)
|
||||
|
||||
# Value function
|
||||
critic_layers = []
|
||||
critic_layers.append(nn.Linear(self.num_privileged_obs + self.num_obs_history, AC_Args.critic_hidden_dims[0]))
|
||||
critic_layers.append(activation)
|
||||
for l in range(len(AC_Args.critic_hidden_dims)):
|
||||
if l == len(AC_Args.critic_hidden_dims) - 1:
|
||||
critic_layers.append(nn.Linear(AC_Args.critic_hidden_dims[l], 1))
|
||||
else:
|
||||
critic_layers.append(nn.Linear(AC_Args.critic_hidden_dims[l], AC_Args.critic_hidden_dims[l + 1]))
|
||||
critic_layers.append(activation)
|
||||
self.critic_body = nn.Sequential(*critic_layers)
|
||||
|
||||
print(f"Adaptation Module: {self.adaptation_module}")
|
||||
print(f"Actor MLP: {self.actor_body}")
|
||||
print(f"Critic MLP: {self.critic_body}")
|
||||
|
||||
# Action noise
|
||||
self.std = nn.Parameter(AC_Args.init_noise_std * torch.ones(num_actions))
|
||||
self.distribution = None
|
||||
# disable args validation for speedup
|
||||
Normal.set_default_validate_args = False
|
||||
|
||||
@staticmethod
|
||||
# not used at the moment
|
||||
def init_weights(sequential, scales):
|
||||
[torch.nn.init.orthogonal_(module.weight, gain=scales[idx]) for idx, module in
|
||||
enumerate(mod for mod in sequential if isinstance(mod, nn.Linear))]
|
||||
|
||||
def reset(self, dones=None):
|
||||
pass
|
||||
|
||||
def forward(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def action_mean(self):
|
||||
return self.distribution.mean
|
||||
|
||||
@property
|
||||
def action_std(self):
|
||||
return self.distribution.stddev
|
||||
|
||||
@property
|
||||
def entropy(self):
|
||||
return self.distribution.entropy().sum(dim=-1)
|
||||
|
||||
def update_distribution(self, observation_history):
|
||||
latent = self.adaptation_module(observation_history)
|
||||
mean = self.actor_body(torch.cat((observation_history, latent), dim=-1))
|
||||
self.distribution = Normal(mean, mean * 0. + self.std)
|
||||
|
||||
def act(self, observation_history, **kwargs):
|
||||
self.update_distribution(observation_history)
|
||||
return self.distribution.sample()
|
||||
|
||||
def get_actions_log_prob(self, actions):
|
||||
return self.distribution.log_prob(actions).sum(dim=-1)
|
||||
|
||||
def act_expert(self, ob, policy_info={}):
|
||||
return self.act_teacher(ob["obs_history"], ob["privileged_obs"])
|
||||
|
||||
def act_inference(self, ob, policy_info={}):
|
||||
return self.act_student(ob["obs_history"], policy_info=policy_info)
|
||||
|
||||
def act_student(self, observation_history, policy_info={}):
|
||||
latent = self.adaptation_module(observation_history)
|
||||
actions_mean = self.actor_body(torch.cat((observation_history, latent), dim=-1))
|
||||
policy_info["latents"] = latent.detach().cpu().numpy()
|
||||
return actions_mean
|
||||
|
||||
def act_teacher(self, observation_history, privileged_info, policy_info={}):
|
||||
actions_mean = self.actor_body(torch.cat((observation_history, privileged_info), dim=-1))
|
||||
policy_info["latents"] = privileged_info
|
||||
return actions_mean
|
||||
|
||||
def evaluate(self, observation_history, privileged_observations, **kwargs):
|
||||
value = self.critic_body(torch.cat((observation_history, privileged_observations), dim=-1))
|
||||
return value
|
||||
|
||||
def get_student_latent(self, observation_history):
|
||||
return self.adaptation_module(observation_history)
|
||||
|
||||
def get_activation(act_name):
|
||||
if act_name == "elu":
|
||||
return nn.ELU()
|
||||
elif act_name == "selu":
|
||||
return nn.SELU()
|
||||
elif act_name == "relu":
|
||||
return nn.ReLU()
|
||||
elif act_name == "crelu":
|
||||
return nn.ReLU()
|
||||
elif act_name == "lrelu":
|
||||
return nn.LeakyReLU()
|
||||
elif act_name == "tanh":
|
||||
return nn.Tanh()
|
||||
elif act_name == "sigmoid":
|
||||
return nn.Sigmoid()
|
||||
else:
|
||||
print("invalid activation function!")
|
||||
return None
|
||||
@@ -0,0 +1,90 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from ml_logger import logger
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
class DistCache:
|
||||
def __init__(self):
|
||||
"""
|
||||
Args:
|
||||
n: Number of slots for the cache
|
||||
"""
|
||||
self.cache = defaultdict(lambda: 0)
|
||||
|
||||
def log(self, **key_vals):
|
||||
"""
|
||||
Args:
|
||||
slots: ids for the array
|
||||
**key_vals:
|
||||
"""
|
||||
for k, v in key_vals.items():
|
||||
count = self.cache[k + '@counts'] + 1
|
||||
self.cache[k + '@counts'] = count
|
||||
self.cache[k] = v + (count - 1) * self.cache[k]
|
||||
self.cache[k] /= count
|
||||
|
||||
def get_summary(self):
|
||||
ret = {
|
||||
k: v
|
||||
for k, v in self.cache.items()
|
||||
if not k.endswith("@counts")
|
||||
}
|
||||
self.cache.clear()
|
||||
return ret
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cl = DistCache()
|
||||
lin_vel = np.ones((11, 11))
|
||||
ang_vel = np.zeros((5, 5))
|
||||
cl.log(lin_vel=lin_vel, ang_vel=ang_vel)
|
||||
lin_vel = np.zeros((11, 11))
|
||||
ang_vel = np.zeros((5, 5))
|
||||
cl.log(lin_vel=lin_vel, ang_vel=ang_vel)
|
||||
print(cl.get_summary())
|
||||
|
||||
|
||||
class SlotCache:
|
||||
def __init__(self, n):
|
||||
"""
|
||||
Args:
|
||||
n: Number of slots for the cache
|
||||
"""
|
||||
self.n = n
|
||||
self.cache = defaultdict(lambda: np.zeros([n]))
|
||||
|
||||
def log(self, slots=None, **key_vals):
|
||||
"""
|
||||
Args:
|
||||
slots: ids for the array
|
||||
**key_vals:
|
||||
"""
|
||||
if slots is None:
|
||||
slots = range(self.n)
|
||||
|
||||
for k, v in key_vals.items():
|
||||
counts = self.cache[k + '@counts'][slots] + 1
|
||||
self.cache[k + '@counts'][slots] = counts
|
||||
self.cache[k][slots] = v + (counts - 1) * self.cache[k][slots]
|
||||
self.cache[k][slots] /= counts
|
||||
|
||||
def get_summary(self):
|
||||
ret = {
|
||||
k: v
|
||||
for k, v in self.cache.items()
|
||||
if not k.endswith("@counts")
|
||||
}
|
||||
self.cache.clear()
|
||||
return ret
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cl = SlotCache(100)
|
||||
reset_env_ids = [2, 5, 6]
|
||||
lin_vel = [0.1, 0.5, 0.8]
|
||||
ang_vel = [0.4, -0.4, 0.2]
|
||||
cl.log(reset_env_ids, lin_vel=lin_vel, ang_vel=ang_vel)
|
||||
|
||||
cl.log(lin_vel=np.ones(100))
|
||||
Executable
+205
@@ -0,0 +1,205 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.optim as optim
|
||||
import torch.nn.functional as F
|
||||
import numpy as np
|
||||
from params_proto import PrefixProto
|
||||
|
||||
from go1_gym_learn.ppo_cse import ActorCritic
|
||||
from go1_gym_learn.ppo_cse import RolloutStorage
|
||||
from go1_gym_learn.ppo_cse import caches
|
||||
|
||||
|
||||
class PPO_Args(PrefixProto):
|
||||
# algorithm
|
||||
value_loss_coef = 1.0
|
||||
use_clipped_value_loss = True
|
||||
clip_param = 0.2
|
||||
entropy_coef = 0.01
|
||||
num_learning_epochs = 5
|
||||
num_mini_batches = 4 # mini batch size = num_envs*nsteps / nminibatches
|
||||
learning_rate = 1.e-3 # 5.e-4
|
||||
adaptation_module_learning_rate = 1.e-3
|
||||
num_adaptation_module_substeps = 1
|
||||
schedule = 'adaptive' # could be adaptive, fixed
|
||||
gamma = 0.99
|
||||
lam = 0.95
|
||||
desired_kl = 0.01
|
||||
max_grad_norm = 1.
|
||||
|
||||
selective_adaptation_module_loss = False
|
||||
|
||||
|
||||
class PPO:
|
||||
actor_critic: ActorCritic
|
||||
|
||||
def __init__(self, actor_critic, device='cpu'):
|
||||
|
||||
self.device = device
|
||||
|
||||
# PPO components
|
||||
self.actor_critic = actor_critic
|
||||
self.actor_critic.to(device)
|
||||
self.storage = None # initialized later
|
||||
self.optimizer = optim.Adam(self.actor_critic.parameters(), lr=PPO_Args.learning_rate)
|
||||
self.adaptation_module_optimizer = optim.Adam(self.actor_critic.parameters(),
|
||||
lr=PPO_Args.adaptation_module_learning_rate)
|
||||
if self.actor_critic.decoder:
|
||||
self.decoder_optimizer = optim.Adam(self.actor_critic.parameters(),
|
||||
lr=PPO_Args.adaptation_module_learning_rate)
|
||||
self.transition = RolloutStorage.Transition()
|
||||
|
||||
self.learning_rate = PPO_Args.learning_rate
|
||||
|
||||
def init_storage(self, num_envs, num_transitions_per_env, actor_obs_shape, privileged_obs_shape, obs_history_shape,
|
||||
action_shape):
|
||||
self.storage = RolloutStorage(num_envs, num_transitions_per_env, actor_obs_shape, privileged_obs_shape,
|
||||
obs_history_shape, action_shape, self.device)
|
||||
|
||||
def test_mode(self):
|
||||
self.actor_critic.test()
|
||||
|
||||
def train_mode(self):
|
||||
self.actor_critic.train()
|
||||
|
||||
def act(self, obs, privileged_obs, obs_history):
|
||||
# Compute the actions and values
|
||||
self.transition.actions = self.actor_critic.act(obs_history).detach()
|
||||
self.transition.values = self.actor_critic.evaluate(obs_history, privileged_obs).detach()
|
||||
self.transition.actions_log_prob = self.actor_critic.get_actions_log_prob(self.transition.actions).detach()
|
||||
self.transition.action_mean = self.actor_critic.action_mean.detach()
|
||||
self.transition.action_sigma = self.actor_critic.action_std.detach()
|
||||
# need to record obs and critic_obs before env.step()
|
||||
self.transition.observations = obs
|
||||
self.transition.critic_observations = obs
|
||||
self.transition.privileged_observations = privileged_obs
|
||||
self.transition.observation_histories = obs_history
|
||||
return self.transition.actions
|
||||
|
||||
def process_env_step(self, rewards, dones, infos):
|
||||
self.transition.rewards = rewards.clone()
|
||||
self.transition.dones = dones
|
||||
self.transition.env_bins = infos["env_bins"]
|
||||
# Bootstrapping on time outs
|
||||
if 'time_outs' in infos:
|
||||
self.transition.rewards += PPO_Args.gamma * torch.squeeze(
|
||||
self.transition.values * infos['time_outs'].unsqueeze(1).to(self.device), 1)
|
||||
|
||||
# Record the transition
|
||||
self.storage.add_transitions(self.transition)
|
||||
self.transition.clear()
|
||||
self.actor_critic.reset(dones)
|
||||
|
||||
def compute_returns(self, last_critic_obs, last_critic_privileged_obs):
|
||||
last_values = self.actor_critic.evaluate(last_critic_obs, last_critic_privileged_obs).detach()
|
||||
self.storage.compute_returns(last_values, PPO_Args.gamma, PPO_Args.lam)
|
||||
|
||||
def update(self):
|
||||
mean_value_loss = 0
|
||||
mean_surrogate_loss = 0
|
||||
mean_adaptation_module_loss = 0
|
||||
mean_decoder_loss = 0
|
||||
mean_decoder_loss_student = 0
|
||||
mean_adaptation_module_test_loss = 0
|
||||
mean_decoder_test_loss = 0
|
||||
mean_decoder_test_loss_student = 0
|
||||
generator = self.storage.mini_batch_generator(PPO_Args.num_mini_batches, PPO_Args.num_learning_epochs)
|
||||
for obs_batch, critic_obs_batch, privileged_obs_batch, obs_history_batch, actions_batch, target_values_batch, advantages_batch, returns_batch, old_actions_log_prob_batch, \
|
||||
old_mu_batch, old_sigma_batch, masks_batch, env_bins_batch in generator:
|
||||
|
||||
self.actor_critic.act(obs_history_batch, masks=masks_batch)
|
||||
actions_log_prob_batch = self.actor_critic.get_actions_log_prob(actions_batch)
|
||||
value_batch = self.actor_critic.evaluate(obs_history_batch, privileged_obs_batch, masks=masks_batch)
|
||||
mu_batch = self.actor_critic.action_mean
|
||||
sigma_batch = self.actor_critic.action_std
|
||||
entropy_batch = self.actor_critic.entropy
|
||||
|
||||
# KL
|
||||
if PPO_Args.desired_kl != None and PPO_Args.schedule == 'adaptive':
|
||||
with torch.inference_mode():
|
||||
kl = torch.sum(
|
||||
torch.log(sigma_batch / old_sigma_batch + 1.e-5) + (
|
||||
torch.square(old_sigma_batch) + torch.square(old_mu_batch - mu_batch)) / (
|
||||
2.0 * torch.square(sigma_batch)) - 0.5, axis=-1)
|
||||
kl_mean = torch.mean(kl)
|
||||
|
||||
if kl_mean > PPO_Args.desired_kl * 2.0:
|
||||
self.learning_rate = max(1e-5, self.learning_rate / 1.5)
|
||||
elif kl_mean < PPO_Args.desired_kl / 2.0 and kl_mean > 0.0:
|
||||
self.learning_rate = min(1e-2, self.learning_rate * 1.5)
|
||||
|
||||
for param_group in self.optimizer.param_groups:
|
||||
param_group['lr'] = self.learning_rate
|
||||
|
||||
# Surrogate loss
|
||||
ratio = torch.exp(actions_log_prob_batch - torch.squeeze(old_actions_log_prob_batch))
|
||||
surrogate = -torch.squeeze(advantages_batch) * ratio
|
||||
surrogate_clipped = -torch.squeeze(advantages_batch) * torch.clamp(ratio, 1.0 - PPO_Args.clip_param,
|
||||
1.0 + PPO_Args.clip_param)
|
||||
surrogate_loss = torch.max(surrogate, surrogate_clipped).mean()
|
||||
|
||||
# Value function loss
|
||||
if PPO_Args.use_clipped_value_loss:
|
||||
value_clipped = target_values_batch + \
|
||||
(value_batch - target_values_batch).clamp(-PPO_Args.clip_param,
|
||||
PPO_Args.clip_param)
|
||||
value_losses = (value_batch - returns_batch).pow(2)
|
||||
value_losses_clipped = (value_clipped - returns_batch).pow(2)
|
||||
value_loss = torch.max(value_losses, value_losses_clipped).mean()
|
||||
else:
|
||||
value_loss = (returns_batch - value_batch).pow(2).mean()
|
||||
|
||||
loss = surrogate_loss + PPO_Args.value_loss_coef * value_loss - PPO_Args.entropy_coef * entropy_batch.mean()
|
||||
|
||||
# Gradient step
|
||||
self.optimizer.zero_grad()
|
||||
loss.backward()
|
||||
nn.utils.clip_grad_norm_(self.actor_critic.parameters(), PPO_Args.max_grad_norm)
|
||||
self.optimizer.step()
|
||||
|
||||
mean_value_loss += value_loss.item()
|
||||
mean_surrogate_loss += surrogate_loss.item()
|
||||
|
||||
data_size = privileged_obs_batch.shape[0]
|
||||
num_train = int(data_size // 5 * 4)
|
||||
|
||||
# Adaptation module gradient step
|
||||
|
||||
for epoch in range(PPO_Args.num_adaptation_module_substeps):
|
||||
|
||||
adaptation_pred = self.actor_critic.adaptation_module(obs_history_batch)
|
||||
with torch.no_grad():
|
||||
adaptation_target = privileged_obs_batch
|
||||
# residual = (adaptation_target - adaptation_pred).norm(dim=1)
|
||||
# caches.slot_cache.log(env_bins_batch[:, 0].cpu().numpy().astype(np.uint8),
|
||||
# sysid_residual=residual.cpu().numpy())
|
||||
|
||||
selection_indices = torch.linspace(0, adaptation_pred.shape[1]-1, steps=adaptation_pred.shape[1], dtype=torch.long)
|
||||
if PPO_Args.selective_adaptation_module_loss:
|
||||
# mask out indices corresponding to swing feet
|
||||
selection_indices = 0
|
||||
|
||||
adaptation_loss = F.mse_loss(adaptation_pred[:num_train, selection_indices], adaptation_target[:num_train, selection_indices])
|
||||
adaptation_test_loss = F.mse_loss(adaptation_pred[num_train:, selection_indices], adaptation_target[num_train:, selection_indices])
|
||||
|
||||
|
||||
|
||||
self.adaptation_module_optimizer.zero_grad()
|
||||
adaptation_loss.backward()
|
||||
self.adaptation_module_optimizer.step()
|
||||
|
||||
mean_adaptation_module_loss += adaptation_loss.item()
|
||||
mean_adaptation_module_test_loss += adaptation_test_loss.item()
|
||||
|
||||
num_updates = PPO_Args.num_learning_epochs * PPO_Args.num_mini_batches
|
||||
mean_value_loss /= num_updates
|
||||
mean_surrogate_loss /= num_updates
|
||||
mean_adaptation_module_loss /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
mean_decoder_loss /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
mean_decoder_loss_student /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
mean_adaptation_module_test_loss /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
mean_decoder_test_loss /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
mean_decoder_test_loss_student /= (num_updates * PPO_Args.num_adaptation_module_substeps)
|
||||
self.storage.clear()
|
||||
|
||||
return mean_value_loss, mean_surrogate_loss, mean_adaptation_module_loss, mean_decoder_loss, mean_decoder_loss_student, mean_adaptation_module_test_loss, mean_decoder_test_loss, mean_decoder_test_loss_student
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
|
||||
|
||||
import torch
|
||||
|
||||
from go1_gym_learn.utils import split_and_pad_trajectories
|
||||
|
||||
class RolloutStorage:
|
||||
class Transition:
|
||||
def __init__(self):
|
||||
self.observations = None
|
||||
self.privileged_observations = None
|
||||
self.observation_histories = None
|
||||
self.critic_observations = None
|
||||
self.actions = None
|
||||
self.rewards = None
|
||||
self.dones = None
|
||||
self.values = None
|
||||
self.actions_log_prob = None
|
||||
self.action_mean = None
|
||||
self.action_sigma = None
|
||||
self.env_bins = None
|
||||
|
||||
def clear(self):
|
||||
self.__init__()
|
||||
|
||||
def __init__(self, num_envs, num_transitions_per_env, obs_shape, privileged_obs_shape, obs_history_shape, actions_shape, device='cpu'):
|
||||
|
||||
self.device = device
|
||||
|
||||
self.obs_shape = obs_shape
|
||||
self.privileged_obs_shape = privileged_obs_shape
|
||||
self.obs_history_shape = obs_history_shape
|
||||
self.actions_shape = actions_shape
|
||||
|
||||
# Core
|
||||
self.observations = torch.zeros(num_transitions_per_env, num_envs, *obs_shape, device=self.device)
|
||||
self.privileged_observations = torch.zeros(num_transitions_per_env, num_envs, *privileged_obs_shape, device=self.device)
|
||||
self.observation_histories = torch.zeros(num_transitions_per_env, num_envs, *obs_history_shape, device=self.device)
|
||||
self.rewards = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.actions = torch.zeros(num_transitions_per_env, num_envs, *actions_shape, device=self.device)
|
||||
self.dones = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device).byte()
|
||||
|
||||
# For PPO
|
||||
self.actions_log_prob = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.values = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.returns = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.advantages = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
self.mu = torch.zeros(num_transitions_per_env, num_envs, *actions_shape, device=self.device)
|
||||
self.sigma = torch.zeros(num_transitions_per_env, num_envs, *actions_shape, device=self.device)
|
||||
self.env_bins = torch.zeros(num_transitions_per_env, num_envs, 1, device=self.device)
|
||||
|
||||
self.num_transitions_per_env = num_transitions_per_env
|
||||
self.num_envs = num_envs
|
||||
|
||||
self.step = 0
|
||||
|
||||
def add_transitions(self, transition: Transition):
|
||||
if self.step >= self.num_transitions_per_env:
|
||||
raise AssertionError("Rollout buffer overflow")
|
||||
self.observations[self.step].copy_(transition.observations)
|
||||
self.privileged_observations[self.step].copy_(transition.privileged_observations)
|
||||
self.observation_histories[self.step].copy_(transition.observation_histories)
|
||||
self.actions[self.step].copy_(transition.actions)
|
||||
self.rewards[self.step].copy_(transition.rewards.view(-1, 1))
|
||||
self.dones[self.step].copy_(transition.dones.view(-1, 1))
|
||||
self.values[self.step].copy_(transition.values)
|
||||
self.actions_log_prob[self.step].copy_(transition.actions_log_prob.view(-1, 1))
|
||||
self.mu[self.step].copy_(transition.action_mean)
|
||||
self.sigma[self.step].copy_(transition.action_sigma)
|
||||
self.env_bins[self.step].copy_(transition.env_bins.view(-1, 1))
|
||||
self.step += 1
|
||||
|
||||
def clear(self):
|
||||
self.step = 0
|
||||
|
||||
def compute_returns(self, last_values, gamma, lam):
|
||||
advantage = 0
|
||||
for step in reversed(range(self.num_transitions_per_env)):
|
||||
if step == self.num_transitions_per_env - 1:
|
||||
next_values = last_values
|
||||
else:
|
||||
next_values = self.values[step + 1]
|
||||
next_is_not_terminal = 1.0 - self.dones[step].float()
|
||||
delta = self.rewards[step] + next_is_not_terminal * gamma * next_values - self.values[step]
|
||||
advantage = delta + next_is_not_terminal * gamma * lam * advantage
|
||||
self.returns[step] = advantage + self.values[step]
|
||||
|
||||
# Compute and normalize the advantages
|
||||
self.advantages = self.returns - self.values
|
||||
self.advantages = (self.advantages - self.advantages.mean()) / (self.advantages.std() + 1e-8)
|
||||
|
||||
def get_statistics(self):
|
||||
done = self.dones
|
||||
done[-1] = 1
|
||||
flat_dones = done.permute(1, 0, 2).reshape(-1, 1)
|
||||
done_indices = torch.cat((flat_dones.new_tensor([-1], dtype=torch.int64), flat_dones.nonzero(as_tuple=False)[:, 0]))
|
||||
trajectory_lengths = (done_indices[1:] - done_indices[:-1])
|
||||
return trajectory_lengths.float().mean(), self.rewards.mean()
|
||||
|
||||
def mini_batch_generator(self, num_mini_batches, num_epochs=8):
|
||||
batch_size = self.num_envs * self.num_transitions_per_env
|
||||
mini_batch_size = batch_size // num_mini_batches
|
||||
indices = torch.randperm(num_mini_batches*mini_batch_size, requires_grad=False, device=self.device)
|
||||
|
||||
observations = self.observations.flatten(0, 1)
|
||||
privileged_obs = self.privileged_observations.flatten(0, 1)
|
||||
obs_history = self.observation_histories.flatten(0, 1)
|
||||
critic_observations = observations
|
||||
|
||||
actions = self.actions.flatten(0, 1)
|
||||
values = self.values.flatten(0, 1)
|
||||
returns = self.returns.flatten(0, 1)
|
||||
old_actions_log_prob = self.actions_log_prob.flatten(0, 1)
|
||||
advantages = self.advantages.flatten(0, 1)
|
||||
old_mu = self.mu.flatten(0, 1)
|
||||
old_sigma = self.sigma.flatten(0, 1)
|
||||
old_env_bins = self.env_bins.flatten(0, 1)
|
||||
|
||||
for epoch in range(num_epochs):
|
||||
for i in range(num_mini_batches):
|
||||
|
||||
start = i*mini_batch_size
|
||||
end = (i+1)*mini_batch_size
|
||||
batch_idx = indices[start:end]
|
||||
|
||||
obs_batch = observations[batch_idx]
|
||||
critic_observations_batch = critic_observations[batch_idx]
|
||||
privileged_obs_batch = privileged_obs[batch_idx]
|
||||
obs_history_batch = obs_history[batch_idx]
|
||||
actions_batch = actions[batch_idx]
|
||||
target_values_batch = values[batch_idx]
|
||||
returns_batch = returns[batch_idx]
|
||||
old_actions_log_prob_batch = old_actions_log_prob[batch_idx]
|
||||
advantages_batch = advantages[batch_idx]
|
||||
old_mu_batch = old_mu[batch_idx]
|
||||
old_sigma_batch = old_sigma[batch_idx]
|
||||
env_bins_batch = old_env_bins[batch_idx]
|
||||
yield obs_batch, critic_observations_batch, privileged_obs_batch, obs_history_batch, actions_batch, target_values_batch, advantages_batch, returns_batch, \
|
||||
old_actions_log_prob_batch, old_mu_batch, old_sigma_batch, None, env_bins_batch
|
||||
|
||||
# for RNNs only
|
||||
def reccurent_mini_batch_generator(self, num_mini_batches, num_epochs=8):
|
||||
|
||||
padded_obs_trajectories, trajectory_masks = split_and_pad_trajectories(self.observations, self.dones)
|
||||
padded_privileged_obs_trajectories, trajectory_masks = split_and_pad_trajectories(self.privileged_observations, self.dones)
|
||||
padded_obs_history_trajectories, trajectory_masks = split_and_pad_trajectories(self.observation_histories, self.dones)
|
||||
padded_critic_obs_trajectories = padded_obs_trajectories
|
||||
|
||||
mini_batch_size = self.num_envs // num_mini_batches
|
||||
for ep in range(num_epochs):
|
||||
first_traj = 0
|
||||
for i in range(num_mini_batches):
|
||||
start = i*mini_batch_size
|
||||
stop = (i+1)*mini_batch_size
|
||||
|
||||
dones = self.dones.squeeze(-1)
|
||||
last_was_done = torch.zeros_like(dones, dtype=torch.bool)
|
||||
last_was_done[1:] = dones[:-1]
|
||||
last_was_done[0] = True
|
||||
trajectories_batch_size = torch.sum(last_was_done[:, start:stop])
|
||||
last_traj = first_traj + trajectories_batch_size
|
||||
|
||||
masks_batch = trajectory_masks[:, first_traj:last_traj]
|
||||
obs_batch = padded_obs_trajectories[:, first_traj:last_traj]
|
||||
critic_obs_batch = padded_critic_obs_trajectories[:, first_traj:last_traj]
|
||||
privileged_obs_batch = padded_privileged_obs_trajectories[:, first_traj:last_traj]
|
||||
obs_history_batch = padded_obs_history_trajectories[:, first_traj:last_traj]
|
||||
|
||||
actions_batch = self.actions[:, start:stop]
|
||||
old_mu_batch = self.mu[:, start:stop]
|
||||
old_sigma_batch = self.sigma[:, start:stop]
|
||||
returns_batch = self.returns[:, start:stop]
|
||||
advantages_batch = self.advantages[:, start:stop]
|
||||
values_batch = self.values[:, start:stop]
|
||||
old_actions_log_prob_batch = self.actions_log_prob[:, start:stop]
|
||||
|
||||
yield obs_batch, critic_obs_batch, privileged_obs_batch, obs_history_batch, actions_batch, values_batch, advantages_batch, returns_batch, \
|
||||
old_actions_log_prob_batch, old_mu_batch, old_sigma_batch, masks_batch
|
||||
|
||||
first_traj = last_traj
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
from .utils import split_and_pad_trajectories, unpad_trajectories
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
# License: see [LICENSE, LICENSES/rsl_rl/LICENSE]
|
||||
|
||||
import torch
|
||||
|
||||
def split_and_pad_trajectories(tensor, dones):
|
||||
""" Splits trajectories at done indices. Then concatenates them and padds with zeros up to the length og the longest trajectory.
|
||||
Returns masks corresponding to valid parts of the trajectories
|
||||
Example:
|
||||
Input: [ [a1, a2, a3, a4 | a5, a6],
|
||||
[b1, b2 | b3, b4, b5 | b6]
|
||||
f]
|
||||
|
||||
Output:[ [a1, a2, a3, a4], | [ [True, True, True, True],
|
||||
[a5, a6, 0, 0], | [True, True, False, False],
|
||||
[b1, b2, 0, 0], | [True, True, False, False],
|
||||
[b3, b4, b5, 0], | [True, True, True, False],
|
||||
[b6, 0, 0, 0] | [True, False, False, False],
|
||||
] | ]
|
||||
|
||||
Assumes that the inputy has the following dimension order: [time, number of envs, aditional dimensions]
|
||||
"""
|
||||
dones = dones.clone()
|
||||
dones[-1] = 1
|
||||
# Permute the buffers to have order (num_envs, num_transitions_per_env, ...), for correct reshaping
|
||||
flat_dones = dones.transpose(1, 0).reshape(-1, 1)
|
||||
|
||||
# Get length of trajectory by counting the number of successive not done elements
|
||||
done_indices = torch.cat((flat_dones.new_tensor([-1], dtype=torch.int64), flat_dones.nonzero()[:, 0]))
|
||||
trajectory_lengths = done_indices[1:] - done_indices[:-1]
|
||||
trajectory_lengths_list = trajectory_lengths.tolist()
|
||||
# Extract the individual trajectories
|
||||
trajectories = torch.split(tensor.transpose(1, 0).flatten(0, 1),trajectory_lengths_list)
|
||||
padded_trajectories = torch.nn.utils.rnn.pad_sequence(trajectories)
|
||||
|
||||
|
||||
trajectory_masks = trajectory_lengths > torch.arange(0, tensor.shape[0], device=tensor.device).unsqueeze(1)
|
||||
return padded_trajectories, trajectory_masks
|
||||
|
||||
def unpad_trajectories(trajectories, masks):
|
||||
""" Does the inverse operation of split_and_pad_trajectories()
|
||||
"""
|
||||
# Need to transpose before and after the masking to have proper reshaping
|
||||
return trajectories.transpose(1, 0)[masks.transpose(1, 0)].view(-1, trajectories.shape[0], trajectories.shape[-1]).transpose(1, 0)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.0 MiB |
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0"?>
|
||||
<robot name="object">
|
||||
<link name="object">+
|
||||
<visual>
|
||||
<origin xyz="0 0 -0.075" rpy="1.57 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="concrete_block.obj" scale="1.0 1.0 1.0"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<!-- <mesh filename="concrete_block.obj" scale="1.0 1.0 1.0"/> -->
|
||||
<box size="1.0 1.0 0.05"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.5"/>
|
||||
<inertia ixx="0.5" ixy="0.0" ixz="0.0" iyy="0.5" iyz="0.0" izz="0.5"/>
|
||||
</inertial>
|
||||
</link>
|
||||
</robot>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,582 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot name="go1_description" xmlns:xacro="http://www.ros.org/wiki/xacro">
|
||||
<material name="black">
|
||||
<color rgba="0.0 0.0 0.0 1.0"/>
|
||||
</material>
|
||||
<material name="blue">
|
||||
<color rgba="0.0 0.0 0.8 1.0"/>
|
||||
</material>
|
||||
<material name="green">
|
||||
<color rgba="0.0 0.8 0.0 1.0"/>
|
||||
</material>
|
||||
<material name="grey">
|
||||
<color rgba="0.2 0.2 0.2 1.0"/>
|
||||
</material>
|
||||
<material name="silver">
|
||||
<color rgba="0.913725490196 0.913725490196 0.847058823529 1.0"/>
|
||||
</material>
|
||||
<material name="orange">
|
||||
<color rgba="1.0 0.423529411765 0.0392156862745 1.0"/>
|
||||
</material>
|
||||
<material name="brown">
|
||||
<color rgba="0.870588235294 0.811764705882 0.764705882353 1.0"/>
|
||||
</material>
|
||||
<material name="red">
|
||||
<color rgba="0.8 0.0 0.0 1.0"/>
|
||||
</material>
|
||||
<material name="white">
|
||||
<color rgba="1.0 1.0 1.0 1.0"/>
|
||||
</material>
|
||||
|
||||
|
||||
<link name="base">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
</link>
|
||||
<joint name="floating_base" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<parent link="base"/>
|
||||
<child link="trunk"/>
|
||||
</joint>
|
||||
<link name="trunk">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/trunk.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.3762 0.0935 0.114"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.011611 0.004437 0.000108"/>
|
||||
<mass value="4.8"/>
|
||||
<inertia ixx="0.016130741919" ixy="0.000593180607" ixz="7.324662e-06" iyy="0.036507810812" iyz="2.0969537e-05" izz="0.044693872053"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="imu_joint" type="fixed">
|
||||
<parent link="trunk"/>
|
||||
<child link="imu_link"/>
|
||||
<origin rpy="0 0 0" xyz="-0.01592 -0.06659 -0.00617"/>
|
||||
</joint>
|
||||
<link name="imu_link">
|
||||
<inertial>
|
||||
<mass value="0.001"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="red"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size=".001 .001 .001"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
<joint name="FR_hip_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.1881 -0.04675 0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="FR_hip"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-0.802851455917" upper="0.802851455917" velocity="50"/>
|
||||
</joint>
|
||||
<link name="FR_hip">
|
||||
<visual>
|
||||
<origin rpy="3.14159265359 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/hip.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 -0.045 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.04" radius="0.046"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="-0.00541 0.00074 6e-06"/>
|
||||
<mass value="0.510299"/>
|
||||
<inertia ixx="0.00030528937" ixy="7.788013e-06" ixz="2.2016e-07" iyy="0.000590894859" iyz="1.7175e-08" izz="0.000396594572"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="FR_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 -0.08 0"/>
|
||||
<parent link="FR_hip"/>
|
||||
<child link="FR_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="FR_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
<joint name="FR_thigh_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 -0.08 0"/>
|
||||
<parent link="FR_hip"/>
|
||||
<child link="FR_thigh"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-1.0471975512" upper="4.18879020479" velocity="28"/>
|
||||
</joint>
|
||||
<link name="FR_thigh">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/thigh_mirror.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.0245 0.034"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="-0.003468 0.018947 -0.032736"/>
|
||||
<mass value="0.898919"/>
|
||||
<inertia ixx="0.005395867678" ixy="-1.02809e-07" ixz="0.000337529085" iyy="0.005142451046" iyz="5.816563e-06" izz="0.00102478732"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="FR_calf_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="FR_thigh"/>
|
||||
<child link="FR_calf"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-2.69653369433" upper="-0.916297857297" velocity="28"/>
|
||||
</joint>
|
||||
<link name="FR_calf">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/calf.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.016 0.016"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.006286 0.001307 -0.122269"/>
|
||||
<mass value="0.158015"/>
|
||||
<inertia ixx="0.003607648222" ixy="1.494971e-06" ixz="-0.000132778525" iyy="0.003626771492" iyz="-2.8638535e-05" izz="3.5148003e-05"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="FR_foot_fixed" type="fixed" dont_collapse="true">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="FR_calf"/>
|
||||
<child link="FR_foot"/>
|
||||
</joint>
|
||||
<link name="FR_foot">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.01"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.02"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.06"/>
|
||||
<inertia ixx="9.6e-06" ixy="0.0" ixz="0.0" iyy="9.6e-06" iyz="0.0" izz="9.6e-06"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="FL_hip_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0.1881 0.04675 0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="FL_hip"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-0.802851455917" upper="0.802851455917" velocity="50"/>
|
||||
</joint>
|
||||
<link name="FL_hip">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/hip.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0.045 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.04" radius="0.046"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="-0.00541 -0.00074 6e-06"/>
|
||||
<mass value="0.510299"/>
|
||||
<inertia ixx="0.00030528937" ixy="-7.788013e-06" ixz="2.2016e-07" iyy="0.000590894859" iyz="-1.7175e-08" izz="0.000396594572"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="FL_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0.08 0"/>
|
||||
<parent link="FL_hip"/>
|
||||
<child link="FL_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="FL_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
<joint name="FL_thigh_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 0.08 0"/>
|
||||
<parent link="FL_hip"/>
|
||||
<child link="FL_thigh"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-1.0471975512" upper="4.18879020479" velocity="28"/>
|
||||
</joint>
|
||||
<link name="FL_thigh">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/thigh.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.0245 0.034"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="-0.003468 -0.018947 -0.032736"/>
|
||||
<mass value="0.898919"/>
|
||||
<inertia ixx="0.005395867678" ixy="1.02809e-07" ixz="0.000337529085" iyy="0.005142451046" iyz="-5.816563e-06" izz="0.00102478732"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="FL_calf_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="FL_thigh"/>
|
||||
<child link="FL_calf"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-2.69653369433" upper="-0.916297857297" velocity="28"/>
|
||||
</joint>
|
||||
<link name="FL_calf">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/calf.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.016 0.016"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.006286 0.001307 -0.122269"/>
|
||||
<mass value="0.158015"/>
|
||||
<inertia ixx="0.003607648222" ixy="1.494971e-06" ixz="-0.000132778525" iyy="0.003626771492" iyz="-2.8638535e-05" izz="3.5148003e-05"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="FL_foot_fixed" type="fixed" dont_collapse="true">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="FL_calf"/>
|
||||
<child link="FL_foot"/>
|
||||
</joint>
|
||||
<link name="FL_foot">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.01"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.02"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.06"/>
|
||||
<inertia ixx="9.6e-06" ixy="0.0" ixz="0.0" iyy="9.6e-06" iyz="0.0" izz="9.6e-06"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="RR_hip_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.1881 -0.04675 0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="RR_hip"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-0.802851455917" upper="0.802851455917" velocity="50"/>
|
||||
</joint>
|
||||
<link name="RR_hip">
|
||||
<visual>
|
||||
<origin rpy="3.14159265359 3.14159265359 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/hip.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 -0.045 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.04" radius="0.046"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.00541 0.00074 6e-06"/>
|
||||
<mass value="0.510299"/>
|
||||
<inertia ixx="0.00030528937" ixy="-7.788013e-06" ixz="-2.2016e-07" iyy="0.000590894859" iyz="1.7175e-08" izz="0.000396594572"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="RR_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 -0.08 0"/>
|
||||
<parent link="RR_hip"/>
|
||||
<child link="RR_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="RR_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
<joint name="RR_thigh_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 -0.08 0"/>
|
||||
<parent link="RR_hip"/>
|
||||
<child link="RR_thigh"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-1.0471975512" upper="4.18879020479" velocity="28"/>
|
||||
</joint>
|
||||
<link name="RR_thigh">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/thigh_mirror.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.0245 0.034"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="-0.003468 0.018947 -0.032736"/>
|
||||
<mass value="0.898919"/>
|
||||
<inertia ixx="0.005395867678" ixy="-1.02809e-07" ixz="0.000337529085" iyy="0.005142451046" iyz="5.816563e-06" izz="0.00102478732"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="RR_calf_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="RR_thigh"/>
|
||||
<child link="RR_calf"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-2.69653369433" upper="-0.916297857297" velocity="28"/>
|
||||
</joint>
|
||||
<link name="RR_calf">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/calf.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.016 0.016"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.006286 0.001307 -0.122269"/>
|
||||
<mass value="0.158015"/>
|
||||
<inertia ixx="0.003607648222" ixy="1.494971e-06" ixz="-0.000132778525" iyy="0.003626771492" iyz="-2.8638535e-05" izz="3.5148003e-05"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="RR_foot_fixed" type="fixed" dont_collapse="true">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="RR_calf"/>
|
||||
<child link="RR_foot"/>
|
||||
</joint>
|
||||
<link name="RR_foot">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.01"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.02"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.06"/>
|
||||
<inertia ixx="9.6e-06" ixy="0.0" ixz="0.0" iyy="9.6e-06" iyz="0.0" izz="9.6e-06"/>
|
||||
</inertial>
|
||||
</link>
|
||||
|
||||
<joint name="RL_hip_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="-0.1881 0.04675 0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="RL_hip"/>
|
||||
<axis xyz="1 0 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-0.802851455917" upper="0.802851455917" velocity="50"/>
|
||||
</joint>
|
||||
<link name="RL_hip">
|
||||
<visual>
|
||||
<origin rpy="0 3.14159265359 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/hip.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0.045 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.04" radius="0.046"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.00541 -0.00074 6e-06"/>
|
||||
<mass value="0.510299"/>
|
||||
<inertia ixx="0.00030528937" ixy="7.788013e-06" ixz="-2.2016e-07" iyy="0.000590894859" iyz="-1.7175e-08" izz="0.000396594572"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="RL_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0.08 0"/>
|
||||
<parent link="RL_hip"/>
|
||||
<child link="RL_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="RL_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
<joint name="RL_thigh_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 0.08 0"/>
|
||||
<parent link="RL_hip"/>
|
||||
<child link="RL_thigh"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-1.0471975512" upper="4.18879020479" velocity="28"/>
|
||||
</joint>
|
||||
<link name="RL_thigh">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/thigh.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="silver"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.0245 0.034"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="-0.003468 -0.018947 -0.032736"/>
|
||||
<mass value="0.898919"/>
|
||||
<inertia ixx="0.005395867678" ixy="1.02809e-07" ixz="0.000337529085" iyy="0.005142451046" iyz="-5.816563e-06" izz="0.00102478732"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="RL_calf_joint" type="revolute">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="RL_thigh"/>
|
||||
<child link="RL_calf"/>
|
||||
<axis xyz="0 1 0"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="33.5" lower="-2.69653369433" upper="-0.916297857297" velocity="28"/>
|
||||
</joint>
|
||||
<link name="RL_calf">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<mesh filename="../meshes/calf.stl" scale="1 1 1"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.1065"/>
|
||||
<geometry>
|
||||
<box size="0.213 0.016 0.016"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.006286 0.001307 -0.122269"/>
|
||||
<mass value="0.158015"/>
|
||||
<inertia ixx="0.003607648222" ixy="1.494971e-06" ixz="-0.000132778525" iyy="0.003626771492" iyz="-2.8638535e-05" izz="3.5148003e-05"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="RL_foot_fixed" type="fixed" dont_collapse="true">
|
||||
<origin rpy="0 0 0" xyz="0 0 -0.213"/>
|
||||
<parent link="RL_calf"/>
|
||||
<child link="RL_foot"/>
|
||||
</joint>
|
||||
<link name="RL_foot">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.01"/>
|
||||
</geometry>
|
||||
<material name="black"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<sphere radius="0.02"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<inertial>
|
||||
<mass value="0.06"/>
|
||||
<inertia ixx="9.6e-06" ixy="0.0" ixz="0.0" iyy="9.6e-06" iyz="0.0" izz="9.6e-06"/>
|
||||
</inertial>
|
||||
</link>
|
||||
</robot>
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
<mujoco model="go1_description">
|
||||
<compiler angle="radian" meshdir="../meshes/" />
|
||||
<size njmax="500" nconmax="100" />
|
||||
<option gravity='0 0 -9.806' iterations='50' solver='Newton' timestep='0.002'/>
|
||||
|
||||
<default>
|
||||
<geom contype="1" conaffinity="1" friction="0.6 0.3 0.3" rgba="0.5 0.6 0.7 1" margin="0.001" group="0"/>
|
||||
|
||||
<light castshadow="false" diffuse="1 1 1"/>
|
||||
<motor ctrlrange="-33.5 33.5" ctrllimited="true"/>
|
||||
<camera fovy="60"/>
|
||||
<joint damping="0.01" armature="0.01" frictionloss="0.2" />
|
||||
|
||||
</default>
|
||||
=
|
||||
<asset>
|
||||
<mesh name="trunk" file="trunk.stl" />
|
||||
<mesh name="hip" file="hip.stl" />
|
||||
<mesh name="thigh_mirror" file="thigh_mirror.stl" />
|
||||
<mesh name="calf" file="calf.stl" />
|
||||
<mesh name="thigh" file="thigh.stl" />
|
||||
</asset>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="1.0 1.0 1.0" rgb2="1.0 1.0 1.0" width="512" height="512"/>
|
||||
<texture name="plane" type="2d" builtin="flat" rgb1="1 1 1" rgb2="1 1 1" width="512" height="512" mark="cross" markrgb="0 0 0"/>
|
||||
<material name="plane" reflectance="0.0" texture="plane" texrepeat="3 3" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<visual>
|
||||
<rgba com="0.502 1.0 0 0.5" contactforce="0.98 0.4 0.4 0.7" contactpoint="1.0 1.0 0.6 0.4"/>
|
||||
<scale com="0.2" forcewidth="0.035" contactwidth="0.10" contactheight="0.04"/>
|
||||
</visual>
|
||||
|
||||
<worldbody>
|
||||
<light directional="true" diffuse=".8 .8 .8" pos="0 0 10" dir="0 0 -10"/>
|
||||
<camera name="track" mode="trackcom" pos="0 -1.3 1.6" xyaxes="1 0 0 0 0.707 0.707"/>
|
||||
<geom name='floor' type='plane' conaffinity='1' condim='3' contype='1' rgba="0.5 0.9 0.9 0.1" material='plane' pos='0 0 0' size='0 0 1'/>
|
||||
|
||||
<body name="trunk" pos="0 0 0.35">
|
||||
<inertial pos="0.0116053 0.00442221 0.000106692" quat="0.0111438 0.707126 -0.00935374 0.706938" mass="4.801" diaginertia="0.0447997 0.0366257 0.0162187" />
|
||||
<joint type="free" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="trunk" />
|
||||
<geom size="0.13 0.04675 0.057" type="box" rgba="0.913725 0.913725 0.847059 1" />
|
||||
<geom size="0.0005 0.0005 0.0005" pos="-0.01592 -0.06659 -0.00617" type="box" contype="0" conaffinity="0" group="1" rgba="0.8 0 0 0" />
|
||||
<geom size="0.0005 0.0005 0.0005" pos="-0.01592 -0.06659 -0.00617" type="box" rgba="0.8 0 0 0" />
|
||||
<site name="imu" pos="0 0 0"/>
|
||||
|
||||
<body name="FR_hip" pos="0.1881 -0.04675 0">
|
||||
<inertial pos="-0.00406411 -0.0193463 4.50733e-06" quat="0.467526 0.531662 -0.466259 0.530431" mass="0.679292" diaginertia="0.00131334 0.00122648 0.000728484" />
|
||||
<joint name="FR_hip_joint" pos="0 0 0" axis="1 0 0" limited="true" range="-0.802851 0.802851" />
|
||||
<geom quat="0 1 0 0" type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="hip" />
|
||||
<geom size="0.046 0.02" pos="0 -0.045 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<geom size="0.031 0.02" pos="0 -0.07 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
|
||||
<body name="FR_thigh" pos="0 -0.08 0">
|
||||
<inertial pos="-0.003468 0.018947 -0.032736" quat="0.999266 0.00067676 -0.0382978 0.000639813" mass="0.898919" diaginertia="0.00542178 0.00514246 0.000998869" />
|
||||
<joint name="FR_thigh_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-1.0472 4.18879" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="thigh_mirror" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<body name="FR_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00455603 0.0009473 -0.147239" quat="0.762045 0.00970173 0.0180098 0.647201" mass="0.218015" diaginertia="0.00399678 0.00398122 3.99428e-05" />
|
||||
<joint name="FR_calf_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-2.69653 -0.916298" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" mesh="calf" />
|
||||
<geom size="0.1065 0.008 0.008" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0 0 0 0" />
|
||||
<geom size="0.01" pos="0 0 -0.213" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" />
|
||||
<geom size="0.02" pos="0 0 -0.213" rgba="0 0 0 1" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="FL_hip" pos="0.1881 0.04675 0">
|
||||
<inertial pos="-0.00406411 0.0193463 4.50733e-06" quat="0.531662 0.467526 -0.530431 0.466259" mass="0.679292" diaginertia="0.00131334 0.00122648 0.000728484" />
|
||||
<joint name="FL_hip_joint" pos="0 0 0" axis="1 0 0" limited="true" range="-0.802851 0.802851" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="hip" />
|
||||
<geom size="0.046 0.02" pos="0 0.045 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<geom size="0.031 0.02" pos="0 0.07 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
|
||||
<body name="FL_thigh" pos="0 0.08 0">
|
||||
<inertial pos="-0.003468 -0.018947 -0.032736" quat="0.999266 -0.00067676 -0.0382978 -0.000639813" mass="0.898919" diaginertia="0.00542178 0.00514246 0.000998869" />
|
||||
<joint name="FL_thigh_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-1.0472 4.18879" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="thigh" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<body name="FL_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00455603 0.0009473 -0.147239" quat="0.762045 0.00970173 0.0180098 0.647201" mass="0.218015" diaginertia="0.00399678 0.00398122 3.99428e-05" />
|
||||
<joint name="FL_calf_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-2.69653 -0.916298" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" mesh="calf" />
|
||||
<geom size="0.1065 0.008 0.008" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<geom size="0.01" pos="0 0 -0.213" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" />
|
||||
<geom size="0.02" pos="0 0 -0.213" rgba="0 0 0 1" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="RR_hip" pos="-0.1881 -0.04675 0">
|
||||
<inertial pos="0.00406411 -0.0193463 4.50733e-06" quat="0.530431 0.466259 -0.531662 0.467526" mass="0.679292" diaginertia="0.00131334 0.00122648 0.000728484" />
|
||||
<joint name="RR_hip_joint" pos="0 0 0" axis="1 0 0" limited="true" range="-0.802851 0.802851" />
|
||||
<geom quat="0 0 0 -1" type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="hip" />
|
||||
<geom size="0.046 0.02" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 1" />
|
||||
<geom size="0.046 0.02" pos="0 -0.045 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<geom size="0.031 0.02" pos="0 -0.07 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
|
||||
<body name="RR_thigh" pos="0 -0.08 0">
|
||||
<inertial pos="-0.003468 0.018947 -0.032736" quat="0.999266 0.00067676 -0.0382978 0.000639813" mass="0.898919" diaginertia="0.00542178 0.00514246 0.000998869" />
|
||||
<joint name="RR_thigh_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-1.0472 4.18879" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="thigh_mirror" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<body name="RR_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00455603 0.0009473 -0.147239" quat="0.762045 0.00970173 0.0180098 0.647201" mass="0.218015" diaginertia="0.00399678 0.00398122 3.99428e-05" />
|
||||
<joint name="RR_calf_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-2.69653 -0.916298" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" mesh="calf" />
|
||||
<geom size="0.1065 0.008 0.008" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0 0 0 0" />
|
||||
<geom size="0.01" pos="0 0 -0.213" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" />
|
||||
<geom size="0.02" pos="0 0 -0.213" rgba="0 0 0 1" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="RL_hip" pos="-0.1881 0.04675 0">
|
||||
<inertial pos="0.00406411 0.0193463 4.50733e-06" quat="0.466259 0.530431 -0.467526 0.531662" mass="0.679292" diaginertia="0.00131334 0.00122648 0.000728484" />
|
||||
<joint name="RL_hip_joint" pos="0 0 0" axis="1 0 0" limited="true" range="-0.802851 0.802851" />
|
||||
<geom quat="0 0 1 0" type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="hip" />
|
||||
<geom size="0.046 0.02" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 1" />
|
||||
<geom size="0.046 0.02" pos="0 0.045 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<geom size="0.031 0.02" pos="0 0.07 0" quat="0.707107 0.707107 0 0" type="cylinder" rgba="0.913725 0.913725 0.847059 0" />
|
||||
|
||||
<body name="RL_thigh" pos="0 0.08 0">
|
||||
<inertial pos="-0.003468 -0.018947 -0.032736" quat="0.999266 -0.00067676 -0.0382978 -0.000639813" mass="0.898919" diaginertia="0.00542178 0.00514246 0.000998869" />
|
||||
<joint name="RL_thigh_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-1.0472 4.18879" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0.913725 0.913725 0.847059 1" mesh="thigh" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0.913725 0.913725 0.847059 0" />
|
||||
<body name="RL_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00455603 0.0009473 -0.147239" quat="0.762045 0.00970173 0.0180098 0.647201" mass="0.218015" diaginertia="0.00399678 0.00398122 3.99428e-05" />
|
||||
<joint name="RL_calf_joint" pos="0 0 0" axis="0 1 0" limited="true" range="-2.69653 -0.916298" />
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" mesh="calf" />
|
||||
<geom size="0.1065 0.008 0.008" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0" type="box" rgba="0 0 0 0" />
|
||||
<geom size="0.01" pos="0 0 -0.213" contype="0" conaffinity="0" group="1" rgba="0 0 0 1" />
|
||||
<geom size="0.02" pos="0 0 -0.213" rgba="0 0 0 1" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
</worldbody>
|
||||
<actuator>
|
||||
|
||||
<motor name="FR_hip" gear="1" joint="FR_hip_joint"/>
|
||||
<motor name="FR_thigh" gear="1" joint="FR_thigh_joint"/>
|
||||
<motor name="FR_calf" gear="1" joint="FR_calf_joint"/>
|
||||
<motor name="FL_hip" gear="1" joint="FL_hip_joint"/>
|
||||
<motor name="FL_thigh" gear="1" joint="FL_thigh_joint"/>
|
||||
<motor name="FL_calf" gear="1" joint="FL_calf_joint"/>
|
||||
<motor name="RR_hip" gear="1" joint="RR_hip_joint"/>
|
||||
<motor name="RR_thigh" gear="1" joint="RR_thigh_joint"/>
|
||||
<motor name="RR_calf" gear="1" joint="RR_calf_joint" />
|
||||
<motor name="RL_hip" gear="1" joint="RL_hip_joint"/>
|
||||
<motor name="RL_thigh" gear="1" joint="RL_thigh_joint"/>
|
||||
<motor name="RL_calf" gear="1" joint="RL_calf_joint"/>
|
||||
</actuator>
|
||||
|
||||
<sensor>
|
||||
|
||||
<jointpos name="FR_hip_pos" joint="FR_hip_joint"/>
|
||||
<jointpos name="FR_thigh_pos" joint="FR_thigh_joint"/>
|
||||
<jointpos name="FR_calf_pos" joint="FR_calf_joint"/>
|
||||
<jointpos name="FL_hip_pos" joint="FL_hip_joint"/>
|
||||
<jointpos name="FL_thigh_pos" joint="FL_thigh_joint"/>
|
||||
<jointpos name="FL_calf_pos" joint="FL_calf_joint"/>
|
||||
<jointpos name="RR_hip_pos" joint="RR_hip_joint"/>
|
||||
<jointpos name="RR_thigh_pos" joint="RR_thigh_joint"/>
|
||||
<jointpos name="RR_calf_pos" joint="RR_calf_joint" />
|
||||
<jointpos name="RL_hip_pos" joint="RL_hip_joint"/>
|
||||
<jointpos name="RL_thigh_pos" joint="RL_thigh_joint"/>
|
||||
<jointpos name="RL_calf_pos" joint="RL_calf_joint"/>
|
||||
|
||||
<jointvel name="FR_hip_vel" joint="FR_hip_joint"/>
|
||||
<jointvel name="FR_thigh_vel" joint="FR_thigh_joint"/>
|
||||
<jointvel name="FR_calf_vel" joint="FR_calf_joint"/>
|
||||
<jointvel name="FL_hip_vel" joint="FL_hip_joint"/>
|
||||
<jointvel name="FL_thigh_vel" joint="FL_thigh_joint"/>
|
||||
<jointvel name="FL_calf_vel" joint="FL_calf_joint"/>
|
||||
<jointvel name="RR_hip_vel" joint="RR_hip_joint"/>
|
||||
<jointvel name="RR_thigh_vel" joint="RR_thigh_joint"/>
|
||||
<jointvel name="RR_calf_vel" joint="RR_calf_joint" />
|
||||
<jointvel name="RL_hip_vel" joint="RL_hip_joint"/>
|
||||
<jointvel name="RL_thigh_vel" joint="RL_thigh_joint"/>
|
||||
<jointvel name="RL_calf_vel" joint="RL_calf_joint"/>
|
||||
|
||||
<accelerometer name="Body_Acc" site="imu"/>
|
||||
|
||||
<gyro name="Body_Gyro" site="imu"/>
|
||||
|
||||
<framepos name="Body_Pos" objtype=site objname="imu"/>
|
||||
|
||||
<framequat name="Body_Quat" objtype=site objname="imu"/>
|
||||
|
||||
|
||||
</sensor>
|
||||
</mujoco>
|
||||
+45396
File diff suppressed because one or more lines are too long
+101
@@ -0,0 +1,101 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1097cf594ed244591b250b735a5eb255
|
||||
ModelImporter:
|
||||
serializedVersion: 20101
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
materials:
|
||||
materialImportMode: 1
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
motionNodeName:
|
||||
rigImportErrors:
|
||||
rigImportWarnings:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
importAnimatedCustomProperties: 0
|
||||
importConstraints: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
extraUserProperties: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
useSRGBMaterialColor: 1
|
||||
sortHierarchyByName: 1
|
||||
importVisibility: 1
|
||||
importBlendShapes: 1
|
||||
importCameras: 1
|
||||
importLights: 1
|
||||
fileIdsGeneration: 2
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
bakeAxisConversion: 0
|
||||
preserveHierarchy: 0
|
||||
skinWeightsMode: 0
|
||||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVMarginMethod: 1
|
||||
secondaryUVMinLightmapResolution: 40
|
||||
secondaryUVMinObjectScale: 1
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 3
|
||||
normalCalculationMode: 4
|
||||
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||
blendShapeNormalImportMode: 1
|
||||
normalSmoothingSource: 0
|
||||
referencedClips: []
|
||||
importAnimation: 1
|
||||
humanDescription:
|
||||
serializedVersion: 3
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
globalScale: 1
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
hasExtraRoot: 0
|
||||
skeletonHasParents: 1
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
autoGenerateAvatarMappingIfUnspecified: 1
|
||||
animationType: 2
|
||||
humanoidOversampling: 1
|
||||
avatarSetup: 0
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+10259
File diff suppressed because it is too large
Load Diff
+97
@@ -0,0 +1,97 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ee26848c5aa74acea976c348931fd00
|
||||
ModelImporter:
|
||||
serializedVersion: 19301
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
materials:
|
||||
materialImportMode: 1
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
motionNodeName:
|
||||
rigImportErrors:
|
||||
rigImportWarnings:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
importAnimatedCustomProperties: 0
|
||||
importConstraints: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
extraUserProperties: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
useSRGBMaterialColor: 1
|
||||
sortHierarchyByName: 1
|
||||
importVisibility: 1
|
||||
importBlendShapes: 1
|
||||
importCameras: 1
|
||||
importLights: 1
|
||||
fileIdsGeneration: 2
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
preserveHierarchy: 0
|
||||
skinWeightsMode: 0
|
||||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 3
|
||||
normalCalculationMode: 4
|
||||
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||
blendShapeNormalImportMode: 1
|
||||
normalSmoothingSource: 0
|
||||
referencedClips: []
|
||||
importAnimation: 1
|
||||
humanDescription:
|
||||
serializedVersion: 3
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
globalScale: 1
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
hasExtraRoot: 0
|
||||
skeletonHasParents: 1
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
autoGenerateAvatarMappingIfUnspecified: 1
|
||||
animationType: 2
|
||||
humanoidOversampling: 1
|
||||
avatarSetup: 0
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+37168
File diff suppressed because it is too large
Load Diff
+97
@@ -0,0 +1,97 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 664a786199a0d4e88a04f46cb19e0c08
|
||||
ModelImporter:
|
||||
serializedVersion: 19301
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
materials:
|
||||
materialImportMode: 1
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
motionNodeName:
|
||||
rigImportErrors:
|
||||
rigImportWarnings:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
importAnimatedCustomProperties: 0
|
||||
importConstraints: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
extraUserProperties: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
useSRGBMaterialColor: 1
|
||||
sortHierarchyByName: 1
|
||||
importVisibility: 1
|
||||
importBlendShapes: 1
|
||||
importCameras: 1
|
||||
importLights: 1
|
||||
fileIdsGeneration: 2
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
preserveHierarchy: 0
|
||||
skinWeightsMode: 0
|
||||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 3
|
||||
normalCalculationMode: 4
|
||||
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||
blendShapeNormalImportMode: 1
|
||||
normalSmoothingSource: 0
|
||||
referencedClips: []
|
||||
importAnimation: 1
|
||||
humanDescription:
|
||||
serializedVersion: 3
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
globalScale: 1
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
hasExtraRoot: 0
|
||||
skeletonHasParents: 1
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
autoGenerateAvatarMappingIfUnspecified: 1
|
||||
animationType: 2
|
||||
humanoidOversampling: 1
|
||||
avatarSetup: 0
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+32069
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd79403848d3a4b4888e9c9bbf98610a
|
||||
ModelImporter:
|
||||
serializedVersion: 19301
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
materials:
|
||||
materialImportMode: 1
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
motionNodeName:
|
||||
rigImportErrors:
|
||||
rigImportWarnings:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
importAnimatedCustomProperties: 0
|
||||
importConstraints: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
extraUserProperties: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
useSRGBMaterialColor: 1
|
||||
sortHierarchyByName: 1
|
||||
importVisibility: 1
|
||||
importBlendShapes: 1
|
||||
importCameras: 1
|
||||
importLights: 1
|
||||
fileIdsGeneration: 2
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
preserveHierarchy: 0
|
||||
skinWeightsMode: 0
|
||||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 3
|
||||
normalCalculationMode: 4
|
||||
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||
blendShapeNormalImportMode: 1
|
||||
normalSmoothingSource: 0
|
||||
referencedClips: []
|
||||
importAnimation: 1
|
||||
humanDescription:
|
||||
serializedVersion: 3
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
globalScale: 1
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
hasExtraRoot: 0
|
||||
skeletonHasParents: 1
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
autoGenerateAvatarMappingIfUnspecified: 1
|
||||
animationType: 2
|
||||
humanoidOversampling: 1
|
||||
avatarSetup: 0
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+45910
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35947fc8881f9458a8a9cd250ca07d76
|
||||
ModelImporter:
|
||||
serializedVersion: 19301
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
materials:
|
||||
materialImportMode: 1
|
||||
materialName: 0
|
||||
materialSearch: 1
|
||||
materialLocation: 1
|
||||
animations:
|
||||
legacyGenerateAnimations: 4
|
||||
bakeSimulation: 0
|
||||
resampleCurves: 1
|
||||
optimizeGameObjects: 0
|
||||
motionNodeName:
|
||||
rigImportErrors:
|
||||
rigImportWarnings:
|
||||
animationImportErrors:
|
||||
animationImportWarnings:
|
||||
animationRetargetingWarnings:
|
||||
animationDoRetargetingWarnings: 0
|
||||
importAnimatedCustomProperties: 0
|
||||
importConstraints: 0
|
||||
animationCompression: 1
|
||||
animationRotationError: 0.5
|
||||
animationPositionError: 0.5
|
||||
animationScaleError: 0.5
|
||||
animationWrapMode: 0
|
||||
extraExposedTransformPaths: []
|
||||
extraUserProperties: []
|
||||
clipAnimations: []
|
||||
isReadable: 0
|
||||
meshes:
|
||||
lODScreenPercentages: []
|
||||
globalScale: 1
|
||||
meshCompression: 0
|
||||
addColliders: 0
|
||||
useSRGBMaterialColor: 1
|
||||
sortHierarchyByName: 1
|
||||
importVisibility: 1
|
||||
importBlendShapes: 1
|
||||
importCameras: 1
|
||||
importLights: 1
|
||||
fileIdsGeneration: 2
|
||||
swapUVChannels: 0
|
||||
generateSecondaryUV: 0
|
||||
useFileUnits: 1
|
||||
keepQuads: 0
|
||||
weldVertices: 1
|
||||
preserveHierarchy: 0
|
||||
skinWeightsMode: 0
|
||||
maxBonesPerVertex: 4
|
||||
minBoneWeight: 0.001
|
||||
meshOptimizationFlags: -1
|
||||
indexFormat: 0
|
||||
secondaryUVAngleDistortion: 8
|
||||
secondaryUVAreaDistortion: 15.000001
|
||||
secondaryUVHardAngle: 88
|
||||
secondaryUVPackMargin: 4
|
||||
useFileScale: 1
|
||||
tangentSpace:
|
||||
normalSmoothAngle: 60
|
||||
normalImportMode: 0
|
||||
tangentImportMode: 3
|
||||
normalCalculationMode: 4
|
||||
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||
blendShapeNormalImportMode: 1
|
||||
normalSmoothingSource: 0
|
||||
referencedClips: []
|
||||
importAnimation: 1
|
||||
humanDescription:
|
||||
serializedVersion: 3
|
||||
human: []
|
||||
skeleton: []
|
||||
armTwist: 0.5
|
||||
foreArmTwist: 0.5
|
||||
upperLegTwist: 0.5
|
||||
legTwist: 0.5
|
||||
armStretch: 0.05
|
||||
legStretch: 0.05
|
||||
feetSpacing: 0
|
||||
globalScale: 1
|
||||
rootMotionBoneName:
|
||||
hasTranslationDoF: 0
|
||||
hasExtraRoot: 0
|
||||
skeletonHasParents: 1
|
||||
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||
autoGenerateAvatarMappingIfUnspecified: 1
|
||||
animationType: 2
|
||||
humanoidOversampling: 1
|
||||
avatarSetup: 0
|
||||
additionalBone: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+10259
File diff suppressed because it is too large
Load Diff
+37168
File diff suppressed because it is too large
Load Diff
+32069
File diff suppressed because it is too large
Load Diff
+45910
File diff suppressed because it is too large
Load Diff
+457
@@ -0,0 +1,457 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot name="mini_cheetah" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
|
||||
<material name="black">
|
||||
<color rgba="0.0 0.0 0.0 1.0"/>
|
||||
</material>
|
||||
<material name="blue">
|
||||
<color rgba="0.0 0.0 0.8 1.0"/>
|
||||
</material>
|
||||
<material name="green">
|
||||
<color rgba="0.0 0.8 0.0 1.0"/>
|
||||
</material>
|
||||
<material name="grey">
|
||||
<color rgba="0.2 0.2 0.2 1.0"/>
|
||||
</material>
|
||||
<material name="silver">
|
||||
<color rgba="0.913725490196 0.913725490196 0.847058823529 1.0"/>
|
||||
</material>
|
||||
<material name="orange">
|
||||
<!-- <color rgba="1.0 0.423529411765 0.0392156862745 1.0"/> -->
|
||||
<color rgba="0.12 0.15 0.2 1.0"/>
|
||||
</material>
|
||||
<material name="brown">
|
||||
<color rgba="0.870588235294 0.811764705882 0.764705882353 1.0"/>
|
||||
</material>
|
||||
<material name="red">
|
||||
<color rgba="0.8 0.0 0.0 1.0"/>
|
||||
</material>
|
||||
<material name="white">
|
||||
<color rgba="1.0 1.0 1.0 1.0"/>
|
||||
</material>
|
||||
|
||||
<link name="base">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<inertial>
|
||||
<mass value="3.3"/>
|
||||
<origin xyz="0.0 0.0 0.0"/>
|
||||
<inertia ixx="0.011253" ixy="0" ixz="0" iyy="0.362030" iyz="0" izz="0.042673"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="floating_base" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<parent link="base"/>
|
||||
<child link="trunk"/>
|
||||
</joint>
|
||||
<link name="trunk">
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_body.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 0.0 0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_body.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
</collision> -->
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.23 0.18 0.1"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="imu_joint" type="fixed">
|
||||
<parent link="trunk"/>
|
||||
<child link="imu_link"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
</joint>
|
||||
<link name="imu_link">
|
||||
<inertial>
|
||||
<mass value="0.001"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="red"/>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<!--!!!!!!!!!!!! Front Right Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="FR_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="0.19 -0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="FR_hip"/>
|
||||
<!-- <actuator gear_ratio="6" voltage="24">
|
||||
<friction damping="0.01" dry_friction="0.2"/>
|
||||
<motor Kt="0.05" R="0.173" TauMax="3"/>
|
||||
<rotor_inertia ixx="33.0e-6" iyy="0.000033" izz="63e-6" iyz="0.0" ixy="0" ixz="0" />
|
||||
</actuator> -->
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
|
||||
</joint>
|
||||
<link name="FR_hip">
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="3.141592 0.0 1.5708" xyz="-0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="3.141592 0 1.5708" xyz="-0.055 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FR_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.062 0.00"/>
|
||||
<parent link="FR_hip"/>
|
||||
<child link="FR_thigh"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FR_thigh">
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_upper_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 -1.5708 0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.105"/>
|
||||
<geometry>
|
||||
<box size="0.17 0.015 0.030"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FR_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="FR_thigh"/>
|
||||
<child link="FR_calf"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="FR_calf">
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 3.141592 0.0" xyz="0 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!--!!!!!!!!!!!! Front Left Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="FL_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="0.19 0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="FL_hip"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FL_hip">
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0. 0. -1.5708" xyz="-0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 0 -1.5708" xyz="-0.055 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FL_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.062 0.00"/>
|
||||
<parent link="FL_hip"/>
|
||||
<child link="FL_thigh"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FL_thigh">
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_upper_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 -1.5708 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.105"/>
|
||||
<geometry>
|
||||
<box size="0.17 0.015 0.030"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FL_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="FL_thigh"/>
|
||||
<child link="FL_calf"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="FL_calf">
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 3.141592 0.0" xyz="0 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!--!!!!!!!!!!!! Hind Right Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="RR_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="-0.19 -0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="RR_hip"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RR_hip">
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 1.5708" xyz="0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 0 1.5708" xyz="0.055 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RR_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.062 0.00"/>
|
||||
<parent link="RR_hip"/>
|
||||
<child link="RR_thigh"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RR_thigh">
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_upper_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 -1.5708 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_upper_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 -1.5708 0.0" xyz="0 0 0"/>
|
||||
</collision> -->
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.105"/>
|
||||
<geometry>
|
||||
<box size="0.17 0.015 0.030"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RR_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="RR_thigh"/>
|
||||
<child link="RR_calf"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="RR_calf">
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 3.141592 0.0" xyz="0 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!--!!!!!!!!!!!! Hind Left Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="RL_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="-0.19 0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="RL_hip"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RL_hip">
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="3.141592 0.0 -1.5708" xyz="0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_abad.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="3.141592 0 -1.5708" xyz="0.055 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RL_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.062 0.00"/>
|
||||
<parent link="RL_hip"/>
|
||||
<child link="RL_thigh"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RL_thigh">
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_upper_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 -1.5708 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<!-- <collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_upper_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 -1.5708 0.0" xyz="0 0 0"/>
|
||||
</collision> -->
|
||||
<collision>
|
||||
<origin rpy="0 1.57079632679 0" xyz="0 0 -0.105"/>
|
||||
<geometry>
|
||||
<box size="0.17 0.015 0.030"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RL_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="RL_thigh"/>
|
||||
<child link="RL_calf"/>
|
||||
<dynamics damping="0" friction="0"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="RL_calf">
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<mesh filename="meshes/mini_lower_link.obj"/>
|
||||
</geometry>
|
||||
<origin rpy="0 3.141592 0.0" xyz="0 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
</robot>
|
||||
+714
@@ -0,0 +1,714 @@
|
||||
<?xml version="1.0" ?>
|
||||
<robot name="mini_cheetah" xmlns:xacro="http://ros.org/wiki/xacro">
|
||||
|
||||
<material name="cheetah_material">
|
||||
<color rgba="0.7 0.7 0.7 1.0"/>
|
||||
</material>
|
||||
|
||||
|
||||
<!--!!!!!!!!!!!!!!!!!!BODY!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<!-- <link name="trunk">
|
||||
<inertial>
|
||||
<mass value="3.3"/>
|
||||
<origin xyz="0.0 0.0 0.0"/>
|
||||
<inertia ixx="0.011253" ixy="0" ixz="0" iyy="0.036203" iyz="0" izz="0.042673"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
mesh filename="meshes/mini_body.obj"/!!!
|
||||
<box size="0.23 0.18 0.1"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
mesh filename="meshes/mini_body.obj"/
|
||||
<box size="0.23 0.18 0.1"/>
|
||||
</geometry>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
</collision>
|
||||
<material name="cheetah_material"/>
|
||||
</link>
|
||||
|
||||
-->
|
||||
<link name="base">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
</visual>
|
||||
<inertial>
|
||||
<origin rpy="0 0 0" xyz="0.012731 0.002186 0.000515"/>
|
||||
<mass value="3.3"/>
|
||||
<inertia ixx="0.01683993" ixy="8.3902e-05" ixz="0.000597679" iyy="0.056579028" iyz="2.5134e-05" izz="0.064713601"/>
|
||||
</inertial>
|
||||
</link>
|
||||
<joint name="floating_base" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<parent link="base"/>
|
||||
<child link="trunk"/>
|
||||
</joint>
|
||||
<link name="trunk">
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.23 0.18 0.1"/>
|
||||
</geometry>
|
||||
<!-- <geometry>
|
||||
<mesh filename="../meshes/trunk.dae" scale="1 1 1"/>
|
||||
</geometry> -->
|
||||
<material name="cheetah_material"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.23 0.18 0.1"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
<material name="cheetah_material"/>
|
||||
</link>
|
||||
<!-- <joint name="imu_joint" type="fixed">
|
||||
<parent link="trunk"/>
|
||||
<child link="imu_link"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
</joint>
|
||||
<link name="imu_link">
|
||||
<inertial>
|
||||
<mass value="0.001"/>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size="0.001 0.001 0.001"/>
|
||||
</geometry>
|
||||
<material name="red"/>
|
||||
</visual> -->
|
||||
<!-- <collision>
|
||||
<origin rpy="0 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<box size=".001 .001 .001"/>
|
||||
</geometry>
|
||||
</collision>
|
||||
</link> -->
|
||||
|
||||
|
||||
<!--!!!!!!!!!!!! Front Left Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="FL_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="0.19 0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="FL_hip"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FL_hip">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<origin rpy="0. 1.5708 -1.5708" xyz="-0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<!--origin rpy="3.141592 0.0 1.5708" xyz="-0.055 0.0 0.0"/-->
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<!--origin rpy="0 0 -1.5708" xyz="-0.055 0 0"/-->
|
||||
<origin rpy="0 1.5708 -1.5708" xyz="-0.055 0.0 0.0"/>
|
||||
</collision>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<!--origin rpy="0 0 -1.5708" xyz="-0.055 0 0"/-->
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FL_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0.081 0"/>
|
||||
<parent link="FL_hip"/>
|
||||
<child link="FL_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="FL_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
|
||||
<joint name="FL_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.062 0.00"/>
|
||||
<parent link="FL_hip"/>
|
||||
<child link="FL_thigh"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FL_thigh">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FL_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="FL_thigh"/>
|
||||
<child link="FL_calf"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="FL_calf">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 -0.095"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0 0 -0.095"/>
|
||||
</collision>
|
||||
</link>
|
||||
<!-- Adapter to Foot joint -->
|
||||
<joint name="FL_foot_fixed" type="fixed">
|
||||
<parent link="FL_calf"/>
|
||||
<child link="FL_foot"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.19"/>
|
||||
</joint>
|
||||
<link name="FL_foot">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.0"/>
|
||||
<inertia ixx="0.000025" ixy="0" ixz="0.0" iyy="0.000025" iyz="0" izz="0.000025"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--!!!!!!!!!!!! Front Right Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
|
||||
<!--!!!!Joint!!!!!!!!!!!!-->
|
||||
<joint name="FR_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="0.19 -0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="FR_hip"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FR_hip">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<!--origin rpy="3.141592 0.0 1.5708" xyz="-0.055 0.0 0.0"/-->
|
||||
<origin rpy="3.141592 1.5708 1.5708" xyz="-0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<!--origin rpy="3.141592 0.0 1.5708" xyz="-0.055 0.0 0.0"/-->
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<origin rpy="3.141592 1.5708 1.5708" xyz="-0.055 0 0"/>
|
||||
</collision>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="-0.055 0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FR_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0.081 0"/>
|
||||
<parent link="FR_hip"/>
|
||||
<child link="FR_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="FR_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
|
||||
<joint name="FR_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.062 0.00"/>
|
||||
<parent link="FR_hip"/>
|
||||
<child link="FR_thigh"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="FR_thigh">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="FR_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="FR_thigh"/>
|
||||
<child link="FR_calf"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="FR_calf">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 -0.095"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 -0.095"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!-- Adapter to Foot joint -->
|
||||
<joint name="FR_foot_fixed" type="fixed">
|
||||
<parent link="FR_calf"/>
|
||||
<child link="FR_foot"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.19"/>
|
||||
</joint>
|
||||
|
||||
<link name="FR_foot">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.0"/>
|
||||
<inertia ixx="0.000025" ixy="0" ixz="0.0" iyy="0.000025" iyz="0" izz="0.000025"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0" xyz="0.0 0.0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--!!!!!!!!!!!! Hind Left Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="RL_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="-0.19 0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="RL_hip"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RL_hip">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<!--origin rpy="3.141592 0.0 -1.5708" xyz="0.055 0.0 0.0"/-->
|
||||
<origin rpy="3.141592 1.5708 -1.5708" xyz="0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<!--origin rpy="3.141592 0.0 1.5708" xyz="-0.055 0.0 0.0"/-->
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<origin rpy="3.141592 1.5708 -1.5708" xyz="0.055 0 0"/>
|
||||
</collision>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</collision>
|
||||
</link>
|
||||
<joint name="RL_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0.081 0"/>
|
||||
<parent link="RL_hip"/>
|
||||
<child link="RL_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="RL_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
|
||||
<joint name="RL_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.062 0.00"/>
|
||||
<parent link="RL_hip"/>
|
||||
<child link="RL_thigh"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RL_thigh">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RL_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="RL_thigh"/>
|
||||
<child link="RL_calf"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="RL_calf">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 -0.095"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 -0.095"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<!-- Adapter to Foot joint -->
|
||||
<joint name="RL_foot_fixed" type="fixed">
|
||||
<parent link="RL_calf"/>
|
||||
<child link="RL_foot"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.19"/>
|
||||
</joint>
|
||||
|
||||
<link name="RL_foot">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.0"/>
|
||||
<inertia ixx="0.000025" ixy="0" ixz="0.0" iyy="0.000025" iyz="0" izz="0.000025"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0" xyz="0.0 0.0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
|
||||
<!--!!!!!!!!!!!! Hind Right Leg !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!-->
|
||||
<joint name="RR_hip_joint" type="revolute">
|
||||
<axis xyz="1 0 0"/>
|
||||
<origin rpy="0 0 0" xyz="-0.19 -0.049 0.0"/>
|
||||
<parent link="trunk"/>
|
||||
<child link="RR_hip"/>
|
||||
<limit effort="18" lower="-1.6" upper="1.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RR_hip">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.54"/>
|
||||
<origin xyz="0.0 0.036 0."/>
|
||||
<inertia ixx="0.000381" ixy="0.000058" ixz="0.00000045"
|
||||
iyy="0.000560" iyz="0.00000095" izz="0.000444"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<!--origin rpy="0.0 0.0 1.5708" xyz="0.055 0.0 0.0"/-->
|
||||
<origin rpy="0.0 1.5708 1.5708" xyz="0.055 0.0 0.0"/>
|
||||
</visual>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<!--origin rpy="3.141592 0.0 1.5708" xyz="-0.055 0.0 0.0"/-->
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.01" radius = "0.0475"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 1.5708 1.5708" xyz="0.055 0.0 0.0"/>
|
||||
</collision>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_abad.obj"/-->
|
||||
<cylinder length ="0.06" radius = "0.0425"/>
|
||||
</geometry>
|
||||
<origin rpy="1.5708 0.0 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RR_hip_fixed" type="fixed">
|
||||
<origin rpy="0 0 0" xyz="0 0.081 0"/>
|
||||
<parent link="RR_hip"/>
|
||||
<child link="RR_thigh_shoulder"/>
|
||||
</joint>
|
||||
<!-- this link is only for collision -->
|
||||
<link name="RR_thigh_shoulder">
|
||||
<!-- <collision>
|
||||
<origin rpy="1.57079632679 0 0" xyz="0 0 0"/>
|
||||
<geometry>
|
||||
<cylinder length="0.032" radius="0.041"/>
|
||||
</geometry>
|
||||
</collision> -->
|
||||
</link>
|
||||
|
||||
<joint name="RR_thigh_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.062 0.00"/>
|
||||
<parent link="RR_hip"/>
|
||||
<child link="RR_thigh"/>
|
||||
<limit effort="18" lower="-2.6" upper="2.6" velocity="40"/>
|
||||
</joint>
|
||||
<link name="RR_thigh">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.634"/>
|
||||
<origin xyz="0.0 0.016 -0.02"/>
|
||||
<inertia ixx="0.001983" ixy="0.000245" ixz="0.000013"
|
||||
iyy="0.002103" iyz="0.0000015" izz="0.000408"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_upper_link.obj"/-->
|
||||
<cylinder length ="0.17" radius = "0.015"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.105"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="RR_calf_joint" type="revolute">
|
||||
<axis xyz="0 -1 0"/>
|
||||
<origin rpy="0.0 0 0.0" xyz="0.0 0.0 -0.209"/>
|
||||
<parent link="RR_thigh"/>
|
||||
<child link="RR_calf"/>
|
||||
<limit effort="26" lower="-2.6" upper="2.6" velocity="26"/>
|
||||
</joint>
|
||||
<link name="RR_calf">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.064"/>
|
||||
<origin xyz="0.0 0.0 -0.209"/>
|
||||
<inertia ixx="0.000245" ixy="0" ixz="0.0" iyy="0.000248" iyz="0" izz="0.000006"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 -0.095"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<cylinder length ="0.15" radius = "0.01"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0 0 -0.095"/>
|
||||
</collision>
|
||||
|
||||
</link>
|
||||
|
||||
<!-- Adapter to Foot joint -->
|
||||
<joint name="RR_foot_fixed" type="fixed">
|
||||
<parent link="RR_calf"/>
|
||||
<child link="RR_foot"/>
|
||||
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.19"/>
|
||||
</joint>
|
||||
|
||||
<link name="RR_foot">
|
||||
<material name="cheetah_material"/>
|
||||
<inertial>
|
||||
<mass value="0.0"/>
|
||||
<inertia ixx="0.000025" ixy="0" ixz="0.0" iyy="0.000025" iyz="0" izz="0.000025"/>
|
||||
</inertial>
|
||||
<visual>
|
||||
<geometry>
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0.0" xyz="0.0 0.0 0.0"/>
|
||||
</visual>
|
||||
<collision>
|
||||
<geometry>
|
||||
<!--mesh filename="meshes/mini_lower_link.obj"/-->
|
||||
<sphere radius = "0.0175"/>
|
||||
</geometry>
|
||||
<origin rpy="0.0 3.141592 0" xyz="0.0 0.0 0"/>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
</robot>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 546 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user