Instructions to use JackDH/microduck-rolling-start with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Microduck
How to use JackDH/microduck-rolling-start with Microduck:
# Replace SLOT with the slot specified in the model card (walk, stand, sitstand, ground_pick, kick_left, kick_right, roulade). sudo robotctl policy load SLOT JackDH/microduck-rolling-start
- Notebooks
- Google Colab
- Kaggle
Download training/export_actor.py from JackDH/microduck-rolling-start: direct link, hf CLI and curl.
- Browser
- Download file 4.27 kB
-
https://huggingface.co/JackDH/microduck-rolling-start/resolve/main/training/export_actor.py
- Command line
-
hf download hf://JackDH/microduck-rolling-start/training/export_actor.py
-
curl -L -o export_actor.py https://huggingface.co/JackDH/microduck-rolling-start/resolve/main/training/export_actor.py
4.27 kB
| """Export the skating, rolling and learned landing actions as one ONNX policy.""" | |
| import learn | |
| import numpy as np | |
| import onnx | |
| from onnx import TensorProto, helper, numpy_helper | |
| def export(weights, destination): | |
| nodes, constants = [], [] | |
| def constant(name, value, dtype=np.float32): | |
| constants.append(numpy_helper.from_array(np.asarray(value, dtype=dtype), name)) | |
| return name | |
| def node(kind, name, *inputs, **attributes): | |
| nodes.append(helper.make_node(kind, inputs, [name], name=name, **attributes)) | |
| return name | |
| def column(name, ix): | |
| return node( | |
| "Gather", name, "obs", constant(name + "_idx", [ix], np.int64), axis=1 | |
| ) | |
| def scaled_clip(value, factor, name): | |
| scaled = node( | |
| "Mul", name + "_scaled", value, constant(name + "_factor", factor) | |
| ) | |
| return node("Clip", name, scaled, neg_one, one) | |
| zero = constant("zero", 0) | |
| one = constant("one", 1) | |
| neg_one = constant("neg_one", -1) | |
| cue = column("cue", 60) | |
| phase = [ | |
| node("Greater", "after_" + str(i), cue, constant("boundary_" + str(i), i + 0.5)) | |
| for i in range(5) | |
| ] | |
| models = {} | |
| for name in learn.BASE_MODELS: | |
| file = learn.model_file(name) | |
| source = onnx.compose.add_prefix(onnx.load(str(file)), name + "/") | |
| scale = 1.12 if name == "roll" else 1.0 | |
| divisor = np.ones(61, np.float32) | |
| divisor[34:48] = scale | |
| mask = np.ones(61, np.float32) | |
| mask[48 if name == "roll" else 49 :] = 0 | |
| prepared = node( | |
| "Div", name + "_obs", "obs", constant(name + "_divisor", divisor) | |
| ) | |
| node( | |
| "Mul", source.graph.input[0].name, prepared, constant(name + "_mask", mask) | |
| ) | |
| nodes.extend(source.graph.node) | |
| constants.extend(source.graph.initializer) | |
| models[name] = node( | |
| "Mul", | |
| name + "_action", | |
| source.graph.output[0].name, | |
| constant(name + "_gain", scale), | |
| ) | |
| base = node("Where", "recovery_base", phase[3], models["rise"], models["roll"]) | |
| arr = np.asarray(weights, np.float32) | |
| biases = arr[:24].reshape(4, 6) | |
| bias = constant("phase2", biases[0]) | |
| for n in range(1, 4): | |
| bias = node( | |
| "Where", | |
| "bias" + str(n), | |
| phase[n + 1], | |
| constant("phase" + str(n + 2), biases[n]), | |
| bias, | |
| ) | |
| pitch_gravity = column("pitch_gravity", 3) | |
| roll_gravity = column("roll_gravity", 4) | |
| pitch_rate = scaled_clip(column("pitch_rate_body", 1), 0.14, "pitch_rate") | |
| roll_rate = scaled_clip(column("roll_rate_body", 0), 0.15, "roll_rate") | |
| def vector_term(obs, name, arr): | |
| return node("Mul", name, obs, constant(name + "_weights", arr)) | |
| # Sum the feedback before adding the offsets, as in the trained policy. | |
| pitch_terms = node( | |
| "Add", | |
| "pitch_feedback", | |
| vector_term(pitch_gravity, "pitch_gravity_term", arr[24:27]), | |
| vector_term(pitch_rate, "pitch_rate_term", arr[27:30]), | |
| ) | |
| roll_terms = node( | |
| "Add", | |
| "roll_feedback", | |
| vector_term(roll_gravity, "roll_gravity_term", arr[30]), | |
| vector_term(roll_rate, "roll_rate_term", arr[31]), | |
| ) | |
| zeros = node("Mul", "zeros", pitch_rate, zero) | |
| feedback = node("Concat", "feedback", pitch_terms, zeros, zeros, roll_terms, axis=1) | |
| activated = node("Tanh", "bounded", node("Add", "raw", bias, feedback)) | |
| adjustment = node( | |
| "MatMul", "joint_adjustment", activated, constant("joint_map", learn.MAP) | |
| ) | |
| corrected = node("Add", "corrected", base, adjustment) | |
| roll_or_recovery = node("Where", "trick", phase[1], corrected, base) | |
| node("Where", "actions", phase[0], roll_or_recovery, models["skate"]) | |
| graph = helper.make_graph( | |
| nodes, | |
| "skate_land_skate", | |
| [helper.make_tensor_value_info("obs", TensorProto.FLOAT, [1, 61])], | |
| [helper.make_tensor_value_info("actions", TensorProto.FLOAT, [1, 14])], | |
| constants, | |
| ) | |
| model = helper.make_model( | |
| graph, ir_version=8, opset_imports=[helper.make_opsetid("", 18)] | |
| ) | |
| onnx.checker.check_model(model, full_check=True) | |
| onnx.save(model, str(destination)) | |