Jetson Orin Nano J3011集成EM12-G GNSS模块:从硬件连接到ROS2定位数据发布全流程

发布时间:2026/8/1 18:54:46
Jetson Orin Nano J3011集成EM12-G GNSS模块:从硬件连接到ROS2定位数据发布全流程 1. 项目概述为什么要在J3011上折腾GNSS模块如果你手头有一台Jetson Orin Nano平台的reComputer Robotics J3011并且正在开发机器人、无人机或者任何需要高精度定位的移动设备那么给这台设备加上一个可靠的全球导航卫星系统模块几乎是绕不开的一步。J3011本身性能强劲但它的定位能力完全依赖于网络或外部传感器。EM12-G模块作为一款支持多星系统GPS、GLONASS、BeiDou、Galileo的GNSS接收器能为你提供厘米级到米级的原始定位数据是赋予机器人“空间感知”能力的关键外设。这篇文章我会基于实际的硬件连接和软件调试经验带你从头到尾完成EM12-G模块在reComputer J3011上的完整设置。这不仅仅是“插上就能用”那么简单你会遇到串口权限、数据解析、坐标系转换、甚至天线选型等一系列细节问题。网上零散的教程往往只讲其一我会把从物理连接到最终在ROS2机器人操作系统中获取并使用定位数据的全链路都捋清楚特别是那些容易踩坑的地方。无论你是机器人领域的在校学生、创客还是正在做产品原型的工程师这篇指南都能帮你省下大量摸索的时间。2. 硬件准备与连接避开那些“理所当然”的坑拿到EM12-G模块和J3011第一步当然是物理连接。这个过程看似简单但几个细节没注意后续的软件调试就会举步维艰。2.1 核心部件清点与作用首先我们得搞清楚手上有哪些东西以及它们各自扮演什么角色reComputer Robotics J3011我们的主控大脑。它运行基于ARM架构的Ubuntu系统拥有丰富的GPIO和接口是我们处理GNSS数据的计算平台。EM12-G GNSS模块定位数据源。它通过天线接收卫星信号解算出经纬度、高度、速度、时间等信息并通过串口UART以NMEA-0183或UBX二进制协议输出。GNSS天线这是信号接收的关键。必须是有源天线主动天线意味着它内部集成了低噪声放大器需要模块为其供电通常是3.3V或5V才能将微弱的卫星信号放大到模块可以处理的水平。无源天线在这里基本无法工作。连接线通常需要杜邦线或专门的连接器。核心需要连接的线只有四根电源正极VCC、电源负极GND、数据发送TX、数据接收RX。2.2 J3011 GPIO引脚定义与连接方案J3011的40针GPIO排针兼容树莓派的标准这大大简化了我们的工作。我们需要从中找到一组可用的串口UART引脚。最常用、最稳定的是UART1对应的引脚是Pin 8 (GPIO14, TXD0): J3011的串口发送引脚。注意这个“发送”是相对于J3011而言的所以它应该连接到EM12-G模块的RX接收引脚。Pin 10 (GPIO15, RXD0): J3011的串口接收引脚。它应该连接到EM12-G模块的TX发送引脚。电源我们可以从J3011的GPIO上取电。例如Pin 1 (3.3V)和Pin 6 (GND)可以提供一组稳定的3.3V电源。务必确认你的EM12-G模块的工作电压是3.3V绝大多数是如果是5V则需要从Pin 2或Pin 4取5V电。天线供电EM12-G模块通常有一个专门的引脚如V_ANT用于为有源天线供电。这个电压也需要从J3011的3.3V或5V引脚引出。连接关系总结如下表J3011 GPIO 引脚引脚名称连接至 EM12-G 模块引脚说明Pin 13.3V PowerVCC模块主电源确认电压匹配Pin 6GroundGND公共地线Pin 8GPIO14 (TXD0)RXJ3011发 - 模块收Pin 10GPIO15 (RXD0)TXJ3011收 - 模块发Pin 2 或 Pin 45V Power (可选)V_ANT有源天线供电若需要5V注意连接时务必断电操作杜邦线连接要确保牢固虚接会导致数据时有时无给调试带来巨大困扰。天线应尽量放置在开阔、无金属遮挡的地方车窗边、屋顶是理想选择室内定位效果会大打折扣甚至完全失效。3. 系统配置与串口通信调试硬件连接妥当后上电启动J3011。我们将进入系统层面让J3011能够“看见”并“听懂”EM12-G模块。3.1 启用串口与权限配置默认情况下J3011的串口可能被控制台占用或者未启用。我们需要先配置它。检查串口设备打开终端输入ls /dev/ttyTHS*。你会看到类似/dev/ttyTHS0/dev/ttyTHS1的设备。Jetson平台的UART设备通常以ttyTHS开头。我们的连接对应的是ttyTHS0即UART1。禁用串口控制台如果需要为了将串口用于我们的应用需要确保它没有被getty登录控制台占用。使用命令sudo systemctl stop nvgetty sudo systemctl disable nvgetty来禁用相关服务。这可以释放ttyTHS0供自由使用。设置串口权限默认情况下普通用户无法直接读写串口设备。每次都用sudo很麻烦。最一劳永逸的方法是将你的用户加入dialout组并修改设备权限。sudo usermod -a -G dialout $USER # 将当前用户加入dialout组 sudo chmod arw /dev/ttyTHS0 # 临时赋予读写权限重启可能失效为了让权限永久生效更好的方法是创建udev规则echo KERNELttyTHS0, MODE0666 | sudo tee /etc/udev/rules.d/99-ttyths0.rules sudo udevadm control --reload-rules sudo udevadm trigger执行后注销并重新登录你的用户就可以直接读写/dev/ttyTHS0了。3.2 使用串口工具测试数据流在编写自己的程序之前先用工具验证硬件和基础通信是否正常。安装串口调试工具screen或minicom是轻量好用的选择。sudo apt update sudo apt install screen minicom -y使用Screen进行快速测试Screen命令简单直接适合快速查看数据。EM12-G模块的默认波特率通常是9600。在终端输入screen /dev/ttyTHS0 9600如果连接和配置正确你将看到屏幕上开始持续滚动输出文本数据内容类似$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 $GPGSA,A,3,19,28,14,18,27,22,31,39,,,,,1.7,1.0,1.3*35 $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A这就是标准的NMEA-0183语句。按下CtrlA然后按K再按Y可以退出screen。解读NMEA数据上面滚动的每一行都是一个NMEA语句。最常用的是$GPGGA全球定位系统固定数据包含时间、经纬度、定位质量、卫星数、海拔等核心信息。$GPRMC推荐最小定位信息包含时间、状态、经纬度、对地速度、航向等。 看到这些数据恭喜你硬件连接和基础通信已经成功了模块已经能收到卫星信号并输出数据。4. 使用Python进行GNSS数据解析与应用通过串口工具看到原始数据只是第一步。我们需要在Python程序中读取、解析这些数据并将其转化为程序可用的变量如浮点型的纬度、经度。4.1 安装依赖与串口读取首先我们使用pyserial库来读取串口数据。pip install pyserial然后编写一个简单的Python脚本来读取数据import serial import time # 配置串口参数必须与模块设置一致 ser serial.Serial( port/dev/ttyTHS0, # 你的串口设备 baudrate9600, # 默认波特率可根据模块配置更改 parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, bytesizeserial.EIGHTBITS, timeout1 # 读超时时间秒 ) try: while True: if ser.in_waiting 0: # 检查缓冲区是否有数据 line ser.readline().decode(ascii, errorsignore).strip() if line.startswith($): # 确保是NMEA语句 print(line) time.sleep(0.01) except KeyboardInterrupt: print(\n程序被用户中断) finally: ser.close()运行这个脚本你应该能看到和screen工具一样的原始数据输出。这一步验证了我们的Python环境能够正常与模块通信。4.2 解析NMEA语句手动解析NMEA语句按逗号分割是可行的但更高效的方法是使用专门的库例如pynmea2。pip install pynmea2下面是一个增强版的脚本它解析特定的NMEA语句并提取有用信息import serial import pynmea2 import time ser serial.Serial(/dev/ttyTHS0, 9600, timeout1) try: while True: try: line ser.readline().decode(ascii, errorsignore).strip() except UnicodeDecodeError: continue if line.startswith($): try: msg pynmea2.parse(line) # 处理GGA语句包含基本定位信息 if isinstance(msg, pynmea2.types.talker.GGA): print(f时间(UTC): {msg.timestamp}) print(f纬度: {msg.latitude} {msg.lat_dir}) print(f经度: {msg.longitude} {msg.lon_dir}) print(f定位质量: {msg.gps_qual} (0无效1单点2差分)) print(f卫星数: {msg.num_sats}) print(f海拔: {msg.altitude} {msg.altitude_units}) print(- * 40) # 处理RMC语句包含速度和航向 elif isinstance(msg, pynmea2.types.talker.RMC): if msg.status A: # A代表数据有效 print(f对地速度: {msg.spd_over_grnd} 节) print(f航向: {msg.true_course} 度) print( * 40) except pynmea2.ParseError as e: # 忽略无法解析的语句 pass time.sleep(0.005) except KeyboardInterrupt: print(\n数据采集停止。) finally: ser.close()这个脚本会清晰地打印出解析后的、可读的定位信息。pynmea2库自动处理了经纬度的度分格式转换例如将4807.038,N转换为十进制的48.1173度和字段提取极大地简化了开发。4.3 坐标转换与基础应用获取到十进制的经纬度后这些数据可以直接用于许多应用。例如计算两个点之间的距离使用Haversine公式或者与预设的航点进行比较实现简单的到达判断。这里提供一个计算距离的简单函数示例from math import radians, sin, cos, sqrt, atan2 def haversine_distance(lat1, lon1, lat2, lon2): 计算两个经纬度坐标之间的球面距离单位米 R 6371000 # 地球平均半径单位米 phi1 radians(lat1) phi2 radians(lat2) delta_phi radians(lat2 - lat1) delta_lambda radians(lon2 - lon1) a sin(delta_phi / 2)**2 cos(phi1) * cos(phi2) * sin(delta_lambda / 2)**2 c 2 * atan2(sqrt(a), sqrt(1 - a)) return R * c # 示例计算当前位置与目标点的距离 current_lat 31.2304 current_lon 121.4737 target_lat 31.2242 target_lon 121.4698 distance haversine_distance(current_lat, current_lon, target_lat, target_lon) print(f距离目标点还有{distance:.2f} 米)将解析函数和距离计算函数结合起来你就能创建一个简单的定位监控或导航逻辑。5. 集成到ROS2让定位数据融入机器人系统对于J3011这样的机器人专用平台最终目标往往是将GNSS数据集成到ROS2中供导航、建图等节点使用。这里我们创建一个简单的ROS2节点来发布GNSS数据。5.1 创建ROS2功能包与节点假设你的ROS2工作空间是~/ros2_ws。创建功能包cd ~/ros2_ws/src ros2 pkg create --build-type ament_python gnss_publisher --dependencies rclpy sensor_msgs编写GNSS发布节点在~/ros2_ws/src/gnss_publisher/gnss_publisher目录下创建gnss_publisher_node.py。#!/usr/bin/env python3 import rclpy from rclpy.node import Node from sensor_msgs.msg import NavSatFix, NavSatStatus import serial import pynmea2 from threading import Thread, Event class GNSSPublisher(Node): def __init__(self): super().__init__(gnss_publisher) self.publisher_ self.create_publisher(NavSatFix, gnss/fix, 10) self.stop_event Event() # 串口配置参数可通过ROS2参数服务器配置 self.declare_parameter(serial_port, /dev/ttyTHS0) self.declare_parameter(baudrate, 9600) port self.get_parameter(serial_port).get_parameter_value().string_value baud self.get_parameter(baudrate).get_parameter_value().integer_value try: self.ser serial.Serial(port, baud, timeout1) self.get_logger().info(f成功打开串口: {port}) except serial.SerialException as e: self.get_logger().error(f无法打开串口 {port}: {e}) raise # 在后台线程中读取和解析数据 self.thread Thread(targetself._read_serial_thread) self.thread.start() def _read_serial_thread(self): 独立的串口数据读取线程 while not self.stop_event.is_set() and rclpy.ok(): try: if self.ser.in_waiting: line self.ser.readline().decode(ascii, errorsignore).strip() if line.startswith($): self._parse_and_publish(line) except Exception as e: self.get_logger().warn(f读取串口时出错: {e}) self.ser.close() def _parse_and_publish(self, nmea_string): 解析NMEA语句并发布ROS2消息 try: msg pynmea2.parse(nmea_string) if isinstance(msg, pynmea2.types.talker.GGA) and msg.gps_qual 0: # 构造NavSatFix消息 nav_msg NavSatFix() nav_msg.header.stamp self.get_clock().now().to_msg() nav_msg.header.frame_id gnss # 坐标系ID nav_msg.status.status NavSatStatus.STATUS_FIX if msg.gps_qual 1 else NavSatStatus.STATUS_GBAS_FIX nav_msg.status.service NavSatStatus.SERVICE_GPS | NavSatStatus.SERVICE_GLONASS # 根据实际使用的系统设置 nav_msg.latitude msg.latitude nav_msg.longitude msg.longitude nav_msg.altitude float(msg.altitude) if msg.altitude else 0.0 # 位置协方差这是一个简化示例实际中需要根据HDOP等计算 # HDOP水平精度因子可以从GSA语句获取这里假设一个值 hdop getattr(msg, horizontal_dil, 1.0) cov (hdop * 3) ** 2 # 非常粗略的估计 nav_msg.position_covariance[0] cov # xx nav_msg.position_covariance[4] cov # yy nav_msg.position_covariance[8] (hdop * 5) ** 2 # zz nav_msg.position_covariance_type NavSatFix.COVARIANCE_TYPE_APPROXIMATED self.publisher_.publish(nav_msg) self.get_logger().debug(f发布定位: Lat{nav_msg.latitude:.6f}, Lon{nav_msg.longitude:.6f}, throttle_duration_sec2.0) except pynmea2.ParseError: pass except AttributeError: pass def destroy_node(self): self.stop_event.set() if self.thread.is_alive(): self.thread.join() super().destroy_node() def main(argsNone): rclpy.init(argsargs) node GNSSPublisher() try: rclpy.spin(node) except KeyboardInterrupt: node.get_logger().info(节点被用户中断) finally: node.destroy_node() rclpy.shutdown() if __name__ __main__: main()5.2 配置与运行节点修改setup.py确保入口点正确。entry_points{ console_scripts: [ gnss_publisher gnss_publisher.gnss_publisher_node:main, ], },编译并运行cd ~/ros2_ws colcon build --packages-select gnss_publisher source install/setup.bash ros2 run gnss_publisher gnss_publisher查看数据打开另一个终端使用ros2 topic echo /gnss/fix命令你应该能看到实时发布的NavSatFix消息流。至此EM12-G模块的数据已经成功接入ROS2生态系统。其他节点如导航栈nav2可以订阅/gnss/fix话题将GNSS定位信息与激光雷达、IMU等传感器数据进行融合实现更鲁棒的机器人定位。6. 进阶配置与故障排查基础功能跑通后你可能会遇到精度不够、数据不稳定或者想启用更高级功能的情况。这部分分享一些进阶配置和常见问题的排查思路。6.1 配置模块参数使用UBX协议EM12-G模块除了输出NMEA语句还支持u-blox自家的UBX二进制协议。通过UBX协议我们可以动态配置模块例如提高输出频率默认1Hz可能不够对于高速移动的机器人可以设置为5Hz或10Hz。启用更多卫星系统确保GPS、GLONASS、北斗、伽利略全部启用。配置消息输出频率选择只输出需要的NMEA语句减少串口数据量。配置通常需要通过发送特定的UBX命令到模块。你可以使用开源的gpsd工具或pyubx2Python库。这里以pyubx2为例pip install pyubx2下面是一个简单的脚本用于将输出频率设置为5Hzimport serial from pyubx2 import UBXMessage, SET # 配置串口 ser serial.Serial(/dev/ttyTHS0, 9600) # 构造CFG-RATE (测量和导航频率) UBX消息 # measRate: 测量周期 (ms) navRate: 导航周期 (测量周期的倍数) timeRef: 时间参考 (1UTC) msg UBXMessage(CFG, CFG-RATE, SET, measRate200, navRate1, timeRef1) # 200ms 5Hz # 将消息发送到模块 ser.write(msg.serialize()) ser.close()重要提示在发送配置命令前最好先备份当前配置。错误的配置可能导致模块不输出数据。大多数模块都有掉电保存的配置内存和易失性内存请注意区分。6.2 常见问题与排查清单没有数据输出检查电源用万用表测量模块VCC和GND之间的电压确保在额定范围内如3.3V。检查天线确认是有源天线且天线接口连接牢固。尝试将天线放到户外完全开阔处。检查串口连接TX/RX是否交叉连接正确用sudo dmesg | grep tty查看系统是否识别到串口设备及有无错误。检查波特率尝试其他常见波特率如115200、38400。EM12-G的默认波特率也可能是115200。数据输出乱码或断断续续地线问题确保J3011和EM12-G模块之间有良好的共地连接。地线不稳定是串口通信大忌。电源干扰尝试使用独立的稳压电源为模块供电而非从J3011的GPIO取电尤其是当天线也需要供电时。缓冲区溢出提高读取数据的频率或在Python中使用ser.reset_input_buffer()定期清空缓冲区。定位精度差或搜星慢天线位置这是最主要因素。确保天线视野开阔远离金属和建筑物遮挡。冷启动 vs 热启动模块完全断电后首次启动为冷启动需要重新搜索星历可能需要几分钟。热启动则快很多。可以配置模块将星历保存到电池备份内存中。查看卫星状态解析$GPGSV(GPS) 或$GLGSV(GLONASS) 等语句查看可见卫星数量和信噪比(SNR)。SNR大于40的卫星越多定位质量通常越好。在ROS2中数据延迟高检查发布频率确保模块的输出频率和ROS2节点的发布频率匹配。检查系统负载使用htop查看J3011的CPU使用率高负载可能影响串口读取线程。使用更高效的数据类型对于高频数据可以考虑使用rosbag2记录并分析时间戳或者使用零拷贝的IPC方式如ROS2的Intra-Process Communication。整个设置过程从硬件连接到ROS2集成最需要耐心的是硬件调试阶段。一旦串口数据流稳定出现后面的软件部分就会顺利很多。记得好的天线和正确的电源是GNSS系统稳定工作的基石在这上面多花点心思后续能避免很多玄学问题。