A题 储油罐的变位识别与罐容表标定
通常加油站都有若干个储存燃油的地下储油罐,并且一般都有与之配套的"油位计量管理系统",采用流量计和油位计来测量进/出油量与罐内油位高度等数据,通过预先标定的罐容表(即罐内油位高度与储油量的对应关系)进行实时计算,以得到罐内油位高度和储油量的变化情况。
许多储油罐在使用一段时间后,由于地基变形等原因,使罐体的位置会发生纵向倾斜和横向偏转等变化(以下称为变位),从而导致罐容表发生改变。按照有关规定,需要定期对罐容表进行重新标定。图1是一种典型的储油罐尺寸及形状示意图,其主体为圆柱体,两端为球冠体。图2是其罐体纵向倾斜变位的示意图,图3是罐体横向偏转变位的截面示意图。
请你们用数学建模方法研究解决储油罐的变位识别与罐容表标定的问题。
(1)为了掌握罐体变位后对罐容表的影响,利用如图4的小椭圆型储油罐(两端平头的椭圆柱体),分别对罐体无变位和倾斜角为a =4.10的纵向变位两种情况做了实验,实验数据如附件1所示。请建立数学模型研究罐体变位后对罐容表的影响,并给出罐体变位后油位高度间隔为1cm的罐容表标定值。
(2)对于图1所示的实际储油罐,试建立罐体变位后标定罐容表的数学模型,即罐内储油量与油位高度及变位参数(纵向倾斜角度a 和横向偏转角度b )之间的一般关系。请利用罐体变位后在进/出油过程中的实际检测数据(附件2),根据你们所建立的数学模型确定变位参数,并给出罐体变位后油位高度间隔为10cm的罐容表标定值。进一步利用附件2中的实际检测数据来分析检验你们模型的正确性与方法的可靠性。




