Files
OrcaSlicer/doc/developer-reference/Print_Time_Estimation_Analysis.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

23 KiB
Raw Blame History

打印时间预估分析文档

概述

本文档详细分析了OrcaSlicer中所有影响打印时间预估的工艺配置项以及温度相关的预估逻辑。

时间预估核心机制

时间预估的核心实现在 src/libslic3r/GCode/GCodeProcessor.cpp 中的 TimeMachine 类。它通过以下方式计算时间:

  1. 梯形速度曲线Trapezoid每个移动块TimeBlock被分解为加速、巡航、减速三个阶段
  2. 加速度限制:根据配置的加速度值计算加速/减速所需的时间和距离
  3. 速度限制:根据配置的速度值计算巡航阶段的时间
  4. 同步等待某些命令如M400、G4、M191等会调用 simulate_st_synchronize() 添加固定等待时间

影响时间预估的配置项

1. 速度相关配置Speed Settings

这些配置直接影响移动速度,从而影响时间预估:

配置名称 配置解释 代码位置 如何影响时间预估
initial_layer_speed 首层打印速度 GCodeProcessor::process_line_G1() 首层所有移动使用此速度,速度越低时间越长
initial_layer_infill_speed 首层填充速度 GCodeProcessor::process_line_G1() 首层填充移动使用此速度
outer_wall_speed 外壁速度 GCodeProcessor::process_line_G1() 外壁移动使用此速度,影响外壁打印时间
inner_wall_speed 内壁速度 GCodeProcessor::process_line_G1() 内壁移动使用此速度,影响内壁打印时间
top_surface_speed 顶面速度 GCodeProcessor::process_line_G1() 顶面移动使用此速度
internal_solid_infill_speed 内部实心填充速度 GCodeProcessor::process_line_G1() 内部实心填充移动使用此速度
sparse_infill_speed 稀疏填充速度 GCodeProcessor::process_line_G1() 稀疏填充移动使用此速度
gap_infill_speed 间隙填充速度 GCodeProcessor::process_line_G1() 间隙填充移动使用此速度
travel_speed 空走速度 GCodeProcessor::process_line_G1() 空走移动使用此速度,影响空走时间
small_perimeter_speed 小周长速度 GCodeProcessor::process_line_G1() 小周长移动使用此速度

代码级描述

  • GCodeProcessor::process_line_G1()根据移动类型ExtrusionRole选择对应的速度配置
  • 速度值直接用于计算 TimeBlockcruise_feedrate
  • 时间计算公式:time = acceleration_time + cruise_time + deceleration_time
  • 其中 cruise_time = cruise_distance / cruise_feedrate

2. 加速度相关配置Acceleration Settings

这些配置影响加速/减速过程,从而影响时间预估:

配置名称 配置解释 代码位置 如何影响时间预估
default_acceleration 默认加速度 GCodeProcessor::apply_config() 作为默认加速度值,影响所有移动的加速/减速时间
initial_layer_acceleration 首层加速度 GCodeProcessor::process_line_G1() 首层移动使用此加速度,加速度越低加速/减速时间越长
outer_wall_acceleration 外壁加速度 GCodeProcessor::process_line_G1() 外壁移动使用此加速度
inner_wall_acceleration 内壁加速度 GCodeProcessor::process_line_G1() 内壁移动使用此加速度
top_surface_acceleration 顶面加速度 GCodeProcessor::process_line_G1() 顶面移动使用此加速度
internal_solid_infill_acceleration 内部实心填充加速度 GCodeProcessor::process_line_G1() 内部实心填充移动使用此加速度
sparse_infill_acceleration 稀疏填充加速度 GCodeProcessor::process_line_G1() 稀疏填充移动使用此加速度
travel_acceleration 空走加速度 GCodeProcessor::set_travel_acceleration() 空走移动使用此加速度
bridge_acceleration 桥接加速度 GCodeProcessor::process_line_G1() 桥接移动使用此加速度

代码级描述

  • GCodeProcessor::apply_config() 中,加速度值被设置到 TimeMachine::acceleration
  • TimeBlock::calculate_trapezoid() 中,使用加速度计算加速/减速距离:
    float accelerate_distance = estimated_acceleration_distance(entry_feedrate, cruise_feedrate, acceleration);
    float decelerate_distance = estimated_acceleration_distance(cruise_feedrate, exit_feedrate, -acceleration);
    
  • 加速度越低,加速/减速距离越长,巡航距离越短,但总时间可能更长(取决于移动距离)

