动车组牵引计算仿真系统的研究

1.前言

用了两周的时间,与电气学院的一个同学合作,开发出了这个“京津线城际动车组运行仿真系统”,他提业务,我根据他提出的业务,也就是需求,编程。系统所用的数据都来自京津城际的真实路线,比如坡道、弯道和限速数据。通过编程,模拟出列车运行的情况。

2.数据准备

列车在真实世界运行的过程中,速度、里程、加速度等都是连续的,而我想不出方法使得计算机能够模拟出连续的数据,于是我将采用离散的数据。系统包括以下几个计算模块:

  • 牵引力计算程序
  • 阻力计算程序
  • 制动力计算程序

2.1牵引力计算

动车组牵引计算仿真系统的研究_第1张图片


2.2阻力计算

阻力包括两个方面:1.基本空气阻力 2.附加阻力(坡道、弯道、桥隧 )

2.2.1基本空气阻力


2.2.2附加阻力

2.2.2.1坡道


2.2.2.2曲线



2.2.2.3隧道


2.3制动力计算


2.4合力计算

合力的计算分为三种情况:牵引、惰行和制动。

2.4.1牵引

作用于列车上的力有牵引力F和运行阻力W,其合力为:
                            C = F - W

2.4.2惰行

作用于列车上的只有列车运行阻力W,其合力为:
                           C = -W

2.4.3制动

作用于列车上的力有制动力B和运行阻力W,其合力为:
                          C = -B - W

2.5制动点

列车在运行的过程中,经过某个弯道的时候,弯道会有限速的要求,下面列出一些弯道的数据:

例如,在半径为600m的弯道,该弯道的长度为360.97m,要求列车限速105km/h。也就是说,当列车行驶到了这个弯道,在360.97m范围内,其速度都要限制在105km/h以下。那么这就涉及到一个提前制动的问题,比如第一个弯道,我们需要在弯道的七点0.15923km的前方找到一个制动点,使得列车从这个点开始制动,制动到0.15923km时,进入弯道的速度要在105km/h以下。
关于制动点的选择,我们是这样处理的:在弯道起点,逆向以制动力作为牵引力加速,起始速度为弯道限制速度,这样可以作出一条S-V图,该速度曲线图与列车正向行驶的S-V图的交点,就是列车需要提前制动的制动点。

2.6关于数据计算的关键代码

2.6.1牵引力计算

/**
 * 计算牵引力
 */
package com.crh.calculate;

public class TractionForce {
	// 定义Fst和F1
	private static double Fst = 261.43416 / 0.875;
	private static double F1 = 31680 / 119.1;

	// 计算牵引力(牵引状态下)
	public static double getTractionForce(double speed) {
		double f = 0.0;
		if (speed >= 0 && speed <= 119.1) {
			f = Fst - ((Fst - F1) / 119.1) * speed;
			return f;
		} else if (speed > 119.1 && speed <= 303) {
			f = 31680 / speed;
			return f;
		} else if (speed > 303) {
			// 返回-1.0,开始进入惰行
			return -1.0;
		} else {
			return 0;
		}
	}
}

2.6.2计算单位合力Cp

/**
 * 计算参数Cp
 */
package com.crh.calculate;

public class Cp {
	// 定义变量
	private static double C = 0;
	public static double Cp = 0;

	// 用于标定当前牵引的状态
	private static int trainFlag = 1;// 1代表牵引,0代表惰行,-1代表停车制动
	private static double tractionForce = 0;

	// 先求出C
	public static double getC(double speed) {
		if (trainFlag == 1) {// 牵引状态
			tractionForce = TractionForce.getTractionForce(speed);
			if (tractionForce == -1.0) {
				tractionForce = LazyDrive.getTractionForce(speed);
				trainFlag = 0;
			}
		} else if (trainFlag == 0) {// 惰行状态
			tractionForce = LazyDrive.getTractionForce(speed);
			if (tractionForce == -1.0) {
				tractionForce = TractionForce.getTractionForce(speed);
				trainFlag = 1;
			}
		} else if (trainFlag == -1) {// 停车制动状态

		}
		// 求C
		C = (tractionForce - AirFriction.getAirFriction(speed)) * 1000;
		return C;
	}

	// 再求出Cp
	public static double getCp(double speed) {
		Cp = (1000 * getC(speed)) / TrainWeight.CRH_WEIGHT;
		return Cp;
	}

	// 返回Cp
	public static double getCp() {
		return Cp;
	}

}

2.6.3弯道和坡道对单位合力Cp的影响

