前几天我上传了坤坤在abaqus中为我一舞的视频,当时视频画风是这样。
这引起了小黑子们的极大不满。他们一度企图攻陷我的评论区和私信。他们声称要抱走我的9950X,16核心,32线程,满载频率5.5ghz的CPU。更可怕的是,他们还要拔走我的32GX4一共是128G,而且是去年2000RMB买的内存条(懂的人请起立鼓掌,我就是为了这句话才发的这篇帖子)。
这使我鸡动的夜不能寐,鉴于此,我连夜制作了灰度版本的坤坤视频,送给大家,希望大家在繁重的工作中能够嘴角上扬,欢乐一下。
我看过坤坤在示波器上面跳舞;我看过坤坤在LCD灯带上跳舞;我还看过坤坤用汉字跳舞。
我已经在有限元领域练习时长2.4坤年了,但是,,,,我就是没有见过坤坤在ABAQUS中为我跳舞,我的余生将得不到坤坤为我起舞,这不合理,这太不抽象了。
我就开始思考,怎么能让坤坤为我一曲肝肠断呢?
我开始了忙忙碌碌,但是碌碌无为的思考过程,我想了很多的方案,包括但不限于下面提到的
以上方案全部失败了,是的,我要失败了,我不能让坤坤为我一舞了,我感觉我要带着无尽的遗憾卸载abaqus了,我的坤坤,呜呜呜......
但我可是练习时长2.4坤年的有限元先生啊!
就在某一个平平无奇的办公室午休时间,我的师弟打着呼噜让我睡不着觉,就像是张雪开着820RR在我脸上折返骑,与此同时我又尽力让自己睡着,再加上眼球被胳膊硌住了,大腿也麻了,想调整屁 股的位置但动不了的时候,这一系列听觉、触觉、睡觉混合在一起的时候,我仿佛看到了坤坤在我的脑海里闪着诡异的光!
坤坤,我悟了!
什么?我知道怎么写程序了?怎么可能!我想起了AI啊!!!!!
后面的事情就像是吃了椒麻拐拐筋之后肚子疼窜稀一样顺畅。
我一开始自作聪明,把我的方案给AI,让他评价一下,就在我已经准备享受这个人工智障对我这个高等灵长类动物的崇拜了!
想到这里,我不由自主的想起了祖先从遥远的非洲大陆,跨越大半个地球,最终来到亚欧大陆的感人故事,
想到这里,生活在伦敦郊区下水道的人也不由自主的顿了顿自己的领带,彷佛他们的前途被大英帝国的金碧辉煌照亮的睁不开眼睛,尽管他们没有领带,尽管他们没有衬衫,尽管他们没有外套,尽管他们只有满身泥污,
想到这里,我看到了墙上的斑点,我不知道这个斑点是怎么形成的,是某种动物留下的吗......
停,这太抽象了...
让我看看AI是怎么惊叹我思想的伟大的,AI一定会说:我对你的赞叹犹如滔滔江水连绵不绝,只可惜你电脑的1T固态+2T机械的容量放不下。
你们失望了,实际上,AI并没有侮辱我的智商低下和唐氏行为,因为我在提示词里面写了,无论我多么抽象,我都是你爹,你不能忤逆我。
但是,我看了他给我的解决方案之后,我发现,我的自惭形秽是真的放不下我的1T固态+2T机械的容量。
如果说人类的想象力和AI相差了一个蜉蝣,我的能力和AI就相差了1mol爱因斯坦+3葛洪数个高斯。
AI给了我一个木凳狗带、拍案惊奇、一刀封喉的解决方案:直接修改ODB文件。
我当时的表情就好像吃到了黄三石没炒熟的豆角那样。就好像曾小贤吃到了陆展博的意大利面。就好像八十万对六十万,优势在我一样。
我甚至都不知道ODB可以修改!我觉得,发明天才可以修改的人一定是个ODB,究竟是出于什么原因发明了ODB可以修改呢,好难猜啊!伟大的掌管圆周率的神——拉马努金,你注意到这个问题了吗?你在做梦的时候有限元女神有没有告诉你为什么ODB可以修改呢?真的好难猜啊!
但是AI他就是知道ODB可以修改!他一定是个甜菜,是的,他一定是个甜菜,下次打瓦的时候喊上AI。
严肃起来,下面开始解释方案。
方案非常简单:视频帧的RGB数据+ODB的云图显示功能。
我上面胡扯了这么多,就是为了凑篇幅,想必你们这么聪明一定早就看出来了,并且直接跳转到这里了。
不许揍我,下面看程序!
下面的程序将MP4文件处理成一帧一阵的rgb数据,导入到外部文件中,等待后续的程序读取并且写入到内存中。
有小伙伴可能会问,为什么不直接把视频文件的rgb信息读取到内存中,直接写出到odb文件中,这不是节省了数据的读写时间吗?
原因很简单:我电脑里的abaqus python环境不支持通过pip安装库文件,所以解析视频的程序无法在我的abaqus python环境中运行,如果你的abaqus版本比较高,可能可以用pip安装库文件,这个我没研究过。
# -*- coding: utf-8 -*-
import cv2
import os
import numpy as np
def an alyze_video_to_bin(video_path):
#严谨的数据前处理:
#直接利用 Numpy 矩阵操作完成 Y 轴翻转,并将灰度值转化为 C 语言标准的单精度浮点数 (float32)。
#输出纯二进制文件 (.bin),每帧体积精确锁定为 852 * 480 * 4 bytes = 1,635,840 bytes (约 1.56 MB)。
if not os.path.exists(video_path):
print("Error: Video file not found: " + video_path)
return
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print("Error: Cannot open video file.")
return
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print("Video: " + video_path)
print("Resolution: {} x {}".format(width, height))
print("FPS: {}".format(fps))
print("Total frames: {}".format(total))
print("=" * 60)
base_dir = os.path.dirname(os.path.abspath(video_path))
out_dir = os.path.join(base_dir, "bin_data")
# Python 3 语法,确保输出目录存在
os.makedirs(out_dir, exist_ok=True)
print("Output binary folder: " + out_dir)
frame_index = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 1. 直接转换为灰度图 (0-255,dtype=uint8)
gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 2. 坐标系对齐:Abaqus 单元编号从底部开始,因此直接将矩阵在 Y 轴翻转
gray_flipped = gray_img[::-1, :]
# 3. 展平为一维数组,并强制转换为单精度浮点型 (float32)
# Abaqus SCALAR 场通常接受浮点数,float32 完美兼容且节省空间
gray_float_array = gray_flipped.flatten().astype(np.float32)
# 4. 直接将底层内存数据以二进制字节流写入文件
bin_path = os.path.join(out_dir, "frame_{:06d}.bin".format(frame_index + 1))
with open(bin_path, 'wb') as f:
f.write(gray_float_array.tobytes())
frame_index += 1
if frame_index % 50 == 0:
print("Processed {:6d} / {} frames".format(frame_index, total))
cap.release()
print("=" * 60)
print("Done. Processed {} frames. Binary files saved to: {}".format(frame_index, out_dir))
if __name__ == '__main__':
an alyze_video_to_bin(os.path.join(os.path.dirname(__file__), 'hua.mp4'))
下面的程序读取外部的rgb数据,然后将rgb数据直接写入到odb文件!对!就是直接简单粗暴的修改odb文件。没有任何弯弯绕绕,没有任何高阶的操作,大道至简。
# -*- coding: utf-8 -*-
import os
import glob
import shutil
import struct
import time
import sys
from odbAccess import openOdb
from abaqusConstants import CENTROID, SCALAR
# ── Configuration ─────────────────────────────────────────────────────────────
SCRIPT_DIR = os.getcwd()
ODB_SRC = os.path.join(SCRIPT_DIR, 'Job-1.odb')
ODB_PATH = os.path.join(SCRIPT_DIR, 'Job-1-videos.odb')
# 指向新的纯二进制数据目录
BIN_DIR = os.path.join(SCRIPT_DIR, 'videos', 'bin_data')
WIDTH = 852 # 视频像素,需要手动修改
HEIGHT = 480 # 视频像素,需要手动修改
NUM_ELEMENTS = WIDTH * HEIGHT # 408960
MAX_FRAMES = None
# ──────────────────────────────────────────────────────────────────────────────
def read_frame_from_bin(bin_path):
"""
利用 struct 从二进制文件一次性还原单精度浮点数。
Abaqus Python 2.7 原生支持,速度极快。
"""
# 按照 4 bytes (float32) * 408960 读取整个文件
with open(bin_path, 'rb') as f:
raw_bytes = f.read(NUM_ELEMENTS * 4)
# 'f' 代表标准单精度浮点数。构造格式化字符串,如 '408960f'
fmt = '{0}f'.format(NUM_ELEMENTS)
# unpack 会直接在底层用 C 语言解析字节流,返回一个包含所有数据的 tuple (元组)
gray_vals = struct.unpack(fmt, raw_bytes)
return gray_vals
def collect_frames_from_bin(bin_dir):
"""生成器:按序读取二进制文件"""
files = sorted(glob.glob(os.path.join(bin_dir, 'frame_*.bin')))
if MAX_FRAMES is not None:
files = files[:MAX_FRAMES]
for i, fpath in enumerate(files):
yield i, read_frame_from_bin(fpath)
def main():
if not os.path.exists(ODB_PATH):
print("Copying {0} -> {1}".format(ODB_SRC, ODB_PATH))
shutil.copy2(ODB_SRC, ODB_PATH)
else:
print("Using existing working copy: " + ODB_PATH)
odb = openOdb(path=ODB_PATH, readOnly=False)
inst = odb.rootAssembly.instances['PART-1-1']
step = odb.steps['Step-1']
existing_frames = len(step.frames)
# 在 Abaqus Python 2.7 中构建标签 tuple
elem_labels = tuple(range(1, NUM_ELEMENTS + 1))
frame_gen = collect_frames_from_bin(BIN_DIR)
bin_files = sorted(glob.glob(os.path.join(BIN_DIR, 'frame_*.bin')))
total_frames = len(bin_files) if MAX_FRAMES is None else min(MAX_FRAMES, len(bin_files))
print("Total binary frames to write: {0}".format(total_frames))
written = 0
t_start = time.time()
for frame_idx, gray_vals in frame_gen:
frame_time = float(frame_idx)
odb_frame = step.Frame(
incrementNumber = existing_frames + frame_idx,
frameValue = frame_time,
description = 'VideoFrame_{0:06d}'.format(frame_idx + 1)
)
field = odb_frame.FieldOutput(
name = 'SDV1',
description = 'Grayscale intensity',
type = SCALAR
)
# 1. 极致优化:利用 C 语言底层的 zip 函数,瞬间完成序列重组
data_block = zip(gray_vals)
field.addData(
position = CENTROID,
instance = inst,
labels = elem_labels,
data = data_block
)
written += 1
# 2. 内存管理优化:强制定期刷写 C++ 底层缓存,防止 ODB 内存碎片化导致降速
# 建议每 10~20 帧 save 一次,具体频率可根据实际内存压力微调
if written % 10 == 0:
odb.save()
# 3. 严谨的进度与耗时计算
elapsed = time.time() - t_start
# 避免除以零的错误
if written > 0:
eta = (elapsed / written) * (total_frames - written)
else:
eta = 0.0
# 使用 Python 2.7 兼容的 format 语法格式化输出
print(" 写入进度: 帧 {0:5d} / {1:5d} ({2:5.1f}%) | 已用时: {3:4.0f}s | 预计剩余: {4:02.0f}m{5:02.0f}s".format(
written, total_frames,
100.0 * written / total_frames,
elapsed,
eta // 60, eta % 60))
# 关键操作:强制刷新 Abaqus 的标准输出缓冲区,确保控制台实时显示
sys.stdout.flush()
# 循环结束后,进行最后一次保存并关闭数据库
odb.save()
odb.close()
print("=" * 60)
print("Done. {0} frames successfully written to ODB.".format(written))
if __name__ == '__main__':
main()
魔术的魅力在于
当看到表演效果的时候
觉得这世界真的有魔法
但是当知道原理的时候
却只道寻常
当看到坤坤在abaqus中跳舞
觉得abaqus还能这么玩
有限元还能这么用
但是核心过程没用abaqus
只是简单粗暴的修改ODB
你会不会只道寻常?
其实
我真正想说的是
这世界
只有你想不到
没有你做不到
充满想象力
去做你喜欢的事