Skip to content

Commit 8197725

Browse files
committed
Merge branch 'master' into juelg/gravcomp-fix
2 parents 4431375 + c7e81ed commit 8197725

28 files changed

Lines changed: 329 additions & 118 deletions

File tree

‎.github/workflows/ci.yaml‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,28 @@ jobs:
4242
- name: Ensure all unittests(pytest) are passing
4343
run: xvfb-run make pytest
4444

45+
pythonpackage-macos:
46+
runs-on: macos-14
47+
env:
48+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49+
steps:
50+
- uses: actions/checkout@v4
51+
- name: Set up Python 3.11
52+
uses: actions/setup-python@v5
53+
with:
54+
python-version: "3.11"
55+
cache: "pip"
56+
- name: upgrade pip
57+
run: pip install --upgrade pip
58+
- name: Install building dependencies
59+
run: python -m pip install --group build_deps
60+
- name: Install wheel
61+
run: python -m pip install wheel
62+
- name: Build and install rcs-core
63+
run: python -m pip install -ve . --no-build-isolation
64+
- name: Import smoke test
65+
run: python -c "import rcs; from rcs import _core; import rcs.sim.sim; print('rcs-core import OK on macOS')"
66+
4567
check-paths:
4668
runs-on: ubuntu-latest
4769
outputs:

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ if __name__ == "__main__":
122122
> **Note:** This and other examples can be found in the [`examples/`]() folder.
123123
124124
## 🛠️ Installation
125+
* *Platform support: The core package `rcs-core` (MuJoCo simulation + Python API) is supported on **Linux and macOS** (Apple Silicon / arm64). The hardware extensions are **Linux only**.*
125126
* *For Python >3.11: The `rcs_realsense` extension won't work due to the `pyrealsense2` version RCS utilizes.*
126127
* *For Python >3.12: The `ompl` python module is currently not available on PyPI. If OMPL is not used, it is safe to remove this dependency in `pyproject.toml`.*
127128
### Via PyPI/pip
@@ -170,6 +171,8 @@ export RCS_PREFIX=/path/to/rcs-assets
170171

171172
RCS supports various hardware extensions to seamlessly connect your policies to the real world (e.g., FR3, xArm7, YAM, RealSense). These are located in the `extensions` directory.
172173

174+
> **Note:** Hardware extensions are supported on **Linux only**. On macOS you can use the core `rcs-core` package for simulation, but the hardware extensions are not supported.
175+
173176
To install a specific robot extension (example for Franka FR3):
174177