目录
[3.1 数据预处理](#3.1 数据预处理)
[3.2 数学模型建立](#3.2 数学模型建立)
[3.3 倾斜变位模型](#3.3 倾斜变位模型)
[3.4 误差修正优化](#3.4 误差修正优化)
[4.1 无变位进油情况分析](#4.1 无变位进油情况分析)
[4.2 倾斜变位进油情况分析](#4.2 倾斜变位进油情况分析)
[4.3 误差修正优化结果](#4.3 误差修正优化结果)
一、项目背景
储油罐在使用过程中由于温度变化、地基沉降等原因,会产生变位,导致油位高度与实际储油量之间的对应关系发生变化。本项目基于2010年全国大学生数学建模竞赛A题,通过Python实现了储油罐标定问题的数学建模与求解。
项目主要解决三个核心问题:
- 问题一:无变位情况下,建立油位高度与储油量的数学模型
- 问题二:倾斜变位情况下,建立修正后的数学模型
- 问题三:通过误差分析,优化模型精度
二、技术架构
本项目采用Python 3.9+作为开发语言,主要使用以下技术栈:
|----------------|------------|
| 技术组件 | 用途说明 |
| NumPy | 数组运算、数值计算 |
| Matplotlib | 数据可视化、图表生成 |
| SciPy | 曲线拟合、科学计算 |
三、核心代码解析
3.1 数据预处理
首先对原始数据进行单位转换,将毫米单位转换为米单位,便于后续计算:
python
# 将毫米单位转化为米单位
h1 = h / 1000
使用NumPy数组进行向量化运算,提高计算效率:
python
v = np.array([312, 362, 412, ...])
h = np.array([159.02, 176.14, ...])
3.2 数学模型建立
无变位情况下,储油罐内油的体积V与油面高度h的关系模型:
python
V = (1.78/1.2) * 2.45 * (
(h1 - 0.6) * np.sqrt(h1 * (1.2 - h1)) +
0.36 * np.arcsin(h1/0.6 - 1) +
0.5 * np.pi * 0.36
)
代码解析:
- 1.78/1.2:椭圆截面长宽比修正系数
- 2.45:储油罐长度(米)
- h1 - 0.6:相对于椭圆中心的油面高度
- np.sqrt(h1 * (1.2 - h1)):椭圆截面弓形面积的几何项
- np.arcsin():椭圆积分项
3.3 倾斜变位模型
考虑倾斜角度后,需要引入倾斜修正参数:
python
# 储油罐参数
a = 0.89 # 椭圆长半轴
b = 0.6 # 椭圆短半轴
l = 2.45 # 罐体长度
# 倾斜角度修正
c = np.tan((4.1 * np.pi) / 180) # 4.1度倾斜角
3.4 误差修正优化
通过线性拟合对模型误差进行修正:
python
# 计算模型误差
v2 = V - v/1000
# 一次多项式拟合误差
a = np.polyfit(h1, v2, 1)
b = a[0] * h1 + a[1]
# 修正后的体积
V1 = V - b
四、运行结果展示
4.1 无变位进油情况分析
无变位情况下,模型预测与实际数据的对比如下:

结果分析:
- 平均误差:0.075525 m³
- 最大误差:0.138452 m³
- 模型整体拟合较好,误差在可接受范围内
4.2 倾斜变位进油情况分析
考虑4.1度倾斜角后,修正模型的表现:

结果分析:
- 平均误差:0.076606 m³
- 最大误差:0.091006 m³
- 倾斜修正后,最大误差显著降低
4.3 误差修正优化结果
通过线性拟合修正后,模型精度大幅提升:

关键成果:
- 线性拟合系数:斜率 = 0.134933,截距 = -0.012031
- 修正前平均绝对误差:0.075525 m³
- 修正后平均绝对误差:0.001539 m³
- 误差减小:97.96% ✨
五 、项目总结
本项目成功将MATLAB代码转换为Python实现,完成了储油罐标定问题的数学建模与求解。主要成果包括:
- ✅ 建立了无变位和倾斜变位两种情况下的数学模型
- ✅ 通过误差分析,将模型精度提升97.96%
- ✅ 生成了详细的可视化结果图表
- ✅ 实现了跨平台代码转换(MATLAB → Python)
- ✅ 解决了中文字体显示和编码问题
项目代码结构清晰、注释完善,可作为数学建模Python实现的参考案例。
六 、参考代码
代码仅供参考,相关精确度不做保障。
python
"""
储油罐标定问题 - 2010年全国大学生数学建模竞赛A题
Python版本解决方案
包含三个程序:
1. 程序一: 无变位情况下的体积与高度关系分析
2. 程序二: 倾斜变位情况下的体积与高度关系分析
3. 程序三: 误差修正后的模型
"""
import sys
import io
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# 设置标准输出编码为UTF-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
def program1():
"""
程序一: 无变位进油情况分析
"""
print("=" * 60)
print("程序一: 无变位进油情况分析")
print("=" * 60)
# 无变位进油中累加进油量数据分别加上罐内初始值 262 升得 v
v = np.array([312, 362, 412, 462, 512, 562, 612, 662, 712, 762, 812, 862, 912, 962,
1012, 1062, 1112, 1162, 1212, 1262, 1312, 1362, 1412, 1462, 1512, 1562,
1612, 1662, 1712, 1762, 1812, 1862, 1912, 1962, 2012, 2062, 2112, 2162,
2212, 2262, 2312, 2315.83, 2365.83, 2367.06, 2417.06, 2467.06, 2517.06,
2567.06, 2617.06, 2666.98, 2668.83, 2718.83, 2768.83, 2818.83, 2868.83,
2918.83, 2968.83, 3018.83, 3068.83, 3118.83, 3168.83, 3168.91, 3218.91,
3268.91, 3318.91, 3368.91, 3418.91, 3468.91, 3518.91, 3568.91, 3618.91,
3668.91, 3718.91, 3768.91, 3818.91, 3868.91, 3918.91, 3968.91])
# 无变位进油中 h 的数据
h = np.array([159.02, 176.14, 192.59, 208.5, 223.93, 238.97, 253.66, 268.04, 282.16,
296.03, 309.69, 323.15, 336.44, 349.57, 362.56, 375.42, 388.16, 400.79,
413.32, 425.76, 438.12, 450.4, 462.62, 474.78, 486.89, 498.95, 510.97,
522.95, 534.9, 546.82, 558.72, 570.61, 582.48, 594.35, 606.22, 618.09,
629.96, 641.85, 653.75, 665.67, 677.63, 678.54, 690.53, 690.82, 702.85,
714.91, 727.03, 739.19, 751.42, 763.7, 764.16, 776.53, 788.99, 801.54,
814.19, 826.95, 839.83, 852.84, 866, 879.32, 892.82, 892.84, 906.53,
920.45, 934.61, 949.05, 963.8, 978.91, 994.43, 1010.43, 1026.99, 1044.25,
1062.37, 1081.59, 1102.33, 1125.32, 1152.36, 1193.49])
# 将毫米单位转化为米单位
h1 = h / 1000
# 建模所得小储油罐内油的体积 V 与油面高度 h 的关系
# 原MATLAB公式: V=(1.78/1.2)*2.45*((h1-0.6).*(sqrt(h1.*(1.2-h1)))+0.36*asin(h1/0.6-1)+0.5*pi*0.36)
# 注意: (1.78/1.2) 和 0.6 存疑,原代码中有语法错误,这里按照公式结构修正
# 假设椭圆截面: 长半轴a=0.89, 短半轴b=0.6, 长度L=2.45
# 体积公式修正
V = (1.78/1.2) * 2.45 * ((h1 - 0.6) * np.sqrt(h1 * (1.2 - h1)) +
0.36 * np.arcsin(h1/0.6 - 1) + 0.5 * np.pi * 0.36)
# 建模所得小储油罐内油的体积 v 与实际体积之差
v2 = V - v/1000
# 计算误差统计
mean_error = np.mean(v2)
max_error = np.max(np.abs(v2))
print(f"平均误差: {mean_error:.6f} m³")
print(f"最大误差: {max_error:.6f} m³")
# 绘图
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(h/1000, v/1000, 'b-', linewidth=2, label='实际数据')
plt.plot(h1, V, 'r--', linewidth=2, label='模型预测')
plt.xlabel('高度 H/m')
plt.ylabel('体积 V/m^3')
plt.title('无变位进油情况 - 实际数据与模型对比')
plt.legend()
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(h1, v2, 'g-', linewidth=2)
plt.xlabel('高度 H/m')
plt.ylabel('误差/m^3')
plt.title('模型误差')
plt.grid(True)
plt.tight_layout()
plt.savefig('program1_result.png', dpi=150, bbox_inches='tight')
plt.close() # 关闭图形,不显示
return v, h, V, v2
def program2():
"""
程序二: 倾斜变位进油情况分析
"""
print("\n" + "=" * 60)
print("程序二: 倾斜变位进油情况分析")
print("=" * 60)
# 附表一倾斜变位进油中油位高度数据并将毫米单位转化为米单位
h1 = np.array([0.41129, 0.42345, 0.43833, 0.45054, 0.4639, 0.47774, 0.48937, 0.50256,
0.51469, 0.52684, 0.53888, 0.55196, 0.5644, 0.57656, 0.58874, 0.59956,
0.61162, 0.62344, 0.63558, 0.64628, 0.65859, 0.67022, 0.68063, 0.69303,
0.70467, 0.71645, 0.72766, 0.73939, 0.7509, 0.76155, 0.77343, 0.78539,
0.79604, 0.80827, 0.8208, 0.8328, 0.84447, 0.85629, 0.8676, 0.88006,
0.89292, 0.90434, 0.91734, 0.9299, 0.94142, 0.9546, 0.96809, 0.98014,
0.99241, 1.00634, 1.01907, 1.03424, 1.03536])
# 倾斜变位进油中累加进油量数据分别加上罐内初始值 215 升并将升单位转化为立方米
v1 = np.array([0.96286, 1.01286, 1.06286, 1.11286, 1.16286, 1.21286, 1.26286, 1.31279,
1.36279, 1.41273, 1.46273, 1.51273, 1.56273, 1.61273, 1.66273, 1.71273,
1.76273, 1.81273, 1.86273, 1.91273, 1.96273, 2.01273, 2.06273, 2.11273,
2.16273, 2.21273, 2.26273, 2.31273, 2.36273, 2.41273, 2.46273, 2.51273,
2.56273, 2.61273, 2.66273, 2.71273, 2.76273, 2.81273, 2.86273, 2.91273,
2.96273, 3.01273, 3.06273, 3.11273, 3.16273, 3.21273, 3.26273, 3.31273,
3.36273, 3.41273, 3.46273, 3.51273, 3.51474])
# 小储油罐横截面椭圆参数
a = 0.89 # 长半轴长
b = 0.6 # 短半轴长
l = 2.45 # 长度
# 倾斜角度参数
c = np.tan((4.1 * np.pi) / 180)
d = h1 + 0.4 * c - b
c1 = c / b
m = d / b
# 建模所得小储油罐内油的体积 v 与油面高度 h1 的关系
# 注意: 原MATLAB代码中有省略号(...)表示续行,这里补全公式
v = (a/b) * ((1/(3*c)) * ((b**2 - (d - c*l)**2)**(3/2) - (b**2 - d**2)**(3/2)) +
(b**2) * ((-(1 - m**2 + 2*c1*m*l - c1**2 * l**2)**(1/2) +
(l*c1 - m) * np.arcsin(m - c1*l) + (1 - m**2)**(1/2) + m * np.arcsin(m)) / c1) +
0.5 * np.pi * b * b * l)
# 建模所得小储油罐内油的体积 v 与实际体积之差
v2 = v - v1
# 计算平均误差
mean_error = np.mean(v2)
max_error = np.max(np.abs(v2))
print(f"平均误差: {mean_error:.6f} m³")
print(f"最大误差: {max_error:.6f} m³")
# 绘图
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(h1, v, 'r-', linewidth=2, label='模型预测')
plt.plot(h1, v1, 'b--', linewidth=2, label='实际数据')
plt.xlabel('高度 H/m')
plt.ylabel('体积 V/m^3')
plt.title('倾斜变位进油情况 - 实际数据与模型对比')
plt.legend()
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(h1, v2, 'g-', linewidth=2)
plt.xlabel('高度 H/m')
plt.ylabel('误差/m^3')
plt.title('模型误差')
plt.grid(True)
plt.tight_layout()
plt.savefig('program2_result.png', dpi=150, bbox_inches='tight')
plt.close() # 关闭图形,不显示
return h1, v1, v, v2
def program3():
"""
程序三: 误差修正后的模型
"""
print("\n" + "=" * 60)
print("程序三: 误差修正后的模型")
print("=" * 60)
# 无变位进油中累加进油量数据
v = np.array([312, 362, 412, 462, 512, 562, 612, 662, 712, 762, 812, 862, 912, 962,
1012, 1062, 1112, 1162, 1212, 1262, 1312, 1362, 1412, 1462, 1512, 1562,
1612, 1662, 1712, 1762, 1812, 1862, 1912, 1962, 2012, 2062, 2112, 2162,
2212, 2262, 2312, 2315.83, 2365.83, 2367.06, 2417.06, 2467.06, 2517.06,
2567.06, 2617.06, 2666.98, 2668.83, 2718.83, 2768.83, 2818.83, 2868.83,
2918.83, 2968.83, 3018.83, 3068.83, 3118.83, 3168.83, 3168.91, 3218.91,
3268.91, 3318.91, 3368.91, 3418.91, 3468.91, 3518.91, 3568.91, 3618.91,
3668.91, 3718.91, 3768.91, 3818.91, 3868.91, 3918.91, 3968.91])
# 无变位进油中 h 的数据
h = np.array([159.02, 176.14, 192.59, 208.5, 223.93, 238.97, 253.66, 268.04, 282.16,
296.03, 309.69, 323.15, 336.44, 349.57, 362.56, 375.42, 388.16, 400.79,
413.32, 425.76, 438.12, 450.4, 462.62, 474.78, 486.89, 498.95, 510.97,
522.95, 534.9, 546.82, 558.72, 570.61, 582.48, 594.35, 606.22, 618.09,
629.96, 641.85, 653.75, 665.67, 677.63, 678.54, 690.53, 690.82, 702.85,
714.91, 727.03, 739.19, 751.42, 763.7, 764.16, 776.53, 788.99, 801.54,
814.19, 826.95, 839.83, 852.84, 866, 879.32, 892.82, 892.84, 906.53,
920.45, 934.61, 949.05, 963.8, 978.91, 994.43, 1010.43, 1026.99, 1044.25,
1062.37, 1081.59, 1102.33, 1125.32, 1152.36, 1193.49])
# 将毫米单位转化为米单位
h1 = h / 1000
# 建模所得小储油罐内油的体积 V 与油面高度 h 的关系
V = (1.78/1.2) * 2.45 * ((h1 - 0.6) * np.sqrt(h1 * (1.2 - h1)) +
0.36 * np.arcsin(h1/0.6 - 1) + 0.5 * np.pi * 0.36)
# 建模所得小储油罐内油的体积 v 与实际体积之差
v2 = V - v/1000
# 拟合误差与液面高度的关系
# 使用一次多项式拟合
a = np.polyfit(h1, v2, 1)
print(f"线性拟合系数: 斜率 = {a[0]:.6f}, 截距 = {a[1]:.6f}")
# 得到的拟合曲线
b = a[0] * h1 + a[1]
# 修正建模所得小储油罐内油的体积 V 与油面高度 h 的关系
V1 = V - b
# 建模所得小储油罐内油的体积 V1 与实际体积之差
v3 = V1 - v/1000
# 计算修正后的误差统计
mean_error_before = np.mean(np.abs(v2))
mean_error_after = np.mean(np.abs(v3))
print(f"\n修正前平均绝对误差: {mean_error_before:.6f} m³")
print(f"修正后平均绝对误差: {mean_error_after:.6f} m³")
print(f"误差减小: {(1 - mean_error_after/mean_error_before)*100:.2f}%")
# 绘图
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(h/1000, v/1000, 'b-', linewidth=2, label='实际数据')
plt.plot(h1, V1, 'r--', linewidth=2, label='修正后模型')
plt.xlabel('高度 H/m')
plt.ylabel('体积 V/m^3')
plt.title('无变位进油情况 - 修正后模型与实际数据对比')
plt.legend()
plt.grid(True)
plt.subplot(2, 1, 2)
plt.plot(h1, v2, 'r-', linewidth=2, label='修正前误差')
plt.plot(h1, v3, 'g-', linewidth=2, label='修正后误差')
plt.xlabel('高度 H/m')
plt.ylabel('误差/m^3')
plt.title('模型误差对比')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.savefig('program3_result.png', dpi=150, bbox_inches='tight')
plt.close() # 关闭图形,不显示
return v, h, V, V1, v2, v3, a
def main():
"""
主函数: 运行所有程序
"""
print("=" * 60)
print("储油罐标定问题 - 2010年全国大学生数学建模竞赛A题")
print("=" * 60)
print()
# 运行程序一
v, h, V, v2 = program1()
# 运行程序二
h1, v1, v_model, v2_tilted = program2()
# 运行程序三
v, h, V, V1, v2, v3, fit_coef = program3()
print("\n" + "=" * 60)
print("所有程序运行完成!")
print("结果图已保存为 program1_result.png, program2_result.png, program3_result.png")
print("=" * 60)
if __name__ == "__main__":
main()