文号
950175
阅读
2397
回复
13
收藏
7
所有教程由网友发布,仅供参考,请谨慎采纳。科创不对教程的科学性、准确性、可靠性负责。
本文包含AI生成内容,并经过作者严格考据审查,作者对AI生成内容的品质和可靠性负责。
[归零]水火箭设计指导建议— H•3火箭飞行问题报告
H SH
摘要:对于水火箭飞行问题的阐述与解决方案,飞控分享
关键词:水火箭,飞控

一、前言

前段时间(大概是4月)发射了我的第一枚飞控水火箭H•3,以下是发射视频

1982_raw.mp4 点击下载

从视频中可以看出飞控的工作状况还是不错的,完成了开伞时序。但是不妨可以发现火箭基本的动力阶段出现了问题:首先飞行高度较低,仅有7~11米左右,其次火箭在落地前一刻仍在喷射。我在发射之后略显沉闷,想解决这个问题并找到原因,于是脑中即有两个方案,一是通过Ansys,二是计算证明。显然我选择后者,意在通deepseek协助完成(当然不要担心,我发帖就是因为这是可以采用的)


开始之前,先了解一下我的火箭动力段组成

4x560ml健力宝碳酸饮料瓶

image.jpg

灯具中的牙管,内径8mm

IMG_1528.jpeg

好,以下进入正文部分


二、计算示例与公式(重要)

先准备好参数,如图1778993700798.png

依次是水,气,瓶内压力,大气压,水密度,比热比,牙管直径,干重,水重,发射时质量


在计算冲量$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]$


IMG_1570.jpeg

注:在这里大家关注一下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

我对这个软件进行了汉化并计算了上述冲量

attachment iconOpenwaterrocket.exe61.05MBEXE17次下载

748.png

可以看到冲量很接近,感兴趣可以导入我的.eng查看

attachment iconH-3.eng314.21KBENG0次下载


在这里考虑一下牙管的飞行高度损失

