RTK的使用 发表于 2024-08-06 | 更新于 2024-08-06
| 阅读量:
1. 理论准备
1.1 RTK工作原理
RTK:Real-Time Kinematic,实时载波技术 ,是一种高精度的卫星导航技术,可实现厘米级别的导航与定位要求。
现在的RTK分为:标准RTK、网络RTK以及ppp方式 。
其工作原理:
基准站先观测和接收卫星数据;
基准站通过旁边的无线电台(数据链),将观测数据实时发送给流动站(距离一般不超过20公里);
流动站收到基准站数据的同时,也观测和接收了卫星数据;
流动站在基准站数据和自身数据的基础上,**根据相对定位原理,进行实时差分运算,**从而解算出流动站的三维坐标及其精度,其定位精度可达1cm~2cm。
1.2 坐标系
在GPS的使用中存在多种坐标系:惯性坐标系、地心坐标系以及导航坐标系。
惯性坐标系( i 系 ):
以地球质心为原点,Z轴指向地球自转轴(即南北极轴,跟地球轨道面夹角66°34‘), X轴位于赤道面指向空间任意点,Y轴与其构成右手系。
该坐标系不随地球自转而转动 ,由于地球质心绕太阳公转以及太阳系绕银河系公转,因此,该系不是绝对惯性系, 但这些影响十分微弱,低于惯导的噪声水平,因而可以忽略不计,该系可以认为是一个惯性系。
地心坐标系( e 系 ):
由于每年的标准不同,我们重点针对2005.0参考元年的WGS84坐标系进行讲解。
1.2.1 WGS84坐标系
一句话解释就是:把前面提到的ECEF坐标系用在GPS中,就是WGS-84坐标系。
其中:
(1):大地纬度 是过用户点P的基准椭球面法线与赤道面的夹角。纬度值在-90°到+90°之间。北半球为正,南半球为负。
(2):大地经度 是过用户点P的子午面与本初子午线之间的夹角。经度值在-180°到+180°之间。
(3):大地高度h是过用户点P到基准椭球面的法线距离 ,基准椭球面以内为负,以外为正。
参考历元2005.0 :
参考历元是用于定义坐标系的一个固定时间点,2005.0表示该版本的参考时间是2005年。
高精度地心坐标系 :
WGS84是一个地心坐标系(地心地固坐标系,ECEF),其原点在地球质心,X轴指向格林尼治本初子午线与赤道的交点,Z轴指向地球的北极,Y轴垂直于X轴和Z轴,形成一个右手坐标系。
椭球参数 :
长半轴(a):6378137.0米
扁率(f):1/298.257223563
坐标转换:
WGS84坐标(经度 λ \lambda λ 、纬度 ϕ \phi ϕ 、高度 h h h )可以转换为地心地固坐标(ECEF):
N = a 1 − e 2 sin 2 ( ϕ ) X = ( N + h ) cos ( ϕ ) cos ( λ ) Y = ( N + h ) cos ( ϕ ) sin ( λ ) Z = ( N ( 1 − e 2 ) + h ) sin ( ϕ ) \begin{aligned}
&\text{N} =\frac a{\sqrt{1-e^2\sin^2(\phi)}} \\
&X=(N+h)\cos(\phi)\cos(\lambda) \\
&Y=(N+h)\cos(\phi)\sin(\lambda) \\
&Z=(N(1-e^{2})+h)\sin(\phi)
\end{aligned} N = 1 − e 2 sin 2 ( ϕ ) a X = ( N + h ) cos ( ϕ ) cos ( λ ) Y = ( N + h ) cos ( ϕ ) sin ( λ ) Z = ( N ( 1 − e 2 ) + h ) sin ( ϕ )
其中:
a a a 是地球的长半轴(6378137.0米)。
e e e 是地球的偏心率(约0.0818191908426)。
N N N 是卯酉圈曲率半径。
假设我们有一个点的WGS84坐标为:
经度 λ λ λ =45
纬度 ϕ \phi ϕ =45
高度 h h h =100
我们可以计算其ECEF坐标:
N = 6378137.0 1 − 0.081819190842 6 2 sin 2 ( 4 5 ∘ ) ≈ 6388838.29 米 X = ( 6388838.29 + 100 ) cos ( 4 5 ∘ ) cos ( 4 5 ∘ ) ≈ 3194669.15 米 Y = ( 6388838.29 + 100 ) cos ( 4 5 ∘ ) sin ( 4 5 ∘ ) ≈ 3194669.15 米 Z = ( 6388838.29 ⋅ ( 1 − 0.081819190842 6 2 ) + 100 ) sin ( 4 5 ∘ ) ≈ 4487351.59 米 \begin{aligned}
&\text{N} =\frac{6378137.0}{\sqrt{1-0.0818191908426^2\sin^2(45^\circ)}}\approx6388838.29\text{米} \\
&\text{X} =(6388838.29+100)\cos(45^\circ)\cos(45^\circ)\approx3194669.15\text{米} \\
&\text{Y} =(6388838.29+100)\cos(45^\circ)\sin(45^\circ)\approx3194669.15\text{米} \\
&\text{Z} =(6388838.29\cdot(1-0.0818191908426^2)+100)\sin(45^\circ)\approx4487351.59\text{米}
\end{aligned} N = 1 − 0.081819190842 6 2 sin 2 ( 4 5 ∘ ) 6378137.0 ≈ 6388838.29 米 X = ( 6388838.29 + 100 ) cos ( 4 5 ∘ ) cos ( 4 5 ∘ ) ≈ 3194669.15 米 Y = ( 6388838.29 + 100 ) cos ( 4 5 ∘ ) sin ( 4 5 ∘ ) ≈ 3194669.15 米 Z = ( 6388838.29 ⋅ ( 1 − 0.081819190842 6 2 ) + 100 ) sin ( 4 5 ∘ ) ≈ 4487351.59 米
这就是该点在2005年WGS84版本中的ECEF坐标。
1.2.2 ENU坐标系与UTM坐标系
ENU坐标系:
ENU(East-North-Up)坐标系是一种常用于局部坐标变换的坐标系 ,它的原点通常设置在某个已知点,如GPS基准站的所在地。
ENU坐标系相对于地球的局部平面定义,其中:
原点 :原点通常是一个已知的地理位置(经度 λ 0 \lambda_0 λ 0 ,纬度 ϕ\phi_0$,高度 h 0 h_0 h 0 )。
轴方向:
E轴 (东轴):在局部水平面内指向正东方向。
N轴 (北轴):在局部水平面内指向正北方向。
U轴 (上轴):垂直于局部水平面向上指向天顶。
由于:三维直角坐标系来描述地球表面,实际应用较为困难,因此一般使用简化后的二维投影坐标系来描述 。
UTM坐标系:
UTM(Universal Transverse Mercator,通用横轴墨卡托)坐标系是一种基于墨卡托投影的平面坐标系,广泛用于大地测量和地图制作。UTM坐标系将地球表面划分为多个投影带,每个投影带覆盖6度的经度范围。
UTM坐标系的特点:
投影带 :
地球被划分为60个投影带,每个投影带覆盖6度的经度。
投影带从西经180度开始,每6度为一个带,编号为1到60。
每个投影带的中心经线称为中央经线。
北半球和南半球 :
北半球的UTM坐标使用N表示(例如,32N)。
南半球的UTM坐标使用S表示(例如,32S)。
坐标单位 :
坐标原点 :
每个投影带的中央经线被赋予500,000米的东向伪东距(False Easting)。
北半球的原点在赤道(0,000,000米),南半球的原点在赤道南5,000,000米。
UTM坐标转换:
将地理坐标(经度、纬度)转换为UTM坐标,主要涉及以下步骤:
确定投影带 :
投影带号 zone \text{zone} zone 由公式计算:
zone = ⌊ longitude + 180 6 ⌋ + 1 \text{zone}=\left\lfloor\frac{\text{longitude}+180}{6}\right\rfloor+1 zone = ⌊ 6 longitude + 180 ⌋ + 1
计算投影坐标 :
使用高斯-克吕格公式或类似方法将地理坐标转换为平面坐标。
应用伪东距和伪北距 :
伪东距为500,000米。
北半球没有伪北距,南半球伪北距为10,000,000米。
UTM坐标系的优点:
精度高 :适合大范围区域的精确测量和地图制作。
计算简单 :平面坐标便于距离和方向的计算。
全球适用 :可以覆盖全球范围。
下面将演示基于ROS环境下ENU坐标系转为UTM坐标系:
#include <ros/ros.h> #include <sensor_msgs/NavSatFix.h> #include <geometry_msgs/Point.h> #include <cmath> class WGS84ToUTMConverter { public : WGS84ToUTMConverter (const std::string& gps_topic, const std::string& utm_topic) { gps_sub_ = nh_.subscribe (gps_topic, 10 , &WGS84ToUTMConverter::gpsCallback, this ); utm_pub_ = nh_.advertise <geometry_msgs::Point>(utm_topic, 10 ); } void gpsCallback (const sensor_msgs::NavSatFix::ConstPtr& msg) { double lat = msg->latitude; double lon = msg->longitude; double northing, easting; int zone; LLtoUTM (lat, lon, northing, easting, zone); geometry_msgs::Point utm_point; utm_point.x = easting; utm_point.y = northing; utm_point.z = msg->altitude; utm_pub_.publish (utm_point); } private : ros::NodeHandle nh_; ros::Subscriber gps_sub_; ros::Publisher utm_pub_; void LLtoUTM (double Lat, double Long, double &UTMNorthing, double &UTMEasting, int &Zone) { double a = 6378137.0 ; double f = 1 / 298.257223563 ; double k0 = 0.9996 ; double eccSquared = 2 * f - f * f; double eccPrimeSquared = eccSquared / (1 - eccSquared); double N, T, C, A, M; double LongOrigin; double LatRad = Lat * M_PI / 180.0 ; double LongRad = Long * M_PI / 180.0 ; double LongOriginRad; int ZoneNumber; ZoneNumber = int ((Long + 180 ) / 6 ) + 1 ; if (Lat >= 56.0 && Lat < 64.0 && Long >= 3.0 && Long < 12.0 ) ZoneNumber = 32 ; if (Lat >= 72.0 && Lat < 84.0 ) { if (Long >= 0.0 && Long < 9.0 ) ZoneNumber = 31 ; else if (Long >= 9.0 && Long < 21.0 ) ZoneNumber = 33 ; else if (Long >= 21.0 && Long < 33.0 ) ZoneNumber = 35 ; else if (Long >= 33.0 && Long < 42.0 ) ZoneNumber = 37 ; } LongOrigin = (ZoneNumber - 1 ) * 6 - 180 + 3 ; LongOriginRad = LongOrigin * M_PI / 180.0 ; N = a / sqrt (1 - eccSquared * sin (LatRad) * sin (LatRad)); T = tan (LatRad) * tan (LatRad); C = eccPrimeSquared * cos (LatRad) * cos (LatRad); A = cos (LatRad) * (LongRad - LongOriginRad); M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256 ) * LatRad - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024 ) * sin (2 * LatRad) + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024 ) * sin (4 * LatRad) - (35 * eccSquared * eccSquared * eccSquared / 3072 ) * sin (6 * LatRad)); UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120 ) + 500000.0 ); UTMNorthing = (k0 * (M + N * tan (LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720 ))); if (Lat < 0 ) UTMNorthing += 10000000.0 ; Zone = ZoneNumber; } }; int main (int argc, char ** argv) { ros::init (argc, argv, "wgs84_to_utm_converter" ); ros::NodeHandle private_nh ("~" ) ; std::string gps_topic; std::string utm_topic; private_nh.param ("gps_topic" , gps_topic, std::string ("/gps_fix" )); private_nh.param ("utm_topic" , utm_topic, std::string ("/utm_point" )); WGS84ToUTMConverter converter (gps_topic, utm_topic) ; ros::spin (); return 0 ; }
1.2.3 车体坐标系
车体坐标系通常以车辆的前方向为x轴,右方向为y轴,上方向为z轴。假设车辆的当前位置为参考点,并且车辆的航向角为 θ \theta θ (相对于北方向的偏转角),则将ENU坐标转换为车体坐标系的方法如下:
旋转矩阵:
[ x y z ] = [ cos ( θ ) sin ( θ ) 0 − sin ( θ ) cos ( θ ) 0 0 0 1 ] [ E N U ] \begin{bmatrix}x\\y\\z\end{bmatrix}=\begin{bmatrix}\cos(\theta)&\sin(\theta)&0\\-\sin(\theta)&\cos(\theta)&0\\0&0&1\end{bmatrix}\begin{bmatrix}E\\N\\U\end{bmatrix} x y z = cos ( θ ) − sin ( θ ) 0 sin ( θ ) cos ( θ ) 0 0 0 1 E N U
基于车身坐标系,就可以完成IMU、雷达、相机坐标系等传感器的坐标系变换了。
同时在实际使用RTK模块的时候一定要注意:
RTK类型,如:标准RTK、网络RTK、PPP
配置信息,如:千寻挂载端口(端口不同对应坐标系不同)、参考元年等
RTK模块对坐标系的定义,如:ENU、END等
2. RTK的ROS驱动
不同的RTK设备,输出的数据格式不一样,通常存在两种方式:NMEA、二进制流
i. NMEA
cd catkin_ws/srcgit clone https://github.com/ros-drivers/nmea_navsat_driver/ cd ..catkin_make -DCATKIN_WHITELIST_PACKAGES='nmea_navsat_driver'
<launch> <!-- 对nmea_serial_driver node的简单启动文件设计 --> <arg name="port" default="/dev/gps" /> <!-- 端口号 --> <arg name="baud" default="115200" /> <!--波特率--> <arg name="frame_id" default="gps" /> <!--参考坐标系--> <arg name="use_GNSS_time" default="False" /> <arg name="time_ref_source" default="gps" /> <arg name="useRMC" default="False" /> <node name="nmea_serial_driver" pkg="nmea_navsat_driver" type="nmea_serial_driver" output="screen"> <param name="port" value="$(arg port)"/> <param name="baud" value="$(arg baud)" /> <param name="frame_id" value="$(arg frame_id)" /> <param name="use_GNSS_time" value="$(arg use_GNSS_time)" /> <param name="time_ref_source" value="$(arg time_ref_source)" /> <param name="useRMC" value="$(arg useRMC)" /> </node> </launch>
PS:nmea_navsat_driver提供四个节点,nmea_topic_driver,nmea_serial_driver,nmea_topic_serial_reader、nmea_socket_driver
nmea_topic_serial_reader :此节点从串口读入GPS数据,然后封装为nmea_msgs/Sentence数据格式,发布话题 nmea_sentence 。
**nmea_topic_driver:**此节点订阅话题nmea_sentence,然后根据NMEA0184协议解析,并发布解析后的数据,发布话题为:经纬度/fix,速度/vel,gps时间/time_reference和航向角/heading.
**nmea_serial_driver:**此节点从串口读数据,直接解析发布数据,相当于nmea_topic_driver节点和nmea_topic_serial_reader节点的结合体 。
上述代码发布话题 :
/fix : gps经纬度
/vel : gps速度
/time_reference : gps时间
/heading : 航向角
/extend_fix : GPSFix消息包含GPS卫星状态和定位信息
ii. 二进制流
该方法需要结合对应通讯协议,下面将基于NAV680AG组合导航设备进行ROS驱动设计,并将其封装。