通过 3D 动画直观演示球形瓶内可滑动半圆机构在倾倒时防粘盖、回正时刮膏体的运动学过程

通过 3D 动画直观演示球形瓶内可滑动半圆机构在倾倒时防粘盖、回正时刮膏体的运动学过程。

```python

-*- coding: utf-8 -*-

"""

球形瓶防粘盖滑动半圆机构 · 数字化实施例(仿真验证程序)

============================================================

本程序对应专利交底书附图 3「运动时序仿真图」,用于验证:

(1) 瓶体倾倒时,半圆滑动件在重力分量作用下由夹层 13 滑出,

于瓶口内侧形成凹面朝上的屏障,阻断膏体 30 与瓶盖 16 接触;

(2) 瓶体回正并开盖时,半圆滑动件滑回夹层 13,

其赤道刮缘 22 将黏附于凹面的膏体 30 刮落回瓶腔。

部件编号与说明书一致:

10 球形瓶体 11 外壁 12 上半球内壁 13 环形夹层腔

14 下腔 15 瓶口 16 瓶盖 20 半圆滑动件

21 半圆球面主体 22 赤道刮缘 30 膏体

运行环境:Python 3.9+ / numpy / matplotlib

输出:sphere_bottle_slider.gif

"""

import numpy as np

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt

from matplotlib.animation import FuncAnimation, PillowWriter

from mpl_toolkits.mplot3d import Axes3D # noqa: F401

中文字体(按系统已有字体回退)

plt.rcParams"font.sans-serif" = ["Noto Sans CJK SC", "SimHei",

"Microsoft YaHei", "PingFang SC", "DejaVu Sans"]

plt.rcParams"axes.unicode_minus" = False

======================================================================

1. 实施例几何参数(单位:归一化无量纲量,实物按比例放大)

======================================================================

R_OUT = 1.00 # 11 外壁半径

R_CAV = 0.96 # 14 内腔半径(壁厚 0.04)

R_SLD = 0.78 # 21 半圆球面主体半径

Z_RET = 0.16 # 20 收纳位(位于 13 夹层腔内)

Z_DEP = -0.50 # 20 展开位(位于 14 内腔下部)

CAP_R = 0.30 # 16 瓶盖半径

======================================================================

2. 运动时序(对应附图 3,横轴为时间 t / s)

======================================================================

FPS, T_END = 24, 9.0

def smoothstep(x):

"""S 型速度曲线,避免启停冲击"""

x = np.clip(x, 0.0, 1.0)

return x * x * (3.0 - 2.0 * x)

def timeline(t):

"""返回 (瓶体倾角 theta, 滑片轴向位置 z_off, 开盖系数 cap_open)"""

if t < 1.2: # ① 正立,滑片收纳

return 0.0, Z_RET, 0.0

if t < 2.8: # ② 倾倒,滑片滑出

k = smoothstep((t - 1.2) / 1.6)

theta = np.pi * k

zo = Z_RET + (Z_DEP - Z_RET) * smoothstep((t - 1.6) / 1.0)

return theta, zo, 0.0

if t < 5.2: # ③ 倒置保持,膏体被托住

return np.pi, Z_DEP, 0.0

if t < 7.0: # ④ 回正

k = smoothstep((t - 5.2) / 1.8)

return np.pi * (1.0 - k), Z_DEP, 0.0

if t < 8.6: # ⑤ 开盖 + 滑片回收刮料

k = smoothstep((t - 7.0) / 1.6)

return 0.0, Z_DEP + (Z_RET - Z_DEP) * k, smoothstep((t - 7.0) / 0.5)

return 0.0, Z_RET, 1.0 # ⑥ 复位

======================================================================

3. 膏体 30 的离散粒子动力学(简化的黏塑性流体模型)

======================================================================

class PasteSim:

"""以 N 个示踪粒子表征膏体,含重力、阻尼、器壁约束与滑片约束"""

def init(self, n=120, seed=20240607):

rng = np.random.default_rng(seed)

d = rng.normal(size=(n, 3))

d /= np.linalg.norm(d, axis=1, keepdims=True)

d *= 0.86 * np.cbrt(rng.random((n, 1)))

d:, 2 = -np.abs(d:, 2) - 0.05 # 初始堆积于瓶底

self.p = d

self.v = np.zeros((n, 3))

self._zprev = Z_RET

def advance(self, theta, z_off, dt=1.0 / 48.0, sub=2):

"""瓶坐标系下的时间推进(世界重力随瓶体旋转投影到瓶坐标)"""

R = rot_x(theta)

g = R.T @ np.array(0.0, 0.0, -1.0) # 瓶坐标下的重力单位向量

for _ in range(sub):

self.v += g * dt * 10.0 # 重力项

self.v *= 0.90 # 内摩擦阻尼

self.p += self.v * dt

self._wall() # 约束 1:瓶腔 14

if z_off < 0.10: # 滑片处于展开状态

self._slider(z_off) # 约束 2:滑片凹面 21

self._zprev = z_off

return self.p

def _wall(self):

"""器壁 11/14 的硬约束:粒子投影回球腔内并清除法向穿透速度"""

p, v = self.p, self.v

r = np.linalg.norm(p, axis=1)

over = r > R_CAV

if np.any(over):

n = pover / rover, None

pover = n * R_CAV

vn = np.einsum("ij,ij->i", vover, n)

vover -= np.maximum(vn, 0.0):, None * n

def _slider(self, z_off):

"""滑片 21 的凹面约束 + 回收时的刮落效应"""

p, v = self.p, self.v

rho = np.hypot(p:, 0, p:, 1)

mask = rho < R_SLD - 1e-9

zs = np.full(len(p), np.inf)

zsmask = z_off + np.sqrt(np.maximum(R_SLD ** 2 - rhomask ** 2, 0.0))

hit = p:, 2 < zs

if not np.any(hit):

return

phit, 2 = zshit # 投影到凹面

vhit, 2 = np.maximum(vhit, 2, 0.0) # 清除法向穿透速度

------ 刮料机理:滑片回程(z_off 增大)时,赤道刮缘 22 将膏体向轴心刮落

dz = z_off - self._zprev

if dz > 1e-6:

push = min(dz * 60.0, 2.5)

rh = np.hypot(phit, 0, phit, 1)

rhrh \< 1e-9 = 1.0

vhit, 0 -= phit, 0 / rh * push

vhit, 1 -= phit, 1 / rh * push

======================================================================

4. 三维几何与渲染

======================================================================

def rot_x(a):

c, s = np.cos(a), np.sin(a)

return np.array(\[1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c])

def rot_apply(X, Y, Z, R):

"""将瓶坐标系网格整体旋转到世界坐标"""

shp = X.shape

P = np.stack(np.asarray(X).ravel(), np.asarray(Y).ravel(), np.asarray(Z).ravel())

Q = R @ P

return Q0.reshape(shp), Q1.reshape(shp), Q2.reshape(shp)

def sph(r, n=36, v0=0.0, v1=np.pi):

u = np.linspace(0.0, 2.0 * np.pi, n)

v = np.linspace(v0, v1, max(n // 2, 8))

uu, vv = np.meshgrid(u, v)

return r * np.sin(vv) * np.cos(uu), r * np.sin(vv) * np.sin(uu), r * np.cos(vv)

def hemi(r, zoff, n=40, m=12):

u = np.linspace(0.0, 2.0 * np.pi, n)

v = np.linspace(0.0, np.pi / 2.0, m)

uu, vv = np.meshgrid(u, v)

return r * np.sin(vv) * np.cos(uu), r * np.sin(vv) * np.sin(uu), r * np.cos(vv) + zoff

def stage_text(t):

if t < 1.2: return "① 正立 · 半圆滑动件 20 收纳于夹层 13"

if t < 2.8: return "② 倾倒 · 20 受重力分量滑出,凹面朝上形成屏障"

if t < 5.2: return "③ 倒置 · 膏体 30 滞留于凹面,不接触瓶盖 16"

if t < 7.0: return "④ 回正 · 膏体随 20 回落瓶腔"

if t < 8.6: return "⑤ 开盖 · 20 回程,刮缘 22 将膏体刮回瓶内"

return "⑥ 复位 · 半圆滑动件重新收纳于夹层,等待下次倾倒"

def render(ax, theta, z_off, sim, cap_open, t):

ax.clear()

R = rot_x(theta)

ax.set_xlim(-1.2, 1.2); ax.set_ylim(-1.2, 1.2); ax.set_zlim(-1.2, 1.2)

ax.set_box_aspect((1, 1, 1))

ax.view_init(elev=16, azim=-60)

ax.set_axis_off()

------ 11 外壁(透明球壳)

X, Y, Z = sph(R_OUT, 44)

ax.plot_surface(*rot_apply(X, Y, Z, R), color="#a8c8dd",

alpha=0.055, linewidth=0, shade=False, antialiased=True)

------ 14 内腔面

X, Y, Z = sph(R_CAV, 36, np.pi / 2, np.pi)

ax.plot_surface(*rot_apply(X, Y, Z, R), color="#9fbfd0",

alpha=0.10, linewidth=0, shade=False)

X, Y, Z = sph(R_CAV, 36, 0, np.pi / 2)

ax.plot_surface(*rot_apply(X, Y, Z, R), color="#9fbfd0",

alpha=0.045, linewidth=0, shade=False)

------ 13 环形夹层腔示意(虚线圈)

tt = np.linspace(0, 2 * np.pi, 64)

for dz in (0.0, 0.12):

ex = R_SLD * 1.03 * np.cos(tt)

ey = R_SLD * 1.03 * np.sin(tt)

ez = np.full_like(tt, Z_RET - dz)

ax.plot(*rot_apply(ex, ey, ez, R), color="#6f93aa",

lw=1.0, alpha=0.65, ls="--")

------ 20 半圆滑动件(21 主体)

X, Y, Z = hemi(R_SLD, z_off, 44, 14)

ax.plot_surface(*rot_apply(X, Y, Z, R), color="#e07b39",

alpha=0.52, linewidth=0, shade=True, antialiased=True)

------ 22 赤道刮缘

ex = R_SLD * np.cos(tt); ey = R_SLD * np.sin(tt)

ez = np.full_like(tt, z_off)

ax.plot(*rot_apply(ex, ey, ez, R), color="#8a3b0a", lw=2.4)

------ 30 膏体粒子

P = (R @ sim.p.T).T

ax.scatter(P:, 0, P:, 1, P:, 2, s=17, c="#f2e3b3",

edgecolors="#b89b45", linewidths=0.4, depthshade=True)

------ 16 瓶盖

if cap_open < 0.5:

X, Y, Z = sph(CAP_R, 26, 0, np.pi / 2)

ax.plot_surface(*rot_apply(X, Y, Z + 0.98, R), color="#4a4a4a",

alpha=0.55, linewidth=0, shade=False)

ax.text2D(0.02, 0.95, stage_text(t), transform=ax.transAxes, fontsize=10.5)

ax.text2D(0.02, 0.05, "theta=%4.1f deg z_off=%+.2f" % (np.degrees(theta), z_off),

transform=ax.transAxes, fontsize=9, color="#555555")

======================================================================

5. 主程序

======================================================================

def main(save="sphere_bottle_slider.gif"):

sim = PasteSim()

fig = plt.figure(figsize=(7.2, 7.2), dpi=100)

ax = fig.add_subplot(111, projection="3d")

frames = np.arange(int(FPS * T_END))

def update(fi):

t = fi / FPS

theta, z_off, cap = timeline(t)

sim.advance(theta, z_off)

render(ax, theta, z_off, sim, cap, t)

return (ax,)

anim = FuncAnimation(fig, update, frames=frames, interval=1000.0 / FPS, blit=False)

anim.save(save, writer=PillowWriter(fps=FPS))

print("已输出动画:", save)

return anim

if name == "main":

main()

```

