2. 本船与目标船运动参数解算
解算对象与坐标约定
本模块的输入是两船在同一时间基准下的位置与运动序列:本船经纬度 (φ₀, λ₀)、航向 C₀、航速 V₀,目标船经纬度 (φ₁, λ₁)、航向 C₁、航速 V₁。为避免“方位从哪边量、速度往哪边加”的混乱,先固定三条约定:
- 方位:真方位,自真北顺时针 0°~360°,相对方位 = 目标真方位 − 本船航向,归一到 (−180°, 180°]。
- 航向:真航向,自真北顺时针,与方位同一基准。
- 速度分量:x 轴指东、y 轴指北,Vx = V·sin C,Vy = V·cos C。相对速度 = 目标速度矢量 − 本船速度矢量。
距离计算采用等距圆柱近似(小范围会遇场景足够):Δx = (λ₁−λ₀)·cos(φ_m)·R,Δy = (φ₁−φ₀)·R,其中 φ_m 为两船平均纬度、R = 6371000 m,角度用弧度。该近似在数十海里内误差可忽略,但必须全程统一使用,不能一处用度、一处用弧度。
解算链路
整个模块是一条确定的数据流:经纬度差 → 平面相对位置 → 相对速度矢量 → 距离与方位 → 最近会遇点参数。下图给出各环节的输入输出与关键公式,便于对照代码检查每一步的单位。
解算模块实现
下面给出一个自洽的 Python 实现,包含单位换算、坐标归一、DCPA/TCPA 计算与一组教学示例数据。示例数据为假设值,用于验证公式与单位,不代表任何实测航行记录。
import math
R_EARTH = 6371000.0 # 地球平均半径,米
KN_TO_MS = 0.514444 # 1 节 = 0.514444 m/s
def norm360(deg):
return deg % 360.0
def norm180(deg):
"""归一到 (-180, 180]"""
d = deg % 360.0
return d - 360.0 if d > 180.0 else d
def to_rad(deg):
return deg * math.pi / 180.0
def to_deg(rad):
return rad * 180.0 / math.pi
def relative_motion(lat0, lon0, cog0, sog0, lat1, lon1, cog1, sog1):
"""
输入:经纬度(度)、真航向(度)、航速(节)
输出:dict,距离(m)、真方位(度)、相对方位(度)、相对航速(节)、DCPA(m)、TCPA(s)
"""
# 1. 平面相对位置(等距圆柱近似)
lat_m = to_rad((lat0 + lat1) / 2.0)
dx = to_rad(lon1 - lon0) * math.cos(lat_m) * R_EARTH
dy = to_rad(lat1 - lat0) * R_EARTH
# 2. 速度矢量(m/s)
v0 = sog0 * KN_TO_MS
v1 = sog1 * KN_TO_MS
vx0, vy0 = v0 * math.sin(to_rad(cog0)), v0 * math.cos(to_rad(cog0))
vx1, vy1 = v1 * math.sin(to_rad(cog1)), v1 * math.cos(to_rad(cog1))
dvx, dvy = vx1 - vx0, vy1 - vy0
# 3. 距离与方位
dist = math.hypot(dx, dy)
tb = norm360(to_deg(math.atan2(dx, dy))) # atan2(东, 北) → 真方位
rb = norm180(tb - cog0)
# 4. DCPA / TCPA
dv2 = dvx * dvx + dvy * dvy
if dv2 < 1e-9:
tcpa = 0.0
dcpa = dist
else:
tcpa = -(dx * dvx + dy * dvy) / dv2
cx, cy = dx + dvx * tcpa, dy + dvy * tcpa
dcpa = math.hypot(cx, cy)
vr = math.hypot(dvx, dvy) / KN_TO_MS
return {
"distance_m": round(dist, 1),
"true_bearing_deg": round(tb, 2),
"relative_bearing_deg": round(rb, 2),
"relative_speed_kn": round(vr, 3),
"DCPA_m": round(dcpa, 1),
"TCPA_s": round(tcpa, 1),
}
# 教学示例:本船北向 12 kn;目标船在本船右前方,航向 270°,航速 10 kn
case = relative_motion(
lat0=30.0000, lon0=122.0000, cog0=0.0, sog0=12.0,
lat1=30.0500, lon1=122.0600, cog1=270.0, sog1=10.0,
)
for k, v in case.items():
print(f"{k:22s}{v}")
示例中两船相距约 6.9 km(约 3.7 n mile),目标船位于本船右前方约 60° 方向,以 10 kn 向西横越。按公式可预期:相对速度约 15.6 kn,TCPA 为正值(尚未到达最近点),DCPA 明显小于当前距离,说明存在横越会遇趋势。运行后应得到类似下面的输出(数值由上述公式直接推出,非实测):
distance_m 6900.0 左右
true_bearing_deg 60 左右
relative_bearing_deg 60 左右
relative_speed_kn 15.6 左右
DCPA_m 小于当前距离
TCPA_s 正值
单位与坐标系一致性验证
解算模块最容易出错的地方不是公式,而是单位混用。建议用三个可判定的检查点做回归验证,而不是只看结果“像不像”:
- 正北基准检查:令目标船在本船正北 1 n mile、两船同向同速,则 TB 应为 0°、RB 应为 0°、Vr 应为 0、DCPA 应等于当前距离、TCPA 为 0。若 TB 输出 90°,说明 atan2 参数顺序写反。
- 单位换算检查:把航速从节改为 m/s 直接代入(不乘 0.514444),DCPA 与 TCPA 会成比例失真;用同一组数据分别以节和 m/s 输入,换算后结果应一致。
- 经度缩放检查:在纬度 60° 处,同样 0.01° 经度差对应的东西向距离应约为赤道处的一半。若未乘 cos(φ_m),该检查会失败。
把上述三个检查点写成断言(assert)放进测试函数,每次修改公式后先跑断言再跑业务数据,可以避免“结果看起来合理但基准错了”的隐性错误。
TCPA 为负表示两船已通过最近会遇点,此时 DCPA 仍是历史最近距离的度量,不能据此判断“当前安全”。实际避碰判断应结合当前距离、相对方位变化趋势与规则要求综合进行。