Skip to content

Commit f60a7ed

Browse files
authored
Merge pull request #195 from utn-mi/juelg/refactor-independent-hw-robots
Independent Hardware Robots
2 parents 88c688b + a82269f commit f60a7ed

86 files changed

Lines changed: 1105 additions & 1003 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/py.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ jobs:
4848
run: python -m pip install wheel
4949
- name: Install the package
5050
run: python -m pip install --no-build-isolation .
51+
- name: Install the fr3 package
52+
run: python -m pip install --no-build-isolation extensions/rcs_fr3
5153
- name: Code linting
5254
run: make pylint
5355
- name: Check that stub files are up-to-date

CMakeLists.txt

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ project(
44
rcs
55
LANGUAGES C CXX
66
VERSION 0.4.0
7-
DESCRIPTION "UTNs Robot Control Stack Library"
7+
DESCRIPTION "Robot Control Stack Library"
88
)
99

1010
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
@@ -31,29 +31,14 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
3131
set(BUILD_SHARED_LIBS OFF)
3232
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
3333

34-
# turn off libfranka tests
35-
set(BUILD_TESTS OFF)
36-
set(BUILD_EXAMPLES OFF)
37-
set(RL_BUILD_DEMOS OFF)
38-
set(RL_BUILD_RL_SG OFF)
39-
set(RL_BUILD_TESTS OFF)
40-
set(RL_BUILD_EXTRAS OFF)
41-
set(BUILD_PYTHON_INTERFACE OFF)
42-
set(BUILD_DOCUMENTATION OFF)
43-
4434
include(FetchContent)
4535

