第 8 章 机器人平台设计
学习到当前阶段大家对ROS已经有一定的认知了,但是之前的内容更偏理论,尤其是介绍完第6章仿真与第7章导航之后,想必相当一部分同学有些疑惑:
实体机器人与仿真实现有什么区别?
ROS系统如何控制机器人底盘运动,并计算里程计数据呢?
实际的传感器如雷达、摄像头等应该怎么使用呢?
...
机器人系统是一套机电一体化的设备,机器人设计也是高度集成的系统性实现,为了给大家解答上述疑惑,方便机器人硬件的快速上手,本章去繁就简旨在从0到1的设计一款入门级、低成本、简单但又具备一定扩展性的两轮差速机器人,学习完本章内容之后,你甚至可以构建属于自己的机器人平台。
本章主要介绍内容如下:
-
机器人的组成部分;
-
Arduino 基本使用;
-
Arduino 与电机驱动;
-
底盘控制实现;
-
基于树莓派的ROS环境搭建;
-
激光雷达与相机的基本使用与集成。
本章学习目标如下:
- 能够独立搭建机器人平台。
注意:
-
该章内容会使用到ROS的分布式框架,树莓派端作为主机,PC端作为从机;
-
PC端使用的ROS版本为noetic,树莓派端使用的版本为melodic,因为树莓派需要与底盘交互,而相关功能包还未更新。
案例演示:
1.机器人底盘实现
底盘正面

Arduino 与 电机驱动板

底盘背面

2.机器人控制系统以及传感器实现

3.机器人集成效果

8.1 概述
立足角度不同,对机器人组成的认识也会有明显差异,从控制的角度来看,机器人系统可以分为四部分:
传感系统、控制系统、驱动系统、执行机构。
1.传感系统
它由内部传感器模块和外部传感器模块组成,获取内部和外部环境中有用的信息,相当于人体的感官与神经,内部传感系统包括电机的编码器、陀螺仪等,可以通过自身信号反馈检测位姿状态;外部传感系统包括摄像头、红外、声纳等,用于感知外部环境。
2.控制系统
控制系统的任务是根据机器人的作业指令以及从传感器反馈回来的信号,输出控制命令信号,类似于人的大脑。
控制系统需要基于处理器实现,在处理器之上,控制系统需要完成算法处理、关节控制、人机交互等复杂功能。
3.驱动系统
驱动系统主要负责驱动执行机构,将控制系统下达的命令转换成执行机构所需要的信号,相当于人的小脑与神经。
采用的动力源不同,驱动系统的传动方式也不同。驱动系统的传动方式主要有四种:液压式、气压式、电气式和机械式。电力驱动是目前使用最多的一种驱动方式,其特点是电源取用方便,响应快,驱动力大,信号检测、传递、处理方便,并可以采用多种灵活的控制方式,驱动电机一般采用步进电机或伺服电机。
4.执行机构
执行机构是机器人组成中的机械部分,类似于人的手与脚,比如:机器人的行走部分与机械臂。
在当前机器人系统中,各组成部分对应硬件清单如下:
执行机构: 主体使用亚克力板拼装,由两个直流电机带动主动轮以及保持平衡的两个万向轮实现机器人行走,由于执行机构比较简单,不再做单独介绍。
**驱动系统:**电池、arduino 以及电机驱动模块;
控制系统: 树莓派;
传感系统: 编码器、单线激光雷达、相机;
其中,执行机构与驱动系统构成了机器人底盘。
8.2 机器人平台设计之arduino基础
在构建差分轮式机器人平台时,驱动系统的常用实现有 STM32 或 Arduino,在此,我们选用后者,因为 Arduino 相较而言更简单、易于上手。本节将介绍如下内容:
- arduino 简介;
- arduino 开发环境搭建;
- arduino 基本语法。
概念
Arduino是一款便捷灵活、方便上手的开源电子原型平台。在它上面可以进行简单的电路控制设计,Arduino能够通过各种各样的传感器来感知环境,通过控制灯光、马达和其他的装置来反馈、影响环境。
作用
或多或少你可能听说过"集成电路"(又称"微电路"、"微芯片"或"芯片")这种概念,集成电路(integrated circuit)是一种微型电子器件或部件,通过集成电路再结合一些外围的电子电子元器件、传感器等,可以感知环境(温度、湿度、声音),也可以影响环境(控制灯的开关、调节电机转速)。但是传统的集成电路应用比较繁琐,一般需要具有一定电子知识基础,并懂得如何进行相关的程序设计的工程师才能熟练使用,而Arduino的出现才使得以往高度专业的集成电路变得平易近人,Arduino主要优点如下:
- **简单:**在硬件方面,Arduino本身是一款非常容易使用的印刷电路板。电路板上装有专用集成电路,并将集成电路的功能引脚引出方便我们外接使用。同时,电路板还设计有USB接口方便与电脑连接;
- **易学:**只需要掌握 C/C++ 基本语法即可;
- **易用:**Arduino提供了专门的程序开发环境Arduino IDE,可以提高程序实现效率。
当前,Arduino已经成为全世界电子爱好者电子制作过程中的重要选项之一。
组成
Arduino 体系主要包含硬件和软件两大部分。硬件部分是可以用来做电路连接的各种型号的Arduino电路板(下图为本章内容使用的 arduino mega 2560);软件部分则是Arduino IDE。你只要在IDE中编写程序代码,将程序上传到Arduino电路板后,程序便会告诉Arduino电路板要做些什么了。

