更新日期:2020/5/17
【注1】其中的代码也许并不完整,您可以作为伪码参看,或者您可以去我主博客逛逛,也许有意外之喜!
【注2】此篇博客是 C# 编程笔记 的子博客。
上一篇: 坐标转换 四参数/正形变换/七参数 回到 主博客 下一篇:—
3、曲线线路坐标计算
(我来现学现卖一下,2020/4/6)
道路的走向和线形受到各种因素的制约,线路在平面上或竖直面上不可能一条直线。平面上的称为平曲线,竖直面上的坡度变化称为竖曲线。下面对圆曲线和带缓和曲线的圆曲线的算法做一个简要的介绍。
3.1 圆曲线
3.1.1 原理
圆曲线的构成如下图所示:
主点有:直圆点、圆直点、曲中点、交点;用字母表示为:YZ, ZY, QZ, JD
主要的曲线要素有:线路转向角α
、圆曲线半径R
、切线长T
(交点至直圆点或圆直点的长度)、曲线长L
(由直圆点经中点至圆直点的弧长)、外矢距E
(交点至圆曲线中点QZ
的距离)和切曲差q
(切线长和曲线之差)称圆曲线的曲线要素。只要知道了圆曲线的上述6个要素就可以计算和实地放样了。
实际上,只要知道R
和α
就可以知道所有的曲线要素了。
至此,基本元素已经计算完成,下面开始根据所给点的里程计算坐标。关于里程,这里多说一句。里程格式:DK a+b m
;其中a
代表公里数,b
代表不足一公里的小数,整体来看就是当前点(按照路线走)到原点的距离为:1000*a+b (m)
。
第一步:计算线点独立坐标
如图,以ZY
为原点,ZY->O
为y轴,圆切线为x轴;以YZ
为原点,YZ->O
为y轴,圆切线为x轴分别建立平面直角坐标系。圆曲线上一点i在其中的坐标称之为其独立坐标。为什么要建立两个坐标系呢?其实是为了和计算带缓和曲线的圆曲线坐标相照应,圆曲线上无论哪点在两个坐标系上算出的精度都是一样的(理直气壮:我们老师说的!)。所以编程时就只用了左边的坐标系。
ZY
到i点的弧长Li
为
3.1.2 代码实现:
/*此函数用于计算简单圆曲线
* 输入:R 半径; a 线路转向角; JD 交点里程; DQ 代求点里程数组; YZD 已知点数组; pd 前定向(q) 右偏(r)(默认)
* 输出:row1: 0 半径; 1 偏角; 2 T; 3 L; 4 E; 5 q; 6 JD; 7 ZY; 8 QZ; 9 YZ; 10 代求点个数; 11 12 圆直点坐标;
* row2: 0 1 交点坐标; 2 3 定向点坐标; 4 5 直圆点坐标; 6.. 代求点里程
* row3: 各个点的x, y坐标(独立)
* row4: 各个点的X, Y坐标(线路)
*/
public static double[,] YQX_D(double R, Angle a, DK JD, DK[] DQ, Point[] YZD, string pd = "qr")
{
pd = pd.ToLower();
char dx = pd[0]; if (dx != 'h') dx = 'q';//默认前定向
char px = pd[1]; if (px != 'l') px = 'r';//默认右偏
int mod = 1; if (px == 'l') mod = -1;//左右偏向的状态参量
int m, n = DQ.Length;//代求坐标个数
if (n > 6) m = 2 * n; else m = 13;
double[,] result_dArray = new double[4, m];
Angle a2 = a / 2;
double T = R * a2.tan;//圆曲线切线长
double L = R * a.rad;//曲线长
double E = R * (a2.sec - 1);//曲线外矢距
double q = 2 * T - L;//切曲差
DK ZY = JD - T;//直圆点里程
DK QZ = ZY + L / 2;//曲中点里程
DK YZ = ZY + L;//圆直点里程
result_dArray[0, 0] = R;
result_dArray[0, 1] = a.rad; ;
result_dArray[0, 2] = T;
result_dArray[0, 3] = L;
result_dArray[0, 4] = E;
result_dArray[0, 5] = q;
result_dArray[0, 6] = JD.a;
result_dArray[0, 7] = ZY.a;
result_dArray[0, 8] = QZ.a;
result_dArray[0, 9] = YZ.a;
result_dArray[0, 10] = n;
Point p_JD = YZD[0];//YZD[0]为交点
Point p_ZD = YZD[1];//YZD[1]为控制点
result_dArray[1, 0] = p_JD.x;
result_dArray[1, 1] = p_JD.y;
result_dArray[1, 2] = p_ZD.x;
result_dArray[1, 3] = p_ZD.y;
for (int i = 0; i < n; i++)
{
result_dArray[1, 6 + i] = DQ[i].a;
}
Angle _a, _a1, PI = new Angle(Math.PI);
if (dx == 'q')
{
_a = new Angle(arctan((p_JD.x - p_ZD.x), (p_JD.y - p_ZD.y)));//定向角
if (px == 'r')//右偏
_a1 = _a + a + PI;
else
_a1 = _a - a + PI;
}
else
{
_a1 = new Angle(arctan((p_JD.x - p_ZD.x), (p_JD.y - p_ZD.y)));//定向角
if (px == 'r')//右偏
_a = _a1 - a - PI;
else
_a = _a1 + a + PI;
}
Matrix _R = (-_a).R2, _R1 = (-_a1).R2;//旋转矩阵
Point p_ZY = new Point(), p_YZ = new Point();
p_ZY.x = p_JD.x - T * _a.cos; p_ZY.y = p_JD.y - T * _a.sin;
p_YZ.x = p_JD.x - T * _a1.cos; p_YZ.y = p_JD.y - T * _a1.sin;
result_dArray[0, 11] = p_YZ.x; result_dArray[0, 12] = p_YZ.y;
result_dArray[1, 4] = p_ZY.x; result_dArray[1, 5] = p_ZY.y;
for (int i = 0; i < n; i++)
{
DK dq = DQ[i];
if (dq < ZY || dq > YZ) continue;//不在圆曲线上的不用这个算
double Li;
Angle fi;
Matrix zb = new Matrix(2, 1), ZB;
if (dq < QZ)
{
Li = dq.a - ZY.a;
fi = new Angle(Li / R);
zb[0, 0] = R * fi.sin;//独立坐标
zb[1, 0] = R * (1 - fi.cos);
result_dArray[2, 2 * i] = zb[0, 0];
result_dArray[2, 2 * i + 1] = zb[1, 0];
zb[1, 0] *= mod;
ZB = _R * zb;
result_dArray[3, 2 * i] = p_ZY.x + ZB[0, 0];
result_dArray[3, 2 * i + 1] = p_ZY.y + ZB[1, 0];
}
else
{
Li = YZ.a - dq.a;
fi = new Angle(Li / R);
zb[0, 0] = R * fi.sin;//独立坐标
zb[1, 0] = R * (1 - fi.cos);
result_dArray[2, 2 * i] = zb[0, 0];
result_dArray[2, 2 * i + 1] = zb[1, 0];
zb[1, 0] *= (-1);
zb[1, 0] *= mod;
ZB = _R1 * zb;
result_dArray[3, 2 * i] = p_YZ.x + ZB[0, 0];
result_dArray[3, 2 * i + 1] = p_YZ.y + ZB[1, 0];
}
}
return result_dArray;
}
/*此函数用于输出 简单圆曲线计算结果
*/
public static void OutPut_D(double[,] M)
{
int n = (int)M[0, 10];
Console.WriteLine("R={0:f3} 半径", M[0, 0]);
Console.WriteLine("a={0:f3} 线路转向角", M[0, 1]);
Console.WriteLine("T={0:f3} 切线长", M[0, 2]);
Console.WriteLine("L={0:f3} 曲线长", M[0, 3]);
Console.WriteLine("E={0:f3} 外矢距", M[0, 4]);
Console.WriteLine("q={0:f3} 切曲差", M[0, 5]);
Console.WriteLine("KJD={0:f3} 交点里程", M[0, 6]);
Console.WriteLine("KZY={0:f3} 直圆点里程", M[0, 7]);
Console.WriteLine("KQZ={0:f3} 曲中点里程", M[0, 8]);
Console.WriteLine("KYZ={0:f3} 圆直点里程", M[0, 9]);
Console.WriteLine("JD X={0:f4}, Y={1:f4} JD坐标", M[1, 0], M[1, 1]);
Console.WriteLine("ZD X={0:f4}, Y={1:f4} ZD坐标", M[1, 2], M[1, 3]);
Console.WriteLine("ZY X={0:f4}, Y={1:f4} ZY坐标", M[1, 4], M[1, 5]);
Console.WriteLine("YZ X={0:f4}, Y={1:f4} YZ坐标", M[0, 11], M[0, 12]);
Console.WriteLine();
Console.WriteLine("n={0:f3} 代求点个数", M[0, 10]);
for (int i = 0; i < n; i++)
{
Console.WriteLine("nu:{0} K={1:f3}, x={2:f4}, y={3:f4} ", i + 1, M[1,6+i], M[2, 2 * i], M[2, 2 * i+1]);
Console.WriteLine(" X={0:f4}, Y={1:f4} ", M[3, 2 * i], M[3, 2 * i + 1]);
}
}
算例:
3.1.3 调用示例:
double R = 490; //半径
Angle a = new Angle(26, 58, 31);//偏向角
DK JD = new DK(1, 523.600); //交点里程
DK[] DQ = new DK[1]; //带求坐标里程
DQ[0] = new DK(1, 430); //右偏时用到
//DQ[0] = new DK(1, 612.842); //左偏时用到
Point[] yzd = new Point[2]; //已知点坐标 0 JD坐标 1 定向点坐标
yzd[0] = new Point(9018.059, 3665.385);
yzd[1] = new Point(8936.445, 3517.359);//右前、左后用到
//yzd[1] = new Point(9023.059, 3816.519); //右后、左前用到
double[,] M = curveLine.YQX_D(R, a, JD, DQ, yzd, "qr");
curveLine.OutPut_D(M);
Console.Read();
程序输出结果更详细了,下图是2.0版本,代码是3.0版本
3.2 带缓和曲线的圆曲线
3.2.1 原理
缓和曲线是直线与圆曲线之间或半径相差较大的两个转向相同的圆曲线之间介入的一段曲率半径由∞渐变至圆曲线半径R
的一种线型,它起缓和及过渡的作用。
如图,其主点有:直缓点ZH
、缓圆点HY
、曲中点QZ
、圆缓点YH
、缓直点HZ
曲线参数有:β0
为缓和曲线的切线角,即缓和曲线所对的中心角。自圆心向直缓点ZH
或缓直点HZ
的切线作垂线OC
和OD
,并将圆曲线两端延长至垂线,则: m
为直缓点ZH
(或缓直点HZ
)至垂足的距离,称为切垂距(也称切线增量); P
为垂线长OC
或OD
与圆曲线半径R
之差,称为圆曲线内移量。
曲线综合要素有:线路转向角a
、圆曲线半径R
、缓和曲线长Ls
,、切线长TH
、曲线长LH
、外矢距EH和切曲差q
。即在圆曲线的曲线要素基础上加缓和曲线长Ls
。
已知线路转向角a
、圆曲线半径R
、缓和曲线长Ls
后,可计算:
至此,基本元素已经计算完成,下面开始根据所给点的里程计算坐标。
第一步:计算线点独立坐标
如图:以ZH
为原点,ZH->O
为y轴,切线为x轴;以HZ
为原点,HZ->O
为y轴,切线为x轴分别建立平面直角坐标系。现在用两个坐标系就十分有必要了,以QZ
为分界线,下分别介绍圆曲线上一点i在其中的坐标计算方法。
X’O’Y’
中,i点至ZH
的弧长Li
为
第二步:计算ZH、HZ
点坐标
设JD
的线路坐标为(XJD,YJD)
ZH
到JD
点的线路坐标方位角为αZH
,HZ
点到JD
点的线路坐标方位角为αHZ
,所以ZY、YZ
点线路坐标为:
这里必须要说明一个问题:无论是简单圆曲线,还是带缓和曲线的圆曲线,线路有向左偏的,有向右偏的,为了将代码做的更加完整,必须要考虑清楚如下问题:
右偏前定向和左偏后定向得到的位置相对关系是一样的,实际上,很少有后定向的情况,因为我们是用已知放样未知,我们用的肯定是已经在实地存在的点来放样规划图上的点。在图中具体可以得到什么信息这里就不在赘述(我怕自己又绕进去)
详细介绍可以参看教材:《工程测量学》第二版 张正禄主编
P114
始
3.2.2 代码实现:
/*此函数用于计算带缓和曲线的圆曲线
* 输入:R 半径; a 线路转向角; Ls 缓和曲线长; JD 交点里程; DQ 代求点里程数组; YZD 已知点数组; pd 前定向(q) 右偏(r)(默认)
* 输出:row1: 0 半径; 1 偏角; 2 m; 3 P; 4 beta0; 5 TH; 6 Ls; 7 Lh; 8 Lt; 9 E; 10 q
* 11 JD; 12 ZH; 13 HY; 14 QZ; 15 YH; 16 HZ; 17 代求点个数
* row2: 0 1 交点坐标; 2 3 定向点坐标; 4 5 直缓点坐标; 6 7 缓直点坐标; 8.. 代求点里程
* row3: 各个点的x, y坐标(独立)
* row4: 各个点的X, Y坐标(线路)
*/
public static double[,] YQX_H(double R, Angle a, double Ls, DK JD, DK[] DQ, Point[] YZD, string pd = "qr")
{
pd = pd.ToLower();
char dx = pd[0]; if (dx != 'h') dx = 'q';//默认前定向 防止输错
char px = pd[1]; if (px != 'l') px = 'r';//默认右偏
int mod = 1; if (px == 'l') mod = -1;//左右偏向的状态参量
int num, n = DQ.Length;//代求坐标个数
if (n > 9) num = 2 * n; else num = 18;
double[,] result_dArray = new double[4, num];
Angle a2 = a / 2;
double m = Ls / 2 - P(Ls, 3) / (240 * R * R),//切垂距
P1 = Ls * Ls / (24 * R),//圆曲线内移值
beta0 = Ls / (2 * R);//缓和曲线切线角
double T = (R + P1) * a2.tan + m;//切线长
double Lh = R * (a.rad - 2 * beta0) + 2 * Ls;//整个曲线长
double Lt = Lh - 2 * Ls;//圆曲线长
double E = (R + P1) * a2.sec - R;//曲线外矢距
double q = 2 * T - Lh;//切曲差
DK ZH = JD - T;//直缓点里程
DK HY = ZH + Ls;//缓圆点里程
DK QZ = ZH + Lh * 0.5;//曲中点里程
DK YH = HY + Lt;//圆直点里程
DK HZ = YH + Ls;//缓直点里程
result_dArray[0, 0] = R;
result_dArray[0, 1] = a.rad;
result_dArray[0, 2] = m;
result_dArray[0, 3] = P1;
result_dArray[0, 4] = beta0;
result_dArray[0, 5] = T;
result_dArray[0, 6] = Ls;
result_dArray[0, 7] = Lh;
result_dArray[0, 8] = Lt;
result_dArray[0, 9] = E;
result_dArray[0, 10] = q;
result_dArray[0, 11] = JD.a;
result_dArray[0, 12] = ZH.a;
result_dArray[0, 13] = HY.a;
result_dArray[0, 14] = QZ.a;
result_dArray[0, 15] = YH.a;
result_dArray[0, 16] = HZ.a;
result_dArray[0, 17] = n;
Point p_JD = YZD[0];//YZD[0]为交点
Point p_ZD = YZD[1];//YZD[1]为定向点
result_dArray[1, 0] = p_JD.x;
result_dArray[1, 1] = p_JD.y;
result_dArray[1, 2] = p_ZD.x;
result_dArray[1, 3] = p_ZD.y;
for (int i = 0; i < n; i++)
{
result_dArray[1, 8 + i] = DQ[i].a;
}
Angle _a, _a1, PI = new Angle(Math.PI);
if (dx == 'q')
{
_a = new Angle(arctan((p_JD.x - p_ZD.x), (p_JD.y - p_ZD.y)));//定向角
if (px == 'r')//右偏
_a1 = _a + a + PI;
else
_a1 = _a - a + PI;
}
else
{
_a1 = new Angle(arctan((p_JD.x - p_ZD.x), (p_JD.y - p_ZD.y)));//定向角
if (px == 'r')//右偏
_a = _a1 - a + PI;
else
_a = _a1 + a + PI;
}
Matrix _R = (-_a).R2, _R1 = (-_a1).R2;//旋转矩阵
Point p_ZH = new Point(), p_HZ = new Point();
p_ZH.x = p_JD.x - T * _a.cos; p_ZH.y = p_JD.y - T * _a.sin;
p_HZ.x = p_JD.x - T * _a1.cos; p_HZ.y = p_JD.y - T * _a1.sin;
result_dArray[1, 4] = p_ZH.x;
result_dArray[1, 5] = p_ZH.y;
result_dArray[1, 6] = p_HZ.x;
result_dArray[1, 7] = p_HZ.y;
for (int i = 0; i < n; i++)
{
DK dq = DQ[i];
if (dq < ZH || dq > HZ) continue;//不在曲线上的不用这个算
Matrix zb = new Matrix(2, 1), ZB;//独立坐标
double Li; Angle fi;
if (dq < QZ)
{//前半段
if (dq < HY)
{//在缓和曲线上
Li = dq.a - ZH.a;
zb[0, 0] = Li - P(Li, 5) / (40 * R * R * Ls * Ls);
zb[1, 0] = P(Li, 3) / (6 * R * Ls);
}
else
{//在圆曲线上
Li = dq.a - ZH.a;
fi = new Angle((Li - 0.5 * Ls) / R);
zb[0, 0] = m + R * fi.sin;//独立坐标
zb[1, 0] = P1 + R * (1 - fi.cos);
}
result_dArray[2, 2 * i] = zb[0, 0];
result_dArray[2, 2 * i + 1] = zb[1, 0];
zb[1, 0] *= mod;
ZB = _R * zb;
result_dArray[3, 2 * i] = p_ZH.x + ZB[0, 0];
result_dArray[3, 2 * i + 1] = p_ZH.y + ZB[1, 0];
}
else
{//后半段
if (dq > YH)
{//在缓和曲线上
Li = HZ.a - dq.a;
zb[0, 0] = Li - P(Li, 5) / (40 * R * R * Ls * Ls);
zb[1, 0] = P(Li, 3) / (6 * R * Ls);
}
else
{//在圆曲线上
Li = HZ.a - dq.a;
fi = new Angle((Li - 0.5 * Ls) / R);
zb[0, 0] = m + R * fi.sin;//独立坐标
zb[1, 0] = P1 + R * (1 - fi.cos);
}
result_dArray[2, 2 * i] = zb[0, 0];
result_dArray[2, 2 * i + 1] = zb[1, 0];
zb[1, 0] = -zb[1, 0];//后半段
zb[1, 0] *= mod; //左右偏
ZB = _R1 * zb;
result_dArray[3, 2 * i] = p_HZ.x + ZB[0, 0];
result_dArray[3, 2 * i + 1] = p_HZ.y + ZB[1, 0];
}
}
return result_dArray;
}
/*此函数用于输出 带缓和曲线的圆曲线计算结果
*/
public static void OutPut_H(double[,] M)
{
int n = (int)M[0, 17];
Console.WriteLine("R={0:f3} 半径", M[0, 0]);
Console.WriteLine("a={0:f3} 线路转向角", M[0, 1]);
Console.WriteLine("m={0:f3} 切垂距", M[0, 2]);
Console.WriteLine("P={0:f3} 圆曲线内移量", M[0, 3]);
Console.WriteLine("beta0={0:f3} 缓和曲线的切线角", M[0, 4]);
Console.WriteLine("TH={0:f3} 切线长", M[0, 5]);
Console.WriteLine("Ls={0:f3} 缓和曲线长", M[0, 6]);
Console.WriteLine("LH={0:f3} 曲线长", M[0, 7]);
Console.WriteLine("Lt={0:f3} 圆曲线长", M[0, 8]);
Console.WriteLine("E={0:f3} 外矢距", M[0, 9]);
Console.WriteLine("q={0:f3} 切曲差", M[0, 10]);
Console.WriteLine("KJD={0:f3} 交点里程", M[0, 11]);
Console.WriteLine("KZH={0:f3} 直缓点里程", M[0, 12]);
Console.WriteLine("KHY={0:f3} 缓圆点里程", M[0, 13]);
Console.WriteLine("KQZ={0:f3} 曲中点里程", M[0, 14]);
Console.WriteLine("KYH={0:f3} 圆缓点里程", M[0, 15]);
Console.WriteLine("KHZ={0:f3} 缓直点里程", M[0, 16]);
Console.WriteLine("JD X={0:f4}, Y={1:f4} JD坐标", M[1, 0], M[1, 1]);
Console.WriteLine("ZD X={0:f4}, Y={1:f4} ZD坐标", M[1, 2], M[1, 3]);
Console.WriteLine("ZH X={0:f4}, Y={1:f4} ZH坐标", M[1, 4], M[1, 5]);
Console.WriteLine("HZ X={0:f4}, Y={1:f4} HZ坐标", M[1, 6], M[1, 7]);
Console.WriteLine();
Console.WriteLine("n={0:f3} 代求点个数", M[0, 17]);
for (int i = 0; i < n; i++)
{
Console.WriteLine("nu:{0} K={1:f3}, x={2:f4}, y={3:f4} ", i + 1, M[1, 8 + i], M[2, 2 * i], M[2, 2 * i + 1]);
Console.WriteLine(" X={0:f4}, Y={1:f4} ", M[3, 2 * i], M[3, 2 * i + 1]);
}
}
算例:
3.2.3 使用示例:
double R = 854.4; //半径
Angle a = new Angle(42, 18, 25.2);//转向角
double Ls = 85; //缓和曲线长
DK JD = new DK(24, 471.762); //交点里程
DK[] DQ = new DK[1]; //代求点里程数组
DQ[0] = new DK(24, 750);//右偏里程
//DQ[0] = new DK(24, 162.94);//左偏里程
Point[] yzd = new Point[2]; //已知点坐标 0 JD坐标 1 定向点坐标
yzd[0] = new Point(399606.2286, 539444.3196);
yzd[1] = new Point(399589.5506, 538373.6835);//右前、左后
//yzd[1] = new Point(399106.2286,540011.1685);//右后、左前
double[,] M = curveLine.YQX_H(R, a, Ls, JD, DQ, yzd, "qr");
curveLine.OutPut_H(M);
Console.Read();
程序输出结果更详细了,下图是2.0版本,代码是3.0版本
里面所用角类和里程类链接:
https://blog.csdn.net/Gou_Hailong/article/details/95803595
程序框架图:
上一篇: 坐标转换 四参数/正形变换/七参数 回到 主博客 下一篇:—
【注1】其中的代码也许并不完整,您可以作为伪码参看,或者您可以去我主博客逛逛,也许有意外之喜!
【注2】此篇博客是 C# 编程笔记 的子博客。
【注3】由于博主水平有限,程序可能存在漏洞或bug, 如有发现,请尽快与博主联系!