从单片机到步态算法:六足机器人控制系统设计全攻略
2026/9/16 16:03:12 网站建设 项目流程

简介:这是一份基于单片机的六足机器人控制系统设计完整工程包,面向嵌入式、STM32及课程设计相关学习者,覆盖硬件原理图、PCB、下位机程序与手机APP配套资源。系统采用STM32F103VET6主控板与STM32F103R8T6舵机控制板双板方案,结合WiFi、蓝牙、语音识别与手势识别实现多种控制模式,并给出云端服务器与Android Studio开发的上位机APP,适合需要参考完整项目架构、硬件设计及多模式交互实现的读者。压缩包共944个文件,约101.91MB,以c/h源码、PCB工程文件(pcbdoc/prjpcb)、hex固件、png原理图截图及APK安装包为主,另含PDF说明与文档资料,目录结构清晰,便于按硬件、软件、上位机分类查阅。目前已有314人学习下载,可用于课程设计答辩、项目二次开发或六足机器人步态与控制算法的入门参考。

1. 为什么六足机器人控制系统要落在单片机上

六足机器人一共有 18 个舵机,第一次通电时最怕的不是机械装歪,而是六条腿同时抽风。有人图省事想把电脑直接当成控制器,试过就会发现 USB 线和电源线先把机器人的活动半径限制在了一米以内,串口一旦拥堵,第十八号舵机就跟不上节奏。把控制系统的主控放到单片机上才符合实际:角度换算、步态时序、串口指令解析这些事都交给它,上位机只负责发目标和看状态。这篇文章就顺着“单片机 + 六足机器人 + 控制系统设计”这条主线往下讲,覆盖器件选型、步态算法、通信协议和调试排错。适合自己做机械和电控的工程师,也适合把六足机器人当成课程设计或毕业设计来做的同学,尤其是已经在用 51 单片机或 STM32 写过程序、想再往上走一步的人。

2. 六足机器人控制系统器件选型与整体架构

2.1 先定自由度与舵机信号类型

六足机器人的经典腿部构型是每条腿 3 个自由度:髋关节负责前后摆动,大腿负责抬起,小腿负责蹬地。6 条腿乘 3 个关节,一共 18 路输出,所以控制系统设计的第一件事不是选单片机,而是确认舵机型号和信号类型。

常见舵机分成两类。普通 PWM 舵机靠脉宽控制角度,周期 20ms,脉宽 500~2500us 对应 0° 到 180°,接口简单,单价便宜;串行总线舵机则把所有舵机并联在同一条总线上,用指令帧寻址,接线数量从 36 根左右降到 5 根左右,但协议和调试工具相对复杂。做毕设或第一个原型,我一般会选 PWM 舵机,理由很直接:信号链路透明,示波器一夹就知道有没有脉冲,排查问题的路径短。

舵机扭力决定机体尺寸。小尺寸机器人用 SG90 这类微型舵机,扭矩不到 2kg·cm,18 个舵机总重量能控制在 400 克以内;如果腿部结构做到 30 厘米以上,就要换成 MG996R、DS3218 这类大扭矩舵机。注意,MG996R 的典型工作电流在 400~700mA,堵转时能到 1.5A 以上,这个数据直接决定了后面的电源方案。

2.2 主控单片机选型:51、STM32 还是 ESP32

控制系统的核心选型是主控单片机,这里把三种最常见的平台放在一起对比:

单片机PWM 资源通信接口上手难度适合场景
STC89C52 系列定时器模拟,路数少UART、I2C51 单片机课程设计,演示性机器人
STM32F103C8T6定时器多路,可外接 PCA9685UART、I2C、SPI课程设计、工程原型主流选择
ESP32 系列LEDC 16 路,仍需扩展UART、I2C、SPI、Wi-Fi中低需要无线数传、手机遥控的场合

很多人看到 18 个舵机就去数单片机上有几路定时器,这是误区。STM32F103C8T6 的通用定时器虽然多,但要把 18 路 PWM 全部从单片机引脚引出,引脚冲突和定时器分配会让人改到第三版 PCB 都改不完。最常见做法是用 I2C 接 PCA9685 十六路 PWM 扩展芯片,再加上定时器输出的两路,凑够 18 路;ESP32 虽然自带 16 路 LEDC,仍然少两路,而且 Wi-Fi 天线和舵机线绞在一起容易引发信号毛刺。选型结论是:非无线场景优先 STM32F103,要做手机 App 控制再上 ESP32。