8.2.1 arduino 开发环境搭建
基于Arduino的开发实现,毋庸置疑的必须先要准备Arduino电路板(建议型号:Arduino Mega 2560),除了硬件之外,还需要准备软件环境,安装 Arduino IDE,在 Ubuntu 下,Arduino 开发环境的搭建步骤如下:
- 硬件准备: Arduino电路板连接 ubuntu
- 软件准备: 安装 Arduino IDE
- 编写 Arduino 程序并上传至 Arduino电路板。
1.Arduino 连接 Ubuntu

你需要确保你对这个接口有访问的权限。假设你的Arduino连接的是/dev/ttyACM0,那么就运行下面这个命令:
$ ls -l /dev/ttyACM0
然后你就可以看到类似于下面的输出结果:
crw-rw---- 1 root dialout 166, 0 2013-02-24 08:31 /dev/ttyACM0
我们注意到在上面的结果中,只有root和"dialout"组才有读写权限。因此,你需要成为dialout组的一个成员。
命令如下:
$ sudo usermod -a -G dialout your_user_name
在这个命令中your_user_name就是你在Linux下登录的用户名。然后需要重启使之生效。执行完上面的操作之后,你可以运行下面的命令查看一下:
$ groups
然后如果你可以在列出的组中找到dialout,这就说明你已经加入到dialout中了。
串口排查命令详解
1.
ls -l /dev/ttyACM0"去门牌号找房间"
/dev/是 Linux 里一个特殊的文件夹,里面存放的不是普通文件,而是硬件设备的"门牌号"。就像你去找某栋楼的 302 室,结果物业说"没有这个房间"------这个命令就是去查这个设备存不存在,结果发现根本没有
ttyACM0这扇门。
2.
lsusb"查看所有插在 USB 口上的设备"
就像保安在门口登记所有来访者,这个命令会列出所有连接到电脑 USB 接口的设备,包括鼠标、键盘、U盘、摄像头等等。
你的结果里出现了:
ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter说明你的 CH340 串口转换器已经被电脑认出来了,设备是好的。
3.
dmesg | tail -20"查看电脑最近的操作日志"
dmesg是内核日志,记录了电脑底层发生的所有事情(就像黑匣子)。tail -20是只看最后 20 条。合在一起就是:"给我看最近发生了什么事"。
你的日志里最关键的一句:
ch341-uart converter now attached to ttyUSB0翻译成人话就是:"CH340 芯片已经安装好了,它的门牌号是 ttyUSB0"。
4.
groups"我有哪些权限?"
Linux 用"用户组"来管理权限,就像公司里不同部门的员工能进不同的门禁区域。
dialout组 = 有权限访问串口设备的"通行证"。你的结果里有
dialout,说明你已经有串口访问权限,不需要管理员帮你开门。
总结一句话
命令 干了什么 ls -l /dev/ttyACM0找设备门牌号,没找到 lsusb确认 USB 设备插上了且被识别 `dmesg tail -20` groups确认你有权限进这扇门
2.安装 Arduino IDE
1.下载arduino ide安装包
官方下载链接:https://www.arduino.cc/en/Main/Software

