Skip to content

Commit b877f82

Browse files
authored
Merge pull request #171 from utn-mi/juelg/on-demand-camera-render
feat(sim camera): on demand camera render
2 parents d93f10f + 21164c8 commit b877f82

File tree

10 files changed

+64
-84
lines changed

10 files changed

+64
-84
lines changed

README.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Robot Control Stack
2+
RCS is a unified and multilayered robot control interface over a MuJoCo simulation and real world robot currently implemented for the FR3.
23
## Requirements
34
We build and test RCS on the latest Debian and on the latest Ubuntu LTS.
45

@@ -29,17 +30,27 @@ Import the library in python:
2930
```python
3031
import rcsss
3132
```
32-
The package includes a command line interface which define useful commands to handle the hardware robot.
33+
Checkout the python examples that we provide in [python/examples](python/examples):
34+
- [fr3.py](python/examples/fr3.py) shows direct robot control with RCS's python bindings
35+
- [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
36+
All of these examples work both in the MuJoCo simulation as well as on your hardware FR3.
37+
Just switch between the following settings in the example script
38+
```python
39+
ROBOT_INSTANCE = RobotInstance.SIMULATION
40+
# ROBOT_INSTANCE = RobotInstance.HARDWARE
41+
```
42+
and add your robot credentials to a `.env` file like this:
43+
```env
44+
DESK_USERNAME=...
45+
DESK_PASSWORD=...
46+
```
47+
48+
### Command Line Interface
49+
The package includes a command line interface which define useful commands to handle the FR3 robot without the need to use the Desk Website.
3350
To list all available subcommands use:
3451
```shell
3552
python -m rcsss --help
3653
```
37-
A sample config can be generated via the following CLI command.
38-
```shell
39-
python -m rcsss sample-config
40-
```
41-
The command will produce a `config.yaml` file with sample values.
42-
See [config.py](python/rcsss/config.py) for a description of the config fields.
4354

4455
## Development
4556
```shell

python/rcsss/_core/sim.pyi

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,18 @@ class SimCameraSet:
159159

160160
class SimCameraSetConfig:
161161
cameras: dict[str, SimCameraConfig]
162-
frame_rate: int
163162
max_buffer_frames: int
164163
resolution_height: int
165164
resolution_width: int
166165
def __init__(self) -> None: ...
166+
@property
167+
def frame_rate(self) -> int:
168+
"""
169+
The frame rate in which the cameras render in Hz. If set to zero, the camera frames will render on demand and without fixed rate which takes away compute effort.
170+
"""
171+
172+
@frame_rate.setter
173+
def frame_rate(self, arg0: int) -> None: ...
167174

168175
def open_gui_window(uuid: str) -> None: ...
169176

python/rcsss/config.py

Lines changed: 0 additions & 58 deletions
This file was deleted.

python/rcsss/envs/factories.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def __call__( # type: ignore
241241
render_mode: str = "human",
242242
control_mode: ControlMode = ControlMode.CARTESIAN_TRPY,
243243
resolution: tuple[int, int] | None = None,
244-
frame_rate: int = 10,
244+
frame_rate: int = 0,
245245
delta_actions: bool = True,
246246
) -> gym.Env:
247247
if resolution is None:
@@ -272,7 +272,7 @@ def __call__( # type: ignore
272272
render_mode: str = "human",
273273
control_mode: ControlMode = ControlMode.CARTESIAN_TRPY,
274274
resolution: tuple[int, int] | None = None,
275-
frame_rate: int = 10,
275+
frame_rate: int = 0,
276276
delta_actions: bool = True,
277277
) -> gym.Env:
278278
if resolution is None:
@@ -319,7 +319,7 @@ def __call__( # type: ignore
319319
render_mode: str = "human",
320320
control_mode: ControlMode = ControlMode.CARTESIAN_TRPY,
321321
resolution: tuple[int, int] | None = None,
322-
frame_rate: int = 10,
322+
frame_rate: int = 0,
323323
delta_actions: bool = True,
324324
) -> gym.Env:
325325
if resolution is None:

src/pybind/rcsss.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,10 @@ PYBIND11_MODULE(_core, m) {
486486
py::class_<rcs::sim::SimCameraSetConfig>(sim, "SimCameraSetConfig")
487487
.def(py::init<>())
488488
.def_readwrite("cameras", &rcs::sim::SimCameraSetConfig::cameras)
489-
.def_readwrite("frame_rate", &rcs::sim::SimCameraSetConfig::frame_rate)
489+
.def_readwrite("frame_rate", &rcs::sim::SimCameraSetConfig::frame_rate,
490+
"The frame rate in which the cameras render in Hz. If set "
491+
"to zero, the camera frames will render on demand and "
492+
"without fixed rate which takes away compute effort.")
490493
.def_readwrite("resolution_width",
491494
&rcs::sim::SimCameraSetConfig::resolution_width)
492495
.def_readwrite("resolution_height",

src/sim/camera.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ namespace sim {
2424
SimCameraSet::SimCameraSet(std::shared_ptr<Sim> sim, SimCameraSetConfig cfg)
2525
: sim{sim}, cfg{cfg}, buffer{}, buffer_lock{}, cameras{} {
2626
for (auto const& [id, cam] : cfg.cameras) {
27+
// if frame_rate is zero, then we only render when an image is requested
28+
// this mode is useful when no videos are required as it speeds up the
29+
// simulation significantly
2730
this->sim->register_rendering_callback(
2831
[this](const std::string& id, mjrContext& ctx, mjvScene& scene,
2932
mjvOption& opt) { this->frame_callback(id, ctx, scene, opt); },
30-
id, 1.0 / this->cfg.frame_rate, this->cfg.resolution_width,
33+
id, this->cfg.frame_rate, this->cfg.resolution_width,
3134
this->cfg.resolution_height);
3235

3336
mjvCamera mjcam;
@@ -57,6 +60,9 @@ void SimCameraSet::clear_buffer() {
5760
}
5861

5962
std::optional<FrameSet> SimCameraSet::get_latest_frameset() {
63+
if (this->cfg.frame_rate == 0) {
64+
this->render_all();
65+
}
6066
if (buffer.empty()) {
6167
return std::nullopt;
6268
}
@@ -73,6 +79,14 @@ std::optional<FrameSet> SimCameraSet::get_timestamp_frameset(float ts) {
7379
return std::nullopt;
7480
}
7581

82+
void SimCameraSet::render_all() {
83+
for (auto const& [id, cam] : this->cfg.cameras) {
84+
mjrContext* ctx = this->sim->renderer.get_context(id);
85+
this->frame_callback(id, *ctx, this->sim->renderer.scene,
86+
this->sim->renderer.opt);
87+
}
88+
}
89+
7690
void SimCameraSet::frame_callback(const std::string& id, mjrContext& ctx,
7791
mjvScene& scene, mjvOption& opt) {
7892
mjrRect viewport = mjr_maxViewport(&ctx);

src/sim/camera.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class SimCameraSet {
6666
std::unordered_map<std::string, mjvCamera> cameras;
6767
std::mutex buffer_lock;
6868
mjtNum last_ts = 0;
69+
void render_all();
6970
};
7071
} // namespace sim
7172
} // namespace rcs

src/sim/gui_client.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ void GuiClient::render_loop() {
6565
mjv_defaultFreeCamera(this->m, &this->cam);
6666
mjv_defaultOption(&this->opt);
6767
mjv_defaultScene(&this->scn);
68-
mjv_makeScene(this->m, &this->scn, mjFONTSCALE_100);
68+
size_t max_geoms = 2000;
69+
mjv_makeScene(this->m, &this->scn, max_geoms);
6970
mjrRect viewport = {0, 0, 0, 0};
7071
this->platform_ui->RefreshMjrContext(this->m, mjFONTSCALE_100);
7172
this->platform_ui->SetVSync(true);

src/sim/sim.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,17 +164,19 @@ void Sim::register_all_cb(std::function<bool(void)> cb,
164164
void Sim::register_rendering_callback(
165165
std::function<void(const std::string&, mjrContext&, mjvScene&, mjvOption&)>
166166
cb,
167-
const std::string& id, mjtNum seconds_between_calls, size_t width,
168-
size_t height) {
167+
const std::string& id, int frame_rate, size_t width, size_t height) {
169168
this->renderer.register_context(id, width, height);
170-
// dont register off screen in normal callback, but special gui callback
171-
this->rendering_callbacks.push_back(
172-
RenderingCallback{.cb = cb,
173-
.id = id,
174-
.seconds_between_calls = seconds_between_calls,
175-
// this is negative so that we will directly render the
176-
// cameras in the first step
177-
.last_call_timestamp = -seconds_between_calls});
169+
// in case frame_rate is zero, rendering needs to be triggered
170+
// manually
171+
if (frame_rate != 0) {
172+
this->rendering_callbacks.push_back(
173+
RenderingCallback{.cb = cb,
174+
.id = id,
175+
.seconds_between_calls = 1.0 / frame_rate,
176+
// this is negative so that we will directly render
177+
// the cameras in the first step
178+
.last_call_timestamp = -1.0 / frame_rate});
179+
}
178180
}
179181

180182
void Sim::start_gui_server(const std::string& id) {

src/sim/sim.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ class Sim {
9898
std::function<void(const std::string& id, mjrContext&, mjvScene&,
9999
mjvOption&)>
100100
cb,
101-
const std::string& id, mjtNum seconds_between_calls, size_t width,
102-
size_t height);
101+
const std::string& id, int frame_rate, size_t width, size_t height);
103102
void start_gui_server(const std::string& id);
104103
void stop_gui_server();
105104
};

0 commit comments

Comments
 (0)