超声波传感器模块

文章目录
- 1.`HC-SR04`介绍
- 2.`HC-SR04`原理介绍
- 2.1原理概述
- 3.2原理详解
- 4驱动代码编写
- 4.1写前思考
- 4.2硬件连线
- 5.总结
- hcsr04.h
- hcsr04.c
1.HC-SR04介绍
超声波传感器有很多种类的型号:HC-SR04、UC-025、UC-026、UC-015、US-100等等,但是他们都大同小异。他们的主要区别是工作参数有点不一样,像是工作的电压或者温度,探测距离或精度有点差别。引脚是一样的,都是4个引脚(us-100多了一个GND引脚),引脚的工作和作用也是一样的。
其中我们最常用的为

接线如下:
HC-SR04 | STM32 | 备注 |
|---|---|---|
VCC | 3.3V/5V | 外接直流电源 |
Trig | 任意一个GPIO口 | 输入端 |
| ECHO | 任意一个GPIO口 | 输出端 |
GND | GND | 接地 |
2.HC-SR04原理介绍
2.1原理概述
超声波测距的工作原理其实很简单,传感器发送超声波,超声波碰到障碍物反弹回来,被传感器接收到。芯片算出发送和接收的时间间隔,再利用公式:s=vxt,看下面示意图,所以实际距离=测量距离/2= 速度 x时间/2。
顺便一提,超声波在空气中的传播速度大概是 343m/,传播速度受到环境条件的影响,如温度、湿度和气压等
超声波模块有两个超声波探头,一个是发送端,负责发送超声波,一个是接受端,负责接收超声波。
3.2原理详解
接下来我们用时序图的方式来介绍超声波发送和接收的过程以及如何计算距离的。
- 正常测距时的时序:
- 单片机会给超声波模块发送大于
10us的高电平的触发信号; - 超声波模块接收到触发信号后
Trig端发送8个40KHz的超声波脉冲。 - Echo端由低电平转为高电平,并同时开始发送超声波。
- 超声波模块接收到返回信号后,
Echo端由高电平转为低电平。 - Echo的高电平宽度即为超声波发出的时间。
4驱动代码编写
明白了超声波测距的原理,我们知道了超声波测距的重点是测量超声波在空气中的时间。接下来我们来写超声波传感器的驱动代码。
4.1写前思考
我们计算差超声波往返所需时间,然后乘于超声波的速度,计算出距离,所以我们需要一个类似于秒表的东西,来测我们的时间。
所以我们可以先用定时器来做一个以微妙为单位的计时。为了方便使用,我们再封装若干个使用函数以便于我们使用这个定时器。
程序如下:
// TIM2 初始化句柄
TIM_HandleTypeDef tim2_handle;/*** @brief TIM2 定时器初始化函数* 设置定时器基本参数,并调用 HAL 库进行初始化*/
void tim2_init(void)
{// 指定定时器实例为 TIM2(即使用 TIM2 作为定时器)tim2_handle.Instance = TIM2;// 设置分频器:72-1 = 71// 如果主频为 72MHz,那么定时器时钟频率为 72MHz / 72 = 1MHz// 即定时器每计数一次所需时间为 1 微秒tim2_handle.Init.Prescaler = 72 - 1;// 设置自动重装载值(ARR):65536-1 = 65535// 当定时器计数到 65535 后溢出,重新从 0 开始// 若计数频率为 1MHz,则溢出周期为 65536 微秒(即 65.536 毫秒)tim2_handle.Init.Period = 65536 - 1;// 向上计数模式(从0计数到ARR)tim2_handle.Init.CounterMode = TIM_COUNTERMODE_UP;// 关闭自动重装载寄存器的预装载功能tim2_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;// 调用 HAL 库函数初始化定时器 TIM2HAL_TIM_Base_Init(&tim2_handle);
}/*** @brief TIM2 的 MSP(MCU Support Package)初始化函数* 一般用于配置定时器的时钟及中断* @param htim TIM句柄指针*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{// 判断当前初始化的是不是 TIM2if(htim->Instance == TIM2){// 使能 TIM2 定时器时钟__HAL_RCC_TIM2_CLK_ENABLE();}
}/*** @brief 启动 TIM2 定时器*/
void tim2_start(void)
{// 启动基础定时器,不带中断HAL_TIM_Base_Start(&tim2_handle);
}/*** @brief 停止 TIM2 定时器*/
void tim2_stop(void)
{// 停止基础定时器HAL_TIM_Base_Stop(&tim2_handle);
}/*** @brief 获取当前 TIM2 定时器计数器的值* @return 当前计数器的值(0~65535)*/
uint16_t tim2_get_cnt(void)
{// 使用 HAL 宏获取定时器当前计数器值return __HAL_TIM_GetCounter(&tim2_handle);
}/*** @brief 设置 TIM2 定时器计数器的值* @param val 要设置的计数器值*/
void tim2_set_cnt(uint16_t val)
{// 使用 HAL 宏设置定时器的当前计数器值__HAL_TIM_SetCounter(&tim2_handle, val);
}
4.2硬件连线
| HC-SR04 | C8T6 |
|---|---|
| VCC | VCC |
| GND | GND |
| Trig | PB6 |
| echo | PB7 |
因为我们涉及到对个别引脚的使用,所以我们需要对引脚进行一些简单的配置(为了方便对引脚进行修改,我们对引脚进行了一些简单的宏定义):
hcsrc04.h:
#define TRIG_PORT GPIOB
#define TRIG_PIN GPIO_PIN_6
#define TRIG_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define TRIG_HIGH() HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_SET)
#define TRIG_LOW() HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_RESET)#define ECHO_PORT GPIOB
#define ECHO_PIN GPIO_PIN_7
#define ECHO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define ECHO_STATUS() HAL_GPIO_ReadPin(ECHO_PORT,ECHO_PIN)
hcsrc04.c
/*** @brief 初始化 HC-SR04 超声波模块所使用的 GPIO 引脚* Trig 为输出模式,用于发出超声波脉冲* Echo 为输入模式,用于接收返回的超声波信号*/
void hcsr04_gpio_init(void)
{// 定义 GPIO 初始化结构体,用于配置引脚属性GPIO_InitTypeDef gpio_initstruct;// 使能 Trig 引脚所在 GPIO 端口的时钟TRIG_GPIO_CLK_ENABLE();// 使能 Echo 引脚所在 GPIO 端口的时钟ECHO_GPIO_CLK_ENABLE();/*************** 配置 Trig 引脚 ***************/// 设置 Trig 引脚号(例如 GPIO_PIN_1)gpio_initstruct.Pin = TRIG_PIN;// 设置为推挽输出模式(用于产生控制信号)gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;// 上拉电阻(防止悬空引起不稳定)gpio_initstruct.Pull = GPIO_PULLUP;// 设置 IO 速度为中速(可根据需要选择低、中、高)gpio_initstruct.Speed = GPIO_SPEED_MEDIUM;// 初始化 Trig 引脚所在端口HAL_GPIO_Init(TRIG_PORT, &gpio_initstruct);/*************** 配置 Echo 引脚 ***************/// 设置 Echo 引脚号(例如 GPIO_PIN_2)gpio_initstruct.Pin = ECHO_PIN;// 设置为输入模式(用于接收超声波返回信号)gpio_initstruct.Mode = GPIO_MODE_INPUT;// Echo 通常不需要特别配置上拉/下拉,默认即可(如需设置可加上)// gpio_initstruct.Pull = GPIO_NOPULL;// 初始化 Echo 引脚所在端口HAL_GPIO_Init(ECHO_PORT, &gpio_initstruct);
}
为了方便我们使用,我们也需要封装一个函数,这个函数名我们定义为:hcsr04_get_length,相信大家通过这个函数名就可以知道这个函数的作用,在前文中,我们介绍了超声波是如何发出的,四个 引脚的作用有什么不同。简单梳理一下:
- 单片机给
Trig引脚至少10us长的一个高电平。 ECHO引脚从低电平到高电平,表示开始发送波;波发出的那一刻,开启软件定时器(开始计时)。ECHO引脚,由高电平转为低电平,表示波回来了,此时我们停止计时器,计算经过时长。- 获取经过的时间,并根据计算公式算出距离。
基于此,我们写出:
/*** @brief 获取 HC-SR04 超声波测距模块测得的距离(单位:厘米)* @return 距离值(cm)*/
float hcsr04_get_length(void)
{// 用于保存 Echo 信号持续的时间(单位:微秒)uint16_t totol_time = 0;// 用于保存最终计算出的距离值(单位:厘米)float distance = 0;/*************** 第1步:发送Trig脉冲 ***************/// 给 Trig 引脚发送一个 10 微秒以上的高电平脉冲,触发超声波发射TRIG_HIGH(); // 设置 Trig 引脚为高电平delay_us(15); // 延迟至少 10us,这里用 15us 更加保险TRIG_LOW(); // 设置 Trig 引脚为低电平,发送完成/*************** 第2步:等待 Echo 信号拉高(开始接收) ***************/// 当 Echo 引脚由低电平变为高电平时,表示超声波已经发射出去// 此时开始计时while(ECHO_STATUS() == GPIO_PIN_RESET); // 等待 Echo 变为高电平tim2_start(); // 启动定时器tim2_set_cnt(0); // 将定时器计数清零/*************** 第3步:等待 Echo 信号拉低(接收到回波) ***************/// 当 Echo 引脚由高电平变为低电平时,表示回波已经接收完成// 此时停止计时while(ECHO_STATUS() == GPIO_PIN_SET); // 等待 Echo 变为低电平tim2_stop(); // 停止定时器/*************** 第4步:获取 Echo 高电平持续时间 ***************/// 获取定时器计数值(单位是 us,取决于定时器配置)totol_time = tim2_get_cnt();/*************** 第5步:根据声速计算距离 ***************/// 声速约为 34300 cm/s,即 0.0343 cm/us// 因为往返距离,所以除以 2// distance = (totol_time × 0.0343) / 2 ≈ totol_time * 0.01715distance = totol_time * 0.01715f;// 返回测得的距离(单位:cm)return distance;
}
5.总结
本文章中,我们介绍了超声波传感器的原理,并以其中较为经典的 HC-SR04为例,给出了驱动代码。现将完整代码粘贴如下:
hcsr04.h
#ifndef __HCSR04_H__#define __HCSR04_H__#define TRIG_PORT GPIOB
#define TRIG_PIN GPIO_PIN_6
#define TRIG_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define TRIG_HIGH() HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_SET)
#define TRIG_LOW() HAL_GPIO_WritePin(TRIG_PORT,TRIG_PIN,GPIO_PIN_RESET)#define ECHO_PORT GPIOB
#define ECHO_PIN GPIO_PIN_7
#define ECHO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define ECHO_STATUS() HAL_GPIO_ReadPin(ECHO_PORT,ECHO_PIN)
void hcsr04_init(void);
float hcsr04_get_length(void);
#endif
hcsr04.c
#include "hcsr04.h"
#include "sys.h"
#include "delay.h"
TIM_HandleTypeDef tim2_handle = {0};//定时器初始化函数
void tim2_init(void)
{tim2_handle.Instance = TIM2;tim2_handle.Init.Prescaler = 72-1;tim2_handle.Init.Period = 65536-1;tim2_handle.Init.CounterMode = TIM_COUNTERMODE_UP;tim2_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_Base_Init(&tim2_handle);}//msp函数
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){__HAL_RCC_TIM2_CLK_ENABLE();}
}void tim2_start(void)
{HAL_TIM_Base_Start(&tim2_handle);
}
void tim2_stop(void)
{HAL_TIM_Base_Stop(&tim2_handle);
}
uint16_t tim2_get_cnt(void)
{return __HAL_TIM_GetCounter(&tim2_handle);
}
void tim2_set_cnt(uint16_t val)
{__HAL_TIM_SetCounter(&tim2_handle,val);
}/******************************************************** */
/*初始化超声波传感器*/
void hcsr04_gpio_init(void)
{GPIO_InitTypeDef gpio_initstruct;//打开时钟TRIG_GPIO_CLK_ENABLE();ECHO_GPIO_CLK_ENABLE();gpio_initstruct.Pin=TRIG_PIN;gpio_initstruct.Mode=GPIO_MODE_OUTPUT_PP;gpio_initstruct.Pull=GPIO_PULLUP;gpio_initstruct.Speed=GPIO_SPEED_MEDIUM;HAL_GPIO_Init(TRIG_PORT,&gpio_initstruct);gpio_initstruct.Pin=ECHO_PIN;gpio_initstruct.Mode=GPIO_MODE_INPUT;HAL_GPIO_Init(ECHO_PORT,&gpio_initstruct);
}
void hcsr04_init(void)
{tim2_init();hcsr04_gpio_init();}
float hcsr04_get_length(void)
{uint16_t totol_time=0;float distance=0;//1Trig引脚,给Trig引脚至少10us的高电平TRIG_HIGH();delay_us(15);TRIG_LOW();//2ECHO引脚,由低电平跳转到高电平,表示开始发送波,波发出去的那一下,开启定时器while(ECHO_STATUS()==GPIO_PIN_RESET);tim2_start();tim2_set_cnt(0);//3ECHO,由高电平条转低电平,表示波回来了,波回来的那一下,我们开始停止定时器,计算出中间经过//多长时间while(ECHO_STATUS()==GPIO_PIN_SET);tim2_stop();//4.计算出中间经过的时间totol_time=tim2_get_cnt();//5.距离=速度(343m/s)*时间/2;distance=totol_time*0.01715;return distance;}
相关文章:
超声波传感器模块
欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 1.HC-SR04介绍2.HC-SR04原理介绍2.1原理概述3.2原理详解 4驱动代码编写4.1写前思考4.2硬件连线 5.总结hcsr04.hhcsr04.c 1.HC-SR04介绍 超声波传感器有很多种类的型号:HC-SR04、UC-025、…...
LeetCode 513 找树左下角的值 LeetCode 112 路径总和 LeetCode106 从中序与后序遍历序列构造二叉树
LeetCode 513 找树左下角的值 迭代法——层序遍历 思路:对树进行层序遍历操作,层序遍历完后,输出树最后一层的第一个节点。 # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val0, leftNone, r…...
Docker常见问题全攻略:从安装到优化
常见Docker安装问题及解决方案 系统兼容性问题排查安装过程中权限不足的解决方法网络配置问题导致安装失败的修复 系统兼容性问题排查 Docker在安装过程中可能会遇到系统兼容性问题,尤其是在较旧的操作系统或特定硬件架构上。确保操作系统版本符合Docker的最低要…...
『大模型笔记』Langchain作者Harrison Chase专访:环境智能体与全新智能体收件箱
Langchain作者Harrison Chase专访:环境智能体与全新智能体收件箱 文章目录 摘要访谈内容什么环境智能体为什么要探索环境智能体怎么让人类能更方便地和环境智能体互动参考文献摘要 LangChain 的 CEO Harrison Chase 提出了_“环境智能体”(Ambient Agents)的概念,这是一种…...
Kafka集群加入新Broker节点会发生什么
Kafka集群加入新Broker节点会发生什么 当向现有的Kafka集群添加新的Broker节点时,会触发一系列自动和手动的过程。以下是详细的流程和影响: 自动发生的流程 集群发现与注册 新Broker启动时会向ZooKeeper注册自己加入集群的/brokers/ids路径下其他Broke…...
SpringBoot的外部化配置
一、什么是外部化配置 外部化配置是指把应用程序中各种可配置的参数、属性等信息,从代码内部提取出来,放置在外部的配置文件、数据库或配置中心等地方(比如使用.properties、.yml 或.xml 等格式的文件)进行管理。提高应用程序的可…...
3.5 统计初步
本章系统阐述统计推断理论基础,涵盖大数定律、抽样分布、参数估计与假设检验等核心内容。以下从六个核心考点系统梳理知识体系: 考点一:大数定律与中心极限定理 1. 大数定律 切比雪夫不等式: 设随机变量 X X X 的数学期望 E (…...
数字IC后端实现教程 | Early Clock Flow和Useful skew完全不是一个东西
数字后端零基础入门系列 | Innovus零基础LAB学习Day10 Q: Early clock flow和useful skew都是做短某段路径,这两个有什么区别呢,既然这样还用useful skew是不是有点多余了? Useful Skew技术 在不使用useful skew技术,第一级FF到第二级FF的…...
.Net HttpClient 管理客户端(初始化与生命周期管理)
HttpClient 初始化与生命周期管理 HttpClient 旨在实例化一次,并在应用程序的整个生命周期内重复使用。 为实现复用,HttpClient类库默认使用连接池和请求管道,可以手动管理(连接池、配置管道、使用Polly); 结合IoC容器、工厂模式(提供了IHt…...
【Docker】Docker环境下快速部署Ollama与Open-WebUI:详细指南
Docker环境下快速部署Ollama与Open-WebUI:详细指南 在本篇文章中,我们将深入探讨如何在Docker中高效部署 Ollama 和 Open-WebUI,并解决在实际使用中常见的问题,确保你的模型服务稳定高效地运行。 一、Ollama 和 Open-WebUI 快速部…...
MySQL OCP试题解析(3)
试题如图所示: 一、解析 正确选项:D)The backup can be impacted when DDL operations run during the backup(备份期间运行的 DDL 操作可能影响备份) 1. 关键知识点解析: 题目中的命令 mysqlbackup 使用了…...
SpringCloud之Gateway基础认识-服务网关
0、Gateway基本知识 Gateway 是在 Spring 生态系统之上构建的 API 网关服务,基于 Spring ,Spring Boot 和 Project Reactor 等技术。 Gateway 旨在提供一种简单而有效的方式来对 API 进行路由,以及提供一些强大的过滤器功能,例如…...
ubuntu----100,常用命令2
目录 文件与目录管理系统信息与管理用户与权限管理网络配置与管理软件包管理打包与压缩系统服务与任务调度硬件信息查看系统操作高级工具开发相关其他实用命令 在 Ubuntu 系统中,掌握常用命令可以大幅提升操作效率。以下是一些常用的命令,涵盖了文件管理…...
数字ic后端设计从入门到精通4(含fusion compiler, tcl教学)CMOS VLSI Design
Layout Design Rules 一、什么是 Layout Design Rules? 布局设计规则是一套用于指导芯片物理设计的几何约束条件,确保设计可以在特定制造工艺下被正确制造。这些规则通常由代工厂(foundry)提供,规定了最小线宽、间距、…...
win10 局域网内聊天
在 Windows 10 的局域网 中,如果你想实现 多个用户之间的聊天功能,可以选择以下几种方案,取决于你需要的是: • ✅ 命令行纯文字聊天(如 Linux talk) • ✅ 图形界面聊天室 • ✅ 局域网广播消息 • ✅ 多人…...
STM32-DMA数据转运(8)
目录 一、简介 二、存储器映像 三、DMA框图编辑 四、DMA基本结构 五、两个数据转运的实例 一、简介 直接存储器存取简称DMA(Direct Memory Access),它是一个数据转运小助手,主要用来协助CPU,完成数据转运的工作…...
电机控制储备知识学习(一) 电机驱动的本质分析以及与磁相关的使用场景
目录 电机控制储备知识学习(一)一、电机驱动的本质分析以及与磁相关的使用场景1)电机为什么能够旋转2)电磁原理的学习重要性 二、电磁学理论知识1)磁场基础知识2)反电动势的公式推导 附学习参考网址欢迎大家…...
使用 React 实现语音识别并转换功能
在现代 Web 开发中,语音识别技术的应用越来越广泛。它为用户提供了更加便捷、自然的交互方式,例如语音输入、语音指令等。本文将介绍如何使用 React 实现一个简单的语音识别并转换的功能。 功能概述 我们要实现的功能是一个语音识别测试页面࿰…...
[Git]ssh下用Tortoisegit每次提交都要输密码
问题描述 ssh模式下,用小乌龟提交代码,即使在git服务端存储了公钥,仍然要每次输入密码。 原因分析 小乌龟需要额外配置自己的密钥,才能免除每次输密码。 解决方案 1.配置好ssh密钥 具体方法参考我前一篇文章: […...
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
前几天刚聊过 《Google 开始正式强制 Android 适配 16 K Page Size》 之后,被问到最多的问题是「怎么查看项目是否支持 16K Page Size」 ?其实有很多直接的方式,但是最难的是当你的项目有很多依赖时,怎么知道这个「不支持的动态库…...
ESP32C3连接wifi
文章目录 🔧 一、ESP32-C3 连接 Wi-Fi 的基本原理(STA 模式)✅ 二、完整代码 注释讲解(适配 ESP32-C3)📌 三、几个关键点解释🔚 四、小结 🔧 一、ESP32-C3 连接 Wi-Fi 的基本原理&a…...
HTTP方法和状态码(Status Code)
HTTP方法 HTTP方法(也称HTTP动词)主要用于定义对资源的操作类型。根据HTTP/1.1规范(RFC 7231)以及后续扩展,常用的HTTP方法有以下几种: GET:请求获取指定资源的表示形式。POST:向指…...
机器学习中分类模型的常用评价指标
评价指标是针对模型性能优劣的一个定量指标。 一种评价指标只能反映模型一部分性能,如果选择的评价指标不合理,那么可能会得出错误的结论,故而应该针对具体的数据、模型选取不同的的评价指标。 本文将详细介绍机器学习分类任务的常用评价指…...
# YOLOv3:基于 PyTorch 的目标检测模型实现
YOLOv3:基于 PyTorch 的目标检测模型实现 引言 YOLOv3(You Only Look Once)是一种流行的单阶段目标检测算法,它能够直接在输入图像上预测边界框和类别概率。YOLOv3 的优势在于其高效性和准确性,使其在实时目标检测任…...
MySQL的Docker版本,部署在ubantu系统
前言 MySQL的Docker版本,部署在ubantu系统,出现问题: 1.执行一个SQL,只有错误编码,没有错误提示信息,主要影响排查SQL运行问题; 2.这个问题,并不影响实际的MySQL运行,如…...
Mac QT水平布局和垂直布局
首先上代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include <QPushButton> #include<QVBoxLayout>//垂直布局 #include<QHBoxLayout>//水平布局头文件 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), …...
服务器制造业中,L2、L6、L10等表示什么意思
在服务器制造业中,L2、L6、L10等是用于描述服务器生产流程集成度的分级体系,从基础零件到完整机架系统共分为L1-L12共12个等级。不同等级对应不同的生产环节和交付形态,以下是核心级别的具体含义: L2(Level 2…...
回答 | 图形数据库neo4j社区版可以应用小型企业嘛?
刚在知乎上看到了一个提问,挺有意思,于是乎,贴到这里再简聊一二。 转自知乎提问 当然可以,不过成本问题不容小觑。另外还有性能上的考量。 就在最近,米国国家航空航天局——NASA因为人力成本问题,摒弃了使…...
Linux操作系统从入门到实战(二)手把手教你安装VMware17pro与CentOS 9 stream,实现Vim配置,并配置C++环境
Linux操作系统从入门到实战(二)手把手教你安装VMware17pro与CentOS 9.0 stream,实现Vim配置,并编译C文件 前言一、安装VMware17pro二、安装CentOS9.02.1 为什么选择CentOS9,与CentOS7对比2.1 官网下载CentOS9.02.2 国内…...
软考架构师考试-UML图总结
考点 选择题 2-4分 案例分析0~1题和面向对象结合考察,前几年固定一题。近3次考试没有出现。但还是有可能考。 UML图概述 1.用例图:描述系统功能需求和用户(参与者)与系统之间的交互关系,聚焦于“做什么”。 2.类图&…...
