|
| 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() |
0 commit comments