牙管($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$


1779023674526.png


此处可以理解为当水喷完后,水火箭内部气压等于外部大气压(注:此公式贴图可能大小实兀,见谅)


而牙管与喷口之间的最佳关系为


$\boxed{d_{\text{喷}} = d_{\text{管}}}$


或者可以选择d喷小于d管(总之要小于连接管内径),可以有效减小压力损失,这次火箭便是一个大忌。



四、建模sldprt和代码文件

分享几个部件的Solidworks建模,图片按顺序为示意图

attachment icon头锥.SLDPRT135.64KBSLDPRT5次下载

attachment icon尾翼.SLDPRT183.81KBSLDPRT0次下载

attachment icon连接段.SLDPRT163.37KBSLDPRT0次下载

屏幕截图 2026-05-20 212554.png屏幕截图 2026-05-20 212658.png屏幕截图 2026-05-20 212803.png

飞控简述

主控 STM32F103C8T6

陀螺仪 MPU6050

舵机 SG90

TF卡槽

电源 VCC( 4.2V和 3.3V)

这款飞控严格意义上仅完成了基本的开伞时序,而没有完成Fatfs MPU6050数据采集,如果感兴趣,可以看一下立创EDA工程和原理图

1779364734586.pngimage.jpg

attachment iconH3火箭飞控.eprj976.00KBEPRJ1次下载

attachment iconSCH_Schematic1_2025-08-04.pdf89.76KBPDF3次下载预览

分享一个SD卡的相关资料,或许对大家写Fatfs有帮助

attachment iconSD卡资料.zip11.95MBZIP3次下载

以下这是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


attachment icon1.0代码.zip8.09MBZIP1次下载

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轴向上,即IMG_1580.jpeg

便与文中相同

屏幕截图 2026-05-20 212930.png

如果Z轴垂直向上,即

IMG_1573.jpeg

此处代码要改为

YawAngle = atan2f(AX, AZ) * 180.0f / 3.1415926535f;
RollAngle = atan2f(AY, AZ) * 180.0f / 3.1415926535f;

而Y轴向上,要改为

IMG_1581.jpeg

YawAngle = atan2f(AX, AY) * 180.0f / 3.1415926535f;
RollAngle = atan2f(AZ, AY) * 180.0f / 3.1415926535f;

效果如视频

a6ae7939-5bcd-4d16-8da6-593b07569b74.jpegcf11250b-91ab-4c3d-b688-8be93515976b.jpeg

VID_20260207_203553.mp4 点击下载

希望有帮助




五、结语

差不多写完了这篇帖子,也想借机向大家展示在如今,AI可以帮助我们完成许多繁琐的计算,大为改善效率。在次引用一句我的一位朋友的话:“判断是数据说真话的前提。”希望大家在未来不要凭手感,要相信数据和计算。也希望我的建议可以帮助大家。That's all,thank you!



H·3(2025.1~2026.5)


End

[ 修改于3个月18天前 - 2026/07/14 13:52:22 ]
+1
科创币
Always_with_myself
2026/08/23
好文
 
置顶回复

FalconHSH作者
5个月9天前IP:山西
950224
2楼

大家可以积极回复哦,前10名给科创币

FalconHSH作者
5个月14天前修改于 5个月9天前IP:山西
950193
1楼

在这里补充一下飞行高度估值

速度增量


$\Delta v = \frac{I_{\text{水}}}{\bar{m}}$


而竖直上抛最大高度(无空气阻力)


$H = \frac{(\Delta v)^2}{2g}$


如果大家这里想加阻力可以乘阻力系数(不同的头锥不同,0.5~0.8左右),这样结果就与真实相近些。



想问大家玩水火箭一个问题:有没有网店可以定制塑料瓶的,5个左右可以做的?劳妨推荐一下,谢谢。



引用
评论
FalconHSH作者
5个月9天前IP:山西
950224
2楼

大家可以积极回复哦,前10名给科创币

引用
评论
5个月8天前修改于 5个月8天前IP:广东
950247
3楼

前几天看到了,不过这几天很忙没时间回,今天有点时间我仔细看了一下。

首先我觉得楼主在推力与总冲的计算上是没有问题的,用了一些近似,认为喷嘴面积远小于内部截面积。如果要考虑喷嘴面积,那么火箭加速度会带来一阶修正,而流体力学会带来二阶修正。我对水火箭不是很了解,不过如果补上和喷嘴面积有关的量应该会好一些,毕竟这对计算机来说计算量不算什么,当然,这不一定能减小误差,因为更复杂的流体力学效应与火箭的偏转影响会更大。

其次是通过总冲判断飞行高度和初速度的,这里我觉得问题可能挺大,只能用作估算。首先由于内部气体是绝热膨胀,火箭质量变化不是线性的,此时用平均质量不是很合理,另外由实验视频可以看出,喷水时间和飞行时间是同一数量级,用瞬间工作的模型来估计是不合理的。论坛常见的固体燃料火箭工作时间相比飞行时间小了至少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生成了一个计算程序,比较简陋,也没考虑开伞啥的,因为我现在没啥时间,你可以拿到程序后继续改改。

attachment iconwater_rocket.py19.83KBPY2次下载

attachment iconwater_rocket.exe39.53MBEXE0次下载

我使用你的数据计算了一下,发现理论高度大概25m,总冲17.6N·s,很接近,我开始还以为会有很大的差异。我最近没什么时间,现在也不太确定是计算器的问题还是就是这样,如果是就是这样的话,那么你的模型其实工程上完全可用,但是要考虑的是发射倾斜,内部流体阻力等等问题,这个就不确定了,楼主可以在此基础上进一步研究。我觉得空气阻力反而不是很大,因为速度很低。

仿真.png

这个程序是理想化的,而且没有考虑气体种类选择,牙管阻力等等,楼主可以试试只移植计算部分来修改,大概有10%到20%的精度提升,这样你的程序可能会更好。还可以给个建议,就是用多文件程序,这样结构更清晰,不过我这个也是单文件的程序。

+1
科创币
FalconHSH
2026/05/25
感谢大佬,我细看一下,感谢
引用
评论(4)
1
5个月8天前IP:广东
950248
4楼

再给个飞控方面的建议,当前main.c中东西太多了,我的建议是用一个新的文件承载飞行的状态机,然后main.c只调用接口即可。当前stm32f103加逻辑绰绰有余,如果后续要进一步的话,建议改为f4甚至h7系列,然后用上freeRTOS。还有HAL库也可以了解一下,是非常方便的,毕竟后续就要把更多精力放在算法而不是硬件配置上。

还有mpu6050,如果只用加速度夹角判断欧拉角只适用于静态,动态下会有问题,如果不加相关算法的话精度会低。还有这里yaw和pitch的定义似乎和常见的不同,自己写飞控的话,我还是建议判断逻辑用四元数,毕竟不存在奇点,还不会像当前这样判断区间是四棱锥而不是圆锥。欧拉角主要传回来给人看。不过具体要改到什么程度就要看你的经济与时间预算了,当前还是工作得很好的。

引用
评论(1)
1
FalconHSH作者
5个月5天前IP:山西
950286
5楼
引用tmn_A发表于3楼的内容
前几天看到了,不过这几天很忙没时间回,今天有点时间我仔细看了一下。首先我觉得楼主在推力与总冲的计算上是没有问题的,用了一些近似,认为喷嘴面积远小于内部截面积。如果要考虑喷嘴面积,那么火箭加速度会带来一...

感谢您的回复和鼓励,看到之后我也是思考了几天,在这里想和您讨论一下(望多多包涵)。首先我看了一下您对于软件积分的计算方法,又对比了一下我汉化的代码。由于我当时在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吗?),可以用笔计本电脑软件控制吗,我在这方面不太懂,希望您可以指点我一下,感谢大佬1f64f1f64f




