-
Notifications
You must be signed in to change notification settings - Fork 19
提交修改代码,模型、任务和配置 #468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
提交修改代码,模型、任务和配置 #468
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
81c2ead
初次代码提交
2088376133zh 7bfc7fa
提交论文第一章
2088376133zh b490c72
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng 7483e6a
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng f07ceaf
初始化项目,上传基础文件
2088376133zh 1114ad6
添加依赖配置与环境说明
2088376133zh 3702d52
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng 99653f8
更改文件夹名称
2088376133zh adc7ed1
添加生物力学肌肉双臂模型与XML文件
2088376133zh 2046276
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng 05babf0
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng 285d64e
添加代码修改文件名
2088376133zh 3a74583
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng 2d0de79
删除旧文件并更新项目结构
2088376133zh a8a588b
提交代码文件
2088376133zh 83fdb97
修改文件夹
2088376133zh 481b33d
提交代码文件
2088376133zh d7d9dda
修改文件
2088376133zh 63b9d5f
重新提交代码文件
2088376133zh 4e961dc
分批上次代码文件
2088376133zh afd3b6f
部分
2088376133zh f361622
删除
2088376133zh f9fb3f7
提交代码
2088376133zh 863961a
分批
2088376133zh e96cdcd
删除
2088376133zh 06fb33d
代码
2088376133zh 72dd0c2
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng c833d3b
提交
2088376133zh 3c7f398
提交部分
2088376133zh 6e536de
重新交
2088376133zh 491d38b
提交部分
2088376133zh b3051e7
Merge branch 'OpenHUTB:master' into master
ZH1024-Heng a9920ea
提交修改的代码
2088376133zh 3862b2b
提交修改代码
2088376133zh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from uitb.simulator import Simulator |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| from .mobl_arms_index.MoblArmsIndex import MoblArmsIndex | ||
| from .mobl_arms.MoblArms import MoblArms | ||
| from .mobl_arms_wrist.MoblArmsWrist import MoblArmsWrist | ||
| from .mobl_arms_bimanual.MoblArmsBimanual import MoblArmsBimanual | ||
| from .mobl_arms_bimanual_motor.MoblArmsBimanualMotor import MoblArmsBimanualMotor |
Binary file added
BIN
+605 Bytes
hci/user-in-the-box/uitb/bm_models/__pycache__/__init__.cpython-311.pyc
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+15.6 KB
hci/user-in-the-box/uitb/bm_models/__pycache__/effort_models.cpython-311.pyc
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,368 @@ | ||
| import numpy as np | ||
| import xml.etree.ElementTree as ET | ||
| import pathlib | ||
| import os | ||
| import shutil | ||
| import inspect | ||
| import mujoco | ||
| from abc import ABC, abstractmethod | ||
| import importlib | ||
| from typing import final | ||
|
|
||
| from ..utils.functions import parent_path | ||
| from ..utils import element_tree as ETutils | ||
|
|
||
|
|
||
| class BaseBMModel(ABC): | ||
|
|
||
| def __init__(self, model, data, **kwargs): | ||
| """Initializes a new `BaseBMModel`. | ||
|
|
||
| Args: | ||
| model: Mujoco model instance of the simulator. | ||
| data: Mujoco data instance of the simulator. | ||
| **kwargs: Many keywords that should be documented somewhere | ||
| """ | ||
|
|
||
| # Initialise mujoco model of the biomechanical model, easier to manipulate things | ||
| bm_model = mujoco.MjModel.from_xml_path(self.get_xml_file()) | ||
|
|
||
| # Get an rng | ||
| self._rng = np.random.default_rng(kwargs.get("random_seed", None)) | ||
|
|
||
| # Total number of actuators | ||
| self._nu = bm_model.nu | ||
|
|
||
| # Number of muscle actuators | ||
| self._na = bm_model.na | ||
|
|
||
| # Number of motor actuators | ||
| self._nm = self._nu - self._na | ||
| self._motor_act = np.zeros((self._nm,)) | ||
| self._motor_alpha = 0.9 | ||
|
|
||
| # Get actuator names (muscle and motor) | ||
| self._actuator_names = [mujoco.mj_id2name(bm_model, mujoco.mjtObj.mjOBJ_ACTUATOR, i) for i in range(bm_model.nu)] | ||
| self._muscle_actuator_names = set(np.array(self._actuator_names)[bm_model.actuator_trntype==3]) | ||
| self._motor_actuator_names = set(self._actuator_names) - self._muscle_actuator_names | ||
|
|
||
| # Sort the names to preserve original ordering (not really necessary but looks nicer) | ||
| self._muscle_actuator_names = sorted(self._muscle_actuator_names, key=self._actuator_names.index) | ||
| self._motor_actuator_names = sorted(self._motor_actuator_names, key=self._actuator_names.index) | ||
|
|
||
| # Find actuator indices in the simulation | ||
| self._muscle_actuators = [mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_ACTUATOR, actuator_name) | ||
| for actuator_name in self._muscle_actuator_names] | ||
| self._motor_actuators = [mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_ACTUATOR, actuator_name) | ||
| for actuator_name in self._motor_actuator_names] | ||
|
|
||
| # Get joint names (dependent and independent) | ||
| self._joint_names = [mujoco.mj_id2name(bm_model, mujoco.mjtObj.mjOBJ_JOINT, i) for i in range(bm_model.njnt)] | ||
| _eq_on = getattr(bm_model, "eq_active", None) | ||
| if _eq_on is None: | ||
| _eq_on = getattr(bm_model, "eq_active0", np.ones(bm_model.neq)) | ||
| _eq_on = np.asarray(_eq_on).astype(bool) | ||
| self._dependent_joint_names = {self._joint_names[idx] for idx in | ||
| np.unique(bm_model.eq_obj1id[_eq_on])} \ | ||
| if bm_model.eq_obj1id is not None else set() | ||
| self._independent_joint_names = set(self._joint_names) - self._dependent_joint_names | ||
|
|
||
| # Sort the names to preserve original ordering (not really necessary but looks nicer) | ||
| self._dependent_joint_names = sorted(self._dependent_joint_names, key=self._joint_names.index) | ||
| self._independent_joint_names = sorted(self._independent_joint_names, key=self._joint_names.index) | ||
|
|
||
| # Find dependent and independent joint indices in the simulation | ||
| self._dependent_joints = [mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, joint_name) | ||
| for joint_name in self._dependent_joint_names] | ||
| self._independent_joints = [mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, joint_name) | ||
| for joint_name in self._independent_joint_names] | ||
|
|
||
| # If there are 'free' type of joints, we'll need to be more careful with which dof corresponds to | ||
| # which joint, for both qpos and qvel/qacc. There should be exactly one dof per independent/dependent joint. | ||
| def get_dofs(joint_indices): | ||
| qpos = [] | ||
| dofs = [] | ||
| for joint_idx in joint_indices: | ||
| if model.jnt_type[joint_idx] not in [mujoco.mjtJoint.mjJNT_HINGE, mujoco.mjtJoint.mjJNT_SLIDE]: | ||
| raise NotImplementedError(f"Only 'hinge' and 'slide' joints are supported, joint " | ||
| f"{self._joint_names[joint_idx]} is of type {mujoco.mjtJoint(model.jnt_type[joint_idx]).name}") | ||
| qpos.append(model.jnt_qposadr[joint_idx]) | ||
| dofs.append(model.jnt_dofadr[joint_idx]) | ||
| return qpos, dofs | ||
| self._dependent_qpos, self._dependent_dofs = get_dofs(self._dependent_joints) | ||
| self._independent_qpos, self._independent_dofs = get_dofs(self._independent_joints) | ||
|
|
||
| # Get the effort model; some models might need to know dt | ||
| self._effort_model = self.get_effort_model(kwargs.get("effort_model", {"cls": "Zero"}), dt=kwargs["dt"]) | ||
|
|
||
| # Define signal-dependent noise | ||
| self._sigdepnoise_type = kwargs.get("sigdepnoise_type", None) #"white") | ||
| self._sigdepnoise_level = kwargs.get("sigdepnoise_level", 0.103) | ||
| self._sigdepnoise_rng = np.random.default_rng(kwargs.get("random_seed", None)) | ||
| self._sigdepnoise_acc = 0 #only used for red/Brownian noise | ||
|
|
||
| # Define constant (i.e., signal-independent) noise | ||
| self._constantnoise_type = kwargs.get("constantnoise_type", None) #"white") | ||
| self._constantnoise_level = kwargs.get("constantnoise_level", 0.185) | ||
| self._constantnoise_rng = np.random.default_rng(kwargs.get("random_seed", None)) | ||
| self._constantnoise_acc = 0 #only used for red/Brownian noise | ||
|
|
||
| ############ The methods below you should definitely overwrite as they are important ############ | ||
|
|
||
| @classmethod | ||
| @abstractmethod | ||
| def _get_floor(cls): | ||
| """ If there's a floor in the bm_model.xml file it should be defined here. | ||
|
|
||
| Returns: | ||
| * None if there is no floor in the file | ||
| * A dict like {"tag": "geom", "name": "name-of-the-geom"}, where "tag" indicates what kind of element the floor | ||
| is, and "name" is the name of the element. | ||
| """ | ||
| pass | ||
|
|
||
|
|
||
| ############ The methods below are overwritable but often don't need to be overwritten ############ | ||
|
|
||
| def _reset(self, model, data): | ||
| """ Resets the biomechanical model. """ | ||
|
|
||
| # Randomly sample qpos, qvel, act around zero values | ||
| nq = len(self._independent_qpos) | ||
| qpos = self._rng.uniform(low=np.ones((nq,))*-0.05, high=np.ones((nq,))*0.05) | ||
| qvel = self._rng.uniform(low=np.ones((nq,))*-0.05, high=np.ones((nq,))*0.05) | ||
| act = self._rng.uniform(low=np.zeros((self._na,)), high=np.ones((self._na,))) | ||
|
|
||
| # Set qpos and qvel | ||
| data.qpos[self._dependent_qpos] = 0 | ||
| data.qpos[self._independent_qpos] = qpos | ||
| data.qvel[self._dependent_dofs] = 0 | ||
| data.qvel[self._independent_dofs] = qvel | ||
| data.act[self._muscle_actuators] = act | ||
|
|
||
| # Sample random initial values for motor activation | ||
| self._motor_act = self._rng.uniform(low=np.zeros((self._nm,)), high=np.ones((self._nm,))) | ||
| # Reset smoothed average of motor actuator activation | ||
| self._motor_smooth_avg = np.zeros((self._nm,)) | ||
|
|
||
| # Reset accumulative noise | ||
| self._sigdepnoise_acc = 0 | ||
| self._constantnoise_acc = 0 | ||
|
|
||
| def _update(self, model, data): | ||
| """ Update the biomechanical model after a step has been taken in the simulator. """ | ||
| pass | ||
|
|
||
| def _get_state(self, model, data): | ||
| """ Return the state of the biomechanical model. These states are used only for logging/evaluation, not for RL | ||
| training | ||
|
|
||
| Args: | ||
| model: Mujoco model instance of the simulator. | ||
| data: Mujoco data instance of the simulator. | ||
|
|
||
| Returns: | ||
| A dict where each key should have a float or a numpy vector as their value | ||
| """ | ||
| return dict() | ||
|
|
||
| def set_ctrl(self, model, data, action): | ||
| """ Set control values for the biomechanical model. | ||
|
|
||
| Args: | ||
| model: Mujoco model instance of the simulator. | ||
| data: Mujoco data instance of the simulator. | ||
| action: Action values between [-1, 1] | ||
|
|
||
| """ | ||
|
|
||
| _selected_motor_control = np.clip(self._motor_act + action[:self._nm], 0, 1) | ||
| _selected_muscle_control = np.clip(data.act[self._muscle_actuators] + action[self._nm:], 0, 1) | ||
|
|
||
| if self._sigdepnoise_type is not None: | ||
| if self._sigdepnoise_type == "white": | ||
| _added_noise = self._sigdepnoise_level*self._sigdepnoise_rng.normal(scale=_selected_muscle_control) | ||
| _selected_muscle_control += _added_noise | ||
| elif self._sigdepnoise_type == "whiteonly": #only for debugging purposes | ||
| _selected_muscle_control = self._sigdepnoise_level*self._sigdepnoise_rng.normal(scale=_selected_muscle_control) | ||
| elif self._sigdepnoise_type == "red": | ||
| # self._sigdepnoise_acc *= 1 - 0.1 | ||
| self._sigdepnoise_acc += self._sigdepnoise_level*self._sigdepnoise_rng.normal(scale=_selected_muscle_control) | ||
| _selected_muscle_control += self._sigdepnoise_acc | ||
| else: | ||
| raise NotImplementedError(f"{self._sigdepnoise_type}") | ||
| if self._constantnoise_type is not None: | ||
| if self._constantnoise_type == "white": | ||
| _selected_muscle_control += self._constantnoise_level*self._constantnoise_rng.normal(scale=1) | ||
| elif self._constantnoise_type == "whiteonly": #only for debugging purposes | ||
| _selected_muscle_control = self._constantnoise_level*self._constantnoise_rng.normal(scale=1) | ||
| elif self._constantnoise_type == "red": | ||
| self._constantnoise_acc += self._constantnoise_level*self._constantnoise_rng.normal(scale=1) | ||
| _selected_muscle_control += self._constantnoise_acc | ||
| else: | ||
| raise NotImplementedError(f"{self._constantnoise_type}") | ||
|
|
||
| # Update smoothed online estimate of motor actuation | ||
| self._motor_act = (1 - self._motor_alpha) * self._motor_act \ | ||
| + self._motor_alpha * np.clip(_selected_motor_control, 0, 1) | ||
|
|
||
| data.ctrl[self._motor_actuators] = model.actuator_ctrlrange[self._motor_actuators,0] + self._motor_act*(model.actuator_ctrlrange[self._motor_actuators, 1] - model.actuator_ctrlrange[self._motor_actuators, 0]) | ||
| data.ctrl[self._muscle_actuators] = np.clip(_selected_muscle_control, 0, 1) | ||
|
|
||
|
|
||
| @classmethod | ||
| def get_xml_file(cls): | ||
| """ Overwrite this method if you want to call the mujoco xml file something other than 'bm_model.xml'. """ | ||
| return os.path.join(parent_path(inspect.getfile(cls)), "bm_model.xml") | ||
|
|
||
| def get_effort_model(self, specs, dt): | ||
| """ Returns an initialised object of the effort model class. | ||
|
|
||
| Overwrite this method if you want to define your effort models somewhere else. But note that in that case you need | ||
| to overwrite the 'clone' method as well since it assumes the effort models are defined in | ||
| uitb.bm_models.effort_models. | ||
|
|
||
| Args: | ||
| specs: Specifications of the effort model, in format of | ||
| {"cls": "name-of-class", "kwargs": {"kw1": value1, "kw2": value2}}} | ||
| dt: Elapsed time between two consecutive simulation steps | ||
|
|
||
| Returns: | ||
| An instance of a class that inherits from the uitb.bm_models.effort_models.BaseEffortModel class | ||
| """ | ||
| module = importlib.import_module(".".join(BaseBMModel.__module__.split(".")[:-1]) + ".effort_models") | ||
| return getattr(module, specs["cls"])(self, **{**specs.get("kwargs", {}), **{"dt": dt}}) | ||
|
|
||
| @classmethod | ||
| def clone(cls, simulator_folder, package_name): | ||
| """ Clones (i.e. copies) the relevant python files into a new location. | ||
|
|
||
| Args: | ||
| simulator_folder: Location of the simulator. | ||
| package_name: Name of the simulator (which is a python package) | ||
| """ | ||
|
|
||
| # Create 'bm_models' folder | ||
| dst = os.path.join(simulator_folder, package_name, "bm_models") | ||
| os.makedirs(dst, exist_ok=True) | ||
|
|
||
| # Copy this file | ||
| base_file = pathlib.Path(__file__) | ||
| shutil.copyfile(base_file, os.path.join(dst, base_file.name)) | ||
|
|
||
| # Create an __init__.py file with the relevant import | ||
| modules = cls.__module__.split(".") | ||
| with open(os.path.join(dst, "__init__.py"), "w") as file: | ||
| file.write("from ." + ".".join(modules[2:]) + " import " + cls.__name__) | ||
|
|
||
| # Copy bm-model folder | ||
| src = parent_path(inspect.getfile(cls)) | ||
| shutil.copytree(src, os.path.join(dst, src.stem), dirs_exist_ok=True) | ||
|
|
||
| # Copy assets | ||
| shutil.copytree(os.path.join(src, "assets"), os.path.join(simulator_folder, package_name, "assets"), | ||
| dirs_exist_ok=True) | ||
|
|
||
| # Copy effort models | ||
| shutil.copyfile(os.path.join(base_file.parent, "effort_models.py"), os.path.join(dst, "effort_models.py")) | ||
|
|
||
| @classmethod | ||
| def insert(cls, simulator_tree): | ||
| """ Inserts the biomechanical model into the simulator by integrating the xml files together. | ||
|
|
||
| Args: | ||
| simulator_tree: An `xml.etree.ElementTree` containing the parsed simulator xml file | ||
| """ | ||
|
|
||
| # Parse xml file | ||
| bm_tree = ET.parse(cls.get_xml_file()) | ||
| bm_root = bm_tree.getroot() | ||
|
|
||
| # Get simulator root | ||
| simulator_root = simulator_tree.getroot() | ||
|
|
||
| # Add defaults | ||
| ETutils.copy_or_append("default", bm_root, simulator_root) | ||
|
|
||
| # Add assets, except skybox | ||
| ETutils.copy_children("asset", bm_root, simulator_root, | ||
| exclude={"tag": "texture", "attrib": "type", "name": "skybox"}) | ||
|
|
||
| # Add bodies, except floor/ground TODO this might not be currently working | ||
| if cls._get_floor() is not None: | ||
| floor = cls._get_floor() | ||
| ETutils.copy_children("worldbody", bm_root, simulator_root, | ||
| exclude={"tag": floor["tag"], "attrib": "name", "name": floor["name"]}) | ||
| else: | ||
| ETutils.copy_children("worldbody", bm_root, simulator_root) | ||
|
|
||
| # Add tendons | ||
| ETutils.copy_children("tendon", bm_root, simulator_root) | ||
|
|
||
| # Add actuators | ||
| ETutils.copy_children("actuator", bm_root, simulator_root) | ||
|
|
||
| # Add equality constraints | ||
| ETutils.copy_children("equality", bm_root, simulator_root) | ||
|
|
||
| def close(self, **kwargs): | ||
| """ Perform any necessary clean up. """ | ||
| pass | ||
|
|
||
| ############ The methods below you should not overwrite ############ | ||
|
|
||
| @final | ||
| def update(self, model, data): | ||
| """ Updates the biomechanical model and effort model. """ | ||
| self._update(model, data) | ||
| self._effort_model.update(model, data) | ||
|
|
||
| @final | ||
| def reset(self, model, data): | ||
| """ Resets the biomechanical model and effort model. """ | ||
| self._reset(model, data) | ||
| self._effort_model.reset(model, data) | ||
| self.update(model, data) | ||
| mujoco.mj_forward(model, data) | ||
|
|
||
| @final | ||
| def get_state(self, model, data): | ||
| """ Returns the state of the biomechanical model (as a dict). """ | ||
| state = dict() | ||
| state.update(self._get_state(model, data)) | ||
| state.update(self._effort_model._get_state(model, data)) | ||
| return state | ||
|
|
||
| @final | ||
| def get_effort_cost(self, model, data): | ||
| """ Returns effort cost from the effort model. """ | ||
| return self._effort_model.cost(model, data) | ||
|
|
||
| @property | ||
| @final | ||
| def independent_joints(self): | ||
| """ Returns indices of independent joints. """ | ||
| return self._independent_joints.copy() | ||
|
|
||
| @property | ||
| @final | ||
| def independent_qpos(self): | ||
| """ Returns qpos indices of independent joints. """ | ||
| return self._independent_qpos.copy() | ||
|
|
||
| @property | ||
| @final | ||
| def independent_dofs(self): | ||
| """ Returns qvel/qacc indices of independent joints. """ | ||
| return self._independent_dofs.copy() | ||
|
|
||
| @property | ||
| @final | ||
| def nu(self): | ||
| """ Returns number of actuators (both muscle and motor). """ | ||
| return self._nu | ||
|
|
||
| @property | ||
| def motor_act(self): | ||
| """ Returns (smoothed average of) motor actuation. """ | ||
| return self._motor_act |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
所有.pyc文件需要删除,它只是运行时生成的临时文件