Skip to content

Commit 154ee66

Browse files
committed
gs and fix the simpublisher spin bug
1 parent b4c733a commit 154ee66

8 files changed

Lines changed: 295 additions & 5 deletions

File tree

-20.6 KB
Binary file not shown.
-21.3 KB
Binary file not shown.

demos/genesis/franka_example.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import genesis as gs
2+
import numpy as np
3+
4+
from simpub.sim.genesis_publisher import GenesisPublisher
5+
6+
if __name__ == "__main__":
7+
gs.init(backend=gs.gpu)
8+
viewer_options = gs.options.ViewerOptions(
9+
camera_pos=(0, -3.5, 2.5),
10+
camera_lookat=(0.0, 0.0, 0.5),
11+
camera_fov=40,
12+
max_FPS=60,
13+
)
14+
15+
scene = gs.Scene(
16+
viewer_options=viewer_options,
17+
sim_options=gs.options.SimOptions(
18+
dt=0.01,
19+
),
20+
show_viewer=False,
21+
)
22+
plane = scene.add_entity(
23+
gs.morphs.Plane(),
24+
)
25+
franka = scene.add_entity(
26+
gs.morphs.MJCF(file="xml/franka_emika_panda/panda.xml"),
27+
)
28+
29+
publisher = GenesisPublisher(scene)
30+
scene.build()
31+
32+
jnt_names = [
33+
"joint1",
34+
"joint2",
35+
"joint3",
36+
"joint4",
37+
"joint5",
38+
"joint6",
39+
"joint7",
40+
"finger_joint1",
41+
"finger_joint2",
42+
]
43+
dofs_idx = [franka.get_joint(name).dof_idx_local for name in jnt_names]
44+
45+
# Optional: set control gains
46+
franka.set_dofs_kp(
47+
np.array([4500, 4500, 3500, 3500, 2000, 2000, 2000, 100, 100]),
48+
dofs_idx,
49+
)
50+
franka.set_dofs_kv(
51+
np.array([450, 450, 350, 350, 200, 200, 200, 10, 10]),
52+
dofs_idx,
53+
)
54+
franka.set_dofs_force_range(
55+
np.array([-87, -87, -87, -87, -12, -12, -12, -100, -100]),
56+
np.array([87, 87, 87, 87, 12, 12, 12, 100, 100]),
57+
dofs_idx,
58+
)
59+
# Hard reset
60+
for i in range(150):
61+
if i < 50:
62+
franka.set_dofs_position(np.array([1, 1, 0, 0, 0, 0, 0, 0.04, 0.04]), dofs_idx)
63+
elif i < 100:
64+
franka.set_dofs_position(np.array([-1, 0.8, 1, -2, 1, 0.5, -0.5, 0.04, 0.04]), dofs_idx)
65+
else:
66+
franka.set_dofs_position(np.array([0, 0, 0, 0, 0, 0, 0, 0, 0]), dofs_idx)
67+
68+
scene.step()
69+
70+
# PD control
71+
for i in range(1250):
72+
if i == 0:
73+
franka.control_dofs_position(
74+
np.array([1, 1, 0, 0, 0, 0, 0, 0.04, 0.04]),
75+
dofs_idx,
76+
)
77+
elif i == 250:
78+
franka.control_dofs_position(
79+
np.array([-1, 0.8, 1, -2, 1, 0.5, -0.5, 0.04, 0.04]),
80+
dofs_idx,
81+
)
82+
elif i == 500:
83+
franka.control_dofs_position(
84+
np.array([0, 0, 0, 0, 0, 0, 0, 0, 0]),
85+
dofs_idx,
86+
)
87+
elif i == 750:
88+
# control first dof with velocity, and the rest with position
89+
franka.control_dofs_position(
90+
np.array([0, 0, 0, 0, 0, 0, 0, 0, 0])[1:],
91+
dofs_idx[1:],
92+
)
93+
franka.control_dofs_velocity(
94+
np.array([1.0, 0, 0, 0, 0, 0, 0, 0, 0])[:1],
95+
dofs_idx[:1],
96+
)
97+
elif i == 1000:
98+
franka.control_dofs_force(
99+
np.array([0, 0, 0, 0, 0, 0, 0, 0, 0]),
100+
dofs_idx,
101+
)
102+
# This is the internal control force computed based on the given control command
103+
# If using force control, it's the same as the given control command
104+
print("control force:", franka.get_dofs_control_force(dofs_idx))
105+
106+
scene.step()
107+
publisher.spin()

simpub/core/net_component.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ async def update_loop(self):
103103
)
104104
except Exception as e:
105105
logger.error(f"Error when streaming {self.topic_name}: {e}")
106+
traceback.print_exc()
106107
logger.info(f"Streamer for topic {self.topic_name} is stopped")
107108

