水下机器人(ROV)在水下作业时,最棘手的问题往往不是动力,而是感知。浑浊水域里摄像头常常只能看清几十厘米,GPS 的信号在水下会快速衰减,激光测距在水里的传播损耗也很大,真正能在较远距离稳定探测障碍物的手段,主要就是声呐。可是,单路声呐只能覆盖一个方向,碰到桥墩、网箱、船底这类复杂环境,机器人依旧可能撞上去。
这篇文章围绕一套“避障声呐 + 雷达表盘 + 自主避障控制”的完整闭环展开:多路避障声呐负责采集不同方向的障碍物距离,雷达表盘把距离读数量化成 360° 可视界面,接近警报和自主避障控制负责在碰撞前做出反应。看完后,你可以先用 Python 搭出一个可运行的雷达表盘与控制模拟原型,也能了解真实水下机器人接入声呐时的工程要点。
1. 背景:水下机器人为什么需要避障声呐与雷达表盘
1.1 水下测距的困难与声呐的优势
很多人会直接把陆地上的测距方案搬到水下,比如超声波测距、激光雷达、摄像头视觉测距。其实水下环境和空气环境差异非常大。普通空气超声波模块的换能器和电路并不是为水密环境设计的,直接泡水很快就会失效;激光在水中衰减严重,射程会大幅缩短;摄像头视觉测距虽然能提供丰富纹理,但水下浊度高、光照不均,单目测距往往需要已知目标尺寸和精确标定,双目测距又对场景纹理和基线距离有较高要求。
相比之下,声呐利用声波在水中传播损耗较小的特点,可以稳定工作几十米甚至更远。它的基本原理并不复杂:发射换能器发出一个声脉冲,声波遇到障碍物后反射回来,接收换能器记录到回波的时间,再结合水中声速算出距离。因为声波在深水、浑水、弱光环境下都能传播,所以避障声呐成为水下机器人测距的首选方案。
除了声呐,TOF 测距、视觉双目测距、单目测距等方案也被讨论得很多。它们在水下并非不能用,但工程化难度更高。更稳妥的做法是:用声呐作为避障主传感器,再用视觉、深度计、惯性导航做多传感器融合,互相弥补短板。
1.2 全方位避障声呐解决什么问题
单路声呐相当于“一根盲杖”,只能告诉你正前方有没有障碍物。水下机器人运动时不仅有前进,还有后退、侧移、上浮和下潜,如果只在正前方装一个声呐,后方和左右两侧就全是盲区。
全方位避障声呐的思路,是在机器人艏部、艉部、左右舷等位置布置多路声呐,形成一个 360° 的测距环。每路声呐返回一个“角度 + 距离”数据对,上位机把这些数据合并成一张极坐标雷达图,就能直观看到机器人周围哪些方向有障碍、距离多远。再配合接近警报和避障控制算法,机器人就能在即将碰撞前自动减速、转向或后退。
典型的应用场景包括:水产养殖网箱巡检、桥墩和坝面检测、船底检查、水下搜救、科研采样。这些场景的共同特点是障碍物多、空间狭小、水流可能把机器人推向结构物,人工遥控反应往往来不及,因此必须有一套自动避障系统兜底。
2. 系统整体设计:从声呐数据到避障动作
2.1 系统分层架构
一个完整的避障声呐测距显示系统,可以分成五层:
感知层:多路避障声呐阵列 ↓ 角度、距离、置信度 处理层:下位机控制板 / 上位机程序 ↓ 滤波、坐标转换、决策 显示层:雷达表盘(360° 距离显示) ↓ 状态与告警 决策层:接近警报、自主避障控制 ↓ 控制指令 执行层:推进器、舵机、报警设备感知层的声呐负责原始数据采集。处理层可以做数据滤波、波束角度修正、阈值判断和避障决策。显示层把处理后的数据画成雷达表盘,方便岸上人员观察。决策层根据距离阈值输出动作,最终由执行层改变机器人的运动状态。
这套分层设计的优点是:每一层都可以单独替换。比如模拟阶段可以把声呐层换成仿真数据,调试阶段可以只改决策参数,而不影响显示逻辑。
2.2 雷达表盘要表达什么信息
雷达表盘的核心信息是“机器人周围一圈的障碍物距离”。它使用极坐标,圆心代表机器人本身,半径代表距离,角度代表障碍物相对机器人的方位。
为了保证操作员能快速判断风险,表盘上通常同时显示三组信息:
- 障碍物散点:每个声呐探测到的障碍物位置,用点的形式画在对应角度和距离上。
- 安全范围圈:用不同颜色画出安全半径、警告半径、危险半径,例如绿色 1.5m、橙色 0.8m、红色 0.4m。
- 最近障碍与动作建议:实时显示当前最近障碍的方向、距离,以及避障控制器给出的动作,比如 stop_reverse、turn_left、turn_right。
这样一来,即使操作员没有盯着原始数字,也能通过颜色和图形变化快速判断机器人是否处于危险状态。
3. 声呐测距原理与选型
3.1 声波测距的核心公式
无论声呐的硬件形态怎么变化,脉冲回波测距的核心公式都是一样的:
distance = sound_speed * round_trip_time / 2声波从发射到接收,走了一个来回,所以要除以 2。这个公式对应的就是 TOF 测距原理(Time of Flight,飞行时间法)。如果声呐是双频体制,通常会同时发射高频和低频声波,高频负责近距离高分辨率,低频负责远距离穿透探测,再把两组数据融合输出,这就是双频测距原理在水下避障中的常见用法。
水中声速并不是固定常数,大约在 1500m/s 附近变化。温度、盐度、静水压力都会影响声速,偏差通常在 1% 到 3% 之间。对近距离避障来说,这个误差可以接受;但如果要做精确测绘,就需要用声速剖面仪或者温盐深传感器修正声速。
3.2 常见测距声呐体制与应用
市面上的避障声呐可以分成几类:
- 单波束声呐:一次测量只覆盖一个角度,结构简单、成本低,适合多点布置组成全方位阵列。
- 多波束声呐:一次发射覆盖一个扇区,能快速得到前方扇形区域的多个距离点,适合前视避障。
- 机械扫描声呐:通过电机旋转换能器,可以获得 360° 扇区数据,但扫描周期长,不适合高速运动避障。
- FMCW/LFMCW 声呐:采用调频连续波体制,通过发送频率随时间变化的连续信号,利用回波和本振信号的频率差计算距离和速度,适合对运动目标测距,也能提供多普勒信息。
对“全方位添加避障声呐”这个需求来说,单波束声呐阵列和多波束声呐是两种主流路线。前者成本可控、布局灵活,雷达表盘很容易把每一路数据画成一个点;后者数据密度高,雷达表盘画出来更像扇区色块,但数据协议和显示逻辑会更复杂。
3.3 多声呐布局与安装注意
要形成全方位测距,声呐的安装角度非常重要。一个常见布局是 8 路声呐,每 45° 一个方向,覆盖正前、右前、正右、右后、正后、左后、正左、左前。
0°(前) 315° 45° 270° 本机 90° 225° 135° 180°(后)安装时需要注意几个问题:
- 波束角和盲区:声呐波束不是一根线,而是一个锥形区域,近距离会存在盲区,安装时要留出合理距离。
- 换能器必须浸水:大多数水声换能器需要和水直接接触,或者通过透声窗耦合,中间不能留空气层。
- 避免推进器流噪声干扰:声呐安装位置尽量远离螺旋桨和气泡区域,否则回波会频繁出现噪声。
- 安装角度要标定:雷达表盘上的角度,必须和机身的真实方位一致,否则避障控制会计算出错误的转向方向。
4. 数据模型、警报分级与避障策略
4.1 用“角度 + 距离”表示全方位数据
雷达表盘的数据模型可以抽象成一张字典表,键是声呐安装角度,值是该方向测得的距离。比如 8 路声呐的数据可能是:
direction_distances = { 0: 3.2, 45: 1.6, 90: 0.7, 135: 2.1, 180: 4.5, 225: 3.8, 270: 1.8, 315: 2.9 }在这个模型里,0° 表示正前,90° 表示正右,180° 表示正后,270° 表示正左。雷达表盘绘制时,只需要把角度转换成弧度,把距离作为半径,就能把一组数据点映射到极坐标图上。
这个模型的好处是简洁、直观,也方便后续扩展。如果声呐不止 8 路,字典里增加更多角度即可;如果一路声呐能返回多个目标回波,可以给同一个角度保存多个距离值,只是显示和控制逻辑会复杂一些。
4.2 接近警报的分级阈值
接近警报不应该是“到了某一个距离突然响”,而应该分级处理。常见做法是设定三个半径:
| 级别 | 半径 | 含义 | 典型动作 |
|---|---|---|---|
| 安全区 | >= 1.5m | 暂时没有碰撞风险 | 正常前进或转向 |
| 警告区 | 0.8m 到 1.5m | 存在潜在碰撞风险 | 降低航速,准备避障 |
| 危险区 | < 0.8m | 碰撞风险很高 | 立即停车、后退或转向 |
实际距离阈值要根据机器人的尺寸、航速、制动距离来调整。大机器人和小机器人的制动能力差别很大,不能照搬别人的参数。另外,为了避免单帧噪声导致警报频繁抖动,最好让警报在连续 N 帧都满足条件时才触发,这叫做“迟滞判断”。
4.3 自主避障控制策略
自主避障控制的核心,是根据“最近障碍方向”和“最近障碍距离”选择一个动作。这里采用一个简化但实用的策略:
- 最近障碍在正前方向(0° 左右),说明前方有障碍,优先停车或后退。
- 最近障碍在右前或正右方向,说明右侧有障碍,控制机器人向左转。
- 最近障碍在左前或正左方向,说明左侧有障碍,控制机器人向右转。
- 最近障碍在后方,说明后方有障碍,可以向前行驶脱离。
在实际机器上,转向指令还会叠加推进器推力分配。比如左右两个水平推进器,左转时右侧推进器增大推力,右转时左侧推进器增大推力。这个动作映射关系必须和推进器布局一致,否则避障时可能越转越靠近障碍物。
5. 环境准备与项目结构
5.1 Python 运行环境
为了让你能直接复现,下面给出一个纯 Python 的模拟原型。它不依赖真实声呐硬件,只需要安装 Python 和两个常用库。
- 操作系统:Windows、Linux、macOS 均可。
- Python 版本:建议 3.8 或更高版本,本文示例以常见 Python 3 环境为例。
- 依赖库:numpy、matplotlib,后续如果接真实串口声呐,还需要 pyserial。
安装命令:
pip install numpy matplotlib pyserial其中 pyserial 在本节模拟代码中不是必需的,只有第 6.5 节接真实串口设备时才需要。版本不需要刻意锁定,以你能正常安装的最新稳定版为主即可。
5.2 项目目录结构
建议新建一个目录,比如sonar_radar,把代码按模块拆分:
sonar_radar/ ├── main.py ├── sonar_simulator.py ├── radar_display.py ├── controller.py └── requirements.txt各文件职责如下:
- sonar_simulator.py:模拟多路声呐数据源。
- radar_display.py:负责绘制极坐标雷达表盘。
- controller.py:实现接近警报与自主避障决策。
- main.py:把三个模块串起来,启动动画显示。
- requirements.txt:记录依赖库。
这种拆分方式也适合后续替换真实数据源。你只需要保留 radar_display.py 和 controller.py,把 sonar_simulator.py 换成串口读取模块即可。
6. 实战:搭一个避障声呐雷达显示与控制原型
6.1 编写模拟声呐数据源
先编写 sonar_simulator.py。这个模块会模拟 8 路声呐,方向每 45° 一个,同时模拟一个“威胁障碍物”在不同方向之间切换,并让它的距离随时间波动,从而触发警报和避障动作。
# 文件路径:sonar_radar/sonar_simulator.py import math import random class SonarSimulator: """模拟多路避障声呐的数据源。 方向定义:0° 表示正前,90° 表示正右, 180° 表示正后,270° 表示正左,步进 45°。 """ DIRECTIONS = [0, 45, 90, 135, 180, 225, 270, 315] def __init__(self, seed=None): random.seed(seed) self.t = 0.0 def read_once(self): """读取一次全部声呐数据,返回 {角度: 距离}。""" self.t += 0.1 data = {} for d in self.DIRECTIONS: # 大部分方向维持在 4m 左右,叠加少量高斯噪声 data[d] = self._clamp(4.0 + random.gauss(0, 0.06), 0.2, 8.0) # 模拟一个威胁障碍物,方向随时间切换,距离在 0.35m~1.95m 之间变化 threat_angle = self.DIRECTIONS[int(self.t / 2.0) % len(self.DIRECTIONS)] threat_dist = 0.35 + 1.6 * abs(math.sin(self.t * 0.8)) data[threat_angle] = self._clamp(threat_dist, 0.2, 8.0) return data @staticmethod def _clamp(value, low, high): return max(low, min(high, value))模拟数据源中,大部分方向的距离都稳定在 4m 左右,这样雷达表盘上不会到处都是警报点。威胁障碍物的距离在 0.35m 到 1.95m 之间变化,因此会周期性进入危险区和警告区,控制逻辑就能被真实触发。
6.2 编写雷达表盘显示模块
接下来编写 radar_display.py。这个模块用 Matplotlib 的极坐标绘制雷达表盘,包括安全范围圈、警告范围圈、危险范围圈,以及实时更新的障碍物散点。
# 文件路径:sonar_radar/radar_display.py import numpy as np import matplotlib.pyplot as plt SAFE_RADIUS = 1.5 WARN_RADIUS = 0.8 DANGER_RADIUS = 0.4 class RadarDisplay: """雷达表盘,用于实时显示多路声呐的距离读数。""" def __init__(self, directions): self.fig = plt.figure(figsize=(7, 7)) self.ax = self.fig.add_subplot(111, projection='polar') self.directions = directions self._init_canvas() def _init_canvas(self): # 0° 指向正上方,角度按顺时针方向增加 self.ax.set_theta_zero_location('N') self.ax.set_theta_direction(-1) self.ax.set_rlim(0, 5) self.ax.set_rticks([0.4, 0.8, 1.5, 3.0, 5.0]) theta = np.linspace(0, 2 * np.pi, 200) self.ax.plot(theta, [SAFE_RADIUS] * 200, 'g--', lw=1, label="安全 1.5m") self.ax.plot(theta, [WARN_RADIUS] * 200, color='orange', lw=1, label="警告 0.8m") self.ax.plot(theta, [DANGER_RADIUS] * 200, 'r-', lw=1, label="危险 0.4m") self.points = self.ax.scatter([], [], s=80, cmap='coolwarm', vmin=0, vmax=5) self.ax.legend(loc='upper right', bbox_to_anchor=(1.2, 1.12)) def update(self, distance_map, min_angle, min_dist, action): angles = np.deg2rad(list(distance_map.keys())) dists = np.array(list(distance_map.values())) self.points.set_offsets(np.c_[angles, dists]) self.points.set_array(dists) self.ax.set_title( f"避障声呐雷达表盘 | 最近障碍 {int(min_angle)}° " f"{min_dist:.2f}m | 动作: {action}" ) return self.points, self.ax.title雷达表盘上的点颜色越偏红,代表距离越近,越偏蓝代表距离越远。三个参考圆环能帮你快速判断障碍物落在哪个风险区间。这里把 0° 设置到正上方,和很多雷达显示习惯一致,控制逻辑中的“正前”就对应表盘上方。
6.3 编写接近警报与避障控制器
controller.py 负责计算最近障碍方向,并根据距离范围输出动作。为了演示,这里用 print 输出动作切换日志,真实项目中可以把这段日志接到告警灯、蜂鸣器或岸基监控平台。
# 文件路径:sonar_radar/controller.py class AvoidanceController: """根据声呐距离数据生成接近警报与自主避障动作。""" def __init__(self, warn=0.8, danger=0.4): self.warn = warn self.danger = danger self.last_action = None def decide(self, distance_map): min_angle = min(distance_map, key=distance_map.get) min_dist = distance_map[min_angle] if min_dist >= self.warn: action = "forward" elif min_angle <= 60 or min_angle >= 300: # 最近障碍在正前方向 action = "stop_reverse" if min_dist < self.danger else "slow_down" elif 60 < min_angle <= 150: # 最近障碍在右前/正右方向 action = "turn_left" elif 150 < min_angle <= 240: # 最近障碍在后方 action = "forward" else: # 最近障碍在左前/正左方向 action = "turn_right" if action != self.last_action: print(f"[动作切换] 最近障碍 {int(min_angle)}° 距离 {min_dist:.2f}m -> {action}") self.last_action = action return min_angle, min_dist, action注意,这里的角度划分和表盘方向是一致的。0° 是正前,90° 是正右,所以右前方向的障碍应该左转,左前方向的障碍应该右转。很多新手在调试时把动作配置反了,原因就是角度坐标系和转向映射没有统一。
6.4 主程序并联调
最后编写 main.py,把三个模块串起来。这里使用 Matplotlib 的 FuncAnimation 实现动画刷新,每 200ms 读取一次模拟声呐数据并更新雷达表盘。
# 文件路径:sonar_radar/main.py import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from sonar_simulator import SonarSimulator from radar_display import RadarDisplay from controller import AvoidanceController def main(): sim = SonarSimulator(seed=42) display = RadarDisplay(sim.DIRECTIONS) controller = AvoidanceController() def animate(frame): distance_map = sim.read_once() min_angle, min_dist, action = controller.decide(distance_map) return display.update(distance_map, min_angle, min_dist, action) anim = FuncAnimation(display.fig, animate, interval=200, blit=True) plt.show() if __name__ == "__main__": main()在项目目录下运行:
python main.py预期效果是:弹出一个极坐标雷达窗口,窗口里有三个参考圆环,障碍物点不断刷新。控制台会在动作切换时打印类似下面的日志:
[动作切换] 最近障碍 45° 距离 0.35m -> stop_reverse [动作切换] 最近障碍 45° 距离 1.20m -> slow_down [动作切换] 最近障碍 90° 距离 0.42m -> turn_left如果程序运行正常,说明你已经打通了“声呐数据采集 → 距离显示 → 警报判断 → 避障决策”的完整链路。
6.5 从模拟数据切换到真实声呐
模拟数据只是验证逻辑,真实项目中如果需要接入声呐,可以把 SonarSimulator 替换成串口读取函数。下面是一个串口读取示例,仅展示思路,协议部分必须依据你的实际设备文档修改。
# 该示例仅为串口接入思路,需要按声呐硬件协议调整 try: import serial except ImportError: serial = None def read_serial_sonar(port="/dev/ttyUSB0", baudrate=115200): if serial is None: raise RuntimeError("未安装 pyserial,请先执行 pip install pyserial") with serial.Serial(port, baudrate, timeout=0.5) as ser: line = ser.readline().decode(errors="ignore").strip() # 假设协议为: 角度,距离,状态;实际请按设备文档修改 angle_str, dist_str, _ = line.split(",") return float(angle_str), float(dist_str)真实声呐的数据往往不是一行字段,而是二进制帧,包含帧头、角度、距离、校验和等字段。接入时首先要确认波特率、数据位、校验位,并且用厂家提供的上位机先验证数据格式,再写解码代码。
7. 真实水下机器人集成要点
7.1 声呐数据接入与协议约定
真实水下机器人的声呐数据,不一定会直接发给显示程序。常见结构是:多路声呐先接入下位机控制板,由控制板统一采集和滤波,再通过串口、CAN 或以太网传给上位机。如果使用带脐带缆的 ROV,声呐数据通常会上传到岸基电脑显示;如果是自主水下机器人,数据会在机载计算机里直接处理。
为了保证多路数据不乱,协议中至少要包含三个信息:角度、距离、置信度或状态。角度告诉系统这是哪个方位的数据,距离用于雷达显示和警报判断,置信度用于判断当前回波是否可信。协议建议加上校验字段,比如 CRC 校验,避免一帧数据受干扰后导致整个避障系统误判。
7.2 数据滤波与航向补偿
声呐在水下容易受到气泡、多径效应、旁瓣干扰,距离值偶尔会突然跳变。工程上常用的办法有:
- 中值滤波:取连续 N 个数据的中间值,能有效去除脉冲式跳变。
- 滑动平均:适合抑制随机噪声,但会带来一定延迟。
- 突变剔除:如果当前距离和前一帧相差太大,直接丢弃,等连续几帧稳定后再更新。
另外,声呐安装角度是相对于机器人机身的,而雷达表盘通常要显示相对于某个稳定坐标系的障碍物方位。如果机器人自身在旋转,就需要把罗盘航向角加进去:
显示角度 = 声呐安装角度 + 机器人当前航向角不做这一步,机器人一旦转头,雷达表盘上的障碍物位置就会发生旋转,操作员容易误判方向。
7.3 水池测试与安全边界
再好的避障算法,也不能跳过水池测试。测试时建议按下面的顺序推进:
- 先在空气中用模拟数据验证显示和控制逻辑。
- 再把声呐安装在测试支架上,放入静水池,验证通信和测距是否稳定。
- 接着以很低的航速靠近障碍物,观察警报和避障动作是否及时。
- 最后才进入模拟作业场景,逐步提高航速。
水下机器人测试涉及推进器、防水舱、供电系统,安全边界必须提前确认。测试前要检查声呐换能器是否正常耦合、推进器是否已设置急停、测试水池是否获得授权且周围无人。任何涉及真实推进和真实声呐的测试,都应该在受控环境下由专业人员操作。
8. 常见问题与排查思路
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 雷达表盘画面不刷新 | 数据源阻塞,串口没打开,或动画刷新函数异常 | 检查 read_once 返回是否正常,串口是否可读,加打印观察 |
| 距离数值频繁跳变 | 水流气泡、多径效应、声呐旁瓣干扰 | 增加中值滤波,丢弃突变帧,调整声呐安装位置 |
| 警报频繁误报 | 警告阈值设置过宽,环境中有悬浮物 | 收紧警告半径,加入连续 N 帧确认机制 |
| 避障转向方向反了 | 角度坐标定义和转向映射不一致 | 核对 0°、90° 与左右转向的对应关系 |
| 显示角度和实际障碍位置不一致 | 声呐安装角未标定,或没有融合航向角 | 重新标定安装角,引入罗盘航向补偿 |
| 刷新卡顿明显 | 绘图刷新太快,或主线程被耗时操作阻塞 | 适当调大 interval,串口读取放到子线程,用队列传数据 |
| 串口输出乱码 | 波特率不一致,电平不匹配,地线干扰 | 核对设备波特率,加隔离模块,检查共用电源 |
遇到异常时,不要急着改算法,先确认“原始数据是否可信”。如果声呐原始距离本身就是跳动的,后面所有显示和决策都会不准。
9. 最佳实践与工程建议
9.1 多传感器融合而不是只信声呐
声呐并不是万能的。它可能漏掉细小缆绳,也可能被气泡干扰。实际系统中,建议把避障声呐和深度计、惯性测量单元、电子罗盘、摄像头组合使用。声呐负责距离和避障,罗盘负责方向,惯性单元负责姿态稳定,摄像头负责近距离纹理和作业细节。
9.2 阈值参数要可配置
接近警报的距离阈值和危险距离,应该做成配置文件,而不是写死在代码里。不同水域、不同航速、不同机器人重量,需要的制动距离完全不同。可配置参数包括:
- 安全区、警告区、危险区半径。
- 连续触发帧数。
- 最大前进航速。
- 转向幅度和避障持续时间。
这样现场调试时不需要改代码,只需要调整参数文件。
9.3 记录完整日志
水下机器人一旦发生碰撞或误动作,事后定位问题非常困难。建议把每一帧声呐数据、角度、距离、计算出的动作、推进器指令、时间戳全部记录到日志文件。这样回放时能还原当时的现场情况,也能用于优化避障策略。
9.4 执行层一定要有安全兜底
自主避障控制只是软件决策,执行层仍然需要物理安全措施。机器人应该具备独立的急停按钮,紧急情况下能切断推进器电源。如果机器人支持上浮,某些极端情况下可以把“上浮脱困”作为最高优先级动作。这些安全逻辑不应该依赖显示程序,而应该在下位机控制板里独立实现。
10. 总结与后续学习路线
把上面几个模块跑通后,你对“避障声呐水下机器人测距显示雷达表盘”就有了一个完整闭环:多路声呐采集角度和距离,雷达表盘实时显示障碍物分布,接近警报根据阈值触发,避障控制器输出转向、停车或后退建议。
下一步可以按自己的需求扩展。如果手头有真实声呐,先学习它的协议格式,用厂家上位机采集原始数据,再把数据接入现在的雷达表盘;如果想做更完整的避障系统,可以加入 IMU 姿态融合、深度计、目标识别声呐和路径规划模块;如果想优化显示效果,可以给雷达表盘增加航迹历史、目标编号和报警音效。
建议先不要急着上硬件,先在模拟环境里把雷达表盘和控制策略调顺,再进水池验证参数。如果你在跑代码或者改造真实声呐时遇到问题,欢迎把现象发在评论区,一起排查。