结构演示与动态时序解析

· 阶段① 正立收纳:瓶体直立,半圆滑动件藏在环形夹层腔里,膏体安静地堆积在瓶底。

· 阶段② 倾倒滑出:瓶身倾斜时,重力分量把半圆滑动件从夹层拉出来,凹面朝上形成物理屏障。

· 阶段③ 倒置阻挡:膏体粒子被滑片凹面托住,不会流到瓶盖位置。

· 阶段④ 回正回落:瓶体回正后,膏体随滑片一起落回瓶腔。

· 阶段⑤ 刮料回收:打开瓶盖,滑片沿轴向滑回夹层,赤道边缘把黏在凹面上的膏体刮回瓶内。

· 阶段⑥ 复位待用:滑动件重新收纳,等待下次倾倒。


禁止一切转载商用。

相关推荐
quantdash_cc1 小时前
ETF 行情数据清洗实战:字段语义拆解、标的池整理与五个校验清单
python·数据分析·pandas·量化交易·股票数据·quantdash
小熊恋旧1 小时前
Python 面向对象编程:方法、属性、继承与多态详解
开发语言·python
威哥爱编程2 小时前
Python 智能体开发实战 05:一问教材内容它就编?先把答案从你自己的资料里检索出来
python·agent
java_prinln2 小时前
RAG 面试题介绍
python·agent
威哥爱编程2 小时前
Python 智能体开发实战 06:一个函数里塞了八百行?用状态机把批改流程拆成能回退的步骤
python·agent
Patrick在香港3 小时前
一个斜杠,把毫秒变成十几秒
android·数据库·python·性能优化·正则表达式·回溯·redos
2601_967980113 小时前
Python 初学者必备的常用内置函数
python·编程语言·内置函数·初学者·常用函数
OnlineProxy3 小时前
移动应用数据爬取:通过模拟器与流量嗅探对 API 进行逆向工程
爬虫·python·httpx
染指11103 小时前
162.Agent-LangGraph-检查点的作用
人工智能·python·langchain·agents