3. 机器限制配置Machine Limits

这些配置限制了速度和加速度的上限:

配置名称 配置解释 代码位置 如何影响时间预估
machine_max_acceleration_extruding 挤出时最大加速度 GCodeProcessor::apply_config() 限制所有挤出移动的加速度上限
machine_max_acceleration_retracting 回抽时最大加速度 GCodeProcessor::apply_config() 限制回抽移动的加速度上限
machine_max_acceleration_travel 空走时最大加速度 GCodeProcessor::apply_config() 限制空走移动的加速度上限
machine_max_speed_x X轴最大速度 GCodeProcessor::process_M203() 限制X轴移动速度上限
machine_max_speed_y Y轴最大速度 GCodeProcessor::process_M203() 限制Y轴移动速度上限
machine_max_speed_z Z轴最大速度 GCodeProcessor::process_M203() 限制Z轴移动速度上限
machine_max_speed_e E轴最大速度 GCodeProcessor::process_M203() 限制挤出速度上限

代码级描述

  • GCodeProcessor::apply_config() 中,机器限制被设置到 TimeMachine::max_acceleration
  • TimeBlock::calculate_trapezoid() 中,加速度会被限制在最大值内
  • GCodeProcessor::process_M203() 中,速度限制被应用到各轴

4. 温度相关配置Temperature Settings

4.1 预热配置

配置名称 配置解释 代码位置 如何影响时间预估
preheat_time 预热时间 GCodeProcessor::apply_config() 不直接影响时间预估仅用于计算何时插入M104预热命令
delta_temperature 预热温度差 GCodeProcessor::apply_config() 不直接影响时间预估,仅用于计算预热温度
preheat_steps 预热步数 GCodeProcessor::apply_config() 不直接影响时间预估,用于预热回溯的步数

代码级描述

  • GCodeProcessor::apply_config() 中,这些值被存储为成员变量
  • GCodeProcessor::process_line_T() 中,使用 preheat_timepreheat_steps 计算何时插入M104命令
  • 重要:预热时间本身被添加到时间预估中因为M104命令是异步的不等待

4.2 温度等待命令

G代码命令 配置解释 代码位置 如何影响时间预估
M109 设置温度并等待 GCodeProcessor::process_M109() 当前实现有问题:只更新温度值,没有添加等待时间
M190 等待热床温度 GCodeProcessor::process_M190() 当前实现有问题:只更新温度值,没有添加等待时间
M191 等待腔室温度 GCodeProcessor::process_M191() 如果温度>40°C添加硬编码的720秒等待时间

代码级描述

M109问题src/libslic3r/GCode/GCodeProcessor.cpp:3640-3655

void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
{
    float new_temp;
    if (line.has_value('R', new_temp)) {
        // ... 更新温度值 ...
    }
    else if (line.has_value('S', new_temp))
        m_extruder_temps[m_extruder_id] = new_temp;
    // ❌ 问题:没有调用 simulate_st_synchronize() 添加等待时间
}

M190问题src/libslic3r/GCode/GCodeProcessor.cpp:3696-3701

void GCodeProcessor::process_M190(const GCodeReader::GCodeLine& line)
{
    float new_temp;
    if (line.has_value('S', new_temp))
        m_highest_bed_temp = m_highest_bed_temp < (int)new_temp ? (int)new_temp : m_highest_bed_temp;
    // ❌ 问题:没有调用 simulate_st_synchronize() 添加等待时间
}

M191实现src/libslic3r/GCode/GCodeProcessor.cpp:3703-3710

void GCodeProcessor::process_M191(const GCodeReader::GCodeLine& line)
{
    float chamber_temp = 0;
    const float wait_chamber_temp_time = 720.0; // 硬编码720秒
    if (line.has_value('S', chamber_temp) && chamber_temp > 40)
        simulate_st_synchronize(wait_chamber_temp_time); // ✅ 正确添加了等待时间
}

问题分析

  • 所有M109命令都没有计算等待时间process_M109() 只更新温度值,没有调用 simulate_st_synchronize()
  • 所有M190命令都没有计算等待时间process_M190() 只更新温度值,没有调用 simulate_st_synchronize()
  • 这导致时间预估显著偏短,因为忽略了所有温度等待时间
  • 实际打印中M109/M190会阻塞直到温度达到但预估中没有计算这部分时间
  • 影响最严重的情况:
    • 首层打印前如果开始G-code中有M109等待时间可能达到30-60秒
    • 工具切换时如果切换工具需要加热等待时间可能达到20-40秒
    • 温度变化大时从低温如150°C加热到高温如280°C等待时间可能达到50-80秒

