news 2026/7/28 9:35:56

基于Arduino与MPU6050的普通直流电机PID平衡控制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Arduino与MPU6050的普通直流电机PID平衡控制实战

1. 从“能转”到“转得稳”:PID控制普通电机的核心挑战

玩无人机或者机器人DIY的朋友,肯定都经历过这个阶段:用Arduino或者STM32,配合一个L298N或者TB6612这样的电机驱动模块,写几行代码让电机转起来,这太简单了。PWM给个固定占空比,电机就呼呼地转。但很快你就会发现,事情没那么简单。当你试图让四轴飞行器平稳悬停,或者让循迹小车沿着直线匀速前进时,问题就来了——电机转速会随着电池电压下降而变慢,负载变化也会让它忽快忽慢,更别提两个电机之间微小的性能差异了。这时候,一个简单的“开环”控制就显得力不从心,我们需要引入“闭环”控制,而PID,就是那个让你从“玩家”迈向“工程师”的第一道,也是最经典的一道坎。

我这次的项目,核心就是抛开那些自带编码器、可以精确反馈转速的“高级”电机(比如MG310霍尔编码电机),而是针对最普通、最廉价的直流有刷电机,实现一个稳定、可靠的转速控制。这类电机只有一个电源线和一根地线,除了通电和断电,它本身不会告诉你“我现在转得多快”。这就像蒙着眼睛开车,你不知道车速,只能凭感觉踩油门。PID控制要的就是这个“感觉”的量化与自动化。我的目标是构建一个完整的系统:用Arduino作为大脑,MPU6050(虽然它主要用来测姿态,但在这个阶段我们先用它来“感知”变化,后面会解释)或类似的传感器作为“眼睛”,去观测由电机转速变化引起的系统状态改变(比如小车的倾角变化),然后通过PID算法计算出新的PWM值,动态调整电机出力,让系统保持稳定。

这听起来像是用牛刀杀鸡?恰恰相反。从普通电机开始理解PID,你能更深刻地抓住反馈控制的本质——如何根据“误差”来决策。等你真正搞懂了,再去玩带编码器的FOC(磁场定向控制)或者更高级的串级PID,就会有一种豁然开朗的感觉。网上很多教程一上来就是位置式PID公式、增量式PID公式,对着编码器读数一顿计算,但很少告诉你,当你的被控对象连一个直接的“速度反馈”都没有时,PID该如何落地。这就是我这次想分享的“辛”路历程:如何为“哑巴”电机装上“智能”控制器。

2. 系统架构与核心部件选型:为什么是它们?

在动手写代码之前,合理的硬件选型和系统架构设计能避免后期无数的麻烦。我的核心思路是:用一个可测量的、受电机转速直接影响的状态量,来间接反映并控制转速。对于四轴无人机,这个状态量就是飞行姿态;对于平衡小车,就是车身的倾角。这里我以更常见、更直观的“平衡小车”或“单轴自平衡装置”作为应用场景来展开,因为它的物理模型更清晰,更容易理解PID的调节作用。

2.1 控制核心:Arduino Uno的得与失

我选择了经典的Arduino Uno R3。原因很简单:生态丰富、资料海量、引脚够用。对于PID算法初探和普通直流电机控制,它的16MHz主频和2KB SRAM完全足够。网上几乎所有的MPU6050库、PID库例程都是基于Arduino平台的,调试起来非常方便。

注意:如果你计划未来扩展功能,比如同时控制多个电机、运行更复杂的滤波算法(互补滤波、卡尔曼滤波),或者使用无线通信,那么可以考虑性能更强的Arduino Mega 2560或者ESP32。ESP32自带Wi-Fi和蓝牙,双核处理器,在需要上位机调试(比如使用VOFA+)时会更流畅。但初期,Uno能让你更专注于算法本身,避免陷入复杂的环境配置问题(比如Arduino IDE下载ESP32支持包时令人头疼的网络问题)。

2.2 感知单元:MPU6050的姿态数据与“间接反馈”

这是关键的一环。MPU6050是一个集成了三轴陀螺仪和三轴加速度计的IMU(惯性测量单元)。我们用它来测量小车的倾角(俯仰角Pitch或横滚角Roll)。

