文章目录
-
- [**完整可运行的 Python 示例**,](#完整可运行的 Python 示例,)
- 一、运动学(简洁明确)
-
- [1. 机械模型(简化)](#1. 机械模型(简化))
- [2. 正运动学(给角度求足端位置)](#2. 正运动学(给角度求足端位置))
- [3. 逆运动学(给目标前后位移求角度)](#3. 逆运动学(给目标前后位移求角度))
- [4. 从"足端轨迹"到"推进力"](#4. 从“足端轨迹”到“推进力”)
- 二、步态(Gait)设计
- [三、代码(完整 --- 使用 gpiozero.AngularServo)](#三、代码(完整 — 使用 gpiozero.AngularServo))
- 四、代码要点说明与调参建议
- 五、进阶改进建议(如果你想进一步开发)
-
- [🧩 一、8舵机四足机器人结构](#🧩 一、8舵机四足机器人结构)
- [🦿 二、简化运动学模型](#🦿 二、简化运动学模型)
-
- [1️⃣ 正运动学(Forward Kinematics)](#1️⃣ 正运动学(Forward Kinematics))
- [2️⃣ 逆运动学(Inverse Kinematics)](#2️⃣ 逆运动学(Inverse Kinematics))
- [🧠 三、步态设计(对角步 Trot Gait)](#🧠 三、步态设计(对角步 Trot Gait))
- [⚙️ 四、完整 Python 示例(8个舵机 + 运动学)](#⚙️ 四、完整 Python 示例(8个舵机 + 运动学))
- [🧾 五、代码讲解(运动学 & 步态)](#🧾 五、代码讲解(运动学 & 步态))
- [🧮 六、简单数值例子(逆运动学验证)](#🧮 六、简单数值例子(逆运动学验证))
- [⚡ 七、调试与建议](#⚡ 七、调试与建议)
- [🧭 八、总结(与4舵机版对比)](#🧭 八、总结(与4舵机版对比))
- [**CPG(中央模式发生器)控制框架**,用于那台 **8 舵机 / 2 DOF 每腿** 的四足机器人。](#CPG(中央模式发生器)控制框架,用于那台 8 舵机 / 2 DOF 每腿 的四足机器人。)
- 1) CPG 简介与数学(直接可用) CPG 简介与数学(直接可用))
- 2) 完整 Python 代码(含模拟和硬件两种模式) 完整 Python 代码(含模拟和硬件两种模式))
- 3) 如何通过 CPG 参数调节不同步态与运动特性 如何通过 CPG 参数调节不同步态与运动特性)
- 4) 调试步骤(推荐顺序) 调试步骤(推荐顺序))
-
- [**8舵机步行机器人基础运动学系统**,接下来加上"**原地转向 / 后退 / 侧移**"是非常自然的一步。](#8舵机步行机器人基础运动学系统,接下来加上“原地转向 / 后退 / 侧移”是非常自然的一步。)
- [🧠 一、运动学思想:通过足端轨迹叠加生成机体运动](#🧠 一、运动学思想:通过足端轨迹叠加生成机体运动)
- [⚙️ 二、核心步态生成器 (Gait Generator)](#⚙️ 二、核心步态生成器 (Gait Generator))
- [🧩 三、不同动作的相位/方向配置](#🧩 三、不同动作的相位/方向配置)
- [🧮 四、完整 Python 控制框架](#🧮 四、完整 Python 控制框架)
- [📘 五、运动学解释(以原地转向为例)](#📘 五、运动学解释(以原地转向为例))
- [🧭 六、动作总结](#🧭 六、动作总结)
完整可运行的 Python 示例,
它用 4 个舵机 (每条腿 1 个舵机)来实现一个非常基础的四足机器人"行走"动作,并且把相关的运动学(正/逆 kinematics)、
重要提醒:只有 1 个舵机/腿(即每条腿只有一个转动自由度)时,机器人只能做非常受限的步态 ------ 主要是前后摆动(在一个平面内推拉地面)而不能做真正的抬腿/降落和复杂足端定位。下面的实现是基于这种受限硬件的"实用折衷" ------ 通过协调前后摆动(trot/two-beat gait)产生向前推进力。
一、运动学(简洁明确)
1. 机械模型(简化)
-
每条腿由一个舵机在"髋关节"处控制,舵机驱动在**矢状面(前-后)**旋转(类似人在前后摆腿),角度记为 θ \theta θ(以垂直向下/中位为 0° 为参考)。
-
每条腿等效为刚性连杆,长度为 L L L。足端(脚)相对于髋关节的位置:
x = L sin ( θ ) ( 前为正 ) x = L \sin(\theta) \quad (\text{前为正}) x=Lsin(θ)(前为正)
z = L cos ( θ ) ( 垂直方向,正向向下 ) z = L \cos(\theta) \quad (\text{垂直方向,正向向下}) z=Lcos(θ)(垂直方向,正向向下)
(注意这里只用到平面 x x x- z z z,横向 y y y 忽略,因为舵机不控制横向)
2. 正运动学(给角度求足端位置)
- 给定舵机角度 θ \theta θ,脚在机器人机身坐标系(前为正)上的前后坐标就是 x = L sin θ x = L \sin\theta x=Lsinθ。
- 舵机角度越向前,脚端 x x x 越前;角度向后,脚端 x x x 越后。
3. 逆运动学(给目标前后位移求角度)
-
如果你把目标足端前后坐标设为 x d x_{d} xd(且 ∣ x d ∣ ≤ L |x_d| \le L ∣xd∣≤L),对应角度
θ = arcsin ( x d L ) \theta = \arcsin\left(\frac{x_d}{L}\right) θ=arcsin(Lxd)
-
逆解需要保证 ∣ x d ∣ ≤ L |x_d| \le L ∣xd∣≤L;并且舵机的物理转角范围也受限(比如 [ − 4 5 ∘ , + 4 5 ∘ ] [-45^\circ, +45^\circ] [−45∘,+45∘]),因此可走的步幅受限。
4. 从"足端轨迹"到"推进力"
-
在真实机器人中,推进力来自"支撑腿"与地面之间的摩擦产生的推力。我们用一种近似方法:
- 当脚放到后方 并将舵机转向前推(从后摆到前摆)时,与地面的摩擦产生向前推动力 -> 机器人向前移动;
- 当脚处于前方回收时,尽量减少与地面的接触力(真实机器人会抬脚);但我们此处只能将脚快速移到前方并尽量减少推力(通过快速回摆或把角度收回到中位),从而实现"步进"效果。
二、步态(Gait)设计
-
使用经典的 对角步(trot):同时移动左前(LF) + 右后(RH) 为一组,右前(RF) + 左后(LH) 为另一组。交替进行。
-
每一步分两相:
- 支撑相(push):支撑腿从后摆到前摆以产生推力(慢速、有力)
- 回收相(return):另一对腿快速从前摆回到后方,为下一次支撑做准备(快速、轻触)
-
由于没有抬腿,我们用"快速回摆"来尽量减小回收相时对地面的推力。
三、代码(完整 --- 使用 gpiozero.AngularServo)
下面代码实现:
Quadruped
类抽象化四条腿(LF, RF, LH, RH)- 可配置舵机引脚、连杆长度 L、舵机角度范围与步幅(前后位移)
step_forward()
实现单步(两相交替)walk(steps, speed)
实现多步行走- 安全释放 GPIO、外接供电提示
python
"""
四足机器人(4 个舵机)行走示例
硬件假设:
- 每条腿一个舵机(髋关节),控制前后摆动(矢状面)
- 舵机最好是 AngularServo(以角度控制),若没有可用普通 Servo,但需要自己 map
- 请确保舵机电源能提供足够电流(外接 5V 电源并共地)
连接示例(BCM 编号):
LF (左前) -> GPIO 17
RF (右前) -> GPIO 27
LH (左后) -> GPIO 22
RH (右后) -> GPIO 23
"""
from gpiozero import AngularServo
from time import sleep
import math
# ========== 参数(按需修改) ==========
# 舵机引脚映射(BCM)
PINS = {
"LF": 17,
"RF": 27,
"LH": 22,
"RH": 23,
}
# 每条腿连杆长度(米)------ 只是用于运动学计算展示,实际角度由舵机控制决定
L = 0.08 # 8 cm,按你的机械臂长度调整
# 舵机角度范围(物理限制) --- 以中位 0 度为参考
MIN_ANGLE = -45 # 向后最大
MAX_ANGLE = 45 # 向前最大
# 步幅参数(足端前后位移, 单位:米)- 必须满足 |x| <= L
STEP_FORWARD = 0.03 # 支撑相推力时,足端前方位置(m)
STEP_BACK = -0.03 # 回收准备时,足端后方位置(m)
# 时间参数(秒)
PUSH_TIME = 0.35 # 支撑相(慢,产生推力)
RETURN_TIME = 0.15 # 回收相(快,尽量减少与地面接触时间)
PAUSE_BETWEEN_STEPS = 0.05
# 舵机脉宽(根据实际舵机可调整)
MIN_PULSE = 0.5/1000
MAX_PULSE = 2.5/1000
# =======================================
def x_to_angle_deg(x, L):
"""
逆运动学:给定足端前后位移 x(相对于髋关节,前为正),计算舵机角度(deg)
θ = arcsin(x / L)
注意:如果 |x| > L 则超出可达范围,会被夹到 ±90°,但物理舵机有更小角度限制。
"""
if abs(x) > L:
# 限制在可达范围
x = max(min(x, L), -L)
theta_rad = math.asin(x / L) # 返回弧度
theta_deg = math.degrees(theta_rad)
return theta_deg
class Quadruped:
def __init__(self, pins, L=0.08,
min_angle=MIN_ANGLE, max_angle=MAX_ANGLE,
min_pulse=MIN_PULSE, max_pulse=MAX_PULSE):
self.L = L
self.min_angle = min_angle
self.max_angle = max_angle
# 创建 AngularServo(角度接口)
self.servos = {}
for leg, pin in pins.items():
# AngularServo(angle_range=(min_angle, max_angle)) 也可用,但 gpiozero 的 AngularServo
# 的默认 angle_range 是 (-90, 90). 我们在设定 angle 时再进行夹位。
self.servos[leg] = AngularServo(pin,
min_angle=min_angle,
max_angle=max_angle,
min_pulse_width=min_pulse,
max_pulse_width=max_pulse)
# 初始都回中位
self.set_all_angles(0)
sleep(0.5)
def set_angle(self, leg, angle_deg):
"""设置指定腿的角度(度),会自动裁剪到 min/max"""
a = max(min(angle_deg, self.max_angle), self.min_angle)
self.servos[leg].angle = a
def set_all_angles(self, angle_deg):
for leg in self.servos:
self.set_angle(leg, angle_deg)
def set_foot_x(self, leg, x):
"""给定足端 x(m),通过逆运动学设置舵机角度"""
ang = x_to_angle_deg(x, self.L)
self.set_angle(leg, ang)
def step_forward(self):
"""
执行一步(trot 对角步):
- 相位 A: 支撑组 (LF, RH) 推(慢从后摆到前)
回收组 (RF, LH) 快速回摆到后方准备
- 相位 B: 支撑组 swap (RF, LH) 推,另两条回收
"""
# 相位 A
supporting = ("LF", "RH")
recovering = ("RF", "LH")
# 恢复组调到后方预备(快速回收)
for leg in recovering:
self.set_foot_x(leg, STEP_BACK)
# 支撑组移动:从后方向前,分帧平滑插值
frames = 6
for i in range(frames + 1):
t = i / frames
# 支撑组角度插值:从 STEP_BACK -> STEP_FORWARD
for leg in supporting:
x = STEP_BACK + (STEP_FORWARD - STEP_BACK) * t
self.set_foot_x(leg, x)
# 恢复组维持 STEP_BACK(或者微调整为中位以减少接触)
sleep(PUSH_TIME / frames)
sleep(PAUSE_BETWEEN_STEPS)
# 切换:相位 B
supporting = ("RF", "LH")
recovering = ("LF", "RH")
for leg in recovering:
# 对刚完成推力的腿,快速回到后方(回收)
self.set_foot_x(leg, STEP_BACK)
frames = 6
for i in range(frames + 1):
t = i / frames
for leg in supporting:
x = STEP_BACK + (STEP_FORWARD - STEP_BACK) * t
self.set_foot_x(leg, x)
sleep(PUSH_TIME / frames)
# 一步结束
sleep(PAUSE_BETWEEN_STEPS)
def walk(self, steps=4, speed=1.0):
"""
连续行走 steps 步。
speed 值会影响整体节奏:speed>1 更快,<1 更慢
"""
# 将时间参数按 speed 缩放
global PUSH_TIME, RETURN_TIME
base_push = PUSH_TIME
base_return = RETURN_TIME
push = base_push / speed
ret = base_return / speed
# 简单实现:循环调用 step_forward
for i in range(steps):
print(f"Step {i+1}/{steps}")
# 直接调用 step_forward(),该函数内部使用 PUSHTIME 全局
self.step_forward()
# 结束后置中
self.set_all_angles(0)
def turn_in_place(self, direction="left"):
"""
一个非常简单的原地转(利用左右两侧生成不均衡推力)
direction: "left" 或 "right"
"""
if direction == "left":
# 让左侧腿倾向推动更强(例如更大的 STEP_FORWARD),右侧回收
self.set_foot_x("LF", STEP_FORWARD)
self.set_foot_x("LH", STEP_FORWARD)
self.set_foot_x("RF", STEP_BACK)
self.set_foot_x("RH", STEP_BACK)
else:
self.set_foot_x("RF", STEP_FORWARD)
self.set_foot_x("RH", STEP_FORWARD)
self.set_foot_x("LF", STEP_BACK)
self.set_foot_x("LH", STEP_BACK)
sleep(0.4)
# 回到中位
self.set_all_angles(0)
def close(self):
for s in self.servos.values():
try:
s.close()
except Exception:
pass
# ========== 使用示例 ==========
if __name__ == "__main__":
robot = Quadruped(PINS, L=L)
try:
print("初始化完毕 -> 中位")
sleep(1)
print("单步示范:走 4 步")
# 走 4 步
for _ in range(4):
robot.step_forward()
sleep(0.1)
print("演示:原地向左转")
robot.turn_in_place("left")
print("返回中位并退出")
except KeyboardInterrupt:
print("用户中断")
finally:
robot.close()
print("清理完成,GPIO 释放。")
四、代码要点说明与调参建议
-
舵机与角度映射
我使用
AngularServo.angle = deg
来直接设置角度,内部由gpiozero
负责把角度映射到脉宽。若你的舵机或板子对角度映射不准确,请调整min_pulse_width
/max_pulse_width
,或微调MIN_ANGLE/MAX_ANGLE
。 -
步幅 STEP_FORWARD / STEP_BACK
- 这些值是"足端相对于髋关节的前后位置(米)",必须满足 ∣ x ∣ ≤ L |x| \le L ∣x∣≤L(不可超出连杆长度)。
- 若步幅太大舵机会饱和(到达角度极限)或产生过大扭矩。开始时建议小于连杆长度的 0.3~0.4 倍测试。
-
PUSH_TIME / RETURN_TIME
- 支撑相(推)应更缓慢,产生较大接触时间,从而获得推进力。
- 回收相尽量快速,减少阻碍前进。
- 根据舵机速度和机械摩擦调节时间以获得稳定推进。
-
牵引与外部电源
- 舵机瞬态电流大,建议使用外接稳定 5V 电源,且确保与树莓派 GND 共地。
- 若用树莓派 5V 供电,注意电流限制与 USB 供电能力。
-
为什么不能像真正四足那样走得稳?
- 真正的四足机器人通常每条腿至少 2~3 个自由度(髋+膝或更多),可以实现抬腿、避障与稳定足端着地。
- 仅 1 DOF/腿无法抬脚,因此回收相仍会与地面接触,行走效率和稳定性受限。这个示例更像"摇摆推进"的简化模型,适合作为硬件受限下的学习原型。
五、进阶改进建议(如果你想进一步开发)
- 把每条腿扩展到 2 DOF(髋 + 膝)或 3 DOF ,实现真实抬腿/着地轨迹,使用完整的逆运动学(IK)规划空间轨迹(例如
x,y,z -> joint angles
)。 - 用
pid
或imu
做姿态控制,保持机身平衡。 - 使用
pigpio
后端提高 PWM 精度与同步性。 - 实现更高级步态(walk, trot, pace, bound)并通过中心模态控制(Central Pattern Generator, CPG)来产生稳定步态。
8个舵机的四足机器人 ------也就是每条腿2个舵机(2自由度 / 2 DOF per leg)的结构。
这就可以实现真正的「抬腿 + 向前摆动」动作,成为一个具备完整步态运动学的基础四足机器人模型。
🧩 一、8舵机四足机器人结构
每条腿有 2 个舵机:
关节 | 功能 | 控制方向 | 举例角度 |
---|---|---|---|
髋关节(Hip) | 前后摆动(推进) | 水平面 (x方向) | -45° ~ +45° |
膝关节(Knee) | 抬腿/放腿(上下) | 垂直面 (z方向) | 0°(伸直)~ 90°(弯曲) |
这样整机就有:
- 4 个髋关节舵机(LF_hip, RF_hip, LH_hip, RH_hip)
- 4 个膝关节舵机(LF_knee, RF_knee, LH_knee, RH_knee)
🦿 二、简化运动学模型
我们把每条腿看作一个二连杆机构(2-Link Manipulator):
(hip)
o------(L1)------o------(L2)------o (foot)
θ1 θ2
- L1:大腿长度
- L2:小腿长度
- θ1:髋关节角度(相对于机身垂直向下为 0°)
- θ2:膝关节角度(相对于大腿)
1️⃣ 正运动学(Forward Kinematics)
给定角度 θ1, θ2,足端坐标为:
x = L 1 sin ( θ 1 ) + L 2 sin ( θ 1 + θ 2 ) x = L_1 \sin(\theta_1) + L_2 \sin(\theta_1 + \theta_2) x=L1sin(θ1)+L2sin(θ1+θ2)
z = − L 1 cos ( θ 1 ) − L 2 cos ( θ 1 + θ 2 ) z = -L_1 \cos(\theta_1) - L_2 \cos(\theta_1 + \theta_2) z=−L1cos(θ1)−L2cos(θ1+θ2)
其中 z 向下为正。
2️⃣ 逆运动学(Inverse Kinematics)
给定目标足端 (x, z),求 θ1, θ2:
cos ( θ 2 ) = x 2 + z 2 − L 1 2 − L 2 2 2 L 1 L 2 \cos(\theta_2) = \frac{x^2 + z^2 - L_1^2 - L_2^2}{2 L_1 L_2} cos(θ2)=2L1L2x2+z2−L12−L22
θ 2 = arccos ( 上式 ) \theta_2 = \arccos(\text{上式}) θ2=arccos(上式)
θ 1 = arctan 2 ( x , − z ) − arctan 2 ( L 2 sin ( θ 2 ) , L 1 + L 2 cos ( θ 2 ) ) \theta_1 = \arctan2(x, -z) - \arctan2(L_2 \sin(\theta_2), L_1 + L_2 \cos(\theta_2)) θ1=arctan2(x,−z)−arctan2(L2sin(θ2),L1+L2cos(θ2))
🧠 三、步态设计(对角步 Trot Gait)
使用对角步(Trot):
- 同时移动左前 + 右后(LF, RH)
- 同时移动右前 + 左后(RF, LH)
每条腿的足端轨迹:
- 抬起 → 向前摆动 → 放下 → 向后推地
典型的足端轨迹可以在平面上表示为一个循环椭圆:
x ( t ) = x c + A sin ( ω t ) x(t) = x_c + A \sin(\omega t) x(t)=xc+Asin(ωt)
z ( t ) = z c + B cos ( ω t ) z(t) = z_c + B \cos(\omega t) z(t)=zc+Bcos(ωt)
其中:
- A A A:步幅(前后)
- B B B:抬脚高度
- z c z_c zc:平衡高度
- ω = 2 π / T \omega = 2\pi/T ω=2π/T:步频角速度
⚙️ 四、完整 Python 示例(8个舵机 + 运动学)
🚨 注意:运行前请确保外接5V电源供舵机,电流足够。
以下代码假设你用
gpiozero.AngularServo
控制每个舵机。
python
"""
8舵机四足机器人行走示例(2自由度/腿)
By 崔乘玮 & Sebastian
"""
from gpiozero import AngularServo
from time import sleep
import math
# ================== 参数 ==================
# 每条腿两个舵机:hip(髋关节),knee(膝关节)
# BCM 引脚分配:
PINS = {
"LF_hip": 17, "LF_knee": 18,
"RF_hip": 27, "RF_knee": 22,
"LH_hip": 23, "LH_knee": 24,
"RH_hip": 25, "RH_knee": 5,
}
# 连杆长度 (m)
L1 = 0.05 # 大腿
L2 = 0.05 # 小腿
# 舵机角度范围
MIN_ANGLE = -90
MAX_ANGLE = 90
# 脉宽范围(根据舵机调整)
MIN_PULSE = 0.5/1000
MAX_PULSE = 2.5/1000
# 足端平衡位置
X_CENTER = 0.0
Z_CENTER = -0.08
# 步态参数
STEP_A = 0.03 # 前后幅度
STEP_B = 0.02 # 抬腿高度
T_CYCLE = 1.0 # 每步周期 (秒)
# ==================================================
def inverse_kinematics(x, z, L1, L2):
"""二维两连杆逆运动学:由(x, z)求(θ1, θ2),返回弧度"""
d = math.sqrt(x**2 + z**2)
if d > (L1 + L2):
d = L1 + L2 # 限幅,防止超出可达
cos_t2 = (x**2 + z**2 - L1**2 - L2**2) / (2 * L1 * L2)
cos_t2 = max(-1, min(1, cos_t2))
t2 = math.acos(cos_t2)
t1 = math.atan2(x, -z) - math.atan2(L2 * math.sin(t2), L1 + L2 * math.cos(t2))
return t1, t2
class Quadruped2DOF:
def __init__(self, pins):
self.servos = {}
for name, pin in pins.items():
self.servos[name] = AngularServo(pin,
min_angle=MIN_ANGLE,
max_angle=MAX_ANGLE,
min_pulse_width=MIN_PULSE,
max_pulse_width=MAX_PULSE)
self.pose_stand()
def set_leg_angles(self, leg, theta1, theta2):
"""设置单腿角度(弧度 -> 度)"""
hip_servo = self.servos[f"{leg}_hip"]
knee_servo = self.servos[f"{leg}_knee"]
hip_servo.angle = math.degrees(theta1)
knee_servo.angle = math.degrees(theta2)
def set_leg_position(self, leg, x, z):
"""通过逆运动学设置足端位置"""
theta1, theta2 = inverse_kinematics(x, z, L1, L2)
self.set_leg_angles(leg, theta1, theta2)
def pose_stand(self):
"""站立姿态"""
for leg in ("LF", "RF", "LH", "RH"):
self.set_leg_position(leg, X_CENTER, Z_CENTER)
def walk_trot(self, steps=2):
"""执行若干步对角步态"""
dt = 0.05
t = 0
while steps > 0:
# 四条腿步态相位(对角相反)
phase = {
"LF": 0,
"RH": 0,
"RF": math.pi,
"LH": math.pi
}
for leg, phi in phase.items():
# 每个腿的轨迹
x = X_CENTER + STEP_A * math.sin(2*math.pi*t/T_CYCLE + phi)
z = Z_CENTER + STEP_B * max(0, math.cos(2*math.pi*t/T_CYCLE + phi))
self.set_leg_position(leg, x, z)
t += dt
if t > T_CYCLE:
t = 0
steps -= 1
sleep(dt)
def close(self):
for s in self.servos.values():
try:
s.close()
except:
pass
# ==================================================
if __name__ == "__main__":
bot = Quadruped2DOF(PINS)
try:
print("🐾 初始化完成,开始走动!")
bot.walk_trot(steps=4)
print("✅ 完成,回到站立姿态")
bot.pose_stand()
except KeyboardInterrupt:
pass
finally:
bot.close()
print("GPIO 已释放。")
🧾 五、代码讲解(运动学 & 步态)
段落 | 含义 |
---|---|
inverse_kinematics(x,z) |
由足端目标位置求两舵机角度,采用标准 2-Link 逆解公式 |
set_leg_position() |
将足端坐标转为舵机角度并发送 |
walk_trot() |
以对角步形式生成足端轨迹(正弦 + 余弦控制) |
phase |
用 π 的相位差让对角腿交替动作 |
z 轨迹中 max(0, cos(...)) |
确保"抬脚"时 z 增大,"支撑"时不穿地 |
🧮 六、简单数值例子(逆运动学验证)
若:
- L 1 = L 2 = 0.05 m L_1 = L_2 = 0.05\,m L1=L2=0.05m
- 足端目标 x = 0.03 , z = − 0.08 x = 0.03, z = -0.08 x=0.03,z=−0.08
则:
cos ( θ 2 ) = 0.0 3 2 + 0.0 8 2 − 0.0 5 2 − 0.0 5 2 2 × 0.05 × 0.05 = 0.28 ⇒ θ 2 ≈ 73.7 ° \cos(\theta_2) = \frac{0.03^2 + 0.08^2 - 0.05^2 - 0.05^2}{2×0.05×0.05} = 0.28 \Rightarrow \theta_2 ≈ 73.7° cos(θ2)=2×0.05×0.050.032+0.082−0.052−0.052=0.28⇒θ2≈73.7°
θ 1 = arctan 2 ( 0.03 , 0.08 ) − arctan 2 ( 0.05 sin 73.7 ° , 0.05 + 0.05 cos 73.7 ° ) ≈ 18.4 ° − 24.7 ° = − 6.3 ° \theta_1 = \arctan2(0.03, 0.08) - \arctan2(0.05\sin73.7°, 0.05+0.05\cos73.7°) ≈ 18.4° - 24.7° = -6.3° θ1=arctan2(0.03,0.08)−arctan2(0.05sin73.7°,0.05+0.05cos73.7°)≈18.4°−24.7°=−6.3°
也就是说,髋关节稍向后倾 (-6°),膝关节弯曲 74°,脚端正好在目标位置。
⚡ 七、调试与建议
-
先测试单腿 :
用
set_leg_position("LF", 0.0, -0.07)
单步测试每条腿运动范围是否正确。 -
调整坐标方向 :
某些舵机安装方向可能相反,如抬脚反而向下,可反转角度符号:
pythonhip_servo.angle = -math.degrees(theta1)
-
供电建议 :
八个舵机需稳定 5V ≥ 3A 电源;Raspberry Pi GPIO 仅输出信号,不可直接供电。
-
扩展步态 :
可进一步加入 相位差偏移 或 中心模式发生器(CPG) 生成更平滑的步态。
🧭 八、总结(与4舵机版对比)
项目 | 4舵机版 | 8舵机版 |
---|---|---|
自由度/腿 | 1 | 2 |
是否能抬腿 | ❌ 否 | ✅ 是 |
是否真实步态 | 模拟拖动 | 可实现步行 |
运动学复杂度 | 一元(单角度) | 二连杆逆运动学 |
机械复杂度 | 低 | 中 |
步态类型 | 摆腿推进 | 抬腿步行 |

上面的图展示了四足机器人每条腿在一个完整步态周期内的 足端轨迹 (x--z 平面):
- 横轴 (x) 表示前后方向(步幅范围约 ±0.03 m)。
- 纵轴 (z) 表示上下方向(抬脚高度约 0.02 m)。
- 蓝/橙 vs 绿/红 两组腿分别对应对角步(LF+RH 与 RF+LH 相反相位)。
可以看到:
- 每条腿的轨迹形成一个"半椭圆形循环";
- 前进相(腿抬起并向前摆动)时,z 值上升;
- 支撑相(腿接地推地)时,z 值维持在下方并向后移动;
- 对角腿的轨迹完全相反,保证了机器人在步行时始终有两个支撑点维持平衡。
CPG(中央模式发生器)控制框架 ,用于那台 8 舵机 / 2 DOF 每腿 的四足机器人。
- CPG 原理与数学(简明、能直接实现)
- 可运行的 Python 代码(含模拟模式与真实硬件模式)------把 CPG 输出映射到每条腿的足端轨迹,再用逆运动学转成舵机角度。
- 调参与调试建议(如何让机器人走得稳、如何改步态)
1) CPG 简介与数学(直接可用)
我们用 相位振荡器(phase oscillator)耦合网络 实现 CPG ------ 简单、稳定、且能通过相位差产生常见步态(walk / trot / pace / bound)。
每个腿用一个振荡器 ϕ i ( t ) \phi_i(t) ϕi(t)(相位)和幅值控制(步幅/抬脚高度可做成常数或受振幅振荡器影响)。振荡器的更新(离散时间):
ϕ ˙ i = ω i + ∑ j K i j sin ( ϕ j − ϕ i − Δ i j ) \dot\phi_i = \omega_i + \sum_{j} K_{ij}\sin(\phi_j - \phi_i - \Delta_{ij}) ϕ˙i=ωi+j∑Kijsin(ϕj−ϕi−Δij)
离散步进实现为:
text
phi_i += (omega_i + sum_j K_ij * sin(phi_j - phi_i - Delta_ij)) * dt
phi_i = phi_i % (2*pi)
其中:
- ω i = 2 π f \omega_i = 2\pi f ωi=2πf(步频)
- K i j K_{ij} Kij 耦合强度(常取同一值 K)
- Δ i j \Delta_{ij} Δij 目标相位差(用于指定步态,例如对角步 trot:LF and RH 同相,RF/LH 同相,相位相差 π)
把相位映射到足端轨迹(x,z):
- x i ( t ) = x c + A sin ( ϕ i ) x_i(t) = x_c + A \sin(\phi_i) xi(t)=xc+Asin(ϕi) (前后)
- z i ( t ) = z c + B max ( 0 , cos ( ϕ i ) ) z_i(t) = z_c + B \max(0, \cos(\phi_i)) zi(t)=zc+Bmax(0,cos(ϕi)) (抬脚只在某相位段发生)
然后对 (x,z) 做逆运动学求出髋/膝角度,发送到舵机。
2) 完整 Python 代码(含模拟和硬件两种模式)
⚠️ 运行注意:真实硬件模式会使用
gpiozero.AngularServo
控制舵机,请先确认引脚、脉宽、外接电源与共地。模拟模式只画图与动画,不触及 GPIO(安全便于调参)。
把下面代码保存为 cpg_quadruped.py
,按需修改 PINS
与几何参数,然后运行。
python
"""
cpg_quadruped.py
CPG 控制 8 舵机四足机器人(2DOF/腿)
Mode: simulation (matplotlib) or hardware (gpiozero)
"""
import math
import time
import numpy as np
# === 如果要在真实机器人上跑,把 USE_HARDWARE=True ===
USE_HARDWARE = False
if USE_HARDWARE:
from gpiozero import AngularServo
# ------------------ 参数 ------------------
# BCM 引脚示例(按你实际接线调整)
PINS = {
"LF_hip": 17, "LF_knee": 18,
"RF_hip": 27, "RF_knee": 22,
"LH_hip": 23, "LH_knee": 24,
"RH_hip": 25, "RH_knee": 5,
}
# 机械几何(米)
L1 = 0.05 # 大腿
L2 = 0.05 # 小腿
X_CENTER = 0.0
Z_CENTER = -0.08
# CPG 参数(可以调)
STEP_A = 0.03 # 水平步幅(m)
STEP_B = 0.02 # 抬脚高度(m)
FREQ = 1.0 # 步频 Hz
OMEGA = 2 * math.pi * FREQ
K_COUPLING = 8.0 # 耦合强度(较大值使相位锁定更快)
DT = 0.02 # 控制循环时间步长(秒)
# 舵机角度 / 脉宽(硬件模式用)
MIN_ANGLE = -90
MAX_ANGLE = 90
MIN_PULSE = 0.5/1000
MAX_PULSE = 2.5/1000
# 腿列表顺序
LEGS = ["LF", "RF", "LH", "RH"]
# 目标相位差矩阵(根据步态选择)
# 对角步 trot: LF & RH 相位相同, RF & LH 相差 pi
PHASE_OFFSETS = {
"LF": 0.0,
"RH": 0.0,
"RF": math.pi,
"LH": math.pi
}
# -------------------------------------------
def inverse_kinematics(x, z, L1=L1, L2=L2):
"""2-link IK: 返回 (theta1_rad, theta2_rad)"""
d = math.hypot(x, z)
# 限幅
if d > (L1 + L2):
scale = (L1 + L2) / d
x *= scale
z *= scale
d = L1 + L2
cos_t2 = (x*x + z*z - L1*L1 - L2*L2) / (2 * L1 * L2)
cos_t2 = max(-1.0, min(1.0, cos_t2))
t2 = math.acos(cos_t2)
t1 = math.atan2(x, -z) - math.atan2(L2 * math.sin(t2), L1 + L2 * math.cos(t2))
return t1, t2
class CPGNetwork:
def __init__(self, legs=LEGS, omega=OMEGA, K=K_COUPLING, phase_offsets=PHASE_OFFSETS, dt=DT):
self.legs = legs
self.n = len(legs)
self.dt = dt
self.omega = omega
self.K = K
# 相位向量初始化
self.phi = {leg: phase_offsets.get(leg, 0.0) for leg in legs}
# 理想相位差 Delta_ij = phi_j* - phi_i* (we'll use pairwise desired differences from phase_offsets)
self.desired = {}
for i in legs:
for j in legs:
self.desired[(i,j)] = (phase_offsets[j] - phase_offsets[i])
def step(self):
# 相位更新(显式 Euler)
dphi = {}
for i in self.legs:
coupling_sum = 0.0
for j in self.legs:
if i == j: continue
Delta_ij = self.desired[(i,j)]
coupling_sum += math.sin(self.phi[j] - self.phi[i] - Delta_ij)
dphi[i] = (self.omega + self.K * coupling_sum) * self.dt
for i in self.legs:
self.phi[i] = (self.phi[i] + dphi[i]) % (2*math.pi)
return self.phi.copy()
class QuadrupedCPG:
def __init__(self, use_hardware=False):
self.use_hw = use_hardware
if self.use_hw:
# 创建舵机对象
self.servos = {}
for leg in LEGS:
hip_name = f"{leg}_hip"
knee_name = f"{leg}_knee"
self.servos[hip_name] = AngularServo(PINS[hip_name], min_angle=MIN_ANGLE, max_angle=MAX_ANGLE,
min_pulse_width=MIN_PULSE, max_pulse_width=MAX_PULSE)
self.servos[knee_name] = AngularServo(PINS[knee_name], min_angle=MIN_ANGLE, max_angle=MAX_ANGLE,
min_pulse_width=MIN_PULSE, max_pulse_width=MAX_PULSE)
else:
self.servos = None
self.cpg = CPGNetwork()
def phase_to_foot(self, phi, leg):
"""把单腿相位映射为足端 (x,z)"""
x = X_CENTER + STEP_A * math.sin(phi)
# 抬脚仅在 cos(phi) > 0 时发生(简单的切割)
z = Z_CENTER + STEP_B * max(0.0, math.cos(phi))
return x, z
def send_leg_angles(self, leg, theta1_rad, theta2_rad):
if self.use_hw:
hip_servo = self.servos[f"{leg}_hip"]
knee_servo = self.servos[f"{leg}_knee"]
# 角度到度,注意方向可能需要根据舵机安装翻转符号
hip_servo.angle = math.degrees(theta1_rad)
knee_servo.angle = math.degrees(theta2_rad)
else:
# 模拟模式:返回值给上层用于绘图或日志
return math.degrees(theta1_rad), math.degrees(theta2_rad)
def step_and_apply(self):
phi_dict = self.cpg.step()
angles_out = {}
for leg, phi in phi_dict.items():
x, z = self.phase_to_foot(phi, leg)
t1, t2 = inverse_kinematics(x, z)
res = self.send_leg_angles(leg, t1, t2)
angles_out[leg] = {
"phi": phi,
"foot": (x, z),
"angles_deg": res
}
return angles_out
def close(self):
if self.use_hw and self.servos:
for s in self.servos.values():
try:
s.close()
except:
pass
# --------------- 模拟运行 / 演示 ---------------
def simulate(duration=4.0):
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
robot = QuadrupedCPG(use_hardware=False)
t = 0.0
steps = int(duration / DT)
# 保存数据以可视化
data = {leg: {"x": [], "z": [], "phi": []} for leg in LEGS}
for _ in range(steps):
out = robot.step_and_apply()
for leg in LEGS:
x,z = out[leg]["foot"]
data[leg]["x"].append(x)
data[leg]["z"].append(z)
data[leg]["phi"].append(out[leg]["phi"])
t += DT
# 绘图:四条腿 x-z 轨迹(每条腿一条曲线)
fig, ax = plt.subplots()
for leg in LEGS:
ax.plot(data[leg]["x"], data[leg]["z"], label=leg)
ax.set_xlabel("x (m)")
ax.set_ylabel("z (m)")
ax.set_title("CPG 生成的足端轨迹 (x-z) over time")
ax.legend()
ax.grid(True)
plt.show()
# 可选:相位随时间变化图
fig2, ax2 = plt.subplots()
times = np.linspace(0, duration, len(data[LEGS[0]]["phi"]))
for leg in LEGS:
ax2.plot(times, np.unwrap(data[leg]["phi"]), label=leg)
ax2.set_xlabel("time (s)")
ax2.set_ylabel("phase (rad)")
ax2.set_title("CPG 相位随时间演化")
ax2.legend()
ax2.grid(True)
plt.show()
# --------------- 硬件运行主循环 ---------------
def run_on_hardware(runtime=10.0):
robot = QuadrupedCPG(use_hardware=True)
start = time.time()
try:
while time.time() - start < runtime:
out = robot.step_and_apply()
# 这里可以加日志或安全检查
time.sleep(DT)
except KeyboardInterrupt:
print("用户中断")
finally:
robot.close()
# --------------- 入口 ---------------
if __name__ == "__main__":
if USE_HARDWARE:
print("Running on hardware mode")
run_on_hardware(runtime=20.0)
else:
print("Simulation mode (no GPIO).")
simulate(duration=6.0)
代码说明亮点:
CPGNetwork
:实现相位耦合更新(简洁的 Kuramoto-like 形式但带目标相位差)。phase_to_foot()
:把相位转换为足端轨迹(x,z)。可以替换为更复杂的轨迹(如椭圆、Bezier)。QuadrupedCPG.step_and_apply()
:每个控制循环产生相位、计算 IK、并(在硬件模式)发送角度或(在模拟模式)返回用于绘图。simulate()
:在不接触硬件时可快速画出轨迹与相位曲线,便于调参。
3) 如何通过 CPG 参数调节不同步态与运动特性
-
步态类型(相位偏移)
- 对角步(trot):LF=0, RH=0, RF=π, LH=π (代码中已设置)
- 行走/walk(四步顺序):设相位为 LF=0, RF=π/2, RH=π, LH=3π/2(每条腿相差 90°)
- 同步 bound:LF=0, RF=0, LH=π, RH=π
- 只要修改
PHASE_OFFSETS
即可切换步态。
-
步频 FREQ(ω)
- 提高
FREQ
会加快步频;但舵机和机械动力学必须能跟上。小步频开始(0.5~1Hz),逐步增加。
- 提高
-
耦合强度 K
- 较高 K 会使相位锁定更快(尤其从随机初相过渡到稳定步态)。
- 太高可能导致数值刚性(需要缩小 dt)。
-
步幅 STEP_A / 抬脚 STEP_B
- 增大 STEP_A 增速度但可能超出可达空间或导致打滑。STEP_B 影响抬脚高度(太大可能舵机受力)。
- 对 2DOF 结构,保证
(x,z)
始终在可达工作空间内: x 2 + z 2 ≤ L 1 + L 2 \sqrt{x^2+z^2} \le L1+L2 x2+z2 ≤L1+L2。
-
轨迹形状
- 目前用 sin/cos + 半波整流(max(0,cos))实现抬脚;要更自然可用参数化曲线(椭圆、Bezier),或用幅值振荡器把抬脚和支撑段连续平滑分开。
-
安全措施
- 在硬件模式下,每次给舵机发送命令前做角度限位检查;并在启动阶段用低幅度慢速收敛到目标相位(warm-up)以避免突然冲击电流。
4) 调试步骤(推荐顺序)
-
模拟阶段(强烈推荐)
- 把
USE_HARDWARE=False
,运行python cpg_quadruped.py
,观察 x-z 轨迹与相位随时间变化,调整 STEP_A/STEP_B/FREQ/K 直到看起来合理。
- 把
-
单腿测试
- 改
PHASE_OFFSETS
只保留一条腿非零相位,使用QuadrupedCPG
的phase_to_foot
+inverse_kinematics
单独让某腿运行,观察舵机响应。
- 改
-
双腿/对角测试
- 先只接两条对角腿(LF + RH)上电做 trot 测试,检查是否有足够摩擦推地、不抖动。
-
整机低幅度测试
- 在硬件运行前把
STEP_A
和STEP_B
缩小到 50% 或更低,慢速 warm-up(低FREQ
),观察电流与舵机温度。
- 在硬件运行前把
-
逐步放大
- 逐步增加步幅和频率,直到达到满意的速度或出现问题(打滑/舵机扭矩不足/失步),再回退并找原因。
8舵机步行机器人基础运动学系统 ,接下来加上"原地转向 / 后退 / 侧移"是非常自然的一步。
这一步的核心思想是:
💡 通过调节四条腿的步态相位和足端轨迹中心方向,控制整体运动矢量。
🧠 一、运动学思想:通过足端轨迹叠加生成机体运动
四足机器人有三个主要机体自由度:
模式 | 控制变量 | 步态调整原理 |
---|---|---|
前进 / 后退 | vx (前向速度) |
改变足端轨迹的前后相位 |
侧移 | vy (横向速度) |
左右腿的轨迹中心对称偏移 |
原地转向 | ω (角速度) |
对角腿间的相位差加上旋转补偿 |
⚙️ 二、核心步态生成器 (Gait Generator)
让我们定义每条腿在局部坐标系下的足端轨迹:
x i ( t ) = x c + A x sin ( ω t + ϕ i ) x_i(t) = x_c + A_x \sin(\omega t + \phi_i) xi(t)=xc+Axsin(ωt+ϕi)
y i ( t ) = y c + A y cos ( ω t + ϕ i ) y_i(t) = y_c + A_y \cos(\omega t + \phi_i) yi(t)=yc+Aycos(ωt+ϕi)
z i ( t ) = z c + B ⋅ max ( 0 , cos ( ω t + ϕ i ) ) z_i(t) = z_c + B \cdot \max(0, \cos(\omega t + \phi_i)) zi(t)=zc+B⋅max(0,cos(ωt+ϕi))
其中:
- A x , A y A_x, A_y Ax,Ay 控制步幅和侧移幅;
- ϕ i \phi_i ϕi 控制腿的步态相位;
- ω = 2 π / T ω = 2π/T ω=2π/T 控制步频;
max(0, cos())
确保抬脚时才上升;- 通过相位组合
(LF,RH)
同步、(RF,LH)
相反,实现对角步 或原地转向步。
🧩 三、不同动作的相位/方向配置
动作 | 参数 | 特征 |
---|---|---|
前进 | vx > 0, vy = 0, ω = 0 |
对角步 |
后退 | vx < 0 |
相位不变但轨迹反向 |
侧移 | vy ≠ 0 |
左右腿轨迹 y 偏移相反 |
原地转向 | ω ≠ 0 |
四腿以不同相位绕中心旋转,(LF, RH) 与 (RF, LH) 反向摆动 |
🧮 四、完整 Python 控制框架
下面这段代码在之前的 8 舵机机器人基础上扩展,加入了通用步态控制,可设定方向与角速度。
✅ 理论兼容 Raspberry Pi + gpiozero 实机控制
💡 若在电脑上仅模拟,可只运行轨迹部分看运动规律
python
import math, time
from gpiozero import AngularServo
# ========== 参数配置 ==========
PINS = {
"LF_hip": 17, "LF_knee": 18,
"RF_hip": 27, "RF_knee": 22,
"LH_hip": 23, "LH_knee": 24,
"RH_hip": 25, "RH_knee": 5,
}
L1, L2 = 0.05, 0.05
X_CENTER, Z_CENTER = 0.0, -0.08
MIN_PULSE, MAX_PULSE = 0.5/1000, 2.5/1000
MIN_ANGLE, MAX_ANGLE = -90, 90
STEP_A, STEP_B = 0.03, 0.02
T_CYCLE = 1.0
def ik(x, z):
"""逆运动学"""
d = math.sqrt(x**2 + z**2)
d = min(max(d, 1e-6), L1 + L2)
cos_t2 = (x**2 + z**2 - L1**2 - L2**2) / (2 * L1 * L2)
cos_t2 = max(-1, min(1, cos_t2))
t2 = math.acos(cos_t2)
t1 = math.atan2(x, -z) - math.atan2(L2 * math.sin(t2), L1 + L2 * math.cos(t2))
return t1, t2
class Quadruped:
def __init__(self):
self.servos = {
k: AngularServo(v, min_angle=MIN_ANGLE, max_angle=MAX_ANGLE,
min_pulse_width=MIN_PULSE, max_pulse_width=MAX_PULSE)
for k, v in PINS.items()
}
self.pose_stand()
def set_leg(self, leg, x, z):
t1, t2 = ik(x, z)
self.servos[f"{leg}_hip"].angle = math.degrees(t1)
self.servos[f"{leg}_knee"].angle = math.degrees(t2)
def pose_stand(self):
for leg in ("LF","RF","LH","RH"):
self.set_leg(leg, X_CENTER, Z_CENTER)
def gait(self, vx=0, vy=0, yaw_rate=0, steps=2):
"""vx,vy: 线速度 (m/s); yaw_rate: 角速度(rad/s)"""
dt = 0.05
t = 0
while steps > 0:
ω = 2 * math.pi / T_CYCLE
for leg, phi in {"LF":0, "RH":0, "RF":math.pi, "LH":math.pi}.items():
# 旋转方向偏移
yaw_dir = 1 if leg in ("LF","LH") else -1
# 轨迹中心 (控制侧移 + 转向)
x_c = X_CENTER + vx * 0.3 + yaw_dir * yaw_rate * 0.02
y_c = vy * yaw_dir * 0.02
# 足端轨迹
x = x_c + STEP_A * math.sin(ω*t + phi)
z = Z_CENTER + STEP_B * max(0, math.cos(ω*t + phi))
self.set_leg(leg, x, z)
t += dt
if t > T_CYCLE:
t = 0
steps -= 1
time.sleep(dt)
def close(self):
for s in self.servos.values():
try: s.close()
except: pass
if __name__ == "__main__":
bot = Quadruped()
try:
print("前进中...")
bot.gait(vx=0.05, vy=0, yaw_rate=0, steps=2)
print("后退中...")
bot.gait(vx=-0.05, vy=0, yaw_rate=0, steps=2)
print("右移中...")
bot.gait(vx=0, vy=0.05, yaw_rate=0, steps=2)
print("左转中...")
bot.gait(vx=0, vy=0, yaw_rate=0.5, steps=2)
print("右转中...")
bot.gait(vx=0, vy=0, yaw_rate=-0.5, steps=2)
except KeyboardInterrupt:
pass
finally:
bot.close()
print("GPIO 已释放。")
📘 五、运动学解释(以原地转向为例)
当设置 yaw_rate ≠ 0
时:
- 左侧两条腿(LF, LH)的步态相位和足端中心向一个方向偏移;
- 右侧两条腿(RF, RH)相反;
- 结果是足端运动围绕机体中心形成一个"旋转矢量场",产生角速度。
这是在四足机器人中模拟"Yaw 旋转"的常见运动学策略。
✅ 实际中,可以把 yaw_rate 映射到每条腿在平面的旋转半径
r_i
:x i ′ = x c + r i sin ( ω t + ϕ i ) , y i ′ = y c + r i cos ( ω t + ϕ i ) x_i' = x_c + r_i \sin(\omega t + \phi_i), \quad y_i' = y_c + r_i \cos(\omega t + \phi_i) xi′=xc+risin(ωt+ϕi),yi′=yc+ricos(ωt+ϕi)
从而得到更加平滑的转向步态。
🧭 六、动作总结
动作类型 | 参数设置示例 | 描述 |
---|---|---|
前进 | gait(vx=0.05) |
对角步推进 |
后退 | gait(vx=-0.05) |
相同相位反向运动 |
侧移 | gait(vy=0.05) |
左右对称横移 |
左转 | gait(yaw_rate=0.5) |
原地逆时针转向 |
右转 | gait(yaw_rate=-0.5) |
原地顺时针转向 |