为实验移液自动化修改 Marlin 固件

Main project image

本文记录液体处理机器人中的 Marlin 固件改造部分。

Marlin 本来是一套面向 3D 打印机的开源固件,常见在笛卡尔结构的 FDM 设备上。它负责的事情很底层,包括 G-code 解析、运动规划、步进驱动、限位、串口通信、屏幕和 SD 卡这些基础功能。对这种“底层本质上还是一台多轴运动平台”的改造项目来说,它已经提供了不少可以直接接着用的控制基础。

这套系统的底层硬件是改造后的 3D 打印机运动平台,控制板换成了 SKR Mini E3 V3.0,并在原本 XYZ 的基础上增加了一根驱动注射器活塞的 U 轴。固件层没有从零重写运动控制系统,而是基于 Marlin 现成的运动规划、G-code 执行和 TMC 驱动支持进行适配。

内容主要包括轴语义修改、板级引脚映射、运动参数重定义,以及编译后刷回控制板的流程。

这份固件修改是为下面这个移液机器人项目服务的。整机的机械结构、上位机和实验结果都放在项目页里。

移液机器人项目封面

基于 3D 打印机改造的低成本自动化液体处理系统

上位机、云端协议生成、G-code 下发和 XYZU 四轴执行之间的关系。Marlin 在这里主要承担的是底层运动控制和硬件接口。
液体处理平台的软件与控制架构图

上位机、云端协议生成、G-code 下发和 XYZU 四轴执行之间的关系。Marlin 在这里主要承担的是底层运动控制和硬件接口。

平台保留了 XYZ 空间定位,并增加了一根驱动注射器活塞的 U 轴。这根额外执行轴是整次固件修改的核心。
XYZU 四轴液体处理平台示意图

平台保留了 XYZ 空间定位,并增加了一根驱动注射器活塞的 U 轴。这根额外执行轴是整次固件修改的核心。

为什么选择 Marlin

对于实验原型而非通用控制器开发,Marlin 已经提供了多项可复用能力:

因此固件改造的重点不是补齐底层基础设施,而是移除 Marlin 默认属于 3D 打印机的假设,并把现有能力重新映射到液体处理平台的控制链路上。

第一步:将机器语义改成 XYZU

Marlin 默认最熟悉的机器模型是 XYZ + E。但对这台平台来说,原本的 E0 不再是挤出机,而是一根推动注射器的线性轴。所以最先要改的不是速度,而是“这台机器到底是什么”。

Configuration.h 中启用额外驱动轴,并将其对外显示成 U 轴:

#define X_DRIVER_TYPE  TMC2209
#define Y_DRIVER_TYPE  TMC2209
#define Z_DRIVER_TYPE  TMC2209
#define I_DRIVER_TYPE  TMC2209

#ifdef I_DRIVER_TYPE
  #define AXIS4_NAME 'U'
#endif

#define EXTRUDERS 0

这里的 TMC2209 指的是步进电机驱动芯片类型。Marlin 里的 X_DRIVER_TYPEY_DRIVER_TYPE 这些定义,本质上是在告诉固件每根轴接的是什么驱动。如果控制板使用的不是 TMC2209,而是 A4988DRV8825 或其他 TMC 型号,这里也需要改成对应的驱动类型,后面的 UART、电流和归零配置才会匹配。

其中有两个关键点。

第一,Marlin 内部额外轴的命名是 I/J/K/U/V/W 这套扩展轴体系,不一定直接等于最终对外暴露的字母。所以这里实际上是启用了 I_DRIVER_TYPE,再把 AXIS4_NAME 改成 'U',这样上层可以用 U 来理解这根轴,但底层还是沿着 Marlin 的额外轴机制工作。

第二,EXTRUDERS 被改成了 0。这一步很重要,因为它等于明确告诉固件:这台机器已经不是“有热端、有耗材、有挤出”的打印机。很多后续配置,包括温度、挤出保护和动作语义,都是在这个前提上继续调整的。

对应地,热端和热床传感器也需要关闭,避免固件继续带着打印机的加热假设:

#define TEMP_SENSOR_0 0
#define TEMP_SENSOR_BED 0
#define EXTRUDE_MINTEMP 0
#define EXTRUDE_MAXLENGTH 600

EXTRUDE_MINTEMP 0EXTRUDE_MAXLENGTH 600 这两项看起来还带着“extrude”这个词,但本质上是在清掉 Marlin 对挤出动作的保护假设,避免它误把 U 轴动作当作热端未升温下的非法挤出。

第二步:把 E0 驱动真正复用成 U 轴

语义改完之后,真正决定能不能跑起来的,是板级引脚映射。

