Instructions to use AlexWortega/tinyvla with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use AlexWortega/tinyvla with LeRobot:
- Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python | |
| """Closed-loop MicroDuck: obs.rs-раскладка 61D из MuJoCo, учитель ONNX или VLA. | |
| Валидация конвенций через учителя: если alpha_walking в этой петле идёт — | |
| obs-билдер и применение действий верны, и той же петлёй можно гнать VLA. | |
| obs.rs layout: | |
| 0..3 gyro (trunk frame, rad/s) | |
| 3..6 projected gravity (trunk frame, unit) | |
| 6..20 joint pos - home (14, без рта) | |
| 20..34 joint vel (14) | |
| 34..48 previous action (14) | |
| 48..61 command: vx,vy,vyaw | 4 head targets | body x,y(=0) | z | roll | pitch | yaw(=0) | |
| Действие: 14 позиционных таргетов; применение ctrl = home + action (STAND offset, | |
| как в mjlab JointPositionAction; teacher выход в тех же единицах). | |
| """ | |
| import sys, json | |
| from pathlib import Path | |
| import numpy as np | |
| import mujoco | |
| SCENE='/root/microduck_rl/src/mjlab_microduck/robot/microduck/scene_walk.xml' | |
| model=mujoco.MjModel.from_xml_path(SCENE) | |
| data=mujoco.MjData(model) | |
| key=model.keyframe('STAND') | |
| HOME=key.ctrl.copy() # home-поза 14 суставов | |
| CTRL_DT=1/50 | |
| SPC=max(1,int(round(CTRL_DT/model.opt.timestep))) | |
| def quat_rotate_inv(q, v): | |
| w,x,y,z=q | |
| # R^T v | |
| R=np.array([[1-2*(y*y+z*z),2*(x*y-w*z),2*(x*z+w*y)], | |
| [2*(x*y+w*z),1-2*(x*x+z*z),2*(y*z-w*x)], | |
| [2*(x*z-w*y),2*(y*z+w*x),1-2*(x*x+y*y)]]) | |
| return R.T@v | |
| def build_obs(prev_action, command): | |
| q=data.qpos[3:7] # trunk quat (w,x,y,z) | |
| gyro=data.qvel[3:6].copy() # угловая скорость в ЛОКАЛЬНОЙ раме (mujoco freejoint) | |
| grav=quat_rotate_inv(q, np.array([0,0,-1.0])) | |
| jpos=data.qpos[7:7+14]-HOME | |
| jvel=data.qvel[6:6+14] | |
| return np.concatenate([gyro,grav,jpos,jvel,prev_action,command]).astype(np.float32) | |
| def run(policy_step, command, seconds=8, tag="run", render_cam=None, vla_hooks=None): | |
| import imageio.v2 as imageio | |
| renderer=mujoco.Renderer(model,height=320,width=320) | |
| cam=mujoco.MjvCamera(); cam.distance,cam.azimuth,cam.elevation=0.65,140,-12 | |
| mujoco.mj_resetDataKeyframe(model,data,key.id); mujoco.mj_forward(model,data) | |
| prev=np.zeros(14,np.float32) | |
| frames=[]; fell_at=None | |
| n_ctrl=int(seconds*50) | |
| for i in range(n_ctrl): | |
| if i%5==0: # 10 Гц перепланирование | |
| obs=build_obs(prev,command) | |
| plan=policy_step(obs, i) # (k,14) минимум 5 шагов | |
| a=plan[i%5] if plan.ndim==2 else plan | |
| data.ctrl[:]=HOME+a | |
| prev=a.astype(np.float32) | |
| for _ in range(SPC): mujoco.mj_step(model,data) | |
| if data.qpos[2]<0.05 and fell_at is None: fell_at=i/50 | |
| if i%3==0: | |
| cam.lookat[:]=[float(data.qpos[0]),float(data.qpos[1]),0.12] | |
| renderer.update_scene(data,camera=cam) | |
| frames.append(renderer.render().copy()) | |
| imageio.mimwrite(f"/root/cl_{tag}.mp4",frames,fps=16,quality=7) | |
| print(f"{tag}: {seconds}s, высота в конце {data.qpos[2]:.3f}, " | |
| f"{'УПАЛ на '+str(round(fell_at,1))+'s' if fell_at else 'НЕ УПАЛ'}, " | |
| f"пройдено xy {np.hypot(data.qpos[0],data.qpos[1]):.2f} м", flush=True) | |
| return fell_at | |
| if __name__=="__main__": | |
| import onnxruntime as ort | |
| which=sys.argv[1] if len(sys.argv)>1 else "walking" | |
| sess=ort.InferenceSession(f"/root/.cache/huggingface/hub/models--pollen-robotics--microduck-policies/snapshots/{'*'}/alpha_{which}.onnx".replace('*','') if False else __import__('huggingface_hub').hf_hub_download("pollen-robotics/microduck-policies", f"alpha_{which}.onnx")) | |
| def teacher(obs,i): | |
| a=sess.run(None,{"obs":obs[None].astype(np.float32)})[0][0] | |
| return np.tile(a,(5,1)) # учитель даёт 1 шаг — держим до реплана | |
| cmd=np.zeros(13,np.float32) | |
| if which=="walking": cmd[0]=0.15 # vx м/с | |
| cmd[9]=0.115 # body z: стоячая высота | |
| run(teacher,cmd,seconds=8,tag=f"teacher_{which}") | |