You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

二维自驱动杆群体行为可视化及交互旋转修正建议

自驱动杆旋转效果修正方案

我们的模型是由奇数个硬球串联而成的自驱动杆,在二维周期性边界介质中以固定速度运动,具备x/y平动扩散和转动扩散三个独立扩散常数,初始取向随机。当不同杆的球体进入相互作用半径时,会产生Lennard-Jones势衍生的作用力,该力通过力矩驱动杆缓慢旋转,最终使所有杆取向一致。

现有代码运行后,杆的旋转效果不符合物理预期,以下是具体修正点:


1. 核心问题:错误的角度更新逻辑

当前update_angles直接将杆的角度设置为杆中心合力的方向,完全违背了“扭矩驱动旋转”的物理机制——杆的旋转由每个球体受到的力对杆中心的力矩总和决定,而非直接朝向合力方向。

2. 球体位置生成错误

当前get_sphere_positions会重复添加杆的中心球体(i=0时,正负方向的位置均为杆中心),导致杆的球体数量变为偶数(比如设置9个球的杆实际生成10个位置)。

修正代码:

def get_sphere_positions(start_pos, angle, num_spheres):
    sphere_positions = [tuple(start_pos)]  # 单独添加中心球
    half_length = (num_spheres - 1) // 2
    for i in range(1, half_length + 1):
        # 向杆的负方向生成球体
        x = start_pos[0] - 2 * i * SPHERE_RADIUS * np.cos(angle)
        y = start_pos[1] - 2 * i * SPHERE_RADIUS * np.sin(angle)
        sphere_positions.append((x, y))
        # 向杆的正方向生成球体
        x1 = start_pos[0] + 2 * i * SPHERE_RADIUS * np.cos(angle)
        y1 = start_pos[1] + 2 * i * SPHERE_RADIUS * np.sin(angle)
        sphere_positions.append((x1, y1))
    return sphere_positions

3. 缺失扩散项(平动+转动)

模型明确要求具备x/y平动扩散和转动扩散,但现有代码完全未实现,导致运动过于规则,不符合介质中自驱动粒子的布朗运动特性。

修正代码:

# 添加到参数定义区
D_TRANS = 0.1  # 平动扩散常数
D_ROT = 0.05   # 转动扩散常数
DT = 0.1       # 时间步长

def update_positions(positions, angles, velocity, dt):
    theta = angles
    dx = velocity * np.cos(theta) * dt
    dy = velocity * np.sin(theta) * dt
    # 添加平动扩散高斯噪声
    dx += np.sqrt(2 * D_TRANS * dt) * np.random.randn(len(positions))
    dy += np.sqrt(2 * D_TRANS * dt) * np.random.randn(len(positions))
    positions[:, 0] += dx
    positions[:, 1] += dy

    # 周期性边界处理
    positions[:, 0] = positions[:, 0] % WIDTH
    positions[:, 1] = positions[:, 1] % HEIGHT
    return positions

4. 力矩计算与角度更新的正确实现

重写力和扭矩计算逻辑,基于力矩驱动角度变化:

def compute_forces_and_torques(positions, angles):
    num_rods = len(positions)
    forces = np.zeros_like(positions)  # 杆中心的合力
    torques = np.zeros(num_rods)       # 杆受到的总扭矩

    for i in range(num_rods):
        sphere_pos_i = np.array(get_sphere_positions(positions[i], angles[i], NUM_SPHERES_PER_ROD))
        for j in range(i + 1, num_rods):
            sphere_pos_j = np.array(get_sphere_positions(positions[j], angles[j], NUM_SPHERES_PER_ROD))
            for idx_i, pos_i in enumerate(sphere_pos_i):
                for idx_j, pos_j in enumerate(sphere_pos_j):
                    # 周期性边界下的最短距离向量
                    dist_vec = pos_j - pos_i
                    dist_vec[0] = np.mod(dist_vec[0] + WIDTH/2, WIDTH) - WIDTH/2
                    dist_vec[1] = np.mod(dist_vec[1] + HEIGHT/2, HEIGHT) - HEIGHT/2
                    dist = np.linalg.norm(dist_vec)
                    if dist < NEIGHBOR_DISTANCE_THRESHOLD and dist > 1e-6:
                        # 计算Lennard-Jones力
                        r6 = (SIGMA / dist) ** 6
                        r12 = r6 ** 2
                        force_mag = 24 * EPSILON * (2 * r12 - r6) / dist
                        force_vec = force_mag * (dist_vec / dist)
                        # 作用与反作用力
                        forces[i] += force_vec
                        forces[j] -= force_vec
                        # 计算对杆中心的力矩(叉乘)
                        r_i = pos_i - positions[i]
                        torques[i] += np.cross(r_i, force_vec)
                        r_j = pos_j - positions[j]
                        torques[j] += np.cross(r_j, -force_vec)

    return forces, torques

def update_angles(angles, torques, dt):
    # 简化假设:转动惯量为1,角加速度=扭矩/转动惯量
    angular_acc = torques
    angles += angular_acc * dt
    # 添加转动扩散噪声
    angles += np.sqrt(2 * D_ROT * dt) * np.random.randn(len(angles))
    # 角度归一化到[0, 2π)
    angles = np.mod(angles, 2 * np.pi)
    return angles

5. 动画循环同步调整

修改animate函数适配新逻辑:

def animate(frame):
    global positions, angles
    positions = update_positions(positions, angles, VELOCITY, DT)
    forces, torques = compute_forces_and_torques(positions, angles)
    angles = update_angles(angles, torques, DT)

    ax.clear()
    for pos, angle in zip(positions, angles):
        draw_rod(ax, pos, angle, NUM_SPHERES_PER_ROD)

    ax.set_xlim(0, WIDTH)
    ax.set_ylim(0, HEIGHT)
    ax.set_aspect('equal')

内容的提问来源于stack exchange,提问作者Anonymous One

火山引擎 最新活动