108109

simpub/core/net_manager.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,16 @@ def stop_node(self):
185185
self.loop.call_soon_threadsafe(self.loop.stop)
186186
except RuntimeError as e:
187187
logger.error(f"One error occurred when stop server: {e}")
188-
self.spin(False)
189-
190-
def spin(self, wait=True):
191-
self.executor.shutdown(wait=wait)
188+
self.executor.shutdown(wait=False)
189+
190+
def spin(self):
191+
while True:
192+
try:
193+
time.sleep(0.01)
194+
except KeyboardInterrupt:
195+
break
196+
self.stop_node()
197+
logger.info("The node has been stopped")
192198

193199
def submit_task(
194200
self,
@@ -217,6 +223,7 @@ async def service_loop(self):
217223
f"One error occurred when processing the Service "
218224
f'"{service_name}": {e}'
219225
)
226+
traceback.print_exc()
220227
await service_socket.send(MSG.SERVICE_ERROR.value)
221228
await async_sleep(0.01)
222229
logger.info("Service loop has been stopped")

simpub/core/simpub_server.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ def __init__(
4444
ip_addr: str = "127.0.0.1",
4545
no_rendered_objects: Optional[List[str]] = None,
4646
no_tracked_objects: Optional[List[str]] = None,
47+
fps: int = 60
4748
) -> None:
4849
self.sim_scene = sim_scene
50+
self.fps = fps
4951
if no_rendered_objects is None:
5052
self.no_rendered_objects = []
5153
else:
@@ -60,7 +62,7 @@ def initialize(self) -> None:
6062
self.scene_update_streamer = Streamer(
6163
topic_name="SceneUpdate",
6264
update_func=self.get_update,
63-
fps=60,
65+
fps=self.fps,
6466
start_streaming=True)
6567
self.asset_service = StrBytesService("Asset", self._on_asset_request)
6668
self.xr_device_set: Set[HashIdentifier] = set()

simpub/parser/gs.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from typing import Tuple, List, Union, Dict
2+
import genesis as gs
3+
from genesis.engine.entities import RigidEntity
4+
from genesis.engine.entities.rigid_entity import RigidLink, RigidGeom
5+
6+
from ..simdata import SimScene, SimObject, SimTransform, SimVisual, VisualType, SimMaterial, SimMesh
7+
8+
9+
def gs2unity_pos(pos: List[float]) -> List[float]:
10+
return [-pos[1], pos[2], pos[0]]
11+
12+
13+
def gs2unity_quat(quat: List[float]) -> List[float]:
14+
return [quat[2], -quat[3], -quat[1], quat[0]]
15+
16+
17+
class GenesisSceneParser:
18+
def __init__(self, gs_scene: gs.Scene):
19+
self.gs_scene = gs_scene
20+
self.sim_scene, self.update_dict = self.parse_gs_scene(gs_scene)
21+
22+
def parse_gs_scene(self, gs_scene: gs.Scene) -> Tuple[SimScene, Dict[str, Union[RigidEntity, RigidLink]]]:
23+
sim_scene = SimScene()
24+
update_dict: Dict[str, Union[RigidEntity, RigidLink]] = {}
25+
body_hierarchy = {}
26+
sim_scene.root = SimObject(name="genesis_scene")
27+
for gs_entity in gs_scene.entities:
28+
sim_object, idx = self.process_rigid_entity(gs_entity, sim_scene)
29+
body_hierarchy[idx] = {
30+
"parent_id": -1,
31+
"sim_object": sim_object,
32+
}
33+
update_dict[sim_object.name] = gs_entity
34+
for link in gs_entity.links:
35+
sim_object, parent_id, idx = self.process_link(
36+
link, sim_scene
37+
)
38+
# update the body hierarchy dictionary
39+
body_hierarchy[idx] = {
40+
"parent_id": parent_id,
41+
"sim_object": sim_object,
42+
}
43+
update_dict[sim_object.name] = link
44+
for body_info in body_hierarchy.values():
45+
parent_id = body_info["parent_id"]
46+
if parent_id == -1:
47+
sim_scene.root.children.append(body_info["sim_object"])
48+
if parent_id in body_hierarchy:
49+
parent_info = body_hierarchy[parent_id]
50+
parent_object: SimObject = parent_info["sim_object"]
51+
parent_object.children.append(body_info["sim_object"])
52+
return sim_scene, update_dict
53+
54+
def process_rigid_entity(
55+
self, gs_rigid_entity: RigidEntity, sim_scene: SimScene
56+
) -> Tuple[SimObject, int]:
57+
sim_object = SimObject(name=str(gs_rigid_entity.uid))
58+
pos: List[float] = [0.0, 0.0, 0.0]
59+
rot: List[float] = [0.0, 0.0, 0.0, 1.0]
60+
if self.gs_scene.is_built:
61+
pos = gs_rigid_entity.get_pos()
62+
rot = gs_rigid_entity.get_quat()
63+
assert type(pos) is List[float]
64+
assert type(rot) is List[float]
65+
sim_object.trans = SimTransform(
66+
gs2unity_pos(pos),
67+
gs2unity_quat(rot),
68+
)
69+
return sim_object, gs_rigid_entity.base_link_idx
70+
71+
def process_link(
72+
self, gs_link: RigidLink, sim_scene: SimScene
73+
) -> Tuple[SimObject, int, int]:
74+
sim_object = SimObject(name=gs_link.name)
75+
sim_object.trans = SimTransform(
76+
gs2unity_pos(gs_link.pos),
77+
gs2unity_quat(gs_link.quat),
78+
)
79+
for gs_geom in gs_link.vgeoms:
80+
sim_visual = self.process_vgeom(gs_geom, sim_scene)
81+
sim_object.visuals.append(sim_visual)
82+
return sim_object, gs_link.parent_idx, gs_link.idx
83+
84+
def process_vgeom(
85+
self, gs_vgeom: RigidGeom, sim_scene: SimScene
86+
) -> SimVisual:
87+
sim_visual = SimVisual(
88+
str(gs_vgeom.uid),
89+
type=VisualType.MESH,
90+
trans=SimTransform(
91+
gs2unity_pos(gs_vgeom.init_pos),
92+
gs2unity_quat(gs_vgeom.init_quat),
93+
),
94+
)
95+
sim_visual.mesh = self.process_mesh(gs_vgeom, sim_scene)
96+
sim_visual.material = SimMaterial(
97+
color=[1.0, 1.0, 1.0, 1.0],
98+
)
99+
return sim_visual
100+
101+
def process_mesh(
102+
self, gs_vgeom: RigidGeom, sim_scene: SimScene
103+
) -> SimMesh:
104+
gs_trimesh_obj = gs_vgeom.get_trimesh()
105+
return SimMesh.create_mesh(
106+
sim_scene,
107+
gs_trimesh_obj.vertices,
108+
gs_trimesh_obj.faces,
109+
)
110+
111+
# TODO: Implement the material and texture from trimesh
112+
# def parse_material(self, gs_material: gs.Material):
113+
# pass
114+
115+
# def parse_mesh(self, gs_mesh: gs.Mesh):
116+
# pass
117+