5. 工具切换相关配置Tool Change Settings

配置名称 配置解释 代码位置 如何影响时间预估
machine_tool_change_time 工具切换时间 GCodeProcessor::apply_config() process_T() 中,每次工具切换添加此时间
machine_load_filament_time 加载耗材时间 GCodeProcessor::apply_config() process_T() 中,工具切换时添加此时间
machine_unload_filament_time 卸载耗材时间 GCodeProcessor::apply_config() process_M702() 中,卸载时添加此时间

代码级描述

  • GCodeProcessor::process_T() 中(src/libslic3r/GCode/GCodeProcessor.cpp:3964-4012
    float extra_time = 0.0f;
    if (m_time_processor.extruder_unloaded) {
        m_time_processor.extruder_unloaded = false;
        extra_time += get_filament_load_time(static_cast<size_t>(m_extruder_id));
        extra_time += m_time_processor.machine_tool_change_time;
        simulate_st_synchronize(extra_time);
    }
    

6. 延迟/等待命令Delay/Wait Commands

G代码命令 配置解释 代码位置 如何影响时间预估
G4 延迟命令 GCodeProcessor::process_G4() 添加指定的延迟时间S参数为秒P参数为毫秒
M1 暂停等待 GCodeProcessor::process_M1() 添加同步等待时间(无限等待,但预估中可能不计算)
M400 等待移动完成 GCodeProcessor::process_M400() 添加同步等待时间

代码级描述

  • G4 命令(src/libslic3r/GCode/GCodeProcessor.cpp:3457-3462
    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);
    }
    

7. 其他影响时间预估的因素

配置/因素 配置解释 代码位置 如何影响时间预估
slow_down_layer_time 层冷却时间 CoolingBuffer 如果层时间太短,会降低速度以增加层时间
fan_cooling_layer_time 风扇冷却层时间 CoolingBuffer 影响冷却逻辑,可能降低速度
G29 (自动调平) 自动调平 GCodeProcessor::process_G29() 硬编码添加260秒等待时间
retraction_speed 回抽速度 GCodeProcessor::process_line_G1() 影响回抽移动的时间
deretraction_speed 回退速度 GCodeProcessor::process_line_G1() 影响回退移动的时间

温度相关时间预估问题总结

问题1M109没有添加等待时间

位置src/libslic3r/GCode/GCodeProcessor.cpp:3640-3655

问题M109命令应该等待温度达到目标值但当前实现只更新了温度值没有添加等待时间。

影响:时间预估会偏短因为忽略了温度等待时间。对于需要从低温加热到高温的情况如从室温到220°C实际等待时间可能达到30-60秒但预估中没有计算。

建议修复

void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
{
    float new_temp;
    float current_temp = m_extruder_temps[m_extruder_id];
    
    if (line.has_value('R', new_temp)) {
        // ... 处理R参数 ...
    }
    else if (line.has_value('S', new_temp)) {
        m_extruder_temps[m_extruder_id] = new_temp;
    }
    
    // 计算等待时间:根据温度差估算
    if (new_temp > current_temp) {
        float temp_diff = new_temp - current_temp;
        // 假设加热速度约为 2-3°C/秒(可根据实际情况调整)
        float wait_time = temp_diff / 2.5f; // 保守估计
        // 最小等待时间5秒最大等待时间120秒
        wait_time = std::clamp(wait_time, 5.0f, 120.0f);
        simulate_st_synchronize(wait_time);
    }
}

问题2M190没有添加等待时间

位置src/libslic3r/GCode/GCodeProcessor.cpp:3696-3701

问题M190命令应该等待热床温度达到目标值但当前实现只更新了温度值没有添加等待时间。

影响:时间预估会偏短,特别是首层打印前的热床预热时间。

建议修复

void GCodeProcessor::process_M190(const GCodeReader::GCodeLine& line)
{
    float new_temp;
    if (line.has_value('S', new_temp)) {
        float current_bed_temp = m_highest_bed_temp;
        m_highest_bed_temp = m_highest_bed_temp < (int)new_temp ? (int)new_temp : m_highest_bed_temp;
        
        // 计算等待时间:热床加热速度较慢,约为 0.5-1°C/秒
        if (new_temp > current_bed_temp) {
            float temp_diff = new_temp - current_bed_temp;
            float wait_time = temp_diff / 0.75f; // 保守估计
            // 最小等待时间10秒最大等待时间300秒
            wait_time = std::clamp(wait_time, 10.0f, 300.0f);
            simulate_st_synchronize(wait_time);
        }
    }
}