2.3 电源与驱动电路:舵机电流比想象中大一倍

六足机器人控制系统的故障里,一半以上出在电源,不在程序。18 个舵机同时动作时,峰值电流可能接近 10A,常见做法是用 7.4V 锂电池组加一个大功率 BEC 降压模块,输出 5V/5A 以上给舵机供电;单片机逻辑电源从另一路 DC-DC 输出,避免舵机堵转时把单片机拉复位。

提示:不要用小开发板上的 5V 引脚直接带舵机,那个引脚通常只能输出几百毫安,六条腿一抬,单片机立刻掉电。

我一般还会在舵机电源轨上并联一个 470uF 电解电容和几个 0.1uF 陶瓷电容,电容就近放在舵机供电端子旁边。这个做法能明显减少舵机启动瞬间的电压跌落,很大程度上避免“单片机复位导致腿部乱抖”的经典问题。

2.4 传感器与执行器整体架构

姿态传感器是六足机器人的必备件,最常见的是 MPU6050 六轴 IMU,挂在 I2C 总线上,用来检测机体倾角,斜坡上行走时做步态补偿。避障常用 HC-SR04 超声波模块或者 VL53L0X 激光测距模块,装在机器人头部,检测前方障碍物后通过串口向主控发送“急停”或者“转向”指令。

整个系统的信息流可以画成一条简单链路:

电池 → 电源模块 → 5V 舵机电源轨 → 18 路 PWM 舵机 └→ 3.3V 逻辑电源 → STM32 主控 STM32 ← I2C ← MPU6050 姿态传感器 STM32 ← UART ← 上位机或遥控器 STM32 ← GPIO ← 超声波测距模块

主控单片机从 UART 收到控制指令,从 I2C 读姿态,从 GPIO 读避障结果,然后把 18 路目标角度换算成 PWM 输出。这个架构里,单片机是唯一决策节点,所有传感器和执行器都挂在它下面,便于在裸机程序里统一调度。

3. 步态规划与单腿逆运动学:让六条腿迈出节奏

3.1 为什么默认先用三角步态

六足机器人的步态很多,波纹步态、波动步态、自由步态各有适用范围,但第一个上手调试的几乎都是三角步态。原因很简单:三角步态把 6 条腿分成两组,1、3、5 号腿为一组,2、4、6 号腿为另一组,两组交替抬起和落下,每一组抬起来的时候,另外三条腿在地面形成三角形支撑结构,重心天然落在支撑三角形内部,静态稳定性最好。

三角步态的控制逻辑也很直接。每组腿的摆动相位相同,支撑相位相同,步态周期只需要一个定时中断就能驱动。六条腿同时推进,而不是一条一条单腿抬放,运动速度要快得多。缺点也明显:重心上下波动大,视觉上像在跳跃,机械结构刚性不足时会出现明显晃动。但对于课程设计和第一个工程样机,把三角步态调通就已经解决了控制系统 80% 的问题。

3.2 单腿坐标系与逆运动学公式

每条腿的 3 个舵机对应 3 个关节角:侧摆角、大腿角、小腿角。控制系统的任务是:给定足端相对于髋关节的三维坐标 (x, y, z),反推出 3 个舵机应该转到的角度,这就是单腿逆运动学。

建立局部坐标系,原点落在髋关节,x 轴指向机体前方,y 轴指向侧面,z 轴竖直向上。先算侧摆角:

alpha = atan2(x, y)

然后取水平距离 d = sqrt(x² + y²),空间距离 dist = sqrt(d² + z²),用余弦定理求膝盖角和髋角。下面这段是用 C 语言实现的单腿逆运动学函数:

// 单腿逆运动学:输入足端坐标(x,y,z),输出侧摆角(yaw)、大腿角(hip)、小腿角(knee) // 坐标单位是米,角度单位是弧度;原点在髋关节,x 向前,y 向侧面,z 向上 void leg_ik(float x, float y, float z, float *yaw, float *hip, float *knee) { float L1 = 0.06f; // 大腿长度,按你的实际结构修改 float L2 = 0.06f; // 小腿长度 float d, dist, cos_knee, cos_hip; *yaw = atan2f(x, y); // 侧摆角只由 (x,y) 决定 d = sqrtf(x * x + y * y); // 足端在水平面的投影距离 dist = sqrtf(d * d + z * z); // 髋关节到足端的空间距离 // 距离超过大腿+小腿可触及范围时截断,避免 acos 输入越界 if (dist > L1 + L2 - 1e-4f) dist = L1 + L2 - 1e-4f; cos_knee = (L1 * L1 + L2 * L2 - dist * dist) / (2.0f * L1 * L2); // 余弦定理求膝角 *knee = acosf(cos_knee); cos_hip = (L1 * L1 + dist * dist - L2 * L2) / (2.0f * L1 * dist); // 大腿与空间距离方向的夹角 *hip = atan2f(z, d) - acosf(cos_hip); // 投影角减内部角得到髋角 }

这段代码的关键点有三个。第一,侧摆角只和水平坐标有关,和抬腿高度无关,这符合实际安装结构;第二,acos 的输入必须限制在 [-1, 1],足端坐标超出机械臂可触及范围时先截断距离再求反余弦,否则返回值是 NaN;第三,髋角的符号取决于舵机安装方向,不同机械结构可能需要对 *hip 取负或者用 180° 减去它。第一次移植这段 C 语言代码时,最常出问题的就是符号方向而不是公式本身。

3.3 步态参数如何整定

逆运动学解决的是“单条腿怎么踩到目标点”,步态规划解决的是“目标点怎么随时间移动”。三角步态下,每组摆动腿按照一条轨迹从当前位置扫到下一个落脚点,支撑腿则沿反方向把身体往前推。

步态参数按下面的表来整定比较快:

参数典型范围调大后的影响
步高15~30mm越容易跨过障碍,但重心上下晃动加剧
步幅20~40mm前进速度越快,舵机峰值电流越大
步态周期600~900ms越短动作越敏捷,但动态扭矩急剧上升
重心偏移5~15mm补偿支撑相质心位置,过大会导致侧翻
3.3.1 摆动腿轨迹的抛物线插值

摆动腿不能直接从 A 点直线插值到 B 点,那样会拖地。常见做法是单独给 z 轴叠加一条抛物线:

// 摆动腿轨迹:t 从 0 到 1,x 线性推进,z 按抛物线抬高后落下 for (float t = 0.0f; t <= 1.0f; t += 0.05f) { float z = start_z + 4.0f * step_height * t * (1.0f - t); float x = start_x + (end_x - start_x) * t; // 约束 y 坐标保持不变,然后调用 leg_ik() 得到三个关节角 }

这里 z 变化的幅度由 step_height 控制,最高点出现在 t=0.5 时刻,正好是整条腿跨到一半的位置。这个插值函数放进单片机主循环里,每隔一个步态周期重新计算一次就能得到平滑的关节角,不需要引入额外的运动学库。

4. 单片机上实现控制系统:PWM 输出、串口帧与状态机

4.1 18 路 PWM:定时器、PCA9685 还是总线舵机

18 路舵机输出有两条常见路线。一条是全部用定时器直接输出 PWM,适合学习,STM32 的定时器通道数也能凑出来,但引脚复用和通道分配非常费精力;另一条是 I2C 外接 PCA9685 模块,靠内置的 25MHz 晶振产生独立 PWM,不占用定时器,我一般推荐后者。

PCA9685 的初始化代码很短,重点是 PRE_SCALE 寄存器要按目标频率计算,舵机用 50Hz:

void pca9685_init(void) { // PCA9685 默认 I2C 地址 0x40,外部晶振 25MHz i2c_write_byte(0x40, 0x00, 0x01); // MODE1: 复位并准备配置 i2c_write_byte(0x40, 0xFE, 0x79); // PRE_SCALE: 配置成 50Hz i2c_write_byte(0x40, 0x00, 0xA1); // MODE1: 恢复工作,使能输出 for (int ch = 0; ch < 16; ch++) pca9685_set_pwm(ch, 0, 0); // 全部通道先置 0 }

PRE_SCALE 的算法是 round(25MHz / (4096 × 50Hz)) - 1,结果约等于 121,十六进制就是 0x79。把 4096 放在公式里是因为 PCA9685 内部是 12 位计数器。设置完成后,调用 pca9685_set_pwm(ch, 0, pwm_value) 就能控制对应通道的舵机角度。需要注意 PCA9685 的 GND 必须和单片机共地,否则 I2C 信号电平无法形成回路。

4.2 单片机上串口帧协议:从上位机到关节角度

控制系统和人交互的入口是串口。上位机或蓝牙模块发来一帧数据,单片机解析后更新目标角度,这是最常见的设计。我采用类似 Modbus 风格的帧格式,结构有三部分:固定帧头、数据区和单字节校验。

字节偏移内容说明
00xAA帧头 1
10x55帧头 2
2length数据区长度
3command0x01 单通道控制
4channel舵机通道号 0~17
5angle目标角度 0~180
6checksum前 6 字节累加和取低 8 位

解析函数要在串口接收中断里把字节放进缓冲区,然后在主循环中调用。下面是一个完整的校验与解析逻辑:

// 接收缓冲区 rx_buf 由串口中断填充,len 是已接收的字节数 // 返回 0 表示帧有效,-1 表示帧无效 int parse_frame(uint8_t *rx_buf, uint8_t len) { if (len != 7) return -1; // 固定长度帧 if (rx_buf[0] != 0xAA || rx_buf[1] != 0x55) // 帧头校验 return -1; uint8_t sum = 0; for (int i = 0; i < len - 1; i++) sum += rx_buf[i]; // 累加除校验位外所有字节 if (sum != rx_buf[len - 1]) return -1; uint8_t ch = rx_buf[4]; uint8_t angle = rx_buf[5]; pca9685_set_pwm(ch, 0, angle_to_pwm(angle)); // 转换并输出 return 0; }

这段代码把帧长度固定为 7 个字节,省去了对 length 字段的复杂解析逻辑。注意 checksum 和 sum 都是单字节无符号数,累加后溢出截断是预期行为,只要上位机用同一规则计算就能匹配。angle_to_pwm 函数需要把 0~180° 映射到 PWM 计数器的 0~4096,映射前还要叠加舵机零点偏置,这个偏置放在配置表里单独校准。

4.3 状态机设计:待机、行走、转向、停止

六足机器人的控制系统不能只沿一个方向跑循环,不同指令下行为完全不同,所以要引入状态机。最简单的状态划分是四态模型:

状态进入条件动作
ST_IDLE上电复位完毕18 路舵机回到中间角度
ST_WALK收到行走指令按三角步态周期推进目标角度
ST_TURN收到转向指令左右腿步幅反向,机体原地旋转
ST_STOP收到停止指令或触发避障摆动腿先落地,再锁定全部关节

对应的裸机程序结构是一段 switch 分发:

typedef enum { ST_IDLE, ST_WALK, ST_TURN, ST_STOP } robot_state_t; robot_state_t state = ST_IDLE; void step_service(void) { switch (state) { case ST_IDLE: // 每 20ms 检查串口命令,收到行走指令则切换状态 break; case ST_WALK: walk_next_step(); // 推进一次三角步态周期 break; case ST_TURN: turn_next_step(); // 左右腿步幅差由目标转向角决定 break; case ST_STOP: stop_legs(); // 先落腿再锁关节 state = ST_IDLE; break; default: state = ST_IDLE; break; } }

状态切换最容易出问题的是 ST_STOP。如果收到急停指令时三条摆动腿还在空中,直接把状态切到 IDLE 会导致机体侧翻。所以 stop_legs() 里要先按当前插值位置把摆动腿的 z 降到支撑平面,再进入停止状态,这一判据在避障功能里尤其重要。

4.4 Python 上位机快速验证

单片机端的帧协议调通后,上位机用 Python 串口库几行就能验证:

import serial import time ser = serial.Serial('COM3', 115200, timeout=0.1) def set_servo_angle(channel, angle): frame = bytes([0xAA, 0x55, 0x04, 0x01, channel, angle]) frame += bytes([sum(frame) & 0xFF]) ser.write(frame) time.sleep(0.02) # 让 0 号和 1 号舵机交替运动,观察腿部动作是否正常 for i in range(10): set_servo_angle(0, 60) set_servo_angle(1, 120) time.sleep(0.3) set_servo_angle(0, 120) set_servo_angle(1, 60) time.sleep(0.3)

这段代码里 frame 的构造和单片机 parse_frame 完全对应,sum(frame) 取低 8 位作为校验字节。如果舵机没动,先用示波器看串口 TX 引脚有没有波形,再看单片机有没有进入串口接收中断,最后检查校验字节,排查顺序不要反过来。

5. 六足机器人控制系统调试技巧与验证

5.1 先让一条腿动起来,不要直接跑步态

三组步态参数同时整定是最浪费时间的做法。我的习惯是把所有步态参数放到一个结构体里,先用诊断模式让一条腿按正弦轨迹运动,确认逆运动学代码和舵机方向都正确,再把六条腿依次扫一遍。下面这段函数可以放在按键中断里触发:

// 诊断模式:让指定通道舵机从 0° 到 180° 往复运动 void servo_test_channel(int ch) { for (int angle = 0; angle <= 180; angle += 10) { set_servo_angle(ch, angle); // 复用上位机协议函数 delay_ms(200); } }

这个测试能做三件事:验证舵机信号线接线没有短路;验证角度映射函数方向正确;验证电源在连续负载下的电压表现。如果某个角度区间出现抖动,先查该通道的 PWM 波形,而不是怀疑代码。

5.2 示波器同时看 PWM 和电源轨

舵机抖动是六足机器人控制系统最隐蔽的问题。抖动来源可能是代码里角度插值有跳变,也可能是电源纹波过大让舵机控制芯片误判信号。排查时用双通道示波器,一个探头夹在舵机信号线,另一个探头夹在舵机电源正极,观察舵机动作瞬间电源轨是否有超过 200mV 的跌落。

如果电源跌落明显是硬件问题,需要在电源模块输出端加大电容,或者把舵机电源和逻辑电源彻底分开布线;如果 PWM 波形本身毛刺多,则要检查单片机与 PCA9685 的 I2C 线长度,超过 20cm 的信号线就可能引入干扰,用屏蔽线或降低 I2C 速率都能缓解。

5.3 用逻辑分析仪验证串口帧时序

上位机发出的帧到了单片机里可能已经错位,尤其是蓝牙串口这种半双工链路。逻辑分析仪抓取 TX 和 RX 两路信号,直接在软件里解析出 0xAA 0x55 帧头,确认上位机的发送周期和单片机解析周期一致。很多“单片机收不到指令”的问题,最后发现是上位机在两条指令之间没有加延时,单片机串口缓冲区还没清完就被下一次发送覆盖。

5.4 在线调试看目标角度数组

调试步态切换时,我一般把 18 路舵机的目标角度集中放到一个数组 target_angle[18] 里,在状态机切换位置打断点,观察数组里是否有突变值。步态周期和状态切换之间最容易出现的一类 bug 是:切换前最后一帧某个摆动腿的 z 坐标没有归零,导致 switch 后第一帧腿部角度瞬间跳到极限位置。解决办法是在状态迁移前调用一次落腿插值,强制所有腿先进入支撑状态。

5.5 最后一个技巧:给每只脚编号做单腿映射表

硬件接线时编号常常和实际安装位置对不上。我建议在程序里维护一张腿号到舵机通道的映射表,例如uint8_t leg_map[6][3],每行是这条腿的 3 个关节通道号。调试时只改这张表,不动逆运动学代码,也不动步态逻辑。校准零点时也按这张表逐一写入偏置值,防止漏掉某个关节。

把这套诊断顺序跑完,控制系统所有子模块的状态都能清晰掌握,下一步再回到步态算法里去优化转弯半径或者动态稳定性,才真正是有据可依的调整。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询