☰
RTK GPS 割草机改造:从定位到路径规划实战
2026/10/22 18:48:45 网站建设 项目流程

简介:这份资源面向农业与园艺自动化方向的开发者、机器人爱好者及嵌入式工程师,提供将廉价现成机器人割草机升级为基于RTK GPS的智能割草机的完整方案,解决传统割草机导航精度低、作业路径不智能的问题。压缩包共183个文件,约4.62MB,涵盖KiCad原理图与封装、C++与头文件源码、音频提示素材、Shell脚本及配置文件等,硬件设计与固件代码并重,便于二次开发与移植。已有391人学习下载,说明该方案在开源割草机器人社区具有一定参考价值。读者可从中获取RTK定位与IMU姿态融合的代码实现、主控与传感器驱动逻辑、声音提示系统设计,以及可直接复用的电路原理图和PCB工程文件,适合作为智能割草机改造项目的起点,帮助快速搭建可运行的软硬件原型并理解各模块间的协作关系。

1. 从一台只会撞墙的割草机说起:RTK GPS 到底能改什么

我手上有一台二手买来的随机碰撞式割草机,工作逻辑就三条:往前走、撞到东西就随机转向、没电了回充。它能把草坪剪短,但边界靠埋线,路径靠运气,边角永远剪不干净,雨天还容易陷进泥里。后来我把它拆了,换成基于 RTK GPS 的定位方案,配合一块能跑路径规划的板子,整机成本控制在可接受范围内,割草覆盖率从“随机游走”变成了可复现的弓字形。这个标题讲的就是这件事:用现成的廉价割草机底盘,加上 RTK GPS 和一套轻量控制逻辑,把它升级成能按规划路径作业的智能割草机。适合谁?适合有基本电子和嵌入式动手能力、愿意折腾定位精度、不想花高价买成品智能割草机的开发者。核心要解决的不是“能不能定位”,而是“定位漂移时怎么保证不压坏花坛、不冲进马路”。

2. RTK GPS 在割草机上的定位链路怎么搭:从基站到车体的最小闭环

2.1 为什么普通 GPS 不够,RTK 补的是哪一段误差

普通单点 GPS 的水平精度在开阔环境下通常只有米级,割草机作业区域往往贴着灌木、墙角、金属围栏,多路径效应会让定位点跳来跳去。RTK 的做法是:一个已知坐标的基准站持续接收卫星信号,计算自身观测值与已知坐标的差值,把差分改正数通过无线链路发给移动站。移动站用改正数修正自己的观测值,水平精度能压到厘米级。对割草机来说,厘米级不是炫技,是刚需——你要让机器贴着边界走,误差超过 20 厘米就可能压到花或者冲出路沿。

常见做法是基准站固定架设在院子开阔处,移动站装在割草机顶部,天线尽量高、尽量远离电机和金属切割盘。差分链路可以用数传电台,也可以用网络 RTK。网络 RTK 省掉了自建基站,但依赖移动网络覆盖,延迟和稳定性受运营商影响。我一般建议第一次做的时候先用自建基站加数传电台,链路可控,排错简单。

2.2 硬件选型:基站、移动站、天线和主控的最小组合

选型上不需要追最新型号,关键是看模块是否支持你需要的频段和差分协议。下面这张表是我实际用过的一套组合,参数按常见规格写,具体型号可以按预算替换。

部件作用关键参数选型注意
基准站接收机产生差分改正数支持 RTCM 3.x 输出最好能同时输出多星座
移动站接收机车体定位更新率 ≥ 5Hz更新率太低,路径跟踪会画龙
数传电台差分链路410-470MHz,功率可调天线要架高,避开金属遮挡
主控板跑路径规划和控制双核以上,带 UART/CAN别用只有单串口的板子
有源天线收卫星信号支持多频馈线尽量短,减少衰减

移动站接收机的更新率是个容易被忽略的参数。5Hz 意味着每 200 毫秒给一次定位,割草机速度按 0.3 米/秒算,两次定位之间走了 6 厘米,路径跟踪还能接受。如果只有 1Hz,两次之间走 30 厘米,弓字形转弯处就会明显切角。

2.3 从串口读到经纬度:最小可运行的数据解析代码

拿到接收机后,第一步不是装车,是在桌面上把数据读通。大多数接收机通过串口输出 NMEA 语句,RTK 固定解的标志是 GGA 语句里的定位质量指示为 4。下面这段 Python 代码用 pyserial 读串口,解析 GGA,打印定位状态和经纬度。

import serial import pynmea2 # 串口参数按接收机手册改,常见 115200 8N1 ser = serial.Serial('/dev/ttyUSB0', baudrate=115200, timeout=1) while True: line = ser.readline().decode('ascii', errors='ignore').strip() if line.startswith('$GNGGA') or line.startswith('$GPGGA'): try: msg = pynmea2.parse(line) # 定位质量:0=无效,1=单点,2=差分,4=RTK固定解,5=RTK浮动解 quality = msg.gps_qual lat = msg.latitude lon = msg.longitude print(f"quality={quality} lat={lat:.8f} lon={lon:.8f}") except pynmea2.ParseError: continue

