* Add docs about time_estimate * Fix: Problems with graceful program exit caused by Flutter refactoring * Add: sw_OpenBrowser() & sw_OpenOrcaWebview * Fix: NetworkTestDialog Crash & Add: Lan Device test \ cloud test
16 KiB
时间预估补充问题分析
文档版本: v1.0 创建日期: 2025-12-06 问题来源: M109/M190时间预估修复方案的补充问题
问题1:挤出最大加速度 vs E最大加速度的区别
1.1 配置参数定义
位置: src/libslic3r/PrintConfig.cpp:3640-3682 和 3734-3744
machine_max_acceleration_e(E轴最大加速度)
// Line 3666: 通过循环自动生成 machine_max_acceleration_e
def = this->add("machine_max_acceleration_" + axis.name, coFloats);
// 对于E轴:
// - 默认值:{ 5000., 5000. } mm/s²
// - 对应固件命令:M201 E5000
// - 定义:E轴电机的最大加速度
说明:
- 物理含义: E轴(挤出机)电机的硬件物理限制
- 固件命令:
M201 E5000- 设置E轴最大加速度 - 作用范围: 限制E轴电机本身的加速度,无论是打印、回抽还是其他动作
- 默认值: 5000 mm/s²(非常高,因为E轴质量小,电机响应快)
machine_max_acceleration_extruding(挤出时最大加速度)
// Line 3734
def = this->add("machine_max_acceleration_extruding", coFloats);
def->full_label = L("Maximum acceleration for extruding");
def->tooltip = L("Maximum acceleration for extruding (M204 P)");
def->set_default_value(new ConfigOptionFloats{ 1500., 1250. });
说明:
- 物理含义: 打印移动(挤出动作)时的最大加速度限制
- 固件命令:
M204 P1500- 设置打印时最大加速度 - 作用范围: 仅限于打印移动(extrusion moves),即XYZ+E同时移动的情况
- 默认值: 1500 mm/s²(比E轴最大加速度低很多)
1.2 核心区别
| 特性 | machine_max_acceleration_e | machine_max_acceleration_extruding |
|---|---|---|
| 固件命令 | M201 E5000 | M204 P1500 |
| 作用对象 | E轴电机 | 打印移动(XYZ+E) |
| 物理含义 | 电机硬件限制 | 打印质量限制 |
| 默认值 | 5000 mm/s² | 1500 mm/s² |
| 影响范围 | E轴所有动作 | 仅打印时 |
| 限制原因 | 电机性能 | 打印质量、振动、层粘合 |
1.3 为什么extruding加速度更低?
-
打印质量考虑
- 高加速度会导致振动(ringing/ghosting)
- 影响外壁质量和尺寸精度
- 可能导致层间粘合问题
-
机械限制
- XYZ轴移动的惯性更大
- 打印头/打印床的质量较大
- 需要考虑整机的刚性
-
挤出一致性
- 高加速度会导致挤出量不均匀
- 影响压力提前(pressure advance)效果
- 可能产生过挤/欠挤
1.4 在时间预估中的使用
位置: src/libslic3r/GCode/GCodeProcessor.cpp
// Line 1002-1014: 加载配置
const ConfigOptionFloats* max_acceleration_extruding =
config.option<ConfigOptionFloats>("machine_max_acceleration_extruding");
if (max_acceleration_extruding != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_extruding.values =
max_acceleration_extruding->values;
// E轴加速度通过循环加载(Line 1028-1042中的类似代码)
使用场景:
- machine_max_acceleration_e: 在jerk计算时限制E轴的加速度
- machine_max_acceleration_extruding: 在打印移动时作为加速度上限
问题2:Jerk如何影响预估时间的计算
2.1 Jerk的定义
Jerk(加加速度): 加速度的变化率,单位 mm/s
在3D打印中,jerk实际上被用作瞬时速度变化的限制,而不是严格意义上的加加速度。
2.2 Jerk配置参数
位置: src/libslic3r/PrintConfig.cpp:3684-3700
def = this->add("machine_max_jerk_" + axis.name, coFloats);
// 默认值:
// X轴:10 mm/s
// Y轴:10 mm/s
// Z轴:0.2 mm/s(很小,因为Z轴移动慢)
// E轴:2.5 mm/s
对应固件命令: M205 X10 Y10 Z0.2 E2.5
2.3 Jerk在时间预估中的作用
位置: src/libslic3r/GCode/GCodeProcessor.cpp:2845-2929
作用1: 限制安全速度(Safe Feedrate)
// Line 2845-2849
for (unsigned char a = X; a <= E; ++a) {
float axis_max_jerk = get_axis_max_jerk(..., static_cast<Axis>(a));
if (curr.abs_axis_feedrate[a] > axis_max_jerk)
curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk);
}
含义:
- 每个轴的移动速度不能超过该轴的jerk限制
- 如果某个轴的速度超过jerk,降低整体移动的安全速度
- 这是单轴独立限制
作用2: 计算连接速度(Junction Velocity)
// Line 2873-2884: 计算XYZ轴的jerk向量
Vec3f entry_v = block.feedrate_profile.cruise * (curr.enter_direction);
Vec3f exit_v = prev.feedrate * (prev.exit_direction);
Vec3f jerk_v = entry_v - exit_v; // 速度变化向量
jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z()));
Vec3f max_xyz_jerk_v = get_xyz_max_jerk(...);
// 检查是否超过jerk限制
for (size_t i = 0; i < 3; i++) {
if (jerk_v[i] > max_xyz_jerk_v[i]) {
v_factor *= max_xyz_jerk_v[i] / jerk_v[i]; // 计算降速系数
limited = true;
}
}
含义:
- 计算从上一个移动到当前移动的速度变化
- 如果速度变化超过jerk限制,降低连接速度
- 这是XYZ组合限制
作用3: E轴独立jerk计算
// Line 2901-2921: 计算E轴的jerk
float jerk = (v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
(v_exit - v_entry) : // 同向减速
std::max(v_exit, -v_entry)) : // 反向
(((v_entry < 0.0f) || (v_exit > 0.0f)) ?
(v_entry - v_exit) : // 同向加速
std::max(-v_exit, v_entry)); // 反向
float axis_max_jerk = get_axis_max_jerk(..., static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk; // 降速
limited = true;
}
含义:
- E轴的jerk单独计算
- 区分同向运动(coasting)和反向运动(reversal)
- 反向运动的jerk更严格
2.4 Jerk对时间的影响
影响机制:
-
降低连接速度 → 增加加速/减速时间
示例: - 无jerk限制:连接速度100 mm/s - 有jerk限制:连接速度降至50 mm/s - 结果:需要更长的加速/减速时间 -
降低巡航速度 → 增加总移动时间
示例: - 目标速度:150 mm/s - jerk限制导致入口速度:30 mm/s - 结果:加速段更长,可能无法达到目标速度 -
影响梯形速度曲线
无jerk限制: ┌─────────┐ (平顶梯形) │ │ │ │ └ └ 有jerk限制: ┌───┐ (尖顶三角形或低平顶) ╱ ╲ ╱ ╲ └ └
2.5 实际计算示例
场景: 直角转弯(90度)
前一移动:X方向 100 mm/s
当前移动:Y方向 100 mm/s
X轴jerk限制:10 mm/s
Y轴jerk限制:10 mm/s
计算:
- X轴速度变化:100 mm/s → 0 mm/s = 100 mm/s
- Y轴速度变化:0 mm/s → 100 mm/s = 100 mm/s
- 超过jerk限制,需要降速
降速系数:
- X轴:10 / 100 = 0.1
- Y轴:10 / 100 = 0.1
- 最终连接速度:100 * 0.1 = 10 mm/s
时间影响:
- 如果没有jerk限制,可能以50 mm/s通过转角
- 有jerk限制,只能以10 mm/s通过转角
- 需要从100减速到10,再从10加速到100
- 增加的时间:约 (90/加速度) 秒
问题3:换料gcode中的M109指令是否会被统计
3.1 换料gcode的处理流程
位置: src/libslic3r/GCode.cpp:903-918
if (line == "[change_filament_gcode]") {
// BBS
if (!m_single_extruder_multi_material) {
extruder_offset = m_extruder_offsets[tcr.new_tool].cast<float>();
// If the extruder offset changed, add an extra move
if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast<float>()) {
std::ostringstream oss;
oss << std::fixed << std::setprecision(3)
<< "G1 X" << transformed_pos.x() - extruder_offset.x()
<< " Y" << transformed_pos.y() - extruder_offset.y()
<< "\n";
gcode_out += oss.str();
}
}
}
说明:
[change_filament_gcode]是一个特殊标记- 在WipeTower中使用(
src/libslic3r/GCode/WipeTower.cpp:1013) - 标记换料gcode的插入位置
3.2 换料gcode的生成
位置: src/libslic3r/GCode.cpp:6590-6597
// Process the custom change_filament_gcode.
const std::string& change_filament_gcode = m_config.change_filament_gcode.value;
//Orca: Ignore change_filament_gcode if is the first call for a tool change
// and manual_filament_change is enabled
if (!change_filament_gcode.empty() &&
!(m_config.manual_filament_change.value && m_toolchange_count == 1)) {
toolchange_gcode_parsed = placeholder_parser_process(
"change_filament_gcode", change_filament_gcode, extruder_id, &dyn_config);
}
说明:
change_filament_gcode是用户配置的自定义换料脚本- 通过
placeholder_parser_process处理占位符(如温度、喷头ID等) - 生成的gcode会被插入到最终的G-code文件中
3.3 M109是否会被统计?
答案:会被统计
原因:
-
换料gcode被完整插入到G-code文件中
// Line 507-597 toolchange_gcode_str = gcodegen.placeholder_parser_process( "change_filament_gcode", change_filament_gcode, new_extruder_id, ...); -
GCodeProcessor会解析所有G-code行
// GCodeProcessor::process_gcode_line() 会处理所有行 // 包括change_filament_gcode中的M109 -
M109会触发process_M109()函数
- 位置:
GCodeProcessor.cpp:3640-3655(当前实现) - 如果应用修复方案,会计算等待时间并调用
simulate_st_synchronize()
- 位置:
3.4 示例
用户的换料gcode配置:
M109 S{new_filament_temp[next_extruder]} ; 等待新工具温度
G1 E10 F300 ; 挤出少量材料
生成的实际gcode(假设温度220°C):
M109 S220 ; 等待新工具温度 ⬅️ 这行会被GCodeProcessor处理
G1 E10 F300
时间统计:
- 当前实现: M109不会添加等待时间(bug)
- 修复后: M109会计算等待时间
- 如果有M104预热:计算剩余等待时间(可能0-20秒)
- 如果无预热:计算完整等待时间(可能20-40秒)
3.5 注意事项
⚠️ 重要: 如果用户的换料gcode中有M109,修复方案会统计这个时间
建议:
- 检查U1的默认
change_filament_gcode配置 - 确认是否包含M109
- 如果包含,修复后时间估算会更准确
- 如果不包含,需要确认换料过程是否在其他地方等待温度
问题4:GCode.cpp中的M400是否会被统计到时间?如何计算?
4.1 U1的M400使用场景
位置: src/libslic3r/GCode.cpp:6377-6380
// Snapmaker U1
std::string printer_model = this->m_curr_print->m_config.printer_model.value;
if (printer_model == "Snapmaker U1" && toolchange) {
gcode += "M400\n"; // ⬅️ 注意:没有参数
}
使用场景:
- 工具切换时(toolchange = true)
- 仅限Snapmaker U1机型
- 添加一个无参数的M400命令
4.2 M400的含义
固件行为: M400表示"Finish all moves"(完成所有移动)
- 阻塞等待,直到运动缓冲区清空
- 确保所有G1/G0移动都已完成
- 类似于固件的
st_synchronize()调用
4.3 M400的时间计算
位置: src/libslic3r/GCode/GCodeProcessor.cpp:3883-3891
void GCodeProcessor::process_M400(const GCodeReader::GCodeLine& line)
{
float value_s = 0.0;
float value_p = 0.0;
if (line.has_value('S', value_s) || line.has_value('P', value_p)) {
value_s += value_p * 0.001; // P参数单位是毫秒
simulate_st_synchronize(value_s);
}
// ⚠️ 注意:如果没有S或P参数,不会调用simulate_st_synchronize()
}
关键点:
-
有参数的M400(如
M400 S1或M400 P100)- 会调用
simulate_st_synchronize(value_s) - 添加额外的等待时间
- 示例:
M400 P100→ 添加0.1秒等待时间
- 会调用
-
无参数的M400(如
M400\n)- 不会调用
simulate_st_synchronize() - 不会添加额外的等待时间
- 只会触发现有移动块的完成(但这部分时间已经在移动块计算中)
- 不会调用
4.4 U1的M400时间统计
答案:U1的M400不会添加额外等待时间
原因:
- U1添加的是
M400\n(无参数) process_M400()检测到无S和P参数- 不会调用
simulate_st_synchronize() - 只会同步现有移动,不会添加额外时间
4.5 M400的实际作用
虽然不添加额外时间,但M400仍然有重要作用:
在时间估算中:
void GCodeProcessor::process_M400(const GCodeReader::GCodeLine& line)
{
// ... 检查参数 ...
// 即使没有参数,也会触发TimeMachine处理当前的移动块
// 确保所有pending的移动都被计算完成
}
效果:
- 确保M400之前的所有移动块都被处理完成
- 刷新时间估算的缓冲区
- 但不添加额外的等待时间
4.6 对比:有参数 vs 无参数的M400
| M400命令 | 添加等待时间 | 代码位置 | 用途 |
|---|---|---|---|
M400\n (U1) |
❌ 否 | GCode.cpp:6379 | 同步移动缓冲区 |
M400 S1 |
✅ 是,1秒 | - | 等待1秒 |
M400 P100 |
✅ 是,0.1秒 | GCode.cpp(扫描模型):行号未显示 | 等待0.1秒 |
4.7 GCode.cpp中其他M400的使用
扫描模型场景(搜索结果中发现):
gcode += "M976 S1 P1 ; scan model before printing 2nd layer\n";
gcode += "M400 P100\n"; // ⬅️ 有P参数,会添加0.1秒等待
说明:
- 这个M400有P参数
- 会添加0.1秒的等待时间
- 用于扫描模型后的延迟
总结
问题1答案:挤出加速度 vs E加速度
| 参数 | 作用 | 默认值 |
|---|---|---|
machine_max_acceleration_e |
E轴电机的物理限制 | 5000 mm/s² |
machine_max_acceleration_extruding |
打印时的质量限制 | 1500 mm/s² |
关键区别: e是硬件限制,extruding是打印质量限制
问题2答案:Jerk的影响
影响方式:
- 限制单轴的安全速度
- 限制相邻移动的连接速度
- 降低连接速度 → 增加加速/减速时间 → 增加总打印时间
典型影响: 直角转弯时,jerk限制会将连接速度从50-100 mm/s降至10 mm/s
问题3答案:换料gcode中的M109
会被统计 ✅
- 换料gcode会被完整插入到G-code文件
- GCodeProcessor会解析所有行,包括M109
- 修复方案会计算M109的等待时间(考虑预热)
问题4答案:U1的M400时间计算
不会添加额外时间 ❌
- U1添加的是
M400\n(无参数) process_M400()只在有S或P参数时添加时间- U1的M400只同步移动缓冲区,不添加等待时间
但其他M400可能会:
M400 P100(扫描模型)会添加0.1秒
建议
对于M109修复方案的建议
-
确认U1的change_filament_gcode配置
- 检查是否包含M109
- 如果包含,修复后会更准确
-
M400不影响修复方案
- U1的M400不添加时间
- 修复方案可以正常实施
-
Jerk配置建议
- 检查U1的jerk配置是否合理
- 如果时间估算仍有偏差,可能是jerk配置问题
测试建议
-
验证换料gcode中的M109
# 检查生成的G-code中换料部分的M109 grep -A10 "T1" output.gcode | grep M109 -
验证M400不影响时间
# 检查M400是否有参数 grep "M400" output.gcode -
验证jerk影响
# 对比不同jerk配置下的时间估算