SKR Mini E3 V3.0 本身是一块典型的打印机控制板,默认提供 X/Y/Z/E0 四个驱动位。在不更换更大控制板的前提下增加一根执行轴,直接方案是复用 E0 驱动资源。

项目中最核心的一段改动位于板级 pins 文件:

#define E0_STOP_PIN PC15

#ifndef I_MIN_PIN
  #define I_MIN_PIN E0_STOP_PIN
#endif

#ifndef I_STEP_PIN
  #define I_STEP_PIN E0_STEP_PIN
#endif
#ifndef I_DIR_PIN
  #define I_DIR_PIN E0_DIR_PIN
#endif
#ifndef I_ENABLE_PIN
  #define I_ENABLE_PIN E0_ENABLE_PIN
#endif

#ifndef I_SERIAL_TX_PIN
  #define I_SERIAL_TX_PIN E0_SERIAL_TX_PIN
#endif
#ifndef I_SERIAL_RX_PIN
  #define I_SERIAL_RX_PIN E0_SERIAL_RX_PIN
#endif
#ifndef I_CS_PIN
  #define I_CS_PIN E0_CS_PIN
#endif
#ifndef I_HARDWARE_SERIAL
  #define I_HARDWARE_SERIAL E0_HARDWARE_SERIAL
#endif

这段映射把 Marlin 眼里的 I 轴,直接接到板卡原本给 E0 预留的步进、方向、使能、UART 和 endstop 资源上。这样一来,控制板不需要换,驱动芯片也不需要换,但在固件层它已经不再被当成挤出机,而是一根额外的线性轴。

这类改法的好处是资源利用率高,缺点是语义层和板级层必须同时理顺。只改 Configuration.h 不够,只改 pins 也不够,必须两边一起改,否则会出现“逻辑上有 U 轴、硬件上映射不到”或者“硬件上映射了、上层还把它当 E 轴”的错位。

这块板原生就是打印机场景,所以额外执行轴本质上是在复用它给 E0 预留的资源。
SKR Mini E3 V3.0 控制板

这块板原生就是打印机场景,所以额外执行轴本质上是在复用它给 E0 预留的资源。

机械上对应的是一套注射器推进机构。固件里把它抽象成 U 轴之后,上位机就可以按线性轴去控制吸液和分液动作。
注射器轴机构 CAD 图

机械上对应的是一套注射器推进机构。固件里把它抽象成 U 轴之后,上位机就可以按线性轴去控制吸液和分液动作。

第三步:重新定义行程、步数、速度和归零

把轴接出来以后,下一步就是让这台机器按液体处理平台的方式去动,而不是按打印机的方式去动。

最终保留的几组核心参数如下:

#define USE_IMIN_PLUG

#define DEFAULT_AXIS_STEPS_PER_UNIT { 80, 80, 800, 4000 }
#define DEFAULT_MAX_FEEDRATE       { 500, 500, 500, 500 }
#define DEFAULT_MAX_ACCELERATION   { 500, 500, 100, 100 }
#define DEFAULT_ACCELERATION       500
#define DEFAULT_RETRACT_ACCELERATION 500
#define DEFAULT_TRAVEL_ACCELERATION  500

#define I_HOME_DIR -1
#define I_MIN_POS 0
#define I_MAX_POS 100

#define X_BED_SIZE 305
#define Y_BED_SIZE 305
#define Z_MAX_POS 605

#define HOMING_FEEDRATE_MM_M { (20*60), (20*60), (4*60), (2*60) }

需要重点关注的参数包括:

除了速度和行程之外,方向也要重新对齐。配置中对 Y/Z/I 的方向进行了重新定义:

#define INVERT_Y_DIR false
#define INVERT_Z_DIR true
#define INVERT_I_DIR true
#define I_HOME_DIR -1

这类值没有固定标准答案,需要结合实际机械装配方向、限位位置和移动结果逐项校准。更稳妥的方式是一次只放开一根轴,确认方向、步数和限位都正确后,再继续向下调试。

第四步:把 TMC2209 和驱动参数调顺

在这套改造里,驱动层的工作量不算小,因为它不只是“能转”就结束了,还要考虑 UART 地址、电流、无感归零和步进表现。

最终保留的几组驱动配置如下:

#define X_CURRENT 580
#define Y_CURRENT 580
#define Z_CURRENT 750
#define I_CURRENT 750

#define X_SLAVE_ADDRESS 0
#define Y_SLAVE_ADDRESS 2
#define Z_SLAVE_ADDRESS 1
#define I_SLAVE_ADDRESS 3