下载老版本1.8.19
新版本有些问题,不建议安装新版本,老版本就够用

方法一:通过终端运行(推荐)
1. 给文件添加可执行权限
chmod +x arduino-ide_2.3.7_Linux_64bit.AppImage
2. 运行程序
./arduino-ide_2.3.7_Linux_64bit.AppImage
如果文件在当前目录,使用上面的命令。如果文件在其他目录,需要先进入该目录:
cd /path/to/folder
./arduino-ide_2.3.7_Linux_64bit.AppImage
如何判断自己是什么版本
使用
uname命令(最常用)
uname -m
如果显示
x86_64或amd64→ 64位系统如果显示
i686、i386或x86→ 32位系统
如何选择压缩包下载
下载好之后,右击提取到页面内解压,或者下面命令行解压
2.使用tar命令对压缩包解压
tar -xvf arduino-1.x.y-linux64.tar.xz
3.将解压后的文件移动到/opt下
sudo mv arduino-1.x.y /opt
4.进入安装目录,对install.sh添加可执行权限,并执行安装
cd /opt/arduino-1.x.y
sudo chmod +x install.sh
sudo ./install.sh
5.启动并配置 Arduino IDE

在命令行直接输入:arduino,或者点击左下的显示应用程序搜索 arduino IDE。启动如下:

Arduino IDE 配置如下:

3.Hello World实现
Arduino IDE 中已经内置了一些相关案例,在此,我们通过一个经典的控制 LED 等闪烁案例来演示 Arduino 的使用流程:
1.案例调用

2.编译及上传
先点击左上的编译按钮,可用于语法检测,编译无异常,再点击右侧的上传按钮,上传至 Arduino 电路板

