news 2026/10/1 17:32:24

Unity AI开发:自主移动控制系统与NavMesh寻路避障实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity AI开发:自主移动控制系统与NavMesh寻路避障实战

做游戏开发这么多年,凡是涉及 AI 角色,我都会跟人强调一个观点:一个角色能不能“活”起来,首先看的不是它打了多炫的伤害数字,而是它会怎么走路、怎么转向、怎么避开障碍、怎么从 A 点自己找到 B 点。这套东西在 Unity 引擎里有一个统一的说法,叫智能角色的自主移动控制系统。今天这篇就围绕这个主题,把我实际项目里用到的原理、组件参数、代码结构和坑一次讲清楚。

这篇内容适合正在入门 Unity AI 的开发者,也适合已经做过简单寻路、但觉得角色行为“很傻”想优化的朋友。标题里的第3章,可以理解成一个系列教程里承上启下的位置——前面已经解决了角色怎么动、动画怎么播的问题,现在要解决的是“让它自己决定怎么动”的问题。你把这套系统理解透彻之后,后面的行为树、状态机、以及任务系统都会顺很多。

1. 先搞清楚:智能角色的“自主移动”到底在解决什么问题

很多人第一次接触 Unity 的 NavMeshAgent,觉得无非就是把一个组件拖上去,然后调用SetDestination(),角色就会自己走了。能跑通一个 Demo 和真正能放进项目里稳定运行,中间差的距离很大。我见过不少新手把寻路做出来了,结果角色卡在墙角不停抖、绕远路、走到一半被一个刚添加的箱子挡住直接原地发呆。这些问题的源头,往往不是代码写错了,而是对“自主移动控制系统”这七个字拆解得不够。

1.1 从“会动”到“会自己动”:自主移动的初级形态

我们先定义一下什么叫“自主移动”。在 Unity 里,让一个物体动起来有很多种方式:直接改 Transform、用 Rigidbody 施加力、用 CharacterController 的 Move 方法、甚至用动画根运动 Root Motion 驱动。这些都属于“动”,但它们的共同特点是:动作的发起方是外部逻辑,角色本身没有“决策权”。

自主移动不一样。它的核心特征是有目标、有路径、有感知、有反馈。角色自己拿到一个目标点之后,需要完成以下几个连续动作:

  • 判断自己在哪,目标在哪。
  • 规划一条可达的运动路径,也就是寻路。
  • 沿着路径前进,过程中要避让障碍物和其他角色。
  • 在到达目标、遇到阻挡、目标变化时,调整自己的行为。

你会发现,这已经不是单纯移动的问题了,而是一个轻量级的闭环控制系统。你在 Unity 里看到角色自己绕过栅栏、走台阶、穿过窄门,这些都是这个闭环跑起来的结果。

所以我说,自主移动控制系统是整个游戏 AI 动作层的地基。地基没打牢,后面的行为树再复杂也白搭。

1.2 自主移动控制系统的核心组成(感知-决策-执行)

既然它是一个系统,那就不能只盯着 NavMeshAgent 组件本身。我在项目里习惯把它拆成三层来看:

感知层负责回答“我现在处在什么环境里”。这一层常见的手段包括:射线检测前方有没有障碍、利用 NavMesh 的 SampleHeight 判断地面高度、用距离计算评估目标是否可达。感知结果决定了决策层要不要重新规划路径。

决策层负责回答“下一步我该干什么”。这是状态机、行为树高层逻辑所在的层次。比如角色当前状态是“巡逻”,那么决策层会不断生成一个又一个巡逻点;状态切换成“追击”后,决策层会持续把玩家的位置丢给移动层。

执行层负责把决策变成实际的位移和表现。这一层就是 NavMeshAgent、Rigidbody、CharacterController、动画系统协同工作的区域。执行层的核心要求是“稳”——移动过程中不能抖动、不能穿墙、不能频繁重算路径。

我见过最典型的错误,是有人把决策层的逻辑写进了执行层里。比如在 Update 里每帧判断目标距离、每帧调用 SetDestination,结果路径被疯狂重算,表现就是角色在原地转圈,CPU 也在无辜飙升。后面讲状态管理时我会专门说这个问题怎么避免。

2. 底层方案选型:为什么 Unity 里绝大多数移动都绕不开 NavMesh

