ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于STM32的Marlin三轴机械臂:从硬件选型到运动学逆解与调参实战

基于STM32的Marlin三轴机械臂:从硬件选型到运动学逆解与调参实战 简介面向嵌入式与自动化专业学生这份基于STM32的Marlin三轴机械臂控制程序源码可用于毕业设计、期末大作业或课程设计。项目内含详细注释代码思路清晰新手也可以快速理解其中还涉及STM32定时器、ADC、I2C等外设驱动适合深入学习机械臂运动控制。压缩包共有168个文件大小约3.38MB文件类型以C语言源文件(.c)和头文件(.h)为主同时还包含Keil工程配置文件(.uvprojx/.uvoptx)、编译输出文件(.hex/.axf)、构建依赖文件(.dep/.map)及少量备份文件便于直接打开工程进行编译和部署。这套项目由作者独立编写获得98分的高分经过严格调试系统功能完善、界面直观运行稳定可直接作为课题原型或功能基础。目前已有152人学习适合需要快速积累机械臂控制经验或想在毕设、大作业中快速落地项目的同学参考。1. 为什么“基于STM32的Marlin三轴机械臂”不是简单编译烧录期末大作业答辩时老师问过最多的一个问题“Marlin是给3D打印机写的固件你怎么让它去控制机械臂”如果你只回答“改配置”八成会被追问到卡壳。Marlin内核里的G代码解析、运动规划、步进电机控制都是现成的但它不知道自己拖动的轴是转动关节还是平移直线。这篇文章从三轴机械臂的关节控制这个需求出发把硬件选型、运动学逆解、Marlin移植、速度规划边界和调参方法逐层讲透最后落到一条可执行的路径上让上位机负责逆解让STM32上的Marlin负责出力。这篇博文的读者定位是正在做STM32相关期末大作业或毕业设计的同学假设你手里有一块普通开发板、三台步进电机或舵机以及愿意熬夜调参的耐心。2. 三轴机械臂的硬件架构与STM32最小系统2.1 舵机臂还是步进臂先按机械结构选驱动方案三轴机械臂从驱动方式上分两种主流做法。一种是舵机直接驱动关节常见于教学用小型机械臂舵机转角由PWM占空比决定控制代码简单但精度和重复定位能力有限重载下容易抖动。另一种是步进电机加减速器驱动关节位置由脉冲数决定没有累计误差适合做轨迹演示类的大作业。Marlin这两类都能管步进轴走的是内置planner的梯形加减速舵机走的是M280指令或SERVO_ANGLES的PWM扩展。从期末大作业的容错率讲我更倾向于让你先用舵机臂打通流程再用步进臂做精化。因为舵机臂的逆解即使有微小误差舵机自己咬合住不会累计而步进臂一旦丢步整条轨迹都会往后偏移。下面是两者在三个维度的直观对比对比项舵机直接驱动步进电机加减速器位置反馈无开环PWM开环脉冲计数可加编码器闭环控制系统定时器PWM定时器脉冲方向引脚精度约1到2度取决于细分数和减速比Marlin适配成本需要额外写舵机扩展原生步进轴支持典型成本几十元SG90/MG996R42步进电机加驱动板如果你的机械臂是打印件或者铝型材小臂臂长在20厘米以内用MG996R级别的舵机就够了。但要注意普通舵机的死区电压和抖动会让末端定位出现肉眼可见的震颤答辩演示时很掉分。所以大家都默认步进方案更“像工程”但代价是机械结构要留出电机安装位和同步带轮。2.2 STM32主控选型与晶振电容参数STM32的型号选择直接影响Marlin能不能跑起来。Marlin 2.x对STM32F103和STM32F407都有现成支持F103的RAM只有48到64KB跑完整版Marlin加LCD容易吃力建议关闭不需要的模块F407性能强很多但如果你的大作业只是三轴联动F103性价比已经足够。市面上还有一种做法是直接选APM32F103或GD32F103它们和STM32F103的引脚和寄存器基本兼容PlatformIO里仍然按stm32f103目标编译烧录器选ST-Link或者J-Link都能识别。晶振部分反而是很多同学忽略的坑。STM32F103最小系统常用8MHz无源晶振加两个负载电容电容值不是随便挑的。负载电容C_L和两个匹配电容C1、C2的关系是$$C_L \frac{C1 \times C2}{C1 C2} C_{\rm stray}$$$C_{\rm stray}$是PCB走线和芯片引脚引入的杂散电容经验值在3到5pF。如果晶振规格书要求C_L18pF取C_stray4pF则C1C2要接近25到30pF市面上常用27pF。如果把负载电容选成22pF再配18pF的晶振起振时间会变长极端情况下Marlin启动时串口打印卡死在时钟初始化之前你拿到手的源码再怎么改配置都没用。2.3 Marlin在STM32上的启动流程概貌Marlin在STM32上的启动流程和裸机程序不一样它是跑在HAL层之上的。先从SystemInit配置时钟树然后进入Marlin的setup()这个setup里依次完成串口初始化、EEPROM参数加载、步进电机引脚初始化、运动缓冲区planner_init、以及热端和风扇等外设初始化。任何一步出错串口上可能只输出一行字符就挂起。调试时先在main.cpp里加打印确认启动到哪一步再沿这个方向查配置这比瞎猜有用得多。3. 运动学逆解从末端坐标到三个关节角度3.1 DH参数与正运动学建模三轴机械臂最常见的构型是底座旋转关节加肩关节加肘关节末端还有一个固定姿态的抓手。DH参数是把每个关节的转动和平移通过四个量描述出来的标准方法。对这样一个三轴臂可以用标准DH表建模关节 i$\theta_i$$d_i$$a_i$$\alpha_i$1$\theta_1$$d_1$$a_1$$\pi/2$2$\theta_2$0$a_2$03$\theta_3$0$a_3$0$a_1$是底座到肩关节的水平偏置$a_2$是大臂长度$a_3$是小臂长度。正运动学就是已知三个$\theta$求末端位置这个用三角函数手推也行先算机体坐标下的水平距离$r a_2\cos\theta_2 a_3\cos(\theta_2\theta_3)$然后$x r\cos\theta_1$$y r\sin\theta_1$$z d_1 a_2\sin\theta_2 a_3\sin(\theta_2\theta_3)$。3.2 三轴逆运动学的C语言实现期末大作业真正要用的是逆解给定机械臂末端要到达的$x,y,z$反推出三个关节角。这个逆解有解析解不涉及数值迭代适合在STM32另一个核、上位机或者Python脚本里算好再喂给Marlin。下面是C语言版本的核心函数// 三段式三轴机械臂逆解输入末端位置输出三个关节角 // 返回值0表示在可达空间内-1表示超限 int inverse_kinematics(float x, float y, float z, float *theta1, float *theta2, float *theta3) { const float d1 100.0f; // 底座高度 mm const float a2 120.0f; // 大臂长度 mm const float a3 100.0f; // 小臂长度 mm *theta1 atan2f(y, x); // 底座转角 float r sqrtf(x*x y*y); // 水平投影距离 float s z - d1; // 竖直方向目标高度 float d sqrtf(r*r s*s); // 末端到肩关节距离 // 距离合法性检查三角形两边之和大于第三边 if (d (a2 a3) || d fabsf(a2 - a3)) { return -1; } float alpha atan2f(s, r); // 目标方向与水平面的夹角 float cos_beta (a2*a2 d*d - a3*a3) / (2.0f * a2 * d); float beta acosf(cos_beta); // 大臂与射线的夹角 *theta2 alpha beta; // 肩关节角 float cos_gamma (a2*a2 a3*a3 - d*d) / (2.0f * a2 * a3); float gamma acosf(cos_gamma); *theta3 -(PI - gamma); // 肘关节角负号表示屈肘方向 return 0; }这段代码里最关键的是第二条余弦定理的用法先求末端到肩关节的距离$d$利用余弦定理求出大臂与连线的夹角$\beta$然后让$\theta_2 \alpha\beta$。$\alpha$是末端矢量的仰角$\beta$补偿大臂自身方向两者相加就是肩关节相对垂直轴的实际转角。很多同学会直接把$\alpha$当$\theta_2$结果末端高度整体偏低一截。肘关节角$\theta_3$用第三条边关系求注意角度符号要跟实际机械装配方向一致。3.3 末端笛卡尔坐标与Marlin虚拟轴的映射Marlin自己没有机械臂关节模型它只知道X、Y、Z三个轴各自前进多少毫米。所以这里有两种做法。第一种是直接给关节臂做“轴映射”把底座轴当X、肩关节当Y、肘关节当ZG1指令里的X值就是底座角度的度数。这种方法实现最快但缺陷很明显Marlin会对三个虚拟轴做线性插值导致关节空间到末端空间的轨迹严重变形画圆时末端走成椭圆形。我更推荐的第二种做法是在PC或另一颗MCU上先对目标轨迹做小步长离散每个点都调用一次逆解把得到的关节角写进G代码文件再烧到Marlin里“盲跑”。也就是说逆解发生在上位机Marlin只忠实地执行运动规划。把这段话写进答辩文档里老师基本就不会再问机械臂轨迹失真这类基础问题了。4. 将Marlin 2.x移植到STM32的最小可运行配置4.1 源码骨架与板级描述文件Marlin 2.x的源码结构里和硬件强相关的部分集中在Marlin/src/pins/目录下。每个板子对应一个pins_xxx.h里面定义了步进电机引脚、使能引脚、限位开关引脚和串口的映射。你要做的第一件事不是改Configuration.h而是找到接近你手里PCB的板级文件。以最常见的RAMPS 1.4结构为例MDA、MDB和MDC三组排针映射到STM32的不同GPIO。如果你自己画板或者用转接板需要新写一个pins_custom.h里面按现有模板把X_STEP_PIN、X_DIR_PIN、X_ENABLE_PIN这些宏改成自己的引脚号。这个过程建议用逻辑分析仪实测因为不少国产板的丝印标号和PCB走线对不上只看原理图会烧驱动板。4.2 用PlatformIO编译并烧录STM32目标Marlin从2.0开始推荐用PlatformIO做构建系统它比Keil省心的地方在于会自动拉取正确的HAL和工具链。下面是一个最简的platformio.ini示例针对STM32F103ZET6[env:stm32f103zet6] platform ststm32 board genericSTM32F103ZE framework arduino upload_protocol stlink build_flags -DHAL_CAN_MODULE_ENABLED ; 关闭热床等无关外设以减小Flash占用编译前先确认Configuration.h里SERIAL_PORT设为1或2对应你实际使用的串口否则G代码发送过去Marlin没有任何反应。烧录用ST-Link最常见upload_protocol stlink表示通过SWD接口写入接线只要SWDIO、SWCLK、GND和3.3V四根线。第一次烧录如果提示Target not found优先检查BOOT0引脚电平而不是怀疑源码。4.3 Configuration.h里必改的关键参数Configuration.h是Marlin里最敏感的文件参数改错轻则不动重则在上电瞬间烧驱动。以下这几个宏是三轴机械臂跑通前必须校准的// 控制板型号直接影响pins_xxx.h的加载 #define MOTHERBOARD BOARD_RAMPS_14_EFB // 每个轴单位脉冲对应的移动量或角度 #define DEFAULT_AXIS_STEPS_PER_UNIT { 80, 80, 80, 445 } // 最大进给率步进臂这里先保守一点 #define DEFAULT_MAX_FEEDRATE { 200, 200, 200, 30 } // 加速度加速度过大容易导致机械结构振动 #define DEFAULT_MAX_ACCELERATION { 300, 300, 300, 100 }DEFAULT_AXIS_STEPS_PER_UNIT是步进电机细分和后级减速比的综合结果。计算公式是驱动细分数乘以电机步距角对应的脉冲数再除以减速比和传动比。比如42步进电机1.8度16细分则一圈需要3200个脉冲如果输出轴有1:5减速器那么输出轴转一圈是16000个脉冲对应360度那么每度约44.44个脉冲。这个值如果不准逆解算得再精确机械臂末端也会越走越偏。DEFAULT_MAX_FEEDRATE设成200意味着每秒最多200个“单位”在你确认步进电机的实际能力前宁小勿大。提示修改完Configuration.h后重新编译第一次上电最好用手捏住机械臂关节防止参数错误导致关节猛甩撞到限位结构。5. 速度是怎么被规划的加减速边界与PID的适用位置5.1 开环步进轴不需要PID但需要加速度规划很多同学一听到运动控制就想到PID但在裸步进驱动的三轴机械臂里Marlin的planner并不对位置环做PID计算。它做的是梯形加减速一个运动段被分成加速、匀速、减速三段每步脉冲的间隔时间按下一帧的目标速度计算出来。PID如果要介入只能出现在闭环步进驱动器的电流环或者舵机的位置反馈环而不是Marlin的用户态。所以你看Marlin源码会发现它有三组关键缓存block_buffer里保存了每个运动段的起始速度和结束速度、加速度、脉冲总数。planner每次收到G1指令就计算能不能在当前速度和下一段速度之间完成加减速不能就主动降速。这种规划方式的好处是CPU开销极小适合F103这种主频不高的MCU。5.2 用M503、M203、M201和M566读取并调整运动参数Marlin的EEPROM里保存着运动参数不需要重新编译就能在线调整。下列指令在串口终端里可以随时执行M503 ; 打印当前所有参数 M203 X200 Y200 Z200 ; 设置最大进给率 M201 X300 Y300 Z300 ; 设置最大加速度 M204 P500 T1000 ; 设置打印和移动加速度 M566 X10 Y10 Z10 ; 设置瞬时速度变化上限即jerk M500 ; 保存到EEPROMM566的jerk参数对机械臂影响很大。jerk是相邻两个运动段之间的速度突变上限设太大会在关节上形成冲击设太小会让轨迹变得绵软无力。对于三轴机械臂我一般从5到20开始试观察哪个值会让机架产生明显共振然后回退到共振点以下的70%。这些参数改完不需要重启直接重新发送G1指令就生效非常适合现场调。5.3 机械臂用闭环舵机时自己补一个位置PID如果换用带角度反馈的舵机或直流减速电机方案Marlin原生就不够用了你需要在STM32的定时器中断里自己补位置环。最简的位置PID代码如下// 位置环PID控制周期1ms输入目标角度和目标当前角度 #define KP 2.5f #define KI 0.05f #define KD 0.1f static float integral 0.0f, prev_error 0.0f; float pid_update(float target, float current) { float error target - current; integral error * 0.001f; if (integral 1.0f) integral 1.0f; // 积分限幅 if (integral -1.0f) integral -1.0f; float derivative (error - prev_error) / 0.001f; prev_error error; return KP * error KI * integral KD * derivative; }这里控制周期固定为1毫秒用大臂角度作为反馈量输出PWM占空比。调试顺序是先使KP让舵机能回正再逐步加KD抑制过冲最后加KI消除稳态误差。注意反馈源最好用磁编码器或电位器不要用舵机内部的模拟电压直接进ADC噪声会让PID输出抖成一团。6. 实战增量用Python生成画圆G代码并让机械臂动起来6.1 上位机逆解后按小线段下发画圆是机械臂轨迹演示的常见项目。因为Marlin不懂关节角正确的姿势是在PC端用Python逐点计算逆解后生成G代码文件import math a2 120.0 # 大臂长度 a3 100.0 # 小臂长度 d1 100.0 # 底座高度 def ik(x, y, z): theta1 math.atan2(y, x) r math.hypot(x, y) s z - d1 d math.hypot(r, s) if d a2 a3 or d abs(a2 - a3): return None alpha math.atan2(s, r) beta math.acos((a2*a2 d*d - a3*a3) / (2*a2*d)) theta2 alpha beta gamma math.acos((a2*a2 a3*a3 - d*d) / (2*a2*a3)) theta3 -(math.pi - gamma) return theta1, theta2, theta3 with open(circle.gcode, w) as f: f.write(G21\n) # 让每个关节先回到基准角度 j0, j1, j2 ik(100.0, 0.0, 180.0) f.write(fG1 X{j0:.3f} Y{j1:.3f} Z{j2:.3f} F300\n) # 画半径20mm的圆每3度一个点 for deg in range(0, 361, 3): x 120.0 20.0 * math.cos(math.radians(deg)) y 20.0 * math.sin(math.radians(deg)) z 180.0 j0, j1, j2 ik(x, y, z) f.write(fG1 X{j0:.3f} Y{j1:.3f} Z{j2:.3f} F500\n)这段脚本把末端坐标换算成关节角后以X、Y、Z名义坐标的形式记录。发送到Marlin后planner会对这三个“角度轴”做直线插补由于每段只有3度机械臂末端在真实空间里近似走圆。如果要追求高精度轨迹需要把步长缩小到1度甚至更细代价是G代码体积变大但F103处理几百个G1指令完全不成问题。6.2 上电归零与软限位机械臂第一次上电不归零关节角可能停在任意位置直接执行G代码会甩飞末端。Marlin里有两种归零方式传统限位开关和软件限位。多数三轴大作业买不到合适的光电限位也来不及设计支架这时我建议直接改Configuration.h里的SOFT_ENDSTOPS#define SOFT_ENDSTOPS #define MIN_SOFT_ENDSTOP_X -90 #define MAX_SOFT_ENDSTOP_X 90 #define MIN_SOFT_ENDSTOP_Y 0 #define MAX_SOFT_ENDSTOP_Y 170 #define MIN_SOFT_ENDSTOP_Z -45 #define MAX_SOFT_ENDSTOP_Z 120软限位的作用是在planner里拦截超出范围的G1指令避免电机撞机。但软限位不解决关节初始位置未知问题所以更稳妥的做法是在机械臂每个关节上留一个手动对准刻度上电后用G92命令把当前角度设成零点。这个操作在答辩里也说得通毕竟工业机械臂也有手动示教零点这一工序。如果一定要自动归零就在底座加一个微动开关让Marlin的HOME流程驱动步进电机往负方向找开关这个改起来要动pins文件里的限位引脚定义。6.3 让源码包看起来像“高分”作业的三个细节大作业源码里除了工程文件和任性改过的Configuration.h之外最好补一份调参记录表记录每组加速度和jerk下的轨迹变形现象这是工程能力最强的证据。其次逆解代码和Marlin移植代码要分开放在不同目录README里写清楚从上位机到固件的完整数据流这样老师能一眼看懂你的系统设计。最后把可执行文件的编译固件版本和PlatformIO环境锁进platformio.ini避免答辩时在对方电脑上重新编译出现依赖不一致。真正做到这一步你就可以对齐老师最关心的可复现性给他同一套源码、同一份编译环境就能复现你的整套机械臂运动过程。本文还有配套的精品资源点击获取
返回列表