Train with RL¶
Alloy's environments follow the shape of Gymnasium's vector API: every world is one environment, stepped together.
pip install "alloy-engine[envs]"
Make a ready-made environment, reset it and take one step with a sampled action.
import numpy as np
import alloy
import alloy.envs
env = alloy.envs.make("GripperLift-v0", num_envs=4)
obs, info = env.reset(seed=0)
env.action_space.seed(0)
obs, reward, terminated, truncated, info = env.step(env.action_space.sample())
print(np.asarray(obs).shape, env.action_space.shape, np.asarray(reward).shape)
env.close()
(4, 22) (4, 3) (4,)
Four worlds give four rows: 22 observation values, 3 action values (lift and two finger closes) and one reward each.
Your own task is a scene, a Task and a VectorEnv. The task names the observations, the actions, the reward terms
and when an episode succeeds:
from alloy.envs import Task, VectorEnv, checks as C
from alloy.envs.robots import gantry_actions, gantry_gripper
scene = alloy.Scene(dt=0.02)
scene.add_ground()
scene.add_box("cube", size=(0.05, 0.05, 0.05), pos=(0.0, 0.0, 0.025), mass=0.1)
scene.add_robot("gripper", mjcf=gantry_gripper(), pos=(0.0, 0.0, 0.2))
lifted = C.lifted("cube", by=0.05)
task = Task(
obs=["gripper", "cube"],
actions=gantry_actions("gripper", yaw=False),
reward={"reach": (-1.0, C.distance("gripper/palm", "cube")), "lift": (10.0, lifted)},
success=lifted & C.upright("cube"), max_steps=100)
env = VectorEnv(scene, task, worlds=4)
obs, info = env.reset(seed=0)
obs, reward, terminated, truncated, info = env.step(env.action_space.sample())
print(env.action_names(), sorted(info["terms"]))
env.close()
['gripper/x', 'gripper/y', 'gripper/z', 'gripper/close'] ['lift', 'reach']
The reward is the weighted sum of its terms, and info["terms"] holds each term. A world that ends starts its next
episode by itself.
To train, one command runs PPO with a library of your choice (it is not run here):
pip install rsl-rl-lib
python -m alloy.envs.wrappers.train GripperLift-v0 --lib rsl_rl
Legged robots¶
from alloy.envs.loco import go2_velocity makes a Unitree Go2 that follows velocity commands on flat ground. Train it
with python -m alloy.envs.loco.train. The policy walks blind, and nothing here was run on a real robot.
Randomize per world¶
Draw a different value in every world with scene.randomize("ball", "friction", 0.6, 1.0): it is seeded by
alloy.build(scene, seed=). Other fields such as density work the same way.
Limits¶
- No shipped trainer learns from camera images: on a camera environment (
GripperLiftCamera-v0) the one-command trainer trains on the state only and does not say so. - A robot with no
<actuator>ignores actions without an error: printenv.sim.names()["actuators"]first. - On a GPU runs do not repeat bit for bit once bodies touch, so two training runs with the same seed differ.
- A world that goes INVALID is frozen and its episode ends: count
info["invalid"]before you trust a result (World health). - Training an arm to grasp does not work reliably yet: treat any grasping task as early.
Next¶
- Imitation learning: record demonstrations and evaluate a policy.
- Robots: load a robot and drive its joints.
- Known limits