大家可以积极回复哦,前10名给科创币
一、前言
前段时间(大概是4月)发射了我的第一枚飞控水火箭H•3,以下是发射视频
1982_raw.mp4 点击下载
从视频中可以看出飞控的工作状况还是不错的,完成了开伞时序。但是不妨可以发现火箭基本的动力阶段出现了问题:首先飞行高度较低,仅有7~11米左右,其次火箭在落地前一刻仍在喷射。我在发射之后略显沉闷,想解决这个问题并找到原因,于是脑中即有两个方案,一是通过Ansys,二是计算证明。显然我选择后者,意在通deepseek协助完成(当然不要担心,我发帖就是因为这是可以采用的)
开始之前,先了解一下我的火箭动力段组成
4x560ml健力宝碳酸饮料瓶
灯具中的牙管,内径8mm
好,以下进入正文部分
二、计算示例与公式(重要)
先准备好参数,如图
依次是水,气,瓶内压力,大气压,水密度,比热比,牙管直径,干重,水重,发射时质量
在计算冲量$l$之前要先计算几个式子,以方便计算
实时压力P(V)注:在下文中V都为已经喷出去的水体积
$P(V) = P_0 \left( \frac{V_{g0}}{V_{g0} + V} \right)^\gamma \qquad (\gamma = 1.4)$
此为压力的变量表达,下面为喷水速度V e(V)计算方法
$v_e(V) = \sqrt{ \frac{2(P(V) - P_{\text{atm}})}{\rho} }$
此外还要计算一下推力F(V),这里使用了瓶口(注意是21.5mm,不是牙管的内径)
先求喷嘴面积
$A_n = \frac{\pi d^2}{4}$
接着求推力(理想完全膨胀)
$F(V) = 2 \cdot A_n \cdot (P(V) - P_{\text{atm}})$
这个要注明一下实际流量会损失,牙管就是干了这件事,理想值取1即可。
算到这里,就可以引出冲量公式
$I = \rho \int_0^{V_{\text{end}}} v_e(V)\ dV = \rho \int_0^{V_{\text{end}}} \sqrt{ \frac{2(P(V)-P_{\text{atm}})}{\rho} }\ dV$
$V_{\text{end}}$是水喷完时的体积,或者瓶内压力降到大气压时的体积
我当时看到这里有点不解,大家也不要慌,看一下怎么来的就可以
众所周知,推力公式为$F = \dot{m} \cdot v$,总冲量$I = \int F \, dt$,因此要先算$F$,由于喷水的质量流率是$\dot{m} = \rho A_n v$,所以
$F = \rho A_n v^2$
时间dt内喷出水的体积$dV = A_n v\, dt$,化简后是$dt = \frac{dV}{A_n v}$,把F和时间相乘
$F\, dt = (\rho A_n v^2) \cdot \frac{dV}{A_n v} = \rho v\, dV$
大家可以发现An最终消掉了,也就是说这个喉部面积不参与计算,再将$I$冲量表示为
$I = \int_{0}^{V_{\text{end}}} \rho v(V) \, dV$
其中$V(v)$是当已喷出水体积为V时的喷射速度,由伯努利方程得出
$v(V) = \sqrt{\frac{2 (P(V) - P_{\text{atm}})}{\rho}} 上文有$
代入
$I = \int_0^{V_{\text{end}}} \sqrt{2\rho \bigl[P(V) - P_{\text{atm}}\bigr]} \ dV$
下一步要进行积分,选择方法是梯形进行10等分(这个大家不一定要掌握,完全扔给AI那可,以下是我得到数据可供参考
$F(V)$为
$f(V) = \sqrt{2\rho \bigl[P(V) - P_{\text{atm}}\bigr]} = \sqrt{2000 \times \bigl(P(V) - 101\,325\bigr)}$
$P(V)$为
$P(V) = P_0 \left( \frac{V_{g0}}{V_{g0} + V} \right)^{1.4} = 506\,625 \times \left( \frac{0.0015}{0.0015 + V} \right)^{1.4}$
积分区间:$V 从 0 到 V_{\text{end}} = V_{w0} = 0.00074\ \text{m}^3$
用梯形法则
$I \approx \frac{\Delta V}{2} \bigl[ f_0 + 2f_1 + 2f_2 + \dots + 2f_9 + f_{10} \bigr]$
注:在这里大家关注一下288900这个数值,证明了当水全部喷完时,气室内仍有1.8 bar左右的表压,这或许就是为什么落地之前仍在喷水气
梯型合成
$\begin{aligned} \sum &= f_0 + f_{10} + 2(f_1 + f_2 + \dots + f_9) \\ &= 28\,471 + 19\,369 + 2 \times (27\,286 + 26\,183 + 25\,151 + 24\,181 + 23\,271 + 22\,409 + 21\,590 + 20\,812 + 20\,074) \\ &= 47\,840 + 2 \times 210\,957 \\ &= 47\,840 + 421\,914 = 469\,754 \end{aligned}$
$I_{\text{水}} = \frac{\Delta V}{2} \times \sum = \frac{0.000074}{2} \times 469\,754 = 0.000037 \times 469\,754 \approx 17.38\ \text{N·s}$
可得水推力阶段总冲量:约 17.4 牛·秒。
如下估算最大速度与飞行高度(无阻力)
$Delta v = \frac{I_{\text{水}}}{\bar{m}} \approx \frac{17.4}{0.72} \approx 24.2\ \text{m/s}$
$H = \frac{(\Delta v)^2}{2g} = \frac{24.2^2}{2 \times 9.8} = \frac{585.6}{19.6} \approx 29.9\ \text{m}$
实际考虑飞行高度是无阻力的40% ~70%,即12~20米左右。
在这里可以用软件计算,GitHub上的原链为XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
我对这个软件进行了汉化并计算了上述冲量
可以看到冲量很接近,感兴趣可以导入我的.eng查看
在这里考虑一下牙管的飞行高度损失
牙管($A_{\text{牙管}} = \frac{\pi \times (0.008)^2}{4} \approx 5.03 \times 10^{-5}\ \text{m}^2$)和最终喷嘴的截面积($A_{\text{喷嘴}} = \frac{\pi \times (0.0215)^2}{4} \approx 3.63 \times 10^{-4}\ \text{m}^2$)只占13.9%,根据流量守恒速度提高约7倍,而反之会形成局部阻力。因此在这里我使用流量系数估算(之前没了解过这个物理概念),$C_d$取0.5-0.7左右,可得总冲10.4N· S左右,高度约为3-7米左右,反映了飞行现象。
在此我们计算一下$C_d$,简而言之,该量是实际流量与理想无摩擦的比值,理想情况下,喷嘴流量
$Q_{\text{理论} } = A \sqrt{\frac{2\Delta P}{\rho}}$
三、设计建议
经过上述计算,我们可以获得两个问题
1.水喷完气仍在大量,未充分使用
2.牙管造成局部阻力,大量降低飞行质量
针对水气分布可以用这个公式反推$V_w0$
此处可以理解为当水喷完后,水火箭内部气压等于外部大气压(注:此公式贴图可能大小实兀,见谅)
而牙管与喷口之间的最佳关系为
$\boxed{d_{\text{喷}} = d_{\text{管}}}$
或者可以选择d喷小于d管(总之要小于连接管内径),可以有效减小压力损失,这次火箭便是一个大忌。
四、建模sldprt和代码文件
分享几个部件的Solidworks建模,图片按顺序为示意图
飞控简述
主控 STM32F103C8T6
陀螺仪 MPU6050
舵机 SG90
TF卡槽
电源 VCC( 4.2V和 3.3V)
这款飞控严格意义上仅完成了基本的开伞时序,而没有完成Fatfs MPU6050数据采集,如果感兴趣,可以看一下立创EDA工程和原理图
分享一个SD卡的相关资料,或许对大家写Fatfs有帮助
以下这是Keil5的工程,.c和.h文件和江科大一样,主文件main.c的触发方式是两个轴的角度值达到控制舵机旋转,打开伞舱,以下是文件。
此代码引角
MPU6050
VCC-3.3V
GND-总地
SCL-PB6
SDA-PB7
AD0-GND
OLED
VCC-4.2V
GND-总地
SCK-PB1
SDA-PB0
SG90
VCC(红)-4.2V
GND(棕)-GND
信号线(黄)-PC14
main.c代码
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
#include "Servo.h"
#include <math.h>
extern volatile uint32_t millis;
int main(void)
{
SysTick_Config(SystemCoreClock / 1000);
int16_t AX, AY, AZ;
float RollAngle;
float YawAngle;
const uint8_t SERVO_ACTIVE_ANGLE = 90;
const uint8_t SERVO_NORMAL_ANGLE = 0;
uint8_t servoTriggered = 0;
uint32_t triggerMillis = 0;
uint8_t waitingReturn = 0;
OLED_Init();
MPU6050_Init();
Servo_Init();
OLED_ShowString(1, 1, "Yaw:");
OLED_ShowString(2, 1, "Roll: ");
OLED_ShowString(3, 1, "Servo:");
OLED_ShowString(3, 7, "Normal ");
while (1)
{
MPU6050_GetAceel(&AX, &AY, &AZ);
YawAngle = atan2f(AY,AX) * 180.0f / 3.1415926535f;
RollAngle = atan2f(AZ,AX) * 180.0f / 3.1415926535f;
int16_t displayRoll = (int16_t)(RollAngle + 0.5f * (RollAngle >= 0 ? 1 : -1));
int16_t displayYaw = (int16_t)(YawAngle + 0.5f * (YawAngle >= 0 ? 1 : -1));
OLED_ShowNum(1, 7, displayYaw, 4);
OLED_ShowNum(2, 7, displayRoll, 4);
OLED_ShowString(1, 11, "deg ");
OLED_ShowString(2, 11, "deg ");
if (!waitingReturn)
{
if (fabsf(RollAngle) > 30.0f || fabsf(YawAngle) > 30.0f)
{
if (!servoTriggered)
{
Servo_SetAngle(SERVO_ACTIVE_ANGLE);
servoTriggered = 1;
triggerMillis = millis;
waitingReturn = 1;
OLED_ShowString(3, 7, "Active ");
}
}
else
{
Servo_SetAngle(SERVO_NORMAL_ANGLE); // ??????????0°
OLED_ShowString(3, 7, "Normal ");
}
}
else
{
if (millis - triggerMillis >= 10000)
{
Servo_SetAngle(SERVO_NORMAL_ANGLE);
OLED_ShowString(3, 7, "Normal ");
waitingReturn = 0;
servoTriggered = 0;
}
}
Delay_ms(20);
}
}在main.c中主要为大家提供一下不同MPU6050放置计算代码
如果X轴向上,即
便与文中相同
如果Z轴垂直向上,即
此处代码要改为
YawAngle = atan2f(AX, AZ) * 180.0f / 3.1415926535f;
RollAngle = atan2f(AY, AZ) * 180.0f / 3.1415926535f;而Y轴向上,要改为
YawAngle = atan2f(AX, AY) * 180.0f / 3.1415926535f;
RollAngle = atan2f(AZ, AY) * 180.0f / 3.1415926535f;效果如视频
VID_20260207_203553.mp4 点击下载
希望有帮助
五、结语
差不多写完了这篇帖子,也想借机向大家展示在如今,AI可以帮助我们完成许多繁琐的计算,大为改善效率。在次引用一句我的一位朋友的话:“判断是数据说真话的前提。”希望大家在未来不要凭手感,要相信数据和计算。也希望我的建议可以帮助大家。That's all,thank you!
H·3(2025.1~2026.5)
End
在这里补充一下飞行高度估值
速度增量
$\Delta v = \frac{I_{\text{水}}}{\bar{m}}$
而竖直上抛最大高度(无空气阻力)
$H = \frac{(\Delta v)^2}{2g}$
如果大家这里想加阻力可以乘阻力系数(不同的头锥不同,0.5~0.8左右),这样结果就与真实相近些。
想问大家玩水火箭一个问题:有没有网店可以定制塑料瓶的,5个左右可以做的?劳妨推荐一下,谢谢。
前几天看到了,不过这几天很忙没时间回,今天有点时间我仔细看了一下。
首先我觉得楼主在推力与总冲的计算上是没有问题的,用了一些近似,认为喷嘴面积远小于内部截面积。如果要考虑喷嘴面积,那么火箭加速度会带来一阶修正,而流体力学会带来二阶修正。我对水火箭不是很了解,不过如果补上和喷嘴面积有关的量应该会好一些,毕竟这对计算机来说计算量不算什么,当然,这不一定能减小误差,因为更复杂的流体力学效应与火箭的偏转影响会更大。
其次是通过总冲判断飞行高度和初速度的,这里我觉得问题可能挺大,只能用作估算。首先由于内部气体是绝热膨胀,火箭质量变化不是线性的,此时用平均质量不是很合理,另外由实验视频可以看出,喷水时间和飞行时间是同一数量级,用瞬间工作的模型来估计是不合理的。论坛常见的固体燃料火箭工作时间相比飞行时间小了至少1-2个数量级,但是水火箭不同,这一点倒是非常有必要,能大大增加精度。
我也用了ai辅助,让GPT帮我推导,我自己检查了一遍。
以下可能有一些量的字母或角标与一楼不同,V为总体积,Vg为气体体积,V0为初始气体体积,P0为大气压,P为初始气压。(emm……的确楼主的标注比较正常……但是我是已经复制粘贴过来并排版了,所以将就看看?)
如果不考虑一阶修正(推力与总冲与一楼一致,略去):
$P_g(t)=P\left(\frac{V_0}{V_g(t)}\right)^\gamma$
$M(t)=m+\rho\left[V-V_g(t)\right]$
$\frac{dV_g}{dt} = A\sqrt{ \frac{2\left[p_g(t)-P_0\right]}{\rho} }$
$\frac{dv}{dt} = \frac{ 2A\left[p_g(t)-P_0\right] } { m+\rho\left[V-V_g(t)\right] } -g$
程序用龙格库塔法求解这个微分方程组即可获得更加精确的解,当然还需要结合喷水结束的条件,这个在后续写。
如果不忽略喷嘴面积,简略的推导如下:
$p_g(t) = P\left(\frac{V_0}{V_g(t)}\right)^\gamma$
$M(t) = m+\rho\left[V-V_g(t)\right]$
$L(t) = \frac{V-V_g(t)}{S}$
$u(t) = \sqrt{ \frac{ 2\left[p_g(t)-P_0\right] } { \rho\left[ 1-\left(\frac{A}{S}\right)^2 - \frac{2\rho A L(t)}{M(t)} \right] } }$
代入得,喷流速度为:
$u(t) = \sqrt{ \frac{ 2\left[ P\left(\frac{V_0}{V_g(t)}\right)^\gamma-P_0 \right] } { \rho\left[ 1-\left(\frac{A}{S}\right)^2 - \frac{ 2\rho A\left[V-V_g(t)\right] } { S\left\{ m+\rho\left[V-V_g(t)\right] \right\} } \right] } }$
又由于$\frac{dV_g}{dt} = A u(t)$,可以求得完整的微分方程组。
$\begin{cases} \displaystyle \frac{dV_g}{dt} = A \sqrt{ \frac{ 2\left[ P\left(\frac{V_0}{V_g}\right)^\gamma-P_0 \right] } { \rho\left[ 1-\left(\frac{A}{S}\right)^2 - \frac{ 2\rho A\left(V-V_g\right) } { S\left[m+\rho\left(V-V_g\right)\right] } \right] } }, \\[2.2em] \displaystyle \frac{dv}{dt} = \frac{ 2A\left[ P\left(\frac{V_0}{V_g}\right)^\gamma-P_0 \right] } { \left[m+\rho\left(V-V_g\right)\right] \left[ 1-\left(\frac{A}{S}\right)^2 - \frac{ 2\rho A\left(V-V_g\right) } { S\left[m+\rho\left(V-V_g\right)\right] } \right] } -g, \\[2.2em] \displaystyle \frac{dh}{dt}=v. \end{cases}$
初始条件略
接下来通过已经求解出的量求推力与总冲:
$T(t) = \rho A u^2(t)$
代入得,推力为:
$T(t) = \frac{ 2A\left[ P\left(\frac{V_0}{V_g(t)}\right)^\gamma-P_0 \right] } { 1-\left(\frac{A}{S}\right)^2 - \frac{ 2\rho A\left[V-V_g(t)\right] } { S\left\{ m+\rho\left[V-V_g(t)\right] \right\} } }$
总冲积分求解:
$I = \int_0^{t_b} \frac{ 2A\left[ P\left(\frac{V_0}{V_g(t)}\right)^\gamma-P_0 \right] } { 1-\left(\frac{A}{S}\right)^2 - \frac{ 2\rho A\left[V-V_g(t)\right] } { S\left\{ m+\rho\left[V-V_g(t)\right] \right\} } } \,dt$
这里需要注意结束条件为水全部喷完或者内外压差相等。
$V_{g,b} = \min\left[ V,\, V_0\left(\frac{P}{P_0}\right)^{1/\gamma} \right]$
既然有要用计算机数值求解,就最好直接求解微分方程组。
我记得我高中的时候做过一到物竞复赛模拟题,好像是苏国珍的,好像是再什么假设下可以求出解析解,我想找但是找不到了,干脆不找了,反正工程上只需要数值解。
我让GPT生成了一个计算程序,比较简陋,也没考虑开伞啥的,因为我现在没啥时间,你可以拿到程序后继续改改。
我使用你的数据计算了一下,发现理论高度大概25m,总冲17.6N·s,很接近,我开始还以为会有很大的差异。我最近没什么时间,现在也不太确定是计算器的问题还是就是这样,如果是就是这样的话,那么你的模型其实工程上完全可用,但是要考虑的是发射倾斜,内部流体阻力等等问题,这个就不确定了,楼主可以在此基础上进一步研究。我觉得空气阻力反而不是很大,因为速度很低。
这个程序是理想化的,而且没有考虑气体种类选择,牙管阻力等等,楼主可以试试只移植计算部分来修改,大概有10%到20%的精度提升,这样你的程序可能会更好。还可以给个建议,就是用多文件程序,这样结构更清晰,不过我这个也是单文件的程序。
再给个飞控方面的建议,当前main.c中东西太多了,我的建议是用一个新的文件承载飞行的状态机,然后main.c只调用接口即可。当前stm32f103加逻辑绰绰有余,如果后续要进一步的话,建议改为f4甚至h7系列,然后用上freeRTOS。还有HAL库也可以了解一下,是非常方便的,毕竟后续就要把更多精力放在算法而不是硬件配置上。
还有mpu6050,如果只用加速度夹角判断欧拉角只适用于静态,动态下会有问题,如果不加相关算法的话精度会低。还有这里yaw和pitch的定义似乎和常见的不同,自己写飞控的话,我还是建议判断逻辑用四元数,毕竟不存在奇点,还不会像当前这样判断区间是四棱锥而不是圆锥。欧拉角主要传回来给人看。不过具体要改到什么程度就要看你的经济与时间预算了,当前还是工作得很好的。
前几天看到了,不过这几天很忙没时间回,今天有点时间我仔细看了一下。首先我觉得楼主在推力与总冲的计算上是没有问题的,用了一些近似,认为喷嘴面积远小于内部截面积。如果要考虑喷嘴面积,那么火箭加速度会带来一...
感谢您的回复和鼓励,看到之后我也是思考了几天,在这里想和您讨论一下(望多多包涵)。首先我看了一下您对于软件积分的计算方法,又对比了一下我汉化的代码。由于我当时在AI帮助下选择了一种简单易行的方法,也方便我理解,因为诚然我没有高数基础,所以导致并不是很精密的推算。我发的软件中使用的矩形积分,您的中是梯形积分,精度会更高,让Grok看了一下,改一个函数就行。
这是汉化代码
import math
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import matplotlib.pyplot
# 设置中文字体(以黑体为例,可根据系统字体调整)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示异常
# 常量声明
delta_t = 0.0001
P_atm = float(1*101325)
# 仿真输出数据
array_time = []
array_h = []
array_Ft = []
array_mass = []
array_temperature = []
array_pressure = []
array_V_water = []
array_V_air = []
# 优化输出数据
array_opt_x = []
array_opt_Ic = []
array_opt_tc = []
array_opt_delta_v = []
array_opt_Ist = []
opt_variable_name = ""
# 导出数据
engine_name = ""
engine_diameter = 0
engine_lenght = 0
engine_dry_mass = 0
engine_full_mass = 0
engine_manufacturer = ""
# 效率数据
efficiency_sim_rod = 1
efficiency_sim_water = 1
efficiency_sim_gas = 1
efficiency_opt_rod = 1
efficiency_opt_water = 1
efficiency_opt_gas = 1
# 用于优化的仿真函数
def opt_sim(k_const, Rs, water_density, P_ins, At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter):
try:
# 局部输出变量声明
Ic = float(0)
max_h = float(0)
total_time = float(0)
delta_v = float(0)
# 局部仿真变量
s = float(0.0)
vrod = float(0.0)
# 考虑导向杆的存在修正初始空气体积
V_air = V_air - At * rod_lenght
# 定义绝热常数
C_const = P_ins * pow(V_air, k_const)
# 计算空气质量
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
total_time = 0
if(rod_inside_diameter > 0 and rod_lenght > 0):
while(s < rod_lenght):
# 更新时间
total_time = total_time + delta_t
# 计算推力和冲量
Ft = P_ins * At * efficiency_opt_rod
# 更新冲量
Ic += Ft * delta_t
arod = Ft / Roc_mass
vrod += delta_t * arod
delta_v = vrod
s += vrod * delta_t + 0.5 * arod * delta_t * delta_t
if(rod_inside_diameter == 0 and rod_lenght > 0):
while(s < rod_lenght):
total_time = total_time + delta_t
# 计算推力和冲量
Ft = P_ins * At * efficiency_opt_rod
Ic += Ft * delta_t
arod = Ft / Roc_mass
vrod += delta_t * arod
delta_v = vrod
s += vrod * delta_t + 0.5 * arod * delta_t * delta_t
delta_V = At * vrod * delta_t
V_air += delta_V
P_ins = C_const / pow(V_air, k_const)
T = (P_ins * V_air) / (mass_air * Rs)
# 水喷出阶段循环
while(float(V_water) > float(0) and float(P_ins) > float(P_atm)):
total_time = total_time + delta_t
# 计算推力
Ft = 2 * At * (P_ins - P_atm) * efficiency_opt_water
Ic += Ft * delta_t
ve = math.sqrt(2 * (P_ins - P_atm) / water_density)
# 计算体积变化
delta_V = At * ve * delta_t
# 计算温度
T = P_ins * V_air / (mass_air * Rs)
# 更新质量
Roc_mass = Roc_mass - delta_V * water_density
# 更新速度增量
delta_v = delta_v + delta_t * Ft / Roc_mass
# 更新体积值
V_air = V_air + delta_V
V_water = V_water - delta_V
# 计算压力
P_ins = C_const * pow(V_air, -k_const)
# 马赫数1的排气阶段
while(P_ins > P_atm and P_ins/P_atm >= pow((k_const+1)/2, k_const/(k_const-1))):
total_time = total_time + delta_t
Tt = T * 2 / (k_const + 1)
dot_m = P_ins * At * pow(2/(k_const+1), 0.5 * (k_const+1)/(k_const-1)) * math.sqrt(k_const/(Rs*T))
ve_air = math.sqrt(k_const * Rs * Tt)
P_throat = pow(2 / (k_const + 1), k_const / (k_const - 1)) * P_ins
Ft = (dot_m * ve_air + At * (P_throat - P_atm)) * efficiency_opt_gas
Ic += Ft * delta_t
delta_m = dot_m * delta_t
# 更新火箭质量
Roc_mass = Roc_mass - delta_m
# 更新速度增量
delta_v = delta_v + delta_t * Ft / Roc_mass
mass_air = mass_air - delta_m
T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
P_ins = mass_air * Rs * T / V_air
# 亚音速排气阶段
while(P_ins > P_atm):
total_time = total_time + delta_t
Mach = math.sqrt(2 / (k_const-1) * (pow(P_ins / P_atm, (k_const-1) / k_const) - 1))
Tt = T / (1 + 0.5 * (k_const-1) * Mach * Mach)
ve_air = Mach * math.sqrt(k_const * Rs * Tt)
density_chamber = mass_air / V_air
density_throat = density_chamber / pow(1 + 0.5 * (k_const - 1) * Mach * Mach, 1 / (k_const-1))
dot_m = At * density_throat * ve_air
Ft = dot_m * ve_air * efficiency_opt_gas
Ic += Ft * delta_t
delta_m = dot_m * delta_t
# 更新火箭质量
Roc_mass = Roc_mass - delta_m
# 更新速度增量
delta_v = delta_v + delta_t * Ft / Roc_mass
mass_air = mass_air - delta_m
T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
P_ins = mass_air * Rs * T / V_air
return [Ic, total_time, Ic / (mass_propelant * 9.81), delta_v]
except ValueError:
error_label_r.config(text="输入无效。")
# 主优化函数
def optimalize():
global opt_variable_name
array_opt_x.clear()
array_opt_Ic.clear()
array_opt_tc.clear()
array_opt_delta_v.clear()
array_opt_Ist.clear()
gas_name = choosen_gas_opt.get()
if(gas_name == "空气"):
k_const = 1.4
Rs = 287
if(gas_name == "氩气"):
k_const = 1.67
Rs = 208
if(gas_name == "氦气"):
k_const = 1.66
Rs = 2077
if(gas_name == "氢气"):
k_const = 1.41
Rs = 4124
if(gas_name == "氮气"):
k_const = 1.4
Rs = 297
if(gas_name == "二氧化碳"):
k_const = 1.3
Rs = 188
if(gas_name == "氙气"):
k_const = 1.65
Rs = 208
try:
if(gas_name == "自定义"):
k_const = float(entry_combobox1_opt.get())
Rs = float(entry_combobox2_opt.get())
water_density = float(entry_6_r.get())
# 读取输入值
P_ins = float(entry_0_r.get())*100000
At = 3.1415*pow(0.001*float(entry_1_r.get())/2, 2)
V_total = float(entry_2_r.get())*0.001
water_content = float(entry_3_r.get()) * 0.01
V_air = float(V_total - water_content * V_total)
V_water = float(water_content * V_total)
dry_mass = float(entry_4_r.get())
T = float(entry_5_r.get())+273.15
rod_lenght = float(entry_launch_lenght_opt.get()) * 0.001
rod_inside_diameter = float(entry_launch_diameter_opt.get()) * 0.001
# 错误保护
if(P_ins <= 0):
raise ValueError
if(k_const <= 0):
raise ValueError
if(Rs <= 0):
raise ValueError
if(water_density <= 0):
raise ValueError
if(At <= 0):
raise ValueError
if(V_total <= 0):
raise ValueError
if(water_content < 0 or water_content >= 1):
raise ValueError
if(dry_mass <= 0):
raise ValueError
if(T <= 0):
raise ValueError
if(rod_inside_diameter < 0):
raise ValueError
if(rod_lenght < 0):
raise ValueError
if(rod_inside_diameter > 0.001 * float(entry_1_r.get())):
raise ValueError
s = float(0.0)
vrod = float(0.0)
# 考虑导向杆的存在修正初始空气体积
V_air = V_air - At * rod_lenght
# 定义绝热常数
C_const = P_ins * pow(V_air, k_const)
# 计算空气质量
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
# 获取优化变量
opt_var = choosen_opt_variable.get()
opt_range_min = float(entry_opt_min.get())
opt_range_max = float(entry_opt_max.get())
opt_it = int(entry_opt_itteration.get())
# 优化变量错误保护
if(opt_range_max <= opt_range_min):
raise ValueError
if(opt_var == "含水量"):
if(opt_range_min < 0 or opt_range_max >= 100):
raise ValueError
if(opt_range_min == 0 and (opt_var == "喉径" or opt_var == "容积")):
raise ValueError
if(opt_it <= 0 and opt_var != "气体温度"):
raise ValueError
if(opt_var == "气体温度" and opt_range_min < -273):
raise ValueError
opt_variable_name = opt_var
if(opt_var == "压强"):
opt_range_min *= 100000
opt_range_max *= 100000
opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
for i in range(opt_it + 1):
array_opt_x.append(opt_range_min)
mass_air = opt_range_min * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
temp_data_opt = opt_sim(k_const, Rs, water_density, opt_range_min, At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
array_opt_Ic.append(temp_data_opt[0])
array_opt_tc.append(temp_data_opt[1])
array_opt_Ist.append(temp_data_opt[2])
array_opt_delta_v.append(temp_data_opt[3])
opt_range_min += opt_eps
if(opt_var == "喉径"):
opt_range_min = 3.1415*pow(0.001*opt_range_min/2, 2)
opt_range_max = 3.1415*pow(0.001*opt_range_max/2, 2)
opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
for i in range(opt_it + 1):
array_opt_x.append(opt_range_min)
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
temp_data_opt = opt_sim(k_const, Rs, water_density, P_ins, opt_range_min, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
array_opt_Ic.append(temp_data_opt[0])
array_opt_tc.append(temp_data_opt[1])
array_opt_Ist.append(temp_data_opt[2])
array_opt_delta_v.append(temp_data_opt[3])
opt_range_min += opt_eps
if(opt_var == "容积"):
opt_range_min *= 0.001
opt_range_max *= 0.001
opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
for i in range(opt_it + 1):
array_opt_x.append(opt_range_min)
V_air = opt_range_min - water_content * opt_range_min
V_water = water_content * opt_range_min
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
temp_data_opt = opt_sim(k_const, Rs, water_density, P_ins, At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
array_opt_Ic.append(temp_data_opt[0])
array_opt_tc.append(temp_data_opt[1])
array_opt_Ist.append(temp_data_opt[2])
array_opt_delta_v.append(temp_data_opt[3])
opt_range_min += opt_eps
if(opt_var == "含水量"):
opt_range_min *= 0.01
opt_range_max *= 0.01
opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
for i in range(opt_it + 1):
array_opt_x.append(opt_range_min)
V_air = float(V_total - opt_range_min * V_total)
V_water = float(opt_range_min * V_total)
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
temp_data_opt = opt_sim(k_const, Rs, water_density, float(P_ins), At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
array_opt_Ic.append(temp_data_opt[0])
array_opt_tc.append(temp_data_opt[1])
array_opt_Ist.append(temp_data_opt[2])
array_opt_delta_v.append(temp_data_opt[3])
opt_range_min += opt_eps
if(opt_var == "干重"):
opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
for i in range(opt_it + 1):
array_opt_x.append(opt_range_min)
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = opt_range_min + mass_propelant
temp_data_opt = opt_sim(k_const, Rs, water_density, float(P_ins), At, V_air, V_water, Roc_mass, T, rod_lenght, rod_inside_diameter)
array_opt_Ic.append(temp_data_opt[0])
array_opt_tc.append(temp_data_opt[1])
array_opt_Ist.append(temp_data_opt[2])
array_opt_delta_v.append(temp_data_opt[3])
opt_range_min += opt_eps
if(opt_var == "气体温度"):
opt_range_min += 273.15
opt_range_max += 273.15
opt_eps = (opt_range_max - opt_range_min) / float(opt_it)
for i in range(opt_it + 1):
array_opt_x.append(opt_range_min)
mass_air = P_ins * V_air / (opt_range_min * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
temp_data_opt = opt_sim(k_const, Rs, water_density, float(P_ins), At, V_air, V_water, Roc_mass, opt_range_min, rod_lenght, rod_inside_diameter)
array_opt_Ic.append(temp_data_opt[0])
array_opt_tc.append(temp_data_opt[1])
array_opt_Ist.append(temp_data_opt[2])
array_opt_delta_v.append(temp_data_opt[3])
opt_range_min += opt_eps
plot_opt_Ic()
# 清除错误信息
error_label_r.config(text="")
except ValueError:
error_label_r.config(text="输入无效。")
def plot_opt_Ic():
try:
# 清除上一个图形
ax_right.clear()
# 绘制新数据
ax_right.plot(array_opt_x, array_opt_Ic, linestyle='-', color='b')
# 设置标签和标题
ax_right.set_xlabel(opt_variable_name)
ax_right.set_ylabel('冲量 Ic [Ns]')
ax_right.set_title('冲量曲线')
# 更新画布
canvas_right.draw()
# 清除错误信息
error_label_r.config(text="")
except ValueError:
error_label_r.config(text="输入无效。")
def plot_opt_tc():
try:
# 清除上一个图形
ax_right.clear()
# 绘制新数据
ax_right.plot(array_opt_x, array_opt_tc, linestyle='-', color='b')
# 设置标签和标题
ax_right.set_xlabel(opt_variable_name)
ax_right.set_ylabel('总时间 [s]')
ax_right.set_title('工作时间曲线')
# 更新画布
canvas_right.draw()
# 清除错误信息
error_label_r.config(text="")
except ValueError:
error_label_r.config(text="输入无效。")
def plot_opt_Ist():
try:
# 清除上一个图形
ax_right.clear()
# 绘制新数据
ax_right.plot(array_opt_x, array_opt_Ist, linestyle='-', color='b')
# 设置标签和标题
ax_right.set_xlabel(opt_variable_name)
ax_right.set_ylabel('比冲 [s]')
ax_right.set_title('比冲曲线')
# 更新画布
canvas_right.draw()
# 清除错误信息
error_label_r.config(text="")
except ValueError:
error_label_r.config(text="输入无效。")
def plot_opt_delta_v():
try:
# 清除上一个图形
ax_right.clear()
# 绘制新数据
ax_right.plot(array_opt_x, array_opt_delta_v, linestyle='-', color='b')
# 设置标签和标题
ax_right.set_xlabel(opt_variable_name)
ax_right.set_ylabel('速度增量 Δv [m/s]')
ax_right.set_title('速度增量曲线')
# 更新画布
canvas_right.draw()
# 清除错误信息
error_label_r.config(text="")
except ValueError:
error_label_r.config(text="输入无效。")
# 主仿真函数
def simulate():
global engine_dry_mass
global engine_full_mass
# 输出变量声明
Ic = float(0)
max_h = float(0)
total_time = float(0)
delta_v = float(0)
gas_name = choosen_gas.get()
try:
if(gas_name == "空气"):
k_const = 1.4
Rs = 287
if(gas_name == "氩气"):
k_const = 1.67
Rs = 208
if(gas_name == "氦气"):
k_const = 1.66
Rs = 2077
if(gas_name == "氢气"):
k_const = 1.41
Rs = 4124
if(gas_name == "氮气"):
k_const = 1.4
Rs = 297
if(gas_name == "二氧化碳"):
k_const = 1.3
Rs = 188
if(gas_name == "氙气"):
k_const = 1.65
Rs = 208
if(gas_name == "自定义"):
k_const = float(entry_combobox1.get())
Rs = float(entry_combobox2.get())
water_density = float(entry_6.get())
# 清除总时间值
total_time = 0
Ic = 0
delta_v = 0
# 清空所有数组
array_time.clear()
array_h.clear()
array_Ft.clear()
array_mass.clear()
array_temperature.clear()
array_pressure.clear()
array_V_water.clear()
array_V_air.clear()
# 读取输入值
P_ins = float(entry_0.get())*100000
At = 3.1415*pow(0.001*float(entry_1.get())/2, 2)
V_total = float(entry_2.get())*0.001
water_content = float(entry_3.get()) * 0.01
V_air = V_total - water_content * V_total
V_water = water_content * V_total
dry_mass = float(entry_4.get())
engine_dry_mass = dry_mass
T = float(entry_5.get())+273.15
rod_lenght = float(entry_launch_lenght.get()) * 0.001
rod_inside_diameter = float(entry_launch_diameter.get()) * 0.001
# 错误保护
if(P_ins <= 0):
raise ValueError
if(k_const <= 0):
raise ValueError
if(Rs <= 0):
raise ValueError
if(water_density <= 0):
raise ValueError
if(At <= 0):
raise ValueError
if(V_total <= 0):
raise ValueError
if(water_content < 0 or water_content >= 1):
raise ValueError
if(dry_mass <= 0):
raise ValueError
if(T <= 0):
raise ValueError
if(rod_inside_diameter < 0):
raise ValueError
if(rod_lenght < 0):
raise ValueError
if(rod_inside_diameter >= 0.001 * float(entry_1.get())):
raise ValueError
s = float(0.0)
vrod = float(0.0)
# 考虑导向杆的存在修正初始空气体积
V_air = V_air - At * rod_lenght
# 定义绝热常数
C_const = P_ins * pow(V_air, k_const)
# 计算空气质量
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
engine_full_mass = Roc_mass
if(rod_inside_diameter > 0 and rod_lenght > 0):
while(s < rod_lenght):
# 数组更新
array_time.append(total_time)
total_time = total_time + delta_t
array_V_water.append(V_water)
array_V_air.append(V_air)
array_mass.append(Roc_mass)
array_pressure.append(P_ins)
array_temperature.append(T)
# 计算推力和冲量
Ft = (P_ins * At) * efficiency_sim_rod
array_Ft.append(Ft)
Ic += Ft * delta_t
arod = Ft / Roc_mass
vrod += delta_t * arod
delta_v = vrod
s += vrod * delta_t + 0.5 * arod * delta_t * delta_t
if(rod_inside_diameter == 0 and rod_lenght > 0):
while(s < rod_lenght):
# 数组更新
array_time.append(total_time)
total_time = total_time + delta_t
array_mass.append(Roc_mass)
array_V_air.append(V_air)
array_V_water.append(V_water)
array_temperature.append(T)
array_pressure.append(P_ins)
# 计算推力和冲量
Ft = (P_ins * At) * efficiency_sim_rod
array_Ft.append(Ft)
Ic += Ft * delta_t
arod = Ft / Roc_mass
vrod = vrod + delta_t * arod
delta_v = vrod
s += vrod * delta_t + 0.5 * arod * delta_t * delta_t
delta_V = At * vrod * delta_t
V_air += delta_V
P_ins = C_const / pow(V_air, k_const)
T = (P_ins * V_air) / (mass_air * Rs)
# 水喷出阶段循环
while(V_water > 0 and P_ins > P_atm):
# 更新时间数组用于绘图
array_time.append(total_time)
total_time = total_time + delta_t
# 计算推力
Ft = (2 * At * (P_ins - P_atm)) * efficiency_sim_water
array_Ft.append(Ft)
Ic += Ft * delta_t
ve = math.sqrt(2 * (P_ins - P_atm) / water_density)
# 计算体积变化
delta_V = At * ve * delta_t
# 更新体积数组
array_V_water.append(V_water)
array_V_air.append(V_air)
# 计算温度
T = P_ins * V_air / (mass_air * Rs)
array_temperature.append(T)
# 更新质量数组
array_mass.append(Roc_mass)
Roc_mass = Roc_mass - delta_V * water_density
# 更新速度增量
delta_v = delta_v + delta_t * Ft / Roc_mass
# 更新体积值
V_air = V_air + delta_V
V_water = V_water - delta_V
# 计算压力并更新数组
P_ins = C_const * pow(V_air, -k_const)
array_pressure.append(P_ins)
# 马赫数1的排气阶段
while(P_ins > P_atm and P_ins/P_atm >= pow((k_const+1)/2, k_const/(k_const-1))):
# 更新时间数组用于绘图
array_time.append(total_time)
total_time = total_time + delta_t
# 添加不变化的值用于绘图
array_V_water.append(V_water)
array_V_air.append(V_air)
Tt = T * 2 / (k_const + 1)
dot_m = P_ins * At * pow(2/(k_const+1), 0.5 * (k_const+1)/(k_const-1)) * math.sqrt(k_const/(Rs*T))
ve_air = math.sqrt(k_const * Rs * Tt)
P_throat = pow(2 / (k_const + 1), k_const / (k_const - 1)) * P_ins
Ft = (dot_m * ve_air + At * (P_throat - P_atm)) * efficiency_sim_gas
array_Ft.append(Ft)
Ic += Ft * delta_t
delta_m = dot_m * delta_t
# 更新火箭质量
Roc_mass = Roc_mass - delta_m
array_mass.append(Roc_mass)
# 更新速度增量
delta_v = delta_v + delta_t * Ft / Roc_mass
mass_air = mass_air - delta_m
T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
array_temperature.append(T)
P_ins = mass_air * Rs * T / V_air
array_pressure.append(P_ins)
# 亚音速排气阶段
while(P_ins > P_atm):
# 更新时间数组用于绘图
array_time.append(total_time)
total_time = total_time + delta_t
# 添加不变化的值用于绘图
array_V_water.append(V_water)
array_V_air.append(V_air)
Mach = math.sqrt(2 / (k_const-1) * (pow(P_ins / P_atm, (k_const-1) / k_const) - 1))
Tt = T / (1 + 0.5 * (k_const-1) * Mach * Mach)
ve_air = Mach * math.sqrt(k_const * Rs * Tt)
density_chamber = mass_air / V_air
density_throat = density_chamber / pow(1 + 0.5 * (k_const - 1) * Mach * Mach, 1 / (k_const-1))
dot_m = At * density_throat * ve_air
Ft = (dot_m * ve_air) * efficiency_sim_gas
array_Ft.append(Ft)
Ic += Ft * delta_t
delta_m = dot_m * delta_t
# 更新火箭质量
Roc_mass = Roc_mass - delta_m
array_mass.append(Roc_mass)
# 更新速度增量
delta_v = delta_v + delta_t * Ft / Roc_mass
mass_air = mass_air - delta_m
T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
array_temperature.append(T)
P_ins = mass_air * Rs * T / V_air
array_pressure.append(P_ins)
# 清除错误信息
error_label.config(text="")
# 打印输出值
text_widget_output = "冲量 Ic = {:.2f} Ns\n".format(Ic) + "工作时间 tc = {:.2f} s\n".format(total_time) + "比冲 Ist = {:.2f} s\n".format(Ic / (mass_propelant * 9.81)) + "速度增量 Δv = {:.2f} m/s\n\n".format(delta_v)
text_widget.insert("1.0", text_widget_output)
plot_Ft()
except ValueError:
error_label.config(text="输入无效。")
def plot_Ft():
try:
# 清除上一个图形
ax_left.clear()
# 绘制新数据
ax_left.plot(array_time, array_Ft, linestyle='-', color='b')
# 设置标签和标题
ax_left.set_xlabel('时间 [s]')
ax_left.set_ylabel('推力 [N]')
ax_left.set_title('推力曲线')
# 更新画布
canvas_left.draw()
# 清除错误信息
error_label.config(text="")
except ValueError:
error_label.config(text="输入无效。")
def plot_mass():
try:
# 清除上一个图形
ax_left.clear()
# 绘制新数据
ax_left.plot(array_time, array_mass, linestyle='-', color='b')
# 设置标签和标题
ax_left.set_xlabel('时间 [s]')
ax_left.set_ylabel('质量 [kg]')
ax_left.set_title('质量变化曲线')
# 更新画布
canvas_left.draw()
# 清除错误信息
error_label.config(text="")
except ValueError:
error_label.config(text="输入无效。")
def plot_temperature():
try:
# 清除上一个图形
ax_left.clear()
# 绘制新数据
ax_left.plot(array_time, array_temperature, linestyle='-', color='b')
# 设置标签和标题
ax_left.set_xlabel('时间 [s]')
ax_left.set_ylabel('温度 [K]')
ax_left.set_title('温度曲线')
# 更新画布
canvas_left.draw()
# 清除错误信息
error_label.config(text="")
except ValueError:
error_label.config(text="输入无效。")
def plot_pressure():
try:
# 清除上一个图形
ax_left.clear()
# 将压力单位转换为 bar
for i in range(len(array_pressure)):
array_pressure[i] /= 100000
# 绘制新数据
ax_left.plot(array_time, array_pressure, linestyle='-', color='b')
# 设置标签和标题
ax_left.set_xlabel('时间 [s]')
ax_left.set_ylabel('压强 [bar]')
ax_left.set_title('压强曲线')
# 更新画布
canvas_left.draw()
# 清除错误信息
error_label.config(text="")
except ValueError:
error_label.config(text="输入无效。")
def plot_volume():
try:
# 清除上一个图形
ax_left.clear()
# 绘制新数据
ax_left.plot(array_time, array_V_water, linestyle='-', color='b', label = "水")
ax_left.plot(array_time, array_V_air, linestyle='-', color='c', label = "空气")
# 设置标签和标题
ax_left.set_xlabel('时间 [s]')
ax_left.set_ylabel('体积 [m³]')
ax_left.set_title('体积变化曲线')
ax_left.legend()
# 更新画布
canvas_left.draw()
# 清除错误信息
error_label.config(text="")
except ValueError:
error_label.config(text="输入无效。")
def save_to_file(entry_text):
file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("文本文件", "*.txt")])
if file_path:
with open(file_path, "w") as file:
# frame_simulate
file.write(choosen_gas.get()+"\n")
file.write(entry_combobox1.get()+"\n")
file.write(entry_combobox2.get()+"\n")
file.write(entry_launch_lenght.get()+"\n")
file.write(entry_launch_diameter.get()+"\n")
# frame_left
file.write(entry_0.get()+"\n")
file.write(entry_1.get()+"\n")
file.write(entry_2.get()+"\n")
file.write(entry_3.get()+"\n")
file.write(entry_4.get()+"\n")
file.write(entry_5.get()+"\n")
file.write(entry_6.get()+"\n")
# frame_simulate_r
file.write(choosen_opt_variable.get()+"\n")
file.write(entry_opt_min.get()+"\n")
file.write(entry_opt_max.get()+"\n")
file.write(entry_opt_itteration.get()+"\n")
file.write(choosen_gas_opt.get()+"\n")
file.write(entry_combobox1_opt.get()+"\n")
file.write(entry_combobox2_opt.get()+"\n")
file.write(entry_launch_lenght_opt.get()+"\n")
file.write(entry_launch_diameter_opt.get()+"\n")
# frame_right
file.write(entry_0_r.get()+"\n")
file.write(entry_1_r.get()+"\n")
file.write(entry_2_r.get()+"\n")
file.write(entry_3_r.get()+"\n")
file.write(entry_4_r.get()+"\n")
file.write(entry_5_r.get()+"\n")
file.write(entry_6_r.get()+"\n")
def load_from_file(entry_text):
array_load_file = []
file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt")])
if file_path:
with open(file_path, "r") as file:
line = file.readline()
# 继续读取行直到文件末尾
while line:
array_load_file.append(line.strip())
line = file.readline()
combobox.set(array_load_file[0])
entry_combobox1.delete(0, tk.END)
entry_combobox1.insert(0, array_load_file[1])
entry_combobox2.delete(0, tk.END)
entry_combobox2.insert(0, array_load_file[2])
entry_launch_lenght.delete(0, tk.END)
entry_launch_lenght.insert(0, array_load_file[3])
entry_launch_diameter.delete(0, tk.END)
entry_launch_diameter.insert(0, array_load_file[4])
entry_0.delete(0, tk.END)
entry_0.insert(0, array_load_file[5])
entry_1.delete(0, tk.END)
entry_1.insert(0, array_load_file[6])
entry_2.delete(0, tk.END)
entry_2.insert(0, array_load_file[7])
entry_3.delete(0, tk.END)
entry_3.insert(0, array_load_file[8])
entry_4.delete(0, tk.END)
entry_4.insert(0, array_load_file[9])
entry_5.delete(0, tk.END)
entry_5.insert(0, array_load_file[10])
entry_6.delete(0, tk.END)
entry_6.insert(0, array_load_file[11])
combobox_variable_opt.set(array_load_file[12])
entry_opt_min.delete(0, tk.END)
entry_opt_min.insert(0, array_load_file[13])
entry_opt_max.delete(0, tk.END)
entry_opt_max.insert(0, array_load_file[14])
entry_opt_itteration.delete(0, tk.END)
entry_opt_itteration.insert(0, array_load_file[15])
combobox_gas_opt.set(array_load_file[16])
entry_combobox1_opt.delete(0, tk.END)
entry_combobox1_opt.insert(0, array_load_file[17])
entry_combobox2_opt.delete(0, tk.END)
entry_combobox2_opt.insert(0, array_load_file[18])
entry_launch_lenght_opt.delete(0, tk.END)
entry_launch_lenght_opt.insert(0, array_load_file[19])
entry_launch_diameter_opt.delete(0, tk.END)
entry_launch_diameter_opt.insert(0, array_load_file[20])
entry_0_r.delete(0, tk.END)
entry_0_r.insert(0, array_load_file[21])
entry_1_r.delete(0, tk.END)
entry_1_r.insert(0, array_load_file[22])
entry_2_r.delete(0, tk.END)
entry_2_r.insert(0, array_load_file[23])
entry_3_r.delete(0, tk.END)
entry_3_r.insert(0, array_load_file[24])
entry_4_r.delete(0, tk.END)
entry_4_r.insert(0, array_load_file[25])
entry_5_r.delete(0, tk.END)
entry_5_r.insert(0, array_load_file[26])
entry_6_r.delete(0, tk.END)
entry_6_r.insert(0, array_load_file[27])
def export_to_file():
entry_window = tk.Toplevel(root)
entry_window.title("导出数据")
label_export_name = ttk.Label(entry_window, text="发动机名称:")
label_export_name.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")
entry_export_name = ttk.Entry(entry_window, width=10)
entry_export_name.grid(row=0, column=2, pady=5)
entry_export_name.insert(0, "0")
label_export_diameter = ttk.Label(entry_window, text="发动机直径 [mm]:")
label_export_diameter.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
entry_export_diameter = ttk.Entry(entry_window, width=10)
entry_export_diameter.grid(row=1, column=2, pady=5)
entry_export_diameter.insert(0, "0")
label_export_lenght = ttk.Label(entry_window, text="发动机长度 [mm]:")
label_export_lenght.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
entry_export_lenght = ttk.Entry(entry_window, width=10)
entry_export_lenght.grid(row=2, column=2, pady=5)
entry_export_lenght.insert(0, "0")
label_export_man = ttk.Label(entry_window, text="发动机制造商:")
label_export_man.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
entry_export_man = ttk.Entry(entry_window, width=10)
entry_export_man.grid(row=3, column=2, pady=5)
entry_export_man.insert(0, "0")
ok_button = ttk.Button(entry_window, text="确定", command=lambda: save_and_close(entry_export_name, entry_export_diameter, entry_export_lenght, entry_export_man, entry_window))
ok_button.grid(row=4, column=1, columnspan=2, pady=10)
def save_and_close(entry_export_name, entry_export_diameter, entry_export_lenght, entry_export_man, window):
global engine_name
global engine_diameter
global engine_lenght
global engine_manufacturer
engine_name = entry_export_name.get()
engine_diameter = entry_export_diameter.get()
engine_lenght = entry_export_lenght.get()
engine_manufacturer = entry_export_man.get()
# 预期错误
try:
if(float(engine_diameter) <= 0 ):
raise ValueError
if(float(engine_lenght) <= 0):
raise ValueError
save_to_export()
window.destroy()
except ValueError:
messagebox.showinfo("错误", "输入无效。")
def save_to_export():
file_path = filedialog.asksaveasfilename(defaultextension=".eng", filetypes=[("发动机文件", "*.eng")])
if file_path:
with open(file_path, "w") as file:
file.write(engine_name + " ")
file.write(str(engine_diameter) + " ")
file.write(str(engine_lenght) + " ")
file.write("P" + " ")
file.write(str(engine_dry_mass) + " ")
file.write(str(engine_full_mass) + " ")
file.write(engine_manufacturer + "\n")
for el_t, el_F in zip(array_time, array_Ft):
file.write(str(el_t) + " " + str(el_F) + "\n")
def efficiency_sim_window():
entry_window = tk.Toplevel(root)
entry_window.title("仿真效率设置")
label_export_name = ttk.Label(entry_window, text="导向杆效率:")
label_export_name.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")
entry_export_name = ttk.Entry(entry_window, width=10)
entry_export_name.grid(row=0, column=2, pady=5)
entry_export_name.insert(0, str(efficiency_sim_rod))
label_export_diameter = ttk.Label(entry_window, text="液相效率:")
label_export_diameter.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
entry_export_diameter = ttk.Entry(entry_window, width=10)
entry_export_diameter.grid(row=1, column=2, pady=5)
entry_export_diameter.insert(0, str(efficiency_sim_water))
label_export_lenght = ttk.Label(entry_window, text="气相效率:")
label_export_lenght.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
entry_export_lenght = ttk.Entry(entry_window, width=10)
entry_export_lenght.grid(row=2, column=2, pady=5)
entry_export_lenght.insert(0, str(efficiency_sim_gas))
ok_button = ttk.Button(entry_window, text="确定", command=lambda: efficiency_sim_save(entry_export_name, entry_export_diameter, entry_export_lenght, entry_window))
ok_button.grid(row=4, column=1, columnspan=2, pady=10)
def efficiency_sim_save(entry_export_name, entry_export_diameter, entry_export_lenght, window):
global efficiency_sim_rod
global efficiency_sim_water
global efficiency_sim_gas
# 预期错误
try:
efficiency_sim_rod = float(entry_export_name.get())
efficiency_sim_water = float(entry_export_diameter.get())
efficiency_sim_gas = float(entry_export_lenght.get())
if(efficiency_sim_rod <= 0 or efficiency_sim_water <= 0 or efficiency_sim_gas <= 0):
raise ValueError
if(efficiency_sim_rod > 100 or efficiency_sim_water > 100 or efficiency_sim_gas > 100):
raise ValueError
window.destroy()
except ValueError:
messagebox.showinfo("错误", "输入无效。")
efficiency_sim_rod = 1
efficiency_sim_water = 1
efficiency_sim_gas = 1
def efficiency_opt_window():
entry_window = tk.Toplevel(root)
entry_window.title("优化效率设置")
label_export_name = ttk.Label(entry_window, text="导向杆效率:")
label_export_name.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")
entry_export_name = ttk.Entry(entry_window, width=10)
entry_export_name.grid(row=0, column=2, pady=5)
entry_export_name.insert(0, str(efficiency_opt_rod))
label_export_diameter = ttk.Label(entry_window, text="液相效率:")
label_export_diameter.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
entry_export_diameter = ttk.Entry(entry_window, width=10)
entry_export_diameter.grid(row=1, column=2, pady=5)
entry_export_diameter.insert(0, str(efficiency_opt_water))
label_export_lenght = ttk.Label(entry_window, text="气相效率:")
label_export_lenght.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
entry_export_lenght = ttk.Entry(entry_window, width=10)
entry_export_lenght.grid(row=2, column=2, pady=5)
entry_export_lenght.insert(0, str(efficiency_opt_gas))
ok_button = ttk.Button(entry_window, text="确定", command=lambda: efficiency_opt_save(entry_export_name, entry_export_diameter, entry_export_lenght, entry_window))
ok_button.grid(row=4, column=1, columnspan=2, pady=10)
def efficiency_opt_save(entry_export_name, entry_export_diameter, entry_export_lenght, window):
global efficiency_opt_rod
global efficiency_opt_water
global efficiency_opt_gas
# 预期错误
try:
efficiency_opt_rod = float(entry_export_name.get())
efficiency_opt_water = float(entry_export_diameter.get())
efficiency_opt_gas = float(entry_export_lenght.get())
if(efficiency_opt_rod <= 0 or efficiency_opt_water <= 0 or efficiency_opt_gas <= 0):
raise ValueError
if(efficiency_opt_rod > 100 or efficiency_opt_water > 100 or efficiency_opt_gas > 100):
raise ValueError
window.destroy()
except ValueError:
messagebox.showinfo("错误", "输入无效。")
efficiency_opt_rod = 1
efficiency_opt_water = 1
efficiency_opt_gas = 1
# 创建主 Tkinter 窗口
root = tk.Tk()
root.title("水火箭仿真器")
menubar = tk.Menu(root)
file_menu = tk.Menu(menubar, tearoff=0)
file_menu.add_command(label="保存", command=lambda: save_to_file(entry_0))
file_menu.add_command(label="加载", command=lambda: load_from_file(entry_0))
file_menu.add_command(label="导出为 .eng", command=lambda: export_to_file())
menubar.add_cascade(label="文件", menu=file_menu)
root.config(menu=menubar)
# 设置窗口为全屏
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.geometry(f"{screen_width}x{screen_height}")
##########################################################################
# 左侧区域
##########################################################################
frame_simulate = ttk.Frame(root, padding="10")
frame_simulate.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
simulate_button = ttk.Button(frame_simulate, text="仿真", command=simulate)
simulate_button.grid(row=0, column=0, columnspan=1, pady=5, sticky="w")
efficiency_button = ttk.Button(frame_simulate, text="效率", command=efficiency_sim_window)
efficiency_button.grid(row=0, column=2, columnspan=1, pady=5, sticky="w")
# 显示所选选项的标签
label_gas = ttk.Label(frame_simulate, text="选择气体:")
label_gas.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
# 创建 StringVar 来保存组合框的选定值
default_option = "空气"
choosen_gas = tk.StringVar(value=default_option)
# 创建组合框(下拉菜单)
options = ["空气", "氩气", "氦气", "氢气", "氮气", "二氧化碳", "氙气", "自定义"]
combobox = ttk.Combobox(frame_simulate, textvariable=choosen_gas, values=options, state="readonly", width=10)
combobox.grid(row=1, column=2, columnspan=2, pady=5, sticky="w")
label_custom_gas_info = ttk.Label(frame_simulate, text="自定义气体数据:")
label_custom_gas_info.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
label_combobox1 = ttk.Label(frame_simulate, text="比热比:")
label_combobox1.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")
entry_combobox1 = ttk.Entry(frame_simulate, width=10)
entry_combobox1.grid(row=4, column=2, pady=5)
entry_combobox1.insert(0, "0")
label_combobox2 = ttk.Label(frame_simulate, text="气体常数 [J/(kg*K)]:")
label_combobox2.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")
entry_combobox2 = ttk.Entry(frame_simulate, width=10)
entry_combobox2.grid(row=5, column=2, pady=5)
entry_combobox2.insert(0, "0")
# 导向杆数据
label_launch_lenght = ttk.Label(frame_simulate, text="导向杆长度 [mm]:")
label_launch_lenght.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")
entry_launch_lenght = ttk.Entry(frame_simulate, width=10)
entry_launch_lenght.grid(row=6, column=2, pady=5)
entry_launch_lenght.insert(0, "0")
label_launch_diameter = ttk.Label(frame_simulate, text="导向杆内径 [mm]:")
label_launch_diameter.grid(row=7, column=0, columnspan=2, pady=5, sticky="w")
entry_launch_diameter = ttk.Entry(frame_simulate, width=10)
entry_launch_diameter.grid(row=7, column=2, pady=5)
entry_launch_diameter.insert(0, "0")
# 左侧输入框架
frame_left = ttk.Frame(root, padding="10")
frame_left.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))
label_0 = ttk.Label(frame_left, text="初始压强 [bar]:")
label_0.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")
entry_0 = ttk.Entry(frame_left, width=10)
entry_0.grid(row=0, column=2, pady=5)
entry_0.insert(0, "0")
label_1 = ttk.Label(frame_left, text="喉部直径 [mm]:")
label_1.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
entry_1 = ttk.Entry(frame_left, width=10)
entry_1.grid(row=1, column=2, pady=5)
entry_1.insert(0, "0")
label_2 = ttk.Label(frame_left, text="总容积 [L]:")
label_2.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
entry_2 = ttk.Entry(frame_left, width=10)
entry_2.grid(row=2, column=2, pady=5)
entry_2.insert(0, "0")
label_3 = ttk.Label(frame_left, text="含水量 [%]:")
label_3.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
entry_3 = ttk.Entry(frame_left, width=10)
entry_3.grid(row=3, column=2, pady=5)
entry_3.insert(0, "0")
label_4 = ttk.Label(frame_left, text="干重 [kg]:")
label_4.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")
entry_4 = ttk.Entry(frame_left, width=10)
entry_4.grid(row=4, column=2, pady=5)
entry_4.insert(0, "0")
label_5 = ttk.Label(frame_left, text="初始温度 [°C]:")
label_5.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")
entry_5 = ttk.Entry(frame_left, width=10)
entry_5.grid(row=5, column=2, pady=5)
entry_5.insert(0, "0")
# 自定义液体密度
label_6 = ttk.Label(frame_left, text="液体密度 [kg/m³]:")
label_6.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")
entry_6 = ttk.Entry(frame_left, width=10)
entry_6.grid(row=6, column=2, pady=5)
entry_6.insert(0, "1000")
frame_button = ttk.Frame(root, padding="10")
frame_button.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S))
# 绘图按钮
plot_button = ttk.Button(frame_button, text="绘制推力", command=plot_Ft)
plot_button.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
plot_button = ttk.Button(frame_button, text="绘制压强", command=plot_pressure)
plot_button.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
plot_button = ttk.Button(frame_button, text="绘制体积", command=plot_volume)
plot_button.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
plot_button = ttk.Button(frame_button, text="绘制质量", command=plot_mass)
plot_button.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")
plot_button = ttk.Button(frame_button, text="绘制温度", command=plot_temperature)
plot_button.grid(row=5, column=0, columnspan=2, pady=5,sticky="w")
# 错误标签
error_label = ttk.Label(frame_button, text="", foreground="red")
error_label.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")
frame_text_widget = ttk.Frame(root, padding="10")
frame_text_widget.grid(row=1, column=3, sticky=(tk.W, tk.E, tk.N, tk.S))
text_widget = tk.Text(frame_text_widget, height=11, width=30)
text_widget.grid(row=0, column=0, sticky="w")
# 左侧绘图区域
frame_plot_left = ttk.Frame(root, padding="10")
frame_plot_left.grid(row=0, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S))
# 创建 Matplotlib 图形和画布嵌入到 Tkinter 窗口
fig_left, ax_left = plt.subplots(figsize=(screen_width / 200, screen_height / 150))
canvas_left = FigureCanvasTkAgg(fig_left, master=frame_plot_left)
canvas_widget = canvas_left.get_tk_widget()
canvas_widget.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
##########################################################################
# 右侧区域(优化部分)
##########################################################################
frame_simulate_r = ttk.Frame(root, padding="10")
frame_simulate_r.grid(row=1, column=4, sticky=(tk.W, tk.E, tk.N, tk.S))
simulate_button = ttk.Button(frame_simulate_r, text="优化", command=optimalize)
simulate_button.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")
# 显示所选选项的标签
label_choose_opt = ttk.Label(frame_simulate_r, text="选择优化变量")
label_choose_opt.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
# 创建 StringVar 来保存组合框的选定值
choosen_opt_variable = tk.StringVar()
# 创建组合框(下拉菜单)
options_variable_opt = ["压强", "喉径", "容积", "含水量", "干重", "气体温度"]
combobox_variable_opt = ttk.Combobox(frame_simulate_r, textvariable=choosen_opt_variable, values=options_variable_opt, state="readonly", width=10)
combobox_variable_opt.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
# 优化范围
label_opt_range = ttk.Label(frame_simulate_r, text="优化范围:")
label_opt_range.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
entry_opt_min = ttk.Entry(frame_simulate_r, width=7)
entry_opt_min.grid(row=4, column=0, sticky="w", pady=5)
entry_opt_min.insert(0, "最小值")
entry_opt_max = ttk.Entry(frame_simulate_r, width=7)
entry_opt_max.grid(row=5, column=0, sticky="w", pady=5)
entry_opt_max.insert(0, "最大值")
# 迭代次数
label_opt_itteration = ttk.Label(frame_simulate_r, text="迭代次数:")
label_opt_itteration.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")
entry_opt_itteration = ttk.Entry(frame_simulate_r, width=7)
entry_opt_itteration.grid(row=6, column=1, sticky="w", pady=5)
entry_opt_itteration.insert(0, "1")
efficiency_button = ttk.Button(frame_simulate_r, text="效率", command=efficiency_opt_window)
efficiency_button.grid(row=0, column=1, columnspan=2, pady=5, sticky="w")
# 右侧第二个框架
frame_overall_data_right = ttk.Frame(root, padding="10")
frame_overall_data_right.grid(row=1, column=5, sticky=(tk.W, tk.E, tk.N, tk.S))
# 显示所选选项的标签
label_gas_r = ttk.Label(frame_overall_data_right, text="选择气体")
label_gas_r.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
# 创建 StringVar 来保存组合框的选定值
choosen_gas_opt = tk.StringVar(value=default_option)
# 创建组合框(下拉菜单)
options_gas_opt = ["空气", "氩气", "氦气", "氢气", "氮气", "二氧化碳", "氙气", "自定义"]
combobox_gas_opt = ttk.Combobox(frame_overall_data_right, textvariable=choosen_gas_opt, values=options_gas_opt, state="readonly", width=10)
combobox_gas_opt.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
label_custom_gas_info_opt = ttk.Label(frame_overall_data_right, text="自定义气体数据:")
label_custom_gas_info_opt.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
label_combobox1_opt = ttk.Label(frame_overall_data_right, text="比热比:")
label_combobox1_opt.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")
entry_combobox1_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_combobox1_opt.grid(row=4, column=2, pady=5)
entry_combobox1_opt.insert(0, "0")
label_combobox2_opt = ttk.Label(frame_overall_data_right, text="气体常数 [J/(kg*K)]:")
label_combobox2_opt.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")
entry_combobox2_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_combobox2_opt.grid(row=5, column=2, pady=5)
entry_combobox2_opt.insert(0, "0")
# 导向杆数据
label_launch_lenght_opt = ttk.Label(frame_overall_data_right, text="导向杆长度 [mm]:")
label_launch_lenght_opt.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")
entry_launch_lenght_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_launch_lenght_opt.grid(row=6, column=2, pady=5)
entry_launch_lenght_opt.insert(0, "0")
label_launch_diameter_opt = ttk.Label(frame_overall_data_right, text="导向杆内径 [mm]:")
label_launch_diameter_opt.grid(row=7, column=0, columnspan=2, pady=5, sticky="w")
entry_launch_diameter_opt = ttk.Entry(frame_overall_data_right, width=10)
entry_launch_diameter_opt.grid(row=7, column=2, pady=5)
entry_launch_diameter_opt.insert(0, "0")
# 右侧输入框架
frame_right = ttk.Frame(root, padding="10")
frame_right.grid(row=1, column=6, sticky=(tk.W, tk.E, tk.N, tk.S))
label_0_r = ttk.Label(frame_right, text="初始压强 [bar]:")
label_0_r.grid(row=0, column=0, columnspan=2, pady=5, sticky="w")
entry_0_r = ttk.Entry(frame_right, width=10)
entry_0_r.grid(row=0, column=2, pady=5)
entry_0_r.insert(0, "0")
label_1_r = ttk.Label(frame_right, text="喉部直径 [mm]:")
label_1_r.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
entry_1_r = ttk.Entry(frame_right, width=10)
entry_1_r.grid(row=1, column=2, pady=5)
entry_1_r.insert(0, "0")
label_2_r = ttk.Label(frame_right, text="总容积 [L]:")
label_2_r.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
entry_2_r = ttk.Entry(frame_right, width=10)
entry_2_r.grid(row=2, column=2, pady=5)
entry_2_r.insert(0, "0")
label_3_r = ttk.Label(frame_right, text="含水量 [%]:")
label_3_r.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
entry_3_r = ttk.Entry(frame_right, width=10)
entry_3_r.grid(row=3, column=2, pady=5)
entry_3_r.insert(0, "0")
label_4_r = ttk.Label(frame_right, text="干重 [kg]:")
label_4_r.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")
entry_4_r = ttk.Entry(frame_right, width=10)
entry_4_r.grid(row=4, column=2, pady=5)
entry_4_r.insert(0, "0")
label_5_r = ttk.Label(frame_right, text="初始温度 [°C]:")
label_5_r.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")
entry_5_r = ttk.Entry(frame_right, width=10)
entry_5_r.grid(row=5, column=2, pady=5)
entry_5_r.insert(0, "0")
# 自定义液体密度
label_6_r = ttk.Label(frame_right, text="液体密度 [kg/m³]:")
label_6_r.grid(row=6, column=0, columnspan=2, pady=5, sticky="w")
entry_6_r = ttk.Entry(frame_right, width=10)
entry_6_r.grid(row=6, column=2, pady=5)
entry_6_r.insert(0, "1000")
frame_button_r = ttk.Frame(root, padding="10")
frame_button_r.grid(row=1, column=7, sticky=(tk.W, tk.E, tk.N, tk.S))
# 绘图按钮
plot_button_r = ttk.Button(frame_button_r, text="绘制冲量", command=plot_opt_Ic)
plot_button_r.grid(row=1, column=0, columnspan=2, pady=5, sticky="w")
plot_button_r = ttk.Button(frame_button_r, text="绘制时间", command=plot_opt_tc)
plot_button_r.grid(row=2, column=0, columnspan=2, pady=5, sticky="w")
plot_button_r = ttk.Button(frame_button_r, text="绘制速度增量", command=plot_opt_delta_v)
plot_button_r.grid(row=3, column=0, columnspan=2, pady=5, sticky="w")
plot_button_r = ttk.Button(frame_button_r, text="绘制比冲", command=plot_opt_Ist)
plot_button_r.grid(row=4, column=0, columnspan=2, pady=5, sticky="w")
# 错误标签
error_label_r = ttk.Label(frame_button_r, text="", foreground="red")
error_label_r.grid(row=5, column=0, columnspan=2, pady=5, sticky="w")
# 右侧绘图区域
frame_plot_right = ttk.Frame(root, padding="10")
frame_plot_right.grid(row=0, column=4, columnspan=5, sticky=(tk.W, tk.E, tk.N, tk.S))
# 创建 Matplotlib 图形和画布嵌入到 Tkinter 窗口
fig_right, ax_right = plt.subplots(figsize=(screen_width / 200, screen_height / 150))
canvas_right = FigureCanvasTkAgg(fig_right, master=frame_plot_right)
canvas_widget_right = canvas_right.get_tk_widget()
canvas_widget_right.grid(row=0, column=0, columnspan=4, sticky=(tk.W, tk.E, tk.N, tk.S))
################################################
# 运行 Tkinter 事件循环
root.mainloop()下面替换原代码中的整个 simulate() 函数
# MAIN SIMULATION FUNCTION
def simulate():
global engine_dry_mass
global engine_full_mass
# Output variables declaration
Ic = float(0)
total_time = float(0)
delta_v = float(0)
gas_name = choosen_gas.get()
try:
if(gas_name == "Air"):
k_const = 1.4
Rs = 287
if(gas_name == "Argon"):
k_const = 1.67
Rs = 208
if(gas_name == "Helium"):
k_const = 1.66
Rs = 2077
if(gas_name == "Hydrogen"):
k_const = 1.41
Rs = 4124
if(gas_name == "Nitrogen"):
k_const = 1.4
Rs = 297
if(gas_name == "CO2"):
k_const = 1.3
Rs = 188
if(gas_name == "Xenon"):
k_const = 1.65
Rs = 208
if(gas_name == "Custom"):
k_const = float(entry_combobox1.get())
Rs = float(entry_combobox2.get())
water_density = float(entry_6.get())
# Clear total_time value
total_time = 0
Ic = 0
delta_v = 0
# Clear all arrays
array_time.clear()
array_h.clear()
array_Ft.clear()
array_mass.clear()
array_temperature.clear()
array_pressure.clear()
array_V_water.clear()
array_V_air.clear()
# Entry values read
P_ins = float(entry_0.get())*100000
At = 3.1415*pow(0.001*float(entry_1.get())/2, 2)
V_total = float(entry_2.get())*0.001
water_content = float(entry_3.get()) * 0.01
V_air = V_total - water_content * V_total
V_water = water_content * V_total
dry_mass = float(entry_4.get())
engine_dry_mass = dry_mass
T = float(entry_5.get())+273.15
rod_lenght = float(entry_launch_lenght.get()) * 0.001
rod_inside_diameter = float(entry_launch_diameter.get()) * 0.001
# Error safety net
if(P_ins <= 0):
raise ValueError
if(k_const <= 0):
raise ValueError
if(Rs <= 0):
raise ValueError
if(water_density <= 0):
raise ValueError
if(At <= 0):
raise ValueError
if(V_total <= 0):
raise ValueError
if(water_content < 0 or water_content >= 1):
raise ValueError
if(dry_mass <= 0):
raise ValueError
if(T <= 0):
raise ValueError
if(rod_inside_diameter < 0):
raise ValueError
if(rod_lenght < 0):
raise ValueError
if(rod_inside_diameter >= 0.001 * float(entry_1.get())):
raise ValueError
s = float(0.0)
vrod = float(0.0)
# V_air fix for existing rod
V_air = V_air - At * rod_lenght
# Define adiabatic constant
C_const = P_ins * pow(V_air, k_const)
# Calculate mass of air
mass_air = P_ins * V_air / (T * Rs)
mass_propelant = mass_air + V_water * water_density
Roc_mass = dry_mass + mass_propelant
engine_full_mass = Roc_mass
# ==================== 模拟阶段(保持原有逻辑)====================
if(rod_inside_diameter > 0 and rod_lenght > 0):
while(s < rod_lenght):
array_time.append(total_time)
total_time = total_time + delta_t
array_V_water.append(V_water)
array_V_air.append(V_air)
array_mass.append(Roc_mass)
array_pressure.append(P_ins)
array_temperature.append(T)
Ft = (P_ins * At) * efficiency_sim_rod
array_Ft.append(Ft)
# Ic += Ft * delta_t # 已删除
arod = Ft / Roc_mass
vrod += delta_t * arod
delta_v = vrod
s += vrod * delta_t + 0.5 * arod * delta_t * delta_t
if(rod_inside_diameter == 0 and rod_lenght > 0):
while(s < rod_lenght):
array_time.append(total_time)
total_time = total_time + delta_t
array_mass.append(Roc_mass)
array_V_air.append(V_air)
array_V_water.append(V_water)
array_temperature.append(T)
array_pressure.append(P_ins)
Ft = (P_ins * At) * efficiency_sim_rod
array_Ft.append(Ft)
# Ic += Ft * delta_t # 已删除
arod = Ft / Roc_mass
vrod = vrod + delta_t * arod
delta_v = vrod
s += vrod * delta_t + 0.5 * arod * delta_t * delta_t
delta_V = At * vrod * delta_t
V_air += delta_V
P_ins = C_const / pow(V_air, k_const)
T = (P_ins * V_air) / (mass_air * Rs)
# Loop for water phase
while(V_water > 0 and P_ins > P_atm):
array_time.append(total_time)
total_time = total_time + delta_t
Ft = (2 * At * (P_ins - P_atm)) * efficiency_sim_water
array_Ft.append(Ft)
# Ic += Ft * delta_t # 已删除
ve = math.sqrt(2 * (P_ins - P_atm) / water_density)
delta_V = At * ve * delta_t
array_V_water.append(V_water)
array_V_air.append(V_air)
T = P_ins * V_air / (mass_air * Rs)
array_temperature.append(T)
array_mass.append(Roc_mass)
Roc_mass = Roc_mass - delta_V * water_density
delta_v = delta_v + delta_t * Ft / Roc_mass
V_air = V_air + delta_V
V_water = V_water - delta_V
P_ins = C_const * pow(V_air, -k_const)
array_pressure.append(P_ins)
# Loop for Mach 1 on exit (choked flow)
while(P_ins > P_atm and P_ins/P_atm >= pow((k_const+1)/2, k_const/(k_const-1))):
array_time.append(total_time)
total_time = total_time + delta_t
array_V_water.append(V_water)
array_V_air.append(V_air)
Tt = T * 2 / (k_const + 1)
dot_m = P_ins * At * pow(2/(k_const+1), 0.5 * (k_const+1)/(k_const-1)) * math.sqrt(k_const/(Rs*T))
ve_air = math.sqrt(k_const * Rs * Tt)
P_throat = pow(2 / (k_const + 1), k_const / (k_const - 1)) * P_ins
Ft = (dot_m * ve_air + At * (P_throat - P_atm)) * efficiency_sim_gas
array_Ft.append(Ft)
# Ic += Ft * delta_t # 已删除
delta_m = dot_m * delta_t
Roc_mass = Roc_mass - delta_m
array_mass.append(Roc_mass)
delta_v = delta_v + delta_t * Ft / Roc_mass
mass_air = mass_air - delta_m
T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
array_temperature.append(T)
P_ins = mass_air * Rs * T / V_air
array_pressure.append(P_ins)
# Loop for sub Mach 1 on exit
while(P_ins > P_atm):
array_time.append(total_time)
total_time = total_time + delta_t
array_V_water.append(V_water)
array_V_air.append(V_air)
Mach = math.sqrt(2 / (k_const-1) * (pow(P_ins / P_atm, (k_const-1) / k_const) - 1))
Tt = T / (1 + 0.5 * (k_const-1) * Mach * Mach)
ve_air = Mach * math.sqrt(k_const * Rs * Tt)
density_chamber = mass_air / V_air
density_throat = density_chamber / pow(1 + 0.5 * (k_const - 1) * Mach * Mach, 1 / (k_const-1))
dot_m = At * density_throat * ve_air
Ft = (dot_m * ve_air) * efficiency_sim_gas
array_Ft.append(Ft)
# Ic += Ft * delta_t # 已删除
delta_m = dot_m * delta_t
Roc_mass = Roc_mass - delta_m
array_mass.append(Roc_mass)
delta_v = delta_v + delta_t * Ft / Roc_mass
mass_air = mass_air - delta_m
T = (P_ins / Rs) * pow(V_air / (mass_air + delta_m), k_const) * pow(V_air / mass_air, 1 - k_const)
array_temperature.append(T)
P_ins = mass_air * Rs * T / V_air
array_pressure.append(P_ins)
# ====================== 新增:梯形积分计算总冲 ======================
Ic = calculate_impulse_trapezoidal()
# Erase error message
error_label.config(text="")
# Print output values
text_widget_output = "Ic = {:.2f} Ns\n".format(Ic) + \
"tc = {:.2f} s\n".format(total_time) + \
"Ist = {:.2f} s\n".format(Ic / (mass_propelant * 9.81)) + \
"delta_v = {:.2f} m/s\n\n".format(delta_v)
text_widget.insert("1.0", text_widget_output)
plot_Ft()
except ValueError:
error_label.config(text="Invalid input.")
def calculate_impulse_trapezoidal():
if len(array_time) < 2 or len(array_Ft) < 2:
return 0.0
impulse = 0.0
for i in range(1, len(array_time)):
dt = array_time[i] - array_time[i-1]
if dt > 0:
impulse += 0.5 * (array_Ft[i-1] + array_Ft[i]) * dt
return impulse我有时间的话pyinstaller打包一下。
您还谈到了我的代码问题,我现在python只会写到循环,并且我想学一下TK库的使用。其他的话就是C语言的 STM32F103了,现在套装还没学完,我了解过Hal库,但是好像网上说建议先学标准库。我的角度计算很别扭是因为只学过个I2C通信计算MPU6050角速度…您说的F4系列好像401和407出名,如果有时间我也很想学。我先在其实很焦虑,代码写几行就不会了,没思路;买了十余本火箭的书,但是我的数理水平无法理解。您有什么建议吗?但是我发现 python和C的语言逻辑真的很像,或许在未来的某一天一通百通吧。
最后我想请您一个问题(可能描述有所磕绊):我想用现在的 STM32F103C8T6进行远距离通信,比如用 PWM的控制一个 SG90,您推荐用什么模块(就是我现在的环境,8266吗?),可以用笔计本电脑软件控制吗,我在这方面不太懂,希望您可以指点我一下,感谢大佬

