Marlin 激光与主轴(Cutter)内联功率控制架构深度解析:M3/M4/M5 三种模式、梯形功率补偿与动态功率计算
Marlin 激光与主轴(Cutter)内联功率控制架构深度解析:M3/M4/M5 三种模式、梯形功率补偿与动态功率计算
本篇技术指南以 Marlin 固件仓库中的 docs/Cutter.md 为核心,系统讲解 Marlin 2.0.9.x 及以上版本引入的激光/主轴(Cutter)功率控制体系:从 G 代码可选的三种工作模式(标准、连续内联、动态内联),到规划器(Planner)与步进器(Stepper)中的功率处理链路,再到 LASER_POWER_TRAP 梯形功率补偿与 calc_dynamic_power() 动态功率计算的具体实现。读完本文,你将掌握如何通过 M3/M4/M5 及 I 参数切换激光工作模式、理解 block->laser.power 等内联功率变量在规划器与步进器之间的流转,并能够在 Marlin/Configuration_adv.h 中正确配置激光功率相关的全部选项。
背景:v2.0.9.x 之前的痛点与本次改进
在 Marlin 2.0.9.x 之前,激光的"内联(inline)"功能选项存在明显限制:
- 内联功能选项不启用并重新编译源码就无法生效,运行期无法切换;
- 一旦启用内联选项,基础(标准)功能反而失效,两种模式互斥,用户必须二选一。
v2.0.9.x 引入的新功能彻底改变了这一状况:标准模式与内联模式可以通过 G 代码在运行期随时选择,并且两种模式彼此兼容,无需重新编译。此外还新增了一个实验性的动态(dynamic)模式。而主轴(spindle)相关的运行特性目前仍需要通过宏定义并重新编译来启用。
总体架构:四个全局模式标志与功率处理链路
激光可选功能通过 G 代码、激光/主轴(spindle_laser)、规划器(planner)和步进器(stepper)四套例程中的 4 个全局模式标志来定义。默认模式保持标准激光功能:G 代码被接收、处理并解析,通过 M3、M4、M5 命令决定设置何种模式。当检测到内联模式参数集时,激光功率处理将交由规划器与步进器例程驱动;初始功率值与设置的处理则由 G 代码解析和激光/主轴例程完成。
模式枚举:CutterMode
在 Marlin/src/feature/spindle_laser.h 中定义了完整的模式枚举:
enum CutterMode : int8_t {
CUTTER_MODE_ERROR = -1,
CUTTER_MODE_STANDARD, // M3 power is applied directly and waits for planner moves to sync.
CUTTER_MODE_CONTINUOUS, // M3 or G1/2/3 move power is controlled within planner blocks, set with 'M3 I', cleared with 'M5 I'.
CUTTER_MODE_DYNAMIC // M4 laser power is proportional to the feed rate, set with 'M4 I', cleared with 'M5 I'.
};
全局实例 SpindleLaser cutter 及模式变量 cutter.cutter_mode 定义于 Marlin/src/feature/spindle_laser.cpp,默认值为 CUTTER_MODE_STANDARD。
内联功率数据流
- 内联功率从
block->inline_power变量流入规划器的laser.power(连续功率模式)。 - 若启用了 激光功率陷阱(laser power trap) 特性,则在加速阶段按步逐步递增功率、减速阶段逐步递减功率;否则
laser.power在步进器中按整个运动块的设定值直接使用。 - 另外两组功率设置由 laser power sync(激光功率同步) 和 laser fan power sync(激光风扇功率同步) 特性喂入规划器,二者均通过规划器的同步块位标志(sync block bit flags)完成:
LASER_POWER_SYNC:当位标志匹配时,全局块laser.power值从激光/主轴标准M3 S-Value功率设置更新;- 风扇同步:功率值从风扇 G 代码
S-Value设置更新到规划器block->fan_speed[i]变量。
- 动态内联功率模式:
F-Value进给率设置通过cutter.calc_dynamic_power()处理,并喂入规划器的laser.power值。
无论激光功率值来自哪个来源,最终的激光输出引脚始终由激光/主轴代码更新,具体通过 apply_power(value) 调用设置激光或主轴输出。这一调用允许在传感器故障发生时进行安全的功率控制。
注意:主轴(Spindle)操作目前尚不能通过 G 代码选择模式。
三种工作模式的 G 代码操作
标准模式(CUTTER_MODE_STANDARD,默认)
M3/M4 直接设置功率并等待规划器运动同步。根据 Marlin/src/gcode/control/M3-M5.cpp:
M3——激光开启/功率设置(斜坡功率);主轴顺时针旋转;M4——激光开启/功率设置;主轴逆时针旋转;S<power>——按当前功率单位设置功率,S0关闭激光/主轴;O<power>——以 0-255 PWM 单位设置功率;- 若未定义 PWM 引脚,
M3/M4仅执行开关操作。
在标准模式下,M3/M4 处理前会调用 planner.synchronize() 等待之前的移动命令(G0/G1/G2/G3)完成,再改变功率。
连续内联模式(CUTTER_MODE_CONTINUOUS)
M3 I 启用连续内联功率模式,由规划器处理;功率存储在运动块中,在步进器 ISR 处理块时应用。其行为特性如下:
M3 I启用连续内联功率,由规划器处理;- 内联模式下
M3 S-Value将为后续移动设置功率(例如G1 X10 Y10将以上一个 S-Value 的功率开启激光); M5 I清除内联模式并将功率设为 0;M5将功率输出设为 0 但保留内联模式;- 使用规划器同步的
M3内联 S-Value(LASER_POWER_SYNC)之前,必须先设置M3 I。
动态内联模式(CUTTER_MODE_DYNAMIC)
M4 I 设置动态模式,该模式根据当前进给率计算激光功率 OCR 值。实验性功能,后续版本可能会完善加减速补偿等功率控制。
M5:关闭
M5 的语义在 Marlin/src/gcode/control/M3-M5.cpp 中实现:
M5——将功率输出设为 0(保留内联模式不变);M5 I——清除内联模式并将功率设为 0,随后自动切回标准模式。
实现中 M5 会先执行 planner.synchronize(),然后 cutter.power = 0; cutter.apply_power(0),若当前处于内联模式且带 I 参数,则 cutter.set_enabled(false) 清除内联功率,并将 cutter.cutter_mode 切回 CUTTER_MODE_STANDARD。
流程控制逻辑图
以下流程图展示了代码库中主轴与激光操作的流程控制逻辑(源自 docs/Cutter.md 原图)。
主轴模式逻辑
┌──────────┐ ┌───────────┐ ┌───────────┐
│M3 S-Value│ │Dir !same ?│ │Stepper │
│Spindle │ │stop & wait│ │processes │
┌──┤Clockwise ├──┤ & start ├──┤moves │
┌──────┐ │ │ │ │spindle │ │ │
│G-Code│ │ └──────────┘ └───────────┘ └───────────┘
│Send ├──┤ ┌──────────┐ ┌───────────┐ ┌───────────┐
└──────┘ │ │M4 S-Value│ │Dir !same ?│ │Stepper │
├──┤Spindle ├──┤stop & wait├──┤processes │
│ │Counter │ │& start │ │moves │
│ │Clockwise │ │spindle │ │ │
│ └──────────┘ └───────────┘ └───────────┘
│ ┌──────────┐ ┌────────┐
│ │M5 │ │Wait for│
└──┤Spindle ├──┤move & │
└──────────┘ │disable │
┌──────────┐ ┌──────────┐
Sensors─────┤Fault ├──┤Disable │
└──────────┘ │power │
└──────────┘
主轴方向改变(SPINDLE_CHANGE_DIR)时,若使能 SPINDLE_STOP_ON_DIR_CHANGE,会先停止并等待,再以新方向启动(对应 Marlin/src/feature/spindle_laser.cpp 的 set_reverse())。
激光模式逻辑
┌──────────┐ ┌─────────────┐ ┌───────────┐
│M3,M4,M5 I│ │Set power │ │Stepper │
┌──┤Standard ├──┤Immediately &├──┤processes │
│ │Default │ │wait for move│ │moves │
│ │ │ │completion │ │ │
│ └──────────┘ └─────────────┘ └───────────┘
│ ┌──────────┐ ┌───────────┐ ┌───────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌───────────┐
┌──────┐ │ │M3 I │ │G0,G1,G2,G4│ │Planner │ │Planner │ │Planner fan │ │Planner │ │Stepper │
│G-Code│ │ │Continuous│ │M3 receive │ │sets block │ │sync power │ │sync power │ │trap power │ │uses block │
│Send │ ├──┤Inline ├──┤power from ├──┤power using│ │? process │ │? process │ │? adjust │ │values to │
└──────┘ │ │ │ │S-Value │ │Gx S-Value │ │M3 power │ │fan power │ │for accel/ │ │apply power│
│ └──────────┘ └───────────┘ └───────────┘ └────────────┘ └────────────┘ └────────────┘ └───────────┘
│ ┌──────────┐ ┌───────────┐ ┌────────────────┐ ┌───────────┐
│ │M4 I │ │Gx F-Value │ │Planner │ │Stepper │
│ │Dynamic │ │set power │ │Calc & set block│ │uses block │
└──┤Inline ├──┤or use ├──┤block power ├──┤values to │
│ │ │default │ │using F-Value │ │apply power│
└──────────┘ └───────────┘ └────────────────┘ └───────────┘
┌──────────┐ ┌──────────┐
Sensors─────┤Fault ├──┤Disable │
└──────────┘ │Power │
└──────────┘
连续内联模式下的陷阱功率计算(LASER_POWER_TRAP)
当同时定义了 LASER_FEATURE 与 LASER_POWER_TRAP 时,规划器会对传入的激光功率 S-Value 进行计算:功率被分解并分配到梯形加速与减速运动中。
计算原则
当 laser.power > 0 时:
- 若定义了
SPEED_POWER_MIN,则设置最小功率,并作为laser_power_floor喂入规划器块。在 Marlin/src/module/planner.cpp 中:
#if ENABLED(LASER_FEATURE)
laser_state_t Planner::laser_inline; // Current state for blocks
const uint8_t laser_power_floor = cutter.pct_to_ocr(SPEED_POWER_MIN);
#endif
- 加速阶段基于入口步进速率与巡航步进速率之比计算降低的入口激光功率因子:
block entry laser power = laser power * ( entry step rate / cruise step rate )
初始功率将被设置为不小于 laser_power_floor 或初始功率计算结果。
- 减速阶段基于最终步进速率与巡航步进速率之比计算降低的最终功率因子:
block exit laser power = laser power * ( exit step rate / cruise step rate )
- 一旦确定入口与出口功率值,就将这些值划分为步进增量,在步进器中应用:
trap step power incr_decr = ( cruize power - entry_exit ) / accel_decel_steps
- 陷阱步进在每次加速或减速步进期间递增或递减,直到块执行完毕;步进功率在梯形斜坡推进过程中累加或累减。
规划器代码
以下是 Marlin/src/module/planner.cpp 中的实际实现:
if (block->laser.power > 0) {
NOLESS(block->laser.power, laser_power_floor);
block->laser.trap_ramp_active_pwr = (block->laser.power - laser_power_floor) * (initial_rate / float(block->nominal_rate)) + laser_power_floor;
block->laser.trap_ramp_entry_incr = (block->laser.power - block->laser.trap_ramp_active_pwr) / accelerate_steps;
float laser_pwr = block->laser.power * (final_rate / float(block->nominal_rate));
NOLESS(laser_pwr, laser_power_floor);
block->laser.trap_ramp_exit_decr = (block->laser.power - laser_pwr) / decelerate_steps;
相关块字段定义于 Marlin/src/module/planner.h:
#if ENABLED(LASER_POWER_TRAP)
float trap_ramp_active_pwr; // Laser power level during active trapezoid smoothing
float trap_ramp_entry_incr; // Acceleration per step laser power increment (trap entry)
float trap_ramp_exit_decr; // Deceleration per step laser power decrement (trap exit)
#endif
规划器注释(Marlin/src/module/planner.cpp)还补充了重要边界说明:LASER_POWER_TRAP 适用于所有 Jerk 与 Curve 模式;但基于圆弧的运动(G2/G3)可能存在问题,因为线段通常太小。若功率为 0,则内联模式变量被置零以防止步进器处理,从而允许 G0 或 G28 等无功率移动。
步进器代码
以下是在加速/减速 ISR 中应用陷阱功率的实际实现(Marlin/src/module/stepper.cpp 与 Marlin/src/module/stepper.cpp):
加速阶段:
if (current_block->laser.trap_ramp_entry_incr > 0) {
cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
current_block->laser.trap_ramp_active_pwr += current_block->laser.trap_ramp_entry_incr;
减速阶段:
if (current_block->laser.trap_ramp_exit_decr > 0) {
current_block->laser.trap_ramp_active_pwr -= current_block->laser.trap_ramp_exit_decr;
cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
实际代码中增量乘以 steps_per_isr(每次 ISR 的步数)。巡航阶段则将活动功率直接设置为块功率(Marlin/src/module/stepper.cpp):
if (current_block->laser.trap_ramp_entry_incr > 0) {
current_block->laser.trap_ramp_active_pwr = current_block->laser.power;
cutter.apply_power(current_block->laser.power);
}
这一"加速逐级加、减速逐级减、巡航恒功率"的过程使激光功率随运动速度同步变化,逼近速度梯形曲线——由于功率分辨率有限,这只是近似实现。
动态内联功率计算(calc_dynamic_power)
动态模式根据 F-Value 进给率计算激光功率。该方法使用位移位将功率级别设置为 0-255:简单且快速,并可使用缩放器将激光功率输出平移到指定功率级别。实现位于 Marlin/src/feature/spindle_laser.h:
// Dynamic mode rate calculation
static uint8_t calc_dynamic_power() {
if (feedrate_mm_m > 65535) return 255; // Too fast, go always on
uint16_t rate = uint16_t(feedrate_mm_m); // 16 bits from the G-code parser float input
rate >>= 8; // Take the G-code input e.g. F40000 and shift off the lower bits to get an OCR value from 1-255
return uint8_t(rate);
}
对应原理(源自 Marlin/Configuration_adv.h 的注释):
- 动态模式下任何移动都将使用当前进给率计算激光功率;
- 进给率由移动命令的
F参数设置,如G1 X0 Y10 F6000; - 激光功率通过位移除去 8 个最低有效位计算,二进制上即除以 256;
- 计算得到 0-255 的 OCR 值,超过
F65535的值将被设为 255; - 更精细的功率控制(如加减速补偿)将在未来版本中处理。
动态功率在规划器与步进器中的流转
规划器中(Marlin/src/module/planner.cpp):
case CUTTER_MODE_CONTINUOUS:
block->laser.power = laser_inline.power;
block->laser.status = laser_inline.status;
break;
case CUTTER_MODE_DYNAMIC:
if (cutter.laser_feedrate_changed()) // Only process changes in rate
block->laser.power = laser_inline.power = cutter.calc_dynamic_power();
break;
动态模式仅在进给率变化时重新计算功率,避免不必要的重复计算。步进器中(Marlin/src/module/stepper.cpp):
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC
&& planner.laser_inline.status.isPowered // isPowered flag set on any parsed G1, G2, G3, or G5 move; cleared on any others.
&& current_block // Block may not be available if steps completed
&& cutter.last_block_power != current_block->laser.power // Prevent constant update without change
) {
cutter.apply_power(current_block->laser.power);
cutter.last_block_power = current_block->laser.power;
}
当没有移动块时(!current_block),动态模式会自动 cutter.apply_power(0) 关闭激光(Marlin/src/module/stepper.cpp)。此外 Marlin/src/gcode/gcode.cpp 会在解析到 F 参数时更新 cutter.feedrate_mm_m,供 M4 I 内联移动使用;G0 移动会清除 isPowered 标志并置零内联功率(Marlin/src/gcode/gcode.cpp)。
内联状态标志
power_status_t 定义于 Marlin/src/module/planner.h,三个标志控制内联功率行为:
bool isEnabled:1; // Set to engage the inline laser power output.
bool isPowered:1; // Set on any parsed G1, G2, G3, or G5 powered move, cleared on G0 and G28.
bool isSyncPower:1; // Set on a M3 sync based set laser power, used to determine active trap power
isPowered:在解析任何 G1/G2/G3/G5 功率移动时置位,在 G0/G28 等其他命令时清除;isEnabled:启用内联激光功率输出,由M3 I/M4 I设置,M5 I清除;isSyncPower:在M3同步设置激光功率时置位,用于确定活动陷阱功率。
apply_power:最终功率输出的安全守门员
无论功率来源如何(标准 S-Value、连续内联、动态计算、同步块),最终都通过 apply_power() 写入输出。其完整实现位于 Marlin/src/feature/spindle_laser.cpp:
void SpindleLaser::apply_power(const uint8_t opwr) {
if (enabled() || opwr == 0) { // 0 check allows us to disable where no ENA pin exists
if (opwr == last_power_applied) return; // Test the last power used to improve performance
#if ENABLED(SPINDLE_LASER_USE_PWM)
if (CUTTER_UNIT_IS(RPM) && unitPower == 0)
ocr_off();
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled() || opwr == 0) {
set_ocr(opwr);
isReadyForUI = true;
}
else
ocr_off();
#elif ENABLED(SPINDLE_SERVO)
servo[SPINDLE_SERVO_NR].move(opwr);
#else
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
isReadyForUI = true;
#endif
last_power_applied = opwr;
}
else {
#if PIN_EXISTS(SPINDLE_LASER_ENA)
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE);
#endif
isReadyForUI = false; // Only used for UI display updates.
TERN_(SPINDLE_LASER_USE_PWM, ocr_off());
}
}
要点:
- 支持 PWM(
SPINDLE_LASER_USE_PWM)、伺服(SPINDLE_SERVO)与纯开关三种输出方式; - 通过
last_power_applied缓存避免重复写入相同功率,提升性能; - 传感器故障等异常场景下可安全禁用功率——这正是流程图底部"Fault → Disable Power"分支的实现所在;
cutter.set_enabled()针对不同模式有不同的行为(Marlin/src/feature/spindle_laser.h):- 标准模式:应用
SPEED_POWER_STARTUP(未指定值时)或当前功率; - 连续/动态模式:仅设置内联启用标志,功率保持上次设定;
CUTTER_MODE_ERROR:直接禁用并使功率归零,实现全局功率关闭。
- 标准模式:应用
配置指南(Configuration_adv.h)
激光/主轴功能在 Marlin/Configuration_adv.h 中配置。以下为关键选项及其含义:
基础开关与引脚
//#define SPINDLE_FEATURE // 启用主轴(电主轴)功能
//#define LASER_FEATURE // 启用激光功能
#if ANY(SPINDLE_FEATURE, LASER_FEATURE)
#define SPINDLE_LASER_ACTIVE_STATE LOW // 使能引脚有效电平
#define SPINDLE_LASER_USE_PWM // 使用 PWM 控制速度/功率
#if ENABLED(SPINDLE_LASER_USE_PWM)
#define SPINDLE_LASER_PWM_INVERT false // PWM 极性反转
#define SPINDLE_LASER_FREQUENCY 2500 // (Hz) PWM 频率(AVR/ESP32/LPC 等平台支持)
#endif
功率单位(CUTTER_POWER_UNIT)
M3 S 设置与显示的功率单位可选四种(Marlin/Configuration_adv.h):
| 单位 | S 值范围 | 适用场景 |
|---|---|---|
PWM255 |
S0 - S255 | 激光 PWM |
PERCENT |
S0 - S100 | 百分比 |
RPM |
S0 - S50000 | 主轴转速 |
SERVO |
S0 - S180 | 伺服角度控制 |
功率范围与启动功率
- 激光模式(默认值定义于 Marlin/src/feature/spindle_laser_types.h):
SPEED_POWER_MIN = 0、SPEED_POWER_MAX = 255、SPEED_POWER_STARTUP = 255; - 主轴模式:
SPEED_POWER_MIN = 5000(RPM)、SPEED_POWER_MAX = 30000(RPM)、SPEED_POWER_STARTUP = 25000(RPM); CUTTER_POWER_RELATIVE:使功率相对[SPEED_POWER_MIN...SPEED_POWER_MAX]范围比例输出(如 SuperPID 路由器控制器:S0 = 5000 RPM、S255 = 30000 RPM)。
SPEED_POWER_MIN 同时作为 LASER_POWER_TRAP 的功率下限 laser_power_floor(Marlin/src/module/planner.cpp),防止梯形功率补偿期间出现零功率输出状态。
激光特有选项
#define LASER_SAFETY_TIMEOUT_MS 1000 // (ms) 激光安全超时:无移动一段时间后关闭激光
//#define LASER_POWER_SYNC // 使能 M3 内联功率规划器同步
//#define LASER_POWER_TRAP // 激光功率随运动速率成比例缩放(梯形补偿)
#define LASER_TEST_PULSE_MIN 1 // (ms) 测试脉冲最小时间
#define LASER_TEST_PULSE_MAX 999 // (ms) 测试脉冲最大时间
//#define I2C_AMMETER // 激光 I2C 电流表(INA226)
//#define LASER_COOLANT_FLOW_METER // 激光冷却液流量计
LASER_SAFETY_TIMEOUT_MS 的取值需权衡:过低可能在慢速移动中误关激光,过高则材料可能被点燃。设置时应考虑材料可燃性、切割速率与 G 代码顺序。
内联模式说明(官方注释原文要点)
Marlin/Configuration_adv.h 中的注释完整说明了内联模式语义:
- 任何带
I参数的M3或G1/2/3/5命令均启用连续内联功率模式; - 例如
M3 I启用由规划器处理的连续内联功率,功率存储在移动块中,在步进器 ISR 处理块时应用; M4 I设置动态模式,使用当前进给率计算激光功率 OCR 值;M5 I清除内联模式并将功率设为 0;M5将功率输出设为 0 但保留内联模式。
辅助功能
AIR_EVACUATION(M10-M11控制排烟/鼓风机)与AIR_ASSIST(M8-M9控制辅助吹气)的启用/禁用/切换实现在 Marlin/src/feature/spindle_laser.cpp;SPINDLE_SERVO:用伺服角度控制功率;- 激光控制菜单中的测试点火功能
test_fire_pulse()(Marlin/src/feature/spindle_laser.h)默认以 80% 功率点火testPulse毫秒。
同步块机制:LASER_POWER_SYNC 与风扇同步
LASER_POWER_SYNC 允许在 CUTTER_MODE_CONTINUOUS 下将任何 M3 S-Value 注入块缓冲区,无需等待规划器同步即可提交 M3 激光功率。其同步块位标志 BLOCK_BIT_LASER_PWR 定义于 Marlin/src/module/planner.h。
Marlin/src/module/planner.cpp 的 buffer_sync_block() 是同步机制的入口:
#if ENABLED(LASER_SYNCHRONOUS_M106_M107)
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
#endif
TERN_(LASER_POWER_SYNC, block->laser.power = cutter.power);
步进器在遇到同步块时(Marlin/src/module/stepper.cpp)处理:
#if ENABLED(LASER_POWER_SYNC)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
if (current_block->is_sync_pwr()) {
planner.laser_inline.status.isSyncPower = true;
cutter.apply_power(current_block->laser.power);
}
}
#endif
isSyncPower 标志随后在 Marlin/src/module/stepper.cpp 的块初始化中被检查:若上一个块是 M3 同步功率,则跳过陷阱功率初始化,避免将同步功率清零;否则根据 current_block->laser.status.isEnabled 应用陷阱功率或内联功率。
风扇同步方面,LASER_SYNCHRONOUS_M106_M107 为接在风扇接口的 PWM/TTL 激光器提供更精确的功率时序(相比普通 M106/M107 的"不久后"应用更精准),代价是牺牲部分冷却风扇速度选项(Marlin/Configuration_adv.h)。
安全机制与限制
传感器故障安全
两幅流程图的底部都展示了传感器故障路径:Sensors → Fault → Disable Power。这对应 apply_power() 在故障场景下的安全禁用能力,以及 CUTTER_MODE_ERROR 模式提供的全局功率关闭机制。
当前限制与注意事项
- 主轴模式不可用 G 代码选择——主轴运行特性仍需宏定义与重新编译(文档明确说明);
- 动态模式为实验性——Marlin/src/module/stepper.cpp 注释指出动态模式存在待办项:为各种激光建立 Min/Max OCR 偏移以支持调优与缩放、将加减速速率集成到动态功率计算中;
- LASER_POWER_TRAP 与圆弧移动的兼容性——规划器注释指出基于圆弧(G2/G3)的移动通常因线段过小而存在问题(Marlin/src/module/planner.cpp);
- 内联模式下 G0 等非功率移动——G0 会清除
isPowered标志并调用cutter.inline_power(0)(Marlin/src/gcode/gcode.cpp),但保留内联控制标志本身; - 激光安全超时——
LASER_SAFETY_TIMEOUT_MS在无移动时自动关闭激光,是防止材料点燃的重要防线。
实战速览:一个完整的激光内联切割会话
综合以上机制,一个典型的连续内联激光切割流程如下:
M3 I S50 ; 启用连续内联模式,设置功率 50%(后续移动以此功率执行)
G1 X50 Y0 F6000 ; 以 50% 功率执行切割移动(功率写入规划器块)
M3 S80 ; 内联模式下改变功率(配合 LASER_POWER_SYNC 可无需等待同步)
G1 X100 Y0 F6000; 以 80% 功率继续切割
M5 I ; 清除内联模式并关闭激光,切回标准模式
动态模式(进给率驱动功率)示例:
M4 I ; 启用动态内联模式
G1 X50 Y0 F20000; 进给率 F20000 → 功率 = 20000 >> 8 ≈ 78 (OCR)
G1 X100 Y0 F40000; 进给率 F40000 → 功率 = 40000 >> 8 ≈ 156 (OCR)
M5 I ; 关闭动态模式
如需深入了解实现细节,可继续阅读以下仓库文件:
- 功能核心:Marlin/src/feature/spindle_laser.h 与 Marlin/src/feature/spindle_laser.cpp
- 类型与默认值:Marlin/src/feature/spindle_laser_types.h
- G 代码命令:Marlin/src/gcode/control/M3-M5.cpp
- 规划器实现:Marlin/src/module/planner.cpp 与 Marlin/src/module/planner.h
- 步进器实现:Marlin/src/module/stepper.cpp
- 配置入口:Marlin/Configuration_adv.h