#!/usr/bin/env python3 """MuJoCo viewer for original Go1 (45-dim, PD 80/1.0, action_scale=0.05)""" import numpy as np, mujoco, onnxruntime as ort, os, time, threading, queue from mujoco import viewer from pynput import keyboard PROJECT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ONNX = os.path.join(PROJECT, "exports_go1_orig", "policy.onnx") XML_DIR = os.path.join(PROJECT, "motrix_envs/src/motrix_envs/locomotion/go1/xmls") # Original params NUM_OBS = 57 KP, KD = 80.0, 0.5 # KD=0.5 + joint_damping(0.5) = 1.0 = training kd ACTION_SCALE = 0.05 CLIP = 23.7 DEFAULT = np.array([-0.0,0.9,-1.8,0.0,0.9,-1.8,-0.0,0.9,-1.8,0.0,0.9,-1.8], dtype=np.float32) class KB: def __init__(self): self._q=queue.Queue(); self.running=True; self.held=set() def _n(self,k): try: if hasattr(k,'char') and k.char: return k.char.lower() except: pass return str(k).lower() def _w(self): while self.running: try: et,k=self._q.get(timeout=0.05); n=self._n(k) if et=='press': self.held.add(n) elif et=='release': self.held.discard(n) except queue.Empty: pass def init(self): self._l=keyboard.Listener(on_press=lambda k:self._q.put(('press',k)), on_release=lambda k:self._q.put(('release',k))) self._l.start(); self._t=threading.Thread(target=self._w,daemon=True); self._t.start() def held_keys(self): return self.held.copy() def stop(self): self.running=False; self._l.stop() def get_sensor(m,d,name): sid=mujoco.mj_name2id(m,mujoco.mjtObj.mjOBJ_SENSOR,name) if sid<0: return None adr=m.sensor_adr[sid]; return d.sensordata[adr:adr+m.sensor_dim[sid]].copy() def compute_obs(model,data,cmd,last_a): obs=np.zeros(NUM_OBS,dtype=np.float32) g=get_sensor(model,data,"gyro") obs[0:3]=(g if g is not None else data.qvel[3:6])*0.25 R=data.xmat[1].reshape(3,3) obs[3:6]=(R.T@np.array([0.,0.,-1.])).astype(np.float32) obs[6:18]=(data.qpos[7:19]-DEFAULT)*1.0 obs[18:30]=data.qvel[6:18]*0.05 obs[30:42]=last_a obs[42:45]=cmd; obs[45:57]=0.0 # contact_force*np.array([2.,2.,0.25],dtype=np.float32) return np.clip(obs,-100.,100.) def main(): os.chdir(XML_DIR) model=mujoco.MjModel.from_xml_string(open("scene_motor_actuator.xml").read()) data=mujoco.MjData(model) data.qpos[0:3]=[0,0,0.42]; data.qpos[3:7]=[1,0,0,0]; data.qpos[7:19]=DEFAULT mujoco.mj_forward(model,data) session=ort.InferenceSession(ONNX,providers=['CPUExecutionProvider']) print(f"[ORIG] kp={KP} kd={KD+0.5} action_scale={ACTION_SCALE} | W/S前后 Q/E左右 A/D旋转") kb=KB(); kb.init() view=viewer.launch_passive(model,data) step,vx,vy,wz=0,0.,0.,0. last_a=np.zeros(12,dtype=np.float32); action=np.zeros(12,dtype=np.float32) dec=2 while view.is_running(): keys=kb.held_keys() if 'escape' in keys: break if 'r' in keys: data.qpos[0:3]=[0,0,0.42]; data.qpos[3:7]=[1,0,0,0]; data.qpos[7:19]=DEFAULT data.qvel[:]=0; last_a[:]=0; mujoco.mj_forward(model,data) if ' ' in keys: vx=vy=wz=0. vx=1.0 if 'w' in keys else (-1.0 if 's' in keys else 0.) vy=1.0 if 'q' in keys else (-1.0 if 'e' in keys else 0.) wz=1.0 if 'a' in keys else (-1.0 if 'd' in keys else 0.) if step%dec==0: obs=compute_obs(model,data,np.array([vx,vy,wz],dtype=np.float32),last_a) action=session.run(None,{'observations':obs.reshape(1,-1).astype(np.float32)})[0][0] action=np.clip(action,-CLIP,CLIP); last_a=action.copy() target=DEFAULT+action*ACTION_SCALE t=KP*(target-data.qpos[7:19])-KD*data.qvel[6:18] data.ctrl[:]=np.clip(t,-CLIP,CLIP) mujoco.mj_step(model,data); view.sync(); step+=1; time.sleep(0.001) kb.stop(); view.close() if __name__=="__main__": main()