Files
OrcaSlicer/doc/developer-reference/Time_Estimation_Detailed_Answers.md
xiaoyeliu b43cfaaaf9 2.2.0 flutter & WCP & Network Test (#54)
* 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
2025-12-09 10:39:27 +08:00

13 KiB
Raw Blame History

补充问题的详细答案

日期: 2025-12-06 问题来源: 时间预估分析的进一步确认


问题1E最大加速度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
// 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
// Line 2827-2831: 获取基础加速度
float acceleration = get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(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<Axis>(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

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_extruding20000
  2. 轴向限制: 每个轴的machine_max_acceleration_*E轴5000
  3. 最终结果: 取决于移动的轴向分量

简化规则:

  • 对于纯E轴移动(回抽/回退直接受E轴5000限制
  • 对于XYZ+E移动(打印):
    • 如果E分量小可能接近20000
    • 如果E分量大会被降低以满足5000限制
    • 实际加速度 ≤ min(20000, 5000/E轴比例)

问题2Jerk怎么参与计算的

完整计算流程

阶段1: 计算安全速度Safe Feedrate - Line 2842-2849

// 初始化为巡航速度
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<Axis>(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

block.feedrate_profile.exit = curr.safe_feedrate;  // 10 mm/s

目的: 确保当前块的出口速度不超过安全速度

阶段3: 计算连接速度Junction Velocity - Line 2856-2929

这是最复杂的部分,涉及三个步骤:

步骤3.1: XYZ向量jerk检查Line 2868-2884
// 前一块的出口速度向量
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
// 对于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_factorLine 2925-2926
if (limited)
    vmax_junction *= v_factor;
    // vmax_junction = 150 * 0.01 = 1.5 mm/s

阶段4: 设置入口速度 - Line 2963

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)

对时间的影响

示例计算

假设:
- 前一移动100mm100 mm/s
- 当前移动100mm100 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%增加)

问题3OK

换料gcode中的M109会被统计 ✓


问题4M400 P100的含义

P参数的定义

位置: 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;

    // 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秒等待时间

是否会记录时间?

答案:会

完整流程

// 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中的扫描模型

gcode += "M976 S1 P1 ; scan model before printing 2nd layer\n";
gcode += "M400 P100\n";  // 等待100毫秒

目的

  • M976触发模型扫描
  • M400 P100确保扫描命令完全执行
  • 在时间估算中添加0.1秒

U1的工具切换

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