Skip to content
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

Kinematics and Dynamics #16

Draft
wants to merge 89 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
01bec92
[dirty] redo of kinematics/dynamics testing
alberthli Nov 9, 2023
5001a6b
wrote forward kinematics test by comparing to pinocchio
alberthli Nov 9, 2023
0d1b3b7
[dirty] pinocchio utils + refined FK test
alberthli Nov 9, 2023
7bcace7
added functionality that parses actuators from URDF and ensures that …
alberthli Nov 10, 2023
c4582e4
added test for actuator loading
alberthli Nov 10, 2023
9b69372
added some guidance in the README about model loading
alberthli Nov 10, 2023
9037cd9
update README with info about transmission tag
alberthli Nov 10, 2023
909b6a2
initial bash script and README update
alberthli Nov 10, 2023
7635df2
update CI workflow and consolidate installation into one command (+sy…
alberthli Nov 10, 2023
18d2ccf
add apt dependencies to CI workflow + minor README update
alberthli Nov 10, 2023
a91b10d
edit workflow to add conda environment
alberthli Nov 10, 2023
59d24e8
try changing a path in the workflow
alberthli Nov 10, 2023
b85fb9c
I have no idea how paths work in github workflows...
alberthli Nov 10, 2023
4d84eeb
change order of steps in workflow
alberthli Nov 10, 2023
c85cfa3
update CI with disk space freeing action + cache conda env
alberthli Nov 10, 2023
caa4cc3
add shell specification to actions so conda is activated correctly in…
alberthli Nov 10, 2023
3130899
check workflow caching + add more processors to cmake commands
alberthli Nov 10, 2023
397f42c
remove less space from test system to reduce workflow runtime
alberthli Nov 10, 2023
d1be7b2
Merge branch 'main' of github.com:Caltech-AMBER/ambersim into source-…
alberthli Nov 10, 2023
58f7043
Merge branch 'source-install-option' of github.com:Caltech-AMBER/ambe…
alberthli Nov 10, 2023
051551b
update workflow config to only run when PRs are marked 'ready for rev…
alberthli Nov 10, 2023
3c77165
updated to allow newton solver to be default if mujoco version is gre…
alberthli Nov 10, 2023
422d51e
bugfix: install mjx from source in addition to mujoco
alberthli Nov 10, 2023
2bd6875
minor update to README
alberthli Nov 10, 2023
e51ef10
split up installation script into two: one specifically for installin…
alberthli Nov 10, 2023
bed2d1d
first stab at saving out a built wheel as an artifact
alberthli Nov 10, 2023
e7910b7
add workflow_dispatch to allow manually triggering the workflow
alberthli Nov 10, 2023
e44fab9
add input for specifying branch
alberthli Nov 10, 2023
0d13524
change to ready_for_review
alberthli Nov 10, 2023
4334596
fix run command
alberthli Nov 10, 2023
5c11252
minor updates to workflow and mj source script to hopefully resolve i…
alberthli Nov 10, 2023
8cb7311
fixed minor bug with tar/wheel path
alberthli Nov 10, 2023
c650518
fixed bug where wheel wasn't saved to right directory
alberthli Nov 11, 2023
cf190f5
bug in workflow preventing it from running
alberthli Nov 11, 2023
90b9e42
hopefully fixed conda env caching and artifact relative path error
alberthli Nov 11, 2023
8148ac9
test whether artifact is retrieved correctly for code checks
alberthli Nov 11, 2023
436288a
try to trigger test workflow
alberthli Nov 11, 2023
a14773f
try to fix weird bug where workflow doesn't run
alberthli Nov 11, 2023
876f9e3
some changes to workflows to try to find the cached artifact correctly
alberthli Nov 11, 2023
1cbf7b9
bugfix: forgot to cache mjx wheel as well
alberthli Nov 11, 2023
532f1fd
fixed bug where same identifier was used for two steps
alberthli Nov 11, 2023
5b5a4ee
fixed mjx wheel filepath
alberthli Nov 11, 2023
18fdccb
updated workflows to run at intended times; pip install new mjx; fixe…
alberthli Nov 11, 2023
e5f01ce
bugfix: missed a problematic unlink call
alberthli Nov 11, 2023
5ee2ffb
expose mujoco git hash option to installation scripts
alberthli Nov 11, 2023
ad39148
added check_artifact and search_artifact flags to artifact retrieval …
alberthli Nov 11, 2023
74b2b16
modify the mujoco nightly build workflow to run when ready to review …
alberthli Nov 11, 2023
e26c70b
trigger code check workflow after nightly build workflow completes
alberthli Nov 11, 2023
3f1b2cb
added missing id to conda cache action that was preventing conditiona…
alberthli Nov 11, 2023
7dc31f2
run the composition of the nightly build + code checks when ready for…
alberthli Nov 11, 2023
46568e6
added workflow_call to make workflows reusable
alberthli Nov 11, 2023
0996ab0
minor: README edit
alberthli Nov 11, 2023
0db0864
append git commit hash to end of artifact name to distinguish builds;…
alberthli Nov 11, 2023
9371454
conditional caching for nightly builds + download artifact from run ids
alberthli Nov 11, 2023
fc0bfa1
minor: syntax error
alberthli Nov 11, 2023
7d1e50c
bugfixes for downloading + checking hash
alberthli Nov 11, 2023
7012c2f
weird bugs regarding not downloading correctly or regexp not working
alberthli Nov 11, 2023
d2ae369
fixed stray quotation mark that might have been causing a bug
alberthli Nov 11, 2023
a595bdb
big debugging run
alberthli Nov 11, 2023
21bb4a9
hopefully better find functionality for getting cached wheels
alberthli Nov 11, 2023
c5a3a9d
try again with searching for file
alberthli Nov 11, 2023
e399755
fix find command again
alberthli Nov 11, 2023
6b06288
wrong variable name fixed
alberthli Nov 11, 2023
959fcc8
try again with extracting the cached hash
alberthli Nov 11, 2023
0ce392b
fixed pip install path
alberthli Nov 11, 2023
2e61012
add back reusable workflow
alberthli Nov 11, 2023
f6f968e
debug: scheduled nightly build
alberthli Nov 11, 2023
d4483c0
conditional mujoco upgrade - only if no wheels were downloaded
alberthli Nov 11, 2023
3ae83d2
try sharing artifact between two jobs in the same workflow
alberthli Nov 11, 2023
1cf1c21
removed two-stage nightly builds, allow mujoco to build when ready fo…
alberthli Nov 11, 2023
3e925d6
clean up workflows + add some comments
alberthli Nov 11, 2023
7896095
very minor README update
alberthli Nov 11, 2023
64d2225
added additional test to ensure that the actuators in the XML and URD…
alberthli Nov 11, 2023
56a1e7d
add parsing for mimic joints
alberthli Nov 12, 2023
c6c5930
for now, temporarily comment out the joint equality constraints in th…
alberthli Nov 12, 2023
37fd3e2
update pendulum to have capsules, add back in equality constraints fo…
alberthli Nov 12, 2023
0a91327
commented out equality-constrained joints for now, since mjx is still…
alberthli Nov 12, 2023
430e78a
changed comments to be about merging #21 instead of mistaken mjx bug
alberthli Nov 12, 2023
be65214
remove stray commented code
alberthli Nov 12, 2023
6142d22
Merge branch 'main' of github.com:Caltech-AMBER/ambersim into add-act…
alberthli Nov 12, 2023
c197600
Merge branch 'add-actuators-urdf' of github.com:Caltech-AMBER/ambersi…
alberthli Nov 12, 2023
339b739
Merge branch 'main' of github.com:Caltech-AMBER/ambersim into source-…
alberthli Nov 12, 2023
54a34ff
Merge branch 'main' of github.com:Caltech-AMBER/ambersim into kin-dyn-2
alberthli Nov 12, 2023
60df122
Merge branch 'add-actuators-urdf' of github.com:Caltech-AMBER/ambersi…
alberthli Nov 12, 2023
3b9f18f
pulled from sibling branches to get changes necessary for continued d…
alberthli Nov 12, 2023
ffc5a95
fixed adding the floating base joint + test random translations and r…
alberthli Nov 12, 2023
5a38234
[dirty] set up for dynamics test, need to figure out underactuated mi…
alberthli Nov 13, 2023
c4dc82f
[dirty] merge conflict fix
alberthli Nov 16, 2023
e9ca03a
merge conflict from upstream changes
alberthli Nov 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ambersim/utils/introspection_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ def get_equality_names(model: mj.MjModel) -> List[str]:
return [mj.mj_id2name(model, mj.mjtObj.mjOBJ_EQUALITY, i) for i in range(model.neq)]


def get_body_names(model: mj.MjModel) -> List[str]:
"""Returns a list of all geom names in a mujoco (NOT mjx) model."""
return [mj.mj_id2name(model, mj.mjtObj.mjOBJ_BODY, i) for i in range(model.nbody)]


def get_geom_names(model: mj.MjModel) -> List[str]:
"""Returns a list of all geom names in a mujoco (NOT mjx) model."""
return [mj.mj_id2name(model, mj.mjtObj.mjOBJ_GEOM, i) for i in range(model.ngeom)]
Expand Down
4 changes: 2 additions & 2 deletions ambersim/utils/io_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
from typing import Optional, Tuple, Union

import coacd
import mujoco as mj
import numpy as np
import trimesh
from dm_control import mjcf
from lxml import etree
from mujoco import mjx
from packaging import version

import mujoco as mj
from ambersim import ROOT
from ambersim.utils._internal_utils import _check_filepath
from ambersim.utils.conversion_utils import save_model_xml
from mujoco import mjx


def _add_actuators(urdf_filepath: Union[str, Path], xml_filepath: Union[str, Path]) -> None:
Expand Down
159 changes: 159 additions & 0 deletions ambersim/utils/pin_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
from typing import List, Tuple

import jax
import mujoco as mj
import numpy as np
import pinocchio as pin
from mujoco import mjx
from mujoco.mjx._src.scan import _q_jointid

from ambersim.utils.introspection_utils import get_joint_names

"""Utils for converting quantities between mujoco/mjx and pinocchio."""


def get_joint_orders(
mj_model: mj.MjModel,
pin_model: pin.Model,
) -> Tuple[List[int], List[int]]:
"""Computes the mapping from joint orders in mujoco and pinocchio.

Pinocchio maintains one extra joint relative to mujoco (the "universe" joint).
The outputs satisfy the following two tests:

assert [mj_joint_names[i] if i is not None else "universe" for i in mj2pin] == pin_joint_names
assert [pin_joint_names[i] for i in pin2mj if i is not None] == mj_joint_names

Args:
mj_model: The MuJoCo model.
pin_model: The Pinocchio model.

Returns:
pin2mj: The mapping from pinocchio joint order to mujoco joint order.
mj2pin: The mapping from mujoco joint order to pinocchio joint order.
"""
mj_joint_names = get_joint_names(mj_model)
pin_joint_names = list(pin_model.names)

mj2pin = [mj_joint_names.index(a) if a in mj_joint_names else None for a in pin_joint_names]
pin2mj = [pin_joint_names.index(a) if a in pin_joint_names else None for a in mj_joint_names]

return mj2pin, pin2mj


def mjx_xanchor_to_pin(mj2pin: List[int], mjx_data: mjx.Data) -> np.ndarray:
"""Converts the mujoco joint anchor positions to pinocchio joint placements.

Args:
mj2pin: The mapping from mujoco joint order to pinocchio joint order.
mjx_data: The mujoco data.

Returns:
xanchor_pin: The pinocchio joint placements.
"""
xanchors = np.array(mjx_data.xanchor)
_xanchor_pin = []
for mj_idx in mj2pin:
if mj_idx is not None:
_xanchor_pin.append(xanchors[mj_idx, :])
else:
_xanchor_pin.append(np.zeros(3))
return np.stack(_xanchor_pin)


def pin_xanchor_to_mjx(pin2mj: List[int], pin_data: pin.Data) -> np.ndarray:
"""Converts the pinocchio joint placements to mujoco joint anchor positions.

Args:
pin2mj: The mapping from pinocchio joint order to mujoco joint order.
pin_data: The pinocchio data.

Returns:
xanchor_mj: The mujoco joint anchor positions.
"""
_xanchor_mj = []
for pin_idx in pin2mj:
if pin_idx is not None:
_xanchor_mj.append(pin_data.oMi[pin_idx].translation)
else:
_xanchor_mj.append(np.zeros(3))
return np.stack(_xanchor_mj)


def mjx_qpos_to_pin(qpos_mjx: jax.Array, mjx_model: mjx.Model, mj2pin: List[int]) -> np.ndarray:
"""Converts the mujoco joint positions to pinocchio joint positions.

Args:
qpos_mjx: The mjx joint positions.
mjx_model: The mjx model.
mj2pin: The mapping from mujoco joint order to pinocchio joint order.

Returns:
qpos_pin: The pinocchio joint positions.
"""
qpos_mjx = np.array(qpos_mjx)
_qpos_pin = []
jnt_types = mjx_model.jnt_type
for mj_idx in mj2pin:
# ignore Nones
if mj_idx is None:
continue

# check how to append based on joint type
jnt_type = jnt_types[mj_idx]
startidx = mjx_model.jnt_qposadr[mj_idx]

if jnt_type == 0: # FREE, 7-dimensional
# pinocchio uses the (x, y, z, w) convention
_qpos_pin.append(qpos_mjx[startidx : startidx + 3])
quat_wxyz = qpos_mjx[startidx + 3 : startidx + 7]
quat_xyzw = np.concatenate((quat_wxyz[1:], quat_wxyz[:1]))
_qpos_pin.append(quat_xyzw)
elif jnt_type == 1: # BALL, 4-dimensional
quat_wxyz = qpos_mjx[startidx : startidx + 4]
quat_xyzw = np.concatenate((quat_wxyz[1:], quat_wxyz[:1]))
_qpos_pin.append(quat_xyzw)
elif jnt_type == 2: # SLIDE, 3-dimensional
_qpos_pin.append(qpos_mjx[startidx : startidx + 3])
else: # HINGE, 1-dimensional
_qpos_pin.append(qpos_mjx[startidx : startidx + 1])

return np.concatenate(_qpos_pin)


def mjx_qvel_to_pin(qvel_mjx: jax.Array, mjx_model: mjx.Model, mj2pin: List[int]) -> np.ndarray:
"""Converts the mujoco joint velocities to pinocchio joint velocities.

Notice that there is one less coordinate for rotational velocities compared to quaternion coordinates.

Args:
qvel_mjx: The mjx joint velocities.
mjx_model: The mjx model.
mj2pin: The mapping from mujoco joint order to pinocchio joint order.

Returns:
qvel_pin: The pinocchio joint velocities.
"""
qvel_mjx = np.array(qvel_mjx)
_qvel_pin = []
jnt_types = mjx_model.jnt_type
for mj_idx in mj2pin:
# ignore Nones
if mj_idx is None:
continue

# check how to append based on joint type
jnt_type = jnt_types[mj_idx]
startidx = mjx_model.jnt_dofadr[mj_idx]

if jnt_type == 0: # FREE, 6-dimensional
_qvel_pin.append(qvel_mjx[startidx : startidx + 3])
_qvel_pin.append(qvel_mjx[startidx + 3 : startidx + 6])
elif jnt_type == 1: # BALL, 3-dimensional
_qvel_pin.append(qvel_mjx[startidx : startidx + 3])
elif jnt_type == 2: # SLIDE, 1-dimensional
_qvel_pin.append(qvel_mjx[startidx : startidx + 1])
else: # HINGE, 1-dimensional
_qvel_pin.append(qvel_mjx[startidx : startidx + 1])

return np.concatenate(_qvel_pin)
36 changes: 36 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# instructions for installing from source are taken from two places:
# (1) https://mujoco.readthedocs.io/en/latest/programming/#building-from-source
# (2) https://mujoco.readthedocs.io/en/latest/python.html#building-from-source

set -e

dev=false
source=false
while getopts dsh: flag; do
case "${flag}" in
d) dev=true;; # Install development dependencies
s) source=true;; # Install MuJoCo from source
h) hash=${OPTARG};; # Hash of the MuJoCo commit to install
esac
done