3.运行结果
电路板上的 LED 灯闪烁
4.代码解释
// 初始化函数
void setup() {
//将LED灯引脚(引脚值为13,被封装为了LED_BUTLIN)设置为输出模式
pinMode(LED_BUILTIN, OUTPUT);
}
// 循环执行函数
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // 打开LED灯
delay(1000); // 休眠1000毫秒
digitalWrite(LED_BUILTIN, LOW); // 关闭LED灯
delay(1000); // 休眠1000毫秒
}
setup 与 loop 函数是固定格式。
这是一段非常经典的Arduino入门程序,通常被称为 "Blink"(闪烁) 。它的功能非常简单:让Arduino电路板上自带的LED小灯(通常与数字引脚13相连)以1秒亮、1秒灭的节奏持续闪烁。
我们来对它进行逐行、详细的拆解,即使你没有任何编程基础,也能完全看懂。
🧠 核心概念:Arduino程序的结构
每个Arduino程序(在Arduino中称为
Sketch)都必须包含以下两个核心函数,它们就像剧本的两个固定章节:
void setup():"准备"阶段 。这个函数里的代码只会在板子上电或复位后,运行一次。它的任务是进行初始设置,比如告诉Arduino哪些引脚是输入,哪些是输出,或者启动串口通信等。
void loop():"循环表演"阶段 。在setup()执行完毕后,loop()函数里的代码会一遍又一遍、永不停止地重复执行。你的主要任务逻辑(比如读取传感器、控制灯光)就写在这里。理解了这两个"舞台",我们就可以分析具体的"台词"(代码)了。
小结 :
setup()阶段只做了一件事------告诉Arduino,我们准备用第13号引脚来"控制"一个东西。第二部分:循环表演阶段 (
loop)这部分代码会无限循环执行,顺序是:亮 -> 等1秒 -> 灭 -> 等1秒 -> 亮 -> ... 如此往复。
第1、2行:点亮LED并保持1秒
cpp
digitalWrite(LED_BUILTIN, HIGH); // 给LED引脚输出高电平(相当于打开开关) delay(1000); // 程序暂停(延时)1000毫秒(即1秒)
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite:另一个内置的命令 ,作用是给一个已经设置为OUTPUT模式的引脚,写入一个数字信号电平。
HIGH:这个参数代表 "高电平" 。对于Arduino Uno,通常是5V电压 。你可以把它想象成打开一个电路的开关 。当引脚13输出HIGH(5V)时,连接到它的LED就会获得电压,从而被点亮。
delay(1000);
delay:延时(等待)命令 。它会让Arduino的处理器暂停在这里,什么都不做,直到指定的时间过去。
1000:参数,单位是毫秒 。1000毫秒 = 1秒。所以这行代码的效果是:让LED保持亮着的状态,持续整整1秒钟。如果没有这行,灯会亮灭得极快,人眼无法察觉。第3、4行:熄灭LED并保持1秒
cpp
digitalWrite(LED_BUILTIN, LOW); // 给LED引脚输出低电平(相当于关闭开关) delay(1000); // 再次暂停1秒
digitalWrite(LED_BUILTIN, LOW);
LOW:这个参数代表 "低电平" ,通常是0V电压 。相当于关闭电路的开关 。当引脚13输出LOW(0V)时,LED两端没有电压差,从而熄灭。
delay(1000);
- 再次延时1秒。效果是让LED保持熄灭的状态,持续整整1秒钟。
loop()函数结束 ,Arduino会自动从头开始再次执行loop()函数,于是LED又被点亮,进入下一个循环。
📝 核心要点总结(给初学者的"口诀")
代码行 口诀 作用 pinMode(x, OUTPUT);"设x为输出" 声明要使用哪个引脚来控制外部设备。 digitalWrite(x, HIGH);"给x高电平" / "打开x" 让指定引脚输出电压,通常用于开启设备。 digitalWrite(x, LOW);"给x低电平" / "关闭x" 让指定引脚停止输出电压,通常用于关闭设备。 delay(时间);"等待xx毫秒" 让程序暂停一段时间,以便观察效果或进行定时。 🚀 如何实践与修改
现在你已经完全理解了这段代码,可以尝试在Arduino IDE里做以下实验来加深理解:
改变闪烁速度 :把两处
delay(1000)中的1000改成500(半秒)或2000(两秒),然后重新上传到板子,观察LED闪烁节奏的变化。创建不同的模式 :试着让灯亮
100毫秒,灭900毫秒,看看会有什么效果?这就像一个快速闪一下的"心跳"信号。这段"Blink"程序是硬件编程世界的 "Hello, World!" 。恭喜你迈出了第一步!当你成功看到LED按照你的指令闪烁时,你就完成了对物理世界的一次控制。
8.2.2 arduino 基本语法概述
Arduino 的语言系统在设计时参考了C、C++、Java,是一种综合性的简洁语言,语法更类似于C++,但是不支持C++的异常处理,没有STL库,你可以把它当作是精简后的C++。
Arduino 基本语法中,注释、宏定义、库文件包含、变量、函数、流程控制、类、继承、多态..... 都与 C++ 高度类似,在此不再赘述,着重要介绍的是,Arduino中的一些API实现。
1.程序结构
一个 Arduino 程序分为两大部分: setup() 与 loop() 函数。
-
void setup(): 在这个函数里初始化Arduino的程序,使主循环程序在开始之前设置好相关参数,初始化变量、设置针脚的输出\输入类型、设置波特率...。该函数只会在上电或重启时执行一次。
-
void loop(): 这是Arduino的主函数。这套程序会一直重复执行,直到电源被断开。
2.常量
在 Arduino 中封装了一些常用常量,比如:
-
HIGH | LOW(引脚电压定义)
-
INPUT|OUTPUT(数字引脚(Digital pins)定义)
-
true | false(逻辑层定义)

