Fixes gait into bezier

This commit is contained in:
Rune Harlyk
2025-09-04 16:34:31 +02:00
committed by Rune Harlyk
parent 10b78e6919
commit 0b5d7b1534
2 changed files with 106 additions and 81 deletions
+9 -26
View File
@@ -149,15 +149,15 @@ export class BezierState extends GaitState {
this.mode = mode this.mode = mode
if (mode === 'crawl') { if (mode === 'crawl') {
this.speed_factor = 0.1 this.speed_factor = 0.5
this.stand_offset = duty ?? 0.85 this.stand_offset = duty ?? 0.85
const o = order ?? [0, 3, 1, 2] const o = order ?? [3, 0, 2, 1]
const base = [0, 0.25, 0.5, 0.75] const base = [0, 0.25, 0.5, 0.75]
const offsets = new Array(4).fill(0) const offsets = new Array(4).fill(0)
for (let i = 0; i < 4; i++) offsets[o[i]] = base[i] for (let i = 0; i < 4; i++) offsets[o[i]] = base[i]
this.offset = offsets this.offset = offsets
} else { } else {
this.speed_factor = 1 this.speed_factor = 2
this.stand_offset = duty ?? 0.6 this.stand_offset = duty ?? 0.6
this.offset = order ? (order.map(v => v % 1) as number[]) : [0, 0.5, 0.5, 0] this.offset = order ? (order.map(v => v % 1) as number[]) : [0, 0.5, 0.5, 0]
} }
@@ -179,7 +179,10 @@ export class BezierState extends GaitState {
update_phase() { update_phase() {
const m = this.gait_state const m = this.gait_state
if (m.step_x === 0 && m.step_z === 0 && m.step_angle === 0) return if (m.step_x === 0 && m.step_z === 0 && m.step_angle === 0) {
this.phase = 0
return
}
this.phase += this.dt * m.step_velocity * this.speed_factor this.phase += this.dt * m.step_velocity * this.speed_factor
if (this.phase >= 1) { if (this.phase >= 1) {
this.phase_num = (this.phase_num + 1) % 2 this.phase_num = (this.phase_num + 1) % 2
@@ -187,19 +190,16 @@ export class BezierState extends GaitState {
} }
} }
protected phase_lead = 0.08
protected feather = 0.05
update_body_position() { update_body_position() {
const m = this.gait_state const m = this.gait_state
const moving = m.step_x !== 0 || m.step_z !== 0 || m.step_angle !== 0 const moving = m.step_x !== 0 || m.step_z !== 0 || m.step_angle !== 0
if (!moving) return if (!moving) return
if (this.mode !== 'crawl') return
const { stance, swing, next_swing, time_to_lift } = this.get_leg_states() const { stance, swing, next_swing, time_to_lift } = this.get_leg_states()
// Only shift when all legs are down and we know which leg lifts next
if (stance.length >= 3 && swing.length === 0 && next_swing !== -1) { if (stance.length >= 3 && swing.length === 0 && next_swing !== -1) {
// Check if we're starting a new shift
if (this.current_shift_leg !== next_swing) { if (this.current_shift_leg !== next_swing) {
this.current_shift_leg = next_swing this.current_shift_leg = next_swing
this.shift_start_pos.x = this.body_state.xm this.shift_start_pos.x = this.body_state.xm
@@ -213,12 +213,10 @@ export class BezierState extends GaitState {
this.shift_start_time = time_to_lift this.shift_start_time = time_to_lift
} }
// Calculate smooth progress using smoothstep
const total_time = this.shift_start_time const total_time = this.shift_start_time
const progress = total_time > 0 ? 1 - time_to_lift / total_time : 1 const progress = total_time > 0 ? 1 - time_to_lift / total_time : 1
const smooth_progress = this.smoothstep01(Math.max(0, Math.min(1, progress))) const smooth_progress = this.smoothstep01(Math.max(0, Math.min(1, progress)))
// Smoothly interpolate to target
this.body_state.xm = this.lerp( this.body_state.xm = this.lerp(
this.shift_start_pos.x, this.shift_start_pos.x,
this.shift_target_pos.x, this.shift_target_pos.x,
@@ -278,21 +276,6 @@ export class BezierState extends GaitState {
return { stance, swing, next_swing, time_to_lift: min_time_to_swing } return { stance, swing, next_swing, time_to_lift: min_time_to_swing }
} }
protected stance_weight(i: number): number {
const s = this.stand_offset
const e = this.feather
let p = this.phase + this.offset[i] + this.phase_lead
p -= Math.floor(p)
if (p < s - e) return 1
if (p > s + e && p < 1 - e) return 0
if (p <= s + e) {
const t = (p - (s - e)) / (2 * e)
return 1 - this.smoothstep01(t)
}
const q = p >= 1 - e ? (p - (1 - e)) / e : (e - p) / e
return this.smoothstep01(q)
}
protected smoothstep01(t: number): number { protected smoothstep01(t: number): number {
const x = Math.max(0, Math.min(1, t)) const x = Math.max(0, Math.min(1, t))
return x * x * (3 - 2 * x) return x * x * (3 - 2 * x)
+97 -55
View File
@@ -14,40 +14,25 @@ class WalkState : public GaitState {
float phase_offset[4] = {0.f, 0.5f, 0.5f, 0.f}; float phase_offset[4] = {0.f, 0.5f, 0.5f, 0.f};
float stand_offset = 0.6f; float stand_offset = 0.6f;
float step_length = 0.0f; float step_length = 0.0f;
float phase_lead = 0.08f;
float feather = 0.05f;
float speed_factor = 1; float speed_factor = 1;
float com_shift_gain = 0.35f;
float com_shift_limit = 0.06f;
float com_tau = 0.12f;
alignas(16) float crawl_target_xz[4][2] = {{0}}; struct ShiftState {
int crawl_order[4] = {0, 1, 2, 3}; float start_x = 0.0f;
float start_z = 0.0f;
float target_x = 0.0f;
float target_z = 0.0f;
float start_time = 0.0f;
int current_shift_leg = -1;
} shift_state;
static float d2(float ax, float az, float bx, float bz) { struct LegStates {
float dx = ax - bx, dz = az - bz; std::array<int, 4> stance;
return std::sqrt(dx * dx + dz * dz); std::array<int, 4> swing;
} int stance_count = 0;
int swing_count = 0;
void buildCrawlTargetsIncenter() { int next_swing = -1;
for (int s = 0; s < 4; ++s) { float time_to_lift = INFINITY;
int a = (s + 1) & 3, b = (s + 2) & 3, c = (s + 3) & 3; };
float Ax = default_feet_pos[a][0], Az = default_feet_pos[a][2];
float Bx = default_feet_pos[b][0], Bz = default_feet_pos[b][2];
float Cx = default_feet_pos[c][0], Cz = default_feet_pos[c][2];
float la = d2(Bx, Bz, Cx, Cz);
float lb = d2(Ax, Az, Cx, Cz);
float lc = d2(Ax, Az, Bx, Bz);
float L = la + lb + lc;
if (L <= 1e-6f) {
crawl_target_xz[s][0] = (Ax + Bx + Cx) / 3.f;
crawl_target_xz[s][1] = (Az + Bz + Cz) / 3.f;
} else {
crawl_target_xz[s][0] = (la * Ax + lb * Bx + lc * Cx) / L;
crawl_target_xz[s][1] = (la * Az + lb * Bz + lc * Cz) / L;
}
}
}
static constexpr uint8_t BEZIER_POINTS = 12; static constexpr uint8_t BEZIER_POINTS = 12;
static constexpr std::array<float, BEZIER_POINTS> COMBINATORIAL_VALUES = { static constexpr std::array<float, BEZIER_POINTS> COMBINATORIAL_VALUES = {
@@ -72,12 +57,12 @@ class WalkState : public GaitState {
0.9f, 1.1f, 1.1f, 1.1f, 0.0f, 0.0f}; 0.9f, 1.1f, 1.1f, 1.1f, 0.0f, 0.0f};
public: public:
WalkState() { buildCrawlTargetsIncenter(); } WalkState() = default;
const char *name() const override { return "Bezier"; } const char *name() const override { return "Bezier"; }
void set_mode_crawl(float duty = 0.85f, std::array<int, 4> order = {0, 1, 2, 3}) { void set_mode_crawl(float duty = 0.85f, std::array<int, 4> order = {3, 0, 2, 1}) {
mode = WALK_GAIT::CRAWL; mode = WALK_GAIT::CRAWL;
speed_factor = 0.1; speed_factor = 0.5;
stand_offset = duty; stand_offset = duty;
const float base[4] = {0.f, 0.25f, 0.5f, 0.75f}; const float base[4] = {0.f, 0.25f, 0.5f, 0.75f};
for (int i = 0; i < 4; ++i) phase_offset[order[i]] = base[i]; for (int i = 0; i < 4; ++i) phase_offset[order[i]] = base[i];
@@ -85,7 +70,7 @@ class WalkState : public GaitState {
void set_mode_trot(float duty = 0.6f, std::array<float, 4> offsets = {0.f, 0.5f, 0.5f, 0.f}) { void set_mode_trot(float duty = 0.6f, std::array<float, 4> offsets = {0.f, 0.5f, 0.5f, 0.f}) {
mode = WALK_GAIT::TROT; mode = WALK_GAIT::TROT;
speed_factor = 1; speed_factor = 2;
stand_offset = duty; stand_offset = duty;
for (int i = 0; i < 4; ++i) phase_offset[i] = std::fmod(std::fabs(offsets[i]), 1.f); for (int i = 0; i < 4; ++i) phase_offset[i] = std::fmod(std::fabs(offsets[i]), 1.f);
} }
@@ -101,33 +86,90 @@ class WalkState : public GaitState {
protected: protected:
void updatePhase(float dt) { void updatePhase(float dt) {
if (gait_state.step_x == 0 && gait_state.step_z == 0 && gait_state.step_angle == 0) {
phase_time = 0;
return;
}
phase_time = std::fmod(phase_time + dt * gait_state.step_velocity * speed_factor, 1.0f); phase_time = std::fmod(phase_time + dt * gait_state.step_velocity * speed_factor, 1.0f);
} }
LegStates getLegStates() {
LegStates states;
float min_time_to_swing = INFINITY;
for (int i = 0; i < 4; i++) {
float phase = std::fmod(phase_time + phase_offset[i], 1.0f);
if (phase <= stand_offset) {
states.stance[states.stance_count++] = i;
float time_to_swing = stand_offset - phase;
if (time_to_swing < min_time_to_swing) {
min_time_to_swing = time_to_swing;
states.next_swing = i;
}
} else {
states.swing[states.swing_count++] = i;
}
}
states.time_to_lift = min_time_to_swing;
return states;
}
std::array<float, 3> stanceCentroid(const LegStates &states) {
if (states.stance_count == 0) {
return {0.0f, 0.0f, 0.0f};
}
float sx = 0.0f, sz = 0.0f;
int remaining_count = 0;
for (int i = 0; i < states.stance_count; i++) {
int leg = states.stance[i];
if (leg != states.next_swing) {
sx += default_feet_pos[leg][0];
sz += default_feet_pos[leg][2];
remaining_count++;
}
}
if (remaining_count > 0) {
return {sx / remaining_count, 0.0f, sz / remaining_count};
}
return {0.0f, 0.0f, 0.0f};
}
static float lerp(float a, float b, float t) { return a + (b - a) * t; }
void updateBodyPosition(body_state_t &body_state, float dt) { void updateBodyPosition(body_state_t &body_state, float dt) {
if (mode != WALK_GAIT::CRAWL) return; if (mode != WALK_GAIT::CRAWL) return;
const bool moving = gait_state.step_x != 0.f || gait_state.step_z != 0.f || gait_state.step_angle != 0.f; const bool moving = gait_state.step_x != 0.f || gait_state.step_z != 0.f || gait_state.step_angle != 0.f;
const float a = dt / (com_tau + dt); if (!moving) return;
if (!moving) {
body_state.xm += (0.f - body_state.xm) * a; LegStates leg_states = getLegStates();
body_state.zm += (0.f - body_state.zm) * a;
return; if (leg_states.stance_count >= 3 && leg_states.swing_count == 0 && leg_states.next_swing != -1) {
if (shift_state.current_shift_leg != leg_states.next_swing) {
shift_state.current_shift_leg = leg_states.next_swing;
shift_state.start_x = body_state.xm;
shift_state.start_z = body_state.zm;
auto target = stanceCentroid(leg_states);
shift_state.target_x = target[0];
shift_state.target_z = target[2];
shift_state.start_time = leg_states.time_to_lift;
}
float total_time = shift_state.start_time;
float progress = total_time > 0 ? 1.0f - (leg_states.time_to_lift / total_time) : 1.0f;
float smooth_progress = smoothstep01(std::clamp(progress, 0.0f, 1.0f));
body_state.xm = lerp(shift_state.start_x, shift_state.target_x, smooth_progress);
body_state.zm = lerp(shift_state.start_z, shift_state.target_z, smooth_progress);
} }
int k = (int)std::floor(std::fmod(phase_time, 1.f) * 8.f) & 7;
int leg = crawl_order[k >> 1];
float tx = crawl_target_xz[leg][0] * com_shift_gain;
float tz = crawl_target_xz[leg][1] * com_shift_gain;
float m = std::hypot(tx, tz);
if (m > com_shift_limit) {
float s = com_shift_limit / m;
tx *= s;
tz *= s;
}
body_state.xm += (tx - body_state.xm) * a;
body_state.zm += (tz - body_state.zm) * a;
} }
static float smoothstep01(float t) { static float smoothstep01(float t) {