7 changed files with 365 additions and 9 deletions
-
2teleop/robot_control/robot_arm.py
-
26teleop/teleop_hand_and_arm.py
-
1teleop/utils/data/-home-unitree-Code-unitree-opject-unitree_sim_isaaclab-.txt
-
4teleop/utils/episode_writer.py
-
8teleop/utils/server_api.py
-
48teleop/utils/sim_state_client.py
-
285teleop/utils/sim_state_topic.py
@ -0,0 +1 @@ |
|||||
|
/home/unitree/Code/unitree-opject/unitree_sim_isaaclab/data |
||||
@ -0,0 +1,8 @@ |
|||||
|
# service name |
||||
|
SERVICE_NAME = "get_sim_state" |
||||
|
|
||||
|
# api version |
||||
|
API_VERSION = "1.0.0.1" |
||||
|
|
||||
|
# api id |
||||
|
API_ID_GET_SIM_STATE = 1008 |
||||
@ -0,0 +1,48 @@ |
|||||
|
import time |
||||
|
import json |
||||
|
from unitree_sdk2py.core.channel import ChannelFactoryInitialize |
||||
|
from unitree_sdk2py.rpc.client import Client |
||||
|
from utils.server_api import SERVICE_NAME, API_VERSION, API_ID_GET_SIM_STATE |
||||
|
|
||||
|
|
||||
|
class SimStateClient(Client): |
||||
|
def __init__(self, enableLease: bool = False): |
||||
|
super().__init__(SERVICE_NAME, enableLease) |
||||
|
|
||||
|
def Init(self): |
||||
|
self._RegistApi(API_ID_GET_SIM_STATE, 0) |
||||
|
self._SetApiVerson(API_VERSION) |
||||
|
|
||||
|
def GetSimState_client_call(self): |
||||
|
c, d = self._Call(API_ID_GET_SIM_STATE, json.dumps("")) |
||||
|
return c, d |
||||
|
|
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
# initialize channel factory. |
||||
|
ChannelFactoryInitialize(0) |
||||
|
|
||||
|
# create client |
||||
|
client = SimStateClient() |
||||
|
client.Init() |
||||
|
client.SetTimeout(5.0) |
||||
|
|
||||
|
# get server version |
||||
|
code, serverApiVersion = client.GetServerApiVersion() |
||||
|
print("server api version:", serverApiVersion) |
||||
|
|
||||
|
# wait lease applied |
||||
|
client.WaitLeaseApplied() |
||||
|
print("lease applied") |
||||
|
# test api |
||||
|
while True: |
||||
|
try: |
||||
|
c, d = client.GetSimState_client_call() |
||||
|
print("client get sim state ret:", c) |
||||
|
print("sim state data:", d) |
||||
|
except Exception as e: |
||||
|
print("Error getting sim state:", e) |
||||
|
time.sleep(1.0) |
||||
|
|
||||
|
|
||||
@ -0,0 +1,285 @@ |
|||||
|
# Copyright (c) 2025, Unitree Robotics Co., Ltd. All Rights Reserved. |
||||
|
# License: Apache License, Version 2.0 |
||||
|
""" |
||||
|
Simple sim state subscriber class |
||||
|
Subscribe to rt/sim_state_cmd topic and write to shared memory |
||||
|
""" |
||||
|
|
||||
|
import threading |
||||
|
import time |
||||
|
import json |
||||
|
from multiprocessing import shared_memory |
||||
|
from typing import Any, Dict, Optional |
||||
|
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize |
||||
|
from unitree_sdk2py.idl.std_msgs.msg.dds_ import String_ |
||||
|
|
||||
|
|
||||
|
class SharedMemoryManager: |
||||
|
"""Shared memory manager""" |
||||
|
|
||||
|
def __init__(self, name: str = None, size: int = 512): |
||||
|
"""Initialize shared memory manager |
||||
|
|
||||
|
Args: |
||||
|
name: shared memory name, if None, create new one |
||||
|
size: shared memory size (bytes) |
||||
|
""" |
||||
|
self.size = size |
||||
|
self.lock = threading.RLock() # reentrant lock |
||||
|
|
||||
|
if name: |
||||
|
try: |
||||
|
self.shm = shared_memory.SharedMemory(name=name) |
||||
|
self.shm_name = name |
||||
|
self.created = False |
||||
|
except FileNotFoundError: |
||||
|
self.shm = shared_memory.SharedMemory(create=True, size=size) |
||||
|
self.shm_name = self.shm.name |
||||
|
self.created = True |
||||
|
else: |
||||
|
self.shm = shared_memory.SharedMemory(create=True, size=size) |
||||
|
self.shm_name = self.shm.name |
||||
|
self.created = True |
||||
|
|
||||
|
def write_data(self, data: Dict[str, Any]) -> bool: |
||||
|
"""Write data to shared memory |
||||
|
|
||||
|
Args: |
||||
|
data: data to write |
||||
|
|
||||
|
Returns: |
||||
|
bool: write success or not |
||||
|
""" |
||||
|
try: |
||||
|
with self.lock: |
||||
|
json_str = json.dumps(data) |
||||
|
json_bytes = json_str.encode('utf-8') |
||||
|
|
||||
|
if len(json_bytes) > self.size - 8: # reserve 8 bytes for length and timestamp |
||||
|
print(f"Warning: Data too large for shared memory ({len(json_bytes)} > {self.size - 8})") |
||||
|
return False |
||||
|
|
||||
|
# write timestamp (4 bytes) and data length (4 bytes) |
||||
|
timestamp = int(time.time()) & 0xFFFFFFFF # 32-bit timestamp, use bitmask to ensure in range |
||||
|
self.shm.buf[0:4] = timestamp.to_bytes(4, 'little') |
||||
|
self.shm.buf[4:8] = len(json_bytes).to_bytes(4, 'little') |
||||
|
|
||||
|
# write data |
||||
|
self.shm.buf[8:8+len(json_bytes)] = json_bytes |
||||
|
return True |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"Error writing to shared memory: {e}") |
||||
|
return False |
||||
|
|
||||
|
def read_data(self) -> Optional[Dict[str, Any]]: |
||||
|
"""Read data from shared memory |
||||
|
|
||||
|
Returns: |
||||
|
Dict[str, Any]: read data dictionary, return None if failed |
||||
|
""" |
||||
|
try: |
||||
|
with self.lock: |
||||
|
# read timestamp and data length |
||||
|
timestamp = int.from_bytes(self.shm.buf[0:4], 'little') |
||||
|
data_len = int.from_bytes(self.shm.buf[4:8], 'little') |
||||
|
|
||||
|
if data_len == 0: |
||||
|
return None |
||||
|
|
||||
|
# read data |
||||
|
json_bytes = bytes(self.shm.buf[8:8+data_len]) |
||||
|
data = json.loads(json_bytes.decode('utf-8')) |
||||
|
data['_timestamp'] = timestamp # add timestamp information |
||||
|
return data |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"Error reading from shared memory: {e}") |
||||
|
return None |
||||
|
|
||||
|
def get_name(self) -> str: |
||||
|
"""Get shared memory name""" |
||||
|
return self.shm_name |
||||
|
|
||||
|
def cleanup(self): |
||||
|
"""Clean up shared memory""" |
||||
|
if hasattr(self, 'shm') and self.shm: |
||||
|
self.shm.close() |
||||
|
if self.created: |
||||
|
try: |
||||
|
self.shm.unlink() |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
def __del__(self): |
||||
|
"""Destructor""" |
||||
|
self.cleanup() |
||||
|
|
||||
|
|
||||
|
class SimStateSubscriber: |
||||
|
"""Simple sim state subscriber class""" |
||||
|
|
||||
|
def __init__(self, shm_name: str = "sim_state_cmd_data", shm_size: int = 3096): |
||||
|
"""Initialize the subscriber |
||||
|
|
||||
|
Args: |
||||
|
shm_name: shared memory name |
||||
|
shm_size: shared memory size |
||||
|
""" |
||||
|
self.shm_name = shm_name |
||||
|
self.shm_size = shm_size |
||||
|
self.running = False |
||||
|
self.subscriber = None |
||||
|
self.subscribe_thread = None |
||||
|
self.shared_memory = None |
||||
|
|
||||
|
# initialize shared memory |
||||
|
self._setup_shared_memory() |
||||
|
|
||||
|
print(f"[SimStateSubscriber] Initialized with shared memory: {shm_name}") |
||||
|
|
||||
|
def _setup_shared_memory(self): |
||||
|
"""Setup shared memory""" |
||||
|
try: |
||||
|
self.shared_memory = SharedMemoryManager(self.shm_name, self.shm_size) |
||||
|
print(f"[SimStateSubscriber] Shared memory setup successfully") |
||||
|
except Exception as e: |
||||
|
print(f"[SimStateSubscriber] Failed to setup shared memory: {e}") |
||||
|
|
||||
|
def _message_handler(self, msg: String_): |
||||
|
"""Handle received messages""" |
||||
|
try: |
||||
|
# parse the received message |
||||
|
data = json.loads(msg.data) |
||||
|
# print(f"[SimStateSubscriber] Received message: {data}") |
||||
|
|
||||
|
# write to shared memory |
||||
|
if self.shared_memory: |
||||
|
self.shared_memory.write_data(data) |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"[SimStateSubscriber] Error processing message: {e}") |
||||
|
|
||||
|
def _subscribe_loop(self): |
||||
|
"""Subscribe loop thread""" |
||||
|
print(f"[SimStateSubscriber] Subscribe thread started") |
||||
|
|
||||
|
while self.running: |
||||
|
try: |
||||
|
time.sleep(0.001) # keep thread alive |
||||
|
except Exception as e: |
||||
|
print(f"[SimStateSubscriber] Error in subscribe loop: {e}") |
||||
|
time.sleep(0.01) |
||||
|
|
||||
|
print(f"[SimStateSubscriber] Subscribe thread stopped") |
||||
|
|
||||
|
def start_subscribe(self): |
||||
|
"""Start subscribing""" |
||||
|
if self.running: |
||||
|
print(f"[SimStateSubscriber] Already running") |
||||
|
return |
||||
|
|
||||
|
try: |
||||
|
# setup subscriber |
||||
|
self.subscriber = ChannelSubscriber("rt/sim_state", String_) |
||||
|
self.subscriber.Init(self._message_handler, 10) |
||||
|
|
||||
|
self.running = True |
||||
|
print(f"[SimStateSubscriber] Subscriber initialized") |
||||
|
# start subscribe thread |
||||
|
self.subscribe_thread = threading.Thread(target=self._subscribe_loop) |
||||
|
self.subscribe_thread.daemon = True |
||||
|
self.subscribe_thread.start() |
||||
|
|
||||
|
print(f"[SimStateSubscriber] Started subscribing to rt/sim_state_cmd") |
||||
|
|
||||
|
except Exception as e: |
||||
|
print(f"[SimStateSubscriber] Failed to start subscribing: {e}") |
||||
|
self.running = False |
||||
|
|
||||
|
def stop_subscribe(self): |
||||
|
"""Stop subscribing""" |
||||
|
if not self.running: |
||||
|
return |
||||
|
|
||||
|
print(f"[SimStateSubscriber] Stopping subscriber...") |
||||
|
self.running = False |
||||
|
|
||||
|
# wait for thread to finish |
||||
|
if self.subscribe_thread: |
||||
|
self.subscribe_thread.join(timeout=1.0) |
||||
|
|
||||
|
print(f"[SimStateSubscriber] Subscriber stopped") |
||||
|
|
||||
|
def read_data(self) -> Optional[Dict[str, Any]]: |
||||
|
"""Read data from shared memory |
||||
|
|
||||
|
Returns: |
||||
|
Dict: received data, None if no data or error |
||||
|
""" |
||||
|
try: |
||||
|
if self.shared_memory: |
||||
|
return self.shared_memory.read_data() |
||||
|
return None |
||||
|
except Exception as e: |
||||
|
print(f"[SimStateSubscriber] Error reading data: {e}") |
||||
|
return None |
||||
|
|
||||
|
def is_running(self) -> bool: |
||||
|
"""Check if subscriber is running""" |
||||
|
return self.running |
||||
|
|
||||
|
def __del__(self): |
||||
|
"""Destructor""" |
||||
|
self.stop_subscribe() |
||||
|
|
||||
|
|
||||
|
# convenient functions |
||||
|
def create_sim_state_subscriber(shm_name: str = "sim_state_cmd_data", shm_size: int = 3096) -> SimStateSubscriber: |
||||
|
"""Create a sim state subscriber instance |
||||
|
|
||||
|
Args: |
||||
|
shm_name: shared memory name |
||||
|
shm_size: shared memory size |
||||
|
|
||||
|
Returns: |
||||
|
SimStateSubscriber: subscriber instance |
||||
|
""" |
||||
|
return SimStateSubscriber(shm_name, shm_size) |
||||
|
|
||||
|
|
||||
|
def start_sim_state_subscribe(shm_name: str = "sim_state_cmd_data", shm_size: int = 3096) -> SimStateSubscriber: |
||||
|
"""Start sim state subscribing |
||||
|
|
||||
|
Args: |
||||
|
shm_name: shared memory name |
||||
|
shm_size: shared memory size |
||||
|
|
||||
|
Returns: |
||||
|
SimStateSubscriber: started subscriber instance |
||||
|
""" |
||||
|
subscriber = create_sim_state_subscriber(shm_name, shm_size) |
||||
|
subscriber.start_subscribe() |
||||
|
return subscriber |
||||
|
|
||||
|
|
||||
|
# if __name__ == "__main__": |
||||
|
# # example usage |
||||
|
# print("Starting sim state subscriber...") |
||||
|
# ChannelFactoryInitialize(0) |
||||
|
# # create and start subscriber |
||||
|
# subscriber = start_sim_state_subscribe() |
||||
|
|
||||
|
# try: |
||||
|
# # keep running and check for data |
||||
|
# while True: |
||||
|
# data = subscriber.read_data() |
||||
|
# if data: |
||||
|
# print(f"Read data: {data}") |
||||
|
# time.sleep(1) |
||||
|
|
||||
|
# except KeyboardInterrupt: |
||||
|
# print("\nInterrupted by user") |
||||
|
# finally: |
||||
|
# subscriber.stop_subscribe() |
||||
|
# print("Subscriber stopped") |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue