# 时间预估补充问题分析 > **文档版本**: 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轴最大加速度) ```cpp // 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(挤出时最大加速度) ```cpp // 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加速度更低? 1. **打印质量考虑** - 高加速度会导致振动(ringing/ghosting) - 影响外壁质量和尺寸精度 - 可能导致层间粘合问题 2. **机械限制** - XYZ轴移动的惯性更大 - 打印头/打印床的质量较大 - 需要考虑整机的刚性 3. **挤出一致性** - 高加速度会导致挤出量不均匀 - 影响压力提前(pressure advance)效果 - 可能产生过挤/欠挤 ### 1.4 在时间预估中的使用 **位置**: `src/libslic3r/GCode/GCodeProcessor.cpp` ```cpp // Line 1002-1014: 加载配置 const ConfigOptionFloats* max_acceleration_extruding = config.option("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` ```cpp 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) ```cpp // Line 2845-2849 for (unsigned char a = X; a <= E; ++a) { float axis_max_jerk = get_axis_max_jerk(..., static_cast(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) ```cpp // 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计算 ```cpp // 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(a)); if (jerk > axis_max_jerk) { v_factor *= axis_max_jerk / jerk; // 降速 limited = true; } ``` **含义**: - E轴的jerk单独计算 - 区分同向运动(coasting)和反向运动(reversal) - 反向运动的jerk更严格 ### 2.4 Jerk对时间的影响 **影响机制**: 1. **降低连接速度** → 增加加速/减速时间 ``` 示例: - 无jerk限制:连接速度100 mm/s - 有jerk限制:连接速度降至50 mm/s - 结果:需要更长的加速/减速时间 ``` 2. **降低巡航速度** → 增加总移动时间 ``` 示例: - 目标速度:150 mm/s - jerk限制导致入口速度:30 mm/s - 结果:加速段更长,可能无法达到目标速度 ``` 3. **影响梯形速度曲线** ``` 无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` ```cpp if (line == "[change_filament_gcode]") { // BBS if (!m_single_extruder_multi_material) { extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); // If the extruder offset changed, add an extra move if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { 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` ```cpp // 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是否会被统计? **答案:会被统计** **原因**: 1. **换料gcode被完整插入到G-code文件中** ```cpp // Line 507-597 toolchange_gcode_str = gcodegen.placeholder_parser_process( "change_filament_gcode", change_filament_gcode, new_extruder_id, ...); ``` 2. **GCodeProcessor会解析所有G-code行** ```cpp // GCodeProcessor::process_gcode_line() 会处理所有行 // 包括change_filament_gcode中的M109 ``` 3. **M109会触发process_M109()函数** - 位置:`GCodeProcessor.cpp:3640-3655`(当前实现) - 如果应用修复方案,会计算等待时间并调用`simulate_st_synchronize()` ### 3.4 示例 **用户的换料gcode配置**: ```gcode M109 S{new_filament_temp[next_extruder]} ; 等待新工具温度 G1 E10 F300 ; 挤出少量材料 ``` **生成的实际gcode**(假设温度220°C): ```gcode M109 S220 ; 等待新工具温度 ⬅️ 这行会被GCodeProcessor处理 G1 E10 F300 ``` **时间统计**: - **当前实现**: M109不会添加等待时间(bug) - **修复后**: M109会计算等待时间 - 如果有M104预热:计算剩余等待时间(可能0-20秒) - 如果无预热:计算完整等待时间(可能20-40秒) ### 3.5 注意事项 ⚠️ **重要**: 如果用户的换料gcode中有M109,修复方案会统计这个时间 **建议**: 1. 检查U1的默认`change_filament_gcode`配置 2. 确认是否包含M109 3. 如果包含,修复后时间估算会更准确 4. 如果不包含,需要确认换料过程是否在其他地方等待温度 --- ## 问题4:GCode.cpp中的M400是否会被统计到时间?如何计算? ### 4.1 U1的M400使用场景 **位置**: `src/libslic3r/GCode.cpp:6377-6380` ```cpp // 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` ```cpp 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() } ``` **关键点**: 1. **有参数的M400**(如`M400 S1`或`M400 P100`) - 会调用`simulate_st_synchronize(value_s)` - 添加额外的等待时间 - 示例:`M400 P100` → 添加0.1秒等待时间 2. **无参数的M400**(如`M400\n`) - **不会**调用`simulate_st_synchronize()` - **不会**添加额外的等待时间 - 只会触发现有移动块的完成(但这部分时间已经在移动块计算中) ### 4.4 U1的M400时间统计 **答案:U1的M400不会添加额外等待时间** **原因**: 1. U1添加的是`M400\n`(无参数) 2. `process_M400()`检测到无S和P参数 3. 不会调用`simulate_st_synchronize()` 4. **只会同步现有移动,不会添加额外时间** ### 4.5 M400的实际作用 虽然不添加额外时间,但M400仍然有重要作用: **在时间估算中**: ```cpp 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的使用 **扫描模型场景**(搜索结果中发现): ```cpp 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的影响 **影响方式**: 1. 限制单轴的安全速度 2. 限制相邻移动的连接速度 3. 降低连接速度 → 增加加速/减速时间 → 增加总打印时间 **典型影响**: 直角转弯时,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修复方案的建议 1. **确认U1的change_filament_gcode配置** - 检查是否包含M109 - 如果包含,修复后会更准确 2. **M400不影响修复方案** - U1的M400不添加时间 - 修复方案可以正常实施 3. **Jerk配置建议** - 检查U1的jerk配置是否合理 - 如果时间估算仍有偏差,可能是jerk配置问题 ### 测试建议 1. **验证换料gcode中的M109** ```bash # 检查生成的G-code中换料部分的M109 grep -A10 "T1" output.gcode | grep M109 ``` 2. **验证M400不影响时间** ```bash # 检查M400是否有参数 grep "M400" output.gcode ``` 3. **验证jerk影响** ```bash # 对比不同jerk配置下的时间估算 ```