simpub/sim/genesis_publisher.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from typing import List, Optional, Dict, Tuple
2+
import numpy as np
3+
import taichi as ti
4+
5+
from genesis.engine.scene import Scene as GSScene
6+
from genesis.engine.entities import RigidEntity
7+
from genesis.engine.entities.rigid_entity import RigidLink, RigidGeom
8+
9+
from ..parser.gs import GenesisSceneParser
10+
from ..core.simpub_server import SimPublisher
11+
12+
13+
@ti.data_oriented
14+
class GenesisPublisher(SimPublisher):
15+
16+
def __init__(
17+
self,
18+
gs_scene: GSScene,
19+
host: str = "127.0.0.1",
20+
no_rendered_objects: Optional[List[str]] = None,
21+
no_tracked_objects: Optional[List[str]] = None,
22+
) -> None:
23+
self.parser = GenesisSceneParser(gs_scene)
24+
sim_scene = self.parser.sim_scene
25+
self.update_dict = self.parser.update_dict
26+
self.tracked_obj_trans: Dict[str, Tuple[np.ndarray, np.ndarray]] = {}
27+
super().__init__(
28+
sim_scene,
29+
host,
30+
no_rendered_objects,
31+
no_tracked_objects,
32+
fps=60,
33+
)
34+
35+
def get_update(self) -> Dict[str, List[float]]:
36+
# TODO: Fix the problem with taiqi kernel issue
37+
return {}
38+
if not self.parser.gs_scene.is_built:
39+
return {}
40+
state = {}
41+
for name, item in self.update_dict.items():
42+
if name in self.no_tracked_objects:
43+
continue
44+
if isinstance(item, RigidEntity):
45+
pos = item.get_pos().tolist()
46+
print(pos, item.get_quat())
47+
quat = item.get_quat().tolist()
48+
elif isinstance(item, RigidLink):
49+
pos = item.get_pos().tolist()
50+
quat = item.get_quat().tolist()
51+
else:
52+
continue
53+
state[name] = [
54+
pos[0], pos[1], pos[2], quat[0], quat[1], quat[2], quat[3]
55+
]
56+
return state

0 commit comments

Comments
 (0)