那么,电机的转速和车身的倾角有什么关系?想象一下用手推一个倒立的扫帚。扫帚快要向前倒(倾角变大)时,你需要让车轮向前加速转动,以带动底座向前移动,“追上”倒下的趋势;反之亦然。在这里,电机的转速(具体表现为车轮的线速度)是控制量,车身的倾角是被控量。我们的目标是让倾角稳定在0度(直立状态)。PID控制器根据倾角误差(当前角度与0度的差值)来计算需要给电机多大的PWM值(相当于多大的推力)。

所以,MPU6050在这里的作用,就是提供那个至关重要的“过程变量”(Process Variable, PV)——实时倾角。电机转速本身没有被直接测量,但它通过动力学关系直接影响倾角,从而被间接控制。这就是一个典型的“闭环负反馈”系统。

关于MPU6050的零点漂移:这是必须处理的问题。陀螺仪测量角速度,通过对时间积分得到角度,但存在累积误差(漂移);加速度计测量重力分量,可以解算瞬时角度,但动态情况下受运动加速度干扰很大。因此,单一数据源都不靠谱。通常需要采用互补滤波或卡尔曼滤波进行数据融合,得到一个相对稳定、准确的倾角估计。在Arduino上,互补滤波因其计算量小、效果不错而被广泛使用。许多现成的MPU6050库(如MPU6050_tocknI2Cdevlib)都内置了滤波和姿态解算功能,可以直接输出欧拉角,这大大降低了入门门槛。

2.3 执行机构:普通直流电机与驱动模块

我用的就是最常见的N20微型减速电机或TT马达,价格低廉,没有编码器。驱动模块选用的是TB6612FNG,它比古老的L298N效率更高、发热更小,支持双路电机控制,且逻辑电压与Arduino的5V兼容。

接线要点

  • 电机电源(VM):接一个独立的7-12V电源(如18650电池两节串联)。切勿从Arduino的5V引脚取电给电机,电机启动瞬间的大电流会拉低整个系统电压,导致单片机复位。
  • 逻辑电源(VCC):接Arduino的5V,为TB6612的内部逻辑电路供电。
  • 控制引脚:STBY( standby)接高电平使能;AIN1/AIN2(或BIN1/BIN2)控制电机转向;PWMA/PWMB接Arduino的PWM引脚(如5, 6, 9, 10号引脚)。
  • PWM频率:Arduino默认的PWM频率约490Hz。对于普通直流电机,这个频率足够。如果你发现电机在低速时振动或噪音大,可以尝试通过修改定时器寄存器来提高PWM频率(例如到1kHz以上),这属于进阶优化。

2.4 算法核心:PID控制器库的选择与理解

自己从头写一个PID算法函数并不难,但使用一个经过验证的库可以节省大量调试底层逻辑的时间,让我们更专注于调参和应用逻辑。Arduino社区最著名的PID库是Brett BeauregardPID_v1库。你可以通过Arduino IDE的库管理器直接搜索安装。

这个库将PID控制器抽象成一个对象,你只需要关心几个核心参数:

  • 输入(Input):就是我们的过程变量(PV),即从MPU6050滤波后得到的倾角。
  • 设定点(Setpoint):我们希望倾角保持的值,对于自平衡就是0度。
  • 输出(Output):计算出的控制量,也就是要送给电机PWM引脚的值(比如0-255之间)。
  • Kp, Ki, Kd:比例、积分、微分系数。

库会自动帮你完成PID运算(位置式),并处理积分限幅、输出限幅等细节。它的基本工作模式是:你在loop()函数中频繁地调用myPID.Compute()方法,该方法会根据最新的输入和设定点,计算并更新输出值。然后你把这个输出值映射到PWM范围,写入电机驱动引脚。

3. 软件实现:从数据采集到闭环控制

有了硬件基础,我们来看软件流程。整个程序的结构应该是清晰的数据流:读取传感器 -> 滤波处理得到角度 -> PID计算 -> 输出PWM。

3.1 MPU6050数据读取与倾角解算

首先,你需要初始化MPU6050并设置合适的参数(量程、滤波器带宽)。使用现成库的示例代码如下:

