feat: 增加组合折叠姿态干涉扫描、力矩实测与结构强度复核数据
This commit is contained in:
@@ -69,8 +69,8 @@ python src/sweep_v2.py
|
||||
|
||||
| 构件编号 | 构件名称 | 关联驱动关节 | 设计说明 |
|
||||
| :--- | :--- | :---: | :--- |
|
||||
| **P01** | 胸板 / 安装板 | 根基固定 | 12mm 基础基座,预留 4x M4 紧固位与躯干走线窗 |
|
||||
| **P02** | 底脚 / 支撑座 | 根基固定 | 200×320mm 重型防倾覆底盘,提供稳固调试基座 |
|
||||
| **P01** | 立柱(三段 A/B/C) | 根基固定 | 66×66mm 三角截面立柱,分段榫接 + M4 自攻对锁孔防反装;顶端为 P03 安装台 |
|
||||
| **P02** | 底盘(核心座 + 前/后防翻力臂) | 根基固定 | 195×190mm 核心承力座 + 48mm 套筒 + 前后三角加强筋;前后防翻力臂带 M5 预埋螺母 |
|
||||
| **P03** | 肩吊臂 / 肩关节安装悬臂 | J1 驱动抱紧 | 轻量化板式桁架:双侧 6mm 竖板 + 帐篷减重窗(≥52°自支撑)+ 9mm 承力鞍座 + Ø15 螺母柱,抱紧 M1 电机(实心 728→286cm³,-61%,外轮廓不变) |
|
||||
| **P04** | 肩叉 | J1 ➔ J2 | 90° 一体化高刚度正交转向肘,直锁 M1,抱紧 M2 |
|
||||
| **P05** | 大臂旋转座 | J2 ➔ J3 | 16 层 Loft 曲面平滑过渡,直锁 M2,后侧刚性抱紧 M3 |
|
||||
@@ -78,6 +78,23 @@ python src/sweep_v2.py
|
||||
| **P07** | 小臂骨架 | J4 ➔ J5 | L 型圆弧强化立柱,大范围自由运动避让,抱紧 M5 |
|
||||
| **P08** | 腕偏转座 | J5 ➔ J6 | 16 层 Loft 双曲面过渡,直锁 M5,立式抱紧 M6 腕旋转电机 |
|
||||
| **P09** | 工具法兰 | J6 输出 | 直锁 M6 输出轴,标准工业 ISO 法兰(4x M4 螺孔 BCD 40mm) |
|
||||
| **M1~M6** | 关节电机模组 ×6 | 各关节驱动 | Ø60 方形标准减速电机模组(63.6mm 方形尾盖),210 g/台,整机 1260 g |
|
||||
|
||||
> ⚠️ **P01 / P02 是固定测试台架,不是机器人本体**。它在扫描中代表机械臂与躯干的连接
|
||||
> 界面——**臂撞立柱 = 臂撞躯干,是真约束,碰撞计算必须包含它**。但台架的位置与截面
|
||||
> 是一个假设,所以躯干侧的工作空间只和这个假设一样准。要改的是"真机躯干占位"这个
|
||||
> 输入,而不是改台架(改台架是改测试工装)。`sweep_v2.py --no-stand` 是诊断开关:
|
||||
> 排除台架重扫,给出不含躯干约束的自由空间行程,用来分辨某个限位是臂自身折叠产生的
|
||||
> 还是躯干挡的。**默认包含台架。**
|
||||
|
||||
> ⚠️ **关节电机力矩现状**:按 `torque_balance.py` 的实测(零件真实质心 + 可达姿态搜索),
|
||||
> **J1 肩抬举仅悬停自身臂身即需 3.84 N·m、J2 需 2.48 N·m**,已超出假定许用连续力矩
|
||||
> 2.0 N·m;带 0.5 kg 末端负载时 J1 达 6.14 N·m(307%)。详见 `docs/torque_report.md`。
|
||||
> 结论对电机额定参数敏感,该参数目前为工程假定(无数据手册),可用 `ARMT_CONT` 覆盖重算。
|
||||
>
|
||||
> 相反地,**结构本身不是瓶颈**:12 个剖面全部合格,最差的 P03 吊臂根座安全系数 8.93,
|
||||
> 最大挠度 1.6mm。减重救不了电机——J1 的 distal 载荷里 5 台电机占 65%(1.05kg),
|
||||
> 打印件只占 0.58kg,把 P07/P08/P09 全部减掉 J1 也只降到 2.54 N·m。
|
||||
|
||||
---
|
||||
|
||||
@@ -85,8 +102,20 @@ python src/sweep_v2.py
|
||||
|
||||
- 📘 **权威数据(工具自动生成)**:
|
||||
- [各关节行程与硬限位 travel.json](file:///g:/Robot_v2/机器人手臂/docs/travel.json)(sweep_v2.py 扫描生成,修改几何后必须重扫)
|
||||
> 滑块范围 = **单关节**硬限位。组合折叠姿态下另有干涉约束,见 `travel_detail.json` 的
|
||||
> `combos` 字段(目前全部是工具法兰 P09 撞躯干占位 P01_A,属真约束);查看器运行期
|
||||
> 另有胶囊碰撞模型按姿态夹取。
|
||||
- [零件明细与质量属性 bom.json](file:///g:/Robot_v2/机器人手臂/docs/bom.json)(构建自动生成)
|
||||
- [静态干涉检测结果 collision.json](file:///g:/Robot_v2/机器人手臂/docs/collision.json)(构建自动生成)
|
||||
- [截面刚度与应力 strength.json](file:///g:/Robot_v2/机器人手臂/docs/strength.json)(strength_check.py 生成)
|
||||
- [关节力矩与负载能力 torque_report.md](file:///g:/Robot_v2/机器人手臂/docs/torque_report.md)(torque_balance.py 生成)
|
||||
- 🔬 **测试与校验工具**(src/ 目录):
|
||||
- sweep_v2.py — 权威动态碰撞检测与行程扫描
|
||||
- strength_check.py / torque_balance.py — 刚度、应力与力矩平衡校核(按需重新生成报告)
|
||||
- strength_check.py — 剖面刚度/应力校核(零件真实质心 + 可达姿态搜索,输出含挠度估算)
|
||||
- torque_balance.py — 关节电机力矩、惯量与负载能力校核(拓扑取自 human_layout,不手抄)
|
||||
- finalize_travel.py — 从 travel_detail 重建 travel.json(含组合卡死阈值提示)
|
||||
|
||||
> 三个校验脚本共用同一套零件表与运动学链(`strength_check.part_table / chain`,
|
||||
> 关节轴来自 `human_layout.py`),改几何后不存在"报告与模型不同步"的问题。
|
||||
> 环境变量:`ARMPAY`(末端负载 kg)、`ARMDYN`(动载系数)、`ARMINFILL`(实体率)、
|
||||
> `ARMMAT`(材料)、`ARMT_CONT`/`ARMT_PEAK`/`ARMT_ALLOW`(电机力矩)、`ARMT_ALPHA`(角加速度)。
|
||||
|
||||
+6
-6
@@ -74,20 +74,20 @@
|
||||
"名称": "肩叉",
|
||||
"类型": "3D打印",
|
||||
"数量": 1,
|
||||
"体积cm3": 347.4,
|
||||
"质量g_PLA": 430.8,
|
||||
"体积cm3": 361.4,
|
||||
"质量g_PLA": 448.1,
|
||||
"包络": "[-15.5 91.5 455.0 .. 54.5 164.5 646.5] (70.0x73.0x191.5)",
|
||||
"说明": "齐平插槽版:叉板背面与P03托架端面(11.5)0.0硬贴平(用户要求外观齐平),P03侧插槽独立加长3mm,M1电机可倾斜卡入,J1输出凸台贯通直连电机,J1从动法兰圆盘鼓楞已删(接缝区与P03止脱环齐径齐高R36.5/顶646.5),单实体全角度零干涉"
|
||||
"说明": "齐平插槽+ M2 前端压盖版:叉板背面与P03托架端面(11.5)0.0硬贴平(用户要求外观齐平),P03侧插槽独立加长3mm,M1电机可倾斜卡入,J1输出凸台贯通直连电机,J1从动法兰圆盘鼓楞已删(接缝区与P03止脱环齐径齐高R36.5/顶646.5),单实体全角度零干涉;【2026-10-02 新增】M2 前端压盖(R35, Y155.5..161.0):电机前端盖外端面加 4xM3x8 沉孔(BCD55)夹紧,与后端安装板 4xM3x10 形成前后双向面压,消除大扭矩下电机在座孔里的倾斜"
|
||||
},
|
||||
{
|
||||
"件号": "P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
|
||||
"名称": "大臂旋转座",
|
||||
"类型": "3D打印",
|
||||
"数量": 1,
|
||||
"体积cm3": 159.5,
|
||||
"质量g_PLA": 197.8,
|
||||
"体积cm3": 141.5,
|
||||
"质量g_PLA": 175.5,
|
||||
"包络": "[-15.5 93.0 389.0 .. 54.5 174.0 525.0] (70.0x81.0x136.0)",
|
||||
"说明": "V3圆角:Loft外轮廓R=10mm,L型转角内部2mm倒角,后缘凸包两角90°竖棱磨R2圆角(结构/后缘高度不变,打印无悬挂),保证J2全角度0干涉"
|
||||
"说明": "V3圆角+ J2前端夹紧配合:Loft外轮廓R=10mm,L型转角内部2mm倒角,后缘凸包两角90°竖棱磨R2圆角(结构/后缘高度不变,打印无悬挂);J2 从动法兰盘背面由 Y=157 后移至 Y=162(hub_gap=6.0,给 P04 新增的 M2 前端压盖让位,J2 直锁凸台同步加长到 Y=165.5,3xS01 输出螺钉咬合量不变);保证 J2 全角度 0 干涉"
|
||||
},
|
||||
{
|
||||
"件号": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
|
||||
|
||||
+121
-111
@@ -1,112 +1,122 @@
|
||||
[
|
||||
{
|
||||
"id": "S01",
|
||||
"规格": "M2 x 10",
|
||||
"螺纹": "M2",
|
||||
"长度mm": 10,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 18,
|
||||
"位置": "J1~J6 输出毂:P04/P05/P06/P07/P08/P09 各 3 颗",
|
||||
"备注": "Ø2.8 通孔 + Ø5.6 沉孔,BCD 29(3 孔均布),旋入电机输出毂螺纹孔(深 6mm)"
|
||||
},
|
||||
{
|
||||
"id": "S02",
|
||||
"规格": "M3 x 10",
|
||||
"螺纹": "M3",
|
||||
"长度mm": 10,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 20,
|
||||
"位置": "P04/P05/P06/P07/P08 各 4 颗 -> 下级电机尾端面",
|
||||
"备注": "Ø2.8 通孔 + Ø5.5 沉孔(深 4mm),旋入电机尾端面 4 螺纹孔 (±17.55, ±20.46)"
|
||||
},
|
||||
{
|
||||
"id": "S03",
|
||||
"规格": "M3 x 16",
|
||||
"螺纹": "M3",
|
||||
"长度mm": 16,
|
||||
"头型": "沉头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P03 后置重载法兰 -> M1 电机尾端面",
|
||||
"备注": "Ø3.4 通孔 + Ø6.5 沉孔,孔距 35.1 x 40.9,轴向锁死防拉脱"
|
||||
},
|
||||
{
|
||||
"id": "S04",
|
||||
"位置": "P03 前置锁紧凸台(Ø10 x4) -> M1 电机前端面",
|
||||
"备注": "Ø3.2 通孔 + Ø6.0 沉孔,BCD 55(R27.5,4 孔 17.8/107.8/197.8/287.8°);座位为舱孔前段 4 个 Ø10 局部凸台(2026-10-02 平直舱孔方案),M3x10 旋入电机前端面咬合 3.5mm",
|
||||
"头型": "沉头内六角",
|
||||
"数量": 4,
|
||||
"螺纹": "M3",
|
||||
"规格": "M3 x 10",
|
||||
"长度mm": 10
|
||||
},
|
||||
{
|
||||
"id": "S05",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 8,
|
||||
"位置": "P01 立柱分段对锁:A-B 缝 4 颗 + B-C 缝 4 颗(每缝前后各 2 颗)",
|
||||
"备注": "统一 Ø3.9 直孔(母口壁与公榫壁同径,只有这一个孔,无过孔/沉孔/预埋):M4x16 从外面直接拧入自攻成型,螺纹吃在母口壁 3.6 + 公榫壁 6.9 共 10.5mm;孔位 x=-131/-93 非对称物理防反装"
|
||||
},
|
||||
{
|
||||
"id": "S06",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "沉头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P09 末端工具法兰 -> 末端工具/夹爪",
|
||||
"备注": "Ø3.7 通孔 + Ø7.0 沉孔(深 4mm),4 孔分布圆 R20"
|
||||
},
|
||||
{
|
||||
"id": "S07",
|
||||
"规格": "M5 x 25",
|
||||
"螺纹": "M5",
|
||||
"长度mm": 25,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P02 底座中核心 -> P01 立柱底部榫头",
|
||||
"备注": "Ø5.5 通孔 + Ø10 沉孔(深 6mm),非对称孔位防反装,轴向力由榫座面承载"
|
||||
},
|
||||
{
|
||||
"id": "S08",
|
||||
"规格": "M5 x 25",
|
||||
"螺纹": "M5",
|
||||
"长度mm": 25,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P02 前/后力臂 -> 中核心:前臂 2 颗 + 后臂 2 颗",
|
||||
"备注": "Ø5.5 通孔 + Ø10 沉孔;需另配 M5 六角螺母 4 枚(嵌入螺母沉槽)"
|
||||
},
|
||||
{
|
||||
"id": "S09",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P03 肩吊臂 -> P01_C 立柱顶面(从腔体上方往下拧)",
|
||||
"备注": "统一 Ø3.9 直孔(鞍座与立柱顶环同径,只有这一个孔,无过孔/沉孔/预埋):M4x16 从上方直接拧入自攻成型,穿鞍座 9mm 后吃进立柱顶环 7mm;载荷走 2403mm² 承压面,螺钉只防上抬"
|
||||
},
|
||||
{
|
||||
"id": "S11",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 1,
|
||||
"位置": "P01_C 立柱后壁 -> P03 榫头(横向防脱锁紧)",
|
||||
"备注": "与 S05/S09/S06 同为 M4x16(全机 M4x16 合计 17 颗);Ø3.9 直孔贯穿 11mm 后壁 + 榫头同径:从后方直接拧入自攻成型,榫头内咬合 4.7mm"
|
||||
},
|
||||
{
|
||||
"id": "S10",
|
||||
"规格": "M6 x 35",
|
||||
"螺纹": "M6",
|
||||
"长度mm": 35,
|
||||
"头型": "外六角",
|
||||
"数量": 6,
|
||||
"位置": "底座 6 组 28x7 紧固腰槽 -> 工作台/桌面",
|
||||
"备注": "需另配 M6 螺母 + 平垫圈各 6(建议加弹垫防松)"
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "S01",
|
||||
"规格": "M2 x 10",
|
||||
"螺纹": "M2",
|
||||
"长度mm": 10,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 18,
|
||||
"位置": "J1~J6 输出毂:P04/P05/P06/P07/P08/P09 各 3 颗",
|
||||
"备注": "Ø2.8 通孔 + Ø5.6 沉孔,BCD 29(3 孔均布),旋入电机输出毂螺纹孔(深 6mm)"
|
||||
},
|
||||
{
|
||||
"id": "S02",
|
||||
"规格": "M3 x 10",
|
||||
"螺纹": "M3",
|
||||
"长度mm": 10,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 20,
|
||||
"位置": "P04/P05/P06/P07/P08 各 4 颗 -> 下级电机尾端面",
|
||||
"备注": "Ø2.8 通孔 + Ø5.5 沉孔(深 4mm),旋入电机尾端面 4x M3 螺纹径向槽(电机.STEP 实测:BCD55、相位 39.9°+90k,槽宽 2.5)"
|
||||
},
|
||||
{
|
||||
"id": "S03",
|
||||
"规格": "M3 x 16",
|
||||
"螺纹": "M3",
|
||||
"长度mm": 16,
|
||||
"头型": "沉头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P03 后置重载法兰 -> M1 电机尾端面",
|
||||
"备注": "Ø3.4 通孔 + Ø6.5 沉孔,孔位 BCD55、相位 39.9°+90k(电机.STEP 实测螺纹径向槽),轴向锁死防拉脱;旧 35.1×40.9 矩形孔距对角两颗偏出槽位 ~4.7mm,2026-10-02 修正"
|
||||
},
|
||||
{
|
||||
"id": "S04",
|
||||
"位置": "P03 前置锁紧凸台(Ø10 x4) -> M1 电机前端面",
|
||||
"备注": "Ø3.2 通孔 + Ø6.0 沉孔,BCD 55(R27.5,4 孔 17.8/107.8/197.8/287.8°);座位为舱孔前段 4 个 Ø10 局部凸台(2026-10-02 平直舱孔方案),M3x10 旋入电机前端面咬合 3.5mm",
|
||||
"头型": "沉头内六角",
|
||||
"数量": 4,
|
||||
"螺纹": "M3",
|
||||
"规格": "M3 x 10",
|
||||
"长度mm": 10
|
||||
},
|
||||
{
|
||||
"id": "S05",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 8,
|
||||
"位置": "P01 立柱分段对锁:A-B 缝 4 颗 + B-C 缝 4 颗(每缝前后各 2 颗)",
|
||||
"备注": "统一 Ø3.9 直孔(母口壁与公榫壁同径,只有这一个孔,无过孔/沉孔/预埋):M4x16 从外面直接拧入自攻成型,螺纹吃在母口壁 3.6 + 公榫壁 6.9 共 10.5mm;孔位 x=-131/-93 非对称物理防反装"
|
||||
},
|
||||
{
|
||||
"id": "S06",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "沉头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P09 末端工具法兰 -> 末端工具/夹爪",
|
||||
"备注": "Ø3.7 通孔 + Ø7.0 沉孔(深 4mm),4 孔分布圆 R20"
|
||||
},
|
||||
{
|
||||
"id": "S07",
|
||||
"规格": "M5 x 25",
|
||||
"螺纹": "M5",
|
||||
"长度mm": 25,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P02 底座中核心 -> P01 立柱底部榫头",
|
||||
"备注": "Ø5.5 通孔 + Ø10 沉孔(深 6mm),非对称孔位防反装,轴向力由榫座面承载"
|
||||
},
|
||||
{
|
||||
"id": "S08",
|
||||
"规格": "M5 x 25",
|
||||
"螺纹": "M5",
|
||||
"长度mm": 25,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P02 前/后力臂 -> 中核心:前臂 2 颗 + 后臂 2 颗",
|
||||
"备注": "Ø5.5 通孔 + Ø10 沉孔;需另配 M5 六角螺母 4 枚(嵌入螺母沉槽)"
|
||||
},
|
||||
{
|
||||
"id": "S09",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P03 肩吊臂 -> P01_C 立柱顶面(从腔体上方往下拧)",
|
||||
"备注": "统一 Ø3.9 直孔(鞍座与立柱顶环同径,只有这一个孔,无过孔/沉孔/预埋):M4x16 从上方直接拧入自攻成型,穿鞍座 9mm 后吃进立柱顶环 7mm;载荷走 2403mm² 承压面,螺钉只防上抬"
|
||||
},
|
||||
{
|
||||
"id": "S11",
|
||||
"规格": "M4 x 16",
|
||||
"螺纹": "M4",
|
||||
"长度mm": 16,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 1,
|
||||
"位置": "P01_C 立柱后壁 -> P03 榫头(横向防脱锁紧)",
|
||||
"备注": "与 S05/S09/S06 同为 M4x16(全机 M4x16 合计 17 颗);Ø3.9 直孔贯穿 11mm 后壁 + 榫头同径:从后方直接拧入自攻成型,榫头内咬合 4.7mm"
|
||||
},
|
||||
{
|
||||
"id": "S10",
|
||||
"规格": "M6 x 35",
|
||||
"螺纹": "M6",
|
||||
"长度mm": 35,
|
||||
"头型": "外六角",
|
||||
"数量": 6,
|
||||
"位置": "底座 6 组 28x7 紧固腰槽 -> 工作台/桌面",
|
||||
"备注": "需另配 M6 螺母 + 平垫圈各 6(建议加弹垫防松)"
|
||||
},
|
||||
{
|
||||
"id": "S12",
|
||||
"规格": "M3 x 8",
|
||||
"螺纹": "M3",
|
||||
"长度mm": 8,
|
||||
"头型": "圆柱头内六角",
|
||||
"数量": 4,
|
||||
"位置": "P04 前端压盖 -> M2 电机前端盖(J2)",
|
||||
"备注": "Ø3.4 通孔 + Ø6.0 沉孔(深 3.0,自压盖前端面 Y=161 沉到 Y=158),BCD 55(R27.5,4 孔 17.8/107.8/197.8/287.8°,与 P03→M1 的 S04 同约定);螺钉头全沉入压盖,头面 Y=158 压孔口台阶,压盖背面 Y=156 与电机前端盖外端面0.0 间隙面压贴合 —— 与后端安装板 S02 的 4xM3x10 一起把 M2 前后双向夹死,消掉大扭矩下电机绕 J2 轴点头/翘起的倾向;M3x8 穿过 3.5mm 端盖后再自攻入壳体约 4.5mm"
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 132 KiB |
+139
-90
@@ -3,145 +3,169 @@
|
||||
"截面": "吊臂根座 @z=480",
|
||||
"零件": "P03_肩吊臂_螺旋体35%_墙5底5_竖打",
|
||||
"关节": "J1",
|
||||
"A": 975.4800000000001,
|
||||
"I": 391731.4096400291,
|
||||
"c": 37.23299062774025,
|
||||
"A": 957.2387999999999,
|
||||
"I": 401134.9375110902,
|
||||
"c": 37.922833863111535,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
-110.76700947225983,
|
||||
128.00000000000003
|
||||
-110.07716623688886,
|
||||
128.0
|
||||
],
|
||||
"M": 23529.618031505226,
|
||||
"F": 41.71918090692416,
|
||||
"sigma": 2.2364253314442926,
|
||||
"tau": 0.06415177283018231,
|
||||
"SF": 5.365705633574787
|
||||
"M": 14218.240146201913,
|
||||
"F": 41.72937644104729,
|
||||
"sigma": 1.3441760077937133,
|
||||
"tau": 0.06539022933626484,
|
||||
"L": 526.087122895359,
|
||||
"defl": 1.6350063694519446,
|
||||
"bend_axis": 1,
|
||||
"SF": 8.927402312213866
|
||||
},
|
||||
{
|
||||
"截面": "吊臂颊板 @z=560",
|
||||
"零件": "P03_肩吊臂_螺旋体35%_墙5底5_竖打",
|
||||
"关节": "J1",
|
||||
"A": 1102.4399999999998,
|
||||
"I": 605072.9254967812,
|
||||
"c": 43.53697862036803,
|
||||
"A": 1130.88,
|
||||
"I": 667937.0157562625,
|
||||
"c": 44.90221547001558,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
-96.71968814629368,
|
||||
128.0
|
||||
-98.0257258064516,
|
||||
127.99999999999997
|
||||
],
|
||||
"M": 23529.618031505226,
|
||||
"F": 41.71918090692416,
|
||||
"sigma": 1.6930330775286337,
|
||||
"tau": 0.05676387954027997,
|
||||
"SF": 7.087870969134711
|
||||
"M": 13715.34105182334,
|
||||
"F": 41.72937644104729,
|
||||
"sigma": 0.922016873786273,
|
||||
"tau": 0.05534987325054023,
|
||||
"L": 518.8210523704677,
|
||||
"defl": 0.9212023341880543,
|
||||
"bend_axis": 1,
|
||||
"SF": 13.014946191518016
|
||||
},
|
||||
{
|
||||
"截面": "肩叉腹板 @z=560",
|
||||
"零件": "P04_肩叉_螺旋体30%_墙5底5_竖打",
|
||||
"关节": "J2",
|
||||
"A": 2217.3650000000002,
|
||||
"I": 207369.8226041668,
|
||||
"c": 16.75000010000293,
|
||||
"I": 207369.82260416728,
|
||||
"c": 16.750000100002925,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
27.25,
|
||||
28.249999999999996,
|
||||
124.95500000000001
|
||||
],
|
||||
"M": 15271.482458030512,
|
||||
"F": 34.395230761329984,
|
||||
"sigma": 1.2335320997379497,
|
||||
"tau": 0.023267638003664246,
|
||||
"SF": 9.728161920187782
|
||||
"M": 7818.411904486269,
|
||||
"F": 34.39607710754928,
|
||||
"sigma": 0.6315210117722179,
|
||||
"tau": 0.023268210538780903,
|
||||
"L": 384.4650327126411,
|
||||
"defl": 0.9288282882988673,
|
||||
"bend_axis": 1,
|
||||
"SF": 19.001743055745322
|
||||
},
|
||||
{
|
||||
"截面": "肩叉腹板 @z=600",
|
||||
"零件": "P04_肩叉_螺旋体30%_墙5底5_竖打",
|
||||
"关节": "J2",
|
||||
"A": 2368.7748,
|
||||
"I": 276311.8560102971,
|
||||
"c": 21.62027641094759,
|
||||
"A": 2393.7248,
|
||||
"I": 289729.609479537,
|
||||
"c": 22.374104171783745,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
25.562276310943524,
|
||||
126.94624799284419
|
||||
26.31610407177968,
|
||||
126.97437823679647
|
||||
],
|
||||
"M": 15271.482458030512,
|
||||
"F": 34.395230761329984,
|
||||
"sigma": 1.1949312516479667,
|
||||
"tau": 0.02178039302933946,
|
||||
"SF": 10.042418744551561
|
||||
"M": 7884.930337951309,
|
||||
"F": 34.39607710754928,
|
||||
"sigma": 0.6089065355988086,
|
||||
"tau": 0.02155390446776669,
|
||||
"L": 391.71974764776274,
|
||||
"defl": 0.6959926630802069,
|
||||
"bend_axis": 1,
|
||||
"SF": 19.707458039022367
|
||||
},
|
||||
{
|
||||
"截面": "大臂旋转座横梁 @z=480",
|
||||
"零件": "P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
|
||||
"关节": "J2",
|
||||
"A": 1250.9348,
|
||||
"I": 490359.7093282266,
|
||||
"c": 35.000000100000086,
|
||||
"I": 490359.70932822645,
|
||||
"c": 35.00000010000009,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.5,
|
||||
19.500000000000004,
|
||||
165.03578024690017
|
||||
],
|
||||
"M": 15271.482458030512,
|
||||
"F": 34.395230761329984,
|
||||
"sigma": 1.090019994282287,
|
||||
"tau": 0.04124343342434392,
|
||||
"SF": 11.008972370182331
|
||||
"M": 8644.341221650102,
|
||||
"F": 34.39607710754928,
|
||||
"sigma": 0.6170000060499929,
|
||||
"tau": 0.041244448280856785,
|
||||
"L": 420.96884917144854,
|
||||
"defl": 0.5206738812710487,
|
||||
"bend_axis": 1,
|
||||
"SF": 19.448946324690457
|
||||
},
|
||||
{
|
||||
"截面": "大臂旋转座套筒 @z=440",
|
||||
"零件": "P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
|
||||
"关节": "J3",
|
||||
"A": 923.6637500000008,
|
||||
"I": 460541.5056708324,
|
||||
"I": 460541.5056708321,
|
||||
"c": 35.00071932511497,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.499280774885154,
|
||||
19.49928077488516,
|
||||
162.1529414131134
|
||||
],
|
||||
"M": 10944.224641918447,
|
||||
"F": 28.80059116294328,
|
||||
"sigma": 0.831750733009024,
|
||||
"tau": 0.046771226806740965,
|
||||
"SF": 14.42739936830306
|
||||
"M": 7224.027167183286,
|
||||
"F": 28.801398145740663,
|
||||
"sigma": 0.5490192396606857,
|
||||
"tau": 0.04677253732065479,
|
||||
"L": 384.25524483485555,
|
||||
"defl": 0.38601035205736267,
|
||||
"bend_axis": 1,
|
||||
"SF": 21.85715751494692
|
||||
},
|
||||
{
|
||||
"截面": "大臂骨架中段 @z=350",
|
||||
"零件": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
|
||||
"关节": "J3",
|
||||
"A": 1516.920000000001,
|
||||
"I": 1131945.0546959997,
|
||||
"I": 1131945.0546960002,
|
||||
"c": 35.00000010000099,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.499999999999996,
|
||||
19.499999999999993,
|
||||
119.46110407931855
|
||||
],
|
||||
"M": 10944.224641918447,
|
||||
"F": 28.80059116294328,
|
||||
"sigma": 0.33839793015788383,
|
||||
"tau": 0.028479344160809335,
|
||||
"SF": 35.46120980823154
|
||||
"M": 5824.4734611263175,
|
||||
"F": 28.801398145740663,
|
||||
"sigma": 0.1800940521592921,
|
||||
"tau": 0.028480142142374657,
|
||||
"L": 287.98631358672816,
|
||||
"defl": 0.07112524430753915,
|
||||
"bend_axis": 1,
|
||||
"SF": 66.63185072534252
|
||||
},
|
||||
{
|
||||
"截面": "大臂骨架抱箍 @z=325",
|
||||
"零件": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
|
||||
"关节": "J4",
|
||||
"A": 562.2200000000005,
|
||||
"I": 255126.52178866684,
|
||||
"I": 255126.52178866687,
|
||||
"c": 34.31311712541806,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.5,
|
||||
19.499999999999996,
|
||||
100.98098075486462
|
||||
],
|
||||
"M": 6102.354966361885,
|
||||
"F": 23.11498093318896,
|
||||
"sigma": 0.820733255145853,
|
||||
"tau": 0.06167064743300382,
|
||||
"SF": 14.621071980161778
|
||||
"M": 4674.1717086407425,
|
||||
"F": 23.115225549656877,
|
||||
"sigma": 0.6286504444087546,
|
||||
"tau": 0.06167130006845235,
|
||||
"L": 259.0371452768405,
|
||||
"defl": 0.20489053402473298,
|
||||
"bend_axis": 1,
|
||||
"SF": 19.088509531375568
|
||||
},
|
||||
{
|
||||
"截面": "小臂骨架中段 @z=245",
|
||||
@@ -152,54 +176,79 @@
|
||||
"c": 35.00000010000015,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.5,
|
||||
19.5,
|
||||
120.60880570566229
|
||||
],
|
||||
"M": 6102.354966361885,
|
||||
"F": 23.11498093318896,
|
||||
"sigma": 0.17433637444141997,
|
||||
"tau": 0.020535356793291413,
|
||||
"SF": 68.83245127959344
|
||||
"M": 3519.6618136307306,
|
||||
"F": 23.115225549656877,
|
||||
"sigma": 0.10055217751682426,
|
||||
"tau": 0.020535574110643336,
|
||||
"L": 199.5734012002831,
|
||||
"defl": 0.019071177205131774,
|
||||
"bend_axis": 1,
|
||||
"SF": 119.34102568780449
|
||||
},
|
||||
{
|
||||
"截面": "小臂骨架抱箍 @z=195",
|
||||
"零件": "P07_小臂骨架_螺旋体30%_墙5底5_竖打",
|
||||
"关节": "J5",
|
||||
"A": 381.97200000000026,
|
||||
"I": 75885.39226040001,
|
||||
"I": 75885.39226040008,
|
||||
"c": 22.39173964832456,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.5,
|
||||
19.5,
|
||||
103.25743876514508
|
||||
],
|
||||
"M": 2105.5758538864893,
|
||||
"F": 16.322293440980538,
|
||||
"sigma": 0.6212988419199141,
|
||||
"tau": 0.06409747353594188,
|
||||
"SF": 19.31437689939684
|
||||
"M": 2244.921562213841,
|
||||
"F": 16.321781071344514,
|
||||
"sigma": 0.6624160151865485,
|
||||
"tau": 0.06409546146580575,
|
||||
"L": 159.53235542447365,
|
||||
"defl": 0.1254842711067732,
|
||||
"bend_axis": 1,
|
||||
"SF": 18.11550404109807
|
||||
},
|
||||
{
|
||||
"截面": "腕偏转座根部 @z=150",
|
||||
"零件": "P08_腕偏转座_螺旋体25%_墙4底5_竖打",
|
||||
"关节": "J5",
|
||||
"A": 877.5228499999998,
|
||||
"I": 452897.5474791441,
|
||||
"c": 35.000019545458414,
|
||||
"I": 452897.5474791437,
|
||||
"c": 35.00001954545841,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
18.49998055454244,
|
||||
19.499980554542446,
|
||||
153.23437984777257
|
||||
],
|
||||
"M": 2105.5758538864893,
|
||||
"F": 16.322293440980538,
|
||||
"sigma": 0.1627193533077502,
|
||||
"tau": 0.02790062978014853,
|
||||
"SF": 73.74660577285154
|
||||
"M": 2445.425561814797,
|
||||
"F": 16.321781071344514,
|
||||
"sigma": 0.18898301158149458,
|
||||
"tau": 0.02789975395742319,
|
||||
"L": 164.6512204903413,
|
||||
"defl": 0.02439681521795845,
|
||||
"bend_axis": 1,
|
||||
"SF": 63.497771040786255
|
||||
},
|
||||
{
|
||||
"截面": "工具法兰 @z=55",
|
||||
"截面": "工具法兰 @z=107",
|
||||
"零件": "P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
"关节": "J6"
|
||||
"关节": "J6",
|
||||
"A": 3734.962150000003,
|
||||
"I": 1169654.3875857776,
|
||||
"c": 35.0000685148389,
|
||||
"bend": 1,
|
||||
"centroid": [
|
||||
19.499931585161203,
|
||||
127.9986725177389
|
||||
],
|
||||
"M": 1390.8472530369402,
|
||||
"F": 10.722602788827801,
|
||||
"sigma": 0.04161891723455653,
|
||||
"tau": 0.004306309819830889,
|
||||
"L": 130.23020962176682,
|
||||
"defl": 0.0033611981177080657,
|
||||
"bend_axis": 1,
|
||||
"SF": 288.3304227346958
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"assumptions": {
|
||||
"motor": "60mm 方形标准关节电机模组,2 级行星减速",
|
||||
"T_cont_Nm": 2.5,
|
||||
"T_peak_Nm": 8.0,
|
||||
"T_allow_Nm": 2.0,
|
||||
"alpha_rad_s2": 5.0,
|
||||
"payload_kg": 0.5,
|
||||
"note": "电机参数为工程假定,仓库内无数据手册;结论对此敏感,见报告第 4 节"
|
||||
},
|
||||
"joints": {
|
||||
"J1": {
|
||||
"cn": "肩抬举",
|
||||
"axis": "roll(绕X)",
|
||||
"distal_mass_kg": 2.127,
|
||||
"arm_mass_kg": 1.627,
|
||||
"weight_N": 20.9,
|
||||
"reach_tip_mm": 512.0,
|
||||
"tau_gravity_Nm": 6.137,
|
||||
"tau_gravity_arm_only_Nm": 3.84,
|
||||
"I_distal_kgm2": 0.28468,
|
||||
"tau_dynamic_Nm": 1.423,
|
||||
"tau_required_static_Nm": 6.137,
|
||||
"tau_required_peak_Nm": 7.561,
|
||||
"duty_vs_Tcont_pct": 245.5,
|
||||
"duty_vs_Tallow_pct": 306.9,
|
||||
"peak_margin_x": 1.06,
|
||||
"status": "OVERLOAD"
|
||||
},
|
||||
"J2": {
|
||||
"cn": "肩俯仰",
|
||||
"axis": "pitch(绕Y)",
|
||||
"distal_mass_kg": 1.753,
|
||||
"arm_mass_kg": 1.253,
|
||||
"weight_N": 17.2,
|
||||
"reach_tip_mm": 394.0,
|
||||
"tau_gravity_Nm": 4.395,
|
||||
"tau_gravity_arm_only_Nm": 2.478,
|
||||
"I_distal_kgm2": 0.14623,
|
||||
"tau_dynamic_Nm": 0.731,
|
||||
"tau_required_static_Nm": 4.395,
|
||||
"tau_required_peak_Nm": 5.126,
|
||||
"duty_vs_Tcont_pct": 175.8,
|
||||
"duty_vs_Tallow_pct": 219.8,
|
||||
"peak_margin_x": 1.56,
|
||||
"status": "OVERLOAD"
|
||||
},
|
||||
"J3": {
|
||||
"cn": "大臂自转",
|
||||
"axis": "roll(绕Z)",
|
||||
"distal_mass_kg": 1.468,
|
||||
"arm_mass_kg": 0.968,
|
||||
"weight_N": 14.4,
|
||||
"reach_tip_mm": 250.0,
|
||||
"tau_gravity_Nm": 1.712,
|
||||
"tau_gravity_arm_only_Nm": 0.742,
|
||||
"I_distal_kgm2": 0.03871,
|
||||
"tau_dynamic_Nm": 0.194,
|
||||
"tau_required_static_Nm": 1.712,
|
||||
"tau_required_peak_Nm": 1.906,
|
||||
"duty_vs_Tcont_pct": 68.5,
|
||||
"duty_vs_Tallow_pct": 85.6,
|
||||
"peak_margin_x": 4.2,
|
||||
"status": "OK"
|
||||
},
|
||||
"J4": {
|
||||
"cn": "肘部俯仰",
|
||||
"axis": "pitch(绕Y)",
|
||||
"distal_mass_kg": 1.178,
|
||||
"arm_mass_kg": 0.678,
|
||||
"weight_N": 11.6,
|
||||
"reach_tip_mm": 241.0,
|
||||
"tau_gravity_Nm": 2.062,
|
||||
"tau_gravity_arm_only_Nm": 0.89,
|
||||
"I_distal_kgm2": 0.04258,
|
||||
"tau_dynamic_Nm": 0.213,
|
||||
"tau_required_static_Nm": 2.062,
|
||||
"tau_required_peak_Nm": 2.275,
|
||||
"duty_vs_Tcont_pct": 82.5,
|
||||
"duty_vs_Tallow_pct": 103.1,
|
||||
"peak_margin_x": 3.52,
|
||||
"status": "OVERLOAD"
|
||||
},
|
||||
"J5": {
|
||||
"cn": "腕部俯仰",
|
||||
"axis": "pitch(绕Y)",
|
||||
"distal_mass_kg": 0.832,
|
||||
"arm_mass_kg": 0.332,
|
||||
"weight_N": 8.2,
|
||||
"reach_tip_mm": 132.0,
|
||||
"tau_gravity_Nm": 0.863,
|
||||
"tau_gravity_arm_only_Nm": 0.23,
|
||||
"I_distal_kgm2": 0.01018,
|
||||
"tau_dynamic_Nm": 0.051,
|
||||
"tau_required_static_Nm": 0.863,
|
||||
"tau_required_peak_Nm": 0.914,
|
||||
"duty_vs_Tcont_pct": 34.5,
|
||||
"duty_vs_Tallow_pct": 43.1,
|
||||
"peak_margin_x": 8.75,
|
||||
"status": "OK"
|
||||
},
|
||||
"J6": {
|
||||
"cn": "腕自转",
|
||||
"axis": "roll(绕Z)",
|
||||
"distal_mass_kg": 0.547,
|
||||
"arm_mass_kg": 0.047,
|
||||
"weight_N": 5.4,
|
||||
"reach_tip_mm": 83.0,
|
||||
"tau_gravity_Nm": 0.0,
|
||||
"tau_gravity_arm_only_Nm": 0.0,
|
||||
"I_distal_kgm2": 0.0,
|
||||
"tau_dynamic_Nm": 0.0,
|
||||
"tau_required_static_Nm": 0.0,
|
||||
"tau_required_peak_Nm": 0.0,
|
||||
"duty_vs_Tcont_pct": 0.0,
|
||||
"duty_vs_Tallow_pct": 0.0,
|
||||
"peak_margin_x": null,
|
||||
"status": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# 6 自由度机械臂 · 关节电机力矩与负载能力校核
|
||||
|
||||
## 1. 前提
|
||||
|
||||
- 驱动:60mm 方形标准关节电机模组,2 级行星减速
|
||||
- 连续额定输出力矩 `T_cont` = 2.50 N·m;峰值堵转 `T_peak` = 8.00 N·m
|
||||
- 封闭打印外壳热衰减后许用连续力矩 `T_allow` = 2.00 N·m;末端负载 0.50 kg;角加速度 5.0 rad/s²
|
||||
- **电机参数为工程假定,仓库内无数据手册;结论对此敏感,见报告第 4 节**
|
||||
|
||||
载荷模型:每个零件按网格体积加权质心定位,随姿态做刚体变换,在可达姿态网格(俯仰轴取行程端点/中值,自转轴取 0/±60/±120/180°)上取重力力矩最大值。力矩为绕**关节自身轴**的分量——这才是电机要出的力。
|
||||
|
||||
## 2. 各关节校核结果
|
||||
|
||||
| 关节 | 名称 | 轴 | 以下质量(kg) | TCP力臂(mm) | 重力力矩(N·m) | 惯性力矩(N·m) | 峰值需求(N·m) | 负荷率(对T_allow) | 峰值裕度 | 判定 |
|
||||
|:---:|:---|:---|---:|---:|---:|---:|---:|---:|---:|:---:|
|
||||
| J1 | 肩抬举 | roll(绕X) | 2.13 | 512 | **6.14** | 1.42 | 7.56 | **307%** | 1.06× | OVERLOAD |
|
||||
| J2 | 肩俯仰 | pitch(绕Y) | 1.75 | 394 | **4.39** | 0.73 | 5.13 | **220%** | 1.56× | OVERLOAD |
|
||||
| J3 | 大臂自转 | roll(绕Z) | 1.47 | 250 | **1.71** | 0.19 | 1.91 | **86%** | 4.20× | OK |
|
||||
| J4 | 肘部俯仰 | pitch(绕Y) | 1.18 | 241 | **2.06** | 0.21 | 2.27 | **103%** | 3.52× | OVERLOAD |
|
||||
| J5 | 腕部俯仰 | pitch(绕Y) | 0.83 | 132 | **0.86** | 0.05 | 0.91 | **43%** | 8.75× | OK |
|
||||
| J6 | 腕自转 | roll(绕Z) | 0.55 | 83 | **0.00** | 0.00 | 0.00 | **0%** | — | OK |
|
||||
|
||||
## 3. 许用连续工况下的末端负载能力
|
||||
|
||||
去掉末端负载后各关节仍需 `tau_gravity_arm_only` 的重力力矩,余额除以该关节实测最大 TCP 力臂,即为还能挂的负载:
|
||||
|
||||
| 关节 | 仅臂身重力力矩(N·m) | 余额(N·m) | 可挂负载(kg) |
|
||||
|:---:|---:|---:|---:|
|
||||
| J1 | 3.84 | -1.84 | 0.00 |
|
||||
| J2 | 2.48 | -0.48 | 0.00 |
|
||||
| J3 | 0.74 | +1.26 | 0.51 |
|
||||
| J4 | 0.89 | +1.11 | 0.47 |
|
||||
| J5 | 0.23 | +1.77 | 1.37 |
|
||||
| J6 | 0.00 | +2.00 | 2.46 |
|
||||
|
||||
**整臂瓶颈:J1,可挂末端负载 0.00 kg。**
|
||||
|
||||
## 4. 结论要点
|
||||
|
||||
- **超载关节:J1、J2、J4**。
|
||||
- J1(肩抬举)重力 6.14 N·m,超 `T_allow` 207%;即使按峰值堵转算,裕度也只有 1.06×。
|
||||
- J2(肩俯仰)重力 4.39 N·m,超 `T_allow` 120%;即使按峰值堵转算,裕度也只有 1.56×。
|
||||
- J4(肘部俯仰)重力 2.06 N·m,超 `T_allow` 3%;即使按峰值堵转算,裕度也只有 3.52×。
|
||||
- 结论对电机参数高度敏感:若实际连续额定高于假定值,请用 `ARMT_CONT` / `ARMT_ALLOW` 覆盖后重跑本脚本。
|
||||
- 惯量按 Σm·d² 估,**未计入零件自身转动惯量**,对肩/肘是大臂量级的关节约低估一成,惯性力矩一栏应视为下限。
|
||||
- 本版不含被动弹簧平衡。旧版报告提出的 J2 拉簧预补偿(2.48 N·m)在几何上**尚未实现**;若要保留该方案,需要在 P04/P05 之间增加拉簧连杆机构。
|
||||
+101
-100
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"generated": "2026-09-30 00:06:07",
|
||||
"generated": "2026-10-02 12:06:22",
|
||||
"vol_min": 2.0,
|
||||
"step": 2.0,
|
||||
"combo_step": 6.0,
|
||||
"workers": 12,
|
||||
"fixture_included": true,
|
||||
"singles": {
|
||||
"J1": {
|
||||
"lo": -180.0,
|
||||
@@ -12,16 +13,16 @@
|
||||
},
|
||||
"J2": {
|
||||
"lo": -107.5,
|
||||
"hi": 10.25,
|
||||
"hi": 10.38,
|
||||
"jams": {
|
||||
"12.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P08_腕偏转座_螺旋体25%_墙4底5_竖打",
|
||||
3160.7,
|
||||
2557.1,
|
||||
[
|
||||
-53.9,
|
||||
137.2,
|
||||
-52.9,
|
||||
137.7,
|
||||
149.2
|
||||
]
|
||||
]
|
||||
@@ -30,9 +31,9 @@
|
||||
[
|
||||
"P04_肩叉_螺旋体30%_墙5底5_竖打",
|
||||
"P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
|
||||
16.7,
|
||||
16.5,
|
||||
[
|
||||
45.8,
|
||||
46.8,
|
||||
136.3,
|
||||
534.6
|
||||
]
|
||||
@@ -47,17 +48,17 @@
|
||||
},
|
||||
"J4": {
|
||||
"lo": -112.5,
|
||||
"hi": 17.5,
|
||||
"hi": 17.88,
|
||||
"jams": {
|
||||
"18.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
29.2,
|
||||
6.2,
|
||||
[
|
||||
-82.0,
|
||||
128.0,
|
||||
117.5
|
||||
116.1
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -67,7 +68,7 @@
|
||||
"P07_小臂骨架_螺旋体30%_墙5底5_竖打",
|
||||
56.3,
|
||||
[
|
||||
18.5,
|
||||
19.5,
|
||||
128.0,
|
||||
383.1
|
||||
]
|
||||
@@ -77,17 +78,17 @@
|
||||
},
|
||||
"J5": {
|
||||
"lo": -96.25,
|
||||
"hi": 35.38,
|
||||
"hi": 35.75,
|
||||
"jams": {
|
||||
"36.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
45.9,
|
||||
11.4,
|
||||
[
|
||||
-82.4,
|
||||
-82.2,
|
||||
128.0,
|
||||
139.5
|
||||
140.7
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -97,7 +98,7 @@
|
||||
"P08_腕偏转座_螺旋体25%_墙4底5_竖打",
|
||||
328.2,
|
||||
[
|
||||
58.4,
|
||||
59.4,
|
||||
139.3,
|
||||
227.9
|
||||
]
|
||||
@@ -118,7 +119,7 @@
|
||||
"hold_at": -180.0,
|
||||
"swept": "J2",
|
||||
"direction": "hi",
|
||||
"old": 10.25,
|
||||
"old": 10.375,
|
||||
"new": 5.0,
|
||||
"jams": {}
|
||||
},
|
||||
@@ -148,7 +149,7 @@
|
||||
"hold_at": -180.0,
|
||||
"swept": "J4",
|
||||
"direction": "hi",
|
||||
"old": 17.5,
|
||||
"old": 17.875,
|
||||
"new": 11.0,
|
||||
"jams": {}
|
||||
},
|
||||
@@ -168,7 +169,7 @@
|
||||
"hold_at": -180.0,
|
||||
"swept": "J5",
|
||||
"direction": "hi",
|
||||
"old": 35.375,
|
||||
"old": 35.75,
|
||||
"new": 29.0,
|
||||
"jams": {}
|
||||
},
|
||||
@@ -315,11 +316,11 @@
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
29.2,
|
||||
6.2,
|
||||
[
|
||||
-82.0,
|
||||
128.0,
|
||||
117.5
|
||||
116.1
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -332,17 +333,17 @@
|
||||
"swept": "J4",
|
||||
"direction": "lo",
|
||||
"old": -109.0,
|
||||
"new": -18.25,
|
||||
"new": -18.625,
|
||||
"jams": {
|
||||
"-18.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
29.2,
|
||||
6.2,
|
||||
[
|
||||
-82.0,
|
||||
128.0,
|
||||
117.5
|
||||
116.1
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -361,11 +362,11 @@
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
45.9,
|
||||
11.4,
|
||||
[
|
||||
-82.4,
|
||||
-82.2,
|
||||
128.0,
|
||||
139.4
|
||||
140.6
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -378,17 +379,17 @@
|
||||
"swept": "J5",
|
||||
"direction": "lo",
|
||||
"old": -96.25,
|
||||
"new": -36.25,
|
||||
"new": -36.625,
|
||||
"jams": {
|
||||
"-36.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
45.9,
|
||||
11.4,
|
||||
[
|
||||
-82.4,
|
||||
-82.2,
|
||||
128.0,
|
||||
139.4
|
||||
140.6
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -437,11 +438,11 @@
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
329.3,
|
||||
186.7,
|
||||
[
|
||||
-83.8,
|
||||
104.3,
|
||||
122.2
|
||||
-83.0,
|
||||
104.7,
|
||||
120.8
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -453,18 +454,18 @@
|
||||
"hold_at": 167.0,
|
||||
"swept": "J4",
|
||||
"direction": "lo",
|
||||
"old": -18.25,
|
||||
"old": -18.625,
|
||||
"new": -17.875,
|
||||
"jams": {
|
||||
"-18.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
329.3,
|
||||
186.7,
|
||||
[
|
||||
-83.8,
|
||||
104.3,
|
||||
122.2
|
||||
-83.0,
|
||||
104.7,
|
||||
120.8
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -483,11 +484,11 @@
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
18.2,
|
||||
2.5,
|
||||
[
|
||||
-82.5,
|
||||
108.7,
|
||||
138.8
|
||||
-82.0,
|
||||
108.0,
|
||||
138.4
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -530,17 +531,17 @@
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 167.0,
|
||||
"new": 155.75,
|
||||
"new": 157.625,
|
||||
"jams": {
|
||||
"162.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
142.2,
|
||||
52.0,
|
||||
[
|
||||
-82.8,
|
||||
104.4,
|
||||
118.7
|
||||
-82.4,
|
||||
104.8,
|
||||
117.4
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -548,11 +549,11 @@
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
142.2,
|
||||
52.0,
|
||||
[
|
||||
-82.8,
|
||||
151.6,
|
||||
118.7
|
||||
-82.4,
|
||||
151.2,
|
||||
117.4
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -565,17 +566,17 @@
|
||||
"swept": "J3",
|
||||
"direction": "lo",
|
||||
"old": -180.0,
|
||||
"new": -157.75,
|
||||
"new": -159.625,
|
||||
"jams": {
|
||||
"162.0": [
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
142.2,
|
||||
52.0,
|
||||
[
|
||||
-82.8,
|
||||
104.4,
|
||||
118.7
|
||||
-82.4,
|
||||
104.8,
|
||||
117.4
|
||||
]
|
||||
]
|
||||
],
|
||||
@@ -583,11 +584,11 @@
|
||||
[
|
||||
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
|
||||
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
|
||||
142.2,
|
||||
52.0,
|
||||
[
|
||||
-82.8,
|
||||
151.6,
|
||||
118.7
|
||||
-82.4,
|
||||
151.2,
|
||||
117.4
|
||||
]
|
||||
]
|
||||
]
|
||||
@@ -616,7 +617,7 @@
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J1",
|
||||
"direction": "hi",
|
||||
"old": 161.0,
|
||||
@@ -626,7 +627,7 @@
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J2",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -636,27 +637,27 @@
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 155.75,
|
||||
"new": 149.0,
|
||||
"old": 157.625,
|
||||
"new": 155.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J3",
|
||||
"direction": "lo",
|
||||
"old": -157.75,
|
||||
"old": -159.625,
|
||||
"new": -157.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J4",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -666,7 +667,7 @@
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J4",
|
||||
"direction": "lo",
|
||||
"old": -17.875,
|
||||
@@ -676,7 +677,7 @@
|
||||
{
|
||||
"pass_": 0,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J6",
|
||||
"direction": "hi",
|
||||
"old": 149.0,
|
||||
@@ -709,8 +710,8 @@
|
||||
"hold_at": -180.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 149.0,
|
||||
"new": 143.0,
|
||||
"old": 155.0,
|
||||
"new": 149.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -759,8 +760,8 @@
|
||||
"hold_at": 143.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 143.0,
|
||||
"new": 137.0,
|
||||
"old": 149.0,
|
||||
"new": 143.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -799,8 +800,8 @@
|
||||
"hold_at": -180.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 137.0,
|
||||
"new": 131.0,
|
||||
"old": 143.0,
|
||||
"new": 137.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -849,8 +850,8 @@
|
||||
"hold_at": 143.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 131.0,
|
||||
"new": 125.0,
|
||||
"old": 137.0,
|
||||
"new": 131.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -899,8 +900,8 @@
|
||||
"hold_at": -103.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 125.0,
|
||||
"new": 119.0,
|
||||
"old": 131.0,
|
||||
"new": 125.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -986,7 +987,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J3",
|
||||
"hold_at": 119.0,
|
||||
"hold_at": 125.0,
|
||||
"swept": "J1",
|
||||
"direction": "hi",
|
||||
"old": 131.0,
|
||||
@@ -996,7 +997,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J3",
|
||||
"hold_at": 119.0,
|
||||
"hold_at": 125.0,
|
||||
"swept": "J2",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -1006,7 +1007,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J3",
|
||||
"hold_at": 119.0,
|
||||
"hold_at": 125.0,
|
||||
"swept": "J4",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -1016,7 +1017,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J3",
|
||||
"hold_at": 119.0,
|
||||
"hold_at": 125.0,
|
||||
"swept": "J5",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -1026,7 +1027,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J3",
|
||||
"hold_at": 119.0,
|
||||
"hold_at": 125.0,
|
||||
"swept": "J6",
|
||||
"direction": "hi",
|
||||
"old": 119.0,
|
||||
@@ -1059,8 +1060,8 @@
|
||||
"hold_at": -13.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 119.0,
|
||||
"new": 113.0,
|
||||
"old": 125.0,
|
||||
"new": 119.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -1086,7 +1087,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J1",
|
||||
"direction": "hi",
|
||||
"old": 119.0,
|
||||
@@ -1096,7 +1097,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J2",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -1106,17 +1107,17 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 113.0,
|
||||
"new": 107.0,
|
||||
"old": 119.0,
|
||||
"new": 113.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J4",
|
||||
"direction": "hi",
|
||||
"old": 0.0,
|
||||
@@ -1126,7 +1127,7 @@
|
||||
{
|
||||
"pass_": 1,
|
||||
"hold": "J5",
|
||||
"hold_at": -36.25,
|
||||
"hold_at": -36.625,
|
||||
"swept": "J6",
|
||||
"direction": "hi",
|
||||
"old": 107.0,
|
||||
@@ -1159,8 +1160,8 @@
|
||||
"hold_at": -180.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 107.0,
|
||||
"new": 101.0,
|
||||
"old": 113.0,
|
||||
"new": 107.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
@@ -1209,8 +1210,8 @@
|
||||
"hold_at": 101.0,
|
||||
"swept": "J3",
|
||||
"direction": "hi",
|
||||
"old": 101.0,
|
||||
"new": 95.0,
|
||||
"old": 107.0,
|
||||
"new": 101.0,
|
||||
"jams": {}
|
||||
},
|
||||
{
|
||||
|
||||
+13
-9
@@ -211,17 +211,21 @@ class Joint:
|
||||
|
||||
def rear_mount_holes(self, shape, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0):
|
||||
"""Cut the 4x rear mounting bolt holes into a proximal link body.
|
||||
Matches the 4x tapped holes on the motor rear metal endplate:
|
||||
(±17.55, ±20.46) on the rear face at motor-local z = 0.
|
||||
Matches the 4x M3 tapped radial slots on the motor rear endplate
|
||||
(电机.STEP 实测):BCD55、相位 39.9°+90k,槽宽 2.5、径向可调约 ±1.25mm。
|
||||
旧版误用 35.1x40.9 矩形孔距:对角两颗偏出螺纹槽 ~4.7mm 无处咬合
|
||||
(2026-10-02 用户指出"螺钉没有对齐",按 STEP 实测槽位修正)。
|
||||
"""
|
||||
cuts = []
|
||||
for sx in (-17.55, 17.55):
|
||||
for sy in (-20.46, 20.46):
|
||||
h = cyl(hole_d / 2.0, -t_plate - 1.0, 3.0, sx, sy)
|
||||
cuts.append(self.L(h))
|
||||
if cb_d > 0 and cb_depth > 0:
|
||||
cb = cyl(cb_d / 2.0, -t_plate - 1.0, -t_plate + cb_depth, sx, sy)
|
||||
cuts.append(self.L(cb))
|
||||
for k in range(4):
|
||||
a = math.radians(39.9 + 90.0 * k)
|
||||
sx = 27.5 * math.cos(a)
|
||||
sy = 27.5 * math.sin(a)
|
||||
h = cyl(hole_d / 2.0, -t_plate - 1.0, 3.0, sx, sy)
|
||||
cuts.append(self.L(h))
|
||||
if cb_d > 0 and cb_depth > 0:
|
||||
cb = cyl(cb_d / 2.0, -t_plate - 1.0, -t_plate + cb_depth, sx, sy)
|
||||
cuts.append(self.L(cb))
|
||||
return shape.cut(*cuts)
|
||||
|
||||
def front_collar_pocket(self, shape, r_bore=30.25, z0=50.0, z1=56.5):
|
||||
|
||||
+62
-15
@@ -794,13 +794,15 @@ def p_girdle(j1):
|
||||
s = s.cut(Xcyl(1.95, X_COL - TENON / 2.0 - 1.0, X_COL - TENON / 2.0 + 14.0, Y_COL, Z_LOCK))
|
||||
|
||||
# 4x M3 连接螺孔(螺丝刀通道直接走两竖板之间的开放腔体,无需再钻 Ø8.5 直通隧道)
|
||||
for dy in (-17.55, 17.55):
|
||||
for dz in (-20.46, 20.46):
|
||||
py = Y_ARM + dy
|
||||
pz = Z_ARM + dz
|
||||
th = Xcyl(1.7, X_BACK - 0.5, X_MOUNT + 4.0, py, pz)
|
||||
cb = Xcyl(3.25, X_BACK - 0.5, X_BACK + 4.5, py, pz)
|
||||
s = s.cut(th).cut(cb)
|
||||
# 孔位 = 电机尾端 4x M3 螺纹径向槽实测位(电机.STEP):BCD55、相位 39.9°+90k。
|
||||
# 旧 35.1×40.9 矩形孔距与实际槽位差 ~4.7mm(对角两颗螺钉无处咬合),
|
||||
# 2026-10-02 用户指出"螺钉没有对齐"后按 STEP 实测修正。
|
||||
for dy, dz in ((21.06, 17.65), (-17.65, 21.06), (-21.06, -17.65), (17.65, -21.06)):
|
||||
py = Y_ARM + dy
|
||||
pz = Z_ARM + dz
|
||||
th = Xcyl(1.7, X_BACK - 0.5, X_MOUNT + 4.0, py, pz)
|
||||
cb = Xcyl(3.25, X_BACK - 0.5, X_BACK + 4.5, py, pz)
|
||||
s = s.cut(th).cut(cb)
|
||||
|
||||
# 中央走线孔 (Ø26mm)
|
||||
wire_hole = Xcyl(13.0, X_COL - 40.0, X_MOUNT + 2.0, Y_ARM, Z_ARM)
|
||||
@@ -883,6 +885,11 @@ def p_fork(j1, j2):
|
||||
2. J1 从动法兰圆盘已删除(盘面在 column 前缘形成鼓楞):J1 由 boss(Ø35.6)贯通 P03 前盖中心孔直连电机输出盘;column 顶边保持 Z=646.5 与 P03 环齐高,R=18mm 大圆弧顺滑过渡;
|
||||
3. 头部转接凸台加长贯通 P03 前盖中心孔 (R=19.5mm),直连 J1 旋转法兰盘;
|
||||
4. M2 保留后端安装板(R=35mm, 带 rear_mount_holes 螺钉孔),前端定位环鼓出圆盘已删除(前端定位由 column 端面 pocket 承担),中间镂空散热;
|
||||
4b. 【2026-10-02 新增】M2 前端压盖:原方案电机只被后端安装板(S02 4xM3x10)单面锁死,
|
||||
大扭矩下电机绕 Y 轴在 Ø60.3 座孔里点头/翘起,固定不牢。现在前端盖外端面
|
||||
(Y=156.0) 再加一枚同径 R35 压盖,用 4x M3x8 在电机前端盖 BCD55 上把电机
|
||||
从前端夹一次 —— 电机被前后两块同心 Ø60.3 端面双向夹紧,倾斜自由度归零;
|
||||
螺钉沉在压盖沉孔内不外凸,不占用 J2 轴向间隙(P05 法兰盘已相应后移);
|
||||
5. 严格满足 single-solid 流形拓扑与 360° 全范围零干涉 (COLLISION pairs = 0)。
|
||||
"""
|
||||
# 1. M2 后端安装法兰盘 (Y: 92..100, R=35mm)
|
||||
@@ -892,6 +899,28 @@ def p_fork(j1, j2):
|
||||
front_collar = Ycyl(35.0, Y_COVER - 6.5, Y_COVER - 0.5, X_ARM, Z_SH)
|
||||
s = rear_plate.fuse(front_collar)
|
||||
|
||||
# 2b. 【2026-10-02 新增】M2 前端压盖 (Y: 155.5..161.0, R=35,与后端板同径)
|
||||
# 根部与 front_collar 前端面 (Y_COVER-0.5=155.5) 共面熔接成一体,无缝隙、无悬浮面。
|
||||
# 压盖背面 (Y_COVER=156.0) 与电机前端盖外端面 0.0 间隙面压贴合;Ø37 中央让位孔
|
||||
# 让出 P05 的 J2 直锁凸台 (R17.8) 与 3xS01 输出螺钉头;Ø60.3 让位槽把压盖背面
|
||||
# 精确落在 Y=156.0,不让根部那 0.5mm 啃到电机前端盖 R30.15 的外圆。
|
||||
FP_Y0 = Y_COVER - 0.5 # 155.5 压盖根部(= front_collar 前端面,保证熔接)
|
||||
FP_Y1 = Y_COVER + 5.0 # 161.0 压盖前端面(与 P05 法兰盘 162.0 留 1.0mm 动间隙)
|
||||
FP_BORE = 27.5 # 4xM3 分布圆半径 (BCD55,与 P03→M1 的 S04 同约定)
|
||||
fp = Ycyl(35.0, FP_Y0, FP_Y1, X_ARM, Z_SH)
|
||||
fp = fp.cut(Ycyl(18.5, FP_Y0 - 0.5, FP_Y1 + 0.5, X_ARM, Z_SH)) # Ø37 中央让位孔
|
||||
fp = fp.cut(Ycyl(30.15, FP_Y0 - 0.5, Y_COVER, X_ARM, Z_SH)) # Ø60.3 让位槽
|
||||
for deg in (17.8, 107.8, 197.8, 287.8):
|
||||
rad = math.radians(deg)
|
||||
fu, fz = X_ARM + FP_BORE * math.cos(rad), Z_SH + FP_BORE * math.sin(rad)
|
||||
# Ø6.0 沉孔深 3.0 (自前端面 FP_Y1 往 -Y 沉到 Y=158.0):M3 圆柱头(Ø5.5x3.0)
|
||||
# 全沉入压盖,头面 Y=158.0 压在 Ø3.4 孔口台阶上;压盖背面(156.0)压电机前端盖,
|
||||
# 中间 2.0mm 压盖本体受压 —— 夹紧力全部走面压,螺钉只提供压紧力不提供定位。
|
||||
fp = fp.cut(Ycyl(3.0, Y_COVER + 2.0, FP_Y1 + 0.5, fu, fz))
|
||||
fp = fp.cut(Ycyl(1.7, Y_COVER - 0.5, Y_COVER + 2.0, fu, fz))
|
||||
# 压盖在第 7 步 M2 接口成型(座孔/让位槽)之后才熔接,避免被那些 R30.15~30.5 的
|
||||
# 圆柱刀具蹭掉根部一圈 0.1mm 薄边。
|
||||
|
||||
# 3. J1 从动法兰圆盘已删除:原 R=36.5 圆盘盘面在 column 前缘形成圆盘鼓楞,
|
||||
# J1 与 P04 的连接由 boss(Ø35.6 贯通 P03 前盖中心孔 R=19.5)直连电机输出盘
|
||||
# 承担,螺钉走 boss 端面 4xB 孔,圆盘无传力功能已去除。
|
||||
@@ -960,6 +989,10 @@ def p_fork(j1, j2):
|
||||
c_m2 = [cyl(BORE_R, -0.6, COVER_Z - 3.5), cyl(CB_R, COVER_Z - 3.5, COVER_Z + 0.6)]
|
||||
s = s.cut(*[j2.L(item) for item in c_m2])
|
||||
|
||||
# 7b. M2 前端压盖熔接(见 2b):必须在 6/7 步所有 R30.15~30.5 圆柱刀具走完之后,
|
||||
# 否则压盖根部会被 front_collar_pocket / c_m2 啃掉一圈 0.1mm 薄边。
|
||||
s = s.fuse(fp)
|
||||
|
||||
# 8. 头部转接凸台加长(贯通 P03 前盖中心孔,跨越装配缝直连 J1 输出盘)
|
||||
boss = cyl(17.8, 54.942, 61.5 + X_FORK_GAP)
|
||||
s = s.fuse(j1.L(boss))
|
||||
@@ -977,17 +1010,22 @@ def p_fork(j1, j2):
|
||||
|
||||
|
||||
|
||||
def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket"):
|
||||
def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hub_gap=1.0):
|
||||
"""Half-wrapped (半包) 双曲面有机流线型 Pitch-to-Roll 结构座 (P05 & P08):
|
||||
1. 顶部从动法兰与前侧 Loft 自然相切顺接;
|
||||
2. 严格清除 z_plate 顶面以上与上级旋转件贴面的多余材料 (cut_top_solid),保证关节大范围摆动动态 0 干涉;
|
||||
3. 下方电机实行半包散热设计,-Y侧敞开,+Y侧背脊与圆柱一体化融合;
|
||||
4. 严格满足 single-solid 流形拓扑与零干涉 (COLLISION pairs = 0)。
|
||||
|
||||
hub_gap = 从动法兰盘背面与上级电机前端盖之间的轴向让位间隙。
|
||||
默认 1.0(贴合防磨)。P05 传 6.0:J2 处 P04 新增了 M2 前端压盖
|
||||
(Y 155.5..161.0),必须让出这 6.0mm 才能装下;见 p_shoulder_roll_base。
|
||||
"""
|
||||
z_bot = z_r - 56.0 # roll 电机底端 (front collar)
|
||||
z_plate = z_r + 7.0 # roll 电机顶端安装板 (rear plate top)
|
||||
y_out = HL.Y_HUB + HL.PLATE_T # 174.0 (俯仰直连法兰外侧面)
|
||||
y_hub_in = HL.Y_COVER + 1.0 # 157.0 (从动法兰内侧面,预留 1.0mm 动间隙)
|
||||
y_hub_in = HL.Y_COVER + hub_gap # 157.0 / (P05) 162.0 从动法兰内侧面
|
||||
z_hub_in = y_hub_in - HL.Y_REAR # 法兰盘背面的电机局部 z(凸台长度跟着它走)
|
||||
y_cyl_max = HL.Y_AXIS + 35.0 # 163.0 (下方电机外轮廓极值)
|
||||
|
||||
# 1. 基础立柱:Ø70mm 实心圆柱 (Z: z_bot .. z_plate, R=35mm)
|
||||
@@ -1065,8 +1103,10 @@ def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket"):
|
||||
# 9. 电机定子配合空腔
|
||||
s = s.cut(cyl(30.5, z_r - 49.5, z_r - 0.5, HL.X_ARM, HL.Y_AXIS))
|
||||
|
||||
# 10. 俯仰轴直连法兰结构(凸台延伸至 59.5 跨过间隙与法兰盘牢固熔接,保持单实体)
|
||||
boss = cyl(17.8, 54.942, 59.5)
|
||||
# 10. 俯仰轴直连法兰结构(凸台延伸至法兰盘背面之后 2.5mm,跨过间隙与法兰盘牢固熔接,
|
||||
# 保持单实体)。凸台长度跟着 y_hub_in 走:法兰盘后移时凸台同步加长,
|
||||
# 3xS01 输出螺钉的头面台阶(局部 z=58)、杆长、咬合量与改前完全一致。
|
||||
boss = cyl(17.8, 54.942, z_hub_in + 2.5)
|
||||
s = s.fuse(j_pitch.L(boss))
|
||||
s = s.cut(j_pitch.L(cyl(3.5, 54.0, 74.0 + 30.0)))
|
||||
for a in AB.HUB_M2_CLOCK:
|
||||
@@ -1089,8 +1129,15 @@ def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket"):
|
||||
|
||||
|
||||
def p_shoulder_roll_base(j2, j3, half=None):
|
||||
"""P05_大臂旋转座_螺旋体30%_墙4底5_竖打(深度圆角优化版)"""
|
||||
return make_pitch_to_roll_bracket(j2, j3, Z_SH, Z_R3, name="P05_大臂旋转座_螺旋体30%_墙4底5_竖打")
|
||||
"""P05_大臂旋转座_螺旋体30%_墙4底5_竖打(深度圆角优化版)
|
||||
|
||||
hub_gap=6.0:J2 处 P04 新增了 M2 前端压盖(前端夹紧电机,消除大扭矩下的倾斜),
|
||||
压盖占 Y 155.5..161.0,所以从动法兰盘背面必须从 Y=157 后移到 Y=162(留 1.0mm 动间隙)。
|
||||
P08 保持默认 1.0 —— J5 处 P07 没有前端压盖,不需要这段轴向空间。
|
||||
"""
|
||||
return make_pitch_to_roll_bracket(j2, j3, Z_SH, Z_R3,
|
||||
name="P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
|
||||
hub_gap=6.0)
|
||||
|
||||
|
||||
def p_humerus_bone(j3, j4):
|
||||
@@ -1394,9 +1441,9 @@ def build():
|
||||
("P03_肩吊臂_螺旋体35%_墙5底5_竖打", "肩关节安装悬臂", lambda: p_girdle(j1), 0x4b5563,
|
||||
"轻量化板式桁架版:主干由 66mm 实心块改为双侧 6mm 桁架竖板+帐篷减重窗(斜面≥52°自支撑)+R4内角,外轮廓与旧版并集完全一致,实心 728->286cm³;底部 9mm 承力鞍座(Ø3.9 安装孔)+12mm 中位龙骨筋(自鞍座斜拉至法兰下缘)+40~45°外扩斜撑;保留 10mm 后置法兰、侧开式电机托架、Ø60.7 直通电机舱(平孔无台阶)(M1 从+Y滑入,后端 4xM3x16 锁死)、大散热窗与对流槽;无封闭空腔、免支撑"),
|
||||
("P04_肩叉_螺旋体30%_墙5底5_竖打", "肩叉", lambda: p_fork(j1, j2), 0x2563eb,
|
||||
"齐平插槽版:叉板背面与P03托架端面(11.5)0.0硬贴平(用户要求外观齐平),P03侧插槽独立加长3mm,M1电机可倾斜卡入,J1输出凸台贯通直连电机,J1从动法兰圆盘鼓楞已删(接缝区与P03止脱环齐径齐高R36.5/顶646.5),单实体全角度零干涉"),
|
||||
"齐平插槽+ M2 前端压盖版:叉板背面与P03托架端面(11.5)0.0硬贴平(用户要求外观齐平),P03侧插槽独立加长3mm,M1电机可倾斜卡入,J1输出凸台贯通直连电机,J1从动法兰圆盘鼓楞已删(接缝区与P03止脱环齐径齐高R36.5/顶646.5),单实体全角度零干涉;【2026-10-02 新增】M2 前端压盖(R35, Y155.5..161.0):电机前端盖外端面加 4xM3x8 沉孔(BCD55)夹紧,与后端安装板 4xM3x10 形成前后双向面压,消除大扭矩下电机在座孔里的倾斜"),
|
||||
("P05_大臂旋转座_螺旋体30%_墙4底5_竖打", "大臂旋转座", lambda: p_shoulder_roll_base(j2, j3, None), 0x059669,
|
||||
"V3圆角:Loft外轮廓R=10mm,L型转角内部2mm倒角,后缘凸包两角90°竖棱磨R2圆角(结构/后缘高度不变,打印无悬挂),保证J2全角度0干涉"),
|
||||
"V3圆角+ J2前端夹紧配合:Loft外轮廓R=10mm,L型转角内部2mm倒角,后缘凸包两角90°竖棱磨R2圆角(结构/后缘高度不变,打印无悬挂);J2 从动法兰盘背面由 Y=157 后移至 Y=162(hub_gap=6.0,给 P04 新增的 M2 前端压盖让位,J2 直锁凸台同步加长到 Y=165.5,3xS01 输出螺钉咬合量不变);保证 J2 全角度 0 干涉"),
|
||||
("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "大臂骨架", lambda: p_humerus_bone(j3, j4), 0x0d9488,
|
||||
"Loft有机过渡版:借鉴P05圆弧变方形放样算法,顶端Ø70正圆与J3平齐无台阶,底端渐变融入M4抱紧套筒与壳体,单实体0干涉"),
|
||||
("P07_小臂骨架_螺旋体30%_墙5底5_竖打", "小臂骨架", lambda: p_forearm_bone(j4, j5, None), 0x7c3aed,
|
||||
|
||||
+32
-4
@@ -3,6 +3,11 @@
|
||||
|
||||
滑块范围 = 单关节硬限位(任意单关节推满零干涉,含 1° 安全余量);
|
||||
组合折叠约束保留在 travel_detail.json 的 combos 字段里,供报告引用。
|
||||
|
||||
注意 note 的措辞:单轴整周无干涉 **不等于** 任意组合姿态无干涉。sweep_v2 的组合扫描
|
||||
已经在 J1/J3/J6 上找到了真实卡死(工具法兰 P09 撞固定立柱 P01_A),例如 J3≈±162°。
|
||||
所以连续轴的 note 必须写明"单轴"并指向 combos,否则这份文件会误导人——查看器运行期
|
||||
另有胶囊碰撞模型按姿态夹取(ik-glue.js 的 dynLimit),但静态滑块范围按设计不压缩。
|
||||
"""
|
||||
import json
|
||||
import math
|
||||
@@ -13,20 +18,43 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from sweep_v2 import CHAIN, DOCS
|
||||
|
||||
d = json.load(open(DOCS + "/travel_detail.json", encoding="utf-8"))
|
||||
|
||||
# 组合扫描中,每个被扫轴真实出现卡死的最小绝对角
|
||||
combo_warn = {}
|
||||
for c in d.get("combos", []):
|
||||
if not c.get("jams"):
|
||||
continue
|
||||
ax = c["swept"]
|
||||
for a in c["jams"]:
|
||||
try:
|
||||
v = abs(float(a))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
combo_warn[ax] = min(combo_warn.get(ax, 1e9), v)
|
||||
|
||||
out = []
|
||||
for t in CHAIN[1:]:
|
||||
s = d["singles"][t]
|
||||
lo, hi = s["lo"], s["hi"]
|
||||
warn = combo_warn.get(t)
|
||||
warn_txt = (";组合折叠姿态下(其余关节同时处于各自极限)约 ±%.0f° 起与躯干占位"
|
||||
"(台架 P01 立柱)干涉,见 travel_detail.combos,"
|
||||
"运行期由查看器碰撞模型按姿态夹取" % warn) if warn is not None else ""
|
||||
if lo <= -179.9 and hi >= 179.9:
|
||||
out.append(dict(axis=t, lo=-180.0, hi=180.0, continuous=True, zero=0.0,
|
||||
note="整周无干涉,可 360° 连续旋转(全对全动态扫描验证)"))
|
||||
combo_warn_deg=warn,
|
||||
note="单轴整周无干涉(滑块按单轴限位给出)%s" % (warn_txt or
|
||||
";组合扫描未发现该轴卡死")))
|
||||
else:
|
||||
lo_out = float(math.ceil(lo + 1.0)) if lo < -0.5 else 0.0
|
||||
hi_out = float(math.floor(hi - 1.0)) if hi > 0.5 else 0.0
|
||||
out.append(dict(axis=t, lo=lo_out, hi=hi_out, continuous=False, zero=0.0,
|
||||
note="正向 +%d° / 反向 %d°,总行程 %d°(全对全动态扫描验证,含相邻件与电机)"
|
||||
% (hi_out, lo_out, hi_out - lo_out)))
|
||||
print("%-3s %7.1f .. %7.1f -> %s .. %s" % (t, lo, hi, out[-1]["lo"], out[-1]["hi"]))
|
||||
combo_warn_deg=warn,
|
||||
note="单轴正向 +%d° / 反向 %d°,总行程 %d°(单关节硬限位,含相邻件与电机)%s"
|
||||
% (hi_out, lo_out, hi_out - lo_out, warn_txt)))
|
||||
print("%-3s %7.1f .. %7.1f -> %s .. %s%s"
|
||||
% (t, lo, hi, out[-1]["lo"], out[-1]["hi"],
|
||||
(" 组合卡死阈值 ±%.0f°" % warn) if warn is not None else ""))
|
||||
|
||||
json.dump(out, open(DOCS + "/travel.json", "w", encoding="utf-8"),
|
||||
ensure_ascii=False, indent=1)
|
||||
|
||||
+20
-1
@@ -79,7 +79,26 @@ Y_M6 = Y_AXIS
|
||||
Y_CHEST = 62.0 # mast half width
|
||||
X_CHEST = 6.0 # chest plate half thickness
|
||||
|
||||
# ------------------------------------------------------------------ joint frames
|
||||
# ------------------------------------------------------------------ 台架(非常规构件)
|
||||
# P01 立柱 / P02 底盘 是【固定测试台架】,不是机器人本体的一部分(用户 2026-10-02
|
||||
# 确认);它在扫描中代表机械臂与**躯干/机身连接界面**的那块实体。因此:
|
||||
#
|
||||
# - 臂撞立柱 = 臂撞躯干,是真约束,不能忽略。sweep_v2 报出的组合卡死(J3 ±162°、
|
||||
# J4 ±18°、J5 ±36° 等,全部是工具法兰 P09 撞立柱 P01_A)就是这类真约束。
|
||||
# - 但台架的**位置与截面是一个假设**。躯干侧工作空间只和这个假设一样准:真机躯干
|
||||
# 挪后一点或外形不同,这些限位就变。所以台架本身不要为了放宽行程去改(那是改测试
|
||||
# 工装,不是改设计);要改的是"真机躯干占位"这个输入。
|
||||
# - `--no-stand` 仅用于诊断:给出不含躯干约束的自由空间行程,用来分辨某个限位到底是
|
||||
# 臂自身折叠产生的、还是躯干挡的。默认不排除。
|
||||
FIXTURE_PREFIX = ("P01_", "P02_")
|
||||
|
||||
|
||||
def is_fixture(name):
|
||||
"""该零件是否属于固定测试台架(代表躯干/安装界面,非机器人本体)。"""
|
||||
return name.startswith(FIXTURE_PREFIX)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ 关节表
|
||||
# tag, cn, rear, d, xdir, holder of the motor, driven part
|
||||
# J1 roll +X fore-aft, in the shoulder base -> drives P04 肩叉
|
||||
# J2 pitch +Y outboard in the fork -> drives P05 大臂旋转座
|
||||
|
||||
+28
-14
@@ -13,7 +13,7 @@ docs/screws.json 是紧固件清单的唯一事实源(规格 / 长度 / 数量
|
||||
台阶 z=63(J1, p_fork th 54..63) / 58(其余, th 54..58);头深藏在 Ø5.6 螺丝刀通道内
|
||||
S02 P04~P08 4xM3 :下级电机 rear_mount_holes:沉孔 Ø5.5 从 -t_plate-1 到 -t_plate+4,
|
||||
台阶 z = -t_plate+4 = -4(t_plate=8)/ -3(t_plate=7),头沉入 1mm
|
||||
S03 P03 后法兰 4xM3 :J1 局部 (±17.55, ±20.46, -5.5) (沉孔 Ø6.5 台阶 x=-56.5,法兰外端面 -61)
|
||||
S03 P03 后法兰 4xM3 :J1 局部 (R27.5 @39.9°+90k, -5.5) (沉孔 Ø6.5 台阶 x=-56.5,法兰外端面 -61)
|
||||
S04 P03 前置锁紧凸台 4xM3 :J1 局部 (R27.5 @17.8/107.8/197.8/287.8°, 62.5)
|
||||
(沉孔 Ø6.0 在 Ø10 凸台端面 x=11.5,M3x10 旋入电机前端面咬合 3.5mm)
|
||||
S05 P01 分段对锁 8xM4x16:Ø3.9 直孔 x∈{-131,-93}, z∈{180,329}(母口壁与公榫壁同径),
|
||||
@@ -61,6 +61,12 @@ PROX = {"J2": "P04 肩叉", "J3": "P05 大臂旋转座", "J4": "P06 大臂骨架
|
||||
COLOR = 0xc3cad6 # 不锈钢冷色
|
||||
LMAX = {"S08": 17.0, "S10": 22.0} # 可见段截断:螺杆伸到底座底面(z=0)即止(台面/预埋螺母未建模)
|
||||
|
||||
# 电机尾端 4x M3 螺纹径向槽的实测孔位(电机.STEP):BCD55、相位 39.9°+90k,
|
||||
# 槽宽 2.5、径向可调约 ±1.25mm。S02(P04~P08 后端板)与 S03(P03 后法兰)共用;
|
||||
# 旧 35.1×40.9 矩形孔距对角两颗偏出螺纹槽 ~4.7mm(2026-10-02 用户指出"螺钉没有对齐")。
|
||||
REAR_SLOTS = tuple((27.5 * math.cos(math.radians(39.9 + 90.0 * k)),
|
||||
27.5 * math.sin(math.radians(39.9 + 90.0 * k))) for k in range(4))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 局部坐标系工具
|
||||
def _norm(v):
|
||||
@@ -127,8 +133,9 @@ def _geom(spec):
|
||||
nom = float(str(spec["螺纹"]).lstrip("Mm"))
|
||||
rs, L = nom / 2.0, float(spec["长度mm"])
|
||||
head = spec.get("头型", "")
|
||||
if "沉头" in head: # 沉头内六角:90° 锥,最宽处在承载面
|
||||
return dict(kind="csk", rs=rs, L=L, rh=2.0 * nom)
|
||||
if "沉头" in head: # 沉头内六角:90° 锥,最宽处(刃口)在承载面
|
||||
rh = {3.0: 3.15, 4.0: 3.5, 6.0: 5.5}.get(nom, 1.05 * nom) # 头径≈沉孔座位径(GB dk)
|
||||
return dict(kind="csk", rs=rs, L=L, rh=rh)
|
||||
if "外六角" in head: # 外六角:M6 对边 10,高 4
|
||||
af = {3.0: 5.5, 4.0: 7.0, 5.0: 8.0, 6.0: 10.0}.get(nom, 1.7 * nom)
|
||||
hh = max(3.0, 0.7 * nom)
|
||||
@@ -142,9 +149,10 @@ def _mesh(g):
|
||||
"""局部网格:头部 z ∈ [-hh,0](0 = 承载头面),杆部 z ∈ [0, L]。"""
|
||||
rs, L = g["rs"], g["L"]
|
||||
if g["kind"] == "csk":
|
||||
hh = g["rh"] - rs
|
||||
head = _merge(_side(rs, g["rh"], -hh, 0.0, 14), _cap(rs, -hh, 14, up=False),
|
||||
_cap(g["rh"], 0.0, 14, up=True))
|
||||
hh = g["rh"] - rs # 90° 锥:头高 = (dk-d2)/2
|
||||
head = _merge(_side(g["rh"], rs, 0.0, hh, 14), # 锥面自承载面收进材料(沉入沉孔座位)
|
||||
_cap(g["rh"], 0.0, 14, up=False), # 承载端外露环形面
|
||||
_cap(rs, hh, 14, up=True))
|
||||
elif g["kind"] == "hex":
|
||||
hh, rh = g["hh"], 0.98 * g["rh"]
|
||||
head = _merge(_side(rh, rh, -hh, 0.0, 6), _cap(rh, 0.0, 6, up=True),
|
||||
@@ -186,10 +194,9 @@ def instances(m2_bcd, m2_clock):
|
||||
for tag, tp in (("J2", 8.0), ("J3", 7.0), ("J4", 8.0), ("J5", 8.0), ("J6", 7.0)):
|
||||
d = frame(tag)[1]
|
||||
zt = -tp + 4.0 # 沉孔台阶 = -t_plate + cb_depth(4)
|
||||
for u in (-17.55, 17.55):
|
||||
for v in (-20.46, 20.46):
|
||||
out.append(("S02", to_global(tag, u, v, zt), tuple(-c for c in d), GRP_PAR[tag],
|
||||
"%s 后端板" % PROX[tag], "孔位(%.2f,%.2f) -> %s 电机尾端面" % (u, v, tag)))
|
||||
for u, v in REAR_SLOTS:
|
||||
out.append(("S02", to_global(tag, u, v, zt), tuple(-c for c in d), GRP_PAR[tag],
|
||||
"%s 后端板" % PROX[tag], "孔位(%.2f,%.2f) -> %s 电机尾端面" % (u, v, tag)))
|
||||
|
||||
d1 = frame("J1")[1]
|
||||
for deg in (17.8, 107.8, 197.8, 287.8):
|
||||
@@ -197,10 +204,17 @@ def instances(m2_bcd, m2_clock):
|
||||
v = 27.5 * math.sin(math.radians(deg))
|
||||
out.append(("S04", to_global("J1", u, v, 62.5), d1, "root",
|
||||
"P03 前置锁紧凸台", "BCD55 @%.1f° -> M1 前端面" % deg))
|
||||
for u in (-17.55, 17.55):
|
||||
for v in (-20.46, 20.46):
|
||||
out.append(("S03", to_global("J1", u, v, -5.5), tuple(-c for c in d1), "root",
|
||||
"P03 后置法兰", "35.1×40.9 孔距 -> M1 尾端面"))
|
||||
for u, v in REAR_SLOTS:
|
||||
out.append(("S03", to_global("J1", u, v, -5.5), tuple(-c for c in d1), "root",
|
||||
"P03 后置法兰", "BCD55 @39.9° 螺纹槽 -> M1 尾端面"))
|
||||
|
||||
# S12 P04 前端压盖 -> M2 前端盖(J2):沉孔台阶 Y=158(局部 z=58),与 S04 同 BCD55 相位
|
||||
d2 = frame("J2")[1]
|
||||
for deg in (17.8, 107.8, 197.8, 287.8):
|
||||
u = 27.5 * math.cos(math.radians(deg))
|
||||
v = 27.5 * math.sin(math.radians(deg))
|
||||
out.append(("S12", to_global("J2", u, v, 58.0), d2, "J1",
|
||||
"P04 前端压盖", "BCD55 @%.1f° -> M2 前端面(前端夹紧)" % deg))
|
||||
|
||||
for x in (X_COL - 16.0, X_COL + 22.0):
|
||||
for z in (180.0, 329.0):
|
||||
|
||||
+253
-32
@@ -17,6 +17,7 @@ import io
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -40,6 +41,8 @@ RHO = {"PLA": 1.24e-3, "motor_g": 210.0} # g/mm3 for printed, g per module
|
||||
INFILL = float(os.environ.get("ARMINFILL", "0.38"))
|
||||
# allowable bending stress across the print layers, after notch + creep derating
|
||||
ALLOW = {"PLA": 12.0, "PETG": 20.0, "PA_CF": 30.0} # MPa
|
||||
# Young's modulus of the printed lattice, for the deflection estimate only
|
||||
EMOD = {"PLA": 3000.0, "PETG": 2100.0, "PA_CF": 8000.0} # MPa
|
||||
MAT = os.environ.get("ARMMAT", "PLA")
|
||||
PAYLOAD = float(os.environ.get("ARMPAY", "0.5")) # kg held at the fingertip
|
||||
DYN = float(os.environ.get("ARMDYN", "2.0")) # dynamic factor on the moving mass
|
||||
@@ -48,6 +51,7 @@ G981 = 9.81
|
||||
GRIP_T = 12.0 # N*mm per motor is far too small to matter; the grip force
|
||||
# case is carried separately below as a normal force at the pad
|
||||
_cache = {}
|
||||
GRP = {"root": 0, "J1": 1, "J2": 2, "J3": 3, "J4": 4, "J5": 5, "J6": 6}
|
||||
|
||||
|
||||
def load(name):
|
||||
@@ -173,6 +177,210 @@ def arm_masses():
|
||||
return out, tot
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ 零件表:质量 + 零位世界质心
|
||||
def _mesh_vol_cg(v, t):
|
||||
"""四面体法求闭合三角网格的体积与体积加权质心。
|
||||
|
||||
网格顶点已在零位世界系(mesh_arm.json 由 arm_model 直接落盘),所以质心就是零件
|
||||
在装配中的真实位置。实测体积与 STEP/BOM 差 <0.1%(电机包络 1.7%),可作载荷输入。
|
||||
"""
|
||||
vol = mx = my = mz = 0.0
|
||||
for a, b, c in t:
|
||||
x1, y1, z1 = v[a]
|
||||
x2, y2, z2 = v[b]
|
||||
x3, y3, z3 = v[c]
|
||||
det = x1 * (y2 * z3 - y3 * z2) - x2 * (y1 * z3 - y3 * z1) + x3 * (y1 * z2 - y2 * z1)
|
||||
vol += det
|
||||
mx += (x1 + x2 + x3) * det
|
||||
my += (y1 + y2 + y3) * det
|
||||
mz += (z1 + z2 + z3) * det
|
||||
if vol <= 0.0:
|
||||
return 0.0, None
|
||||
V = vol / 6.0
|
||||
return V, (mx / (24.0 * V), my / (24.0 * V), mz / (24.0 * V))
|
||||
|
||||
|
||||
def part_table(payload_kg=None):
|
||||
"""[(name, mass_g, cg_xyz, grp)],含末端负载这个集中质量点。"""
|
||||
meta = json.load(io.open(OUT.replace("/out", "/web") + "/mesh_arm.json", encoding="utf-8"))
|
||||
rows = []
|
||||
for p in meta["parts"]:
|
||||
nm = p["name"]
|
||||
if nm.startswith("ENV_") or not p.get("v") or not p.get("t") or p.get("vol", 0) <= 0:
|
||||
continue # 可达包络网格 / 无实体螺钉实例,不承载
|
||||
V, cg = _mesh_vol_cg(p["v"], p["t"])
|
||||
if V <= 0.0 or cg is None:
|
||||
continue
|
||||
if p.get("printed"):
|
||||
m = V * RHO["PLA"] * INFILL
|
||||
elif re.match(r"^M\d", nm):
|
||||
m = RHO["motor_g"]
|
||||
else:
|
||||
continue
|
||||
rows.append([nm, m, cg, GRP.get(p["grp"], 0)])
|
||||
if payload_kg:
|
||||
rows.append(["PAYLOAD", payload_kg * 1000.0,
|
||||
(HL.X_ARM, HL.Y_AXIS, HL.Z_TIP), 6])
|
||||
return rows
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ 运动学:产品外参链
|
||||
def _rodrigues(deg, d, p):
|
||||
"""绕世界轴 d 过点 p 转 deg -> (R, t),使 x -> R x + t。"""
|
||||
n = math.sqrt(sum(c * c for c in d)) or 1.0
|
||||
x, y, z = d[0] / n, d[1] / n, d[2] / n
|
||||
a = math.radians(deg)
|
||||
c, s = math.cos(a), math.sin(a)
|
||||
t1 = 1.0 - c
|
||||
R = [[t1 * x * x + c, t1 * x * y - s * z, t1 * x * z + s * y],
|
||||
[t1 * x * y + s * z, t1 * y * y + c, t1 * y * z - s * x],
|
||||
[t1 * x * z - s * y, t1 * y * z + s * x, t1 * z * z + c]]
|
||||
t = [p[i] - (R[i][0] * p[0] + R[i][1] * p[1] + R[i][2] * p[2]) for i in range(3)]
|
||||
return R, t
|
||||
|
||||
|
||||
def chain(ang):
|
||||
"""acc[g] = (R, t):第 g 组零件的刚体变换。
|
||||
|
||||
acc[g] = acc[g-1] o R_g(产品外参,右侧先作用),与 sweep_v2.accum_trsf、
|
||||
查看器嵌套 pivot 三者一致。
|
||||
"""
|
||||
R = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
|
||||
t = [0.0, 0.0, 0.0]
|
||||
out = [(R, t)]
|
||||
for j in range(1, 7):
|
||||
ax = HL.AXIS["J%d" % j]
|
||||
Rj, tj = _rodrigues(ang.get(j, 0.0), ax["d"], ax["rear"])
|
||||
Rn = [[sum(R[i][k] * Rj[k][j2] for k in range(3)) for j2 in range(3)] for i in range(3)]
|
||||
tn = [sum(R[i][k] * tj[k] for k in range(3)) + t[i] for i in range(3)]
|
||||
R, t = Rn, tn
|
||||
out.append((R, t))
|
||||
return out
|
||||
|
||||
|
||||
def _xf(T, c):
|
||||
R, t = T
|
||||
return [sum(R[i][k] * c[k] for k in range(3)) + t[i] for i in range(3)]
|
||||
|
||||
|
||||
def _dir(T, v):
|
||||
"""只取旋转部分——方向向量绝不能带平移,否则投影出的"力矩"会被平移量放大。"""
|
||||
R = T[0]
|
||||
return [sum(R[i][k] * v[k] for k in range(3)) for i in range(3)]
|
||||
|
||||
|
||||
def _cross(a, b):
|
||||
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]
|
||||
|
||||
|
||||
def joint_loads(parts, payload_kg=None):
|
||||
"""在可达姿态网格上搜索,返回每个关节的:
|
||||
|
||||
F 该关节以下的总重力 (N)(与姿态无关)
|
||||
Mxyz 重力对关节轴点取矩后,三个全局分量各自的最大绝对值 (N*mm)
|
||||
tau 重力绕该关节自身轴的最大力矩 (N*mm) —— 电机真正要出的力
|
||||
Lp 出现 tau 最大值时,TCP 到该关节轴点的距离 (mm)
|
||||
|
||||
为什么搜索姿态:力臂不是常数。J1 是绕 X 的自转轴,手臂在轴正下方时重力对它
|
||||
只产生绕 X 的扭矩(不产生弯曲),手臂摆到侧方时扭矩最大。旧算法把全部 distal
|
||||
质量当作集中在指尖,量级完全错掉。
|
||||
"""
|
||||
trv = {t["axis"]: t for t in json.load(io.open(DOCS + "/travel.json", encoding="utf-8"))}
|
||||
|
||||
def picks(j):
|
||||
t = trv.get("J%d" % j, {"lo": -180.0, "hi": 180.0})
|
||||
if t.get("continuous"):
|
||||
return [0.0, 60.0, -60.0, 120.0, -120.0, 180.0]
|
||||
lo, hi = t["lo"], t["hi"]
|
||||
return sorted({lo, 0.0 if lo < 0 < hi else lo, hi, 0.5 * (lo + hi)})
|
||||
|
||||
grid = [picks(j) for j in range(1, 7)]
|
||||
res = {}
|
||||
for j in range(1, 7):
|
||||
res["J%d" % j] = dict(F=0.0, Mxyz=[0.0, 0.0, 0.0], tau=0.0, Lp=0.0, I=0.0, Iarm=0.0)
|
||||
|
||||
import itertools
|
||||
for combo in itertools.product(*grid):
|
||||
ang = {k + 1: v for k, v in enumerate(combo)}
|
||||
acc = chain(ang)
|
||||
pos = [(nm, m, _xf(acc[g], cg), g) for nm, m, cg, g in parts]
|
||||
tcp = None
|
||||
for nm, m, c, g in pos:
|
||||
if nm == "PAYLOAD":
|
||||
tcp = c
|
||||
for j in range(1, 7):
|
||||
ax = HL.AXIS["J%d" % j]
|
||||
pj = _xf(acc[j - 1], ax["rear"])
|
||||
dj = _dir(acc[j - 1], ax["d"])
|
||||
F = M = [0.0, 0.0, 0.0]
|
||||
wsum = 0.0
|
||||
I = Iarm = 0.0
|
||||
for _nm, m, c, g in pos:
|
||||
if g < j:
|
||||
continue
|
||||
w = m * G981 / 1000.0
|
||||
wsum += w
|
||||
r = [c[i] - pj[i] for i in range(3)]
|
||||
proj = sum(r[i] * dj[i] for i in range(3))
|
||||
perp2 = sum(r[i] * r[i] for i in range(3)) - proj * proj
|
||||
I += m * perp2 / 1e9 # g*mm^2 -> kg*m^2
|
||||
if _nm != "PAYLOAD":
|
||||
Iarm += m * perp2 / 1e9
|
||||
rc = _cross(r, [0.0, 0.0, -w])
|
||||
for i in range(3):
|
||||
M[i] += rc[i]
|
||||
r = res["J%d" % j]
|
||||
r["F"] = max(r["F"], wsum)
|
||||
for i in range(3):
|
||||
r["Mxyz"][i] = max(r["Mxyz"][i], abs(M[i]))
|
||||
tau = abs(M[0] * dj[0] + M[1] * dj[1] + M[2] * dj[2])
|
||||
if tau > r["tau"]:
|
||||
r["tau"] = tau
|
||||
r["Lp"] = math.dist(tcp, pj) if tcp else 0.0
|
||||
r["I"] = max(r["I"], I)
|
||||
r["Iarm"] = max(r["Iarm"], Iarm)
|
||||
return res
|
||||
|
||||
|
||||
def section_moment(jid, bend_axis, ref, refgrp, parts):
|
||||
"""截面绕自身形心、沿 bend_axis 的弯矩最大值 (N*mm),以及该姿态下 TCP 到该
|
||||
截面的距离 (mm)(挠度估算用)。
|
||||
|
||||
参考点取【剖面形心】而不是关节轴:这两个参考点差得很远。P03 在 z=480 的剖面
|
||||
形心 x≈-111,而臂身所有零件质心 x≈+19.5,真实力臂 130mm;若误用关节轴点
|
||||
(J1 在 x=-51)只会得到 70mm,把弯矩低估 1.85 倍。
|
||||
"""
|
||||
trv = {t["axis"]: t for t in json.load(io.open(DOCS + "/travel.json", encoding="utf-8"))}
|
||||
jj = int(jid[1])
|
||||
|
||||
def picks(j):
|
||||
t = trv.get("J%d" % j, {"lo": -180.0, "hi": 180.0})
|
||||
if t.get("continuous"):
|
||||
return [0.0, 60.0, -60.0, 120.0, -120.0, 180.0]
|
||||
lo, hi = t["lo"], t["hi"]
|
||||
return sorted({lo, 0.0 if lo < 0 < hi else lo, hi, 0.5 * (lo + hi)})
|
||||
|
||||
import itertools
|
||||
best = 0.0
|
||||
best_L = 0.0
|
||||
for combo in itertools.product(*[picks(j) for j in range(1, 7)]):
|
||||
acc = chain({k + 1: v for k, v in enumerate(combo)})
|
||||
rw = _xf(acc[refgrp], ref)
|
||||
s = 0.0
|
||||
tcp = rw
|
||||
for nm, m, cg, g in parts:
|
||||
if g < jj:
|
||||
continue
|
||||
c = _xf(acc[g], cg)
|
||||
if nm == "PAYLOAD":
|
||||
tcp = c
|
||||
s += m * (c[bend_axis] - rw[bend_axis])
|
||||
if abs(s) > best:
|
||||
best = abs(s)
|
||||
best_L = math.dist(tcp, rw)
|
||||
return best * G981 / 1000.0, best_L
|
||||
|
||||
|
||||
# critical cuts: (label, part, plane normal, plane origin, which load case owns it)
|
||||
def cuts():
|
||||
"""(label, part, cut-plane normal, point, joint whose load it carries, bend axis)
|
||||
@@ -190,7 +398,9 @@ def cuts():
|
||||
("小臂骨架中段 @z=245", "P07_小臂骨架_螺旋体30%_墙5底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 245.0), "J4", 1),
|
||||
("小臂骨架抱箍 @z=195", "P07_小臂骨架_螺旋体30%_墙5底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 195.0), "J5", 1),
|
||||
("腕偏转座根部 @z=150", "P08_腕偏转座_螺旋体25%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 150.0), "J5", 1),
|
||||
("工具法兰 @z=55", "P09_工具法兰_螺旋体100%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 55.0), "J6", 0),
|
||||
# P09 包络 z=93..121,剖面取 107(法兰中截面)。原 -55 落在零件之外,
|
||||
# 剖切返回空,工具法兰其实一直没被校核过。
|
||||
("工具法兰 @z=107", "P09_工具法兰_螺旋体100%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 107.0), "J6", 1),
|
||||
]
|
||||
|
||||
|
||||
@@ -200,16 +410,12 @@ MOMENT = { # worst reachable bending moment per joint, N*mm
|
||||
|
||||
|
||||
def main():
|
||||
masses, per = arm_masses()
|
||||
# Moment arms DERIVED from the swept travel (docs/travel.json), not hand-typed: they were
|
||||
# stale (J4 said 24 deg while the arm now sweeps 360, and J1 was missing entirely), which
|
||||
# silently under-loaded those sections. A continuous or >90 deg axis is capped at 90 deg
|
||||
# because that is where a gravity load sits furthest off the axis.
|
||||
TRV = json.load(io.open(DOCS + "/travel.json", encoding="utf-8"))
|
||||
dev = {t["axis"]: min(max(abs(t["hi"]), abs(t["lo"])), 90.0) for t in TRV}
|
||||
# distance from each joint axis out to the fingertip load, mm
|
||||
dist = {"J1": 564.0, "J2": 444.0, "J3": 380.0, "J4": 264.0, "J5": 129.0, "J6": 60.0}
|
||||
reach = {k: dist[k] * math.sin(math.radians(dev.get(k, 90.0))) for k in dist}
|
||||
parts = part_table(PAYLOAD)
|
||||
grpof = {nm: g for nm, _, _, g in parts}
|
||||
jl = joint_loads(parts)
|
||||
masses = {"J%d" % j: sum(m for _, m, _, g in parts if g >= j) for j in range(1, 7)}
|
||||
masses["all"] = sum(m for _, m, _, _ in parts)
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
def compute_cut(c_item):
|
||||
@@ -218,46 +424,61 @@ def main():
|
||||
sp = section_props(sh, n, o, bend)
|
||||
if not sp:
|
||||
return (lab, part, None, jid)
|
||||
m_g = masses[jid] * DYN + PAYLOAD * 1000.0 * DYN
|
||||
F = m_g * G981 / 1000.0 # N
|
||||
# the section carries everything distal to its own joint, at that joint's worst
|
||||
# reachable moment arm, plus the grip reaction on the finger root
|
||||
M = F * reach[jid] # N*mm
|
||||
if jid == "J6":
|
||||
M = max(M, GRIP_T * 60.0)
|
||||
I = sp["I"] # already the real bend axis, not min(Iu,Iv)
|
||||
sigma = M * sp["c"] / max(I, 1e-6)
|
||||
# 剖面形心的三维重建 + 弯矩所绕的轴
|
||||
nvec = [float(x) for x in n]
|
||||
p_ax, q_ax = [k for k in (0, 1, 2) if abs(nvec[k]) < 0.5]
|
||||
bend_ax = sp["bend"]
|
||||
far_ax = p_ax if bend_ax == q_ax else q_ax # I 绕 bend_ax、c 沿 far_ax
|
||||
ref = list(o)
|
||||
ref[p_ax] += sp["centroid"][0]
|
||||
ref[q_ax] += sp["centroid"][1]
|
||||
M, L = section_moment(jid, far_ax, ref, grpof.get(part, 0), parts)
|
||||
M *= DYN
|
||||
F = jl[jid]["F"] * DYN
|
||||
I, c = sp["I"], sp["c"]
|
||||
sigma = M * c / max(I, 1e-6)
|
||||
tau = 1.5 * F / sp["A"]
|
||||
return (lab, part, dict(sp, M=M, F=F, sigma=sigma, tau=tau,
|
||||
SF=ALLOW[MAT] / sigma if sigma > 0 else 99.0), jid)
|
||||
defl = M * L * L / (2.0 * EMOD[MAT] * I) if I > 1e-6 and L > 0 else 0.0
|
||||
return (lab, part, dict(sp, M=M, F=F, sigma=sigma, tau=tau, L=L, defl=defl,
|
||||
bend_axis=bend_ax, SF=ALLOW[MAT] / sigma if sigma > 0 else 99.0), jid)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=min(13, os.cpu_count() or 4)) as executor:
|
||||
rows = list(executor.map(compute_cut, cuts()))
|
||||
print("材料 %s 许用弯曲 %g MPa(跨层、含缺口与蠕变折减) 末端负载 %g kg 动载系数 %g\n"
|
||||
% (MAT, ALLOW[MAT], PAYLOAD, DYN))
|
||||
print("材料 %s 许用弯曲 %g MPa(跨层、含缺口与蠕变折减) 弹性模量 %g MPa 末端负载 %g kg 动载系数 %g"
|
||||
% (MAT, ALLOW[MAT], EMOD[MAT], PAYLOAD, DYN))
|
||||
print("载荷模型:每个零件按真实质心(网格体积加权)随姿态变换,在可达姿态网格上取弯矩最大值;"
|
||||
"参考点为剖面形心\n")
|
||||
print("打印件按 %d%% 实体率计质量(壁+填充),电机按 %.0f g/台" % (INFILL * 100, RHO["motor_g"]))
|
||||
print("%-26s %-10s %7s %9s %6s %8s %7s %5s %9s" %
|
||||
("截面", "零件", "面积mm2", "Imin mm4", "c mm", "M N*mm", "sigma", "SF", "需Z mm3"))
|
||||
print("%-24s %-9s %7s %10s %6s %9s %7s %6s %7s %7s"
|
||||
% ("截面", "零件", "面积mm2", "I mm4", "c mm", "M N*mm", "sigma", "SF", "挠度mm", "需Z mm3"))
|
||||
bad = []
|
||||
for lab, part, d, jid in rows:
|
||||
if not d:
|
||||
print("%-26s %-10s 剖切失败" % (lab, part))
|
||||
print("%-24s %-9s 剖切失败" % (lab, part))
|
||||
continue
|
||||
Zreq = d["M"] / ALLOW[MAT] * 1.5 # section modulus x1.5 SF
|
||||
print("%-26s %-10s %7.0f %9.0f %6.1f %8.0f %7.1f %5.2f %9.0f%s" %
|
||||
(lab, part, d["A"], d["I"], d["c"], d["M"], d["sigma"], d["SF"], Zreq,
|
||||
" <<< 不足" if d["SF"] < 1.5 else ""))
|
||||
print("%-24s %-9s %7.0f %10.0f %6.1f %9.0f %7.2f %6.2f %7.1f %7.0f%s"
|
||||
% (lab, part[:9], d["A"], d["I"], d["c"], d["M"], d["sigma"], d["SF"], d["defl"],
|
||||
Zreq, " <<< 不足" if d["SF"] < 1.5 else ""))
|
||||
if d["SF"] < 1.5:
|
||||
bad.append((lab, part, d))
|
||||
print("\n各关节以下质量(g, 含电机): " + " ".join("%s=%.0f" % (k, v) for k, v in masses.items() if k != "all"))
|
||||
print("整机质量(含 6 电机) %.0f g" % masses["all"])
|
||||
print("\n各关节以下质量(g, 含电机与末端负载): "
|
||||
+ " ".join("%s=%.0f" % (k, v) for k, v in masses.items() if k != "all"))
|
||||
print("合计 %.0f g (其中末端负载 %.0f g)" % (masses["all"], PAYLOAD * 1000))
|
||||
print("\n各关节电机需出的重力力矩(绕自身轴,姿态搜索最大值):")
|
||||
for j in range(1, 7):
|
||||
r = jl["J%d" % j]
|
||||
print(" J%d %6.2f N·m (该关节以下总重 %.1f N,力臂 %.0f mm)"
|
||||
% (j, r["tau"] / 1000.0, r["F"], r["Lp"]))
|
||||
io.open(DOCS + "/strength.json", "w", encoding="utf-8").write(
|
||||
json.dumps([dict(截面=a, 零件=b, 关节=d, **(c or {})) for a, b, c, d in rows],
|
||||
ensure_ascii=False, indent=1, default=float))
|
||||
return bad
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
b = main()
|
||||
print("\n不合格截面 %d 处" % len(b))
|
||||
sys.stdout.flush() # os._exit 不刷缓冲,重定向到文件时否则输出全丢
|
||||
os._exit(0)
|
||||
|
||||
+23
-8
@@ -23,8 +23,13 @@
|
||||
- 预剪枝:按关节轴计算每对零件的轴向/径向区间,区间不相交的零件对在该关节的
|
||||
任何角度都不可能碰撞,直接跳过(对单关节扫描是精确安全的)。
|
||||
|
||||
台架(P01 立柱 / P02 底盘)代表躯干与机身的连接界面,默认【参与】碰撞计算——臂撞立柱
|
||||
就是臂撞躯干,是真约束。--no-stand 仅用于诊断:排除台架后重扫,给出"不含躯干约束"的
|
||||
自由空间行程,用来分辨某个限位是臂自身折叠产生的还是躯干挡的(台架位置本身是假设,
|
||||
改它等于改测试工装,不等于改设计)。
|
||||
|
||||
运行:python src/sweep_v2.py [--workers 20] [--step 2] [--combo-step 6]
|
||||
[--no-combos] [--joints J2,J4] [--no-gjk]
|
||||
[--no-combos] [--joints J2,J4] [--no-gjk] [--no-stand]
|
||||
"""
|
||||
import json
|
||||
import math
|
||||
@@ -44,6 +49,7 @@ import multiprocessing as mp
|
||||
|
||||
import arm_build as AB
|
||||
import cadquery as cq
|
||||
import human_layout as HL
|
||||
from armcore import place, bb, DOCS
|
||||
|
||||
VOL_MIN = 2.0 # mm3,超过即计为干涉
|
||||
@@ -61,6 +67,7 @@ MOUNT_PAIRS = {
|
||||
MESH_TOL = 0.5 # mm,零件网格化容差
|
||||
MESH_MARGIN = 0.8 # mm,凸包安全外扩(> MESH_TOL 保证网格内缩不致漏判)
|
||||
USE_GJK = True # 进程内全局;工作进程由 initializer 传入
|
||||
INCLUDE_FIXTURE = True # True=把 P01 立柱/P02 底盘(代表躯干)算进碰撞;--no-stand 置 False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 网格化 + GJK
|
||||
@@ -165,11 +172,15 @@ def _do_simplex(s):
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 模型与上下文
|
||||
def load_model():
|
||||
def load_model(include_fixture=True):
|
||||
"""include_fixture=False 时把 P01 立柱 / P02 底盘(固定测试台架,非本体)排除在
|
||||
碰撞配对之外——用来得到真机工作空间而不是台架工作空间。见 human_layout.FIXTURE_PREFIX。"""
|
||||
import arm_model as AM
|
||||
JS = AM.build()
|
||||
parts = []
|
||||
for p in AB.PARTS:
|
||||
if not include_fixture and HL.is_fixture(p["name"]):
|
||||
continue
|
||||
g = CHAIN.index(AB.GRP.get(p["name"], "root"))
|
||||
lo, hi = bb(p["shape"])
|
||||
corners = [(x, y, z) for x in (lo[0], hi[0]) for y in (lo[1], hi[1]) for z in (lo[2], hi[2])]
|
||||
@@ -212,8 +223,8 @@ def axis_intervals(parts, rear, d):
|
||||
return out
|
||||
|
||||
|
||||
def make_ctx():
|
||||
parts, joints = load_model()
|
||||
def make_ctx(include_fixture=True):
|
||||
parts, joints = load_model(include_fixture)
|
||||
impossible = {}
|
||||
for k in range(1, len(CHAIN)):
|
||||
rear, d = joints[CHAIN[k]]
|
||||
@@ -395,10 +406,10 @@ _POOL = None
|
||||
_WORKERS = 4
|
||||
|
||||
|
||||
def _init_worker(use_gjk=True):
|
||||
def _init_worker(use_gjk=True, include_fixture=True):
|
||||
global _CTX, USE_GJK
|
||||
USE_GJK = bool(use_gjk)
|
||||
_CTX = make_ctx()
|
||||
_CTX = make_ctx(include_fixture)
|
||||
|
||||
|
||||
def _task(task):
|
||||
@@ -410,7 +421,7 @@ def get_pool(workers):
|
||||
global _POOL, _WORKERS
|
||||
_WORKERS = workers
|
||||
if _POOL is None and workers > 1:
|
||||
_POOL = mp.Pool(workers, initializer=_init_worker, initargs=(USE_GJK,))
|
||||
_POOL = mp.Pool(workers, initializer=_init_worker, initargs=(USE_GJK, INCLUDE_FIXTURE))
|
||||
return _POOL
|
||||
|
||||
|
||||
@@ -448,7 +459,7 @@ def eval_angles(angles_list, workers, chunk=None, label="姿态"):
|
||||
def _ctx_single():
|
||||
global _CTX
|
||||
if _CTX is None:
|
||||
_CTX = make_ctx()
|
||||
_CTX = make_ctx(INCLUDE_FIXTURE)
|
||||
return _CTX
|
||||
|
||||
|
||||
@@ -555,6 +566,9 @@ def main():
|
||||
if "--no-gjk" in sys.argv:
|
||||
global USE_GJK
|
||||
USE_GJK = False
|
||||
if "--no-stand" in sys.argv:
|
||||
global INCLUDE_FIXTURE
|
||||
INCLUDE_FIXTURE = False
|
||||
# 内存感知的默认进程数:每个工作进程预留 ~2.5GB 峰值(建模+布尔瞬时开销)
|
||||
avail_gb = 16.0
|
||||
try:
|
||||
@@ -691,6 +705,7 @@ def main():
|
||||
open(DOCS + "/travel.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1)
|
||||
detail = dict(generated=time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
vol_min=VOL_MIN, step=step, combo_step=combo_step, workers=workers,
|
||||
fixture_included=INCLUDE_FIXTURE,
|
||||
singles={t: dict(lo=round(singles[t]["lo"], 2), hi=round(singles[t]["hi"], 2),
|
||||
jams={str(a): v for a, v in singles[t]["jams"].items()})
|
||||
for t in singles},
|
||||
|
||||
+187
-279
@@ -1,27 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Motor Torque Balance and Dynamic Capacity Analysis.
|
||||
"""关节电机力矩与负载能力校核。
|
||||
|
||||
This module evaluates static and dynamic torque demand across all 6 joints (J1 to J6)
|
||||
for the 6-DOF compact robotic arm driven by the standardized 60mm gearmotor module.
|
||||
与旧版的区别(这些是旧版实际出错的地方):
|
||||
|
||||
Motor Module Parameters (measured from 电机.STEP):
|
||||
- Reduction: 2-stage planetary 25:1
|
||||
- Mass: 210g per unit
|
||||
- Continuous Rated Torque: T_cont = 2.5 N·m
|
||||
- Peak Stall Torque: T_peak = 8.0 N·m
|
||||
- Safe Continuous Allowable (in 3D printed housing): T_allow = 2.0 N·m
|
||||
1. 拓扑。旧版把关节表手抄成 "J3 肘俯仰 / J4 前臂自转 / J5 腕俯仰 / J6 夹爪",
|
||||
并据此虚构了一个夹爪机构和 135mm 大臂——与 human_layout.py 的实际拓扑
|
||||
(J3 大臂自转 / J4 肘俯仰 / J6 腕自转)完全不符,整份报告的结论都是错的。
|
||||
本版不再复制拓扑:零件表、关节轴、运动学链全部复用 strength_check,
|
||||
而 strength_check 的轴又直接来自 human_layout,单一事实源。
|
||||
2. 力臂。旧版把每个关节的质量与力臂写成硬编码常数(J2 = 1.75kg @ 464mm),
|
||||
几何一改就失效且不会报错。本版按每个零件的真实质心、在可达姿态网格上
|
||||
搜索最大重力力矩。
|
||||
3. 惯量。旧版用一组手填的 kg·m²。本版按 Σm·d²(对关节轴的平行轴距离)
|
||||
在同一姿态网格上取最大值。零件自身转动惯量未计入,对大臂量级的关节
|
||||
是小修正,已在报告中标注。
|
||||
|
||||
Load Cases Evaluated:
|
||||
1. Horizontal Outstretched (Worst-Case Static Overhang):
|
||||
theta_2 = 90°, theta_3 = 0°, theta_5 = 0°, payload = 0.5 kg (tip)
|
||||
2. Standard Working Posture (90° Elbow Flexion):
|
||||
theta_2 = 45°, theta_3 = 90°, payload = 0.5 kg
|
||||
3. Dynamic Acceleration (worst-case sweep @ alpha = 5 rad/s^2)
|
||||
4. Spring Gravity Compensation (Passive Assist at J2)
|
||||
电机参数:仓库内没有原始数据手册,以下为工程假定,可用环境变量覆盖。
|
||||
ARMT_CONT 连续额定输出力矩 (N·m)
|
||||
ARMT_PEAK 峰值堵转输出力矩 (N·m)
|
||||
ARMT_ALLOW 封闭塑料外壳热衰减后的许用连续力矩 (N·m)
|
||||
ARMT_ALPHA 角加速度 (rad/s²)
|
||||
ARMPAY 末端负载 (kg)
|
||||
|
||||
Outputs:
|
||||
- docs/torque_balance.json
|
||||
- docs/torque_report.md
|
||||
输出:docs/torque_balance.json + docs/torque_report.md
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
@@ -30,277 +31,184 @@ import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from armcore import DOCS, WEB, OUT
|
||||
try:
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
import human_layout as HL
|
||||
from armcore import DOCS
|
||||
import strength_check as SC
|
||||
|
||||
G = 9.81
|
||||
T_CONT = 2.5 # N·m
|
||||
T_PEAK = 8.0 # N·m
|
||||
T_ALLOW = 2.0 # N·m derated thermal continuous for enclosed plastic housing
|
||||
PAYLOAD_KG = 0.5 # 500g rated tip payload
|
||||
T_CONT = float(os.environ.get("ARMT_CONT", "2.5"))
|
||||
T_PEAK = float(os.environ.get("ARMT_PEAK", "8.0"))
|
||||
T_ALLOW = float(os.environ.get("ARMT_ALLOW", "2.0"))
|
||||
ALPHA = float(os.environ.get("ARMT_ALPHA", "5.0"))
|
||||
PAYLOAD = float(os.environ.get("ARMPAY", "0.5"))
|
||||
|
||||
# Link geometric parameters (measured from human_layout / arm_model)
|
||||
# Joint stations (mm):
|
||||
# J1: Z=652 (Torso roll)
|
||||
# J2: Z=560 (Shoulder pitch, offset from J1 by 92mm in Z)
|
||||
# J3: Z=425 (Elbow pitch, L_upper = 135mm)
|
||||
# J4: Z=300 (Forearm roll, internal coaxial)
|
||||
# J5: Z=267 (Wrist pitch, L_fore = 158mm)
|
||||
# J6: Z=171 (Gripper tip, L_hand = 96mm, total wrist-to-tip = 171mm)
|
||||
# 关节名与轴向,全部取自 human_layout(不手抄)
|
||||
JOINT_CN = {t[0]: t[1] for t in HL.JOINTS}
|
||||
JOINT_D = {t[0]: t[3] for t in HL.JOINTS}
|
||||
|
||||
def analyze_torque(masses=None):
|
||||
if masses is None:
|
||||
# Default mass distribution (in kg) based on 38% infill PLA + 210g motor per joint
|
||||
# Distal mass carried by each joint (including its driven link + all distal motors/links)
|
||||
masses = {
|
||||
"J1": 2.15, # carries entire arm (J2..J6 motors + links)
|
||||
"J2": 1.75, # carries humerus + forearm + hand + payload
|
||||
"J3": 1.20, # carries forearm + wrist + hand + payload
|
||||
"J4": 0.85, # carries internal forearm rotor + wrist + hand + payload
|
||||
"J5": 0.65, # carries palm + gripper + payload
|
||||
"J6": 0.25 # gripper jaws + payload
|
||||
|
||||
def _axis_kind(tag):
|
||||
d = JOINT_D[tag]
|
||||
if abs(d[2]) == 1:
|
||||
return "roll(绕Z)"
|
||||
if abs(d[0]) == 1:
|
||||
return "roll(绕X)"
|
||||
return "pitch(绕Y)"
|
||||
|
||||
|
||||
def analyze(payload_kg=PAYLOAD):
|
||||
parts = SC.part_table(payload_kg)
|
||||
jl = SC.joint_loads(parts)
|
||||
jl0 = SC.joint_loads(SC.part_table(0.0)) # 去掉末端负载,用于反推负载能力
|
||||
masses = {"J%d" % j: sum(m for _, m, _, g in parts if g >= j) for j in range(1, 7)}
|
||||
arm_mass = {k: v - payload_kg * 1000.0 for k, v in masses.items()}
|
||||
|
||||
res = {"assumptions": {
|
||||
"motor": "60mm 方形标准关节电机模组,2 级行星减速",
|
||||
"T_cont_Nm": T_CONT, "T_peak_Nm": T_PEAK, "T_allow_Nm": T_ALLOW,
|
||||
"alpha_rad_s2": ALPHA, "payload_kg": payload_kg,
|
||||
"note": "电机参数为工程假定,仓库内无数据手册;结论对此敏感,见报告第 4 节",
|
||||
}, "joints": {}}
|
||||
|
||||
for j in range(1, 7):
|
||||
tag = "J%d" % j
|
||||
r = jl[tag]
|
||||
tau_g = r["tau"] / 1000.0 # N*m,重力
|
||||
tau_dyn = r["I"] * ALPHA # N*m,惯性
|
||||
tau_g_arm = jl0[tag]["tau"] / 1000.0 # 去掉负载后的重力力矩
|
||||
req_static = tau_g
|
||||
req_peak = tau_g + tau_dyn
|
||||
res["joints"][tag] = {
|
||||
"cn": JOINT_CN[tag], "axis": _axis_kind(tag),
|
||||
"distal_mass_kg": round(masses[tag] / 1000.0, 3),
|
||||
"arm_mass_kg": round(arm_mass[tag] / 1000.0, 3),
|
||||
"weight_N": round(r["F"], 1),
|
||||
"reach_tip_mm": round(r["Lp"], 0),
|
||||
"tau_gravity_Nm": round(tau_g, 3),
|
||||
"tau_gravity_arm_only_Nm": round(tau_g_arm, 3),
|
||||
"I_distal_kgm2": round(r["I"], 5),
|
||||
"tau_dynamic_Nm": round(tau_dyn, 3),
|
||||
"tau_required_static_Nm": round(req_static, 3),
|
||||
"tau_required_peak_Nm": round(req_peak, 3),
|
||||
"duty_vs_Tcont_pct": round(100.0 * req_static / T_CONT, 1),
|
||||
"duty_vs_Tallow_pct": round(100.0 * req_static / T_ALLOW, 1),
|
||||
"peak_margin_x": round(T_PEAK / req_peak, 2) if req_peak > 1e-3 else None,
|
||||
"status": "OK" if (req_static <= T_ALLOW and req_peak <= T_PEAK) else "OVERLOAD",
|
||||
}
|
||||
|
||||
# Moment arms to distal center of mass and payload in horizontal outstretched pose (meters)
|
||||
# L_cg: distance from joint axis to combined distal link CG
|
||||
# L_tip: distance from joint axis to tip payload (0.5 kg)
|
||||
dist_tip = {
|
||||
"J1": 0.547,
|
||||
"J2": 0.464,
|
||||
"J3": 0.329,
|
||||
"J4": 0.171,
|
||||
"J5": 0.171,
|
||||
"J6": 0.060
|
||||
}
|
||||
|
||||
# Distance to the distal mass CG (excluding payload)
|
||||
dist_cg = {
|
||||
"J1": 0.280,
|
||||
"J2": 0.235,
|
||||
"J3": 0.165,
|
||||
"J4": 0.085,
|
||||
"J5": 0.085,
|
||||
"J6": 0.030
|
||||
}
|
||||
|
||||
# --- Case 1: Worst-Case Horizontal Outstretched ---
|
||||
# In horizontal pose:
|
||||
# J2 (pitch) and J3 (pitch) and J5 (pitch) bear full gravity moment.
|
||||
# J1 (roll) sees zero gravity bending torque if arm is purely along pitch plane,
|
||||
# but if arm swings 90° sideways, J1 sees the full horizontal moment.
|
||||
# J4 (roll) has axis aligned with forearm, so gravity torque on J4 is near 0.
|
||||
|
||||
# Masses excluding payload
|
||||
m_arm_only = {k: masses[k] - PAYLOAD_KG for k in masses}
|
||||
|
||||
horiz_static = {}
|
||||
for j in ["J1", "J2", "J3", "J4", "J5", "J6"]:
|
||||
if j in ["J1", "J2", "J3", "J5"]:
|
||||
# Arm gravity moment + Payload gravity moment
|
||||
m_arm = m_arm_only[j]
|
||||
t_arm = m_arm * G * dist_cg[j]
|
||||
t_pay = PAYLOAD_KG * G * dist_tip[j]
|
||||
t_tot = t_arm + t_pay
|
||||
elif j == "J4":
|
||||
# Forearm roll: minimal eccentricity (< 10mm)
|
||||
t_tot = (m_arm_only[j] + PAYLOAD_KG) * G * 0.008
|
||||
else: # J6 gripper
|
||||
# Grip clamping force is normal, axial reaction torque:
|
||||
t_tot = 0.08
|
||||
horiz_static[j] = t_tot
|
||||
|
||||
# --- Passive Spring Gravity Assist at J2 ---
|
||||
# Mounting lugs placed on shoulder fork and elbow
|
||||
# Designed counter-moment: Ts(theta) = T_spring_max * sin(theta_2)
|
||||
# Target: compensate ~65% of horizontal gravity torque (approx 2.48 N·m)
|
||||
t_spring_j2 = 2.48
|
||||
horiz_net_j2 = horiz_static["J2"] - t_spring_j2
|
||||
|
||||
# --- Case 2: Natural Working Posture (Elbow 90° flexed, shoulder 45°) ---
|
||||
# Shoulder angle = 45°, elbow angle = 90°
|
||||
# The effective moment arm to payload is reduced from 464mm to ~210mm
|
||||
# The effective moment arm of humerus is 135mm * sin(45°) = 95mm
|
||||
work_static = {}
|
||||
# J2 in working pose:
|
||||
# Humerus mass (~0.55kg) at 48mm: 0.55 * 9.81 * 0.048 = 0.26 N·m
|
||||
# Forearm + Hand + Payload (~1.20kg) hanging vertically at L_upper*sin(45°) = 95mm:
|
||||
# 1.20 * 9.81 * 0.095 = 1.12 N·m
|
||||
# Total J2 static working torque: ~1.38 N·m (without spring), with spring (~1.75 N·m counter): net is ~0.37 N·m
|
||||
work_static["J1"] = 0.65
|
||||
work_static["J2"] = 1.38
|
||||
work_static["J3"] = 0.88
|
||||
work_static["J4"] = 0.05
|
||||
work_static["J5"] = 0.52
|
||||
work_static["J6"] = 0.08
|
||||
work_net_j2 = abs(work_static["J2"] - t_spring_j2 * math.sin(math.radians(45)))
|
||||
|
||||
# --- Dynamic Acceleration Load (alpha = 5 rad/s^2) ---
|
||||
# Approximate moment of inertia I_distal = sum(m_i * r_i^2)
|
||||
inertia = {
|
||||
"J1": 0.085, # kg·m^2
|
||||
"J2": 0.072, # kg·m^2
|
||||
"J3": 0.031, # kg·m^2
|
||||
"J4": 0.003, # kg·m^2
|
||||
"J5": 0.004, # kg·m^2
|
||||
"J6": 0.001 # kg·m^2
|
||||
}
|
||||
alpha = 5.0 # rad/s^2 (corresponds to high speed reach)
|
||||
dyn_torque = {k: inertia[k] * alpha for k in inertia}
|
||||
|
||||
# Total peak combined torque during fast horizontal move
|
||||
peak_combined = {}
|
||||
for k in masses:
|
||||
if k == "J2":
|
||||
peak_combined[k] = horiz_net_j2 + dyn_torque[k]
|
||||
else:
|
||||
peak_combined[k] = horiz_static[k] + dyn_torque[k]
|
||||
|
||||
results = {
|
||||
"motor_specs": {
|
||||
"module": "电机.STEP (60mm x 56mm)",
|
||||
"ratio": "25:1 2-stage planetary",
|
||||
"rated_continuous_Nm": T_CONT,
|
||||
"peak_stall_Nm": T_PEAK,
|
||||
"allowable_thermal_Nm": T_ALLOW
|
||||
},
|
||||
"payload_kg": PAYLOAD_KG,
|
||||
"joints": {}
|
||||
}
|
||||
|
||||
for j in ["J1", "J2", "J3", "J4", "J5", "J6"]:
|
||||
t_raw = horiz_static[j]
|
||||
t_work = work_static[j]
|
||||
t_dyn = dyn_torque[j]
|
||||
t_opt = horiz_net_j2 if j == "J2" else t_raw
|
||||
duty_raw = (t_raw / T_CONT) * 100.0
|
||||
duty_opt = (t_opt / T_CONT) * 100.0
|
||||
margin_peak = T_PEAK / max(peak_combined[j], 0.01)
|
||||
|
||||
results["joints"][j] = {
|
||||
"distal_mass_kg": round(masses[j], 3),
|
||||
"reach_tip_mm": int(dist_tip[j] * 1000),
|
||||
"static_horizontal_raw_Nm": round(t_raw, 2),
|
||||
"static_horizontal_opt_Nm": round(t_opt, 2),
|
||||
"static_working_pose_Nm": round(work_static[j], 2),
|
||||
"dynamic_accel_Nm": round(t_dyn, 2),
|
||||
"peak_combined_Nm": round(peak_combined[j], 2),
|
||||
"duty_cycle_raw_pct": round(duty_raw, 1),
|
||||
"duty_cycle_opt_pct": round(duty_opt, 1),
|
||||
"peak_stall_safety_factor": round(margin_peak, 2),
|
||||
"status": "PASS" if duty_opt <= 100.0 and margin_peak >= 1.5 else "WARN"
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
def generate_report(res):
|
||||
lines = []
|
||||
lines.append("# 6自由度机械臂关节电机力矩平衡校核与优化分析报告\n")
|
||||
lines.append("## 1. 电机性能基准与工况参数")
|
||||
lines.append("- **驱动电机规格**: 标准化一体化行星减速关节电机 (`电机.STEP`)")
|
||||
lines.append("- **外形尺寸**: 直径 Ø60.0 mm × 长度 56.0 mm,方形端盖 63.6 mm")
|
||||
lines.append("- **传动形式**: 2级行星减速器 (减速比 25:1)")
|
||||
lines.append("- **连续额定扭矩 ($T_{rated}$)**: 2.50 N·m")
|
||||
lines.append("- **峰值堵转扭矩 ($T_{peak}$)**: 8.00 N·m")
|
||||
lines.append("- **封闭塑料外壳热衰减后容许连续扭矩 ($T_{allow}$)**: 2.00 N·m")
|
||||
lines.append("- **末端负载 ($m_{load}$)**: 0.50 kg (500g 全行程工况)")
|
||||
lines.append("- **角加速度 ($\alpha$)**: 5.0 rad/s²\n")
|
||||
|
||||
lines.append("## 2. 各关节力矩校核与平衡计算表")
|
||||
lines.append("| 关节编号 | 运动自由度 | 下属质量(kg) | 极限量臂(mm) | 极限水平静力矩(N·m) | 弹簧平衡后力矩(N·m) | 工作姿态静力矩(N·m) | 额定负荷率 | 峰值安全裕度 | 状态 |")
|
||||
lines.append("|:---|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|")
|
||||
|
||||
labels = {
|
||||
"J1": "肩抬举 (Roll)",
|
||||
"J2": "肩俯仰 (Pitch)",
|
||||
"J3": "肘俯仰 (Pitch)",
|
||||
"J4": "前臂自转 (Roll)",
|
||||
"J5": "腕俯仰 (Pitch)",
|
||||
"J6": "手爪开合 (Grip)"
|
||||
}
|
||||
|
||||
for jid, d in res["joints"].items():
|
||||
spring_note = f"**{d['static_horizontal_opt_Nm']:.2f}**" if jid == "J2" else "-"
|
||||
lines.append(f"| {jid} | {labels[jid]} | {d['distal_mass_kg']:.2f} | {d['reach_tip_mm']} | "
|
||||
f"{d['static_horizontal_raw_Nm']:.2f} | {spring_note} | {d['static_working_pose_Nm']:.2f} | "
|
||||
f"**{d['duty_cycle_opt_pct']:.1f}%** | {d['peak_stall_safety_factor']:.2f}x | {d['status']} |")
|
||||
|
||||
lines.append("\n## 3. 关键瓶颈分析与优化措施落地")
|
||||
lines.append("### 3.1 瓶颈定位:J2 肩俯仰关节点")
|
||||
lines.append("- **原始状态**: 在全臂水平伸直极限工况下,下属总质量达 1.75 kg(含大臂、小臂、腕部电机及0.5kg末端负载),质心距离达 464 mm。由此产生的重力力矩高达 **3.78 N·m**。")
|
||||
lines.append("- **风险**: 3.78 N·m 超过电机连续额定值 2.5 N·m(负荷率 151%)。长时间悬停会导致电机过热保护甚至失步;同时 3D 打印结构受长期持续弯矩容易产生塑性蠕变下垂。")
|
||||
lines.append("\n### 3.2 优化方案一:被动式拉簧重力预补偿 (Spring Gravity Balance)")
|
||||
lines.append("- **设计**: 在 J2 肩叉 (P04) 与大臂 (P05) 之间布置平行拉伸弹簧连杆机构。")
|
||||
lines.append("- **力矩对冲**: 水平展开时拉簧产生反向补偿力矩 $T_s = -2.48\\text{ N}\\cdot\\text{m}$。")
|
||||
lines.append("- **效果**: J2 电机实际所需静扭矩骤降至 **1.30 N·m**,额定负荷率从 **151% 降至 52.0%**,安全落入连续工况安全区(小于 $T_{allow} = 2.0\\text{ N}\\cdot\\text{m}$)。")
|
||||
lines.append("\n### 3.3 优化方案二:四点角接触薄壁轴承解耦 (QJ204 Moment Decoupling)")
|
||||
lines.append("- **设计**: J2 驱动侧配置薄壁四点接触轴承 (Ø20ר47×14mm)。")
|
||||
lines.append("- **效果**: 臂身产生的 38 N·m 侧向倾覆弯矩完全由外圈刚性轴承与肩叉支架承担,电机行星减速箱输出轴端面齿仅承受纯扭矩载荷,彻底杜绝行星齿轮偏磨卡死。")
|
||||
lines.append("\n### 3.4 优化方案三:闭口箱涵与内腔纵向加强筋 (Closed-Box Beam Stiffness)")
|
||||
lines.append("- **设计**: 大臂与小臂由原始开缝受弯槽钢结构升级为**带中肋闭口箱型梁**,壁厚 3.0mm,外廓 66mm。")
|
||||
lines.append("- **效果**: 截面抗弯惯性矩 $I_{min}$ 从 $8.25\\times 10^4\\text{ mm}^4$ 提升至 $3.12\\times 10^5\\text{ mm}^4$(**刚度提升 3.78 倍**),彻底消除悬臂颤振与末端抖动。")
|
||||
lines.append("\n### 3.5 优化方案四:典型作业姿态规划 (Elbow Flexion Workload)")
|
||||
lines.append("- 在日常搬运抓取姿态下(肘部弯曲 90°),末端质心力臂由 464 mm 缩短至 210 mm。")
|
||||
lines.append("- 各关节电机工作负荷均在 0.37~1.38 N·m 之间(负荷率 15%~55%),发热温升处于环境温标以内,保证长时间稳定运行。")
|
||||
|
||||
return "\n".join(lines)
|
||||
return res, jl0, jl
|
||||
|
||||
|
||||
def get_actual_arm_masses():
|
||||
json_path = WEB + "/mesh_arm.json"
|
||||
if not os.path.exists(json_path):
|
||||
return None
|
||||
d = json.load(io.open(json_path, encoding="utf-8"))
|
||||
TREE = ["root", "J1", "J2", "J3", "J4", "J5", "J6"]
|
||||
tot = {}
|
||||
for p in d.get("parts", []):
|
||||
nm = p["name"]
|
||||
if p.get("printed", False):
|
||||
v_cm3 = p.get("vol", 0.0) / 1000.0
|
||||
g = v_cm3 * 1.24 * 0.38
|
||||
elif nm.startswith("M"):
|
||||
g = 210.0 # motor module
|
||||
elif "半联轴器" in nm:
|
||||
g = 14.0 # aluminum face spline half-coupling
|
||||
elif "抱箍" in nm:
|
||||
g = 8.0 # aluminum clamp band
|
||||
else:
|
||||
g = 20.0
|
||||
tot[nm] = (g, p.get("grp", "root"))
|
||||
|
||||
m_calc = {}
|
||||
for k, jid in enumerate(TREE[1:], 1):
|
||||
down = set(TREE[k:])
|
||||
m_calc[jid] = (sum(g for g, grp in tot.values() if grp in down) + PAYLOAD_KG * 1000.0) / 1000.0
|
||||
return m_calc
|
||||
def max_payload(res):
|
||||
"""在许用连续力矩下,末端还能带多少 kg(按各关节取最小)。
|
||||
|
||||
线性化:tau_g(p) ≈ tau_g_arm + p·g·L。用的是该关节实测的最大 TCP 力臂,
|
||||
即最不利姿态下的力臂。
|
||||
"""
|
||||
out = {}
|
||||
for tag, d in res["joints"].items():
|
||||
L = d["reach_tip_mm"] / 1000.0
|
||||
if L <= 0:
|
||||
out[tag] = None
|
||||
continue
|
||||
head = T_ALLOW - d["tau_gravity_arm_only_Nm"]
|
||||
out[tag] = round(max(head, 0.0) / (G * L), 3) if head > 0 else 0.0
|
||||
return out
|
||||
|
||||
|
||||
def generate_report(res, mp):
|
||||
L = []
|
||||
L.append("# 6 自由度机械臂 · 关节电机力矩与负载能力校核\n")
|
||||
a = res["assumptions"]
|
||||
L.append("## 1. 前提\n")
|
||||
L.append("- 驱动:%s" % a["motor"])
|
||||
L.append("- 连续额定输出力矩 `T_cont` = %.2f N·m;峰值堵转 `T_peak` = %.2f N·m"
|
||||
% (a["T_cont_Nm"], a["T_peak_Nm"]))
|
||||
L.append("- 封闭打印外壳热衰减后许用连续力矩 `T_allow` = %.2f N·m;末端负载 %.2f kg;"
|
||||
"角加速度 %.1f rad/s²" % (a["T_allow_Nm"], a["payload_kg"], a["alpha_rad_s2"]))
|
||||
L.append("- **%s**\n" % a["note"])
|
||||
L.append("载荷模型:每个零件按网格体积加权质心定位,随姿态做刚体变换,在可达姿态"
|
||||
"网格(俯仰轴取行程端点/中值,自转轴取 0/±60/±120/180°)上取重力力矩最大值。"
|
||||
"力矩为绕**关节自身轴**的分量——这才是电机要出的力。\n")
|
||||
|
||||
L.append("## 2. 各关节校核结果\n")
|
||||
L.append("| 关节 | 名称 | 轴 | 以下质量(kg) | TCP力臂(mm) | 重力力矩(N·m) | 惯性力矩(N·m) | 峰值需求(N·m) | 负荷率(对T_allow) | 峰值裕度 | 判定 |")
|
||||
L.append("|:---:|:---|:---|---:|---:|---:|---:|---:|---:|---:|:---:|")
|
||||
for tag, d in res["joints"].items():
|
||||
L.append("| %s | %s | %s | %.2f | %.0f | **%.2f** | %.2f | %.2f | **%.0f%%** | %s | %s |"
|
||||
% (tag, d["cn"], d["axis"], d["distal_mass_kg"], d["reach_tip_mm"],
|
||||
d["tau_gravity_Nm"], d["tau_dynamic_Nm"], d["tau_required_peak_Nm"],
|
||||
d["duty_vs_Tallow_pct"],
|
||||
("%.2f×" % d["peak_margin_x"]) if d["peak_margin_x"] else "—", d["status"]))
|
||||
L.append("")
|
||||
L.append("## 3. 许用连续工况下的末端负载能力\n")
|
||||
L.append("去掉末端负载后各关节仍需 `tau_gravity_arm_only` 的重力力矩,余额除以该关节"
|
||||
"实测最大 TCP 力臂,即为还能挂的负载:\n")
|
||||
L.append("| 关节 | 仅臂身重力力矩(N·m) | 余额(N·m) | 可挂负载(kg) |")
|
||||
L.append("|:---:|---:|---:|---:|")
|
||||
for tag, d in res["joints"].items():
|
||||
head = T_ALLOW - d["tau_gravity_arm_only_Nm"]
|
||||
L.append("| %s | %.2f | %+.2f | %s |"
|
||||
% (tag, d["tau_gravity_arm_only_Nm"], head,
|
||||
("%.2f" % mp[tag]) if mp[tag] is not None else "—"))
|
||||
ok = [t for t, v in mp.items() if v is not None]
|
||||
worst = min(ok, key=lambda t: mp[t]) if ok else None
|
||||
if worst:
|
||||
L.append("")
|
||||
L.append("**整臂瓶颈:%s,可挂末端负载 %.2f kg。**\n" % (worst, mp[worst]))
|
||||
L.append("## 4. 结论要点\n")
|
||||
over = [t for t, d in res["joints"].items() if d["status"] == "OVERLOAD"]
|
||||
if over:
|
||||
L.append("- **超载关节:%s**。" % "、".join(over))
|
||||
for t in over:
|
||||
d = res["joints"][t]
|
||||
L.append(" - %s(%s)重力 %.2f N·m,超 `T_allow` %.0f%%;即使按峰值堵转算,"
|
||||
"裕度也只有 %s。" % (t, d["cn"], d["tau_gravity_Nm"],
|
||||
d["duty_vs_Tallow_pct"] - 100.0,
|
||||
("%.2f×" % d["peak_margin_x"]) if d["peak_margin_x"] else "—"))
|
||||
L.append("- 结论对电机参数高度敏感:若实际连续额定高于假定值,"
|
||||
"请用 `ARMT_CONT` / `ARMT_ALLOW` 覆盖后重跑本脚本。")
|
||||
else:
|
||||
L.append("- 全部关节在许用连续力矩内。")
|
||||
L.append("- 惯量按 Σm·d² 估,**未计入零件自身转动惯量**,对肩/肘是大臂量级的关节"
|
||||
"约低估一成,惯性力矩一栏应视为下限。")
|
||||
L.append("- 本版不含被动弹簧平衡。旧版报告提出的 J2 拉簧预补偿(2.48 N·m)在几何上"
|
||||
"**尚未实现**;若要保留该方案,需要在 P04/P05 之间增加拉簧连杆机构。")
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def main():
|
||||
masses = get_actual_arm_masses()
|
||||
if masses:
|
||||
print("Accurate physical mass distribution (including 0.5kg payload):")
|
||||
for k, v in masses.items():
|
||||
print(f" {k}: {v:.2f} kg (arm: {v - PAYLOAD_KG:.2f} kg + payload: {PAYLOAD_KG:.2f} kg)")
|
||||
res = analyze_torque(masses)
|
||||
|
||||
# Save JSON
|
||||
out_json = DOCS + "/torque_balance.json"
|
||||
io.open(out_json, "w", encoding="utf-8").write(json.dumps(res, ensure_ascii=False, indent=2))
|
||||
print(f"Torque balance JSON written to: {out_json}")
|
||||
res, jl0, jl = analyze(PAYLOAD)
|
||||
mp = max_payload(res)
|
||||
io.open(DOCS + "/torque_balance.json", "w", encoding="utf-8").write(
|
||||
json.dumps(res, ensure_ascii=False, indent=2))
|
||||
io.open(DOCS + "/torque_report.md", "w", encoding="utf-8").write(generate_report(res, mp))
|
||||
|
||||
# Save Markdown report
|
||||
rep = generate_report(res)
|
||||
out_md = DOCS + "/torque_report.md"
|
||||
io.open(out_md, "w", encoding="utf-8").write(rep)
|
||||
print(f"Torque report MD written to: {out_md}")
|
||||
print("末端负载 %.2f kg T_cont=%.2f T_allow=%.2f T_peak=%.2f alpha=%.1f\n"
|
||||
% (PAYLOAD, T_CONT, T_ALLOW, T_PEAK, ALPHA))
|
||||
print("%-4s %-8s %-12s %8s %8s %8s %9s %8s %s"
|
||||
% ("关节", "名称", "轴", "重力N·m", "惯性N·m", "峰值N·m", "负荷率", "峰值裕度", "判定"))
|
||||
for tag, d in res["joints"].items():
|
||||
print("%-4s %-8s %-12s %8.2f %8.2f %8.2f %8.0f%% %8s %s"
|
||||
% (tag, d["cn"], d["axis"], d["tau_gravity_Nm"], d["tau_dynamic_Nm"],
|
||||
d["tau_required_peak_Nm"], d["duty_vs_Tallow_pct"],
|
||||
("%.2fx" % d["peak_margin_x"]) if d["peak_margin_x"] else "-",
|
||||
d["status"]))
|
||||
print("\n许用连续力矩下可挂末端负载 (kg):")
|
||||
for tag in res["joints"]:
|
||||
print(" %-4s %s" % (tag, ("%.2f" % mp[tag]) if mp[tag] is not None else "—"))
|
||||
ok = [t for t, v in mp.items() if v is not None]
|
||||
if ok:
|
||||
w = min(ok, key=lambda t: mp[t])
|
||||
print(" 整臂瓶颈 %s -> %.2f kg" % (w, mp[w]))
|
||||
print("\n已写出 docs/torque_balance.json + docs/torque_report.md")
|
||||
sys.stdout.flush()
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("TORQUE BALANCE SUMMARY (WITH PASSIVE SPRING & STIFFNESS OPTIMIZATION)")
|
||||
print("="*80)
|
||||
for jid, d in res["joints"].items():
|
||||
print(f"[{jid}] Horiz Raw: {d['static_horizontal_raw_Nm']:5.2f} N·m | "
|
||||
f"Opt: {d['static_horizontal_opt_Nm']:5.2f} N·m | "
|
||||
f"Duty: {d['duty_cycle_opt_pct']:5.1f}% | "
|
||||
f"Peak Margin: {d['peak_stall_safety_factor']:4.2f}x | Status: {d['status']}")
|
||||
print("="*80)
|
||||
os._exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
os._exit(0)
|
||||
|
||||
+134
-119
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+134
-119
File diff suppressed because one or more lines are too long
+134
-119
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user