问题3M191使用硬编码时间

位置src/libslic3r/GCode/GCodeProcessor.cpp:3703-3710

问题M191使用硬编码的720秒12分钟等待时间没有根据实际温度差计算。

影响:对于不同的温度目标,等待时间可能不准确。

建议修复

void GCodeProcessor::process_M191(const GCodeReader::GCodeLine& line)
{
    float chamber_temp = 0;
    if (line.has_value('S', chamber_temp) && chamber_temp > 40) {
        // 腔室加热速度很慢,约为 0.1-0.2°C/秒
        // 假设从室温(~25°C加热到目标温度
        float temp_diff = chamber_temp - 25.0f;
        float wait_time = temp_diff / 0.15f; // 保守估计
        // 最小等待时间60秒最大等待时间1800秒30分钟
        wait_time = std::clamp(wait_time, 60.0f, 1800.0f);
        simulate_st_synchronize(wait_time);
    }
}

问题4预热时间不参与时间预估

位置src/libslic3r/GCode/GCodeProcessor.cpp:4594-4668

问题preheat_time 配置仅用于计算何时插入M104命令但不参与时间预估。

影响:如果预热时间设置较长,实际打印中工具切换时的等待时间可能被缩短,但预估中没有考虑这个优化。

分析

  • 预热机制的目的是减少工具切换时的等待时间
  • 如果预热成功M109的等待时间应该减少(因为工具已经预热)
  • 当前实现中,预热时间不参与预估是合理的,因为:
    1. M104是异步命令不阻塞打印
    2. 预热是否成功取决于实际打印进度
    3. 如果预热失败仍然需要等待M109

建议保持当前实现但可以考虑在M109中添加逻辑如果检测到之前有M104预热命令可以减少等待时间。

补充问题解答

1. 打印准备时间prepare_time是怎么来的

位置src/libslic3r/GCode/GCodeProcessor.cpp:375-376, 2774, 3228

机制

  1. 标记准备阶段:在 process_line_G1()当处理G1/G0命令时会创建 TimeBlock 并设置:

    block.flags.prepare_stage = m_processing_start_custom_gcode;
    
  2. 设置准备阶段标志m_processing_start_custom_gcodeprocess_gcode_line() 中设置:

    // 当遇到自定义G代码erCustom且是第一条G1命令时
    m_processing_start_custom_gcode = (m_extrusion_role == erCustom && m_g1_line_id == 0);
    

    这意味着开始G代码start_gcode中的移动会被标记为准备阶段。

  3. 累加准备时间:在 TimeMachine::calculate_time() 中:

    if (block.flags.prepare_stage)
        prepare_time += block_time;
    

包含的内容

  • 开始G代码machine_start_gcode)中的所有移动时间
  • 开始G代码中的温度设置、回零等操作时间
  • 首层打印前的所有准备操作时间

注意准备阶段中的空走Travel移动不计入空走时间统计,但计入准备时间:

//BBS: don't calculate travel of start gcode into travel time
if (!block.flags.prepare_stage || block.move_type != EMoveType::Travel)
    moves_time[static_cast<size_t>(block.move_type)] += block_time;

用途

  • 在UI中显示"首层打印时间"时,会从总时间中减去准备时间
  • 用于区分"准备时间"和"实际打印时间"

2. simulate_st_synchronize 是干什么的?

位置src/libslic3r/GCode/GCodeProcessor.cpp:257-263

功能模拟固件如Marlinst_synchronize() 函数调用,表示等待所有移动完成并添加额外时间

实现

void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time)
{
    if (!enabled)
        return;
    
    calculate_time(0, additional_time);
}

作用

  1. 处理时间块队列:调用 calculate_time() 处理 blocks 队列中累积的移动块
  2. 添加额外时间:将 additional_time 添加到第一个待处理块的时间中
  3. 更新统计:累加到总时间、层时间、角色时间等统计中