其实Cp可以理解成列车的加速度。在列车上坡的时候,Cp应该是要减去一个值,上坡的时候,Cp需要加上一个数值,同样,列车经过弯道的时候,无论是左拐弯还是右拐弯,都要减去一个值。
在这里,我们遇到了一个难点,这里涉及到判断列车是否进入下一个弯道或者坡道,如果进入了下一个,则Cp要相应调整,如果仍然在本弯道或者坡道,则Cp需要调整的值是不变的。于是就有了下面这段代码:
/**
 * 通过获取数据库中坡道和弯道的数据,修改Cp的数值
 */
package com.crh.service;

import java.util.List;

import com.crh.domain.Curve;
import com.crh.domain.Slope;

public class UpdateCp {
	// 定义两个List,分别存放坡道和弯道的数据
	// 计算牵引时候的情况
	private List<Slope> slopeList = null;
	private List<Curve> curveList = null;
	// 计算制动点时候的情况
	// private List<Slopeback> slopeList = null;
	// private List<Curveback> curveList = null;
	// 定义一个变量,用于记录下标
	private int slopeFlag = 0;
	private int curveFlag = 0;
	// 用于记录坡度值
	private double slope = 0;
	// 用于记录弯道值
	private double curve = 0;

	// 构造函数,初始化List
	public UpdateCp() {
		DataService ds = new DataService();
		// 计算牵引时候的情况
		slopeList = ds.getSlopeData();
		curveList = ds.getCurveData();

		// 计算制动点时候的情况
		// slopeList = ds.getSlopebackData();
		// curveList = ds.getCurvebackData();
	}

	// 弯道修改Cp
	public double byCurve(double Cp, double Sn, int index) {
		double newCp = Cp;
		double curveStart = 0;// 记录每段弯道的起点
		for (int i = index; i < curveList.size(); i++) {
			curveStart = curveList.get(i).getStart();
			if (Sn >= curveStart) {// 进入弯道
				curveFlag = i;
				curve = 600 / Math.abs(curveList.get(i).getRadius());
				newCp -= curve;
				break;
			}
		}
		return newCp;
	}

	// 判断是否到达下一个弯道,如果到达,则调用byCurve
	public boolean isGetNextCurve(double Sn, int index) {
		if (index >= curveList.size()) {
			return false;
		}
		if (Sn >= curveList.get(index).getStart()) {
			return true;// 到达
		} else {
			return false;// 未到达
		}
	}

	// 判断是在弯道内还是弯道外
	public boolean isInCurve(double Sn, int index) {
		if (Sn > curveList.get(index).getStart()
				+ curveList.get(index).getLength() / 1000) {
			return false;// 在弯道外
		} else {
			return true;// 在弯道内
		}
	}

	// 坡道修改Cp
	public double bySlope(double Cp, double Sn, int index) {
		double newCp = Cp;
		double slopeStart = 0;// 记录每段坡道的起点
		for (int i = index; i < slopeList.size(); i++) {
			slopeStart = slopeList.get(i).getEnd()
					- (slopeList.get(i).getLength() / 1000);
			// slopeStart = slopeList.get(i).getStart();
			if (Sn >= slopeStart) {
				slopeFlag = i;
				slope = slopeList.get(i).getSlope();
				newCp -= slope;
				break;
			}
		}
		return newCp;
	}

	// 判断是否到达下一个坡道,如果到达,则调用bySlope
	public boolean isGetNextSlope(double Sn, int index) {
		if (index >= slopeList.size()) {
			return false;
		}
		double startPoint = slopeList.get(index).getEnd()
				- (slopeList.get(index).getLength() / 1000);
		// double startPoint = slopeList.get(index).getStart();
		if (Sn >= (startPoint)) {
			return true;// 达到
		} else {
			return false;// 未到达
		}
	}

	public double getCurve() {
		return curve;
	}

	public double getSlope() {
		return slope;
	}

	public int getSlopeFlag() {
		return slopeFlag;
	}

	public int getCurveFlag() {
		return curveFlag;
	}
}

2.7计算结果

求出来的数据,保存到了txt文件中,总共有约20000条数据,我取的时间梯度是0.1秒,也就是每隔0.1秒获取列车运行的参数,包括时间,速度,里程,加速度这四项。再将这些数据导入到MySQL文件中,数据如下:
动车组牵引计算仿真系统的研究_第2张图片
其中id为主键,表示序号;runtime表示运行时间,单位时秒;speed是速度,单位km/h;distance是里程,单位时km;cp表示单位合力,单位是N/KN。

2.8数据计算部分的总结

