Skip to content

Commit a5f6da0

Browse files
authored
[FEATURE] Add Sensor.draw_debug method. (Genesis-Embodied-AI#1770)
1 parent cfea607 commit a5f6da0

File tree

13 files changed

+679
-369
lines changed

13 files changed

+679
-369
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import argparse
2+
3+
from tqdm import tqdm
4+
5+
import genesis as gs
6+
from genesis.recorders.plotters import IS_MATPLOTLIB_AVAILABLE, IS_PYQTGRAPH_AVAILABLE
7+
8+
9+
def main():
10+
parser = argparse.ArgumentParser()
11+
parser.add_argument("-dt", "--timestep", type=float, default=1e-2, help="Simulation time step")
12+
parser.add_argument("-v", "--vis", action="store_true", default=True, help="Show visualization GUI")
13+
parser.add_argument("-nv", "--no-vis", action="store_false", dest="vis", help="Disable visualization GUI")
14+
parser.add_argument("-c", "--cpu", action="store_true", help="Use CPU instead of GPU")
15+
parser.add_argument("-t", "--seconds", type=float, default=2, help="Number of seconds to simulate")
16+
parser.add_argument("-f", "--force", action="store_true", default=True, help="Use ContactForceSensor (xyz float)")
17+
parser.add_argument("-nf", "--no-force", action="store_false", dest="force", help="Use ContactSensor (boolean)")
18+
19+
args = parser.parse_args()
20+
21+
########################## init ##########################
22+
gs.init(backend=gs.cpu if args.cpu else gs.gpu, logging_level=None)
23+
24+
########################## scene setup ##########################
25+
scene = gs.Scene(
26+
sim_options=gs.options.SimOptions(dt=args.timestep),
27+
rigid_options=gs.options.RigidOptions(
28+
use_gjk_collision=True,
29+
constraint_timeconst=max(0.01, 2 * args.timestep),
30+
),
31+
vis_options=gs.options.VisOptions(show_world_frame=True),
32+
profiling_options=gs.options.ProfilingOptions(show_FPS=False),
33+
show_viewer=args.vis,
34+
)
35+
36+
scene.add_entity(gs.morphs.Plane())
37+
38+
foot_link_names = ["FR_foot", "FL_foot", "RR_foot", "RL_foot"]
39+
go2 = scene.add_entity(
40+
gs.morphs.URDF(
41+
file="urdf/go2/urdf/go2.urdf",
42+
pos=(0.0, 0.0, 0.2),
43+
links_to_keep=foot_link_names,
44+
)
45+
)
46+
47+
for link_name in foot_link_names:
48+
if args.force:
49+
sensor_options = gs.sensors.ContactForce(
50+
entity_idx=go2.idx,
51+
link_idx_local=go2.get_link(link_name).idx_local,
52+
draw_debug=True,
53+
)
54+
plot_kwargs = dict(
55+
title=f"{link_name} Force Sensor Data",
56+
labels=["force_x", "force_y", "force_z"],
57+
)
58+
else:
59+
sensor_options = gs.sensors.Contact(
60+
entity_idx=go2.idx,
61+
link_idx_local=go2.get_link(link_name).idx_local,
62+
draw_debug=True,
63+
)
64+
plot_kwargs = dict(
65+
title=f"{link_name} Contact Sensor Data",
66+
labels=["in_contact"],
67+
)
68+
69+
sensor = scene.add_sensor(sensor_options)
70+
71+
if IS_PYQTGRAPH_AVAILABLE:
72+
sensor.start_recording(gs.recorders.PyQtLinePlot(**plot_kwargs))
73+
elif IS_MATPLOTLIB_AVAILABLE:
74+
print("pyqtgraph not found, falling back to matplotlib.")
75+
sensor.start_recording(gs.recorders.MPLLinePlot(**plot_kwargs))
76+
else:
77+
print("matplotlib or pyqtgraph not found, skipping real-time plotting.")
78+
79+
scene.build()
80+
81+
try:
82+
steps = int(args.seconds / args.timestep)
83+
for _ in tqdm(range(steps)):
84+
scene.step()
85+
except KeyboardInterrupt:
86+
gs.logger.info("Simulation interrupted, exiting.")
87+
finally:
88+
gs.logger.info("Simulation finished.")
89+
90+
scene.stop_recording()
91+
92+
93+
if __name__ == "__main__":
94+
main()

examples/sensors/force.py

Lines changed: 0 additions & 84 deletions
This file was deleted.
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def main():
4444
gs.sensors.IMU(
4545
entity_idx=franka.idx,
4646
link_idx_local=end_effector.idx_local,
47+
pos_offset=(0.0, 0.0, 0.15),
4748
# noise parameters
4849
acc_axes_skew=(0.0, 0.01, 0.02),
4950
gyro_axes_skew=(0.03, 0.04, 0.05),
@@ -54,22 +55,24 @@ def main():
5455
delay=0.01,
5556
jitter=0.01,
5657
interpolate=True,
58+
# visualize
59+
draw_debug=True,
5760
)
5861
)
5962
labels = {"lin_acc": ("acc_x", "acc_y", "acc_z"), "ang_vel": ("gyro_x", "gyro_y", "gyro_z")}
6063
if args.vis:
6164
if IS_PYQTGRAPH_AVAILABLE:
62-
imu.start_recording(gs.recorders.PyQtPlot(title="IMU Measured Data", labels=labels))
65+
imu.start_recording(gs.recorders.PyQtLinePlot(title="IMU Measured Data", labels=labels))
6366
scene.start_recording(
6467
imu.read_ground_truth,
65-
gs.recorders.PyQtPlot(title="IMU Ground Truth Data", labels=labels),
68+
gs.recorders.PyQtLinePlot(title="IMU Ground Truth Data", labels=labels),
6669
)
6770
elif IS_MATPLOTLIB_AVAILABLE:
6871
gs.logger.info("pyqtgraph not found, falling back to matplotlib.")
69-
imu.start_recording(gs.recorders.MPLPlot(title="IMU Measured Data", labels=labels))
72+
imu.start_recording(gs.recorders.MPLLinePlot(title="IMU Measured Data", labels=labels))
7073
scene.start_recording(
7174
imu.read_ground_truth,
72-
gs.recorders.MPLPlot(title="IMU Ground Truth Data", labels=labels),
75+
gs.recorders.MPLLinePlot(title="IMU Ground Truth Data", labels=labels),
7376
)
7477
else:
7578
print("matplotlib or pyqtgraph not found, skipping real-time plotting.")