使用场景

  • G4延迟命令process_G4() 调用 simulate_st_synchronize(value_s) 添加延迟时间
  • M400等待完成process_M400() 调用 simulate_st_synchronize() 等待移动完成
  • M1暂停process_M1() 调用 simulate_st_synchronize() 添加暂停时间
  • M191等待腔室温度process_M191() 调用 simulate_st_synchronize(720.0) 添加等待时间
  • 工具切换process_T() 调用 simulate_st_synchronize(extra_time) 添加切换和加载时间
  • G29自动调平process_G29() 调用 simulate_st_synchronize(260.0) 添加调平时间

为什么需要

  • 固件的 st_synchronize() 会阻塞直到所有移动完成
  • 某些命令如M109、M190需要等待操作完成
  • 需要在时间预估中反映这些等待时间

问题当前实现中M109和M190没有调用 simulate_st_synchronize(),导致等待时间没有被计算。

3. 算时间是所有G-code都生成了之后后处理的还是生成一条G-code就算一行

答案混合模式 - 大部分是实时处理,最终计算是后处理

详细流程

3.1 实时处理阶段G-code生成/解析时)

位置src/libslic3r/GCode/GCodeProcessor.cpp:1550-2074 (process_gcode_line)

过程

  1. 解析G-code行每解析一行G-code调用 process_gcode_line()
  2. 处理G1/G0命令遇到G1/G0命令时调用 store_move_vertex()process_line_G1()
  3. 创建时间块:在 process_line_G1() 中创建 TimeBlock 并添加到 blocks 队列:
    TimeBlock block;
    block.move_type = type;
    block.role = m_extrusion_role;
    block.distance = distance;
    // ... 设置其他属性 ...
    machine.blocks.push_back(block);
    
  4. 立即计算(部分):某些命令会立即调用 simulate_st_synchronize()
    • G4延迟立即添加延迟时间
    • M400等待立即处理队列
    • 工具切换:立即添加切换时间

特点

  • 时间块TimeBlock实时创建
  • 不立即计算最终时间(因为需要前后块的信息来优化速度曲线)

3.2 后处理阶段所有G-code处理完成后

位置src/libslic3r/GCode/GCodeProcessor.cpp:1309-1361 (finalize)

过程

  1. 最终计算:在 finalize() 中调用 calculate_time()

    for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
        TimeMachine& machine = m_time_processor.machines[i];
        machine.calculate_time(); // 处理所有剩余的blocks
    }
    
  2. 速度曲线优化calculate_time() 执行:

    • 前向传递forward_pass:从前往后优化入口速度
    • 反向传递reverse_pass:从后往前优化出口速度
    • 重新计算梯形:根据优化后的速度重新计算加速/巡航/减速阶段
  3. 累加统计:计算每个块的时间并累加到:

    • time:总时间
    • prepare_time:准备时间
    • layers_time:每层时间
    • roles_time:每种角色时间
    • moves_time:每种移动类型时间

为什么需要后处理

  • 速度曲线优化:需要知道后续块的速度才能优化当前块的出口速度
  • 加速度限制:需要确保相邻块之间的速度转换符合加速度限制
  • 全局优化:需要反向传递来确保从后往前的速度优化

3.3 混合模式的原因

实时处理

  • 创建时间块TimeBlock
  • 处理立即生效的命令G4、M400等
  • 累积到队列中

后处理

  • 优化速度曲线(需要全局信息)
  • 计算最终时间(需要所有块的信息)
  • 更新统计信息

性能考虑

  • 如果每条G-code都完整计算性能会很差需要重新计算所有块
  • 采用批量处理 + 最终优化的方式,平衡了实时性和准确性

总结

影响时间预估的主要因素:

  1. 速度配置:直接影响移动时间
  2. 加速度配置:影响加速/减速时间
  3. 机器限制:限制速度和加速度上限
  4. 温度等待当前实现有问题M109/M190没有添加等待时间
  5. 工具切换:添加固定的切换和加载时间
  6. 延迟命令G4、M400等添加固定延迟
  7. 准备时间开始G代码中的操作时间单独统计
  8. 时间计算模式:混合模式 - 实时创建时间块,后处理优化和计算

最严重的问题M109和M190没有添加等待时间这会导致时间预估显著偏短,特别是对于需要从低温加热到高温的打印任务。

时间计算流程

  1. 实时解析G-code → 创建TimeBlock → 添加到队列
  2. 实时(部分):某些命令立即调用 simulate_st_synchronize() 处理队列
  3. 后处理所有G-code处理完成后调用 finalize()calculate_time() 优化速度曲线并计算最终时间