#include <Wire.h> #include <MPU6050_tockn.h> // 以这个库为例 MPU6050 mpu6050(Wire); void setup() { Serial.begin(115200); Wire.begin(); mpu6050.begin(); mpu6050.calcGyroOffsets(true); // 上电后自动计算陀螺仪零偏,保持小车静止 } void loop() { mpu6050.update(); // 更新传感器数据并进行内部滤波、解算 float angleX = mpu6050.getAngleX(); // 获取绕X轴的旋转角度(俯仰角) // ... 后续使用angleX }

getAngleX()返回的值通常就是经过库内部互补滤波后的倾角。这个角度就是PID控制器的“输入”。

3.2 PID控制器初始化与配置

接下来,初始化PID控制器对象。

#include <PID_v1.h> // 定义PID变量 double Setpoint = 0; // 设定点:我们希望角度为0度(直立) double Input; // 输入:从MPU6050读取的角度 double Output; // 输出:计算出的PWM值 double Kp=10, Ki=1, Kd=0.1; // PID参数,这是一个非常初级的猜测值,必须调整 // 创建PID对象 // 参数:输入指针,输出指针,设定点,Kp, Ki, Kd, 控制方向(DIRECT为正作用) PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT); void setup() { // ... 其他初始化 myPID.SetMode(AUTOMATIC); // 设置为自动模式(开始计算) myPID.SetOutputLimits(-255, 255); // 输出限幅。我们打算用正负值控制电机正反转 myPID.SetSampleTime(10); // 设置计算周期为10毫秒 }

这里有几个关键点:

  1. 控制方向(DIRECT/REVERSE):这决定了误差增大时,输出是增大还是减小。对于自平衡系统,如果小车向前倾(角度为正),我们需要让电机向前转(输出为正)来纠正。你需要根据你的电机接线和角度正负定义来测试。如果发现系统一上电就剧烈振荡并飞出去,很可能是方向反了,改成REVERSE试试。
  2. 输出限幅:根据你的PWM分辨率(8位为0-255)和电机驱动逻辑来设置。我设置了-255到255,计划用正负号控制方向,绝对值控制速度。
  3. 采样时间(SetSampleTime):这个极其重要!它指定了PID计算的频率。并不是loop()循环越快,PID算得越快越好。PID算法中的积分项和微分项都对时间敏感。采样时间必须稳定。设置为10ms意味着每秒计算100次,这对于平衡控制来说是一个合理的起点。设置后,myPID.Compute()方法会内部判断是否到达计算时间,避免过度计算。

3.3 主循环中的闭环控制逻辑

loop()函数中,我们将所有环节串联起来。