自己玩的时候你随便写写没问题,但放到真实项目里,寻路方案基本只有两条路:用 Unity 自带的 NavMesh,或者自己实现一套 A*。我的建议很简单——除非你有非常特殊的需求,否则默认选 Unity 自带的 NavMesh 系统。因为自主移动控制系统最值钱的部分往往不在寻路算法本身,而在寻路之外的避障、动态更新、动画衔接这类工程问题上。

2.1 NavMesh 与 A* 的关系(NavMesh 不是算法,是数据结构)

这里必须纠正一个常见误区:NavMesh 不等于寻路算法。NavMesh 全称 Navigation Mesh,导航网格,它其实是把关卡的地形、台阶、障碍物边界预先处理成一张“可走面的多边形网格”。真正在这张网上找路的过程,背后往往仍然是 A* 或类似算法。

打个比方,A* 是“怎么在地图上找路线”的思路,NavMesh 是“地图本身被提前画成了适合行走的区域”。你提前烘焙好 NavMesh,相当于给角色准备了一张只标记了“人能走”和“人不能走”的地图。寻路时,系统在这张网格上做多边形到多边形的路径搜索,最后输出一条折线路径,再由 NavMeshAgent 平滑跟随。

那为什么不直接用格子地图加 A*,要搞一个 NavMesh?因为格子地图在 3D 游戏里有两个麻烦:一是格子数量巨大,二是有斜坡、台阶、高低差的时候,格子很难表达“可攀爬”或者“可跳跃跨越”。NavMesh 直接基于地面坡度、障碍物高度生成一块块可导航的面,天然解决这两个问题。

2.2 两种常见实现路线:内置 NavMesh 与手写寻路

如果你在项目中看到有人坚持手写 A*,大概原因有三类:网格地图过关、走廊窄路特别多,希望精确控制;或者游戏玩法本身强依赖格子逻辑,比如塔防;又或者是对 NavMesh 在动态场景下的烘焙更新性能不满意,想自己搞一套轻量方案。

我给你列个对比,方便自己判断:

对比维度Unity 内置 NavMesh手写 A* + 格子地图
落地速度快,烘焙 + 组件即可用慢,需要自己处理网格数据与路径平滑
3D 环境适配斜坡、台阶、高差处理出色需要大量额外编码
动态障碍物NavMeshObstacle + Carve 模式可用需要动态更新格子数据
多人同屏性能有局部避障,但仍要控制代理数量可控性强,但算法与数据结构全要自己维护
典型适用大多数 3D 动作、RPG、模拟经营2D 策略、塔防、固定路线迷宫

我的判断标准很简单:如果你的关卡是连续的地面地形,不是网格化的棋盘,那就老老实实用 NavMesh。手写 A* 表面上看是“更底层更酷”,但后续维护量会让你后悔。自主移动控制系统需要的是一套成熟稳定的路径生成机制,而不是让你从头造轮子。

3. 动手搭建一个基础的自主移动控制系统(实操重点)

这一部分是最重要的。我按项目里实际操作顺序,从场景准备、组件参数到最小可运行代码,一步步拆给你看。

3.1 场景准备与 NavMesh 烘焙

先说最简单的场景搭建思路:创建一个 Plane 当底板,再放几个 Cube 当障碍物,然后做一个 Capsule 当玩家角色。注意,Plane 和 Cube 都需要有 Mesh Collider,否则烘焙时不会参与计算。这一步很多新手会忘,结果场景看起来挺正常,但烘焙出来的 NavMesh 就是一片空白。

选中场景里的所有静态物体,在 Inspector 顶部把 Static 勾上,这是烘焙导航网格的前提。如果不勾 Static,烘焙时会默认忽略这些物体。然后打开 Window > AI > Navigation,在 Bake 面板里设置参数:

  • Agent Radius:代理的半径,要跟你实际角色的碰撞半径匹配。
  • Agent Height:代理高度,防止路径穿过低矮缝隙。
  • Max Slope:可行走的坡度角度,默认 45 度。
  • Step Height:允许的台阶高度,比如 0.3 意味着角色能迈上 30 厘米以内的台阶。
  • Min Region Area:小于这个面积的独立可走区会被剔除,防止出现一些不可达的小碎片。

点 Bake,正常情况下场景里会出现一层蓝色覆盖区域,这就是烘焙出来的 NavMesh。蓝色区域必须完整覆盖角色可行走的全部地面,如果有空洞或者裂缝,后面的寻路一定会出问题。

