
简介这是一份基于单片机的六足机器人控制系统设计完整工程包面向嵌入式、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脉宽 5002500us 对应 0° 到 180°接口简单单价便宜串行总线舵机则把所有舵机并联在同一条总线上用指令帧寻址接线数量从 36 根左右降到 5 根左右但协议和调试工具相对复杂。做毕设或第一个原型我一般会选 PWM 舵机理由很直接信号链路透明示波器一夹就知道有没有脉冲排查问题的路径短。舵机扭力决定机体尺寸。小尺寸机器人用 SG90 这类微型舵机扭矩不到 2kg·cm18 个舵机总重量能控制在 400 克以内如果腿部结构做到 30 厘米以上就要换成 MG996R、DS3218 这类大扭矩舵机。注意MG996R 的典型工作电流在 400700mA堵转时能到 1.5A 以上这个数据直接决定了后面的电源方案。2.2 主控单片机选型51、STM32 还是 ESP32控制系统的核心选型是主控单片机这里把三种最常见的平台放在一起对比单片机PWM 资源通信接口上手难度适合场景STC89C52 系列定时器模拟路数少UART、I2C低51 单片机课程设计演示性机器人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 步态参数如何整定逆运动学解决的是“单条腿怎么踩到目标点”步态规划解决的是“目标点怎么随时间移动”。三角步态下每组摆动腿按照一条轨迹从当前位置扫到下一个落脚点支撑腿则沿反方向把身体往前推。步态参数按下面的表来整定比较快参数典型范围调大后的影响步高1530mm越容易跨过障碍但重心上下晃动加剧步幅2040mm前进速度越快舵机峰值电流越大步态周期600900ms越短动作越敏捷但动态扭矩急剧上升重心偏移515mm补偿支撑相质心位置过大会导致侧翻3.3.1 摆动腿轨迹的抛物线插值摆动腿不能直接从 A 点直线插值到 B 点那样会拖地。常见做法是单独给 z 轴叠加一条抛物线// 摆动腿轨迹t 从 0 到 1x 线性推进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 控制最高点出现在 t0.5 时刻正好是整条腿跨到一半的位置。这个插值函数放进单片机主循环里每隔一个步态周期重新计算一次就能得到平滑的关节角不需要引入额外的运动学库。4. 单片机上实现控制系统PWM 输出、串口帧与状态机4.1 18 路 PWM定时器、PCA9685 还是总线舵机18 路舵机输出有两条常见路线。一条是全部用定时器直接输出 PWM适合学习STM32 的定时器通道数也能凑出来但引脚复用和通道分配非常费精力另一条是 I2C 外接 PCA9685 模块靠内置的 25MHz 晶振产生独立 PWM不占用定时器我一般推荐后者。PCA9685 的初始化代码很短重点是 PRE_SCALE 寄存器要按目标频率计算舵机用 50Hzvoid 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帧头 110x55帧头 22length数据区长度3command0x01 单通道控制4channel舵机通道号 0175angle目标角度 01806checksum前 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 函数需要把 0180° 映射到 PWM 计数器的 04096映射前还要叠加舵机零点偏置这个偏置放在配置表里单独校准。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, timeout0.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 个关节通道号。调试时只改这张表不动逆运动学代码也不动步态逻辑。校准零点时也按这张表逐一写入偏置值防止漏掉某个关节。把这套诊断顺序跑完控制系统所有子模块的状态都能清晰掌握下一步再回到步态算法里去优化转弯半径或者动态稳定性才真正是有据可依的调整。本文还有配套的精品资源点击获取