實戰(zhàn):用Python自動計算零件坯料尺寸)
做NX二次開發(fā)這些年最常被問到的需求除了批量出圖、自動BOM就是這種給我一個3D零件幫我算出它的坯料尺寸。這個“坯料尺寸”聽起來簡單真做起來坑不少——單位搞錯、坐標(biāo)系歪了、多實體漏算、斜著放的零件包圍盒虛大每一步都能讓人折騰半天。這篇文章講的是這個系列的第一集用Python在NX里實現(xiàn)坯料尺寸的自動計算。我會從需求邏輯、環(huán)境準(zhǔn)備講到完整代碼、經(jīng)驗參數(shù)和避坑清單目標(biāo)是讓拿到腳本的人跑完就能直接用。適合兩類人剛?cè)腴TNX二次開發(fā)想找個完整案例的以及有NX操作基礎(chǔ)、想用腳本把重復(fù)計算工作省掉的工程師。1. 這個需求到底在解決什么問題1.1 坯料尺寸從3D零件到一塊“能加工”的料先把這個概念聊透。零件設(shè)計出來之后不管你是要車、銑、刨、磨還是走鑄造、鍛造路線手上拿到的第一塊材料都不可能直接是成品形狀。做機(jī)加工前要先去倉庫找一塊比零件大的料做鑄造時模具型腔里先有一個帶余量的毛坯形狀做CAM編程開粗時軟件也要先知道毛坯邊界在哪。這塊“比成品大一圈”的初始材料就是坯料。計算邏輯拆開就三步拿到成品零件的最大外形尺寸在需要加工的面或方向上留出加工余量再乘上材料收縮率。用公式表示就是坯料尺寸 成品外形尺寸 ×1 收縮率 單邊加工余量 × 2為什么加收縮率鑄造件從高溫冷卻到常溫會縮水模具型腔必須按“放大了的成品”來設(shè)計鍛造件也有熱脹冷縮和氧化皮損耗。為什么左右都加因為零件放在坯料中間X、Y兩個方向都要余量所以是“兩倍”。這份邏輯看著簡單但真正跑到代碼里需要處理的邊界情況比想象中多。1.2 方案選型包圍盒為什么是第一步計算三維模型外形尺寸常見思路有三條。第一條包圍盒法。直接問NX要這個體的最小包圍盒Bounding Box拿到X、Y、Z三個方向的最小和最大坐標(biāo)一減就是長寬高。優(yōu)點是快、穩(wěn)、API簡單適合外形接近方塊、長條、圓柱的絕大多數(shù)機(jī)械零件。缺點是零件斜著放或者有曲面輪廓時包圍盒會比真實投影外形“虛大”。第二條面投影輪廓法。把零件往基準(zhǔn)平面投影提取最外輪廓曲線再量輪廓的包絡(luò)尺寸。精度高能處理斜面、異形面但需要遍歷面、邊、環(huán)的拓?fù)潢P(guān)系代碼量上了一個臺階。第三條Structures/Parasolid底層分析。通過內(nèi)核函數(shù)直接訪問幾何拓?fù)渥鼍_的最小外接尺寸計算最準(zhǔn)但開發(fā)成本也最高。我做第一集時定下的原則是先把包圍盒方案做成型跑通把參數(shù)框架、單位處理、批量邏輯沉淀下來。等到第二集再針對異形輪廓、通孔盲孔這些做拓?fù)鋵用娴木?。事實證明這個節(jié)奏是對的——包圍盒方案解決了80%的常規(guī)零件剩下的20%才有必要上高級手段。2. 環(huán)境準(zhǔn)備Python跑進(jìn)NX的兩條路2.1 NX版本和Python運行環(huán)境先回答一個被反復(fù)問的問題NX的Python二次開發(fā)要不要自己裝Python答案是不用。NX從11.0開始內(nèi)置了Python解釋器12.0之后對NXOpen Python API的支持已經(jīng)相當(dāng)完整我日常用的NX版本是12.0和2206系列下面的代碼在兩個版本上都實測過。確認(rèn)你的NX支持Python最簡單的辦法是打開菜單文件 - 執(zhí)行 - Python。如果彈出的是Python腳本選擇對話框那環(huán)境基本沒問題。有些老版本比如NX 10沒有這個入口那就需要先升級到NX 11以上或者在命令行里手動配置NXOpen Python環(huán)境那種情況建議直接換版本沒必要糾結(jié)。另外NX內(nèi)置的Python版本和操作系統(tǒng)沒關(guān)系也不影響系統(tǒng)中是否裝了Anaconda或者其他Python發(fā)行版。腳本里寫的import NXOpen、import NXOpen.UF指向的都是NX自帶的那套API庫這一點在剛開始寫的時候容易混淆。2.2 Journal錄制法讓NX自己教你寫API對剛接觸NX二次開發(fā)的朋友我強(qiáng)烈建議先學(xué)會“錄制Journal”。操作方法在NX里點“錄制”按鈕圖標(biāo)是個圓點選擇語言為Python老版本默認(rèn)是C#或VB記得在錄制選項里切換然后手動做一遍操作停止錄制后就會生成一個.py文件。打開這個文件你會發(fā)現(xiàn)NX把你剛才點菜單、選對象、改參數(shù)的動作全翻譯成了API調(diào)用。我的習(xí)慣是拿錄制出來的文件當(dāng)“API字典”遇到不知道的函數(shù)就去錄制一段對應(yīng)操作再對照代碼找函數(shù)名和參數(shù)。比如想確認(rèn)“獲取當(dāng)前顯示部件”的寫法錄一下就知道是theSession.Parts.Display還是theSession.Parts.Work。這套方法比翻幫助文檔直觀很多尤其適合Python這種相對小眾的NX開發(fā)語言。2.3 腳本存放與執(zhí)行寫好的Python腳本建議統(tǒng)一放在固定目錄比如C:\NXScripts文件名用英文。執(zhí)行腳本有兩種方式第一種是文件 - 執(zhí)行 - Python直接選擇腳本運行第二種是寫一個簡單的菜單腳本.men文件注冊到NX里之后從菜單按鈕一鍵觸發(fā)。第一集先用第一種方式簡單直接調(diào)通邏輯再考慮做成菜單按鈕。調(diào)試技巧方面print輸出是主力工具。NX執(zhí)行Python時print的內(nèi)容會輸出到系統(tǒng)控制臺或者“信息”窗口具體看NX版本。我建議在腳本開頭加一段輸出分隔線把運行過程打印出來方便定位問題。3. 核心代碼從零件到坯料尺寸的完整腳本3.1 獲取當(dāng)前工作部件里的所有實體先寫最基礎(chǔ)的骨架連上NX會話拿到當(dāng)前工作部件遍歷部件里的所有Body篩出實體Solid Body而不是片體。import NXOpen import NXOpen.UF the_session NXOpen.Session.GetSession() the_ufsession NXOpen.UF.UFSession.GetUFSession() work_part the_session.Parts.Work if work_part is None: raise RuntimeError(請先打開一個部件文件) all_bodies work_part.Bodies solid_tags [] for body in all_bodies: if body.IsSolidBody: solid_tags.append(body.Tag) print(有效實體數(shù)量:, len(solid_tags)) if len(solid_tags) 0: raise RuntimeError(當(dāng)前工作部件中沒有實體)這段代碼里有個細(xì)節(jié)值得注意the_session.Parts.Work拿的是當(dāng)前工作部件如果NX界面里打開了多個文件顯示部件和工作部件可能不是同一個文件。我處理批量零件時都會先彈出一個提示讓使用者確認(rèn)工作部件是對的否則腳本可能跑在錯誤的文件上結(jié)果看起來“莫名其妙”。body.IsSolidBody這個判斷必須加。NX里既有Solid實體也有Sheet片體。如果不過濾片體的包圍盒也會被算進(jìn)來最后毛坯尺寸會整體偏大。我在測試時有一次忘了加這個判斷一個帶曲面片體的零件算出來的X方向尺寸比實際大了將近50毫米排查了半天才鎖定是片體在搗亂。3.2 核心UF_MODL_ask_bounding_box 包圍盒計算獲取包圍盒的接口我推薦用UF函數(shù)而不是NXOpen原生API原因后面專門說。UF_MODL_ask_bounding_box這個函數(shù)在Python里的調(diào)用形式是the_ufsession.Modl.AskBoundingBox(body_tag)。# 初始化范圍 inf float(inf) min_point [inf, inf, inf] max_point [-inf, -inf, -inf] for tag in solid_tags: result the_ufsession.Modl.AskBoundingBox(tag) # 不同NX版本對返回值的封裝不同這里做兼容 if isinstance(result, tuple) and len(result) 2: error_code result[0] box result[1] else: error_code 0 box result if error_code ! 0: print(包圍盒計算失敗Body Tag:, tag) continue # box依次是 min_x, min_y, min_z, max_x, max_y, max_z min_point[0] min(min_point[0], box[0]) min_point[1] min(min_point[1], box[1]) min_point[2] min(min_point[2], box[2]) max_point[0] max(max_point[0], box[3]) max_point[1] max(max_point[1], box[4]) max_point[2] max(max_point[2], box[5]) part_len max_point[0] - min_point[0] part_wid max_point[1] - min_point[1] part_hei max_point[2] - min_point[2] print(成品最大外形尺寸: {:.3f} x {:.3f} x {:.3f}.format(part_len, part_wid, part_hei))這里必須說清楚一個容易踩的返回坑順便提一句這個函數(shù)返回的坐標(biāo)是基于絕對坐標(biāo)系的跟WCS沒關(guān)系。所以如果零件離原點很遠(yuǎn)min和max的數(shù)值會很大但差值也就是長寬高不受影響我們計算毛坯尺寸時只需要差值問題不大。3.3 把余量和收縮率算進(jìn)去拿到成品外形尺寸之后下一步就是套公式加余量、乘收縮率。我在腳本里把參數(shù)都放在文件頂部方便每個人按自己工廠的工藝習(xí)慣去改。# 加工余量單邊單位mm allowance_xy 2.0 allowance_z 2.0 # 材料收縮率普通鑄鋼約1.5%鋁合金約1.0%這里先用常見值 shrink_rate 0.005 blank_len part_len * (1 shrink_rate) 2 * allowance_xy blank_wid part_wid * (1 shrink_rate) 2 * allowance_xy blank_hei part_hei * (1 shrink_rate) 2 * allowance_z print(含余量毛坯尺寸: {:.3f} x {:.3f} x {:.3f}.format(blank_len, blank_wid, blank_hei))實話說這個公式是坯料計算的基礎(chǔ)框架真正的工廠會加上更多條件。比如有的鍛件要考慮拔模斜度毛坯頂部和底部的余量不一樣有的鑄件要留出分型面飛邊尺寸還要再大一圈。這些在后續(xù)版本里都可以慢慢加但第一版先把最核心的“外形尺寸余量收縮”跑通。腳本最后我把尺寸向上取整到整數(shù)毫米因為實際買料、下料不可能精確到小數(shù)點后三位import math blank_len_r int(math.ceil(blank_len)) blank_wid_r int(math.ceil(blank_wid)) blank_hei_r int(math.ceil(blank_hei)) print(圓整后的坯料尺寸: {} x {} x {}.format(blank_len_r, blank_wid_r, blank_hei_r))圓整這步看上去不起眼實際用途很大。鋁合金型材、棒料、板材都有標(biāo)準(zhǔn)規(guī)格圓整到整數(shù)之后更好去庫存里對號入座。4. 原理、細(xì)節(jié)與經(jīng)驗參數(shù)4.1 為什么用UF接口而不是NXOpen原生APINXOpen的原生API里其實有一個方法可以拿包圍盒body.GetBoundingBox()。但我在實際項目里發(fā)現(xiàn)這個方法在不同NX版本上的返回類型不穩(wěn)定有的版本返回兩個點坐標(biāo)有的版本返回一個BoundingBox對象升級NX之后原有代碼經(jīng)常要改。UF函數(shù)UFUN則穩(wěn)定得多UF_MODL_ask_bounding_box這個接口在NX老版本里就有函數(shù)簽名幾乎沒變過而且UF函數(shù)在Python里調(diào)用性能開銷小大批量算幾百個零件時優(yōu)勢明顯。用UF也不是沒有代價最典型的就是錯誤處理不如NXOpen那么優(yōu)雅。NXOpen的API很多會拋異常UF函數(shù)通常只返回一個整數(shù)錯誤碼需要自己判斷。我在腳本里做了一版兼容處理判斷返回值是不是元組是就按“錯誤碼數(shù)據(jù)”解析不是就直接當(dāng)數(shù)據(jù)用。這套兼容邏輯幫我省了不少和不同版本NX“商量”的時間。4.2 單位、坐標(biāo)系、隱藏體和多實體四個高頻翻車點單位問題是我見過最多的坑。UF_MODL_ask_bounding_box返回的數(shù)值單位是“部件單位”不一定就是毫米。如果零件模板是英制的返回的就是英寸直接拿去當(dāng)毫米算毛坯尺寸會差25.4倍。穩(wěn)妥的做法是腳本開頭判斷部件單位if work_part.Units NXOpen.Part.Units.Inches: scale 25.4 print(檢測到英制部件結(jié)果將轉(zhuǎn)換為毫米) else: scale 1.0 part_len_mm part_len * scale part_wid_mm part_wid * scale part_hei_mm part_hei * scale坐標(biāo)系方面包圍盒基于絕對坐標(biāo)系跟WCS無關(guān)。如果零件是斜著建模的包圍盒會“虛大”這種情況我會提示用戶先把裝配或零件擺正再跑腳本或者等第二集的投影輪廓方案來解決。隱藏體也要注意。work_part.Bodies返回的是部件里所有Body包括被Blank掉的。多數(shù)場景下隱藏體不影響毛坯計算因為它的外形包圍盒依然需要參與合并。但有些設(shè)計人員會故意把老版本、舊外形放在某個圖層里隱藏掉這時候就需要判斷body.IsBlankskip_blank False # 默認(rèn)不跳過隱藏體 if body.IsBlank and skip_blank: continue多實體零件的處理邏輯我在腳本里已經(jīng)做了對每個體分別求包圍盒再把所有體的最小值和最大值合并。這相當(dāng)于求出“多實體組合體的總包圍盒”對大多數(shù)毛坯計算場景是準(zhǔn)確的。但如果多個實體之間有重疊的干涉部分這種方法不會自動把它們“合并成一個凸包”理論上會偏大一點。實際零件很少出現(xiàn)這種情況遇到了再單獨處理就行。4.3 加工余量與收縮率參考表余量和收縮率不能拍腦袋給至少要對得上工廠的常用工藝。我把自己整理的一份參考值放出來僅供參考具體以各自車間的工藝卡片為準(zhǔn)。加工余量方面常見單邊余量參考如下加工類型單邊余量mm適用場景粗銑、開粗1.5 ~ 3.0數(shù)控銑削前備料半精銑0.5 ~ 1.0粗加工后留半精余量精銑、磨削0.1 ~ 0.3最終精加工余量鉆孔按孔徑留量孔徑大于30mm時毛坯可預(yù)鑄或預(yù)鉆材料收縮率方面常見材料參考如下材料類型收縮率灰鑄鐵0.7% ~ 1.0%球墨鑄鐵0.5% ~ 0.9%鑄鋼1.5% ~ 2.0%鋁合金鑄件0.8% ~ 1.3%鍛件碳鋼0.5% ~ 1.0%我在腳本里默認(rèn)用的是0.5%收縮率、2mm單邊余量這組參數(shù)適合大多數(shù)中小型鋼制零件。如果是大件或者精密件強(qiáng)烈建議先和工藝工程師確認(rèn)再修改腳本開頭的參數(shù)不要直接套默認(rèn)值。5. 常見問題與排查實錄貼幾個我實際踩過、且群里同行反復(fù)問過的問題整理成速查表故障現(xiàn)象可能原因解決辦法執(zhí)行腳本報“沒有實體”打開了裝配但沒有激活工作部件或當(dāng)前部件全是片體用CtrlW確認(rèn)工作部件檢查Body過濾條件輸出的尺寸數(shù)值明顯偏大英制部件沒做單位換算或者片體參與計算加Part.Units判斷嚴(yán)格過濾IsSolidBody局部實體數(shù)值為0AskBoundingBox返回了錯誤碼被腳本continue跳過了打印錯誤碼檢查該體是不是被抑制或有問題中文print輸出亂碼NX內(nèi)置Python控制臺編碼不匹配腳本開頭重設(shè)stdout編碼為GBK或UTF-8零件斜著放尺寸偏大包圍盒隨絕對坐標(biāo)系走了虛大的矩形包絡(luò)先擺正零件再計算或升級用投影輪廓方案有多個體但只算了一個遍歷邏輯只取了Bodies[0]循環(huán)遍歷所有SolidBody合并min/max挑兩個典型問題展開說。一個典型問題是print中文亂碼。NX在Windows上執(zhí)行Python時內(nèi)置解釋器的stdout輸出編碼和NX的控制臺窗口經(jīng)常對不上打印中文要么報錯要么亂碼。我在這類腳本開頭都會加一段import sys import io try: sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodinggbk, errorsignore) except Exception: pass這段代碼在部分NX版本上可能會因為sys.stdout.buffer不可用而拋異常所以用try包住不影響主流程。加上之后中英文輸出基本都能正常顯示。另一個典型問題是“算出來的結(jié)果和你手工測的差很多”。有一次我拿一個帶大量圓角、倒角的鑄件測試包圍盒算出來是310x220x180但工藝?yán)蠋煾凳止ち砍鰜硎?05x215x178。原因很簡單圓角處的外形不是直角包圍盒按最大包絡(luò)算自然比實際可貼合的加工尺寸大幾個毫米。這個場景就屬于我之前說的“包圍盒虛大”。解決辦法是評估一下虛大量對工廠備料的影響如果備料本身就要留安全余量這幾個毫米根本無所謂如果是精密鍛造模具設(shè)計那就必須走拓?fù)漭喞肪€。6. 還沒完異形輪廓、通孔盲孔怎么辦6.1 包圍盒的局限到底在哪拿一個最直觀的例子一根直徑50毫米、長200毫米的圓棒包圍盒算出來會是多少200x50x50。這沒問題因為圓棒的包圍盒剛好是個方條和實際棒料占用的矩形空間一致買圓鋼還是方鋼都能對上。但如果是一個斜放45度的方塊邊長為100毫米包圍盒算出來的X方向尺寸會變成約141毫米比實際大了41%。這種虛大在一些對尺寸敏感的場合是沒法接受的。同理通孔和盲孔的問題也會在坯料計算里冒出來。鑄造毛坯有一個經(jīng)典原則直徑比較小的通孔毛坯階段不鑄出來直接做實心后續(xù)全靠機(jī)加工鉆直徑足夠大的通孔毛坯才需要預(yù)鑄出孔來減少后續(xù)加工量。要不要在毛坯里鑄出這個孔判斷依據(jù)就是這個孔是通孔還是盲孔、孔多深、孔徑多大。而這些判斷沒法靠包圍盒解決必須遍歷面、邊、環(huán)的拓?fù)潢P(guān)系。6.2 下一篇預(yù)告拓?fù)渑c孔特征分析第二集我會重點拆解兩塊內(nèi)容第一塊如何從實體上提取面、邊、環(huán)的拓?fù)鋽?shù)據(jù)用拓?fù)潢P(guān)系準(zhǔn)確判斷一個孔到底是通孔還是盲孔第二塊如何基于面投影輪廓做更精確的最小包絡(luò)尺寸計算把斜件、異形件的虛大問題解決掉。會用到UF_MODL_ask_face_data、邊循環(huán)遍歷這類接口也會講清楚拓?fù)鋽?shù)據(jù)結(jié)構(gòu)在NX里到底是怎么組織的。第一集先把包圍盒這套基礎(chǔ)功能用熟把環(huán)境、單位、余量這些基礎(chǔ)設(shè)施打牢。后續(xù)所有高級功能都建立在這套代碼框架之上。最后說點實在的。我在實際項目里用這套腳本跑了幾個月的坯料計算最大的體會是腳本本身不復(fù)雜真正花時間的是把邊界情況都想到。單位有沒有轉(zhuǎn)、片體有沒有過濾、隱藏體要不要參與合體、余量是不是按工藝來這些才是決定腳本能不能被車間師傅接受的關(guān)鍵。建議拿到代碼之后先找兩三個自己熟悉的零件手工測量比對一遍確認(rèn)結(jié)果符合預(yù)期再批量使用。等確認(rèn)穩(wěn)定了再把固定參數(shù)抽成界面輸入或者做成菜單按鈕真正變成自己手里的趁手工具。