引用
评论
5个月4天前IP:山西
950295
6楼
引用FalconHSH发表于5楼的内容
感谢您的回复和鼓励,看到之后我也是思考了几天,在这里想和您讨论一下(望多多包涵)。首先我看了一下您对于软件积分的计算方法,又对比了一下我汉化的代码。由于我当时在AI帮助下选择了一种简单易行的方法,也方...

无线通信,如果你不想折腾的话用那种无线串口模块就行了,市面上不少,诸如HC-12/GC433等。

+1
科创币
FalconHSH
2026/05/30
谢谢大佬回复 HC-05行吗
引用
评论(2)
1
5个月3天前IP:广东
950302
7楼
引用FalconHSH发表于5楼的内容
感谢您的回复和鼓励,看到之后我也是思考了几天,在这里想和您讨论一下(望多多包涵)。首先我看了一下您对于软件积分的计算方法,又对比了一下我汉化的代码。由于我当时在AI帮助下选择了一种简单易行的方法,也方...

我看了一下代码,我觉得是没什么问题的,推进过程比我的要精确,不过没有瓶口修正项,也没有直接求解火箭的速度和高度,后续再估算可能就不准了,但是这个梯形积分是很有意义的。不过我现在还是认为仿真和实际差别不在于数值精度,我想到了损失水头这个东西,可能可以用它较准确估算Cd。我现在可能时间不多,我看看过几个小时或者明天能不能让GPT写一个。我现在先回答一些比较宽泛的东西。

首先是python以及TK,我是觉得如果只是用它做个上位机或者仿真程序,python学会语法就行,各种库不必都会。尤其是TK这样的前端库,这些可以全部交给AI。我认为在这个AI的时代,要求又只是能用,那么前端这种东西就不应该花太多时间。我之前也学过TK库,但是现在全忘了,全推给AI做。我觉得比较重要的库也就numpy和scipy这两个可以了解(仅仅是了解就行)最最基础的用法,比如花10分钟看一下,就行了。然后有几个我觉得比TK好用的库可以推荐一下,pyside6,UI更好看,方便实时曲线,pyqtgraph,可以用于实时曲线图,matplotlib,后期处理数据绘制更精致的图,还能做gif。我现在上位机就是用的它(但是现在我时间比较紧,飞控开发一直推迟……1f62d我把全部搞好了会开源的)。

关于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……我觉得如果没必要就可以先不趟这个浑水了……

最后我想说件事,就是以后至少对我的回复用“你”就行了,其实我应该也就比你大几岁,我才大一呢,知识量也未必多多少1f602

引用
评论
2
5个月3天前IP:广东
950305
8楼

attachment iconwater_rocket_complete_sim.zip49.04KBZIP5次下载

你可以试试这个,我让GPT融合了两个程序,然后加了一下损失水头,你可以根据你的喷管填一下,看看拟合效果好不好。

+1
科创币
FalconHSH
2026/06/02
必须感谢
引用
评论(6)
1
FalconHSH作者
5个月0天前IP:山西
950369
9楼
引用tmn_A发表于7楼的内容
我看了一下代码,我觉得是没什么问题的,推进过程比我的要精确,不过没有瓶口修正项,也没有直接求解火箭的速度和高度,后续再估算可能就不准了,但是这个梯形积分是很有意义的。不过我现在还是认为仿真和实际差别不...

首先向您表示歉意,由于我每次回复您的消息真的要酌思许久。当然啦,还是称呼您为您,因为我粗略的算一下您比我大7-8岁左右,况且对我来说您真的是大佬1f601。没骗您,我现在python学的还是“3小时掌握python”,所以我似乎不具备手搓软件的能力,这也是我懊恼之处。您给我推荐的库我都下了一下,您特意提到matplotlib,这个我在知乎上看说不如原软件的效果好?但是我感觉科学绘图就够了,有时间我学一下。STM32这个星期我还没学,但是我了解过野火好像也讲得不错,未来也是个选择。关于火箭的目标,我就是想要通过考试离开我的家乡,去到达更多的平台,反正我感觉身旁是没有志同道合的人...您推荐怎么用VScode代替Keil5啊,当年我那这个东西配置个环境难死了,还启动慢,最后卸载了。最后发现又简单的IDLE更利于初学者(这只是我的偏见,望您包涵)。我最近想做个东西,可以使水火箭摆脱对发射台的依赖,通过远程舵机操控球阀控制推力,也方便点火。您看一下可行性,关键是想请教用什么无线电模块,方便我开发的,谢谢。嗯.....软件下一条评论再说,稍等下我

