1. 项目概述:基于STM32F103C8T6的写字机器人开发实录
去年冬天在电子垃圾堆里翻出几个旧光驱,拆解时发现里面的步进电机居然还能用,于是萌生了做个写字机器人的想法。这个项目从机械结构设计到控制程序编写全部自主完成,主控选用STM32F103C8T6这款性价比极高的Cortex-M3内核芯片,整体成本控制在200元以内。下面我将从硬件设计、运动控制算法、PCB布局、软件调试四个维度详细拆解实现过程,特别会重点说明那些在教程里找不到的实战经验。
2. 硬件系统设计
2.1 机械结构搭建
十字滑台采用经典的XY双轴结构,其中X轴使用8mm光轴配合直线轴承实现平滑移动,Y轴则选用T型丝杆传动确保定位精度。这里有几个关键设计细节:
-
材料选择:框架使用3mm亚克力板激光切割,相比金属框架更轻量化且易于加工。但要注意亚克力板的热膨胀系数较大(约0.07mm/m·℃),在温差超过10℃的环境需要重新校准。
-
电机选型:X轴采用回收的DVD光驱步进电机(28BYJ-48型),Y轴使用标准的42步进电机。实测发现光驱电机虽然扭矩较小(约0.25N·m),但在配合减速齿轮后完全能满足水平移动需求。
-
装配公差:导轨安装孔必须比实际尺寸大0.5mm,这是用三块亚克力板报废换来的教训。过紧的配合会导致运动阻力增大,影响书写流畅度。
重要提示:丝杆两端一定要加装角接触轴承,普通深沟球轴承在轴向受力时会产生明显回差,导致画圆时出现明显的"棱角"现象。
2.2 电路设计要点
主控电路围绕STM32F103C8T6搭建,核心外设包括:
-
电机驱动:放弃常见的ULN2003方案,改用专业级的A4988步进电机驱动模块。这个选择基于三点考虑:
- 支持1/16微步进,理论精度可达0.1125度
- 内置过流保护(最大2A输出)
- 仅需STEP/DIR两个信号即可控制
-
电源设计:
- 主控部分采用AMS1117-3.3稳压芯片
- 电机驱动单独使用12V/2A开关电源
- 关键改进:在A4988的VMOT引脚并联470μF电解电容+100nF陶瓷电容组合,实测可将电机启停时的电压波动抑制在5%以内
-
信号隔离:步进电机驱动信号通过74HC245缓冲器与MCU隔离,避免电机干扰导致MCU复位。这是经过多次死机后总结的经验。
3. 运动控制实现
3.1 核心算法解析
采用改进型Bresenham直线插补算法,相比标准算法主要优化了三点:
- 整数运算优化:避免STM32F103的软浮点性能瓶颈
- 速度自适应:根据线段长度动态调整步进间隔
- 误差补偿:引入累计误差修正机制
关键代码实现(经过实际验证的稳定版本):
c复制typedef struct {
int32_t current_pos[2]; // X,Y当前坐标
int32_t target_pos[2]; // 目标坐标
uint16_t step_delay; // 步进间隔(us)
} MotionState;
void bresenham_line(MotionState *m) {
int32_t dx = abs(m->target_pos[0] - m->current_pos[0]);
int32_t dy = -abs(m->target_pos[1] - m->current_pos[1]);
int32_t sx = m->current_pos[0] < m->target_pos[0] ? 1 : -1;
int32_t sy = m->current_pos[1] < m->target_pos[1] ? 1 : -1;
int32_t err = dx + dy, e2; // 误差项
while(1) {
if(m->current_pos[0]==m->target_pos[0] &&
m->current_pos[1]==m->target_pos[1]) break;
stepper_move_to(m->current_pos[0], m->current_pos[1]);
delay_us(m->step_delay);
e2 = 2 * err;
if(e2 >= dy) { // X轴步进
err += dy;
m->current_pos[0] += sx;
}
if(e2 <= dx) { // Y轴步进
err += dx;
m->current_pos[1] += sy;
}
}
}
3.2 圆弧插补实现
圆弧采用中点画圆算法,关键点在于:
- 使用定时器硬件PWM生成步进脉冲(TIM1_CH1/CH2)
- 开启自动重载功能避免脉冲丢失
- 动态调整步进速率实现恒线速控制
配置代码示例:
c复制// TIM1 PWM配置
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71; // 1MHz计数频率
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 1000; // 初始1kHz
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // 关键配置!
HAL_TIM_PWM_Init(&htim1);
4. 软件系统设计
4.1 G代码解析器
通过串口接收Inkscape生成的G代码,设计了一个带错误校验的解析器:
- 环形缓冲区:256字节容量,支持115200bps全速接收
- 指令缓存:支持Look-ahead预处理
- 运动队列:三级流水线实现连续运动
关键数据结构:
c复制typedef struct {
char cmd; // G/M代码
float params[8]; // 参数值
uint8_t checksum; // 校验和
} GCodeCommand;
#define QUEUE_SIZE 16
typedef struct {
GCodeCommand queue[QUEUE_SIZE];
uint8_t head, tail, count;
} MotionQueue;
4.2 速度规划算法
采用S型加减速曲线,避免步进电机失步:
c复制void calculate_s_curve(float distance, float max_v, float a) {
float t1 = max_v / a; // 加速时间
float s1 = 0.5 * a * t1 * t1; // 加速段距离
if(2*s1 <= distance) { // 能达到最大速度
float t2 = (distance - 2*s1) / max_v; // 匀速段时间
// 三段式速度规划
} else { // 三角形速度曲线
t1 = sqrt(distance / a);
max_v = a * t1;
// 两段式速度规划
}
}
5. 调试经验与优化技巧
5.1 常见问题排查
-
画圆有棱角:
- 检查丝杆反向间隙(应<0.1mm)
- 确认TIM1的ARPE位已使能
- 增加插补精度(减小步进间隔)
-
书写断墨:
- 调整笔架弹簧压力(建议50-100g)
- 检查Z轴抬笔高度(建议2-3mm)
- 降低运动速度(建议<50mm/s)
-
电机发热严重:
- 减小A4988的VREF电压(建议0.8-1.2V)
- 添加散热片或风扇
- 启用电机空闲时自动半流模式
5.2 性能优化记录
通过以下优化将书写速度提升40%:
- 将GPIO操作改为寄存器直接写入:
c复制// 优化前
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, SET);
// 优化后
GPIOA->BSRR = GPIO_PIN_1;
- 使用DMA传输步进脉冲序列:
c复制[HAL](https://taotoken.net/?utm_source=hardware)_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)pulse_buf, len);
- 开启STM32的预取缓冲和指令缓存:
c复制__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
6. 项目扩展方向
当前系统预留了三个升级接口:
- Z轴压力控制:通过FSR压力传感器实现毛笔书写
- 无线控制:添加ESP-01S WiFi模块支持远程传输
- 视觉校准:配合OpenMV实现自动对位
机械结构方面,下一步计划改用2020铝型材框架,配合直线导轨提升刚性。控制算法上正在试验B样条曲线插补,以实现更流畅的连笔字效果。
