Instructions to use Histochemichael/microduck-electric-slide-policy with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Microduck
How to use Histochemichael/microduck-electric-slide-policy 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 Histochemichael/microduck-electric-slide-policy
- Notebooks
- Google Colab
- Kaggle
Download code/simple_choreography.py from Histochemichael/microduck-electric-slide-policy: direct link, hf CLI and curl.
- Browser
- Download file 6.64 kB
-
https://huggingface.co/Histochemichael/microduck-electric-slide-policy/resolve/main/code/simple_choreography.py
- Command line
-
hf download hf://Histochemichael/microduck-electric-slide-policy/code/simple_choreography.py
-
curl -L -o simple_choreography.py https://huggingface.co/Histochemichael/microduck-electric-slide-policy/resolve/main/code/simple_choreography.py
6.64 kB
| """Ultra-simple 16-count Electric Slide retargeted for Microduck. | |
| The sequence follows the morphology-specific choreography supplied by the | |
| user: three travelling steps and a tap/bob in each direction, three steps | |
| back and a tap, then rock forward/back, step forward, and pivot left. The | |
| official 50 Hz walking policy remains active throughout; these functions only | |
| provide dance commands and small additive joint-space targets. The free root | |
| is never written after reset. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| import math | |
| import numpy as np | |
| from choreography import BEAT_SECONDS | |
| COUNTS_PER_WALL = 16 | |
| WALLS = 4 | |
| SHOW_BEATS = COUNTS_PER_WALL * WALLS | |
| SHOW_SECONDS = SHOW_BEATS * BEAT_SECONDS | |
| class SimpleCount: | |
| label: str | |
| swing: str | |
| placement: str | |
| weight_transfer: bool = True | |
| SIMPLE_COUNTS = ( | |
| SimpleCount("right step right", "right", "side_right"), | |
| SimpleCount("left follows right", "left", "follow_right"), | |
| SimpleCount("right step right", "right", "side_right"), | |
| SimpleCount("left tap + beak-snap head bob", "left", "touch_bob", False), | |
| SimpleCount("left step left", "left", "side_left"), | |
| SimpleCount("right follows left", "right", "follow_left"), | |
| SimpleCount("left step left", "left", "side_left"), | |
| SimpleCount("right tap + beak-snap head bob", "right", "touch_bob", False), | |
| SimpleCount("right step back", "right", "back"), | |
| SimpleCount("left step back", "left", "back"), | |
| SimpleCount("right step back", "right", "back"), | |
| SimpleCount("left tap", "left", "touch", False), | |
| SimpleCount("rock forward", "none", "lean_forward", False), | |
| SimpleCount("rock back", "none", "lean_back", False), | |
| SimpleCount("left step forward", "left", "forward"), | |
| SimpleCount("right scuff + pivot 90 degrees left", "right", "turn_scuff"), | |
| ) | |
| def _smoothstep(value: float) -> float: | |
| value = max(0.0, min(1.0, float(value))) | |
| return value * value * (3.0 - 2.0 * value) | |
| def count_phase(time_seconds: float) -> tuple[int, int, float]: | |
| beat = max(0.0, time_seconds) / BEAT_SECONDS | |
| whole = int(math.floor(beat)) | |
| return whole // COUNTS_PER_WALL, whole % COUNTS_PER_WALL, beat - whole | |
| def policy_for_time(time_seconds: float) -> str: | |
| """Keep a single continuous balance policy for the complete phrase.""" | |
| return "walk" | |
| def command_vector(time_seconds: float) -> np.ndarray: | |
| """Return the official walking policy's 13D command block.""" | |
| wall, count, phase = count_phase(time_seconds) | |
| spec = SIMPLE_COUNTS[count] | |
| command = np.zeros(13, dtype=np.float32) | |
| groove = math.sin(2.0 * math.pi * phase) | |
| pulse = math.sin(math.pi * phase) | |
| command[3] = 0.030 * groove | |
| command[4] = -0.025 * groove | |
| command[5] = 0.070 * groove | |
| command[6] = 0.055 * groove | |
| command[9] = -0.004 * abs(groove) | |
| command[10] = 0.020 * groove | |
| if spec.placement == "turn_scuff": | |
| command[2] = 4.9 | |
| elif spec.placement == "forward": | |
| command[0] = 0.32 | |
| elif spec.placement == "lean_forward": | |
| amount = _smoothstep(min(1.0, phase / 0.55)) * _smoothstep(min(1.0, (1.0 - phase) / 0.22)) | |
| command[11] += 0.075 * amount | |
| command[3] += 0.080 * amount | |
| command[4] -= 0.060 * amount | |
| command[9] -= 0.008 * amount | |
| elif spec.placement == "lean_back": | |
| amount = _smoothstep(min(1.0, phase / 0.55)) * _smoothstep(min(1.0, (1.0 - phase) / 0.22)) | |
| command[11] -= 0.070 * amount | |
| command[3] -= 0.060 * amount | |
| command[4] += 0.050 * amount | |
| if spec.placement == "touch_bob": | |
| snap = math.sin(math.pi * min(1.0, phase / 0.52)) ** 2 | |
| command[3] += 0.145 * snap | |
| command[4] -= 0.125 * snap | |
| command[9] -= 0.010 * snap | |
| if spec.weight_transfer and spec.placement != "turn_scuff": | |
| stance_side = 1.0 if spec.swing == "right" else -1.0 | |
| transition_scale = 0.80 if count == 0 and wall > 0 else 1.0 | |
| command[8] = 0.020 * stance_side * pulse * transition_scale | |
| command[10] += 0.045 * stance_side * pulse * transition_scale | |
| return command | |
| def joint_bias(time_seconds: float) -> np.ndarray: | |
| """Return a 14D morphology-aware additive joint target.""" | |
| wall, count, phase = count_phase(time_seconds) | |
| spec = SIMPLE_COUNTS[count] | |
| bias = np.zeros(14, dtype=np.float32) | |
| pulse = math.sin(math.pi * max(0.0, min(1.0, phase))) | |
| accent = math.sin(math.pi * _smoothstep(min(1.0, phase / 0.82))) | |
| transition_scale = 0.80 if count == 0 and wall > 0 else 1.0 | |
| p = pulse * transition_scale | |
| if spec.swing != "none" and spec.placement != "turn_scuff": | |
| lift = 0.30 if spec.placement in ("touch", "touch_bob") else 0.75 | |
| if spec.swing == "left": | |
| bias[3] += lift * pulse | |
| bias[4] += lift * 0.80 * pulse | |
| else: | |
| bias[12] -= lift * pulse | |
| bias[13] -= lift * 0.80 * pulse | |
| if spec.placement == "side_right": | |
| bias[10] -= 0.20 * p | |
| bias[9] -= 0.05 * p | |
| elif spec.placement == "follow_right": | |
| bias[0] -= 0.12 * p | |
| bias[1] -= 0.15 * p | |
| elif spec.placement == "side_left": | |
| bias[1] += 0.20 * p | |
| bias[0] += 0.05 * p | |
| elif spec.placement == "follow_left": | |
| bias[9] += 0.12 * p | |
| bias[10] += 0.15 * p | |
| elif spec.placement == "back": | |
| if spec.swing == "left": | |
| bias[0] -= 0.08 * p | |
| bias[2] += 0.30 * p | |
| else: | |
| bias[9] += 0.08 * p | |
| bias[11] -= 0.30 * p | |
| elif spec.placement == "forward": | |
| bias[0] += 0.05 * p | |
| bias[2] -= 0.24 * p | |
| elif spec.placement in ("touch", "touch_bob"): | |
| if spec.swing == "left": | |
| bias[2] += 0.10 * accent | |
| bias[3] += 0.10 * accent | |
| bias[4] += 0.08 * accent | |
| else: | |
| bias[11] -= 0.10 * accent | |
| bias[12] -= 0.10 * accent | |
| bias[13] -= 0.08 * accent | |
| elif spec.placement == "lean_forward": | |
| bias[2] -= 0.055 * pulse | |
| bias[11] += 0.055 * pulse | |
| bias[4] += 0.030 * pulse | |
| bias[13] -= 0.030 * pulse | |
| elif spec.placement == "lean_back": | |
| bias[2] += 0.050 * pulse | |
| bias[11] -= 0.050 * pulse | |
| bias[4] -= 0.025 * pulse | |
| bias[13] += 0.025 * pulse | |
| elif spec.placement == "turn_scuff": | |
| bias[12] -= 0.04 * pulse | |
| bias[13] -= 0.03 * pulse | |
| bias[7] += 0.22 * pulse | |
| return bias | |
| __all__ = [ | |
| "COUNTS_PER_WALL", "SHOW_BEATS", "SHOW_SECONDS", "SIMPLE_COUNTS", | |
| "SimpleCount", "command_vector", "count_phase", "joint_bias", "policy_for_time", | |
| ] | |