
简介这是一份面向嵌入式开发与机器人控制初学者的STM32F103蜘蛛机器人控制程序源码演示了基于ARM Cortex-M3内核的完整机器人运动控制流程。源码覆盖硬件接口配置、电机驱动、传感器采集以及PID控制算法并体现FreeRTOS等实时操作系统在多任务调度中的应用适合希望从代码层面理解机器人底层控制逻辑的读者。压缩包共一百八十八个文件主要包括三十四个h头文件、三十一个c源程序、三十一个o编译目标及crf、dep等工程辅助文件头文件用于接口声明源文件承载主体逻辑整体仅1.6MB下载和查阅都很方便。目前已有1378人下载学习。通过研读源码读者可掌握标准外设库的驱动写法、步进电机控制思路、传感器数据融合方法以及调试工具链使用并能直接打开配套工程编译验证缩短自建机器人项目的排错时间是一份紧凑的机器人嵌入式入门参考资料。 玩蜘蛛机器人最难的不是把零件拼起来而是让它真的“活”起来——六条腿协调摆动、转弯、爬行每一步都涉及舵机控制、步态规划、姿态平衡这些东西。这篇文章里我把自己用STM32F103从零写蜘蛛机器人控制程序的完整过程梳理一遍包含硬件选型、控制框架、核心源码拆解以及调试时踩过的那些坑给正在纠结“源码怎么写”的朋友一份可以直接上手的参考。1. 项目概述与整体设计思路1.1 为什么选STM32F103做蜘蛛机器人主控蜘蛛机器人这类多足机器人本质上是一个多舵机运动控制系统。市面上常见的方案有Arduino、ESP32、STM32我个人更推荐STM32F103原因很直接定时器资源丰富STM32F103C8T6有4个通用定时器、2个高级定时器可以生成多路独立PWM信号。蜘蛛机器人一般需要8~12路舵机控制用定时器的PWM输出通道刚好够用而且互不干扰。主频够用72MHz主频跑步态计算和逆运动学解算绰绰有余。相比之下Arduino UNO的16MHz在同时控制12路舵机时CPU占用已经逼近极限。生态成熟标准外设库StdPeriph和HAL库资料满天飞哪怕你是第一次接触ARM Cortex-M3照着参考手册和例程也能把工程跑起来。这个项目的目标是做一个8自由度蜘蛛机器人每条腿2个舵机共4条腿。主控板采用STM32F103C8T6最小系统板外接一个PCA9685舵机驱动板可选也可以直接用定时器PWM上位机通过串口或蓝牙发送运动指令下位机解析后驱动舵机运动。整体架构如下上位机(手机/PC) --串口/蓝牙-- STM32F103 --PWM信号-- 舵机驱动板 --舵机角度-- 蜘蛛机器人腿部1.2 控制系统架构与代码分层这块是源码设计的关键。在动笔写代码之前先把代码结构理清楚我采用的是类似分层驱动的思想底层HAL层基于STM32标准外设库封装舵机控制函数负责PWM输出、定时器初始化、IO配置。这部分最靠近硬件一旦写好基本不用动。中层运动学层负责腿部逆运动学IK解算把三维空间坐标值转化为每个舵机的角度值。蜘蛛机器人的每条腿可以简化成一个二连杆机构解算过程就是给定脚端坐标求出髋关节和膝关节的角度。上层步态与命令层负责步态规划比如三角步态、波动步态以及接收上位机指令并调度动作。这样分层的好处很明显如果你以后想换成树莓派或者ESP32只需要替换底层HAL层中层和上层的运动学、步态代码几乎可以原封不动迁移。提示新手最容易犯的错误是上来就把所有代码塞进一个main.c循环里结果后面调步态时各种混乱。强烈建议先按模块拆分文件哪怕费点事后期调试会省下大量时间。2. 硬件搭建与舵机控制基础2.1 蜘蛛机器人的自由度配置这里做的8自由度版本每条腿分配2个舵机髋关节舵机左右摆动控制整条腿向外张开或收拢的角度范围通常为0°~180°。膝关节舵机前后抬腿控制大腿相对身体的抬升和落下是步态中真正“迈步”的核心舵机。每条腿的根部还有一个物理限位结构防止舵机活动角度超出自身机械极限导致扫齿或堵转。这个限制在机械设计阶段就要考虑好然后在代码里再做一层软件限位双保险。2.2 PWM频率与舵机控制信号常见模拟舵机如SG90、MG996R的控制信号是50Hz的PWM波脉冲宽度0.5ms~2.5ms对应0°~180°。这里有一个容易搞混的点STM32的定时器溢出周期配置的是ARR值不是直接填“舵机角度值”。以STM32F103C8T6为例如果采用72MHz主频PSC预分频系数设为71那么定时器时钟就是1MHz周期1微秒。要想得到50Hz的PWM频率也就是周期20ms20000微秒ARR就要设为19999从0计到19999共20000个计数单位。那么舵机角度和脉冲宽度的换算关系就是0°0.5ms 500微秒对应计数500180°2.5ms 2500微秒对应计数2500线性映射角度值 → 500 (角度值 / 180) * 2000下面是我在实际工程里用的舵机PWM初始化代码标准库版本void Servo_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_tim1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 配置PA8为TIM1_CH1输出PA0-PA3为TIM2_CH1-CH4输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 定时器时基72MHz / 71 1MHzARR19999PWM频率50Hz TIM_TimeBaseStructure.TIM_Prescaler 71; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period 19999; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, TIM_TimeBaseStructure); // PWM模式1输出极性高 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, TIM_OCInitStructure); TIM_OC2Init(TIM2, TIM_OCInitStructure); TIM_OC3Init(TIM2, TIM_OCInitStructure); TIM_OC4Init(TIM2, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM2, ENABLE); TIM_Cmd(TIM2, ENABLE); }每个舵机角度最终被映射到SetServoAngle(servo_id, angle)函数里内部重新计算比较寄存器CCR的值并写入TIM_SetComparex()。整个过程不占用CPU轮询舵机控制完全由定时器硬件完成。2.3 舵机供电的坑这部分必须重点说舵机千万别直接接STM32板的3.3V引脚。MG996R堵转电流可以到2A以上瞬间把主控板电压拉垮导致单片机重启。我的供电方案是外接一个7.4V 2S锂电池经过UBEC降压模块输出5V/5A给舵机阵列单独供电STM32最小系统板则由USB口单独供电两边只共地不共电源。调试中发现电压低于4.8V时舵机就开始抽搐抖动其实是供电不足的表现不是代码问题。3. 控制程序核心实现步态规划与腿部运动学3.1 三角步态的基本原理四足机器人的步态里最简单实用的就是“三角步态”——四足中两两分组一组抬起移动另一组支撑身体。以俯视图看对角线两条腿组成“腰”字形的支撑面此时机器人重心始终落在支撑腿围成的三角形区域内保证稳定。我把4条腿编号为FL左前、FR右前、BL左后、BR右后三角步态的抬腿分组为第一相位FL BR 同时抬起向前摆动第二相位FR BL 同时抬起向前摆动每一相位动作间隔通常设为200ms~400ms数值越短蜘蛛跑得越快但步态稳定性下降。这套思路说白了就是“交叉抬腿——保持重心稳定——交替迈步”的循环理解了这个基础后续想改成螃蟹步、旋转步都只是在相位分配上做文章。3.2 二连杆腿部逆运动学解算每条腿的两个舵机构成平面二连杆结构。已知髋关节到脚端的水平距离x和垂直高度y求两个关节角度。这种问题在高中几何里就是余弦定理但在写代码时容易卡住我直接给出推导后的公式。设大腿长度L1小腿长度L2髋关节为原点z轴向下为正注意坐标系方向不同公式的符号会有差异r sqrt(x*x y*y) // 脚端到髋关节的距离 cos_alpha (L1*L1 L2*L2 - r*r) / (2*L1*L2) alpha acos(cos_alpha) // 膝关节角度余弦定理 beta atan2(y, x) // 大腿与水平面的夹角 delta acos((L1*L1 r*r - L2*L2) / (2*L1*r)) knee_angle 180 - alpha hip_angle beta - delta有了这个公式控制脚端位置就变成了纯数学问题——想向前走就调整脚端的x坐标想爬坡就调整y坐标。我把逆运动学封装成ik_solve_leg()函数输入目标坐标x,y以及每条腿的安装偏移角输出髋和膝两个舵机的目标角度。3.3 步态控制与定时器调度直接在主循环里delay实现步态是最幼稚的写法会导致舵机动作生硬且CPU被占死。我改用系统时基SysTick或定时器中断驱动一个状态机每50ms进入一次中断判断当前所处步态相位、计算脚端轨迹、调用逆运动学解算、发送舵机指令。主循环则只负责监听串口指令和状态管理两者互不阻塞。void Motion_Step(void) { static uint8_t phase 0; static uint32_t last_tick 0; float x, y; if (now_ms - last_tick MOTION_INTERVAL) return; last_tick now_ms; switch (gait_mode) { case GAIT_TRIPOD: if (phase 0 || phase 1) { // 第一相位FL和BR腿抬起前摆 x step_length; y -leg_lift_height; } else { // 第二相位这两条腿着地另外两条抬起 x -step_length; y 0; } ik_solve_leg(LEG_FL, x, y, angle_hip, angle_knee); Servo_SetAngle(LEG_FL_HIP, angle_hip); Servo_SetAngle(LEG_FL_KNEE, angle_knee); // 其余腿同理... break; case GAIT_ROTATE: // 旋转步态所有腿左右分组摆动 break; } phase (phase 1) % 4; }中间再叠加一个正弦插值让脚端轨迹呈现“椭圆”平滑过渡而非直线动作看起来就自然得多。这一部分属于锦上添花但在演示视频里的效果差距非常大值得花时间调。4. 上位机通信与指令协议设计4.1 串口指令帧格式蜘蛛机器人不能只会“原地踏步”还需要能被人控制。我用了一个极简的串口帧协议每帧固定5个字节字节序号内容说明00xAA帧头1命令字0x01前进、0x02后退、0x03左转、0x04右转、0x10急停2速度档位0~10数字越大速度越快3模式字保留字段用于扩展其他功能40x55帧尾STM32在串口接收中断里做状态机解析等待帧头、收满5字节、校验帧尾全部通过才更新全局运动参数。这里我没有用校验和而是用固定帧头帧尾简单场景够用如果想要更保险可以加入CRC8。4.2 蓝牙远程控制扩展实际使用中手机连蓝牙模块HC-06比拖一根串口线舒服得多。接线方式很简单HC-06的TXD接STM32的PA10USART1_RXRXD接PA9USART1_TX波特率设为9600代码里完全不用动。要注意的是HC-06默认只能当从机配对密码一般是1234要想修改波特率需要在上电前按住模块上的小按钮再上电进入AT指令模式发送ATBAUD8之类指令切换。这个坑我踩过一次当初图省电把HC-06和舵机共用5V电源结果舵机一动作蓝牙就疯狂丢包后来给HC-06单独引了一路带LC滤波的5V电源才稳定。4.3 手机端控制App方案若不想自己写App可以直接用“蓝牙串口”类的现成工具发送16进制“AA 01 03 00 55”这种指令控制。我实际测试了多个App稳定性和使用体验存在差异最好自己多试几个再决定。如果你愿意折腾可以开源项目方案用Flutter写一个简单的蓝牙串口助手界面就三个按钮——前进、左转、右转加一个速度滑条。手机和HC-06蓝牙配对后通过经典蓝牙SPP协议发指令效果很好而且你还能把代码和源码一起打包发给朋友。最后再加一个按键防抖、回连重试逻辑项目完整度会提升不少。5. 调试实录常见问题与避坑经验5.1 舵机抖动、卡顿与发热问题这是反馈最多的问题。抖动原因基本可以分为三类供电不足舵机瞬间拉低电压MCU或舵机驱动板的逻辑电路复位。用万用表在舵机动作瞬间测供电电压如果跌落超过0.5V基本就是电源问题优先换大电流电源模块。PWM信号频率不对有些数字舵机不兼容50Hz需要用到333Hz甚至更高频率。这时候ARR和PSC就要重新计算比如333Hz对应周期约3msARR设为29991MHz计数频率。舵机扫齿或齿轮磨损机械结构卡死时舵机堵转发热时间长了必然扫齿。排查方法把舵机拆下来空载测试如果还抖就是舵机本身的问题换舵机最省事。5.2 上电瞬间机器人“抽搐”一下这个问题特别典型。原因是STM32上电瞬间所有GPIO默认状态是浮空输入舵机控制引脚的电平不确定舵机自然会乱动一下。解决办法在初始化代码里先把所有舵机PWM通道关闭或拉到安全电平等待电源稳定后再依次初始化定时器并输出占空比。严格讲这叫“上电时序控制”实际做的时候可以简单粗暴地在舵机供电回路加一个MOS管开关由STM32的一个引脚控制舵机电源延迟上电。5.3 逆运动学算出的角度是反的这里大概率不是公式错而是舵机安装方向导致的角度方向反了。处理方式很粗暴但有效定义一个SERVO_DIR[8]数组每个舵机初始化为1或-1在设置角度时做一次乘法翻转再写一段“舵机角度扫描”测试程序手动校准每个舵机的正反方向。int servo_dir[8] {1, -1, 1, 1, -1, 1, -1, -1}; void Servo_SetAngle(uint8_t id, float angle) { uint16_t pulse (uint16_t)(500 servo_dir[id] * angle / 180.0f * 2000.0f); // 限幅保护 if (pulse 2500) pulse 2500; if (pulse 500) pulse 500; TIM_SetCompare1(TIM2, pulse); // 实际根据舵机通道选择compare函数 }5.4 代码烧录后板子没反应先用电脑上的串口工具测试STM32的串口是否正常发送数据排除USB转串口芯片的问题再用逻辑分析仪量PWM引脚的波形最后检查舵机信号线是否插反。不要一上来就怀疑舵机坏了按信号链路从主控到舵机逐级排查才能快速定位。5.5 常见问题速查表现象可能原因处理办法舵机完全不动供电异常 / PWM引脚配置错测量舵机电源电压核对初始化代码引脚号舵机抖动供电不足 / PWM频率不对加大电源电流检查PSC和ARR值机器人走路歪斜各腿初始零点不一致加零点校准流程记录每个舵机零偏上电瞬间乱动GPIO默认电平不确定增加舵机电源软启动蓝牙连不上HC-06未进入配对模式检查波特率、配对密码、TX/RX是否交叉6. 源码工程组织与后续扩展建议6.1 工程文件结构参考整个源码工程的目录结构大致如下用Keil MDK打开主工程文件即可直接编译SpiderRobot/ │ ├── Core/ │ ├── main.c // 主函数初始化与主循环 │ ├── stm32f10x_it.c // 中断服务函数 │ └── system_stm32f10x.c // 系统时钟配置 │ ├── Hardware/ │ ├── servo.c / servo.h // 舵机PWM底层驱动 │ ├── usart.c / usart.h // 串口初始化与接收解析 │ └── timer.c / timer.h // 定时器调度时基 │ ├── Motion/ │ ├── kinematics.c/h // 逆运动学解算 │ ├── gait.c/h // 步态规划与相位切换 │ └── command.c/h // 命令解析与状态管理 │ └── User/ ├── config.h // 全局参数配置文件 └── debug.c/h // 调试打印与测试函数在config.h里集中管理所有可调参数腿部连杆长度、舵机安装方向、运动速度档位、串口波特率等。这样朋友拿到的代码即使型号不同改几处宏定义就能适配不需要到处找散落的魔法数字。6.2 后续升级方向目前这套代码已经能实现基本的前进、后退、左右转向和调速。想继续折腾的话可以考虑以下几个方向加入IMU陀螺仪姿态反馈通过MPU6050获取机身倾斜角度动态调整各腿长度让机器人在斜面上也能保持身体水平。无线遥控器控制改用2.4G NRF24L01模块延时比蓝牙低不少控制距离也更远。视觉巡线模块在机头位置加一个灰度传感器或OpenMV让蜘蛛机器人自动循迹这是比较有意思的进阶玩法。移植FreeRTOS当任务多了之后裸机状态机的可维护性会下降把步态调度、串口处理、传感器采集拆成独立任务代码结构性会清晰很多。我个人在实际操作中最深的体会是蜘蛛机器人的“灵魂”说到底是算法不是硬件堆料。舵机加STM32的成本其实不高但从三角步态到逆运动学、从串口协议到状态机调度每一步都在逼你把数学和C语言基本功打扎实。拿着这套源码去改去调比照着教程抄一遍收获大得多。最后再分享一个小技巧调步态时先在桌面上把脚端轨迹用上位机画出来确认每个关键坐标点符合预期再让机器人真正落地跑能省下大量反复拆装舵机的时间。机器人在悬浮状态下调试动作跟在真实地面上的受力完全不同所以最后还是要回归地板实测这个区分很多初学者容易忽略。本文还有配套的精品资源点击获取