逻辑说明:只处理 GGA 语句,因为它同时包含定位质量和经纬度。参数说明:baudrate必须和接收机配置一致,常见 9600 或 115200;timeout=1防止 readline 永久阻塞。如果打印出来 quality 长期是 1 或 2,说明差分链路没通,先查电台和基站,不要急着装车。

2.4 把经纬度转成平面坐标:局部投影的工程做法

路径规划如果直接在经纬度上做,距离计算会随纬度变化,弓字形间距会不均匀。常见做法是在作业区域中心选一个原点,用局部等距投影把经纬度转成米制平面坐标。下面这段代码用简单的 equirectangular 近似,适合小区域。

import math # 以作业区域中心为原点 ORIGIN_LAT = 30.000000 ORIGIN_LON = 120.000000 R = 6378137.0 # 地球长半轴,米 def latlon_to_xy(lat, lon): # 纬度方向:纬度差转弧度乘地球半径 x = math.radians(lon - ORIGIN_LON) * R * math.cos(math.radians(ORIGIN_LAT)) y = math.radians(lat - ORIGIN_LAT) * R return x, y

逻辑说明:x 对应东西方向,y 对应南北方向,单位是米。参数说明:ORIGIN_LAT和ORIGIN_LON换成你实际作业区域中心,越接近中心,近似误差越小。这个投影在几百米范围内误差可以忽略,但如果你家草坪超过一公里,建议换 UTM 投影。

3. 路径规划与边界学习:让割草机知道哪里能走、哪里不能走

3.1 边界点采集:推着走一圈还是遥控走一圈

边界学习最直接的办法是手动推着割草机沿草坪边缘走一圈,移动站持续记录 RTK 固定解的点,存成边界点序列。推的时候要注意:天线在车体上的位置和切割盘位置有偏移,记录的是天线轨迹,实际边界要按偏移量做补偿。常见做法是记录天线点后,统一往草坪内侧偏移一个车体半宽加安全距离。

采集时不要只走一圈就完事。我一般会走两圈:第一圈慢速走,确认 RTK 全程固定解;第二圈正常速度走,对比两圈的点位偏差。如果两圈在某个角落偏差超过 10 厘米,说明那个位置有遮挡或多路径,需要调整基站位置或加高天线。

3.2 弓字形路径生成:间距、转弯半径和重叠率

边界点闭合后,用多边形填充算法生成弓字形路径。核心参数是行间距和转弯半径。行间距一般取切割盘有效宽度的 70% 到 80%,留出重叠率,防止漏割。转弯半径不能小于车体最小转弯半径,否则路径跟踪时会出现大偏差。

下面这段代码用 shapely 做多边形内缩和弓字形线生成,只保留核心逻辑。

from shapely.geometry import Polygon, LineString import numpy as np def generate_zigzag(boundary_points, spacing, angle_deg=0): poly = Polygon(boundary_points).buffer(-0.15) # 内缩 15cm 安全距离 minx, miny, maxx, maxy = poly.bounds lines = [] # 按角度旋转后生成水平线,再旋转回来 theta = np.radians(angle_deg) y = miny while y < maxy: line = LineString([(minx - 1, y), (maxx + 1, y)]) clipped = line.intersection(poly) if not clipped.is_empty: lines.append(clipped) y += spacing return lines

逻辑说明:buffer(-0.15)把边界往内缩 15 厘米,给定位误差留余量。参数说明:spacing按切割盘宽度乘 0.75 左右设置;angle_deg可以调整弓字形方向,避开频繁转弯的角落。生成后的线还需要做转弯连接,不能直接跳线,否则割草机会走直线穿过未割区域。

3.3 路径跟踪:纯追踪算法和速度前馈

路径跟踪我用的是纯追踪(Pure Pursuit)的简化版:在路径上找离当前位置最近的点,再往前看一个前视距离,算出转向角。前视距离太短会震荡,太长会切内弯。割草机速度慢,前视距离取 0.5 到 1 米比较合适。

速度控制上,直线段可以跑快一点,转弯前要提前减速。常见做法是根据路径曲率做速度前馈:曲率大就把目标速度降下来。下面这段伪代码说明控制循环的结构。

def pure_pursuit(current_xy, heading, path, lookahead=0.8): # 找最近点索引 dists = [np.hypot(current_xy[0]-p[0], current_xy[1]-p[1]) for p in path] nearest = int(np.argmin(dists)) # 沿路径往前找前视点 target = path[min(nearest + 5, len(path)-1)] dx = target[0] - current_xy[0] dy = target[1] - current_xy[1] target_heading = np.arctan2(dy, dx) steer = np.arctan2(np.sin(target_heading - heading), np.cos(target_heading - heading)) return steer

逻辑说明:nearest + 5是简化写法,实际应按弧长找前视点。参数说明:lookahead越大越平滑但跟踪误差越大;steer输出给转向执行器,需要限幅。如果车体没有转向舵机,而是差速转向,就把 steer 映射成左右轮速差。

