Arduino红外遥控与舵机联动:从激光宠物玩具到模拟温度计
1. 项目概述与核心思路
如果你手头有一块Arduino开发板、一个红外遥控器和一个舵机,你会先拿它们来做什么?几年前我第一次接触这些元件时,想法很简单:做个能遥控开关的灯,或者让一个小风扇转起来。但当我真正开始摆弄,特别是把红外接收头和舵机连在一起后,我发现这个组合的潜力远不止于此。它本质上是一个无线指令接收端加上一个精准的角度执行器,这简直就是为物理交互项目量身定做的黄金搭档。
这次我们要聊的两个项目,正是基于这个核心组合的经典应用。第一个是红外遥控激光宠物玩具,用遥控器控制舵机带动激光笔,让你的猫主子追着光点跑,实现“懒人遛宠”。第二个是模拟指针式温度计,用温度传感器读取环境数据,然后驱动舵机指针在刻度盘上摆动,将看不见的温度变化转化为直观的物理运动。这两个项目看似不同,但内核高度一致:都是通过Arduino解析特定信号(红外编码或模拟电压),再映射为舵机的旋转角度。
为什么选择这个组合?从工程角度看,红外遥控提供了低成本、易实现的无线控制方案,而舵机(伺服电机)则提供了开箱即用的位置闭环控制,你只需要告诉它角度,它就能自己转到那个位置并保持住,省去了自己设计电机驱动和反馈电路的麻烦。对于嵌入式入门和快速原型开发来说,效率和可靠性是关键,这个组合恰好两者兼备。接下来,我会带你从硬件连线、库安装、代码解析到调试排错,完整走一遍这两个项目的实现过程,过程中我会穿插很多只有实际焊过板子、调过程序才能知道的细节和“坑点”。
2. 硬件准备与核心元件解析
工欲善其事,必先利其器。在开始焊接或插线之前,我们得先搞清楚手头这几个核心元件到底是什么,以及为什么要选它们。
2.1 控制核心:Arduino开发板
无论是Adafruit Metro、Arduino Uno还是其他兼容板,它们在这里的角色都是大脑。负责读取红外传感器的信号,处理温度传感器的模拟电压,然后计算出舵机应该转动的角度,并发出相应的控制脉冲。选择标准Arduino板(如Uno)或Adafruit Metro(本质是Arduino的变种)在代码层面几乎没有区别,其核心是ATmega328P这类单片机。需要注意的是,舵机工作电流较大(尤其在堵转时),虽然单个小型舵机可以直接从板载5V引脚取电,但为了系统稳定,我强烈建议后续若有扩展,或使用多个舵机时,为其准备独立的5V电源,避免因电流过大导致开发板复位或损坏。
2.2 无线指令入口:红外接收头与遥控器
红外通信是一种非常普遍的短距离无线技术。我们用的红外接收头(通常是一个三引脚的小黑块,如VS1838B)内部集成了光电二极管、放大器、解调器和滤波器。它只接收特定频率(通常是38kHz)的红外信号,并将其解调为数字电平信号输出给Arduino,能有效滤除环境光干扰。
遥控器则是一个红外发射装置。当你按下按键时,其内部的芯片会生成一套特定的编码脉冲(如NEC、RC5协议),并通过红外LED以38kHz的载波频率发射出去。这套编码就像是每个按键的“身份证”。我们项目中使用的Adafruit Mini Remote默认采用NEC编码协议,这也是很多家电遥控器的标准。理解协议很重要,因为后续代码中的#define定义的各个按键值(如0xfd50af),就是该遥控器在NEC协议下各个按键对应的唯一编码。如果你换用其他遥控器,这些编码值会完全不同,需要重新捕获和定义。
2.3 动作执行器:伺服电机(舵机)
舵机是项目的“手”和“脚”。我们常用的是标准舵机,它内部包含一个小型直流电机、减速齿轮组和一个位置反馈电位器,构成一个闭环控制系统。当你发送一个目标角度信号(通常是一个周期20ms,脉宽在0.5ms到2.5ms之间的PWM波)时,控制电路会比较目标位置与电位器反馈的当前位置,驱动电机正转或反转,直到两者一致。这就是为什么我们调用myServo.write(90),它就能精准地转到90度位置并保持。
这里有个关键参数:角度范围。大部分标准舵机的有效旋转范围是0到180度。试图让它转到这个范围之外(比如write(200)),轻则齿轮打滑发出“吱吱”声,重则烧毁电机内部的驱动电路。因此,在代码中我们必须对目标角度进行限幅,这正是后面代码中min()和max()函数的核心作用。
2.4 感知元件:TMP36温度传感器(用于温度计项目)
对于温度计项目,我们需要一个“感官”。TMP36是一款模拟输出温度传感器。它的输出电压与摄氏温度成线性关系,公式大致为:电压 (V) = 0.01 * 温度 (°C) + 0.5。也就是说,在25°C时,输出电压约为0.75V。Arduino的模拟输入引脚(如A0)可以读取0-5V的电压并将其转换为0-1023的整数值。我们通过analogRead()获取这个数字,再换算回电压值,最后通过上述公式反推出当前温度。它的精度足以满足室内温度监测的需求,而且接口非常简单,只有电源、地和信号三根线。
3. 项目一:红外遥控激光宠物玩具详解
这个项目的趣味性很强,但实现起来需要严谨的步骤。核心逻辑是:红外接收头捕获遥控信号 -> Arduino解码并识别按键 -> 根据按键改变舵机目标角度变量 -> 驱动舵机转动 -> 固定在舵机上的激光笔光点随之移动。
3.1 电路连接与组装要点
首先,根据原理图完成电路连接。红外接收头通常有三个引脚:输出(Out)、电源(Vcc)、地(GND)。输出脚接Arduino的数字引脚2(与代码中IRrecv myReceiver(2)对应),Vcc接5V,GND接地。舵机有三根线:**信号线(通常是黄色或白色)**接数字引脚9,**电源线(红色)**接5V,**地线(棕色或黑色)**接地。
注意:务必确保电源和地线的连接牢固。舵机在启动和堵转时电流可能瞬间超过500mA,接触不良会导致电压跌落,引起Arduino意外复位。如果条件允许,使用面包板电源模块或外部5V电源为舵机单独供电是最稳妥的方案。
组装激光笔部分有两种思路:
- 简单方案:用扎带或强力胶带将一支常亮的激光笔固定在舵机舵盘上。确保固定牢固,避免晃动。激光笔的开关需要事先打开并用胶带固定住。
- 可控方案(高级):拆开一个激光笔模块,将其内部电路引出。将激光二极管的阳极(正极)通过一个限流电阻(如100欧姆)连接到Arduino的某个数字引脚(如代码中的引脚11),阴极接地。这样你就可以通过代码
digitalWrite(laserPin, HIGH/LOW)来控制激光的开关。安全警告:操作时务必断开电池,小心静电,并绝对避免激光直射眼睛,尤其是宠物的眼睛。
3.2 库安装与代码深度解析
项目依赖IRLib2库来处理红外信号。很多新手遇到的第一个“拦路虎”就是编译错误:Fatal error: IRLibAll.h: No such file or directory。这是因为没有正确安装库。
正确的库安装方法:
- 在Arduino IDE中,点击「工具」->「管理库…」。
- 在库管理器中搜索“IRLib2”。注意,是“IRLib2”而不是“IRremote”。要选择由Chris Young维护的版本。
- 点击安装。安装完成后,在「文件」->「示例」中应该能找到
IRLib2的示例文件夹。
如果库管理器中没有,你需要去GitHub下载ZIP包,然后在IDE中通过「项目」->「加载库」->「添加.ZIP库…」来手动安装。确保安装后,IRLib2的文件夹位于你的Arduino库目录下(例如文档/Arduino/libraries/)。
核心代码逻辑拆解:我们结合提供的代码片段,深入理解其工作流程。
#include <IRLibAll.h> #include <Servo.h> // 1. 定义遥控器协议与按键值 #define MY_PROTOCOL NEC #define RIGHT_ARROW 0xfd50af #define LEFT_ARROW 0xfd10ef #define SELECT_BUTTON 0xfd906f // ... 其他按键定义 // 2. 初始化红外接收与解码对象,指定接收引脚为2 IRrecv myReceiver(2); IRdecode myDecoder; uint32_t Previous; // 用于处理NEC协议的长按重复码 // 3. 初始化舵机对象与变量 Servo myServo; int16_t pos = 90; // 当前舵机位置,初始在中间(90度) int16_t Speed = 5; // 每次按键舵机移动的步进角度 void setup() { randomSeed(analogRead(0)); // 初始化随机数种子 myServo.attach(9); // 将舵机连接到引脚9 myServo.write(pos); // 初始化舵机位置 myReceiver.enableIRIn(); // 启动红外接收 } void loop() { if (myReceiver.getResults()) { // 检测是否收到红外信号 myDecoder.decode(); // 解码信号 if(myDecoder.protocolNum == MY_PROTOCOL) { // 判断是否为NEC协议 // 处理重复码:NEC协议下长按会发送0xFFFFFFFF,此时应沿用上一个有效键值 if(myDecoder.value == 0xFFFFFFFF) { myDecoder.value = Previous; } switch(myDecoder.value) { // 根据键值执行不同操作 case LEFT_ARROW: // 关键点:使用min函数限制最大值。pos+Speed不能超过180度。 pos = min(180, pos + Speed); break; case RIGHT_ARROW: // 关键点:使用max函数限制最小值。pos-Speed不能小于0度。 pos = max(0, pos - Speed); break; case BUTTON_0: // 生成一个0到179之间的随机数,让舵机跳转到随机位置 pos = random(0, 180); break; // ... 可以在这里添加更多按键case,例如控制激光开关 } myServo.write(pos); // 将计算好的新位置发送给舵机 Previous = myDecoder.value; // 保存当前键值,用于下次可能出现的重复码 } myReceiver.enableIRIn(); // 重新使能接收器,准备接收下一个信号 } }代码中的精妙之处与避坑指南:
min()和max()函数:这是保护舵机的关键。没有它们,持续按左键可能导致pos值超过180,myServo.write(200)会强制舵机向机械极限转动,极易损坏。这两个函数确保了目标角度始终被约束在0-180的安全范围内。- 重复码处理:
if(myDecoder.value==0xFFFFFFFF){myDecoder.value=Previous;}这行代码是针对NEC协议的优化。在长按时,遥控器为了省电,只会发送一次完整编码,之后发送特殊的重复码。这行代码将重复码替换为上一次的有效按键值,实现了长按连续移动的效果。 - 随机函数
random():random(0, 180)会生成一个0到179的整数。用随机位置来移动激光点,可以模拟更不可预测的“猎物”行为,对宠物来说趣味性大增。 - 激光控制(可选):如果你想用遥控器开关激光,可以定义一个激光控制引脚(如
const int laserPin = 11;)和一个状态变量(如bool laserOn = false;)。在setup()中设置pinMode(laserPin, OUTPUT),然后在switch语句中添加一个case(比如SELECT_BUTTON),在其中写入laserOn = !laserOn; digitalWrite(laserPin, laserOn);,即可实现按键切换激光开关。
3.3 调试与问题排查实录
即使按照步骤操作,也可能会遇到问题。下面是我在实际制作和教学中遇到的一些典型情况及其解决方法。
问题1:上传代码后,舵机毫无反应,或者抽搐一下就不动了。
- 检查电源:这是最常见的原因。确保舵机的红线(5V)和棕线(GND)分别牢固地连接在Arduino的5V和GND引脚上。尝试单独给舵机供电(外部5V电源,共地)。
- 检查信号线:确认舵机的信号线(黄/白)连接到了代码中指定的引脚(示例中是数字引脚9)。
- 检查代码:确认
myServo.attach(9);中的引脚号与实际连接一致。检查pos变量是否被正确初始化(如90)并在loop()中被更新。
问题2:按下遥控器,Arduino板载LED(引脚13)闪烁,但舵机不转。
- 这通常是好迹象!它说明红外信号已经被接收和解码。问题可能出在舵机控制部分。
- 检查
switch-case语句:确保你按下的按键编码与代码中case后的值完全匹配。Adafruit Mini Remote的编码是固定的,但如果你用的是其他遥控器,需要先用IRLib2的示例代码“IRrecvDump”来捕获你遥控器各个按键的真实编码,然后替换掉代码中的#define值。 - 检查限幅逻辑:如果
pos变量因为min/max函数限制已经到达边界(0或180),继续按同方向按键是不会再改变pos的。可以尝试按相反方向的按键,或者加入Serial.print(pos);语句在串口监视器中查看pos的实际变化。
问题3:遥控反应迟钝,或者需要很近才能遥控。
- 检查红外接收头方向:确保接收头的光敏元件正对遥控器方向,中间没有遮挡。
- 检查环境光干扰:强烈的日光灯或太阳光中含有红外线,可能会干扰接收。尝试在光线较暗的环境下测试。
- 检查电池:遥控器的电池可能电量不足,导致发射的红外信号强度弱。
问题4:我想用我自己的遥控器,该怎么办?这是非常实际的需求。步骤如下:
- 打开Arduino IDE,在「文件」->「示例」->「IRLib2」中找到并打开「IRrecvDump」示例。
- 按照示例代码的注释连接好红外接收头(通常也是接引脚2)。
- 上传代码,打开串口监视器(波特率9600)。
- 用你的遥控器对准接收头,按下你想用的按键(比如音量+)。串口监视器会打印出一串信息,其中类似
Decoded NEC: Value:FFA25D (32 bits)的行就是关键。记录下Value:后面的十六进制数(例如FFA25D)。 - 在你的项目代码中,用这个值替换掉原有的按键定义,例如
#define VOLUME_UP 0xFFA25D。然后在switch语句中添加对应的case VOLUME_UP:即可。
4. 项目二:模拟指针式温度计实现
完成宠物玩具后,我们对舵机和Arduino的编程有了基本了解。现在我们来做一个更“实用”的项目:一个实体化的温度计。它的核心思想是将TMP36传感器读到的模拟电压值(代表温度),通过map()函数,线性映射到舵机的0-180度角度上。
4.1 硬件连接与传感器校准
电路连接:
- TMP36传感器:扁平一面朝向自己,从左至右三只脚分别为:Vcc、Vout、GND。Vcc接Arduino的3.3V(注意,不是5V,接5V会损坏传感器),GND接地,Vout接模拟引脚A0。
- 舵机:连接方式与上一个项目完全相同:信号线->D9, 红线->5V, 黑/棕线->GND。
重要提示:为什么TMP36接3.3V?TMP36的输出电压范围是0.1V到2.0V(对应-40°C到+125°C)。如果使用5V供电,其最大输出电压仍约为2.0V,但Arduino的模拟参考电压是5V,这意味着我们只使用了ADC量程(0-1023)的一小部分(约0-409),精度损失严重。使用3.3V供电,传感器的最大输出(2.0V)更接近ADC的满量程(3.3V),能获得更高的测量分辨率。代码中需要根据实际供电电压来调整电压换算公式。
传感器读数验证:在上传完整代码前,我们先写一个简单的测试程序来验证TMP36工作是否正常,并确定我们当地的温度范围,以便后续校准。
void setup() { Serial.begin(9600); } void loop() { int sensorValue = analogRead(A0); // 读取A0引脚的值 float voltage = sensorValue * (3.3 / 1023.0); // 换算为电压值(假设接3.3V) float temperatureC = (voltage - 0.5) * 100; // TMP36公式:温度(°C) = (电压 - 0.5) * 100 float temperatureF = temperatureC * 9.0 / 5.0 + 32; // 转换为华氏度 Serial.print("Voltage: "); Serial.print(voltage); Serial.print(" V, Temp: "); Serial.print(temperatureC); Serial.print(" C, "); Serial.print(temperatureF); Serial.println(" F"); delay(1000); // 每秒读一次 }上传后打开串口监视器,用手捏住传感器(给它升温),观察读数变化。记录下你房间大致的温度范围(例如20°C到30°C)。这个范围将用于后续map()函数的输入区间校准。
4.2 代码整合与映射逻辑
温度计项目的代码是之前所学知识的综合应用。核心在于loop()函数中的数据处理流程。
#include <Servo.h> Servo metroServo; const int temperaturePin = A0; // 温度传感器接在A0 const int servoPin = 9; void setup() { Serial.begin(9600); metroServo.attach(servoPin); } void loop() { // 1. 读取并计算温度 float voltage = getVoltage(temperaturePin); // 自定义函数,获取电压 float temperatureC = convertToC(voltage); // 自定义函数,电压转摄氏度 // float temperatureF = convertToF(voltage); // 或者转华氏度 // 2. 将温度映射到舵机角度(核心步骤) int servoPos; // 假设我们关心的温度范围是10°C到35°C,对应舵机0到180度 // map函数:将temperatureC从[10, 35]线性映射到[0, 180] servoPos = map((int)temperatureC, 10, 35, 0, 180); // 3. 可选:对映射结果进行限幅,防止超出舵机范围 servoPos = constrain(servoPos, 0, 180); // 4. 驱动舵机 metroServo.write(servoPos); // 5. 串口输出调试信息 Serial.print("Voltage: "); Serial.print(voltage); Serial.print(" V, Temp: "); Serial.print(temperatureC); Serial.print(" C, Servo Pos: "); Serial.println(servoPos); delay(500); // 每半秒更新一次 } // 辅助函数:将模拟读数转换为电压值(假设接3.3V) float getVoltage(int pin) { return (analogRead(pin) * 3.3) / 1023.0; } // 辅助函数:将电压值转换为摄氏度 float convertToC(float voltage) { return (voltage - 0.5) * 100.0; } // 辅助函数:将电压值转换为华氏度 float convertToF(float voltage) { return ((voltage - 0.5) * 100.0 * 9.0 / 5.0) + 32.0; }map()函数详解:这是本项目的灵魂。map(value, fromLow, fromHigh, toLow, toHigh)函数的作用是,将value从区间[fromLow, fromHigh]线性映射到区间[toLow, toHigh]。
(int)temperatureC:当前的温度值(整数形式)。10, 35:这是我们预设的温度测量范围下限和上限。你需要根据之前测试的结果调整这两个值。例如,你房间温度在18-28°C之间波动,就应该设为18, 28。这样设计能让温度计指针充分利用整个刻度盘,显示更灵敏。0, 180:舵机角度的下限和上限。- 如果温度是22.5°C(在10和35中间),那么
map计算后的servoPos就是90度(在0和180中间)。
constrain()函数:这是一个安全措施。如果由于传感器误差或计算问题,map后的servoPos超出了0-180的范围(比如-5或185),constrain(servoPos, 0, 180)会强制将其拉回到0或180,保护舵机。
4.3 表盘制作与系统校准
一个漂亮的表盘能让项目成就感倍增。你可以像资料中那样打印一个刻度盘,也可以自己手绘。
制作与校准步骤:
- 确定指针零位和满量程位:上传一个简单的舵机测试程序,分别执行
metroServo.write(0)和metroServo.write(180),观察舵机臂的实际位置。用笔在表盘背景上标记出这两个极限点。 - 绘制刻度:在0位和180位之间等分,并标上你预设的温度范围值。例如,0度对应10°C,180度对应35°C,那么90度就对应22.5°C。
- 物理安装:将舵机固定在表盘背面,让舵机轴穿过表盘中心。将一根轻质的指针(如剪短的牙签、塑料片)固定在舵盘上。
- 软件校准:运行完整的温度计代码。用另一个可靠的温度计(或手机上的天气APP显示室内温度)作为参考。观察你的指针指示是否准确。如果不准,调整代码中
map()函数的fromLow和fromHigh参数。例如,指针指示偏高(实际20°C却指在25°C刻度),说明映射区间下限设低了,可以尝试将10调高到15。
提升精度的小技巧:
- 软件滤波:模拟读数会有微小波动。可以采用“滑动平均滤波”,即连续读取10次温度,然后取平均值作为最终结果,这样指针会更稳定,不会轻微抖动。
const int numReadings = 10; float readings[numReadings]; int readIndex = 0; float total = 0; float average = 0; // 在loop中替换单次读取 total = total - readings[readIndex]; // 减去最旧的读数 readings[readIndex] = convertToC(getVoltage(temperaturePin)); // 读取新值 total = total + readings[readIndex]; // 加上最新的读数 readIndex = (readIndex + 1) % numReadings; // 循环索引 average = total / numReadings; // 计算平均值 // 使用average进行映射 servoPos = map((int)average, 10, 35, 0, 180);
5. 项目优化与扩展思路
完成以上两个基本项目后,你已经掌握了红外控制与舵机联动的核心技能。我们可以在此基础上进行优化和扩展,让项目更实用、更智能。
5.1 激光宠物玩具的优化
- 增加激光开关与模式切换:如前所述,增加一个按键(如
SELECT_BUTTON)来控制激光的开关。你甚至可以增加一个“自动模式”,让舵机在一定角度范围内随机、缓慢地移动,完全解放双手。 - 速度可调:定义两个按键(如
UP_ARROW和DOWN_ARROW)来增加或减少Speed变量的值,这样你就可以控制激光点移动的快慢,适应不同活跃度的宠物。 - 加入声音或灯光反馈:当激光点移动到边界时,让蜂鸣器响一下,或者让一个LED闪烁,增加互动感。这只需要在
case语句中,当pos达到0或180时,添加相应的tone()或digitalWrite()语句即可。
5.2 模拟温度计的扩展
- 双尺度显示:像传统的温度计一样,在表盘上同时绘制摄氏度和华氏度两种刻度。代码中可以同时计算两种温度,但映射关系只需一套(因为指针位置是唯一的)。
- 历史记录与趋势:加入一个OLED屏幕,除了实时温度,还可以显示过去一段时间内的最高/最低温度,甚至用简单的图表显示温度变化趋势。这需要引入
U8g2等显示库,并学习在Arduino上使用数组来存储历史数据。 - 阈值报警:设定一个温度阈值(比如高于28°C为高温)。当温度超过阈值时,让舵机来回快速摆动(像警报器),同时让LED闪烁或蜂鸣器响起。这只需要在
loop()中加入一个if判断语句。 - 改为其他仪表:这个框架是通用的。你可以把TMP36换成光敏电阻,做一个模拟光照强度表;换成土壤湿度传感器,做一个盆栽湿度指示器;甚至连接网络模块获取股市指数,做一个“物理版”股指仪表盘。核心就是将你需要监控的物理量,通过传感器和
map()函数,映射到0-180度的舵机角度上。
5.3 常见问题深度排查
在扩展过程中,你可能会遇到更复杂的问题。这里分享几个深层次的排查经验:
问题:系统运行一段时间后,舵机反应变慢或Arduino无响应。
- 电源不足:这是最可能的原因。舵机,尤其是稍大一点的舵机,在移动时瞬间电流很大。长期从Arduino板载稳压器取电会导致其过热甚至触发保护。终极解决方案是使用独立电源为舵机供电,确保Arduino和舵机的“地”(GND)连接在一起即可。
- 代码阻塞:检查
loop()中是否有长时间的delay()。长的延时会阻塞整个程序,包括红外接收。尽量使用millis()进行非阻塞定时,或者将delay()拆分成多个短的。
问题:红外控制距离变短,或角度控制不精准。
- 供电电压影响:Arduino的5V输出如果因为负载重而下降,会直接影响红外接收头的工作电压和灵敏度。同样,舵机供电不足也会导致扭矩下降,无法精准到位。用万用表测量一下运行时的5V引脚电压是否稳定在5V左右。
- 机械阻力:检查激光笔或指针的安装是否过紧,增加了舵机的负载。确保转动部分顺滑。舵机有“死区”,如果目标位置与当前位置相差角度很小(比如1-2度),它可能不会动作,这是正常现象。
问题:温度读数跳动大,指针抖动厉害。
- 参考电压噪声:Arduino的模拟参考电压可能受到数字电路噪声干扰。可以在代码开头使用
analogReference(EXTERNAL);,并连接一个稳定的外部基准电压源到AREF引脚(但需注意,使用外部基准时,getVoltage函数中的换算系数也要变)。 - 电源噪声:确保传感器供电(3.3V)稳定。可以在TMP36的Vcc和GND之间并联一个0.1uF的陶瓷电容,用于滤波。
- 实施软件滤波:如前所述,采用滑动平均滤波是解决读数跳动的有效且简单的方法。
从一个小小的红外遥控开始,到让舵机精准转动,再到构建出一个实体化的温度显示装置,这个过程充满了嵌入式开发的典型乐趣:将代码逻辑与物理世界连接起来。这两个项目像是一把钥匙,帮你打开了用微控制器感知和控制物理世界的大门。我自己的第一个舵机项目是一个用遥控器控制的摄像头云台,当时为了一点点抖动调试了很久。现在的你,掌握了限幅保护、信号解码、模拟读取和映射变换这些核心概念,完全可以去构思更复杂的互动装置了。记住,所有复杂的项目都是由这些基础模块像搭积木一样组合而成的。下次当你看到某个酷炫的自动化项目时,不妨拆解一下,很可能它的核心就是“接收信号->处理数据->驱动执行器”这个我们刚刚熟练掌握的流程。