3.通信_Serial
Serial用于Arduino控制板和一台计算机或其他设备之间的通信。您可以使用Arduino IDE内置的串口监视器与Arduino板通信。点击工具栏上的串口监视器按钮,调用begin()函数(选择相同的波特率)。
-
Serial.begin() 初始化串口波特率
**描述:**将串行数据传输速率设置为位/秒(波特)。与计算机进行通信时,可以使用这些波特率:300,1200,2400,4800,9600,14400,19200,28800,38400,57600或115200。当然,您也可以指定其他波特率 - 例如,引脚0和1和一个元件进行通信,它需要一个特定的波特率。
**语法:**Serial.begin(speed)
**参数:**speed: 位/秒 (波特) - long
**返回:**无
-
Serial.print() 从串口打印输出数据
需求:
以人们可读的ASCII文本形式打印数据到串口输出。此命令可以采取多种形式。每个数字的打印输出使用的是ASCII字符。浮点型同样打印输出的是ASCII字符,保留到小数点后两位。Bytes型则打印输出单个字符。字符和字符串原样打印输出。Serial.print()打印输出数据不换行,Serial.println()打印输出数据自动换行处理。
**语法:**Serial.print(val)
参数
val:打印输出的值 - 任何数据类型
**返回:**字节 print()将返回写入的字节数,但是否使用(或读出)这个数字是可设定的
-
**Serial.println()**打印输出数据自动换行处理。参考 Serial.print();
-
Serial.available()
**描述:**获取从串口读取有效的字节数(字符)。这是已经传输到,并存储在串行接收缓冲区(能够存储64个字节)的数据。 available()继承了 Stream类。
**语法:**Serial.available()
**参数:**无
**返回:**可读取的字节数
-
Serial.read()
**描述:**读取传入的串口的数据。read() 继承自 Stream 类。
**语法:**serial.read()
**参数:**无
**返回:**传入的串口数据的第一个字节(或-1,如果没有可用的数据)
4.函数_数字IO
-
pinMode()
**描述:**将指定的引脚配置成输出或输入。
**语法:**pinMode(pin, mode)
参数
pin:要设置模式的引脚
mode:INPUT或OUTPUT
**返回:**无
-
digitalWrite()
**描述:**给一个数字引脚写入HIGH或者LOW。
**语法:**digitalWrite(pin, value)
参数
pin: 引脚编号(如1,5,10,A0,A3)
value: HIGH or LOW
**返回:**无
-
digitalRead()
**描述:**读取指定引脚的值,HIGH或LOW。
**语法:**digitalRead(PIN)
参数
pin:你想读取的引脚号(int)
**返回:**HIGH 或 LOW
**注意:**如果引脚悬空,digitalRead()会返回HIGH或LOW(随机变化)
5.函数_模拟IO
-
analogWrite() PWM
**描述:**从一个引脚输出模拟值(PWM)。可用于让LED以不同的亮度点亮或驱动电机以不同的速度旋转。analogWrite()输出结束后,该引脚将产生一个稳定的特殊占空比方波,直到下次调用analogWrite()(或在同一引脚调用digitalRead()或digitalWrite())。PWM信号的频率大约是490赫兹。
在大多数arduino板(ATmega168或ATmega328),只有引脚3,5,6,9,10和11可以实现该功能。在aduino Mega上,引脚2到13可以实现该功能。老的Arduino板(ATmega8)的只有引脚9、10、11可以使用analogWrite()。在使用analogWrite()前,你不需要调用pinMode()来设置引脚为输出引脚。
**语法:**analogWrite(pin,value)
参数
pin:用于输入数值的引脚。
value:占空比:0(完全关闭)到255(完全打开)之间。
**返回:**无
6.函数_时间
-
delay()
**描述:**使程序暂定设定的时间(单位毫秒)。(一秒等于1000毫秒)
**语法:**delay(ms)
参数
ms:暂停的毫秒数(unsigned long)
**返回:**无
-
millis()
**描述:**返回Arduino开发板从运行当前程序开始的毫秒数。这个数字将在约50天后溢出(归零)。
**参数:**无
**返回:**返回从运行当前程序开始的毫秒数(无符号长整数)。
7.函数_中断
-
attachInterrupt()
**描述:**当发生外部中断时,调用一个指定函数。当中断发生时,该函数会取代正在执行的程序。大多数的Arduino板有两个外部中断:0(数字引脚2)和1(数字引脚3)。
arduino Mege还有其它有四个外部中断:数字2(引脚21),3(引脚20),4(引脚19),5(引脚18)。
**语法:**attachInterrupt(interrupt, function, mode)
interrupt:中断引脚数
function:中断发生时调用的函数,此函数必须不带参数和不返回任何值。该函数有时被称为中断服务程序。
mode:定义何时发生中断以下四个contstants预定有效值:
-
LOW 当引脚为低电平时,触发中断
-
CHANGE 当引脚电平发生改变时,触发中断
-
RISING 当引脚由低电平变为高电平时,触发中断
-
FALLING 当引脚由高电平变为低电平时,触发中断.
**返回:**无
**注意事项:**当中断函数发生时,delay()和millis()的数值将不会继续变化。当中断发生时,串口收到的数据可能会丢失。你应该声明一个变量来在未发生中断时储存变量。
-
-
noInterrupts()(禁止中断)
**描述:**禁止中断(重新使能中断interrupts())。中断允许在后台运行一些重要任务,默认使能中断。禁止中断时部分函数会无法工作,通信中接收到的信息也可能会丢失。
中断会稍影响计时代码,在某些特定的代码中也会失效。
**参数:**无
**返回:**无
-
interrupts()(中断)
**描述:**重新启用中断(使用noInterrupts()命令后将被禁用)。中断允许一些重要任务在后台运行,默认状态是启用的。禁用中断后一些函数可能无法工作,并传入信息可能会被忽略。中断会稍微打乱代码的时间,但是在关键部分可以禁用中断。
**参数:**无
**返回:**无
Arduino 的API还有很多,但是受于篇幅限制,当前只是简单介绍了和本教程相关的一些API实现。
8.2.3 arduino 基本语法演示
通信操作
1.通信实现01
需求: 通过串口,由 arduino 向计算机发送数据
实现:
cpp
/*
* 需求:通过串口,由 arduino 向计算机发送数据
* 实现:
* 1.setup中设置波特率
* 2.setup 或 loop 中使用 Serial.print 或 Serial.println() 发送数据
*
*
*
*/
void setup() {
Serial.begin(57600);
Serial.println("setup");
}
void loop() {
delay(3000);
Serial.print("loop");
Serial.print(" ");
Serial.println("hello");
}
2.通信实现02
需求: 通过串口,由计算机向Arduino发送数据
实现:
cpp
/*
* 需求:通过串口,由计算机向 arduino 发送数据
* 实现:
* 1.setup中设置波特率
* 2.loop 中接收发送的数据,并打印
*
*
*
*/
char num;
void setup() {
Serial.begin(57600);
}
void loop() {
if(Serial.available() > 0){
num = Serial.read();
Serial.print("I accept:");
Serial.println(num);
}
}