175178
```shell

‎cmake/FindMuJoCo.cmake‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,35 @@ if (NOT MuJoCo_FOUND)
3838
return()
3939
endif()
4040

41-
file(GLOB mujoco_library_path "${MUJOCO_PATH}/libmujoco.so.*")
42-
if (NOT mujoco_library_path)
41+
set(_mujoco_library_globs)
42+
if (APPLE)
43+
list(APPEND _mujoco_library_globs "${MUJOCO_PATH}/libmujoco.*.dylib")
44+
elseif (WIN32)
45+
list(APPEND _mujoco_library_globs "${MUJOCO_PATH}/mujoco.dll")
46+
list(APPEND _mujoco_library_globs "${MUJOCO_PATH}/bin/mujoco.dll")
47+
else()
48+
list(APPEND _mujoco_library_globs "${MUJOCO_PATH}/libmujoco.so.*")
49+
endif()
50+
51+
file(GLOB mujoco_library_paths LIST_DIRECTORIES FALSE ${_mujoco_library_globs})
52+
list(LENGTH mujoco_library_paths _mujoco_library_count)
53+
if (_mujoco_library_count EQUAL 0)
4354
set(MuJoCo_FOUND FALSE)
4455
if (MuJoCo_FIND_REQUIRED)
45-
message(FATAL_ERROR "Could not find MuJoCo. Please install MuJoCo using pip 4.")
56+
message(FATAL_ERROR "Could not find MuJoCo shared library. Searched: ${_mujoco_library_globs}")
4657
endif()
4758
return()
4859
endif()
60+
list(GET mujoco_library_paths 0 mujoco_library_path)
4961

5062
# Extract version from the library filename
5163
cmake_path(GET mujoco_library_path FILENAME mujoco_library_filename)
52-
string(REPLACE "libmujoco.so." "" MuJoCo_VERSION "${mujoco_library_filename}")
64+
set(MuJoCo_VERSION "")
65+
if (mujoco_library_filename MATCHES "^libmujoco\\.so\\.(.+)$")
66+
set(MuJoCo_VERSION "${CMAKE_MATCH_1}")
67+
elseif (mujoco_library_filename MATCHES "^libmujoco\\.(.+)\\.dylib$")
68+
set(MuJoCo_VERSION "${CMAKE_MATCH_1}")
69+
endif()
5370

5471
# Create the imported target
5572
add_library(MuJoCo::MuJoCo SHARED IMPORTED)
@@ -59,6 +76,9 @@ if (NOT MuJoCo_FOUND)
5976
PROPERTIES
6077
IMPORTED_LOCATION "${mujoco_library_path}"
6178
)
79+
if (APPLE)
80+
set_target_properties(MuJoCo::MuJoCo PROPERTIES IMPORTED_NO_SONAME TRUE)
81+
endif()
6282

6383
set(MuJoCo_FOUND TRUE)
6484
endif()

‎cmake/Findpinocchio.cmake‎

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,44 @@ if (NOT pinocchio_FOUND)
1717
return()
1818
endif()
1919

20-
# Check if the library file exists
21-
cmake_path(APPEND Python3_SITELIB cmeel.prefix lib libpinocchio_default.so OUTPUT_VARIABLE pinocchio_library_path)
22-
if (NOT EXISTS ${pinocchio_library_path})
20+
cmake_path(APPEND Python3_SITELIB cmeel.prefix lib OUTPUT_VARIABLE pinocchio_LIBRARY_DIR)
21+
22+
set(_pinocchio_default_globs)
23+
set(_pinocchio_parsers_globs)
24+
if (APPLE)
25+
list(APPEND _pinocchio_default_globs "${pinocchio_LIBRARY_DIR}/libpinocchio_default*.dylib")
26+
list(APPEND _pinocchio_parsers_globs "${pinocchio_LIBRARY_DIR}/libpinocchio_parsers*.dylib")
27+
elseif (WIN32)
28+
list(APPEND _pinocchio_default_globs "${pinocchio_LIBRARY_DIR}/pinocchio_default*.dll")
29+
list(APPEND _pinocchio_default_globs "${pinocchio_LIBRARY_DIR}/libpinocchio_default*.dll")
30+
list(APPEND _pinocchio_parsers_globs "${pinocchio_LIBRARY_DIR}/pinocchio_parsers*.dll")
31+
list(APPEND _pinocchio_parsers_globs "${pinocchio_LIBRARY_DIR}/libpinocchio_parsers*.dll")
32+
else()
33+
list(APPEND _pinocchio_default_globs "${pinocchio_LIBRARY_DIR}/libpinocchio_default.so*")
34+
list(APPEND _pinocchio_parsers_globs "${pinocchio_LIBRARY_DIR}/libpinocchio_parsers.so*")
35+
endif()
36+
37+
file(GLOB pinocchio_library_paths LIST_DIRECTORIES FALSE ${_pinocchio_default_globs})
38+
list(LENGTH pinocchio_library_paths _pinocchio_library_count)
39+
if (_pinocchio_library_count EQUAL 0)
2340
set(pinocchio_FOUND FALSE)
2441
if (pinocchio_FIND_REQUIRED)
25-
message(FATAL_ERROR "Could not find pinocchio. Please install pinocchio using pip.")
42+
message(FATAL_ERROR "Could not find pinocchio library. Searched: ${_pinocchio_default_globs}")
2643
endif()
2744
return()
2845
endif()
46+
list(GET pinocchio_library_paths 0 pinocchio_library_path)
2947

30-
# Check if the library file exists
31-
cmake_path(APPEND Python3_SITELIB cmeel.prefix lib libpinocchio_parsers.so OUTPUT_VARIABLE pinocchio_parsers_path)
32-
if (NOT EXISTS ${pinocchio_parsers_path})
48+
file(GLOB pinocchio_parsers_paths LIST_DIRECTORIES FALSE ${_pinocchio_parsers_globs})
49+
list(LENGTH pinocchio_parsers_paths _pinocchio_parsers_count)
50+
if (_pinocchio_parsers_count EQUAL 0)
3351
set(pinocchio_FOUND FALSE)
3452
if (pinocchio_FIND_REQUIRED)
35-
message(FATAL_ERROR "Could not find pinocchio parsers path. Please install pinocchio using pip.")
53+
message(FATAL_ERROR "Could not find pinocchio parsers library. Searched: ${_pinocchio_parsers_globs}")
3654
endif()
3755
return()
3856
endif()
57+
list(GET pinocchio_parsers_paths 0 pinocchio_parsers_path)
3958

4059
# Extract version from the library filename
4160
file(GLOB pinocchio_dist_info "${Python3_SITELIB}/pin-*.dist-info")

‎docs/extensions/overview.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
RCS is designed to be modular. Core functionality is kept minimal, while specific hardware support and additional features are provided through **extensions**.
44

5+
```{note}
6+
Extensions are supported on **Linux only**. The core `rcs-core` package (MuJoCo
7+
simulation + Python API) also runs on macOS (Apple Silicon / arm64), but the
8+
hardware extensions are not supported there.
9+
```
10+
511
## What is an Extension?
612

713
An extension is a separate Python package that integrates with RCS. Extensions can provide:

‎docs/getting_started/index.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
We build and test RCS on the latest Debian and on the latest Ubuntu LTS.
66

7+
```{note}
8+
**Platform support:** The core package `rcs-core` (MuJoCo simulation + Python API)
9+
is supported on **Linux and macOS** (Apple Silicon / arm64). The hardware
10+
extensions are **Linux only**.
11+
```
12+
713
### Prerequisites
814

915
1. Install the system dependencies:

‎examples/inference/franka.py‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from rcs.envs.base import ControlMode, RelativeTo
1616
from rcs.envs.configs import EmptyWorldFR3Duo
1717
from rcs.envs.storage_wrapper import StorageWrapper
18-
from rcs.utils import SimpleFrameRate
1918

2019
# from rcs_duobench.tasks.bin_sort import BinSortEnvConfig
2120
from vlagents.client import RemoteAgent
@@ -133,7 +132,6 @@ def __init__(self, env: gym.Env, cfg: InferenceConfig):
133132
cfg.jpeg_encoding,
134133
cfg.image_size,
135134
)
136-
self.frame_rate = SimpleFrameRate(self._cfg.fps)
137135
self._action_buffer = []
138136

139137
def submit_command(self, command: str) -> None:
@@ -298,9 +296,6 @@ def loop(self):
298296

299297
obs_dict = self.obs_rcs2agents(obs)
300298

301-
if ROBOT_INSTANCE == RobotPlatform.HARDWARE:
302-
self.frame_rate()
303-
304299

305300
def command_loop(controller: ModelInference) -> None:
306301
prompt = "Command [e=start, r=record, s=success/reset, q=stop/reset, o=reload, x=exit]: "
@@ -393,6 +388,7 @@ def get_env(cfg: InferenceConfig) -> gym.Env:
393388
# interpolation window of the controllers must match the rate at which we stream actions
394389
hw_cfg.robot_cfgs["left"].policy_rate = cfg.fps
395390
hw_cfg.robot_cfgs["right"].policy_rate = cfg.fps
391+
hw_cfg.frequency = cfg.fps
396392
hw_cfg.gripper_cfgs["left"].serial_number = ROBOTIQ_SERIAL["left"]
397393
hw_cfg.gripper_cfgs["right"].serial_number = ROBOTIQ_SERIAL["right"]
398394
env_rel = env_creator.create_env(hw_cfg)

‎examples/teleop/franka.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def get_env():
159159
# interpolation window of the controllers must match the rate at which we stream actions
160160
hw_cfg.robot_cfgs["left"].policy_rate = RECORD_FPS
161161
hw_cfg.robot_cfgs["right"].policy_rate = RECORD_FPS
162+
hw_cfg.frequency = RECORD_FPS
162163
hw_cfg.gripper_cfgs["left"].serial_number = ROBOTIQ_SERIAL["left"] # type: ignore
163164
hw_cfg.gripper_cfgs["right"].serial_number = ROBOTIQ_SERIAL["right"] # type: ignore
164165
env_rel = env_creator.create_env(hw_cfg)

‎extensions/rcs_fr3/src/rcs_fr3/creators.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ class FR3HardwareEnvCreatorConfig:
161161
camera_cfgs: dict[str, HardwareCameraCreatorConfig] | None = None
162162
max_relative_movement: float | tuple[float, float] | None = None
163163
relative_to: RelativeTo = RelativeTo.LAST_STEP
164+
frequency: float | None = None
165+
"""Control frequency in Hz, rate limits env.step(). None disables rate limiting."""
164166
wrapper_cfg: WrapperConfig = field(default_factory=WrapperConfig)
165167

166168

@@ -173,6 +175,8 @@ class FR3MultiHardwareEnvCreatorConfig:
173175
max_relative_movement: float | tuple[float, float] | None = None
174176
relative_to: RelativeTo = RelativeTo.LAST_STEP
175177
robot_to_shared_base_frame: dict[str, rcs.common.Pose] | None = None
178+
frequency: float | None = None
179+
"""Control frequency in Hz, rate limits env.step(). None disables rate limiting."""
176180
wrapper_cfg: WrapperConfig = field(default_factory=WrapperConfig)
177181

178182

@@ -185,7 +189,7 @@ def create_env(self, cfg: FR3HardwareEnvCreatorConfig) -> gym.Env:
185189
)
186190
robot = hw.Franka(cfg.robot_cfg, ik)
187191

188-
env: gym.Env = HardwareEnv()
192+
env: gym.Env = HardwareEnv(frequency=cfg.frequency)
189193
env = RobotWrapper(env, robot, cfg.control_mode, home_on_reset=cfg.wrapper_cfg.home_on_reset)
190194
env = FR3HW(env)
191195
if isinstance(cfg.gripper_cfg, rcs.hand.tilburg_hand.THConfig):
@@ -229,6 +233,7 @@ def create_env(self, cfg: FR3MultiHardwareEnvCreatorConfig) -> gym.Env:
229233
camera_cfgs=None,
230234
max_relative_movement=cfg.max_relative_movement,
231235
relative_to=cfg.relative_to,
236+
frequency=cfg.frequency,
232237
wrapper_cfg=cfg.wrapper_cfg,
233238
)
234239
)

‎extensions/rcs_panda/src/rcs_panda/creators.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ class PandaHardwareEnvCreatorConfig:
119119
camera_cfgs: dict[str, HardwareCameraCreatorConfig] | None = None
120120
max_relative_movement: float | tuple[float, float] | None = None
121121
relative_to: RelativeTo = RelativeTo.LAST_STEP
122+
frequency: float | None = None
123+
"""Control frequency in Hz, rate limits env.step(). None disables rate limiting."""
122124
wrapper_cfg: WrapperConfig = field(default_factory=WrapperConfig)
123125

124126

@@ -131,6 +133,8 @@ class PandaMultiHardwareEnvCreatorConfig:
131133
max_relative_movement: float | tuple[float, float] | None = None
132134
relative_to: RelativeTo = RelativeTo.LAST_STEP
133135
robot_to_shared_base_frame: dict[str, rcs.common.Pose] | None = None
136+
frequency: float | None = None
137+
"""Control frequency in Hz, rate limits env.step(). None disables rate limiting."""
134138
wrapper_cfg: WrapperConfig = field(default_factory=WrapperConfig)
135139

136140

@@ -143,7 +147,7 @@ def create_env(self, cfg: PandaHardwareEnvCreatorConfig) -> gym.Env:
143147
)
144148
robot = hw.Franka(cfg.robot_cfg, ik)
145149

146-
env: gym.Env = HardwareEnv()
150+
env: gym.Env = HardwareEnv(frequency=cfg.frequency)
147151
env = RobotWrapper(env, robot, cfg.control_mode, home_on_reset=cfg.wrapper_cfg.home_on_reset)
148152
env = PandaHW(env)
149153
if isinstance(cfg.gripper_cfg, rcs.hand.tilburg_hand.THConfig):
@@ -185,6 +189,7 @@ def create_env(self, cfg: PandaMultiHardwareEnvCreatorConfig) -> gym.Env:
185189
camera_cfgs=None,
186190
max_relative_movement=cfg.max_relative_movement,
187191
relative_to=cfg.relative_to,
192+
frequency=cfg.frequency,
188193
wrapper_cfg=cfg.wrapper_cfg,
189194
)
190195
)

0 commit comments

Comments
 (0)