genesis/recorders/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .file_writers import CSVFileWriterOptions as CSVFile
33
from .file_writers import NPZFileWriterOptions as NPZFile
44
from .file_writers import VideoFileWriterOptions as VideoFile
5-
from .plotters import MPLPlotterOptions as MPLPlot
6-
from .plotters import PyQtPlotterOptions as PyQtPlot
5+
from .plotters import MPLImagePlotterOptions as MPLImagePlot
6+
from .plotters import MPLLinePlotterOptions as MPLLinePlot
7+
from .plotters import PyQtLinePlotterOptions as PyQtLinePlot
78
from .recorder_manager import RecorderManager, register_recording

genesis/recorders/base_recorder.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import queue
22
import threading
33
import time
4-
from typing import Callable, Generic, TypeVar
4+
from typing import TYPE_CHECKING, Callable, Generic, TypeVar
55

66
import genesis as gs
77
from genesis.options import Options
88

9-
from .recorder_manager import RecorderManager
9+
if TYPE_CHECKING:
10+
from .recorder_manager import RecorderManager
1011

1112
T = TypeVar("T")
1213

@@ -50,7 +51,7 @@ class Recorder(Generic[T]):
5051
done through the RecorderManager.
5152
"""
5253

53-
def __init__(self, manager: RecorderManager, options: RecorderOptions, data_func: Callable[[], T]):
54+
def __init__(self, manager: "RecorderManager", options: RecorderOptions, data_func: Callable[[], T]):
5455
self._options = options
5556
self._manager = manager
5657
self._data_func = data_func

0 commit comments

Comments
 (0)