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

540 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 时间预估补充问题分析
> **文档版本**: v1.0
> **创建日期**: 2025-12-06
> **问题来源**: M109/M190时间预估修复方案的补充问题
---
## 问题1挤出最大加速度 vs E最大加速度的区别
### 1.1 配置参数定义
**位置**: `src/libslic3r/PrintConfig.cpp:3640-3682``3734-3744`
#### machine_max_acceleration_eE轴最大加速度
```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<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**: 在打印移动时作为加速度上限
---
## 问题2Jerk如何影响预估时间的计算
### 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<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
```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<Axis>(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<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`
```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. 如果不包含,需要确认换料过程是否在其他地方等待温度
---
## 问题4GCode.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配置下的时间估算
```