mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-11 06:23:08 +00:00
* 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
540 lines
16 KiB
Markdown
540 lines
16 KiB
Markdown
# 时间预估补充问题分析
|
||
|
||
> **文档版本**: 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<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**: 在打印移动时作为加速度上限
|
||
|
||
---
|
||
|
||
## 问题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<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. 如果不包含,需要确认换料过程是否在其他地方等待温度
|
||
|
||
---
|
||
|
||
## 问题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配置下的时间估算
|
||
```
|