Unity 无人机物理模拟开发:从零打造穿越机手感
记录了在 Unity 中构建高拟真 FPV 穿越机物理模拟系统的过程。通过手动计算力和力矩,结合 PID 控制器控制角速度,实现了 Pitch、Roll 和 Yaw 的独立控制。引入了电机惯性低通滤波消除瞬移响应,利用射线检测实现地面效应气垫感,并采用非线性空气阻力模拟真实风阻。支持 Acro(手动)与 Angle(自稳)双模式切换,包含怠速保护与 PID 限幅等稳定性优化。最终构建了一个具备惯性、风阻及物理反馈的飞行模拟器架构。

记录了在 Unity 中构建高拟真 FPV 穿越机物理模拟系统的过程。通过手动计算力和力矩,结合 PID 控制器控制角速度,实现了 Pitch、Roll 和 Yaw 的独立控制。引入了电机惯性低通滤波消除瞬移响应,利用射线检测实现地面效应气垫感,并采用非线性空气阻力模拟真实风阻。支持 Acro(手动)与 Angle(自稳)双模式切换,包含怠速保护与 PID 限幅等稳定性优化。最终构建了一个具备惯性、风阻及物理反馈的飞行模拟器架构。

本文记录了在 Unity 中构建一个高拟真 FPV 穿越机(Drone)物理模拟系统的过程。从基础的 PID 控制到引入空气动力学阻力、地面效应和电机惯性,一步步逼近真实的飞行手感。
最初的实现使用键盘鼠标控制,但这对于模拟穿越机来说完全不够。真实的穿越机需要细腻的模拟量输入。
Unity 的 Rigidbody 提供了基础物理,但要飞得像穿越机,必须手动计算力和力矩。
这是飞控的灵魂。我们实现了三个独立的 PID 控制器分别控制 Pitch、Roll 和 Yaw 的角速度。
rb.angularVelocity。将 PID 输出分配到四个电机。采用标准的 Quad X 布局:
// FL (左前): +Pitch +Roll -Yaw
// FR (右前): +Pitch -Roll +Yaw
// BL (左后): -Pitch +Roll +Yaw
// BR (右后): -Pitch -Roll -Yaw
基础 PID 能飞,但手感像'在真空中飞行'或者'完美的刚体'。为了真实感,引入了三个关键特性:
真实的电机从 0 加速到 100% 需要时间。
Mathf.Lerp 对油门输入进行低通滤波。// 更新动力 (在 FlightController 的 FixedUpdate 中调用)
public void UpdatePhysics(float targetThrottle)
{
// 模拟电机惯性 (一阶低通滤波)
// 从当前油门平滑过渡到目标油门
float dt = Time.fixedDeltaTime;
currentThrottle = Mathf.Lerp(currentThrottle, targetThrottle, dt * motorResponseSpeed);
// 1. 施加升力 (垂直于机臂向上)
Vector3 force = transform.up * (currentThrottle * maxThrust);
rb.AddForceAtPosition(force, transform.position);
// 2. 施加反扭矩 (Yaw 控制)
// 顺时针旋转的电机,会给机身施加逆时针的扭矩,反之亦然
float torqueDir = isClockwise ? -1f : 1f;
float torqueMagnitude = currentThrottle * maxThrust * torqueFactor * torqueDir;
rb.AddTorque(transform.up * torqueMagnitude, ForceMode.Force);
}
当无人机贴近地面时,下洗气流受阻,升力会增加。
// --- 地面效应 (Ground Effect) ---
RaycastHit hit;
if (Physics.Raycast(transform.position, Vector3.down, out hit, groundEffectMaxHeight))
{
float ratio = 1.0f - (hit.distance / groundEffectMaxHeight);
float groundEffectMultiplier = 1.0f + (ratio * groundEffectLiftFactor);
m1 *= groundEffectMultiplier;
m2 *= groundEffectMultiplier;
m3 *= groundEffectMultiplier;
m4 *= groundEffectMultiplier;
}
Unity 默认的 Drag 是线性的 ( F ∝ v ),这让无人机感觉像在水里游。
// 计算平方阻力:F = -v * |v| * dragFactor
Vector3 dragForceLocal = Vector3.zero;
dragForceLocal.x = -localVel.x * Mathf.Abs(localVel.x) * dragFactors.x;
dragForceLocal.y = -localVel.y * Mathf.Abs(localVel.y) * dragFactors.y;
// 垂直阻力通常较大
dragForceLocal.z = -localVel.z * Mathf.Abs(localVel.z) * dragFactors.z;
无人机操作一共配置了两种模式。
if (mode == FlightMode.Angle)
{
// --- Angle Mode (自稳模式) ---
// 摇杆输入映射为目标角度 (-45 ~ 45 度)
float targetPitchAngle = input.Pitch * maxTiltAngle;
float targetRollAngle = -input.Roll * maxTiltAngle;
// Unity Z 轴旋转方向可能需要反转
// 获取当前角度 (将 0-360 转换为 +/- 180)
Vector3 currentEuler = transform.localEulerAngles;
float currentPitch = Mathf.DeltaAngle(0, currentEuler.x);
float currentRoll = Mathf.DeltaAngle(0, currentEuler.z);
// 外环 P 控制:角度误差 -> 目标角速度
// Pitch: 目标 - 当前 (因为后面混控器 Pitch 反转了,所以这里保持 目标 - 当前)
targetPitchRate = (targetPitchAngle - currentPitch) * angleKP;
// Roll: 当前 - 目标 (反转逻辑,防止正反馈翻滚)
// 右滚是负角度,如果不反转,误差为正,导致继续右滚
targetRollRate = (currentRoll - targetRollAngle) * angleKP;
// Yaw 轴通常保持 Rate 模式
targetYawRate = input.Yaw * 150f;
}
else
{
// --- Acro Mode (特技/手动模式) ---
// 摇杆输入直接映射为目标角速度 (-200 ~ 200 度/秒)
targetPitchRate = input.Pitch * 200f;
targetRollRate = input.Roll * 200f;
targetYawRate = input.Yaw * 150f;
}
除了核心物理,还有很多细节决定了模拟器的可用性:
// --- 怠速保护逻辑 ---
if (throttleBase < 0.05f)
{
m1 = m2 = m3 = m4 = 0f;
pitchPID.Reset();
rollPID.Reset();
yawPID.Reset();
}
// --- PID 限幅保护 ---
float pOut = Mathf.Clamp(pitchCorrection * correctionScale, -maxPIDAuthority, maxPIDAuthority);
float rOut = Mathf.Clamp(rollCorrection * correctionScale, -maxPIDAuthority, maxPIDAuthority);
float yOut = Mathf.Clamp(yawCorrection * correctionScale, -maxPIDAuthority, maxPIDAuthority);
代码结构比较简单,一共就 4 个脚本实现了整个无人机模拟,以下是流程图:
| 架构分层 | 核心脚本 | 角色定位 | 主要功能与逻辑 | 数据流向 / 物理作用 |
|---|---|---|---|---|
| 输入层 | DroneInput.cs | 信号预处理 | 1. 读取硬件:接收物理手柄输入。 |
0 ~ 1
• 俯仰/横滚/偏航:-1 ~ 1 |
| 控制层 | FlightController.cs | 大脑 (Brain) | 1. 模式处理:根据 Angle/Acro 模式将输入转为目标角速度。PID.cs 计算误差。PID.cs | 纯数学计算 | 1. 误差计算:对比目标值与当前值。MotorEngine.cs | 四肢 (Limbs) | 1. 惯性模拟:通过低通滤波模拟电机响应延迟。通过以上步骤,从一个简单的刚体运动,进化到了一个具备空气动力学特性的飞行模拟器。目前的物理手感已经能传达出穿越机的'惯性'和'风阻'。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online