4. 避坑与排查:RTK 割草机最容易翻车的五个地方

4.1 定位长期浮动解,固定解迟迟不来

现象:GGA 的 quality 一直是 5 或 2,偶尔跳到 4 又掉回去。原因:差分链路丢包、基站坐标未准确定位、天线遮挡或多路径严重。解决:先确认基站坐标是否用长时间观测平均得到,不要随手填一个;再查电台信号强度,天线尽量架高;最后把移动站天线远离电机和金属,必要时加接地平面。

4.2 弓字形路径在转弯处切角,压到边界外

现象:直线段走得直,一到转弯就冲到边界外。原因:前视距离太大,或者转弯时速度没降下来。解决:把前视距离按曲率动态调整,转弯段降到 0.3 到 0.5 米;同时在路径生成时把转弯半径放大到车体最小转弯半径的 1.5 倍。

4.3 边界点采集时天线偏移没补偿,实际切割越界

现象:按记录边界走,切割盘压到花坛。原因:记录的是天线位置,天线不在车体中心,切割盘有偏移。解决:测量天线到切割盘中心的纵向和横向偏移,在生成路径前把边界点统一平移补偿。补偿方向别搞反,我在这上面翻过车,花了一下午才定位到是符号写反了。

4.4 电机启动后 RTK 失锁,定位跳变

现象:静止时固定解稳定,一开切割电机就掉到浮动解。原因:电机电刷噪声通过电源和空间辐射干扰接收机。解决:接收机单独供电,加磁环和屏蔽线;天线馈线远离电机线束;必要时给接收机加金属屏蔽罩。这个坑很玄学,不同机型表现不一样,但电源隔离基本都能改善。

4.5 雨天或露水导致天线性能下降

现象:晴天正常,早晨有露水时固定解率明显下降。原因:天线罩积水或馈线接头受潮。解决:天线接头做防水处理,天线罩选疏水材质;如果还是不行,在固件里加一条逻辑:固定解丢失超过 3 秒就暂停行走,等恢复再继续。这条后悔药能避免大部分失控。

5. 进阶技巧:用日志回放把路径跟踪调稳

5.1 记录什么:定位、航向、控制输出和时间戳

调路径跟踪最有效的方法不是反复跑车,而是记录日志回放。我一般让主控每 100 毫秒写一行 CSV,字段包括时间戳、经纬度、RTK 质量、航向、目标速度、实际速度、转向输出。跑完一圈把日志拉出来,在桌面上用 Python 画图,一眼就能看出是定位跳变还是控制震荡。

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('mower_log.csv') # 画定位轨迹和规划路径对比 plt.plot(df['x'], df['y'], label='actual') plt.plot(df['target_x'], df['target_y'], '--', label='planned') plt.legend() plt.axis('equal') plt.show()

逻辑说明:actual 是实际轨迹,planned 是规划路径,两条线偏离大的地方就是问题段。参数说明:日志频率不要低于 10Hz,否则转弯段细节看不出来;CSV 文件按日期分,别一直追加,不然回放时找段很麻烦。

5.2 用回放调前视距离和速度前馈系数

回放时重点看两个量:横向跟踪误差和转向输出抖动。横向误差在直线段应该收敛到 5 厘米以内,转弯段允许到 15 厘米。如果直线段就来回摆,说明前视距离太小;如果转弯段切内弯,说明前视距离太大或速度没降够。速度前馈系数可以按曲率线性调,先设一个保守值,回放确认不震荡再往上加。

5.3 固定解丢失时的降级策略

再好的 RTK 也会偶尔丢固定解。我的习惯是在控制循环里加一个状态机:固定解正常时按规划路径走;浮动解超过 2 秒就降速到最低;超过 5 秒就原地暂停,等固定解恢复。暂停时切割电机也停,避免在原地重复切割。这个策略看起来保守,但能避免大部分因为定位跳变导致的越界。

5.4 一个具体技巧:用边界点做安全围栏

除了路径规划,边界点还可以直接当安全围栏用。每 200 毫秒判断一次当前位置是否在多边形内,如果连续 3 次在边界外,立即停机。这个判断用射线法实现,计算量很小,放在主控里完全不占资源。我一般把安全围栏的边界比切割边界再往外放 20 厘米,给定位误差留最后一道余量。

def point_in_poly(x, y, poly): n = len(poly) inside = False px, py = poly[0] for i in range(n + 1): cx, cy = poly[i % n] if ((cy > y) != (py > y)) and \ (x < (px - cx) * (y - cy) / (py - cy) + cx): inside = not inside px, py = cx, cy return inside

逻辑说明:标准射线法,返回 True 表示在边界内。参数说明:poly是边界点列表,顺序要闭合;判断频率不用太高,200 毫秒足够。这个函数我一般和路径跟踪放在同一个循环里,先判安全再发控制指令。

这套方案我前后调了几个月,最大的教训是:别一上来就追高精度,先把差分链路和电源干扰解决掉,固定解稳定了,后面路径跟踪就是调参数的事。另一个习惯是每次改完参数必须回放日志,凭感觉调车十次有八次是白跑。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询