3.2 NavMeshAgent 组件参数逐个拆解

给角色挂上 NavMeshAgent 组件之后,先别急着写代码,把参数过一遍。每个参数我都用实际碰到的现象给你解释,这样你印象能深一点。

Speed:代理的最大移动速度。如果你的角色还有自己的移动速度上限,比如动画根运动或者角色的额外移动脚本,这些数值一定要统一,否则会出现走得比动画快、或者速度变量互相覆盖的问题。

Angular Speed:最大转向角速度。降低这个值,角色转弯更慢,看起来更自然;但调太低会导致角色一边走路一边画圈。我一般习惯把转向平滑交给转向逻辑,这个值会给一个中等水平,比如 360 到 720 之间。

Acceleration:加速度。这个参数影响角色从静止到全速的响应速度。数值太高,角色起步像被兔子蹬了一脚;数值太低,角色追人的时候会很肉。常规值 8 到 12 之间比较舒服。

Stopping Distance:到达目标多少距离内算“到达”。这个很关键。如果设成 0,角色会试图完全贴到点位上,而路径终点往往有误差,于是它会在终点附近一直调整,看起来像原地抖动。建议至少设到 0.5 或者更大,再配合你自己的到达判距来切换状态。

Auto Braking:到达终点附近是否自动减速。巡逻、走到目标点这类场景保持开启比较自然。如果是追击玩家这种需要贴身跟的目标,建议关闭,让代理保持速度,避免到了玩家身边突然一顿一顿。

Radius 和 Height:物理尺寸,影响避障计算,务必与角色实际尺寸接近。

Obstacle Avoidance Type:避障精度。精度越高 CPU 开销越大,普通移动用 High Quality 即可,几百个单位同屏再考虑降档。

3.3 最简移动控制代码:设定目标、路径跟随、到达判定

下面这个代码是自主移动控制系统的最小骨架,我直接给你一个能跑的版本。

