# 补充问题的详细答案 > **日期**: 2025-12-06 > **问题来源**: 时间预估分析的进一步确认 --- ## 问题1:E最大加速度5000 vs 挤出最大加速度20000,实际使用哪个? ### 快速答案 **会使用5000**(取最小值) ### 详细分析 #### 配置场景 ``` machine_max_acceleration_e = 5000 mm/s² (E轴电机硬件限制) machine_max_acceleration_extruding = 20000 mm/s² (打印时加速度限制) ``` #### 代码执行流程 **位置**: `src/libslic3r/GCode/GCodeProcessor.cpp` ##### 步骤1: 初始化(Line 770-773) ```cpp // Line 771: 读取machine_max_acceleration_extruding配置 float max_acceleration = get_option_value( m_time_processor.machine_limits.machine_max_acceleration_extruding, i); // max_acceleration = 20000 // Line 772-773: 设置到machines[i] m_time_processor.machines[i].max_acceleration = max_acceleration; // 20000 m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION; // 20000 ``` 此时:`machines[i].acceleration = 20000` ##### 步骤2: 计算移动块加速度(Line 2827-2838) ```cpp // Line 2827-2831: 获取基础加速度 float acceleration = get_acceleration(static_cast(i)); // acceleration = 20000 (从machines[i].acceleration读取) // 🔥 关键步骤:Line 2834-2838 // 检查每个轴的最大加速度限制 for (unsigned char a = X; a <= E; ++a) { float axis_max_acceleration = get_axis_max_acceleration(..., static_cast(a)); // 对于E轴:axis_max_acceleration = 5000 // 计算这个轴的实际加速度分量 // acceleration * |delta_pos[a]| / distance if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration) acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance); // acceleration被降低以满足E轴限制 } // Line 2840 block.acceleration = acceleration; // 最终加速度 ``` ##### 步骤3: get_axis_max_acceleration函数 **位置**: Line 4850-4862 ```cpp float GCodeProcessor::get_axis_max_acceleration( PrintEstimatedStatistics::ETimeMode mode, Axis axis) const { switch (axis) { case X: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_x, ...); } case Y: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_y, ...); } case Z: { return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_z, ...); } case E: { // 🔥 关键:E轴返回machine_max_acceleration_e return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_e, ...); // 返回 5000 } default: { return 0.0f; } } } ``` ### 实际计算示例 **场景**:打印移动(XYZ+E同时运动) ``` 初始配置: - machine_max_acceleration_extruding = 20000 mm/s² - machine_max_acceleration_e = 5000 mm/s² 移动参数: - 距离: 100mm - XY移动: 99mm - E挤出: 5mm - inv_distance = 1/100 = 0.01 计算过程: 1. acceleration = 20000 (从extruding配置) 2. 检查E轴限制: - E轴分量加速度 = 20000 * 5 * 0.01 = 1000 mm/s² - E轴最大 = 5000 mm/s² - 1000 < 5000,满足 ✓ 3. 但如果E挤出量更大(如30mm): - E轴分量加速度 = 20000 * 30 * 0.01 = 6000 mm/s² - E轴最大 = 5000 mm/s² - 6000 > 5000,超限! - 调整:acceleration = 5000 / (30 * 0.01) = 16666.7 mm/s² 4. 最终使用: 16666.7 mm/s² (被E轴限制降低) ``` ### 结论 **多重限制机制**: 1. **初始限制**: `machine_max_acceleration_extruding`(20000) 2. **轴向限制**: 每个轴的`machine_max_acceleration_*`(E轴5000) 3. **最终结果**: 取决于移动的轴向分量 **简化规则**: - 对于**纯E轴移动**(回抽/回退):直接受E轴5000限制 - 对于**XYZ+E移动**(打印): - 如果E分量小:可能接近20000 - 如果E分量大:会被降低以满足5000限制 - **实际加速度 ≤ min(20000, 5000/E轴比例)** --- ## 问题2:Jerk怎么参与计算的 ### 完整计算流程 #### 阶段1: 计算安全速度(Safe Feedrate) - Line 2842-2849 ```cpp // 初始化为巡航速度 curr.safe_feedrate = block.feedrate_profile.cruise; // 假设150 mm/s // 🔥 检查每个轴的jerk限制 for (unsigned char a = X; a <= E; ++a) { float axis_max_jerk = get_axis_max_jerk(..., static_cast(a)); // X: 10 mm/s, Y: 10 mm/s, Z: 0.2 mm/s, E: 2.5 mm/s if (curr.abs_axis_feedrate[a] > axis_max_jerk) // 如果当前轴速度超过jerk,降低安全速度 curr.safe_feedrate = std::min(curr.safe_feedrate, axis_max_jerk); } // 示例: // X轴速度: 120 mm/s > jerk 10 → safe_feedrate = 10 mm/s // 最终: curr.safe_feedrate = 10 mm/s ``` **目的**: 限制当前块能够安全达到的最大速度 #### 阶段2: 设置出口速度 - Line 2851 ```cpp block.feedrate_profile.exit = curr.safe_feedrate; // 10 mm/s ``` **目的**: 确保当前块的出口速度不超过安全速度 #### 阶段3: 计算连接速度(Junction Velocity) - Line 2856-2929 这是**最复杂的部分**,涉及三个步骤: ##### 步骤3.1: XYZ向量jerk检查(Line 2868-2884) ```cpp // 前一块的出口速度向量 Vec3f exit_v = prev.feedrate * prev.exit_direction; // 假设: 100 mm/s 向X方向 = (100, 0, 0) // 当前块的入口速度向量 Vec3f entry_v = block.feedrate_profile.cruise * curr.enter_direction; // 假设: 100 mm/s 向Y方向 = (0, 100, 0) // 计算速度变化向量(jerk向量) Vec3f jerk_v = entry_v - exit_v; // jerk_v = (0, 100, 0) - (100, 0, 0) = (-100, 100, 0) jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z())); // jerk_v = (100, 100, 0) // 获取XYZ最大jerk Vec3f max_xyz_jerk_v = get_xyz_max_jerk(...); // max_xyz_jerk_v = (10, 10, 0.2) // 检查是否超限 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]; // i=0 (X): v_factor *= 10/100 = 0.1 // i=1 (Y): v_factor *= 10/100 = 0.1 (再次降低) // 最终 v_factor = 0.01 limited = true; } } ``` **物理意义**: 限制XYZ空间中的速度变化,防止机械冲击 ##### 步骤3.2: E轴独立jerk检查(Line 2889-2922) ```cpp // 对于E轴(a = E) float v_exit = prev.axis_feedrate[E]; // 前一块的E速度: 5 mm/s float v_entry = curr.axis_feedrate[E]; // 当前块的E速度: 10 mm/s // 应用XYZ的v_factor if (limited) { v_exit *= v_factor; // 5 * 0.01 = 0.05 mm/s v_entry *= v_factor; // 10 * 0.01 = 0.1 mm/s } // 计算E轴的jerk(区分同向和反向) float jerk; if (v_exit > v_entry) { // 减速 if ((v_entry > 0.0f) || (v_exit < 0.0f)) { jerk = v_exit - v_entry; // 同向减速 } else { jerk = std::max(v_exit, -v_entry); // 反向 } } else { // 加速 if ((v_entry < 0.0f) || (v_exit > 0.0f)) { jerk = v_entry - v_exit; // 同向加速: 0.1 - 0.05 = 0.05 } else { jerk = std::max(-v_exit, v_entry); // 反向 } } // 检查E轴jerk限制 float axis_max_jerk = get_axis_max_jerk(..., E); // 2.5 mm/s if (jerk > axis_max_jerk) { v_factor *= axis_max_jerk / jerk; // 0.05 < 2.5,不需要进一步限制 limited = true; } ``` **物理意义**: 限制挤出机的速度变化,防止挤出不均匀 ##### 步骤3.3: 应用最终v_factor(Line 2925-2926) ```cpp if (limited) vmax_junction *= v_factor; // vmax_junction = 150 * 0.01 = 1.5 mm/s ``` #### 阶段4: 设置入口速度 - Line 2963 ```cpp block.feedrate_profile.entry = vmax_junction; // 1.5 mm/s ``` ### 可视化示例:直角转弯 ``` 场景: - 前一移动:X方向 100 mm/s - 当前移动:Y方向 100 mm/s - X/Y jerk: 10 mm/s 计算: ┌─────────────────────────────────────────┐ │ 1. 速度向量 │ │ exit_v = (100, 0, 0) │ │ entry_v = (0, 100, 0) │ │ jerk_v = (100, 100, 0) │ ├─────────────────────────────────────────┤ │ 2. jerk限制检查 │ │ X: 100 > 10 → v_factor = 10/100=0.1│ │ Y: 100 > 10 → v_factor = 0.1*0.1=0.01│ ├─────────────────────────────────────────┤ │ 3. 最终连接速度 │ │ vmax_junction = 100 * 0.01 = 1 mm/s│ └─────────────────────────────────────────┘ 速度曲线: 前一块 当前块 100 mm/s ┐ ┌ 100 mm/s │\ /│ │ \ / │ │ \ / │ │ \ / │ 1 mm/s └────\ /────┘ └─┘ 连接点(1 mm/s) 没有jerk限制的理想情况: 100 mm/s ┐ ┌────┬────┐ │ / \ │ │ / \ │ 50 mm/s └─┘ └─┘ 连接点(50 mm/s) ``` ### 对时间的影响 **示例计算**: ``` 假设: - 前一移动100mm,100 mm/s - 当前移动100mm,100 mm/s - 加速度:1000 mm/s² - jerk限制:10 mm/s 无jerk限制(连接速度50 mm/s): - 前一块减速:(100-50)/1000 = 0.05s,距离2.5mm - 前一块总时间:2.5mm/(75mm/s) + 97.5mm/100 = 1.008s - 当前块加速:(100-50)/1000 = 0.05s,距离2.5mm - 当前块总时间:2.5mm/(75mm/s) + 97.5mm/100 = 1.008s - 总计:2.016s 有jerk限制(连接速度1 mm/s): - 前一块减速:(100-1)/1000 = 0.099s,距离5mm - 前一块总时间:5mm/(50.5mm/s) + 95mm/100 = 1.049s - 当前块加速:(100-1)/1000 = 0.099s,距离5mm - 当前块总时间:5mm/(50.5mm/s) + 95mm/100 = 1.049s - 总计:2.098s 时间增加:2.098 - 2.016 = 0.082s (约4%增加) ``` --- ## 问题3:OK 换料gcode中的M109会被统计 ✓ --- ## 问题4:M400 P100的含义 ### P参数的定义 **位置**: `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; // S参数:秒 // P参数:毫秒 if (line.has_value('S', value_s) || line.has_value('P', value_p)) { value_s += value_p * 0.001; // 🔥 P转换为秒:P/1000 simulate_st_synchronize(value_s); } } ``` ### P100的含义 ``` M400 P100 ↑ P参数 = 100毫秒 计算: value_p = 100 value_s = 100 * 0.001 = 0.1秒 结果: simulate_st_synchronize(0.1) // 添加0.1秒等待时间 ``` ### 是否会记录时间? **答案:会** ✅ **完整流程**: ```cpp // 1. 解析M400 P100 process_M400(line) ↓ // 2. 提取参数 value_p = 100 value_s = 0.1 ↓ // 3. 调用同步 simulate_st_synchronize(0.1) ↓ // 4. 添加到时间估算 for (size_t i = 0; i < machines.size(); ++i) { machines[i].simulate_st_synchronize(0.1); ↓ machines[i].calculate_time(0, 0.1); // distance=0, additional_time=0.1 ↓ // 在时间统计中添加0.1秒 } ``` ### M400参数对比 | 命令 | S参数 | P参数 | 总等待时间 | 记录时间? | |-----|-------|-------|-----------|----------| | `M400` | 0 | 0 | 0秒 | ❌ 否 | | `M400 S1` | 1 | 0 | 1秒 | ✅ 是 | | `M400 P100` | 0 | 100 | 0.1秒 | ✅ 是 | | `M400 S1 P500` | 1 | 500 | 1.5秒 | ✅ 是 | ### 实际使用场景 **GCode.cpp中的扫描模型**: ```cpp gcode += "M976 S1 P1 ; scan model before printing 2nd layer\n"; gcode += "M400 P100\n"; // 等待100毫秒 ``` **目的**: - M976触发模型扫描 - M400 P100确保扫描命令完全执行 - 在时间估算中添加0.1秒 **U1的工具切换**: ```cpp if (printer_model == "Snapmaker U1" && toolchange) { gcode += "M400\n"; // 无参数 } ``` **区别**: - 无参数的M400**不添加额外时间** - 只是同步移动缓冲区 - 确保所有移动完成后再切换工具 --- ## 总结 ### 问题1答案:E加速度限制 **实际使用5000** - 虽然extruding设置为20000,但E轴分量会受到machine_max_acceleration_e (5000)的限制 ### 问题2答案:Jerk计算流程 1. 计算安全速度(限制单轴) 2. 计算XYZ速度变化向量 3. 应用jerk限制降低连接速度 4. 独立检查E轴jerk 5. 设置最终入口/出口速度 ### 问题4答案:M400 P100 - **P = 毫秒数** - **P100 = 100毫秒 = 0.1秒** - **会记录时间** ✅ - 通过`simulate_st_synchronize(0.1)`添加到时间估算 --- ## 关键代码位置总结 | 功能 | 文件 | 行号 | |-----|------|------| | E轴加速度限制检查 | GCodeProcessor.cpp | 2834-2838 | | get_axis_max_acceleration | GCodeProcessor.cpp | 4850-4862 | | Jerk安全速度计算 | GCodeProcessor.cpp | 2842-2849 | | Jerk连接速度计算 | GCodeProcessor.cpp | 2856-2929 | | M400处理 | GCodeProcessor.cpp | 3883-3891 | | U1的M400插入 | GCode.cpp | 6378-6380 |