mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-11 22:43:04 +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
13 KiB
13 KiB
补充问题的详细答案
日期: 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)
// 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轴限制降低)
结论
多重限制机制:
- 初始限制:
machine_max_acceleration_extruding(20000) - 轴向限制: 每个轴的
machine_max_acceleration_*(E轴5000) - 最终结果: 取决于移动的轴向分量
简化规则:
- 对于纯E轴移动(回抽/回退):直接受E轴5000限制
- 对于XYZ+E移动(打印):
- 如果E分量小:可能接近20000
- 如果E分量大:会被降低以满足5000限制
- 实际加速度 ≤ min(20000, 5000/E轴比例)
问题2:Jerk怎么参与计算的
完整计算流程
阶段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_factor(Line 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)
对时间的影响
示例计算:
假设:
- 前一移动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
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计算流程
- 计算安全速度(限制单轴)
- 计算XYZ速度变化向量
- 应用jerk限制降低连接速度
- 独立检查E轴jerk
- 设置最终入口/出口速度
问题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 |