using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(NavMeshAgent))] public class SmartAgentController : MonoBehaviour { private NavMeshAgent agent; private Transform target; [SerializeField] private float arriveDistance = 1f; private void Start() { agent = GetComponent<NavMeshAgent>(); } private void Update() { if (target != null) { agent.SetDestination(target.position); } // 到达判定:根据剩余的路径长度判断,而不是直接看剩余距离 if (agent.pathPending == false && agent.remainingDistance <= agent.stoppingDistance && agent.hasPath == true) { // 到达目标之后的逻辑,比如切换到 Idle } } public void MoveToPoint(Vector3 point) { agent.SetDestination(point); target = null; } public void FollowTarget(Transform t) { target = t; } }

这里有个新手最容易看走眼的地方:到达判定不要只看remainingDistance,因为当 agent 还没有计算好路径时,remainingDistance是 0。如果你不先查pathPending,角色可能站在原地就误判成“到目的地了”。这个细节我实际排查过很久,路径计算的第一帧就会遇到这种问题。

另外,SetDestination并不需要每帧调用。在这个例子里,我处理得偷懒了一些,把目标点写在 Update 里方便演示。真实项目里你应该只在目标变化时才调用,后面讲状态管理时我会给更完整的结构。

4. 让移动看起来“聪明”:避障、动态障碍与平滑转向

路径找出来是一回事,角色真正走起来,能不能顺畅绕过别人,能不能在障碍物突然出现时做出反应,这才是“智能”和“死板”的分水岭。

4.1 静态避障 vs 动态避障(RVO 避障原理)

NavMesh 本身只代表静态场景,如果两个角色迎面而行,或者一个箱子突然被推到路中间,纯 NavMesh 路径规划是反应不过来的。这时候要靠动态避障机制。

Unity 的避障底层叫 RVO,Reciprocal Velocity Obstacles,互惠速度障碍。这个概念听起来复杂,实际可以理解成:每个带避障的代理都会推测周围其他代理在一小段时间之后的位置,再去调整自己的移动方向,避免撞上。它跟物理碰撞不一样,物理碰撞是已经撞上再弹开,RVO 是提前绕开。

这就带来一个重要结论:避障层处理的是“短时修正”,路径规划处理的是“全局路线”。角色沿着 NavMesh 路径走,遇到别的角色挡住去路时,避障层会让它稍微偏一下,绕过去之后又自动回到原路径上。所以你在项目中不必对每个 NPC 做复杂的排队逻辑,RVO 已经帮你处理了一部分。

4.2 动态障碍物的两种处理方式(NavMeshObstacle vs 重新烘焙)

动态障碍物的处理,Unity 提供了 NavMeshObstacle 组件。它有两种模式:一种是纯粹的“避开”,一种是“切洞”。我主要讲切洞模式,也就是 Carve 选项。

勾选 Carve 后,障碍物会在 NavMesh 上动态挖出一个洞,正在寻路的代理能感知到这块区域不能走,路径会重新规划;没有勾选 Carve 的障碍物,只是通过避障让代理绕一下。区别在于:如果障碍物很大,比如一辆卡车停在路中间,靠 RVO 避障可能绕不过去,因为路径本身仍然横穿障碍物;而 Carve 模式会强迫路径重新规划,从障碍物周围走。

Carve 模式有个性能铁律:动态切洞操作比较消耗资源,不能大量同时存在。我踩过坑,在关卡里放了十几个 Door 全部开了 Carve,结果运行时 NavMesh 每帧都在重新切洞,帧率直接掉到 30 以下。后来改成只有真正需要动态阻挡的门调用enabled开启,其他一律静态烘焙,性能就回来了。

4.3 平滑旋转与动画混合(让角色移动不僵硬)

移动控制做到这一步,角色已经能找路和避障了,但看起来还不够像人。原因通常是两点:转向太生硬,动画没有和速度匹配。

NavMeshAgent 默认的旋转是希望角色面向移动方向。如果你想让角色转身更自然,可以关掉 agent 的updateRotation,然后自己用Vector3.Slerp或者Quaternion.Slerp做平滑旋转:

private void SmoothRotate(float rotateSpeed) { if (agent.velocity.sqrMagnitude > 0.1f) { Quaternion targetRotation = Quaternion.LookRotation(agent.velocity.normalized, Vector3.up); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * rotateSpeed); } }

动画混合这块,最常见的方式是用 Animator 的参数绑定 agent 的即时速度。比如在 Update 里把agent.velocity.magnitude换算成 0 到 1 之间的量,传给 Animator 的speed参数。但要注意,agent.velocity 和动画速度之间需要做平滑过渡,直接赋值会导致动画像被拉扯一样。我习惯用Mathf.SmoothDamp做一个标量版的平滑:

private float smoothSpeed; [SerializeField] private float smoothTime = 0.2f; private void UpdateAnimatorSpeed() { float targetSpeed = agent.velocity.magnitude / agent.speed; smoothSpeed = Mathf.SmoothDamp(smoothSpeed, targetSpeed, ref smoothSpeedVelocity, smoothTime); animator.SetFloat("Speed", smoothSpeed); }

这套写法是我项目里一直沿用的小模板。重点是让动画曲线和实际移动速度之间留出一个缓冲,看起来就不会有“轮子转了但方向没跟上”的违和感。

5. 移动控制系统的状态管理:别让角色像个愣头青

如果你让一个角色只做一件“走到 A 点”的事,上面的代码已经够了。但只要角色稍微复杂一点,比如巡逻、追击、返回、发呆几种状态来回切换,你就要考虑移动控制系统的状态管理了。不然代码会变成一堆 if 套 else,改一个需求要牵连一大片。

5.1 移动状态机设计与代码骨架

我的习惯是用一个枚举加一个简单状态类来控制。这里直接给你一个参考结构,它是从实际项目里简化出来的:

public enum MovementState { Idle, Patrol, Chase, Return }

每个状态里,需要关心的核心接口有三个:进入状态时做什么、每帧更新做什么、退出状态时做什么。在移动控制系统这个层面,状态机主要管两件事:目标是谁、当前副本,也就是移动目标的合法性。

private MovementState currentState; private void UpdateStateMachine() { switch (currentState) { case MovementState.Idle: // 不发目标 break; case MovementState.Patrol: HandlePatrol(); break; case MovementState.Chase: HandleChase(); break; case MovementState.Return: HandleReturn(); break; } } private void ChangeState(MovementState newState) { if (currentState == newState) return; currentState = newState; // 进入新状态时,重置代理的路径,防止旧路径残留影响新状态 agent.ResetPath(); }

注意ResetPath()这个细节。切换状态时如果不重置路径,代理可能还会按旧路径走一小段,导致角色看起来“违抗指令”。这是状态切换里最容易犯的毛病。

5.2 到达、等待、巡逻的切换逻辑

巡逻是自主移动控制系统里很典型的一个场景。它要回答的问题很简单:我到了这个点,接下来去哪?

我常用一个循环数组的写法:

[SerializeField] private Transform[] patrolPoints; private int patrolIndex; private void HandlePatrol() { if (agent.pathPending) return; if (agent.remainingDistance <= agent.stoppingDistance) { patrolIndex = (patrolIndex + 1) % patrolPoints.Length; agent.SetDestination(patrolPoints[patrolIndex].position); } }

下面这种写法有个优势:巡逻点之间的停留时间可以用Idle状态插入,比如到达之后先切换成Idle,等三秒再切回Patrol,从用户视角看就是角色在站点巡逻时有停顿感,这个可以看巡逻业务的需求。如果你不想做得太复杂,直接在到达后做一个Timer再发下一个点也可以。

5.3 多代理目标分配时的常见坑(同时更新、目标被夺)

当多个角色同时跟进一个目标点,比如一堆小兵同时追主角,最容易出现的问题是目标点被反复竞争。每个小兵每帧都往玩家当前坐标SetDestination,路径重算非常频繁,而且所有小兵很容易挤在同一方向,被彼此的 RVO 避障互相堵住。

我实际项目里的处理方式是:把追击目标的更新频率降低,不要每帧更新,而是每隔 0.2 秒或者 0.5 秒更新一次目标位置。这样既不会出现明显滞后,路径重算压力也小很多。

另外还要注意“目标被夺”的问题。比如两个角色都想去同一个资源点,结果两个都跑过去,发现只能由一个拾取。这时移动控制系统自身不应负责处理资源归属,它只负责走到点附近。到了之后具体谁能拿,要交给上一层的职责判断。很多新手会试图在移动层强行写“先到先得”,结果把移动逻辑和业务逻辑搅在一起,后面想改需求改到头皮发麻。

6. 实测中的常见问题与排查技巧实录

下面这些坑,我基本都在真实项目里撞过。写成速查表,你照着排查能省一下午。

6.1 角色不走、原地抖动、卡死角

这三类问题是寻路系统最典型的“日常”。角色不走,通常的检查顺序是:

  1. 先看 NavMesh 有没有烘焙成功,场景里有没有蓝色覆盖面。
  2. 再看角色挂的 NavMeshAgent 是否 enabled,某个状态切换时有没有被误关。
  3. 再检查SetDestination传入的目标点是不是在 NavMesh 上。用NavMesh.SamplePosition在代码里打个点,确认落在可走区域,这一步是排查“角色没反应”最快的方法。

原地抖动,绝大多数因为StoppingDistance太小,代理在终点附近反复微调。把停止距离调到 0.5 以上再观察。

卡死角,往往发生在角落、窄道,路径规划认为可以通过,但代理的物理半径或避障半径太大,实际身体过不去。这时候调小 Radius,或者检查窄道处的 NavMesh 间隙是否小于代理直径。烘焙参数里 Agent Radius 设大了,也会导致窄通道烘焙出来就是断的。

6.2 寻路路径不合理 / 绕远路

NavMesh 默认只会输出一条可达路径,不代表最短路径。如果你发现角色明显绕远,常见原因是某一片区域被烘焙成了不可达,或者Max Slope设置太小,把本来能走的斜坡剔除了。还有一个容易被忽视的点:相邻区域必须共享 NavMesh 边界,否则寻路会认为两个区域完全不连通。

排查时可以在 Scene 视图打开 Navigation 可视化,把 Areas 显示打开,看路径经过的每个节点的面积颜色是否正常。如果绕远是因为多个区域之间的连接太过稀疏,可以手动添加 Off-Mesh Link,让角色可以跳过两个可走区域之间的缺口。

6.3 Agent 与刚体同时控制的冲突

最后一个坑十个人九个踩:角色既挂了 NavMeshAgent,又挂了 Rigidbody 或者 CharacterController,然后还写了一段物理移动代码。两个移动源同时生效的结果,就是角色要么抽搐,要么被物理撞飞。

规则很简单:如果做寻路移动,就让 NavMeshAgent 作为唯一的移动执行者,物理组件只做碰撞检测,不做力驱动的位移。如果你用的不是 NavMeshAgent 自带移动,而是想自己做,那就干脆把 agent 的updatePosition关掉,只让它提供路径数据,实际位移由你的物理移动代码自己处理。两者只能选一条线。

我这里给一个排查路径是否异常的调试可视化技巧:在角色身上挂一个小的 LineRenderer,或者用Debug.DrawLine,把agent.path.corners绘制出来。这样你一眼就能看到角色当前走的完整路径,比盯着 Game 视图猜疑乱猜快得多。

写在最后的实际建议

根据我自己的项目经验,自主移动控制系统最容易低估的部分不是寻路算法,而是移动时的“手感”和“稳定性”。路径计算通常几百行代码就能跑通,但要让一个角色在上下坡、窄道、拥堵环境里走起来不穿模、不抖动、不原地打转,需要反复调参和大量场景测试。如果你正在做一个多角色的项目,我建议你从一开始就把上面这些状态切换、动态障碍、动画混合的逻辑想清楚,而不是等功能堆起来后再返工。最后再给一个顺手的小技巧:在 Unity 的 Navigation 窗口里,把Show Pathfinding勾上,运行时就能看到代理每一次路径搜索的边界和结果,排查某些“为什么走过去又走回来”的问题时特别好用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 17:31:26

中文车牌识别实战:10类车牌检测与CRNN+CTC识别方案

简介&#xff1a;这是一套基于Python实现的中文多类型车牌检测与识别系统源码&#xff0c;面向计算机视觉初学者、智能交通项目开发者及深度学习实践者&#xff0c;解决复杂场景下蓝牌、黄牌、双层黄牌、农用车牌、警车、校车、教练车、港澳车牌、使领馆车牌及新能源绿牌等10余…

作者头像 李华
网站建设 2026/10/1 17:29:18

SpringBoot露营装备租赁系统:从状态机到订单闭环的毕设实战解析

我前后做了三个SpringBoot的毕设项目&#xff0c;其中露营装备租赁系统这一个&#xff0c;是让我觉得业务闭环最完整、也最能体现Java后端开发核心能力的题目。先说说结论&#xff1a;如果你正在准备计算机毕业设计&#xff0c;又想要一个“看起来有工作量、答辩时能讲清楚逻辑…

作者头像 李华
网站建设 2026/10/1 17:29:00

vssadmin.exe丢失怎么办?系统还原与卷影复制服务修复实操指南

1. 问题概述&#xff1a;vssadmin.exe到底是个什么东西&#xff0c;丢了你为什么抓瞎Windows系统提示“vssadmin.exe文件丢失找不到”&#xff0c;这不是你电脑中了什么花里胡哨的病毒&#xff0c;也不是你的Windows彻底报废了&#xff0c;绝大多数情况下就是系统文件被清理工具…

作者头像 李华
网站建设 2026/10/1 17:28:11

Git Reset深度解析:三种模式、误删恢复与团队协作禁区

先把结论撂这儿&#xff1a; git reset 是我见过被误解最深的 Git 命令&#xff0c;没有之一。 我遇到过不少同事&#xff0c;把 git reset 当"后悔药"用&#xff0c;结果一吃就吃过头&#xff0c;把别人提交的代码也一块儿抹了&#xff1b;也有人把 git reset…

作者头像 李华
网站建设 2026/10/1 17:27:03

Spring Boot小学生在校管理系统毕设设计与实现

做一个基于Spring Boot的小学生在校情况管理系统当毕业设计&#xff0c;是我这两年带学弟学妹做项目时见到的频次最高的题目之一。说它热门&#xff0c;不只是因为学校管理类系统需求量真实存在&#xff0c;更因为这个题目天然适合用Spring Boot这种主流框架去落地——既能体现…

作者头像 李华
网站建设 2026/10/1 17:26:50

FreeSWITCH基于detect_speech和mrcp做实时识别(质检)

freeswitch关于语音识别有detect_speech和play_and_detect_speech 2个模块&#xff0c;怎么区分这两个模块的用途呢&#xff0c;我是这样理解的&#xff0c;做机器人人机交互的时候&#xff0c;首先play_and_detect_speech&#xff0c;因为可控制的参数还蛮多的&#xff0c;尤其…

作者头像 李华