数据计算部分,开始我以为会比较简单,但是我们用了大约一周左右的时间来完成数据的计算,主要瓶颈是中间的限速,以及最后的进站制动点的确定。这里要十分感谢我的搭档,他对业务十分熟悉,如果没有他的帮助,数据计算部分将会更加艰难的。
关于数据计算,并没有100%完全模拟真实环境,比如说,我们制动的时候,采用的是完全制动,而在实际情况下,列车的制动是分级的,不同级别的制动,其制动力是不同的。

3.编程模拟

程序用的是java语言,软件界面如下:
1)初始状态是这样的:
动车组牵引计算仿真系统的研究_第3张图片

2)运行起来是这样的:
动车组牵引计算仿真系统的研究_第4张图片

3.1实现方法概述

整个布局是BorderLayout布局,North部分,是一个表格,我没有什么好的办法,只能用画直线的方式,一条线一条线画出来,这部分的难点在于纵断面的画法。这也是本程序困扰我最大的地方。首先在数据库中存放好所有的坡路信息,然后将坡路数据转化成一个一个的点的坐标,再借助Graphics的drawPolygon方法,将每个点连成直线:
//将坡道信息,转化为一个一个坐标点
	public void infoToPoints(){
		//先放入起点坐标
		pointsX.add(x);
		pointsY.add(y);
		double infx = x,infy = y;//记录先前一个点的坐标,为记录坡道信息做准备
		for(int i=0;i<slopesList.size();i++){
			int t = i + 1;//探测下一个坡道是什么
			double px=0,py=0;//将要加入点的坐标
			Slopes slopes = slopesList.get(i);
			double end = slopes.getEnd()*1000;//坡道末端
			double slope = slopes.getSlope();
			if(slope == 0){//说明是平地
				px = x + end * para;
				py = y ;
				this.insertIntoList(px, py);
				this.checkNextSlope(t, px, py);
			}else if(slope > 0){//上坡
				px = x + end * para;
				py = y - 16;
				this.insertIntoList(px, py);
				this.checkNextSlope(t, px, py);
			}else if(slope < 0){//下坡
				px = x + end * para;
				py = y + 16;
				this.insertIntoList(px, py);
				this.checkNextSlope(t, px, py);
			}
			
			//设置坡道坐标
			this.insertIntoInfoList((px+infx)/2, (py+infy)/2);
			//重新给infx和infy赋值,使之每次都等于前一个坐标
			infx = px;
			infy = py;
			//放入坡道信息
			slopesInfo.add(slopes.getSlope()+", "+slopes.getLength());
		}
		//转化为数组
		this.toArray();
	}
这段代码,开始我一直以为我写不出来。后面工程做到这个地步,只剩下这最后一个难题,终于攻破了。这里要多谢梦伦同学提醒的drawPolygon()方法,之前我是不知道这个方法的。如果没有这个方法,这个难点怕是难以突破。

布局的Center部分是列车运行部分,这里我采取的策略是,小车不前进,而是让小车下面的轨道向后运动,由于相对运动,小车前进的感觉就出来了。
列车运动这一块,难点是列车的上下坡问题。应该是本项目中最难的一个地方。最后解决的办法是,列车的纵坐标根据的是它下方轨道的变化而变化。至于轨道的画法,用的是fillPolygon方法

布局下方部分,是一些参数的显示。其中速度仪表,用的是jfreechart。“×1 ×2 ×3”是变换刷新速度,也即调整列车运行的速度和里程的比例。

3.2总结

在最开始,我设计的轨道是这样的:轨道是一块一块的矩形,定义了一个RailWay类,每块矩形有宽度和高度,这样,所有的矩形连续放在一起,就组成了轨道。这样设计的理由是能很方便的得到经过列车下方的轨道的纵坐标,然后列车就能很方便的上下坡了。而且每个RailWay是一个线程,这样,跑完全程,我大概需要40000个线程。但是一运行,发现java虚拟机就报错,因为没有那么多内存来开辟这么多的线程。于是,我改用了另一种方法,只用一个线程,就解决了这个问题。
实现的方式是,一个线程负责去数据库中读取数据,然后根据数据,设置每块RailWay的坐标。
后面实现轨道时,用的新的方式,也就是上面说的fillPolygon方法来画轨道。
另一个需要总结的问题是,在线程中,需要调用repaint()方法,重绘图形。如果repaint()方法写在Thread.sleep(50)函数下方,将会出现有些RailWay不能绘出的情况,即出现空白的情况。后来将repaint()写在Thread.sleep(50)上方,空白地方就没有了,这个问题就解决了。
另外,这个程序,用了很多静态变量和静态方法,不知道对性能上会有什么影响。
在数据库访问方面,我用的是Hibernate来操作数据库。对于那两万条运行数据,我采用的是将数据一次性读出,常驻内存当中,提高性能。

你可能感兴趣的:(java,awt,动车组,运行仿真)