Saggita RTK定位定向模块
Saggita®
版本: V1.2
本手册内容仅用于 SIM 2 产品的安装、配置和使用,不作为其他用途。
阅读提示
- 建议先观看官方教学视频
- 再阅读本手册及随产品附带的文档
- 如有问题请联系官方技术支持工程师
1. 产品介绍
S-SIM 2 集成 RTK 高精度定位与双天线航向测向技术,支持串口 RTCM 差分数据输入 及 4G CORS 网络RTK接入, 可实现厘米级定位精度与高可靠航向解算。模块面向复杂户外应用场景设计,适配 无人机、人形机器人、四足机器人及无人驾驶车辆等 移动机器人系统。对外提供 DroneCAN、UART 及 MQTT 等多种通信接口,便于系统集成与多平台接入。
参数指标
| 项目 | 分类 | 参数 |
|---|---|---|
| 星座 | BDS / GPS / GLONASS / Galileo / QZSS | |
| 主 / 从天线频点 | BDS | B1I、B2I、B3I |
| GPS | L1C/A、L2P(Y)、L2C、L5 | |
| GLONASS | G1、G2 | |
| Galileo | E1、E5a、E5b | |
| QZSS | L1、L2、L5 | |
| 定位精度 | 单点定位 (RMS) | 平面:1.5 m 高程:2.5 m |
| DGPS | 平面:0.4 m + 1 ppm 高程:0.8 m + 1 ppm |
|
| RTK | 平面:0.8 cm + 1 ppm 高程:1.5 cm + 1 ppm |
|
| PPP | 平面:5 cm 高程:10 cm |
|
| 定向精度 (RMS) | 0.1° / 1 m 基线 | |
| PPS 精度 (RMS) | 20 ns | |
| 速度精度 (RMS) | 0.03 m/s | |
| 首次定位时间 | 冷启动 | < 30 s |
| 热启动 | < 4 s | |
| 数据更新率 | 调试软件可修改 | 默认 10 Hz,最大 20 Hz |
接口定义
该模块预留了 CAN 和 UART 接口,定义如图所示。其中 2 组 CAN 信号线内部短接在一起,方便用户外部串接其它 CAN 设备。如果该模块为 CAN 总线的终端设备,建议在剩余的一组 CAN 信号线上串接 120 欧电阻。注意,VCC 和 GND内部直接连在一起,同时只能有一个外部电源供电输入,可通过多余的电源线给其它设备供电。串口 1 用于输出定位信息,串口 2 用于传输 RTCM 信息。
| 名称 | 定义 | 备注说明 |
|---|---|---|
| VCC | 电源正 | 5–16 V,绝对值不可超过 18 V |
| GND | 电源地 | 同时作为信号地 |
| CAN_H | CAN 总线 H | CAN 标准信号 |
| CAN_L | CAN 总线 L | CAN 标准信号 |
| TX1 | 串口 1 发送 | 3.3 V TTL 信号;若外部接口为 5 V TTL,请串接限流电阻 |
| RX1 | 串口 1 接收 | 3.3 V TTL 信号;若外部接口为 5 V TTL,请串接限流电阻 |
| TX2 | 串口 2 发送 | 3.3 V TTL 信号;若外部接口为 5 V TTL,请串接限流电阻 |
| RX2 | 串口 2 接收 | 3.3 V TTL 信号;若外部接口为 5 V TTL,请串接限流电阻 |
2. 参数设置
通过本模块配套的调参软件可对模块的参数进行设置。RTK 通过 type-C 接口连接至电脑,运行 RTK-Manager.exe 后选择正确的端口号并打开串口。正常连后切换到配置页面,点参数读取按钮读取模块的参数,如下图所示。
⚙️ 参数说明
-
设备模式
模块可配置为 “移动端” 和 “基站端”,正常使用下建议不要更改该配置。 -
数据模式
可设置为 “默认” 和 “自定义” 两种模式。
该参数仅影响 串口输出数据,CAN 口输出数据不受影响。 - 默认数据:Fix3 数据格式
-
自定义数据:UM982 直接输出数据,用户可自定义 UM982 输出的数据帧
-
默认数据
默认数据包含三种数据类型,可选择是否输出,仅影响 CAN 口数据,串口数据不受影响。建议不要更改 RPH、Hdg 和 Fix2 的配置。 - ODR:数据更新频率,最大 20 Hz
-
Offset:双天线测向角度偏移量
-
波特率
可分别设置 CAN、用户串口(串口 1) 和 电台串口(串口 2) 的波特率。 -
CORS
配置通过 4G 网络 获取 RTK 校正数据 的相关信息。 -
MQTT
预留 MQTT 协议接口,模块定位、定向等关键信息可通过该协议传输至远程服务器。
⚠️ 注意
- 对于输入型参数(如cors账号),输入新参数后请每次需要 按回车键 写入模块
- 写入成功后将弹出 “写入成功” 提示框
- 所有参数配置完成后,请点击 “参数保存”
- 若未保存,重新断电后参数将恢复为上一次配置
3.安装配置
-
天线安装
根据飞行器 / 机器人尺寸,尽可能增大天线安装跨度。建议 Master 与 Slave 天线间距 ≥ 25 cm,以获得最佳定向性能。 -
天线固定
安装天线支架后,旋紧螺旋天线,确保牢固可靠。 -
模块固定
使用双面胶将 S-SIM 2 模块固定在机体表面,避免振动或松动。 -
通信连接
根据控制器需求,通过 CAN 或 UART 与主控系统建立通信。
4.偏移设置
本模块的定位主要参考 Master 天线,定向结果为 Master 天线指向 Slave 天线与正北方向的夹角。如需设置定位天线的偏移量,请以 Master 天线 的偏移量为准。模块默认采用 NED 坐标系,定义如下(见示意图):
- 北偏(dx):正值, 南偏(dx):负值
- 右偏(dy):正值, 左偏(dy):负值
- 下偏(dz):正值, 上偏(dz): 负值
正常情况下将 Master 天线安装在正后方,Slave 天线安装在正前方,模块输出的航向角和机头方向保持一致。如果安装条件受限,需要将两个天线旋转一定角度安装,则按照 Master 天线指向 Slave 天线与机头方向逆时针方向的夹角来设置航向偏移量。下图所示为四种典型的安装方式及相应的航向偏移量。注意,航向偏移量需通过RTK Manager调参软件设置,详见参数设置中对默认数据的描述(设置Offset的值)。
DroneCAN 配置
本模块的 CAN 接口通过 DroneCAN 协议适配当前主流的开源飞控。下面分别对使用 APM 和 PX4 固件的开源飞控应用该模块的配置做详细说明。
APM 固件参数配置:(上位机软件采用 MissionPlanner,固件版本 4.6 以上)
CAN_P1_DRIVER = 1 (启用 CAN1 驱动)
GPS_AUTO_CONFIG = 0 (关闭 DroneCAN GPS自动配置,模块出厂已完成配置,建议保持关闭)
GPS1_TYPE = 9 (设置 GPS 类型为 **DroneCAN**)
GPS1_POS_X:主天线相对于重心的 [X轴]偏移量 dx(前偏移为正,后偏移为负)
GPS1_POS_Y:主天线相对于重心的 [Y轴]偏移量 dy(右偏移为正,左偏移为负)
GPS1_POS_Z:主天线相对于重心的 [Z轴]偏移量 dz (下偏移为正,上偏移为负)
EK2_SRC1_YAW = 2 / 3
- 2:仅使用 GPS 作为航向源
- 3:优先使用 GPS,罗盘作为备份
PX4 固件参数配置:(上位机软件采用 QGC)
UAVCAN_ENABLE = "Sensors Automatic Config" (启用 UAVCAN 传感器自动配置)
EKF2_GPS_CTRL = 15(使用双天线定向作为航向源)
EKF2_GPS_POS_X:主天线相对于重心的[X轴]偏移量 dx(前偏移为正,后偏移为负)
EKF2_GPS_POS_Y:主天线相对于重心的[Y轴]偏移量 dy (右偏移为正,左偏移为负)
EKF2_GPS_POS_Z:主天线相对于重心的[Z轴]偏移量 dz(下偏移为正,上偏移为负)
EKF2_MAG_TYPE = None / Init
- None:不使用电子罗盘
- Init:仅使用电子罗盘进行航向初始化
✅ 系统检查
安装及部件检查
- 确认 S-SIM 2 模块 安装正确且牢固。
- 确认 通信线缆与天线 连接正确、可靠。
工作状态指示检查
- 检查 LED 状态指示灯 是否与当前工作状态对应。
- 确认设定的 螺旋天线间距 以及 主 / 副天线位置 是否正确。
- 在地面站软件中检查 RTK 是否获得固定解(RTK Fixed)。
💡 LED 指示灯速查
| 状态 | 绿灯常亮 | 红灯常亮 |
|---|---|---|
| RTK | RTK 固定解,输出高精度定位信息 | 非 RTK 固定解,输出常规定位信息 |
| PVT | 已正常定位,输出常规定位信息 | 未定位,无有效定位信息 |
| ERR | 系统正常 | 系统故障 |
常见问题排查与解决
| 问题描述 | 解决办法 |
|---|---|
| PVT亮红灯 | 此时卫星接收数量不足,检查周围环境是否有严重遮挡。移动新的位置进行测试 |
| 室内是无法接收卫星信息,需要到户外测试 | |
| RTK亮红灯,始终无法进入固定解 | 检查SMA接头是否拧紧;射频线是否断裂(射频线比较脆弱不能小半径折弯) |
| 可以尝试将slave和master的天线互换,判断是否是射频线损坏 | |
| 检查CORS的账号输入是否正确,每次输入后都要敲一个回车,界面会跳出来【写入成功】 | |
| 螺旋天线的45°的范围内,尽量不要有遮挡,会影响卫星的接收能力 | |
| 物联网卡的每月免费流量是2GB,用完后次月恢复。可更换SIM后,再次测试 | |
| 4G天线是否折断,sma射频线很脆弱,不能大角度折弯 | |
| 串口调试助手乱码 | 不要使用TypeC接口,这是上位机的调试口,不能拿来解析Fix3的协议 |
| 需要用【串口1】,可以使用USB转TTL模块接入PC查看输出数据,电压需要是3.3VDC;若为5V TTL,请串接限流电阻 | |
| 请更换TypeC后再次测试 | |
| 机器人走动的时候,RTK灯就会灭掉,停下来就恢复 | 检查master接口的SMA线是否过度折弯损坏,接口是否松动? |
| 可将slave和master的天线互换,判断射频线是否损坏 |
GNSS_FIX3 消息定义
| 区域 | 定义 | 数据类型 | 起始字节 | 字节长度 | 说明 |
|---|---|---|---|---|---|
| 帧头 | head1 | uint8 | 1 | 1 | 帧头 |
| 帧头 | head2 | uint8 | 2 | 1 | 帧头 |
| 帧头 | id | uint16 | 3 | 2 | 消息 ID |
| 帧头 | addr | uint16 | 5 | 2 | 消息地址 |
| 帧头 | len | uint16 | 7 | 2 | 数据长度字段:最高位 len[15] 表示是否存在 data 域;len[14:0] 表示 data 域长度。• len[15] = 1:存在 data,实际长度为 len[14:0]• len[15] = 0:无 data,data 长度为 0 |
| 帧头 | check | uint16 | 9 | 2 | 校验值:除 check 字段外所有字节求和,结果取 uint16 |
| 数据域 | data | 参见具体消息定义 | 11 | len[14:0] 指定 |
当 len[15] = 0 时,数据长度为 0 |
| 区域 | 定义 | 数据类型 | 起始字节 | 字节长度 | 实际数据 | 说明 |
|---|---|---|---|---|---|---|
| 帧头 | head1 | uint8 | 1 | 1 | 0xFA | 帧头 |
| head2 | uint8 | 2 | 1 | 0xAA | 帧头 | |
| id | uint16 | 3 | 2 | 0x0427 | 消息 ID = 1063 | |
| addr | uint16 | 5 | 2 | 0x0000 | 消息地址 0 | |
| len | uint16 | 7 | 2 | 0x803F | 数据域长度为 63 | |
| check | uint16 | 9 | 2 | B9~B10 | 除 check 外所有字节求和,结果取 uint16 |
|
| 数据域 | gps_time | uint64 | 11 | 8 | B11~B18 | UTC 时间,自 1970-01-01 00:00:00 起计,单位 μs(本地时间需加时区偏移) |
| lon | int40 | 19 | 5 | B19~B23 | 经度,保留 8 位小数,实际数据放大10的8次方倍,单位 0.00000001° | |
| lat | int40 | 24 | 5 | B24~B28 | 纬度,保留 8 位小数,实际数据放大10的8次方倍,单位 0.00000001° | |
| alt | int24 | 29 | 3 | B29~B31 | 海拔高度,保留 3 位小数,实际数据 = 米 × 1000,单位 mm | |
| vel_n | int32 | 32 | 4 | B32~B35 | 北向速度,保留 3 位小数,实际数据 = m/s × 1000,单位 mm/s | |
| vel_e | int32 | 36 | 4 | B36~B39 | 东向速度,保留 3 位小数,实际数据 = m/s × 1000,单位 mm/s | |
| vel_d | int32 | 40 | 4 | B40~B43 | 下向速度,保留 3 位小数,实际数据 = m/s × 1000,单位 mm/s | |
| heading | int16 | 44 | 2 | B44~B45 | 双天线航向角,保留 2 位小数,实际数据 = ° × 100,范围 [-180, 180],无效值 32767 | |
| heading_acc | uint16 | 46 | 2 | B46~B47 | 双天线航向精度,保留 2 位小数,实际数据 = ° × 100,范围 [0, 360],无效值 32767 | |
| pos_n_acc | uint24 | 48 | 3 | B48~B50 | 北向位置精度,保留 3 位小数,实际数据放大1000倍,单位 mm | |
| pos_e_acc | uint24 | 51 | 3 | B51~B53 | 东向位置精度,保留 3 位小数,实际数据放大1000倍,单位 mm | |
| pos_d_acc | uint24 | 54 | 3 | B54~B56 | 下向位置精度,保留 3 位小数,实际数据放大1000倍,单位 mm | |
| vel_n_acc | uint24 | 57 | 3 | B57~B59 | 北向速度精度,保留 3 位小数,实际数据放大1000倍,单位 mm/s | |
| vel_e_acc | uint24 | 60 | 3 | B60~B62 | 东向速度精度,保留 3 位小数,实际数据放大1000倍,单位 mm/s | |
| vel_d_acc | uint24 | 63 | 3 | B63~B65 | 下向速度精度,保留 3 位小数,实际数据放大1000倍,单位 mm/s | |
| pdop | uint16 | 66 | 2 | B66~B67 | 位置精度因子,保留 1 位小数,实际数据放大10倍,单位 0.1 | |
| vdop | uint16 | 68 | 2 | B68~B69 | 速度精度因子,保留 1 位小数,实际数据放大10倍,单位 0.1 | |
| hdop | uint16 | 70 | 2 | B70~B71 | 垂直精度因子,保留 1 位小数,实际数据放大10倍,单位 0.1 | |
| sats_used | uint8 | 72 | 1 | B72 | 使用的卫星数量 | |
| fix_type | uint8 | 73 | 1 | B73 | 定位状态:0 无效,1 单点,2 差分,3 PPP,4 RTK FLOAT,5 RTK FIXED |
解析过程示例(C / C++)
#include <stdint.h>
uint64_t val;
int64_t lon;
/*
* 原始字节(小端):
* 98 DE BE 6B 02
*/
val = ((uint64_t)0x02 << 56)
+ ((uint64_t)0x6B << 48)
+ ((uint64_t)0xBE << 40)
+ ((uint64_t)0xDE << 32)
+ ((uint64_t)0x98 << 24);
/* 右移 24 位,得到 40-bit 有符号整数 */
lon = (int64_t)val >> 24;
说明
- 所有字段均采用 小端模式(Little Endian);
- 解析时应先将字节 按无符号整数方式组合;
- 对于有符号数据,应在组合完成后再转换为 有符号整数;
- 本示例中,经纬度字段为 40-bit 有符号整数,实际物理值需再按协议约定比例缩放。
FIX3解析代码示例(Python)
import datetime
def read_uint16_le(b, p):
return int.from_bytes(b[p:p + 2], "little", signed=False)
def read_uint24_le(b, p):
return b[p] | (b[p + 1] << 8) | (b[p + 2] << 16)
def read_int24_le(b, p):
v = read_uint24_le(b, p)
return v - (1 << 24) if v & 0x800000 else v
def read_uint40_le(b, p):
return (
b[p]
| (b[p + 1] << 8)
| (b[p + 2] << 16)
| (b[p + 3] << 24)
| (b[p + 4] << 32)
)
def read_int40_le(b, p):
v = read_uint40_le(b, p)
return v - (1 << 40) if (v & (1 << 39)) else v
def checksum16(data: bytes) -> int:
return sum(data) & 0xFFFF
def parse_ulink_frame(frame: bytes):
if len(frame) < 11:
raise ValueError("Frame too short")
if frame[0] != 0xFA or frame[1] != 0xAA:
raise ValueError("Invalid frame header")
msg_id = read_uint16_le(frame, 2)
addr = read_uint16_le(frame, 4)
len_raw = read_uint16_le(frame, 6)
has_data = bool(len_raw & 0x8000)
data_len = (len_raw & 0x7FFF) if has_data else 0
total_len = 11 + data_len
if len(frame) < total_len:
raise ValueError("Frame length mismatch")
check = read_uint16_le(frame, 8)
calc = checksum16(frame[:8] + frame[10:10 + data_len])
check_ok = (check == calc)
data = frame[10:10 + data_len] if has_data else b""
return {
"id": msg_id,
"addr": addr,
"data_len": data_len,
"check_ok": check_ok,
"data": data,
"total_len": total_len,
}
def parse_gnss_fix3(data: bytes):
if len(data) != 63:
raise ValueError("GNSS_FIX3 payload length error")
gps_time_us = int.from_bytes(data[0:8], "little", signed=False)
gps_utc = datetime.datetime(1970, 1, 1) + datetime.timedelta(
microseconds=gps_time_us
)
lon = read_int40_le(data, 8) / 1e8
lat = read_int40_le(data, 13) / 1e8
alt = read_int24_le(data, 18) / 1000.0
vn = int.from_bytes(data[21:25], "little", signed=True) / 1000.0
ve = int.from_bytes(data[25:29], "little", signed=True) / 1000.0
vd = int.from_bytes(data[29:33], "little", signed=True) / 1000.0
heading_raw = int.from_bytes(data[33:35], "little", signed=True)
heading = None if heading_raw == 32767 else heading_raw / 100.0
heading_acc_raw = int.from_bytes(data[35:37], "little", signed=False)
heading_acc = None if heading_acc_raw == 32767 else heading_acc_raw / 100.0
posN = read_uint24_le(data, 37) / 1000.0
posE = read_uint24_le(data, 40) / 1000.0
posD = read_uint24_le(data, 43) / 1000.0
vNacc = read_uint24_le(data, 46) / 1000.0
vEacc = read_uint24_le(data, 49) / 1000.0
vDacc = read_uint24_le(data, 52) / 1000.0
pdop = int.from_bytes(data[55:57], "little") / 10.0
vdop = int.from_bytes(data[57:59], "little") / 10.0
hdop = int.from_bytes(data[59:61], "little") / 10.0
sats = data[61]
fix = data[62]
return {
"time": gps_utc,
"lat": lat,
"lon": lon,
"alt": alt,
"vn": vn,
"ve": ve,
"vd": vd,
"heading": heading,
"heading_acc": heading_acc,
"pdop": pdop,
"vdop": vdop,
"hdop": hdop,
"sats": sats,
"fix_type": fix,
}
def scan_frames(stream: bytes):
frames = []
i = 0
n = len(stream)
while i + 11 <= n:
if stream[i] == 0xFA and stream[i + 1] == 0xAA:
try:
fr = parse_ulink_frame(stream[i:])
frames.append(fr)
i += fr["total_len"]
continue
except Exception:
i += 1
else:
i += 1
return frames
if __name__ == "__main__":
with open("Serial Debug.txt", "r", encoding="utf-8") as f:
hex_text = f.read().strip()
raw = bytes(int(x, 16) for x in hex_text.split())
frames = scan_frames(raw)
for fr in frames:
if fr["id"] == 0x0427 and fr["data_len"] == 63 and fr["check_ok"]:
fix = parse_gnss_fix3(fr["data"])
print(
f"time={fix['time']} "
f"lat={fix['lat']:.8f} lon={fix['lon']:.8f} "
f"alt={fix['alt']:.3f}m "
f"sats={fix['sats']} fix={fix['fix_type']}"
)
自定义模式
1. 模式配置
首先,将数据模式设置为 自定义模式(Custom Mode),并完成以下配置:
- 设置 User 波特率(即用户实际使用的串口波特率)
- 配置完成后,点击 “保存参数”
- 重新上电设备,使配置生效
2. 指令配置(UART1)
设备重启后,进入 UART1 界面,发送所需的配置指令。
GPGGA 输出频率设置
- GPGGA 1(说明:1表示时间间隔为 1 秒,即 1 Hz。)
- GPGGA 0.1(说明:0.1 表示时间间隔为 0.1 秒,即 10 Hz。)
- 重新上电设备,使配置生效
数据输出说明
- 指令发送成功后,即可在界面看到输出数据。同时,UART1 串口也会同步输出对应数据
3.使用示例
示例:输出 NMEA0183 标准格式 GGA + RMC 数据(10 Hz)
完成自定义模式及波特率配置后,重新上电,在 UART1 界面发送以下指令:
GPGGA 0.1
GPRMC 0.1
SAVECONFIG
发送完成后,按 回车键(Enter) 执行。配置成功后,设备将通过 UART1 持续输出以下数据:GPGGA 定位数据+ GPRMC 推荐最小导航数据(均符合 NMEA0183 标准格式)。
-
所有指令必须以 回车(Enter) 结束,否则不会生效
-
未执行 SAVECONFIG 保存时,断电后配置将丢失
-
修改波特率后,请确保上位机串口参数同步更新,否则可能无法通信