RTK GPS 割草机改造:从定位到路径规划实战
简介这份资源面向农业与园艺自动化方向的开发者、机器人爱好者及嵌入式工程师提供将廉价现成机器人割草机升级为基于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, baudrate115200, timeout1) while True: line ser.readline().decode(ascii, errorsignore).strip() if line.startswith($GNGGA) or line.startswith($GPGGA): try: msg pynmea2.parse(line) # 定位质量0无效1单点2差分4RTK固定解5RTK浮动解 quality msg.gps_qual lat msg.latitude lon msg.longitude print(fquality{quality} lat{lat:.8f} lon{lon:.8f}) except pynmea2.ParseError: continue逻辑说明只处理 GGA 语句因为它同时包含定位质量和经纬度。参数说明baudrate必须和接收机配置一致常见 9600 或 115200timeout1防止 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_deg0): 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, lookahead0.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], labelactual) plt.plot(df[target_x], df[target_y], --, labelplanned) 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 毫秒足够。这个函数我一般和路径跟踪放在同一个循环里先判安全再发控制指令。这套方案我前后调了几个月最大的教训是别一上来就追高精度先把差分链路和电源干扰解决掉固定解稳定了后面路径跟踪就是调参数的事。另一个习惯是每次改完参数必须回放日志凭感觉调车十次有八次是白跑。希望帮到你。本文还有配套的精品资源点击获取