8.2.4 arduino 基本语法演示02
**1.**数字IO操作
需求:控制LED灯开关,在一个循环周期内前两秒使LED灯处于点亮状态,后两秒关闭LED灯
实现:
cpp
/*
* 控制LED灯开关,在一个循环周期内前两秒使LED灯处于点亮状态,后两秒关闭LED灯
* 1.setup 中设置引脚为输出模式
* 2.loop 中向引脚输出高电压,休眠 2000 毫秒后,再输出低电压,再休眠 2000 毫秒
*
*/
int led = 13;
void setup() {
Serial.begin(57600);
pinMode(led,OUTPUT);
}
void loop() {
digitalWrite(led,HIGH);//输出高电压
delay(2000);
digitalWrite(led,LOW);//输出低电压
delay(2000);
}
2.模拟IO操作
需求:控制LED灯亮度
原理:在1中LED灯只有关闭或开启两种状态,是无法控制 LED 灯亮度,如果要实现此功能,那么需要借助于 PWM(Pulse width modulation 脉冲宽度调制)技术,通过设置占空比为LED间歇性供电,PWM 的取值范围 [0,255]。
实现:
cpp
/*
* 需求:控制LED灯亮度
* 实现:
* 1.setup 中设置 led 灯的引脚为输出模式
* 2.设置不同的 PWM 并输出
*
*/
int led = 13;
int l1 = 255;
int l2 = 50;
int l3 = 0;
void setup() {
pinMode(led,OUTPUT);
}
void loop() {
analogWrite(led,l1);
delay(2000);
analogWrite(led,l2);
delay(2000);
analogWrite(led,l3);
delay(2000);
}
运行结果:在一个周期内LED灯亮度递减直至熄灭
8.2.5 arduino 基本语法演示03
需求:调用 millis() 函数获取程序当前已经执行的时间,调用delay()函数实现休眠
实现:
cpp
/*
* 需求:调用 millis() 函数获取程序当前已经执行的时间,调用delay()函数实现休眠
*
* 1.setup 中设置波特率
* 2.loop 中使用delay休眠,使用millis获取程序执行时间并输出
*
*/
unsigned long past_time;
void setup() {
Serial.begin(57600);
}
void loop() {
delay(2000);//休眠 2 秒
past_time = millis();
Serial.println(past_time);
}
通过串口监视器查看输出结果。
