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
File size: 6,640 Bytes
f510e5f 39b3d48 f510e5f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | """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
@dataclass(frozen=True)
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",
]
|