# Install regular or development dependencies
if [ "$dev" = true ] ; then
echo "[NOTE] Installing development dependencies..."
pip install --upgrade --upgrade-strategy only-if-needed -e .[all] \
--find-links https://storage.googleapis.com/jax-releases/jax_cuda_releases.html \
--find-links https://download.pytorch.org/whl/cu118
pre-commit autoupdate
pre-commit install
else
echo "[NOTE] Installing non-developer dependencies..."
pip install --upgrade --upgrade-strategy only-if-needed -e . --default-timeout=100 future \
--find-links https://storage.googleapis.com/jax-releases/jax_cuda_releases.html \
--find-links https://download.pytorch.org/whl/cu118
fi

# Checking whether to install mujoco from source
if [ "$source" = true ] ; then
bash install_mj_source.sh -h "$hash"
fi
101 changes: 101 additions & 0 deletions install_mj_source.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/bin/bash

while getopts h: flag; do
case "${flag}" in
h) hash=${OPTARG};; # Hash of the MuJoCo commit to install
esac
done

echo -e "\n[NOTE] Installing mujoco from source..."

# the script directory and mujoco directory
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
mujoco_dir="$script_dir/../mujoco"

# check whether we already have the most recent release cached to save build time
LATEST_GIT_HASH=$(git ls-remote https://github.com/google-deepmind/mujoco HEAD | awk '{ print $1}')
if [ -d "$mujoco_dir/python/dist" ]; then
tar_path=$(find "$mujoco_dir/python/dist" -name "mujoco-*${LATEST_GIT_HASH}.tar.gz" 2>/dev/null)
whl_path=$(find "$mujoco_dir/python/dist" -name "mujoco-*.whl" 2>/dev/null)

if [ -f "$tar_path" ]; then
echo -e "[NOTE] Found cached mujoco tar.gz file..."
if [ -f "$whl_path" ]; then
echo -e "[NOTE] Wheel found! Installing from wheel..."
cd "$mujoco_dir/python/dist"

# [Nov 10, 2023] we install with --no-deps because this would upgrade numpy to a version incompatible with cmeel-boost 1.82.0
MUJOCO_PATH="$mujoco_dir/mujoco_install" MUJOCO_PLUGIN_PATH="$mujoco_dir/plugin" pip install --no-deps --force-reinstall "$whl_path"
cd "$mujoco_dir/mjx"
pip install --no-deps --force-reinstall .
exit 0
else
echo -e "[NOTE] No wheel found! Building it and installing..."
cd "$mujoco_dir/python/dist"
MUJOCO_PATH="$mujoco_dir/mujoco_install" MUJOCO_PLUGIN_PATH="$mujoco_dir/plugin" pip wheel -w $(dirname "whl_path") "$tar_path"

# [Nov 10, 2023] we install with --no-deps because this would upgrade numpy to a version incompatible with cmeel-boost 1.82.0
MUJOCO_PATH="$mujoco_dir/mujoco_install" MUJOCO_PLUGIN_PATH="$mujoco_dir/plugin" pip install --no-deps --force-reinstall "$whl_path"
cd "$mujoco_dir/mjx"
pip install --no-deps --force-reinstall .
exit 0
fi
fi
fi

# Check if CMake and a compiler are installed
command -v cmake &> /dev/null || { echo "CMake is not installed. Please install CMake before proceeding."; exit 1; }
command -v g++ &> /dev/null || { echo "C++ compiler is not available. Please install a C++ compiler before proceeding."; exit 1; }


# Clone the Mujoco repo to the directory above where this script is located
# If it exists already, then just git pull new changes
if [ -d "$mujoco_dir" ]; then
echo "Mujoco exists already - running git pull to update it!"
cd "$mujoco_dir"
git pull origin main
if [ ! -z "$hash" ]; then
echo "Checking out with provided commit hash!"
git checkout "$hash"
fi
else
echo "Mujoco does not exist - cloning it!"
git clone https://github.com/google-deepmind/mujoco.git "$mujoco_dir"
if [ ! -z "$hash" ]; then
echo "Checking out to provided hash!"
(cd "$mujoco_dir" && git checkout "$hash")
fi
fi

# Configure and build
export MAKEFLAGS="-j$(nproc)" # allow the max number of processors when building
cd "$mujoco_dir"
if [ -z "$hash" ]; then
SAVED_GIT_HASH=$(git rev-parse HEAD) # getting the git hash
else
SAVED_GIT_HASH="$hash"
fi
cmake . && cmake --build .

# Install Mujoco
cmake . -DCMAKE_INSTALL_PREFIX="./mujoco_install" && cmake --install .

# Generate source distribution required for Python bindings
cd "$mujoco_dir/python"
./make_sdist.sh
tar_path=$(find "$mujoco_dir/python/dist" -name 'mujoco-*.tar.gz' 2>/dev/null)

# Renaming the tar file by appending the commit hash
new_tar_path="$(dirname "$tar_path")/$(basename "$tar_path" .tar.gz)-$SAVED_GIT_HASH.tar.gz"
mv "$tar_path" "$new_tar_path"

# manually building wheel so we can cache it
cd "$mujoco_dir/python/dist"
MUJOCO_PATH="$mujoco_dir/mujoco_install" MUJOCO_PLUGIN_PATH="$mujoco_dir/plugin" pip wheel -w $(dirname "new_tar_path") "$new_tar_path"

# installing mujoco from wheel and then finally mjx
whl_path=$(find "$mujoco_dir/python/dist" -name "mujoco-*.whl" 2>/dev/null)
MUJOCO_PATH="$mujoco_dir/mujoco_install" MUJOCO_PLUGIN_PATH="$mujoco_dir/plugin" pip install --no-deps --force-reinstall "$whl_path"
cd "$mujoco_dir/mjx"
pip wheel -w "$mujoco_dir/mjx" --no-deps .
pip install --no-deps --force-reinstall .
Loading