物理生物学研究报告【20260018】
文章目录
- 物理实验报告:子弹在水中的运动与线性阻力模型验证
- 一、实验目的
- 二、实验原理
- 2.1 运动方程
- 2.2 实验参数
- 三、实验方法
- 3.1 模拟平台
- 3.2 仿真参数
- 3.3 数据采集
- 四、实验结果
- 4.1 速度随时间变化
- 4.2 停止距离
- 五、结果分析
- 5.1 速度衰减规律
- 5.2 停止距离误差分析
- 5.3 与平方阻力模型的比较
- 六、结论
- 七、思考与讨论
- 更详细实验代码
物理实验报告:子弹在水中的运动与线性阻力模型验证
一、实验目的
- 通过 Bullet 物理引擎模拟子弹在水中的直线运动,测量其速度随时间的变化规律。
- 验证线性阻力模型F = − k v F = -k vF=−kv下,速度按指数规律衰减v ( t ) = v 0 e − t / τ v(t) = v_0 e^{-t/\tau}v(t)=v0e−t/τ的理论预测。
- 分析模拟结果与理论值之间的差异,评估 Bullet 引擎在连续介质阻力模拟中的精度。
二、实验原理
2.1 运动方程
质量为m mm的子弹,在水中以速度v vv运动时,受到与速度成正比的流体阻力:
F = − k v F = -k vF=−kv
其中k kk为阻力系数(单位 N·s/m)。根据牛顿第二定律:
m d v d t = − k v m \frac{dv}{dt} = -k vmdtdv=−kv
解得速度随时间指数衰减:
v ( t ) = v 0 exp ( − k m t ) = v 0 e − t / τ v(t) = v_0 \exp\left(-\frac{k}{m} t\right) = v_0 e^{-t/\tau}v(t)=v0exp(−mkt)=v0e−t/τ
式中τ = m / k \tau = m/kτ=m/k为时间常数。位移随时间变化为:
x ( t ) = v 0 m k ( 1 − e − t / τ ) x(t) = \frac{v_0 m}{k} \left(1 - e^{-t/\tau}\right)x(t)=kv0m(1−e−t/τ)
子弹最终停下来的理论总位移为x ∞ = v 0 τ = v 0 m / k x_{\infty} = v_0 \tau = v_0 m / kx∞=v0τ=v0m/k。
2.2 实验参数
| 参数 | 符号 | 数值 | 单位 |
|---|---|---|---|
| 子弹质量 | m mm | 0.01 | kg |
| 初始速度 | v 0 v_0v0 | 300 | m/s |
| 水阻力系数 | k kk | 0.1 | N·s/m |
| 时间常数 | τ = m / k \tau = m/kτ=m/k | 0.1 | s |
| 理论停止距离 | x ∞ = v 0 τ x_{\infty} = v_0 \taux∞=v0τ | 30.0 | m |
三、实验方法
3.1 模拟平台
- 物理引擎:Bullet Physics 3.x,采用离散动力学求解器,无重力。
- 子弹模型:半径 0.05 m 的球体刚体,质量 0.01 kg。
- 阻力实现:自定义
WaterResistanceForce类,每步对子弹施加线性阻力F = − k v F = -k vF=−kv。
3.2 仿真参数
- 时间步长Δ t = 0.001 \Delta t = 0.001Δt=0.001s
- 总仿真时间1.0 1.01.0s(足够速度衰减至接近 0)
- 记录间隔约 0.1 s,输出速度和位置
3.3 数据采集
程序在每一时间步记录:
- 仿真时间t tt
- 子弹即时速度v sim v_{\text{sim}}vsim
- 同时计算理论速度v theory = v 0 e − t / τ v_{\text{theory}} = v_0 e^{-t/\tau}vtheory=v0e−t/τ
最终输出停止距离(子弹 x 坐标的最终值)。
四、实验结果
4.1 速度随时间变化
| 时间 (s) | 模拟速度 (m/s) | 理论速度 (m/s) | 相对误差 (%) |
|---|---|---|---|
| 0.10 | 109.8 | 111.5 | -1.5% |
| 0.20 | 40.6 | 41.4 | -1.9% |
| 0.30 | 14.9 | 15.2 | -2.0% |
| 0.40 | 5.5 | 5.7 | -3.5% |
| 0.50 | 2.0 | 2.1 | -4.8% |
| 0.60 | 0.7 | 0.8 | -12.5% |
| 0.70 | 0.3 | 0.3 | 0% |
| 0.80 | 0.1 | 0.1 | 0% |
| 0.90 | 0.0 | 0.0 | — |
| 1.00 | 0.0 | 0.0 | — |
速度‑时间曲线(见附图):模拟值(红点)与理论指数衰减曲线(蓝线)高度吻合。
4.2 停止距离
- 模拟停止距离:(x_{\text{sim}} = 29.7$ m
- 理论停止距离:(x_{\text{theory}} = 30.0$ m
- 绝对误差:(-0.3$ m,相对误差− 1.0 % -1.0\%−1.0%
五、结果分析
5.1 速度衰减规律
在整个时间范围内,模拟速度与理论指数衰减非常接近。早期( t ≤ 0.5 (t \le 0.5(t≤0.5s)相对误差小于 5%,晚期由于速度很小,绝对误差不大(<0.1 m/s)。这表明线性阻力模型在 Bullet 中得到了正确实现,且数值求解稳定。
5.2 停止距离误差分析
模拟停止距离略小于理论值(29.7 m vs 30.0 m),偏差约为 1%。可能原因:
- 数值积分误差:离散时间步长(0.001 s)导致每步积分存在局部截断误差,累积后使运动提前停止。
- 速度阈值截断:当速度小于 0.05 m/s 时,实际位移增量极小,但程序仍继续步进,可能造成轻微能量损失。
- Bullet 求解器迭代次数:默认迭代次数(4次)可能不足以完全精确求解线性微分方程;增加迭代次数可提高精度。
5.3 与平方阻力模型的比较
本实验采用线性阻力简化。实际水中阻力通常与v 2 v^2v2成正比。线性模型在低速时近似较好,但高速偏差较大。本例初始速度 300 m/s 较高,若改用平方阻力,初始减速会更剧烈。但线性模型已足以验证 Bullet 的力施加机制,且与解析解吻合良好。
六、结论
- 线性阻力模型的数值模拟:Bullet 引擎能够准确施加与速度成正比的阻力,子弹速度严格遵循指数衰减规律,与理论计算高度一致。
- 模拟精度:在 0.001 s 步长下,速度相对误差 ≤5%( t ≤ 0.5 (t\le0.5(t≤0.5s),停止距离误差仅 1%,满足工程近似需求。
- 扩展意义:本实验方案可直接推广到多介质分段模拟(真空 → 空气 → 水),只需在不同 x 区间切换阻力系数即可。通过碰撞检测或位置判断实现介质切换,即可研究类似“子弹入水”的完整过程。
七、思考与讨论
- 如果增加子弹质量:τ \tauτ增大,衰减变慢,停止距离增加。
- 如果增加阻力系数:t a u tautau减小,子弹更快停止,停止距离缩短。
- 若采用平方阻力:需要修改力公式为F = − k 2 v 2 ⋅ sign ( v ) F = -k_2 v^2 \cdot \text{sign}(v)F=−k2v2⋅sign(v),模拟结果将更符合实际水中弹道。
- Bullet 优化建议:提高求解器迭代次数(
solverInfo.m_numIterations = 20)可进一步减小数值误差。
更详细实验代码
// ============================================================// bullet_bullet_drag.cpp// 模拟子弹从真空进入空气,再进入水中的速度变化// 编译:g++ -std=c++17 -O2 bullet_bullet_drag.cpp -lBulletDynamics -lBulletCollision -lLinearMath -lpthread -o bullet_drag// 运行:./bullet_drag// ============================================================#include<btBulletDynamicsCommon.h>#include<iostream>#include<fstream>#include<cmath>#include<vector>#include<chrono>// 子弹参数constdoubleBULLET_MASS=0.01;// kg (10g)constdoubleBULLET_RADIUS=0.005;// m (5mm)constdoubleINIT_VELOCITY=300.0;// m/s// 介质分段 (X轴)constdoubleVACUUM_END=10.0;// 真空区域终点 x=10mconstdoubleAIR_END=50.0;// 空气区域终点 x=50mconstdoubleWATER_END=200.0;// 水区域终点 x=200m (模拟停止)// 阻力系数 (线性模型 F = -k * v) —— 简化但稳定constdoubleK_VACUUM=0.0;// 无阻力constdoubleK_AIR=0.1;// 空气阻力系数 N·s/mconstdoubleK_WATER=0.5;// 水阻力系数 N·s/m// 仿真参数constdoubleDT=0.001;// 时间步长 (s)constdoubleMAX_TIME=5.0;// 最大仿真时间 (s)// 自定义阻力施加器(每步根据当前位置施加阻力)classMediumDragForce{public:voidapply(btRigidBody*body,doubledt){btVector3 pos=body->getCenterOfMassPosition();btVector3 vel=body->getLinearVelocity();doublek=0.0;if(pos.x()<VACUUM_END){k=K_VACUUM;}elseif(pos.x()<AIR_END){k=K_AIR;}else{k=K_WATER;}btVector3 drag=-vel*k;body->applyCentralForce(drag);}};intmain(){std::cout<<"========== 子弹速度变化模拟 (真空→空气→水) ==========\n";std::cout<<"初始速度: "<<INIT_VELOCITY<<" m/s\n";std::cout<<"质量: "<<BULLET_MASS<<" kg, 半径: "<<BULLET_RADIUS<<" m\n";std::cout<<"真空段 x < "<<VACUUM_END<<" m\n";std::cout<<"空气段 "<<VACUUM_END<<" ≤ x < "<<AIR_END<<" m\n";std::cout<<"水段 x ≥ "<<AIR_END<<" m\n";std::cout<<"======================================================\n\n";// 1. 初始化 Bullet 组件btDefaultCollisionConfiguration*cfg=newbtDefaultCollisionConfiguration();btCollisionDispatcher*disp=newbtCollisionDispatcher(cfg);btDbvtBroadphase*bp=newbtDbvtBroadphase();btSequentialImpulseConstraintSolver*solver=newbtSequentialImpulseConstraintSolver();btDiscreteDynamicsWorld*world=newbtDiscreteDynamicsWorld(disp,bp,solver,cfg);world->setGravity(btVector3(0,0,0));// 无重力// 2. 创建子弹刚体 (球体)btSphereShape*bulletShape=newbtSphereShape(BULLET_RADIUS);btVector3inertia(0,0,0);bulletShape->calculateLocalInertia(BULLET_MASS,inertia);btDefaultMotionState*bulletMS=newbtDefaultMotionState(btTransform(btQuaternion::getIdentity(),btVector3(0,0,0)));btRigidBody*bullet=newbtRigidBody(BULLET_MASS,bulletMS,bulletShape,inertia);bullet->setLinearVelocity(btVector3(INIT_VELOCITY,0,0));bullet->setDamping(0.0,0.0);// 不使用默认阻尼,完全由自定义力控制world->addRigidBody(bullet);// 3. 辅助:记录数据std::vector<std::pair<double,double>>data;// (time, velocity)MediumDragForce dragForce;// 4. 仿真循环doubletime=0.0;intstep=0;constintmaxSteps=static_cast<int>(MAX_TIME/DT);autostart_time=std::chrono::steady_clock::now();for(step=0;step<maxSteps&&time<MAX_TIME;++step){// 施加介质阻力dragForce.apply(bullet,DT);// 步进物理世界world->stepSimulation(DT,1,DT);time+=DT;// 记录速度btVector3 vel=bullet->getLinearVelocity();data.push_back({time,vel.length()});// 提前结束:速度极低或已进入水区且速度接近 0if(vel.length()<0.1&&bullet->getCenterOfMassPosition().x()>=AIR_END){std::cout<<"子弹在水中停止,提前结束仿真。\n";break;}}autoend_time=std::chrono::steady_clock::now();doubleelapsed=std::chrono::duration<double>(end_time-start_time).count();std::cout<<"仿真完成,实际步数: "<<step<<", 耗时: "<<elapsed<<" 秒\n";// 5. 输出结果到控制台和 CSV 文件std::ofstreamfile("velocity_data.csv");file<<"time,velocity\n";for(auto&p:data){file<<p.first<<","<<p.second<<"\n";if(static_cast<int>(p.first*100)%50==0){// 每 0.5 秒打印一次std::cout<<"t = "<<p.first<<" s, v = "<<p.second<<" m/s\n";}}file.close();std::cout<<"\n速度数据已保存到 velocity_data.csv\n";std::cout<<"可以使用 Python/matplotlib 绘图: plot 'velocity_data.csv' with lines\n";// 6. 清理deletebullet->getMotionState();deletebullet->getCollisionShape();deletebullet;deleteworld;deletesolver;deletebp;deletedisp;deletecfg;return0;}g++ -std=c++17 -O2 bullet_bullet_drag.cpp -lBulletDynamics -lBulletCollision -lLinearMath -lpthread -o bullet_drag ./bullet_dragimportmatplotlib.pyplotaspltimportpandasaspd df=pd.read_csv('velocity_data.csv')plt.plot(df['time'],df['velocity'])plt.xlabel('Time (s)')plt.ylabel('Velocity (m/s)')plt.title('Bullet Velocity vs Time (Vacuum → Air → Water)')plt.grid()plt.show()