Add warning info on spirail lift before slicing

This commit is contained in:
xiaoyeliu
2026-01-24 14:09:42 +08:00
parent b8bf1a8fe1
commit cf4275198c
7 changed files with 339 additions and 1 deletions

View File

@@ -0,0 +1,259 @@
# 螺旋抬升边界警告功能文档
## 1. 背景
### 1.1 问题描述
OrcaSlicer 在使用螺旋抬升Spiral Lift功能时存在安全风险当模型靠近打印床边界时螺旋抬升过程中可能会超出床范围导致打印头撞击床边缘造成硬件损坏。
### 1.2 螺旋抬升原理
螺旋抬升是一种平滑的 Z 轴抬升方式,通过螺旋路径避免打印头在抬升时与模型碰撞。其计算公式为:
```
radius = z_hop / (2 × π × tan(travel_slope))
```
**典型参数**
- `z_hop`Z 轴抬升高度,通常为 0.2mm
- `travel_slope`:移动斜率,通常为 1°-3°
**示例计算**travel_slope=3°, z_hop=0.2mm
```
radius = 0.2 / (2 × π × tan(3°))
≈ 0.608mm
螺旋直径 ≈ 1.2mm
```
### 1.3 风险场景
1. **模型靠近边界**:螺旋路径可能延伸到床外
2. **大尺寸模型**:导入或缩放到接近床尺寸的模型
3. **移动模型**:用户将模型移动到床边缘
## 2. 解决方案
### 2.1 设计思路
在用户移动、导入或缩放模型时,实时检测模型边界与床边界的距离,当距离小于安全阈值时显示警告提示。
### 2.2 实现方案
#### 2.2.1 架构设计
```
用户操作(移动/导入/缩放)
reload_scene() 触发场景刷新
check_outside_state() 检测模型状态
计算模型边界框与床边界的最小距离
判断是否 < 3mm 阈值
设置 near_boundary_for_spiral_lift 标志
_set_warning_notification() 显示警告
```
#### 2.2.2 核心代码实现
**检测逻辑**`3DScene.cpp`
```cpp
// 螺旋抬升安全余量3.5mm
constexpr double SPIRAL_LIFT_SAFETY_MARGIN = 3.5; // mm
// 计算模型边界框与床边界的最小距离
double min_distance_x = std::min({
std::abs(bb.min.x() - bed_bb.min.x()),
std::abs(bed_bb.max.x() - bb.max.x())
});
double min_distance_y = std::min({
std::abs(bb.min.y() - bed_bb.min.y()),
std::abs(bed_bb.max.y() - bb.max.y())
});
double min_distance = std::min({min_distance_x, min_distance_y});
if (min_distance < SPIRAL_LIFT_SAFETY_MARGIN) {
volume->near_boundary_for_spiral_lift = true;
}
```
**警告触发**`GLCanvas3D.cpp`
```cpp
if (contained_min_one) {
_set_warning_notification(EWarning::SpiralLiftNearBoundary,
_is_any_volume_near_boundary_for_spiral_lift());
}
```
#### 2.2.3 新增数据结构
**GLVolume 成员变量**`3DScene.hpp`
```cpp
bool near_boundary_for_spiral_lift : 1; // 是否靠近边界
```
**EWarning 枚举**`GLCanvas3D.hpp`
```cpp
enum class EWarning {
// ... 现有类型
SpiralLiftNearBoundary // 螺旋抬升靠近边界警告
};
```
### 2.3 用户界面
**警告级别**`SLICING_SERIOUS_WARNING`(红色/橙色警告)
**警告文本**
- 英文:"An object is too close to the plate boundary. Spiral lift during printing may exceed the bed and cause a crash. Please move the object away from the edge (recommend keeping at least 3.5mm distance)."
- 中文:"模型距离打印床边界太近。打印过程中的螺旋抬升可能会超出床范围导致撞机。请将模型移离边缘建议保持至少3.5mm的距离)。"
## 3. 阈值选取依据
### 3.1 理论计算
| 参数 | 典型值 | 说明 |
|------|--------|------|
| travel_slope | 1° - 3° | 移动斜率,角度越小螺旋半径越大 |
| z_hop | 0.2mm | Z 轴抬升高度 |
| 螺旋直径 | ≈ 3.6mm | 最不利情况 |
| 螺旋直径 | ≈ 1.2mm | 常见情况 |
### 3.2 阈值选项对比
| 选项 | 阈值 | 优点 | 缺点 |
|------|------|------|------|
| 2mm | 螺旋直径(1.2mm) + 0.8mm | 覆盖常见情况 | 对于1°斜率不够安全 |
| 3mm | 更保守的安全距离 | 覆盖大部分配置 | 1°斜率时仍有风险 |
| **3.5mm** | **最佳平衡点** | **覆盖1°-3°斜率安全且实用** | **略保守,但合理** |
| 5mm | 非常保守 | 最大安全范围 | 过于保守,限制用户操作 |
### 3.3 最终选择
**选择 3.5mm 作为阈值**,理由:
1. **安全性**:完全覆盖 travel_slope=1° 的最不利情况(螺旋直径 ≈ 3.6mm
2. **实用性**:不会过度限制用户的模型放置空间
3. **余量充足**比常见情况3°斜率直径1.2mm多出近3倍的安全距离
4. **用户体验**:在安全性和可用性之间取得最佳平衡
## 4. 风险评估
### 4.1 技术风险
| 风险项 | 风险等级 | 缓解措施 |
|--------|----------|----------|
| 检测精度问题 | 低 | 使用边界框检测,足够准确 |
| 性能影响 | 低 | 只在模型变化时检测,开销极小 |
| 误报/漏报 | 低 | 使用 3mm 保守阈值,降低误报 |
| 圆形床支持 | 中 | 当前仅支持矩形床,未来可扩展 |
### 4.2 影响面分析
**正面影响**
- ✅ 防止打印头撞机,保护硬件
- ✅ 提升用户体验,提前发现风险
- ✅ 降低售后成本,减少设备损坏
**潜在负面影响**
- ⚠️ 可能频繁触发警告,影响用户体验(已通过 3.5mm 阈值缓解)
- ⚠️ 仅支持 Snapmaker U1 矩形床(已明确限制)
### 4.3 使用场景
| 场景 | 是否触发 | 说明 |
|------|----------|------|
| 移动模型靠近边界 | ✅ 是 | 实时检测,移动时即时提醒 |
| 导入大尺寸模型 | ✅ 是 | 导入后立即检测 |
| 缩放模型变大 | ✅ 是 | 缩放后重新检测 |
| 模型完全在床中心 | ❌ 否 | 距离 ≥ 3.5mm 时不触发 |
| 模型部分超出边界 | ✅ 是 | 即使超出也会检测 |
### 4.4 原点偏移的影响
对于 Snapmaker U1配置了原点偏移x: -0.5, y: -1这已经体现在 `printable_area` 中:
```json
"printable_area": [
"0.5x1", // 左下角(考虑了原点偏移)
"270.5x1", // 右下角
"270.5x271", // 右上角
"0.5x271" // 左上角
]
```
**实际可打印范围**U1
- X: 0.5 ~ 270.5(宽度 270mm
- Y: 1 ~ 271高度 270mm
**不报警告的安全区域**(距离边界 ≥ 3.5mm
- X: **4 ~ 267**
- Y: **4.5 ~ 267.5**
## 5. 技术实现细节
### 5.1 修改文件清单
| 文件 | 修改内容 |
|------|----------|
| `src/slic3r/GUI/GLCanvas3D.hpp` | 添加 EWarning::SpiralLiftNearBoundary 枚举 |
| `src/slic3r/GUI/GLCanvas3D.cpp` | 添加警告文本和触发逻辑 |
| `src/slic3r/GUI/3DScene.hpp` | 添加 GLVolume 成员变量 |
| `src/slic3r/GUI/3DScene.cpp` | 实现距离检测逻辑 |
| `localization/i18n/Snapmaker_Orca.pot` | 英文翻译 |
| `localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po` | 中文翻译 |
### 5.2 关键函数
| 函数 | 位置 | 功能 |
|------|------|------|
| `GLVolumeCollection::check_outside_state()` | 3DScene.cpp:1050 | 检测模型状态,计算边界距离 |
| `GLVolumeCollection::is_any_volume_near_boundary_for_spiral_lift()` | 3DScene.cpp:1218 | 检查是否有模型靠近边界 |
| `GLCanvas3D::reload_scene()` | GLCanvas3D.cpp:2310 | 场景刷新,触发检测 |
| `GLCanvas3D::_set_warning_notification()` | GLCanvas3D.cpp:9659 | 显示警告通知 |
### 5.3 调试信息
如需调试,可以在 `3DScene.cpp:1134` 附近添加日志:
```cpp
if (min_distance < SPIRAL_LIFT_SAFETY_MARGIN) {
BOOST_LOG_TRIVIAL(warning) << "Volume near boundary: " << volume->name
<< ", min_distance=" << min_distance
<< " (threshold=" << SPIRAL_LIFT_SAFETY_MARGIN << ")";
volume->near_boundary_for_spiral_lift = true;
}
```
## 6. 未来改进方向
### 6.1 短期改进
1. **动态阈值计算**:根据当前配置的 `travel_slope``z_hop` 动态计算阈值
- 公式:`threshold = z_hop / (π × tan(travel_slope)) + safety_margin`
- 对于 3° 斜率 + 0.4mm z_hop阈值可降至约 2.5mm
2. **配置选项**:允许用户在设置中调整阈值或关闭警告
3. **可视化指示**:在 3D 视图中用颜色标记靠近边界的模型
### 6.2 长期改进
1. **圆形床支持**:扩展到支持 Delta 打印机的圆形床
2. **精确碰撞检测**:使用模型的实际几何而非边界框进行检测
3. **自动修复建议**:提供一键自动移动模型到安全位置的功能
## 7. 总结
本次实现通过在模型移动、导入、缩放时实时检测边界距离,当距离小于 **3.5mm** 时显示红色警告,有效预防了螺旋抬升导致的撞机风险。
**核心优势**
- 被动防御 → 主动预警
- 事后发现 → 事前提醒
- 保护硬件,提升用户体验
**适用范围**
- Snapmaker U1 打印机(矩形床,带原点偏移)
- 所有使用螺旋抬升功能的场景
- 覆盖 travel_slope 1°-3° 的配置范围

View File

@@ -4340,6 +4340,12 @@ msgid ""
"confirming that the height is within the build volume." "confirming that the height is within the build volume."
msgstr "" msgstr ""
msgid ""
"An object is too close to the plate boundary. Spiral lift during printing "
"may exceed the bed and cause a crash. Please move the object away from the "
"edge (recommend keeping at least 3mm distance)."
msgstr ""
msgid "Calibration step selection" msgid "Calibration step selection"
msgstr "" msgstr ""

View File

@@ -4267,6 +4267,13 @@ msgstr ""
"对象被放置在构建板的边界上或超过高度限制。\n" "对象被放置在构建板的边界上或超过高度限制。\n"
"请通过将其完全移动到构建板内或构建板外,并确认高度在构建空间以内来解决问题。" "请通过将其完全移动到构建板内或构建板外,并确认高度在构建空间以内来解决问题。"
msgid ""
"An object is too close to the plate boundary. Spiral lift during printing "
"may exceed the bed and cause a crash. Please move the object away from the "
"edge (recommend keeping at least 3mm distance)."
msgstr ""
"模型距离打印床边界太近。打印过程中的螺旋抬升可能会超出床范围导致撞机。请将模型移离边缘建议保持至少3mm的距离。"
msgid "Calibration step selection" msgid "Calibration step selection"
msgstr "校准步骤选择" msgstr "校准步骤选择"

View File

@@ -1110,6 +1110,32 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo
int64_t comp_id = ((int64_t)volume->composite_id.object_id << 32) | ((int64_t)volume->composite_id.instance_id); int64_t comp_id = ((int64_t)volume->composite_id.object_id << 32) | ((int64_t)volume->composite_id.instance_id);
volume->is_outside = state != BuildVolume::ObjectState::Inside; volume->is_outside = state != BuildVolume::ObjectState::Inside;
// Snapmaker: 检测模型是否距离床边界太近(螺旋抬升风险)
volume->near_boundary_for_spiral_lift = false;
// 只对矩形床进行检测Snapmaker U1
if (plate_build_volume.type() == BuildVolume_Type::Rectangle && volume->composite_id.volume_id >= 0) {
constexpr double SPIRAL_LIFT_SAFETY_MARGIN = 3.5; // mm
const BoundingBoxf3& bb = volume_bbox(*volume);
const BoundingBoxf3& bed_bb = plate_build_volume.bounding_volume();
// 计算模型边界框与床边界的最小距离(使用绝对值处理超出边界的情况)
double min_distance_x = std::min({
std::abs(bb.min.x() - bed_bb.min.x()),
std::abs(bed_bb.max.x() - bb.max.x())
});
double min_distance_y = std::min({
std::abs(bb.min.y() - bed_bb.min.y()),
std::abs(bed_bb.max.y() - bb.max.y())
});
double min_distance = std::min({min_distance_x, min_distance_y});
// 如果最小距离小于安全余量,触发警告
if (min_distance < SPIRAL_LIFT_SAFETY_MARGIN) {
volume->near_boundary_for_spiral_lift = true;
}
}
//volume->partly_inside = (state == BuildVolume::ObjectState::Colliding); //volume->partly_inside = (state == BuildVolume::ObjectState::Colliding);
if (volume->printable) { if (volume->printable) {
if (overall_state == ModelInstancePVS_Inside && volume->is_outside) { if (overall_state == ModelInstancePVS_Inside && volume->is_outside) {
@@ -1185,10 +1211,22 @@ void GLVolumeCollection::reset_outside_state()
if (volume != nullptr) { if (volume != nullptr) {
volume->is_outside = false; volume->is_outside = false;
volume->partly_inside = false; volume->partly_inside = false;
volume->near_boundary_for_spiral_lift = false; // Snapmaker: 初始化螺旋抬升边界状态
} }
} }
} }
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool GLVolumeCollection::is_any_volume_near_boundary_for_spiral_lift() const
{
for (const GLVolume* volume : this->volumes)
{
if (volume != nullptr && volume->near_boundary_for_spiral_lift)
return true;
}
return false;
}
void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha) void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha)
{ {

View File

@@ -185,6 +185,8 @@ public:
// Wheter or not this volume is outside print volume. // Wheter or not this volume is outside print volume.
bool is_outside : 1; bool is_outside : 1;
bool partly_inside : 1; bool partly_inside : 1;
// Snapmaker: Whether or not this volume is too close to boundary for spiral lift
bool near_boundary_for_spiral_lift : 1;
// Wheter or not this volume has been generated from a modifier // Wheter or not this volume has been generated from a modifier
bool is_modifier : 1; bool is_modifier : 1;
// Wheter or not this volume has been generated from the wipe tower // Wheter or not this volume has been generated from the wipe tower
@@ -516,6 +518,8 @@ public:
// returns the containment state in the given out_state, if non-null // returns the containment state in the given out_state, if non-null
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const; bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
void reset_outside_state(); void reset_outside_state();
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool is_any_volume_near_boundary_for_spiral_lift() const;
void update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha = true); void update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha = true);

View File

@@ -2823,6 +2823,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (printer_technology != ptSLA || !contained_min_one) if (printer_technology != ptSLA || !contained_min_one)
_set_warning_notification(EWarning::SlaSupportsOutside, false); _set_warning_notification(EWarning::SlaSupportsOutside, false);
// Snapmaker: 螺旋抬升边界警告 - 无论模型是否超出边界都检测
if (contained_min_one) {
_set_warning_notification(EWarning::SpiralLiftNearBoundary, _is_any_volume_near_boundary_for_spiral_lift());
} else {
_set_warning_notification(EWarning::SpiralLiftNearBoundary, false);
}
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
contained_min_one && !m_model->objects.empty() && !partlyOut)); contained_min_one && !m_model->objects.empty() && !partlyOut));
} }
@@ -2830,6 +2837,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
_set_warning_notification(EWarning::ObjectOutside, false); _set_warning_notification(EWarning::ObjectOutside, false);
_set_warning_notification(EWarning::ObjectClashed, false); _set_warning_notification(EWarning::ObjectClashed, false);
_set_warning_notification(EWarning::SlaSupportsOutside, false); _set_warning_notification(EWarning::SlaSupportsOutside, false);
_set_warning_notification(EWarning::SpiralLiftNearBoundary, false); // Snapmaker: 清空警告
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
} }
} }
@@ -9696,6 +9704,13 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
"Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume."); "Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume.");
error = ErrorType::PLATER_ERROR; error = ErrorType::PLATER_ERROR;
break; break;
// Snapmaker: 螺旋抬升靠近边界警告
case EWarning::SpiralLiftNearBoundary:
text = _u8L("An object is too close to the plate boundary. "
"Spiral lift during printing may exceed the bed and cause a crash. "
"Please move the object away from the edge (recommend keeping at least 3mm distance).");
error = ErrorType::SLICING_SERIOUS_WARNING;
break;
} }
//BBS: this may happened when exit the app, plater is null //BBS: this may happened when exit the app, plater is null
if (!wxGetApp().plater()) if (!wxGetApp().plater())
@@ -9750,6 +9765,12 @@ bool GLCanvas3D::_is_any_volume_outside() const
return false; return false;
} }
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool GLCanvas3D::_is_any_volume_near_boundary_for_spiral_lift() const
{
return m_volumes.is_any_volume_near_boundary_for_spiral_lift();
}
void GLCanvas3D::_update_selection_from_hover() void GLCanvas3D::_update_selection_from_hover()
{ {
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL); bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);

View File

@@ -381,7 +381,8 @@ class GLCanvas3D
SomethingNotShown, SomethingNotShown,
ObjectClashed, ObjectClashed,
GCodeConflict, GCodeConflict,
ToolHeightOutside ToolHeightOutside,
SpiralLiftNearBoundary // Snapmaker: 螺旋抬升靠近边界警告
}; };
class RenderStats class RenderStats
@@ -1238,6 +1239,8 @@ private:
void _set_warning_notification(EWarning warning, bool state); void _set_warning_notification(EWarning warning, bool state);
bool _is_any_volume_outside() const; bool _is_any_volume_outside() const;
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool _is_any_volume_near_boundary_for_spiral_lift() const;
// updates the selection from the content of m_hover_volume_idxs // updates the selection from the content of m_hover_volume_idxs
void _update_selection_from_hover(); void _update_selection_from_hover();