#define CHOPPER_TIMING CHOPPER_DEFAULT_24V
#define SENSORLESS_HOMING
#define X_STALL_SENSITIVITY 75
#define Y_STALL_SENSITIVITY 50
#define I_STALL_SENSITIVITY 75
#define IMPROVE_HOMING_RELIABILITY
#define SQUARE_WAVE_STEPPING

这部分里最容易踩坑的通常是三类值:

sensorless homing 在资料中通常看起来很直接,但落到具体机器上仍然需要逐轴调试。不同的皮带张力、负载、导轨摩擦和供电条件,都会让这一组参数发生明显变化。

图中是原打印机保留下来的 Y 轴皮带传动结构。U 轴机构最后安装在这套原有运动平台上,因此固件调试也需要和这部分机构一起配合来看。
改造后的液体处理平台局部照片

图中是原打印机保留下来的 Y 轴皮带传动结构。U 轴机构最后安装在这套原有运动平台上,因此固件调试也需要和这部分机构一起配合来看。

第五步:把构建裁成只保留需要的部分

platformio.ini 的裁剪也是本次改造中的一项重要工作。

构建目标没有直接沿用整套 Marlin 默认模块,而是压缩成一个更接近“运动控制器”的集合。核心配置思路如下:

[platformio]
default_envs = STM32G0B1RE_btt

[common]
default_src_filter = +<src/*> -<src/config> -<src/tests>
  -<src/feature>
  -<src/gcode/bedlevel>
  -<src/gcode/probe>
  -<src/gcode/sd>
  -<src/module>
  -<src/module/stepper>
  +<src/gcode/calibrate/G28.cpp>
  +<src/gcode/config/M92.cpp>
  +<src/gcode/motion/G0_G1.cpp>
  +<src/module/endstops.cpp>
  +<src/module/motion.cpp>
  +<src/module/planner.cpp>
  +<src/module/stepper.cpp>
  +<src/module/temperature.cpp>

这里的思路不是“追求极端瘦身”,而是让编译结果更贴近这台机器真正会用到的能力。

这样做的作用包括:

这类裁剪并不是越狠越好。更稳妥的方式是先让完整链路跑通,再回头删功能。否则一开始就大面积裁模块,最后遇到问题时很难判断是参数错误,还是依赖文件被一起删掉。

还有一些配套修改

前面几节主要是控制链路本身。除此之外,还调整了几组更偏“配套”的设置,主要和串口、界面、状态上报以及风扇管理有关。

#define SHOW_CUSTOM_BOOTSCREEN
#define CUSTOM_STATUS_SCREEN_IMAGE
#define SERIAL_PORT 2
#define BAUDRATE 115200
#define SERIAL_PORT_2 -1
#define ENDSTOP_INTERRUPTS_FEATURE
#define EEPROM_SETTINGS
#define SDSUPPORT
#define INDIVIDUAL_AXIS_HOMING_MENU
#define CR10_STOCKDISPLAY

这几项里比较实际的有几类:

Configuration_adv.h 里还有一组和散热、上报有关的配置:

#define USE_CONTROLLER_FAN
#define E0_AUTO_FAN_PIN FAN1_PIN
#define AUTO_REPORT_POSITION
#define M114_DETAIL
#define HOST_ACTION_COMMANDS

编译与刷写

编译与刷写是验证配置修改的最后步骤。前面的固件配置需要生成实际固件并刷回控制板,才能进入硬件验证。

platformio.ini 中的目标环境设为 STM32G0B1RE_btt,编译也围绕这个环境生成固件:

[platformio]
default_envs = STM32G0B1RE_btt

如果直接用 PlatformIO,命令大致就是:

pio run -e STM32G0B1RE_btt

编译完成之后会生成 firmware.bin。对 SKR Mini E3 V3.0 这种板子,比较直接的刷写方式就是:

  1. firmware.bin 拷到 SD 卡根目录
  2. 断电状态下把 SD 卡插回控制板
  3. 重新上电,让板载 bootloader 自动读取并刷写

通常刷写完成后,这个文件会被改名成 FIRMWARE.CUR,或者不再保持原来的 firmware.bin 文件名。这个流程不需要额外下载器,适合配置修改后的快速验证。

小结

这次修改里真正重要的部分,主要还是这几件事:把 E0 资源改造成 U 轴、把机器语义从 XYZ + E 改成 XYZU、重新定义运动和归零参数、把 TMC2209 的 UART 与无感归零调通,再把最终固件编成 firmware.bin 刷回控制板。

在这个项目中,Marlin 更像是一个已经成熟的底层执行框架。本次修改的主要工作,是把它从打印机场景重新适配到实验移液自动化平台真正需要的那一组功能上。