感谢您的回复和鼓励,看到之后我也是思考了几天,在这里想和您讨论一下(望多多包涵)。首先我看了一下您对于软件积分的计算方法,又对比了一下我汉化的代码。由于我当时在AI帮助下选择了一种简单易行的方法,也方...
我看了一下代码,我觉得是没什么问题的,推进过程比我的要精确,不过没有瓶口修正项,也没有直接求解火箭的速度和高度,后续再估算可能就不准了,但是这个梯形积分是很有意义的。不过我现在还是认为仿真和实际差别不在于数值精度,我想到了损失水头这个东西,可能可以用它较准确估算Cd。我现在可能时间不多,我看看过几个小时或者明天能不能让GPT写一个。我现在先回答一些比较宽泛的东西。
首先是python以及TK,我是觉得如果只是用它做个上位机或者仿真程序,python学会语法就行,各种库不必都会。尤其是TK这样的前端库,这些可以全部交给AI。我认为在这个AI的时代,要求又只是能用,那么前端这种东西就不应该花太多时间。我之前也学过TK库,但是现在全忘了,全推给AI做。我觉得比较重要的库也就numpy和scipy这两个可以了解(仅仅是了解就行)最最基础的用法,比如花10分钟看一下,就行了。然后有几个我觉得比TK好用的库可以推荐一下,pyside6,UI更好看,方便实时曲线,pyqtgraph,可以用于实时曲线图,matplotlib,后期处理数据绘制更精致的图,还能做gif。我现在上位机就是用的它(但是现在我时间比较紧,飞控开发一直推迟……
我把全部搞好了会开源的)。
关于STM32的标准库和HAL库,一年前我会说标准库好,半年前我会说各有优劣,现在我会毫不犹豫地劝你使用HAL库,会方便很多很多,而且换一块STM32系列的芯片也能立刻上手,修改配置及其简单,什么FreeRTOS,FATFS的第三方库移植连复制粘贴都不需要。但是呢我还没找到讲HAL库有能和江协科技比的up……所以我推荐是先把江协科技的课听完然后用HAL库,最好用vscode加EIDE替代keil5(git,ai编程超级方便)。我现在用的是STM32F407VET6,用HAL库的话和F1系列没啥区别。而且它的性能足够强大,应该是STM32系列里最兼顾性能,价格和开发难度的了。不过你现在的飞控比较简单,可以先用着F1系列,什么时候性能不够用再说。
然后数学物理方面嘛,我看着你像中学生,我建议是不影响课内的情况下学一点高数线代,应该就行了,不用懂定理,只用会算,有必要看一点大物。然后火箭方面毕竟很复杂,一个人肯定搞不定的……我建议是先找到方向,比如飞控还是推进部分,然后再读相关的书。我现在也是和朋友合作,毕竟我发动机那方面的确不行。
关于编程,我是觉得python懂语法即可,都不用怎么写,我现在基本都没写过python了,全用的ai,但是C要了解,因为核心代码自己还是要看的。现在AI盛行,必须要利用这个机会加快自己的效率,我认为只是我们火箭板块的人是应该把自己的精力转到状态估计,控制,飞行规划等上层的算法上来,而不应该继续花大量时间做AI能轻松搞定的配置寄存器、设计通讯协议方面。
关于AI,因为我课内代码量也很大,所以我买了GPTplus,不过我觉得你现在的代码量并不大,所以用一些免费或者便宜的AI就行,毕竟GPT太贵了……但是就要求至少核心代码你要看一遍,检查问题要更仔细。
关于远距离通信,你要多远呢?我学校的项目就是几米内调车,我用的无线串口,搜一下,百元内,很便宜的,也很方便。但是如果要更远距离就要用lora模块了,一般是SPI的协议,不过底层交给AI就行,我飞控用的是E28-2G4M12SX,我要求的距离是1000m。当然这个你要平衡一下通讯链路和距离了,豁免的无线模块功率是否有限……不过很多违法的模块还在售卖,很多人都在用,emm……我觉得如果没必要就可以先不趟这个浑水了……
最后我想说件事,就是以后至少对我的回复用“你”就行了,其实我应该也就比你大几岁,我才大一呢,知识量也未必多多少
你可以试试这个,我让GPT融合了两个程序,然后加了一下损失水头,你可以根据你的喷管填一下,看看拟合效果好不好。
我看了一下代码,我觉得是没什么问题的,推进过程比我的要精确,不过没有瓶口修正项,也没有直接求解火箭的速度和高度,后续再估算可能就不准了,但是这个梯形积分是很有意义的。不过我现在还是认为仿真和实际差别不...
首先向您表示歉意,由于我每次回复您的消息真的要酌思许久。当然啦,还是称呼您为您,因为我粗略的算一下您比我大7-8岁左右,况且对我来说您真的是大佬
。没骗您,我现在python学的还是“3小时掌握python”,所以我似乎不具备手搓软件的能力,这也是我懊恼之处。您给我推荐的库我都下了一下,您特意提到matplotlib,这个我在知乎上看说不如原软件的效果好?但是我感觉科学绘图就够了,有时间我学一下。STM32这个星期我还没学,但是我了解过野火好像也讲得不错,未来也是个选择。关于火箭的目标,我就是想要通过考试离开我的家乡,去到达更多的平台,反正我感觉身旁是没有志同道合的人...您推荐怎么用VScode代替Keil5啊,当年我那这个东西配置个环境难死了,还启动慢,最后卸载了。最后发现又简单的IDLE更利于初学者(这只是我的偏见,望您包涵)。我最近想做个东西,可以使水火箭摆脱对发射台的依赖,通过远程舵机操控球阀控制推力,也方便点火。您看一下可行性,关键是想请教用什么无线电模块,方便我开发的,谢谢。嗯.....软件下一条评论再说,稍等下我
首先向您表示歉意,由于我每次回复您的消息真的要酌思许久。当然啦,还是称呼您为您,因为我粗略的算一下您比我大7-8岁左右,况且对我来说您真的是大佬[表情]。没骗您,我现在python学的还是“3小时掌握...
现在如果是做简单的上位机啊、计算器啊其实靠ai就行了,我做上位机真的没有写过一行python,主要是和ai描述清除要怎么做,再一点点改,没有codex这样的agent也没问题,这种小工程完全可以靠网页版的。
关于matplotlib我也没有具体了解,我只是很早就用的它,后面gpt也给我推荐了它,它的效果也不错,所以我才推荐的,如果有更好的也麻烦推荐给我一下哈。
关于VSCODE和EIDE的环境配置,我是大学的社团给了一份配置的详细教程,不过似乎不能外传哈,如果你觉得有必要用vscode的话可以私聊我我直接教你,其实不复杂。不过初学阶段用keil5也很正常,或者说大部分人都是这样的,我也是,我只是觉得vscode非常方便git和ai编程。你可以先学着,当开始开发一些比较复杂的工程再考虑用vscode。
关于无线模块,你要多远的控制距离呢?距离要求不高的话,基于nrf24l01的遥控串口是最方便的了,你完全可以把它当作有线的串口模块用,但是要注意一次不要发太多数据之类的,开始还要配置一下,不过很方便,问商家要资料即可。我在用的那款也就50多,很便宜。据商家说能有500m的通讯距离,我没试过,我也就5m内调试用,你可以试试,不行可以无理由退货。然后nrf24l01本身性能一样,协议还复杂,当然便宜,我是不推荐的。现在我用的是E28-2G4MSX,是lora模块,资料写的是3km,不过好像更麻烦,我现在还在处理这个的问题……还有和遥控串口差不多简单的,买那种遥控开关,然后单片机拾取电平,再控制舵机就行,这俩都一样,就是遥控开关可选的型号多不少。
我加了一下空气阻力,这是新的仿真程序。可是即便考虑空气阻力,仿真结果是理论上能飞到23.7m左右,不考虑甚至有30-40m。我之前说25m也是把牙管当成喷口了。你可以看看仿真结果。我也不知道是什么原因,如果加入空气阻力都有问题的话我只能怀疑中间的牙管了,毕竟很细,不管是对水还是对空气阻力都不小,如果要连接塑料瓶可能需要尝试别的方法,比如切开底面,插入后热熔,等等。你可以再检查下代码,我是没有发现任何问题。关于那个喷口选择的话,应该是选圆弧过渡喷嘴(但是没有考虑前面牙管的损失),材料的各种系数影响其实不大,可以保持默认。然后空气阻力Cd选择我是让gpt估算的,你可以根据描述选,我是看出现大幅偏转选了Cd=1.6,我个人是觉得不是精细设计的水火箭都要选这个,因为水火箭毕竟重心变化极大,和固体燃料火箭不一样。
如果有必要,你有空可以改一下它,加点辅助功能之类的。不过我最近可能会非常忙,学校的事情很多,可能要至少15号才会轻松点,但是后面又要继续搞我那边的飞控,所以可能最近都没时间了。
您好,打扰了(虽然您很忙,但…我才疏学浅.)。今天我才有时间用电脑,我填了一下发现还是这个水相等效的问题,我查询这个好像是流体阻力系数,如果填0.4到0.6效果便很接近真实。尝试让DS写了一下
但无奈对不上
如果您忙,可以有时间再回。我认为这个软件已经满足了很多的应用需求,毕竟不死磕牙管,这是创造世界记录的密封方式,碳纤维管+ PVC双头密封(我也想从这个方面创新),很新颖
谢谢
您好,打扰了(虽然您很忙,但…我才疏学浅.)。今天我才有时间用电脑,我填了一下发现还是这个水相等效的问题,我查询这个好像是流体阻力系数,如果填0.4到0.6效果便很接近真实。尝试让DS写了一下[附件]...
有个问题,是这样的我一开始不知道你的牙管是用于连接的,所以这里的是喷口,Cd相同的情况下,多一个牙管肯定会有影响,还有就是我让GPT看了下你给出的工程,是有很多错误的,比如它是按照多个并联的牙管喷出算的,你是串联的,而且水并不完全浸没,而且对水和对空气的阻力肯定不一样,还有一些公式明显的错误。我把具体的发上来。
另外这个大概率就是牙管的问题,但是我当前给你的那个文件没法分析这种连接,那个计算器认为损失只发生在喷口处。你可能是想做一个全局等效的Cd,但是这个难度真的挺大的,毕竟物理模型完全不同,未必能做这样的等效。我觉得要么换掉牙管,要么单独为了水火箭做一个程序,最好是跑仿真,但是我的确不会。你下次要发射水火箭的时候可以发我一下,我可以试试做一个简单的计算程序,或者我试试codex现在能不能帮忙跑仿真。