4636
find_package(Eigen3 REQUIRED)
4737
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
4838
find_package(MuJoCo REQUIRED)
4939
find_package(pinocchio REQUIRED)
50-
FetchContent_Declare(
51-
libfranka
52-
GIT_REPOSITORY https://github.com/frankaemika/libfranka.git
53-
GIT_TAG 0.15.0
54-
GIT_PROGRESS TRUE
55-
EXCLUDE_FROM_ALL
56-
)
40+
41+
5742
FetchContent_Declare(rl
5843
GIT_REPOSITORY https://github.com/roboticslibrary/rl.git
5944
GIT_TAG 0b3797215345a1d37903634095361233d190b2e6
@@ -67,7 +52,7 @@ FetchContent_Declare(pybind11
6752
EXCLUDE_FROM_ALL
6853
)
6954

70-
FetchContent_MakeAvailable(libfranka rl pybind11)
55+
FetchContent_MakeAvailable(rl pybind11)
7156
include(compile_scenes)
7257

7358
add_subdirectory(src)

Makefile

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,27 @@ COMPILE_MODE = Release
55
# CPP
66
cppcheckformat:
77
clang-format --dry-run -Werror -i $(shell find ${CPPSRC} -name '*.cpp' -o -name '*.cc' -o -name '*.h')
8+
clang-format --dry-run -Werror -i $(shell find extensions/rcs_fr3/src -name '*.cpp' -o -name '*.cc' -o -name '*.h')
89

910
cppformat:
1011
clang-format -Werror -i $(shell find ${CPPSRC} -name '*.cpp' -o -name '*.cc' -o -name '*.h')
12+
clang-format -Werror -i $(shell find extensions/rcs_fr3/src -name '*.cpp' -o -name '*.cc' -o -name '*.h')
1113

1214
cpplint:
1315
clang-tidy -p=build --warnings-as-errors='*' $(shell find ${CPPSRC} -name '*.cpp' -o -name '*.cc' -name '*.h')
1416

17+
# import errors
18+
# clang-tidy -p=build --warnings-as-errors='*' $(shell find extensions/rcs_fr3/src -name '*.cpp' -o -name '*.cc' -name '*.h')
19+
1520
gcccompile:
1621
pip install --upgrade --requirement requirements_dev.txt
1722
cmake -DCMAKE_BUILD_TYPE=${COMPILE_MODE} -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -B build -G Ninja
18-
cmake --build build --target _core franka
23+
cmake --build build --target _core
1924

2025
clangcompile:
2126
pip install --upgrade --requirement requirements_dev.txt
2227
cmake -DCMAKE_BUILD_TYPE=${COMPILE_MODE} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -B build -G Ninja
23-
cmake --build build --target _core franka
28+
cmake --build build --target _core
2429

2530
# Auto generation of CPP binding stub files
2631
stubgen:
@@ -30,25 +35,33 @@ stubgen:
3035
find ./python/rcs/_core -name '*.pyi' -print | xargs sed -i 's/tuple\[typing\.Literal\[\([0-9]\+\)\], typing\.Literal\[1\]\]/tuple\[typing\.Literal[\1]\]/g'
3136
find ./python/rcs/_core -name '*.pyi' -print | xargs sed -i 's/tuple\[\([M|N]\), typing\.Literal\[1\]\]/tuple\[\1\]/g'
3237
ruff check --fix python/rcs/_core
33-
isort python/rcs/_core
34-
black python/rcs/_core
38+
pybind11-stubgen -o extensions --numpy-array-use-type-var rcs_fr3
39+
find ./extensions/rcs_fr3 -not -path "./extensions/rcs_fr3/_core/*" -name '*.pyi' -delete
40+
find ./extensions/rcs_fr3 -name '*.pyi' -print | xargs sed -i '1s/^/# ATTENTION: auto generated from C++ code, use `make stubgen` to update!\n/'
41+
find ./extensions/rcs_fr3 -name '*.pyi' -print | xargs sed -i 's/tuple\[typing\.Literal\[\([0-9]\+\)\], typing\.Literal\[1\]\]/tuple\[typing\.Literal[\1]\]/g'
42+
find ./extensions/rcs_fr3 -name '*.pyi' -print | xargs sed -i 's/tuple\[\([M|N]\), typing\.Literal\[1\]\]/tuple\[\1\]/g'
43+
rm -r extensions/rcs_fr3/src/rcs_fr3/_core
44+
mv extensions/rcs_fr3/_core/ extensions/rcs_fr3/src/rcs_fr3/
45+
ruff check --fix extensions/rcs_fr3/src/rcs_fr3/_core
46+
isort python/rcs/_core extensions/rcs_fr3/src/rcs_fr3/_core
47+
black python/rcs/_core extensions/rcs_fr3/src/rcs_fr3/_core
3548

3649
# Python
3750
pycheckformat:
38-
isort --check-only ${PYSRC}
39-
black --check ${PYSRC}
51+
isort --check-only ${PYSRC} extensions examples
52+
black --check ${PYSRC} extensions examples
4053

4154
pyformat:
42-
isort ${PYSRC}
43-
black ${PYSRC}
55+
isort ${PYSRC} extensions examples
56+
black ${PYSRC} extensions examples
4457

4558
pylint: ruff mypy
4659

4760
ruff:
48-
ruff check ${PYSRC}
61+
ruff check ${PYSRC} extensions examples
4962

5063
mypy:
51-
mypy ${PYSRC} --install-types --non-interactive --no-namespace-packages
64+
mypy ${PYSRC} extensions examples --install-types --non-interactive --no-namespace-packages --exclude 'build'
5265

5366
pytest:
5467
pytest -vv

README.md

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,108 @@ pip install -ve .
2222

2323
## Usage
2424
The python package is called `rcs`.
25-
Import the library in python:
25+
26+
### Direct Robot Control
27+
Simple direct robot control:
2628
```python
2729
import rcs
30+
from rcs import sim
31+
from rcs._core.sim import CameraType
32+
from rcs.camera.sim import SimCameraConfig, SimCameraSet
33+
simulation = sim.Sim(rcs.scenes["fr3_empty_world"]["mjb"])
34+
urdf_path = rcs.scenes["fr3_empty_world"]["urdf"]
35+
ik = rcs.common.RL(str(urdf_path))
36+
cfg = sim.SimRobotConfig()
37+
cfg.add_id("0")
38+
cfg.tcp_offset = rcs.common.Pose(rcs.common.FrankaHandTCPOffset())
39+
robot = rcs.sim.SimRobot(simulation, ik, cfg)
40+
41+
gripper_cfg_sim = sim.SimGripperConfig()
42+
gripper_cfg_sim.add_id("0")
43+
gripper = sim.SimGripper(simulation, gripper_cfg_sim)
44+
45+
# add camera to have a rendering gui
46+
cameras = {
47+
"wrist": SimCameraConfig(
48+
identifier="wrist_0",
49+
type=CameraType.fixed,
50+
resolution_width=640,
51+
resolution_height=480,
52+
frame_rate=30,
53+
),
54+
}
55+
camera_set = SimCameraSet(simulation, cameras)
56+
simulation.open_gui()
57+
robot.set_cartesian_position(
58+
robot.get_cartesian_position() * rcs.common.Pose(translation=np.array([0.05, 0, 0]))
59+
)
60+
gripper.grasp()
61+
simulation.step_until_convergence()
62+
```
63+
### Gym Env Interface
64+
```python
65+
from rcs.envs.creators import SimEnvCreator
66+
from rcs.envs.utils import (
67+
default_mujoco_cameraset_cfg,
68+
default_sim_gripper_cfg,
69+
default_sim_robot_cfg,
70+
)
71+
from rcs.envs.base import ControlMode, RelativeTo
72+
env_rel = SimEnvCreator()(
73+
control_mode=ControlMode.JOINTS,
74+
collision_guard=False,
75+
robot_cfg=default_sim_robot_cfg(),
76+
gripper_cfg=default_sim_gripper_cfg(),
77+
cameras=default_mujoco_cameraset_cfg(),
78+
max_relative_movement=np.deg2rad(5),
79+
relative_to=RelativeTo.LAST_STEP,
80+
)
81+
env_rel.get_wrapper_attr("sim").open_gui()
82+
83+
for _ in range(10):
84+
obs, info = env_rel.reset()
85+
for _ in range(10):
86+
# sample random relative action and execute it
87+
act = env_rel.action_space.sample()
88+
print(act)
89+
obs, reward, terminated, truncated, info = env_rel.step(act)
90+
print(obs)
91+
if truncated or terminated:
92+
logger.info("Truncated or terminated!")
93+
return
2894
```
29-
Checkout the python examples that we provide in [python/examples](python/examples):
30-
- [fr3.py](python/examples/fr3.py) shows direct robot control with RCS's python bindings
31-
- [env_joint_control.py](python/examples/env_joint_control.py) and [env_cartesian_control.py](python/examples/env_cartesian_control.py) demonstrates RCS's high level [gymnasium](https://gymnasium.farama.org/) interface both for joint- and end effector space control
95+
### Examples
96+
Checkout the python examples in the [examples](examples) folder:
97+
- [fr3_direct_control.py](examples/fr3.py) shows direct robot control with RCS's python bindings
98+
- [fr3_env_joint_control.py](examples/env_joint_control.py) and [fr3_env_cartesian_control.py](examples/env_cartesian_control.py) demonstrates RCS's high level [gymnasium](https://gymnasium.farama.org/) interface both for joint- and end effector space control
3299
All of these examples work both in the MuJoCo simulation as well as on your hardware FR3.
33-
Just switch between the following settings in the example script
100+
101+
102+
### Hardware Extensions
103+
To enable hardware usage in RCS, install the needed hardware extensions via pip. RCS itself comes with a couple of supported extensions e.g. control of the FR3 via the [`rcs_fr3`](extensions/rcs_fr3) extension. All native supported extension are located in [extensions](extensions).
104+
To install extensions:
105+
```shell
106+
pip install -ve extensions/rcs_fr3
107+
```
108+
For more details real the readme file of the respective extension.
109+
110+
After the required hardware extensions are installed the examples also above work on real hardware:
111+
Switch to hardware by setting the following flag:
34112
```python
35113
ROBOT_INSTANCE = RobotPlatform.SIMULATION
36114
# ROBOT_INSTANCE = RobotPlatform.HARDWARE
37115
```
38-
and add your robot credentials to a `.env` file like this:
39-
```env
40-
DESK_USERNAME=...
41-
DESK_PASSWORD=...
42-
```
43116

44-
### Command Line Interface
45-
The package includes a command line interface which define useful commands to handle the FR3 robot without the need to use the Desk Website.
46-
To list all available subcommands use:
117+
#### Command Line Interface
118+
Some modules include command line interfaces, e.g. rcs_fr3 defines useful commands to handle the FR3 robot without the need to use the Desk Website.
119+
You can see the available subcommands as follows:
47120
```shell
48-
python -m rcs --help
121+
python -m rcs_fr3 --help
122+
python -m rcs_realsense --help
49123
```
50124

51125
## Development
126+
### Formatting and Linting
52127
```shell
53128
# check for c++ formatting errors
54129
make cppcheckformat
@@ -65,10 +140,13 @@ make pylint
65140
# Testing
66141
make pytest
67142
```
68-
69143
### Stub Files for Python Bindings
70144
We use autogenerated python stub files (`.pyi`) in the [`_core`](python/rcs/_core/) folder to show our linters the expected types of the C++ Python bindings.
71145
If the python bindings in the C++ code have changed you might need to regenerate them by using:
72146
```shell
73147
make stubgen
74148
```
149+
150+
### Develop Your Own Hardware Extension
151+
TODO
152+

cmake/Findpinocchio.cmake

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,23 @@ if (NOT pinocchio_FOUND)
4646
# Create the imported target
4747
add_library(pinocchio::pinocchio SHARED IMPORTED)
4848
target_include_directories(pinocchio::pinocchio INTERFACE ${pinocchio_INCLUDE_DIRS})
49-
set_target_properties(
50-
pinocchio::pinocchio
51-
PROPERTIES
49+
set_target_properties(pinocchio::pinocchio
50+
PROPERTIES
5251
IMPORTED_LOCATION "${pinocchio_library_path}"
5352
)
54-
55-
add_library(pinocchio_parsers SHARED IMPORTED)
56-
target_include_directories(pinocchio_parsers INTERFACE ${pinocchio_INCLUDE_DIRS})
57-
set_target_properties(
58-
pinocchio_parsers
59-
PROPERTIES
53+
54+
add_library(pinocchio::parsers SHARED IMPORTED)
55+
target_include_directories(pinocchio::parsers INTERFACE ${pinocchio_INCLUDE_DIRS})
56+
set_target_properties(pinocchio::parsers
57+
PROPERTIES
6058
IMPORTED_LOCATION "${pinocchio_parsers_path}"
6159
)
60+
61+
add_library(pinocchio::all INTERFACE IMPORTED)
62+
set_target_properties(pinocchio::all
63+
PROPERTIES
64+
INTERFACE_LINK_LIBRARIES "pinocchio::pinocchio;pinocchio::parsers"
65+
)
6266
set(pinocchio_FOUND TRUE)
6367

6468
endif()
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import logging
22

33
import numpy as np
4-
import rcs
5-
from rcs import sim
64
from rcs._core.common import RobotPlatform
7-
from rcs._core.hw import FR3Config, IKSolver
85
from rcs._core.sim import CameraType
96
from rcs.camera.sim import SimCameraConfig, SimCameraSet
10-
from rcs.control.fr3_desk import FCI, ContextManager, Desk, load_creds_fr3_desk
7+
from rcs_fr3._core import hw
8+
from rcs_fr3.desk import FCI, ContextManager, Desk, load_creds_fr3_desk
9+
10+
import rcs
11+
from rcs import sim
1112

1213
ROBOT_IP = "192.168.101.1"
1314
ROBOT_INSTANCE = RobotPlatform.SIMULATION
@@ -30,19 +31,19 @@
3031
3132
When you use a real FR3 you first need to unlock its joints using the following cli script:
3233
33-
python -m rcs fr3 unlock <ip>
34+
python -m rcs_fr3 unlock <ip>
3435
3536
or put it into guiding mode using:
3637
37-
python -m rcs fr3 guiding-mode <ip>
38+
python -m rcs_fr3 guiding-mode <ip>
3839
3940
When you are done you lock it again using:
4041
41-
python -m rcs fr3 lock <ip>
42+
python -m rcs_fr3 lock <ip>
4243
4344
or even shut it down using:
4445
45-
python -m rcs fr3 shutdown <ip>
46+
python -m rcs_fr3 shutdown <ip>
4647
"""
4748

4849

@@ -72,7 +73,7 @@ def main():
7273

7374
# add camera to have a rendering gui
7475
cameras = {
75-
"default_free": SimCameraConfig(
76+
"default_free": sim.SimCameraConfig(
7677
identifier="",
7778
type=CameraType.default_free,
7879
resolution_width=1280,
@@ -93,17 +94,17 @@ def main():
9394
else:
9495
urdf_path = rcs.scenes["fr3_empty_world"]["urdf"]
9596
ik = rcs.common.RL(str(urdf_path))
96-
robot = rcs.hw.FR3(ROBOT_IP, ik)
97-
robot_cfg = FR3Config()
97+
robot = hw.FR3(ROBOT_IP, ik)
98+
robot_cfg = hw.FR3Config()
9899
robot_cfg.tcp_offset = rcs.common.Pose(rcs.common.FrankaHandTCPOffset())
99-
robot_cfg.ik_solver = IKSolver.rcs_ik
100-
robot.set_parameters(robot_cfg)
100+
robot_cfg.ik_solver = hw.IKSolver.rcs_ik
101+
robot.set_parameters(robot_cfg) # type: ignore
101102

102-
gripper_cfg_hw = rcs.hw.FHConfig()
103+
gripper_cfg_hw = hw.FHConfig()
103104
gripper_cfg_hw.epsilon_inner = gripper_cfg_hw.epsilon_outer = 0.1
104105
gripper_cfg_hw.speed = 0.1
105106
gripper_cfg_hw.force = 30
106-
gripper = rcs.hw.FrankaHand(ROBOT_IP, gripper_cfg_hw)
107+
gripper = hw.FrankaHand(ROBOT_IP, gripper_cfg_hw)
107108
input("the robot is going to move, press enter whenever you are ready")
108109

109110
# move to home position and open gripper

0 commit comments

Comments
 (0)