void loop() { // 1. 读取并更新角度 mpu6050.update(); Input = mpu6050.getAngleX(); // 将角度值赋给PID输入 // 2. 执行PID计算 // Compute()方法会根据SetSampleTime决定是否真的进行计算 if (myPID.Compute()) { // 只有当Compute()返回true(即到达计算周期)时,Output才被更新 // 3. 将PID输出映射为电机控制信号 int pwmValue = (int)Output; // Output是double,转为int int dir = 1; if (pwmValue < 0) { dir = -1; pwmValue = -pwmValue; // 取绝对值 } pwmValue = constrain(pwmValue, 0, 255); // 确保在0-255范围内 // 4. 根据方向和速度控制电机 // 假设使用TB6612,控制电机A,AIN1=高,AIN2=低为正转 if (dir > 0) { digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW); } else { digitalWrite(AIN1, LOW); digitalWrite(AIN2, HIGH); } analogWrite(PWMA, pwmValue); // 输出PWM } // 可以添加一些调试信息输出到串口,但注意不要太频繁,以免影响时序 static unsigned long lastPrint = 0; if (millis() - lastPrint > 100) { // 每100ms打印一次 Serial.print("Angle: "); Serial.print(Input); Serial.print(" | Output: "); Serial.println(Output); lastPrint = millis(); } }

这个逻辑流程就是整个控制系统的核心。它稳定地以10ms为周期,读取当前姿态,通过PID算法计算出为恢复平衡所需的“推力”(PWM值),并立即执行。

4. PID参数整定:从“蹦迪”到“站如松”的修炼

参数整定是PID应用的灵魂,也是最让人“头秃”的部分。网上流传的“口诀”(先P后I最后D)有一定道理,但面对一个具体的物理系统(你的小车),你需要理解每个参数在系统中的实际表现。

初始参数安全警告:在开始调参前,务必做好安全措施!用绳子吊住你的小车,或者放在一个它能来回摆动但不会跑出去的狭小空间里。因为错误的参数,尤其是过大的Kp,会导致电机全力加速,让小车像火箭一样冲出去或疯狂抖动,可能损坏硬件或造成危险。

我的调参过程,是一个典型的从振荡到稳定的过程:

第一步:确定控制方向(DIRECT/REVERSE)先将Kp设为一个很小的正值(如2),KiKd设为0。用手轻轻推一下小车,观察它的反应。正确的反应是:小车朝倾倒的方向加速,试图“追”上倒下的趋势。比如向前推它(让它前倾),它应该向前转动车轮。如果它反向转动,那就把PID对象初始化时的DIRECT改为REVERSE

第二步:比例系数Kp——系统的“刚度”Kp决定了系统对误差的反应强度。增大Kp,小车回正的速度更快、力度更大。

  • 现象Kp太小,小车反应迟钝,轻轻一推就慢慢倒下,无力回天。Kp增大,小车开始有能力抵抗倾斜,但会围绕平衡点来回摆动(振荡)。Kp过大,摆动会加剧,变成高频剧烈抖动,甚至失控。
  • 调法:从小逐渐增大Kp,直到小车被推开后能快速回弹,并在平衡点附近出现持续、稳定的低频摆动(比如周期1-2秒)。此时,Kp基本合适。记录下这个临界值Kp0

第三步:微分系数Kd——系统的“阻尼”Kd能预测误差的变化趋势,并施加一个抑制变化的力。它的作用是抑制振荡,让摆动尽快停下来。

  • 现象:在只有Kp的系统中,小车像一根有弹性的杆,来回晃。加入Kd后,相当于给这个杆子泡在粘稠的油里,晃动会受到阻力。
  • 调法:保持KpKp0,将Kd从0开始慢慢增加。你会观察到小车的摆动幅度迅速减小,最终能够稳稳地停在平衡点。注意Kd对噪声非常敏感。MPU6050的角度数据即使经过滤波,仍有高频噪声。过大的Kd会放大这些噪声,导致电机高频颤动(听起来是滋滋声)。因此,Kd不是越大越好,调到刚好能抑制主要振荡即可。有时需要对角度数据进行额外的低通滤波来平滑信号,供微分项使用。

第四步:积分系数Ki——消除“静差”静差是指系统稳定后,实际值(角度)与设定点(0度)之间存在的微小恒定偏差。对于自平衡系统,由于机械不对称、电机差异等原因,可能需要一个持续的微小力矩才能维持绝对直立,这个力矩就由积分项提供。

  • 现象:只有PD时,小车可能稳定在一个非常接近0度但又不是0度的位置(比如0.5度)。或者,当你用手施加一个持续的小力(模拟持续的风力),小车会倾斜一个角度来抵消这个力,而无法回到0度。
  • 调法:将Ki设为非常小的值(比如0.01或更小)。观察小车是否能更精确地回到0度,或者抵抗恒定干扰的能力是否增强。警告Ki过大非常危险!积分项会累积历史误差,如果小车因为一个干扰持续偏向一边,积分项会不断增大输出,可能导致输出饱和(达到限幅值),然后当误差反向时,积分项需要很长时间“退饱和”,这会引起巨大的超调和振荡,甚至直接失控。务必小心谨慎,从小值开始。

一个实用的调参流程是:Kp->Kd-> 微调KpKd-> 最后尝试引入极小的Ki。很多时候,一个纯PD控制器对于平衡小车来说已经足够优秀了。

实操心得:调参时,串口绘图仪(Serial Plotter)是你的最佳伙伴。将角度(Input)和输出(Output)同时打印出来,可以直观地看到系统的响应曲线。理想的曲线应该是:受到扰动后,角度快速、平滑地回归零点,超调量小,无稳态振荡。通过曲线来理解每个参数的作用,比盲目尝试有效十倍。

5. 进阶问题与实战避坑指南

当你调出一个能勉强站住的小车后,接下来会遇到一系列更精细的问题。这些问题不解决,你的系统永远只是个“实验室玩具”。

5.1 电机死区与非线性补偿

普通直流电机有一个“死区电压”,即PWM值低于某个阈值时,电机根本不会转动。例如,PWM值在0-50之间时,电机不转;50-255之间,转速才大致线性增加。这个死区会严重破坏PID控制。

  • 问题:当PID计算出一个很小的输出值(比如10)时,电机实际不动作,没有控制力。而当误差累积到PID输出超过50时,电机突然启动,给予一个过大的力,导致系统抖动或过冲。
  • 解决方案:在将PID输出(Output)发送给analogWrite()之前,进行非线性映射。
int pwmMapped; if (abs(Output) < deadzoneThreshold) { pwmMapped = 0; } else if (Output > 0) { pwmMapped = map(Output, deadzoneThreshold, 255, minEffectivePWM, 255); } else { pwmMapped = -map(-Output, deadzoneThreshold, 255, minEffectivePWM, 255); } pwmMapped = constrain(pwmMapped, -255, 255);

你需要通过实验测出电机的死区阈值(deadzoneThreshold,对应PID输出值)和有效最小PWM(minEffectivePWM)。这能极大提升低速控制性能。

5.2 采样时间与系统响应速度的权衡

前面提到设置了SetSampleTime(10)。这个值如何确定?

  • 太慢(如50ms):系统响应迟钝,无法及时纠正快速变化的角度,容易失控。
  • 太快(如2ms):首先,MPU6050的数据更新率可能跟不上(通常最高1kHz)。其次,过快的计算会导致微分项对噪声极度敏感(因为微分是差值除以时间,时间极短时,微小的噪声会被放大成巨大的波动)。同时,Arduino的loop()循环中可能还有其他任务(如串口打印),无法保证每次都准时在2ms内完成一次循环,导致实际采样时间不稳定,破坏PID计算的时序基础。
  • 经验值:对于基于MPU6050的平衡系统,10-20ms是一个很好的起点。你可以通过测量loop()一次循环的实际时间(用micros()函数)来验证。确保在最坏情况下,循环时间也小于你设定的采样时间。

5.3 传感器安装与机械对称性

PID不是万能的。如果硬件本身有严重问题,软件再怎么调也无力回天。

  • MPU6050安装:必须牢固地安装在小车的主结构上,并且其坐标系必须与车的运动轴对齐。如果传感器是歪的,那么它测得的“0度”就不是机械上的真实平衡点。你可以让小车在你认为绝对水平的平面上静止,读取传感器输出的角度,将这个值作为“零偏”在软件中减去。
  • 重心位置:小车的重心高度至关重要。重心越低,越稳定(就像不倒翁)。重心越高,惯性越大,控制难度呈指数级上升。尽量将电池等重物放在底盘下方。
  • 电机与车轮一致性:两个电机的性能、减速比、车轮直径如果有差异,会导致小车在平衡时产生旋转(偏航)或缓慢漂移。除了尽量选购配对电机外,可以在软件中为两个电机设置微调系数。例如,发现小车总是向左转,可以给右侧电机的PWM输出乘以一个略大于1的系数(如1.05)进行补偿。

5.4 电源噪声与系统复位

电机是巨大的噪声源。在电机启停瞬间,会产生强烈的电流尖峰和电压跌落,可能通过电源线干扰单片机,导致MPU6050数据出错甚至Arduino程序跑飞、复位。

  • 解决方案
    1. 电源隔离:电机驱动电源和单片机逻辑电源务必分开(使用两节电池,或者一个电池组配合两个稳压模块)。这是铁律。
    2. 滤波电容:在电机的电源引脚两端,就近并联一个大的电解电容(如100-470uF)和一个小的陶瓷电容(0.1uF),用于吸收低频和高频噪声。
    3. 软件看门狗:启用Arduino的内部看门狗(#include <avr/wdt.h>),在loop()中定期喂狗。如果程序因干扰跑飞,看门狗会自动复位系统,比完全死机要好。

6. 从平衡到运动:设定点的动态控制

当小车能稳稳立住之后,你就可以尝试更高级的控制:让它前进、后退。这涉及到PID设定点(Setpoint)的动态管理。

平衡的设定点是0度。如果我们想让小车向前匀速运动,该怎么做?错误的想法是直接给电机一个固定的PWM。正确的方法是:改变姿态的设定点

例如,想让小车以较慢速度匀速前进,可以将姿态设定点从0度改为一个微小的负角度(比如-2度)。PID控制器会发现当前角度(0度)大于设定点(-2度),误差为正,于是它会产生一个向后的控制力(让车轮向后转)。但由于小车是平衡的,车轮向后转的效应会使得车身整体有向前倾的趋势(就像人走路时身体前倾),为了维持这个“前倾”的设定点,PID会持续输出控制量,小车就会持续向前移动。

通过一个外部输入(比如遥控器摇杆),动态地、平滑地改变Setpoint变量,你就可以实现小车的加速、减速和转向。转向则可以通过给左右轮设定不同的Setpoint或对输出乘以一个转向系数来实现。

这实际上已经触摸到了“串级PID”的概念。外环是速度环(控制设定点),内环是姿态环(我们刚才调好的PID)。不过,对于入门项目,先用一个PID环实现平衡和简单的运动控制,已经是一个巨大的成功了。

回顾这条“辛”路,从让电机转起来,到让它智能地维持一个动态平衡,其核心在于理解了反馈的威力。PID算法本身并不复杂,但将其成功应用到一个物理系统上,需要你综合考虑传感器特性、执行器非线性、机械结构、时序控制等一系列工程问题。每一次调试,每一次观察系统的响应,都是对“控制”二字更深的理解。当你看到那个最初东倒西歪的小家伙,终于在你调出的参数下稳稳立住,甚至能听从指令缓缓移动时,那种成就感,远非点亮一个LED所能比拟。这,就是嵌入式控制的魅力所在。

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

MATLAB实现水平集进化(LSE)图像分割技术详解

1. 水平集进化&#xff08;LSE&#xff09;在轮廓提取中的应用价值水平集进化&#xff08;Level Set Evolution&#xff0c;LSE&#xff09;作为图像分割领域的经典算法&#xff0c;在医学影像、工业检测等场景中展现出独特优势。不同于传统边缘检测算子&#xff08;如Canny、S…

作者头像 李华
网站建设 2026/7/28 9:35:25

《黄帝内经》028章丨导引疏滞 循序培元

摘要&#xff1a;本文基于《黄帝内经》经络理论&#xff0c;系统梳理了人体气机运行的三层分区&#xff08;上焦、中焦、下焦&#xff09;与易淤滞的八虚、四大气街关键点位&#xff0c;揭示了气血通路因弯折、紧缩而闭塞&#xff0c;导致正邪相合、虚实夹杂的病理机制。文章提…

作者头像 李华
网站建设 2026/7/28 9:34:07

Unity游戏翻译神器:XUnity.AutoTranslator完全使用指南

Unity游戏翻译神器&#xff1a;XUnity.AutoTranslator完全使用指南 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 还在为外语游戏中的对话和菜单而烦恼吗&#xff1f;XUnity.AutoTranslator是一款强大的…

作者头像 李华
网站建设 2026/7/28 9:33:56

MIT App Inventor 2023决赛项目解析:零代码开发趋势与实战指南

1. 项目概述&#xff1a;一场属于所有人的创新马拉松如果你对移动应用开发感兴趣&#xff0c;但又觉得学习Java或Swift门槛太高&#xff0c;那么MIT App Inventor这个名字你一定不陌生。它就像一个图形化的编程积木&#xff0c;让任何有想法的人&#xff0c;哪怕没有任何代码基…

作者头像 李华
网站建设 2026/7/28 9:33:31

提示工程架构师:AI交互设计的核心方法论与实践

1. 提示工程架构师的角色定位 提示工程架构师是AI时代新兴的技术岗位&#xff0c;主要负责设计和优化与大型语言模型交互的提示流程。这个角色需要同时具备技术架构思维和语言设计能力&#xff0c;就像传统系统架构师需要理解硬件和软件的协同一样&#xff0c;提示工程架构师必…

作者头像 李华