引用
评论
4个月29天前IP:广东
950398
10楼
引用FalconHSH发表于9楼的内容
首先向您表示歉意,由于我每次回复您的消息真的要酌思许久。当然啦,还是称呼您为您,因为我粗略的算一下您比我大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,不过好像更麻烦,我现在还在处理这个的问题……还有和遥控串口差不多简单的,买那种遥控开关,然后单片机拾取电平,再控制舵机就行,这俩都一样,就是遥控开关可选的型号多不少。

引用
评论
1
4个月23天前IP:广东
950490
11楼

我加了一下空气阻力,这是新的仿真程序。可是即便考虑空气阻力,仿真结果是理论上能飞到23.7m左右,不考虑甚至有30-40m。我之前说25m也是把牙管当成喷口了。你可以看看仿真结果。我也不知道是什么原因,如果加入空气阻力都有问题的话我只能怀疑中间的牙管了,毕竟很细,不管是对水还是对空气阻力都不小,如果要连接塑料瓶可能需要尝试别的方法,比如切开底面,插入后热熔,等等。你可以再检查下代码,我是没有发现任何问题。关于那个喷口选择的话,应该是选圆弧过渡喷嘴(但是没有考虑前面牙管的损失),材料的各种系数影响其实不大,可以保持默认。然后空气阻力Cd选择我是让gpt估算的,你可以根据描述选,我是看出现大幅偏转选了Cd=1.6,我个人是觉得不是精细设计的水火箭都要选这个,因为水火箭毕竟重心变化极大,和固体燃料火箭不一样。

attachment iconwater_rocket_complete_sim.zip65.94KBZIP1次下载

image.jpg

如果有必要,你有空可以改一下它,加点辅助功能之类的。不过我最近可能会非常忙,学校的事情很多,可能要至少15号才会轻松点,但是后面又要继续搞我那边的飞控,所以可能最近都没时间了。

引用
评论(4)
1
FalconHSH作者
4个月18天前修改于 4个月18天前IP:山西
950565
12楼

您好,打扰了(虽然您很忙,但…我才疏学浅.)。今天我才有时间用电脑,我填了一下发现还是这个水相等效的问题,我查询这个好像是流体阻力系数,如果填0.4到0.6效果便很接近真实。尝试让DS写了一下

attachment icondeepseek_python_20260614_061ca7.py5.78KBPY1次下载

但无奈对不上

屏幕截图 2026-06-14 191102.png

屏幕截图 2026-06-14 185413.png

如果您忙,可以有时间再回。我认为这个软件已经满足了很多的应用需求,毕竟不死磕牙管,这是创造世界记录的密封方式,碳纤维管+ PVC双头密封(我也想从这个方面创新),很新颖

attachment iconthesis_ebe_2019_mawire_nyasha_nigel.pdf8.91MBPDF6次下载预览

谢谢

引用
评论
2个月28天前IP:美国
951470
13楼
引用FalconHSH发表于12楼的内容
您好,打扰了(虽然您很忙,但…我才疏学浅.)。今天我才有时间用电脑,我填了一下发现还是这个水相等效的问题,我查询这个好像是流体阻力系数,如果填0.4到0.6效果便很接近真实。尝试让DS写了一下[附件]...

有个问题,是这样的我一开始不知道你的牙管是用于连接的,所以这里的是喷口,Cd相同的情况下,多一个牙管肯定会有影响,还有就是我让GPT看了下你给出的工程,是有很多错误的,比如它是按照多个并联的牙管喷出算的,你是串联的,而且水并不完全浸没,而且对水和对空气的阻力肯定不一样,还有一些公式明显的错误。我把具体的发上来。

attachment icon管道Cd计算程序问题总结.txt10.32KBTXT2次下载

另外这个大概率就是牙管的问题,但是我当前给你的那个文件没法分析这种连接,那个计算器认为损失只发生在喷口处。你可能是想做一个全局等效的Cd,但是这个难度真的挺大的,毕竟物理模型完全不同,未必能做这样的等效。我觉得要么换掉牙管,要么单独为了水火箭做一个程序,最好是跑仿真,但是我的确不会。你下次要发射水火箭的时候可以发我一下,我可以试试做一个简单的计算程序,或者我试试codex现在能不能帮忙跑仿真。

引用
评论(6)
1
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的