CPP-Summit-2022 学习:嵌入式软件产品化架构
你这套资料本质是在表达一个核心思想:这叫做:移动机器人不是单一电机控制,而是完整控制系统。设:我们从系统工程角度理解。控制对象:控制对象:多了:这里的“十五管、十二管、九管”等指:基本电功率公式:P=U×IP = U \times IP=U×I但电机驱动里要考虑效率 η\etaηPoutput=U×I×ηP_{output} = U \times I \times \etaPoutput=U×I
一、产品化成就头部企业 —— 核心逻辑理解
你这套资料本质是在表达一个核心思想:
通过“分层控制平台 + 多型号覆盖 + 典型场景适配”,形成完整产品矩阵,从而实现规模化与头部地位。
产品分级体系
| 型号 | 定位 | 说明 |
|---|---|---|
| H3U | 高性能型 | 高算力 + 多轴控制 + 复杂机器人 |
| H2U | 通用型 | 主流AGV/AMR控制 |
| H1U | 经济型 | 成本敏感场景 |
| H0U | 简单逻辑型 | 基础逻辑/继电器替代 |
这叫做:
性能分级覆盖市场全价位带
二、移动机器人控制系统 —— 完整控制链条理解
移动机器人不是单一电机控制,而是完整控制系统。
1⃣ 功能维度
(1)运动控制
- 底盘运动学
- 差速控制
- 舵轮控制
- 麦克纳姆轮运动解算
(2)动作控制
- 顶升
- 货叉
- 液压
- 拉伸机构
(3)安全控制
- 安全激光雷达
- 超声波
- 防撞条
- 视觉避障
(4)HMI控制
- 灯光
- 开关
- 车载屏幕
- 指示系统
2⃣ 底盘类型理解
你列的这些其实是运动学模型分类:
| 类型 | 特点 |
|---|---|
| 两驱差速 | 经典差速模型 |
| 四驱差速 | 更大负载 |
| 单舵轮 | 单驱动单转向 |
| 双舵轮 | 双转向驱动 |
| 麦克纳姆轮 | 全向移动 |
| 单差速总成 | 集成驱动模块 |
| 双差速总成 | 重载型 |
差速运动学公式
设:
- 左轮速度 v L v_L vL
- 右轮速度 v R v_R vR
- 轮距 L L L
则:
v = v L + v R 2 v = \frac{v_L + v_R}{2} v=2vL+vR
ω = v R − v L L \omega = \frac{v_R - v_L}{L} ω=LvR−vL
其中: - v v v = 机器人线速度
- ω \omega ω = 角速度
三、典型应用场景控制对象解析
我们从系统工程角度理解。
1⃣ 叉式堆高 / 前移 AGV
控制对象:
- 驱动电机
- 顶升机构
- 安全激光
- BMS
- 灯光
- 操纵杆
本质:
这是一个“运动控制 + 升降控制 + 安全控制”三合一系统
2⃣ 顶升潜入式 AMR
控制对象:
- 顶升电机
- 驱动器
- 安全激光
- BMS
核心特点:
强调自动化搬运 + 地面潜入式结构
3⃣ 料箱机器人
- 拉绳
- 灯光
- 驱动器
- 安全系统
属于轻载物流机器人。
4⃣ 辊筒 AGV
多了:
- 辊筒电机
核心:
运动控制 + 物流输送控制
四、控制器“管数”的真正含义
这里的“十五管、十二管、九管”等指:
MOSFET功率管数量
三相无刷电机驱动基本结构:
每相桥臂需要2个MOS管(上桥+下桥)
三相总共:
3 × 2 = 6 3 \times 2 = 6 3×2=6
但为了增加电流能力,会并联MOS管。
例如:
- 六管 → 每桥臂1颗
- 九管 → 部分桥臂并联
- 十二管 → 每桥臂2颗
- 十五管 → 每桥臂更多并联
五、电压、电流、功率关系理解
基本电功率公式:
P = U × I P = U \times I P=U×I
但电机驱动里要考虑效率 η \eta η
P o u t p u t = U × I × η P_{output} = U \times I \times \eta Poutput=U×I×η
假设:
- 电压 48V
- 电流 35A
- 效率 85%
则:
P = 48 × 35 × 0.85 = 1428 W P = 48 \times 35 \times 0.85 = 1428W P=48×35×0.85=1428W
但标称功率只有 500~600W,为什么?
因为: - 那是持续功率
- 35A 是峰值电流
六、相电流 vs 母线电流
你表里有两种电流:
- 电流最大值(母线电流)
- 相电流最大值
关系近似:
I p h a s e ≈ 3 × I b u s I_{phase} \approx \sqrt{3} \times I_{bus} Iphase≈3×Ibus
比如:
母线 35A
35 × 1.732 ≈ 60 A 35 \times 1.732 \approx 60A 35×1.732≈60A
而表中相电流 120A
说明:
有PWM调制 + 峰值瞬态能力
七、代码片段示例(带详细注释)
下面给你一个三相电机控制逻辑示例(伪代码):
// 电机控制类
class MotorController
{
public:
// 构造函数
MotorController(float voltage, float maxCurrent)
: m_voltage(voltage), m_maxCurrent(maxCurrent)
{
// 初始化PWM模块
initPWM();
}
// 设置目标转速
void setSpeed(float rpm)
{
// 根据转速计算目标电流
float targetCurrent = calculateCurrent(rpm);
// 限流保护
if (targetCurrent > m_maxCurrent)
{
targetCurrent = m_maxCurrent;
}
// 根据电流计算PWM占空比
float duty = targetCurrent / m_maxCurrent;
// 输出PWM
setPWMDuty(duty);
}
private:
float m_voltage; // 母线电压
float m_maxCurrent; // 最大允许电流
void initPWM()
{
// 初始化定时器
// 配置频率
// 使能输出
}
float calculateCurrent(float rpm)
{
// 简化模型:假设电流与转速线性关系
// 实际应使用FOC算法
return rpm * 0.1f;
}
void setPWMDuty(float duty)
{
// 将占空比写入硬件寄存器
}
};
八、不同控制器产品定位理解
我们总结为一个金字塔结构:
H3U 高性能
H2U 通用型
H1U 经济型
H0U 简单逻辑
向上:
- 算力提升
- IO增加
- 通信接口增加
- 控制轴数增加
向下: - 成本下降
- 功能减少
- 面向单一场景
九、产品化成功的本质
真正的“头部企业”不是靠单一爆款,而是:
- 全功率段覆盖
- 全车型覆盖
- 全场景覆盖
- 控制算法平台统一
- 模块化设计
- 规模化生产
十、技术总结
这份资料核心表达的是:
一套从小功率两轮车控制器,到工业AGV控制系统的完整产品体系
技术维度包括:
- 电机驱动技术
- 功率电子设计
- 运动学建模
- 安全控制
- 系统集成
- 平台化架构
杭州海康威视数字技术有限公司 为主线,系统性解读其“做器件 → 做整机 → 做系统 → 做平台 → 做总包”的升级路径。
一个典型的 技术纵向整合 + 平台化 + 生态化 演进模型。
一、总体战略路径理解
企业能力升级路径可以抽象为:
核心算法 → 硬件产品 → 系统解决方案 → 平台架构 → 行业生态 → 城市级底座
本质上是三次“控制权升级”:
- 控制技术
- 控制产品
- 控制数据与系统架构
二、阶段一:技术与器件起步期(2001–2006)
2001年 — 公司成立
定位:视频压缩技术企业
核心能力:嵌入式视频编码算法
2002年 — 发布板卡、DVR
从算法走向硬件产品。
DVR核心是视频压缩与存储,其码率可近似表示为:
R = f × B P P × R e s o l u t i o n R = f \times BPP \times Resolution R=f×BPP×Resolution
其中:
- f f f = 帧率
- B P P BPP BPP = 每像素比特数
- R e s o l u t i o n Resolution Resolution = 分辨率
目标是降低 R R R,提高压缩效率。
2003年 — 引入 H.264
H.264 的核心优势:
- 帧间预测
- 运动补偿
- 变换编码
- 熵编码
压缩效率提升可表示为:
C o m p r e s s i o n R a t i o = O r i g i n a l S i z e E n c o d e d S i z e Compression\ Ratio = \frac{Original\ Size}{Encoded\ Size} Compression Ratio=Encoded SizeOriginal Size
相对上一代编码标准,压缩效率约提升 30%–50%。
战略意义:
控制底层编码算法 = 控制视频数据入口
三、阶段二:整机与规模扩张(2007–2014)
2007年 — 推出摄像机
完成从“后端设备”到“前端采集”的布局。
视频链条变为:
摄像机 → 编码 → 存储 → 显示
掌握完整链路意味着:
可以定义行业标准
2009年 — 成立系统公司
从“卖设备”升级为“卖解决方案”。
行业方案包含:
- 公安
- 交通
- 园区
- 金融
这一步是“产品公司 → 解决方案公司”的关键转折。
2010年 — 深交所上市
股票代码:002415
资本进入后:
R & D I n v e s t m e n t ↑ ⇒ P r o d u c t I t e r a t i o n S p e e d ↑ R\&D\ Investment \uparrow \Rightarrow Product\ Iteration\ Speed \uparrow R&D Investment↑⇒Product Iteration Speed↑
规模效应公式:
U n i t C o s t = F i x e d C o s t Q + V a r i a b l e C o s t Unit\ Cost = \frac{Fixed\ Cost}{Q} + Variable\ Cost Unit Cost=QFixed Cost+Variable Cost
随着产量 Q Q Q 上升,单位成本下降。
2011年 — 全球视频监控市占率第一(IHS)
标志:
在传统安防硬件时代达到全球领先
2013年 — 营收突破百亿
说明:
- 规模经济形成
- 渠道全球化成功
四、阶段三:智能化转型(2015–2016)
2015年 — 深度智能产品首次亮相
推出视频结构化服务器、车辆分析服务器。
核心变化:
从“记录视频”到“理解视频”
神经网络基本形式:
y = f ( W x + b ) y = f(Wx + b) y=f(Wx+b)
多层网络:
O u t p u t = f n ( W n ( . . . f 1 ( W 1 x ) ) ) Output = f_n(W_n(...f_1(W_1x))) Output=fn(Wn(...f1(W1x)))
AI带来的商业价值:
- 车牌识别
- 人脸识别
- 行为分析
2016年 — 全球安防50强第一
智能化转型成功。
推出全系列深度智能产品,说明:
AI能力开始嵌入全产品线
五、阶段四:平台化与云化(2017–2019)
2017年 — AI Cloud 架构
提出三层结构:
边缘节点 → 边缘域 → 云中心
计算分布模型:
T o t a l L a t e n c y = T e d g e + T n e t w o r k + T c l o u d Total\ Latency = T_{edge} + T_{network} + T_{cloud} Total Latency=Tedge+Tnetwork+Tcloud
优化方向:
- 减少 T n e t w o r k T_{network} Tnetwork
- 将推理下沉到边缘
2018年 — AI开放平台
核心:
平台化,让合作伙伴调用AI能力
从封闭系统 → 开放生态。
2019年 — 物信融合数据平台
物信融合 = IoT + 信息系统
数据模型统一示例:
// 统一设备数据结构
struct DeviceData
{
std::string deviceId; // 设备ID
std::string deviceType; // camera / sensor / radar
uint64_t timestamp; // 时间戳(毫秒)
std::vector<float> payload; // 采集数据或特征向量
};
平台逻辑:
// 数据接入服务
class DataIngestionService
{
public:
void receive(const DeviceData& data)
{
store(data); // 存储原始数据
analyze(data); // 调用AI模型分析
}
private:
void store(const DeviceData& data)
{
// 写入数据库
}
void analyze(const DeviceData& data)
{
float score = runModel(data.payload); // AI推理
if(score > 0.8f)
{
triggerAlarm(data.deviceId); // 触发告警
}
}
float runModel(const std::vector<float>& input)
{
// 伪代码:模型推理
return 0.92f;
}
void triggerAlarm(const std::string& id)
{
// 推送告警到管理平台
}
};
六、阶段五:城市级数字底座(2021)
发布智慧城市数智底座。
架构抽象为:
数据接入层
↓
数据中台
↓
AI中台
↓
行业应用层
城市系统规模计算:
若城市有 N N N 个摄像机,单台码率为 R R R:
T o t a l B a n d w i d t h = N × R Total\ Bandwidth = N \times R Total Bandwidth=N×R
若:
- N = 100000 N = 100000 N=100000
- R = 4 M b p s R = 4Mbps R=4Mbps
则:
T o t a l B a n d w i d t h = 400 G b p s Total\ Bandwidth = 400Gbps Total Bandwidth=400Gbps
这已经是“城市级系统工程”。
七、做器件 → 做总包的战略本质
| 阶段 | 控制什么 |
|---|---|
| 器件阶段 | 控制算法 |
| 整机阶段 | 控制产品 |
| 系统阶段 | 控制项目 |
| 平台阶段 | 控制数据 |
| 总包阶段 | 控制资金流 |
最终商业模型:
R e v e n u e = H a r d w a r e + S o f t w a r e + P l a t f o r m + I n t e g r a t i o n Revenue = Hardware + Software + Platform + Integration Revenue=Hardware+Software+Platform+Integration
八、三大历史阶段总结
① 数字化阶段(2001–2010)
目标:视频数字化
② 网络高清阶段(2010–2015)
目标:规模扩张 + IP化
③ 智能化与平台化阶段(2015–至今)
目标:AI + 云 + 城市级底座
九、核心启示
这条路径说明:
- 技术起家是基础
- 产品规模化是跳板
- AI是转折点
- 平台化是长期壁垒
- 城市级底座是终极形态
本质上,这是从“硬件利润”升级为“系统价值”的过程。
安川电机 的事业变迁路径,并用“做器件 → 做整机 → 做系统 → 做总包”的战略模型来理解。
这是一条比海康更“工业底层”的升级路线,本质是:
电机技术 → 控制技术 → 机电一体化 → 工业机器人 → 行业解决方案
一、总体战略主线
可以抽象为:
电动机 → 电机系统 → 控制器 → 伺服系统 → 机电一体化 → 工业机器人 → 行业总包
这是典型的“工业底层技术向上整合”。
二、创立期:电动机时代(1915–1950)
1915年 创立
创始人:安川敬一郎
起点是煤矿用电动机。
当时核心能力:
- 三相感应电机设计
- 重载驱动
- 工业电气化
电机基本原理
电机输出机械功率:
P = T × ω P = T \times \omega P=T×ω
其中:
- T T T = 转矩
- ω \omega ω = 角速度
电机输入电功率:
P = U × I × η P = U \times I \times \eta P=U×I×η
η \eta η 为效率。
早期优势:
做重载、高可靠性的工业电机
三、电机系统化阶段(1950–1980)
1950年:安川第五郎 上任。
开始进入“电机系统”阶段。
1958年 Minasha电机
这是一次技术升级:
- 更高效率
- 更高稳定性
- 更强控制能力
本质:
从卖电机 → 卖电机系统
系统化意味着:
系统价值 > 单一设备价值 系统价值 > 单一设备价值 系统价值>单一设备价值
四、关键转折:机电一体化(1969–1972)
1969年申请“机电一体化(Mechatronics)”商标,1972年注册。
这是全球首次将“机电一体化”作为战略方向明确化。
机电一体化本质公式:
M e c h a t r o n i c s = 机械 + 电子 + 控制 + 软件 Mechatronics = 机械 + 电子 + 控制 + 软件 Mechatronics=机械+电子+控制+软件
这标志:
从机械制造商 → 控制系统公司
五、控制技术深化:变频器与伺服系统
1⃣ 变频器(Inverter)
变频调速原理:
电机同步转速:
n = 60 f p n = \frac{60f}{p} n=p60f
其中:
- f f f = 频率
- p p p = 极对数
通过改变 f f f,即可改变转速。
这一步意味着:
从电机制造 → 控制电机行为
2⃣ 伺服系统(DC → AC)
伺服控制核心是闭环控制。
位置误差:
e ( t ) = r ( t ) − y ( t ) e(t) = r(t) - y(t) e(t)=r(t)−y(t)
PID 控制器:
u ( t ) = K p e ( t ) + K i ∫ e ( t ) d t + K d d e d t u(t) = K_p e(t) + K_i \int e(t) dt + K_d \frac{de}{dt} u(t)=Kpe(t)+Ki∫e(t)dt+Kddtde
伺服系统带来:
- 高精度
- 高响应
- 高稳定性
六、机器人时代(1977起)
1977年 开发 MOTOMAN-L10
代表产品:
MOTOMAN-L10
这是工业机器人历史里程碑。
机器人运动学模型:
T = A 1 A 2 A 3 . . . A n T = A_1 A_2 A_3 ... A_n T=A1A2A3...An
每个 A i A_i Ai 为关节变换矩阵。
正运动学:
X = f ( θ 1 , θ 2 , . . . , θ n ) X = f(\theta_1, \theta_2, ..., \theta_n) X=f(θ1,θ2,...,θn)
逆运动学:
θ = f − 1 ( X ) \theta = f^{-1}(X) θ=f−1(X)
机器人带来的升级:
不只是卖伺服,而是卖“自动化生产单元”
七、产业机器人扩展
后续发展方向:
- FPD(平板显示器)机器人
- 洁净室机器人
- 真空机器人
- 次世代机器人
- 人类协作机器人
这代表:
机器人从工业 → 半导体 → 精密制造 → 人机协作
八、三大事业领域形成(2010年代)
形成三大支柱:
① 环境・能源事业(绿色)
- 水处理
- 节能系统
- 可再生能源驱动
② 机电一体化解决方案(蓝色)
- 伺服系统
- 变频器
- 控制系统
③ 机器人・人类援助(粉色)
- 工业机器人
- 协作机器人
- 医疗辅助
九、从器件到总包的逻辑升级
| 阶段 | 控制什么 |
|---|---|
| 电机阶段 | 控制动力 |
| 控制阶段 | 控制运动 |
| 伺服阶段 | 控制精度 |
| 机器人阶段 | 控制生产 |
| 总包阶段 | 控制整条产线 |
最终模型:
R e v e n u e = 设备 + 控制系统 + 机器人 + 整线集成 + 维护服务 Revenue = 设备 + 控制系统 + 机器人 + 整线集成 + 维护服务 Revenue=设备+控制系统+机器人+整线集成+维护服务
十、机器人控制示例代码(带详细注释)
// 六轴工业机器人控制类
class RobotArm
{
public:
RobotArm()
{
// 初始化6个关节角度为0
for(int i = 0; i < 6; ++i)
{
jointAngle[i] = 0.0;
}
}
// 设置目标位置(简化为笛卡尔坐标)
void moveTo(double x, double y, double z)
{
// 逆运动学求解
inverseKinematics(x, y, z);
// 执行关节运动
executeMotion();
}
private:
double jointAngle[6]; // 六个关节角度
void inverseKinematics(double x, double y, double z)
{
// 伪代码:根据目标坐标计算关节角度
// 实际需用DH参数建模
jointAngle[0] = x * 0.01;
jointAngle[1] = y * 0.01;
jointAngle[2] = z * 0.01;
}
void executeMotion()
{
for(int i = 0; i < 6; ++i)
{
// 每个关节使用伺服控制器闭环控制
servoControl(i, jointAngle[i]);
}
}
void servoControl(int jointId, double targetAngle)
{
// 计算误差
double error = targetAngle - getCurrentAngle(jointId);
// 简化PID控制
double output = 1.2 * error;
// 输出控制信号给伺服驱动器
}
double getCurrentAngle(int jointId)
{
return 0.0; // 假设当前为0
}
};
十一、安川模式的本质
与海康不同:
- 海康:信息系统 → AI → 城市级平台
- 安川:动力系统 → 控制系统 → 自动化生产线
安川是:
工业自动化底层控制权掌握者
它控制的是:
- 电机
- 运动控制
- 工业机器人
- 整条自动化产线
十二、核心战略洞察
1969年的“机电一体化”是关键分水岭。
如果没有那次战略定义,就不会有:
- 伺服系统优势
- MOTOMAN品牌
- 自动化整线能力
这就是:
从卖零件,到卖系统能力
企业规模化与差异化竞争的核心能力模型。
一、什么是产品化架构?
定义:
产品化架构,是为“相同或相近领域内的一组产品”提供统一的结构设计与技术底座。
核心思想:
不是做一个产品
而是做一套可持续演进的产品体系
二、产品化架构要包容的“两种变化”
你图里表达的是一个闭环:
市场变化 → 产品化架构 → 产品实现(功能变化 + 硬件变化)→ 反向影响市场
本质是:
M a r k e t C h a n g e ⇒ A r c h i t e c t u r e E v o l u t i o n Market\ Change \Rightarrow Architecture\ Evolution Market Change⇒Architecture Evolution
同时:
A r c h i t e c t u r e F l e x i b i l i t y ⇒ F a s t e r P r o d u c t R e s p o n s e Architecture\ Flexibility \Rightarrow Faster\ Product\ Response Architecture Flexibility⇒Faster Product Response
三、第一种变化:功能变化(Feature Change)
1⃣ 功能差异化
例如:
- 基础版
- Pro版
- 高性能版
功能集合可以抽象为:
P r o d u c t i = B a s e + F e a t u r e S e t i Product_i = Base + FeatureSet_i Producti=Base+FeatureSeti
其中: - B a s e Base Base = 公共核心能力
- F e a t u r e S e t i FeatureSet_i FeatureSeti = 某一版本特有功能
代码示例:功能可配置设计(策略模式)
// 功能接口定义
class Feature
{
public:
virtual void execute() = 0; // 执行功能
virtual ~Feature() {}
};
// 具体功能A
class BasicFeature : public Feature
{
public:
void execute() override
{
// 基础功能逻辑
}
};
// 高级功能
class AdvancedFeature : public Feature
{
public:
void execute() override
{
// 高级算法逻辑
}
};
// 产品架构
class Product
{
public:
void addFeature(Feature* feature)
{
features.push_back(feature); // 插入可插拔功能
}
void run()
{
for(auto f : features)
{
f->execute(); // 执行所有配置的功能
}
}
private:
std::vector<Feature*> features; // 功能模块列表
};
核心思想:
功能模块化 + 插件化
四、第二种变化:硬件变化(Hardware Variation)
产品系列往往有:
- 不同功率
- 不同CPU
- 不同接口
- 不同传感器数量
抽象为:
H a r d w a r e V a r i a n t = f ( C P U , M e m o r y , I O , P o w e r ) HardwareVariant = f(CPU, Memory, IO, Power) HardwareVariant=f(CPU,Memory,IO,Power)
产品性能近似为:
P e r f o r m a n c e ∝ C P U F r e q u e n c y × C o r e C o u n t Performance \propto CPU_Frequency \times Core_Count Performance∝CPUFrequency×CoreCount
代码示例:硬件抽象层(HAL)
// 硬件抽象接口
class HardwareLayer
{
public:
virtual void init() = 0;
virtual void readSensor() = 0;
virtual void writeMotor(float value) = 0;
virtual ~HardwareLayer() {}
};
// 低端硬件实现
class LowEndHardware : public HardwareLayer
{
public:
void init() override
{
// 初始化低端MCU
}
void readSensor() override
{
// 读取简单传感器
}
void writeMotor(float value) override
{
// 输出PWM控制
}
};
// 高端硬件实现
class HighEndHardware : public HardwareLayer
{
public:
void init() override
{
// 初始化高性能CPU
}
void readSensor() override
{
// 读取多路传感器 + 高精度数据
}
void writeMotor(float value) override
{
// 高级控制算法输出
}
};
架构层只依赖接口,不依赖具体硬件。
五、产品化架构的四大技术特性
1⃣ 可重用
复用率公式:
R e u s e R a t e = R e u s a b l e C o d e T o t a l C o d e Reuse\ Rate = \frac{Reusable\ Code}{Total\ Code} Reuse Rate=Total CodeReusable Code
复用率越高,开发成本越低。
2⃣ 可配置
通过参数控制行为:
B e h a v i o r = f ( C o n f i g u r a t i o n ) Behavior = f(Configuration) Behavior=f(Configuration)
例如:
- 开关功能
- 调整功率等级
- 启用高级算法
3⃣ 可插拔
模块之间低耦合:
C o u p l i n g ↓ ⇒ F l e x i b i l i t y ↑ Coupling \downarrow \Rightarrow Flexibility \uparrow Coupling↓⇒Flexibility↑
4⃣ 可扩展
支持未来增长:
A r c h i t e c t u r e f u t u r e = A r c h i t e c t u r e n o w + E x t e n s i o n Architecture_{future} = Architecture_{now} + Extension Architecturefuture=Architecturenow+Extension
六、战略层面的意义
产品化架构支撑:
长期战略
- 市场分层
- 产品矩阵
- 技术演进
中期规划
- 产品路线图
- 版本规划
可以表示为:
S t r a t e g y ⇒ A r c h i t e c t u r e ⇒ P r o d u c t R o a d m a p Strategy \Rightarrow Architecture \Rightarrow Product\ Roadmap Strategy⇒Architecture⇒Product Roadmap
七、为什么说“包容两种变化”?
市场变化有两种典型来源:
① 市场分层变化
- 低端市场
- 中端市场
- 高端市场
② 客户个性化变化
- 功能定制
- 接口定制
- 协议定制
如果没有产品化架构:
每个新需求 = 重新开发 每个新需求 = 重新开发 每个新需求=重新开发
成本爆炸。
如果有产品化架构:
新产品 = B a s e + C o n f i g + O p t i o n a l M o d u l e s 新产品 = Base + Config + OptionalModules 新产品=Base+Config+OptionalModules
八、系统抽象模型
可以用集合表示:
设:
- H H H = 硬件集合
- F F F = 功能集合
- C C C = 配置集合
则某一产品:
P i = ( H i , F i , C i ) P_i = (H_i, F_i, C_i) Pi=(Hi,Fi,Ci)
所有产品共享:
C o r e = ( H b a s e , F b a s e ) Core = (H_{base}, F_{base}) Core=(Hbase,Fbase)
九、架构闭环解释你图中的逻辑
你图表达的是:
1⃣ 市场变化
2⃣ 架构设计
3⃣ 产品实现
4⃣ 产品影响市场
数学闭环:
M a r k e t t + 1 = f ( P r o d u c t t ) Market_{t+1} = f(Product_t) Markett+1=f(Productt)
十、本质总结
产品化架构的真正目标是:
用一套稳定内核,应对不断变化的市场
如果没有架构:
- 每个产品是孤岛
- 技术无法沉淀
- 成本无法下降
如果有架构: - 版本差异来自配置
- 研发效率指数级提升
- 企业可以做“产品矩阵”
十一、一句话理解
产品化架构 =
以稳定核心 + 可变外层 的方式,包容功能变化与硬件变化。
“需求变化模型”。
当市场与产品变化时,需求如何从“模糊”逐步被细化为可实现、可验证、可架构支撑的工程元素?
这张图不是简单流程图,而是一个 分层约束 + 多维细化 + 质量闭环 的模型。
一、整体结构理解
图可以抽象为三大纵向区域:
左:约束来源
中:接口需求
右:功能需求
上层是抽象需求,下层是可实现需求,中间通过“细化”不断分解。
可以抽象为:
需 求 抽象 → 细化 需 求 结构化 → 细化 需 求 可实现 需求_{抽象} \xrightarrow{细化} 需求_{结构化} \xrightarrow{细化} 需求_{可实现} 需求抽象细化需求结构化细化需求可实现
二、第一层:需求来源(顶层输入)
① 来自方案设计的约束
包括:
- 系统部署形态
- 网络结构
- 节点划分
- 安全边界
例如: - 单机部署
- 主从结构
- 云边架构
数学表达为:
D e p l o y m e n t = f ( N o d e C o u n t , T o p o l o g y , L a t e n c y ) Deployment = f(NodeCount, Topology, Latency) Deployment=f(NodeCount,Topology,Latency)
② 来自硬件设计的约束
包括:
- CPU能力
- 存储容量
- IO数量
- 功率限制
性能约束模型:
P e r f o r m a n c e ≤ H a r d w a r e C a p a c i t y Performance \leq HardwareCapacity Performance≤HardwareCapacity
例如:
T o t a l L o a d = ∑ i = 1 n T a s k i TotalLoad = \sum_{i=1}^{n} Task_i TotalLoad=i=1∑nTaski
必须满足:
T o t a l L o a d ≤ C P U M a x TotalLoad \leq CPU_{Max} TotalLoad≤CPUMax
三、中间层:接口需求
接口需求处在中间,是架构的关键。
图中包括:
- 通信协议
- 软控接口
- 界面原型
- 菜单导航
- 界面转换流
这是“系统耦合边界”。
可以表示为:
S y s t e m = C o r e + I n t e r f a c e System = Core + Interface System=Core+Interface
接口定义决定模块可替换性:
C o u p l i n g ∝ I n t e r f a c e C o m p l e x i t y Coupling \propto InterfaceComplexity Coupling∝InterfaceComplexity
通信协议细化
例如:
- CAN
- Modbus
- TCP/IP
协议定义:
// 通信接口抽象类
class CommunicationInterface
{
public:
virtual void send(const std::string& msg) = 0; // 发送数据
virtual std::string receive() = 0; // 接收数据
virtual ~CommunicationInterface() {}
};
通过接口隔离变化。
四、右侧:功能需求
图中写:
每个功能定义成操作与处理流程
这说明:
功能不能停留在“描述层”,必须转为“行为模型”。
功能抽象模型
设某功能 F F F:
F = ( T r i g g e r , P r o c e s s , O u t p u t ) F = (Trigger, Process, Output) F=(Trigger,Process,Output)
其中:
- Trigger:触发条件
- Process:处理逻辑
- Output:输出结果
示例代码:功能流程化
// 功能基类
class FunctionModule
{
public:
virtual void execute() = 0; // 定义操作流程
virtual ~FunctionModule() {}
};
// 举例:电机控制功能
class MotorControl : public FunctionModule
{
public:
void execute() override
{
readSensor(); // 读取传感器
computeControl(); // 计算控制量
outputPWM(); // 输出PWM信号
}
private:
void readSensor()
{
// 获取转速或位置反馈
}
void computeControl()
{
// PID或FOC算法计算
}
void outputPWM()
{
// 控制电机驱动
}
};
五、左侧下层:部署形态与目标机硬件结构
这里强调“列出所有变体”。
这就是 变体管理(Variant Management)。
设:
- D D D = 部署变体集合
- H H H = 硬件变体集合
产品集合:
P = D × H P = D \times H P=D×H
如果: - 3种部署
- 4种硬件
则理论组合:
∣ P ∣ = 3 × 4 = 12 |P| = 3 \times 4 = 12 ∣P∣=3×4=12
这就是需求复杂度爆炸来源。
六、最底层:质量因素分解树 + 质量场景定义表
这是架构设计的核心。
质量属性包括:
- 性能
- 可用性
- 安全性
- 可扩展性
- 可维护性
质量模型:
Q u a l i t y = ∑ i = 1 n W e i g h t i × M e t r i c i Quality = \sum_{i=1}^{n} Weight_i \times Metric_i Quality=i=1∑nWeighti×Metrici
性能示例
响应时间:
R e s p o n s e T i m e = T c o m p u t e + T I O + T n e t w o r k ResponseTime = T_{compute} + T_{IO} + T_{network} ResponseTime=Tcompute+TIO+Tnetwork
可用性:
A v a i l a b i l i t y = M T B F M T B F + M T T R Availability = \frac{MTBF}{MTBF + MTTR} Availability=MTBF+MTTRMTBF
七、图中“细化”箭头的真正含义
图中多处“细化”表示:
从模糊业务目标 → 工程级定义
细化过程可以表示为:
R e q u i r e m e n t n + 1 = R e f i n e ( R e q u i r e m e n t n ) Requirement_{n+1} = Refine(Requirement_n) Requirementn+1=Refine(Requirementn)
每一次细化都增加:
- 可验证性
- 可实现性
- 可度量性
八、需求变化的动态模型
需求不是静态的。
设:
R e q u i r e m e n t ( t ) Requirement(t) Requirement(t)
随时间变化:
d R e q u i r e m e n t d t ≠ 0 \frac{dRequirement}{dt} \neq 0 dtdRequirement=0
架构必须满足:
C h a n g e C o s t ∝ 1 A r c h i t e c t u r e F l e x i b i l i t y ChangeCost \propto \frac{1}{ArchitectureFlexibility} ChangeCost∝ArchitectureFlexibility1
架构越灵活,变更成本越低。
九、本质理解总结
这张图表达的是:
需求变化并不是简单功能增加,而是三维变化:
- 部署变化
- 硬件变化
- 功能变化
而产品化架构必须承载:
A r c h i t e c t u r e ⊇ ( D e p l o y m e n t + H a r d w a r e + F u n c t i o n ) Architecture \supseteq (Deployment + Hardware + Function) Architecture⊇(Deployment+Hardware+Function)
十、一句话总结
需求变化模型的核心思想是:
通过分层细化,把市场变化逐步转化为可实现的接口、功能流程与质量指标,并控制变体复杂度。
可修改性设计
软件架构中的可修改性(Modifiability)战术(tactics),重点在于:
如何让系统在“变化到来”时,能够在时间和预算约束下完成修改、测试和部署。
这套思想来自经典架构著作 Software Architecture in Practice。
下面我会:
- 先解释“绑定时间”相关战术
- 再整体解读图中的“可修改性结构”
- 用代码示例说明每个战术
- 给出形式化理解(含公式)
一、Load Time / Runtime 战术的详细理解
1⃣ Runtime Registration(运行时注册)
原文:
Runtime registration supports plug-and-play operation at the cost of additional overhead to manage the registration.
理解
运行时注册支持“插件式”动态扩展,但代价是:
- 需要维护注册表
- 有额外查找开销
- 生命周期管理复杂
示例:发布/订阅机制
// 发布订阅的运行时注册示例
class EventBus {
public:
// 注册订阅者
void subscribe(const std::string& topic,
std::function<void()> handler) {
subscribers[topic].push_back(handler); // 运行时注册
}
// 发布事件
void publish(const std::string& topic) {
for (auto& handler : subscribers[topic]) {
handler(); // 动态调用
}
}
private:
std::map<std::string,
std::vector<std::function<void()>>> subscribers;
};
特点
| 优点 | 缺点 |
|---|---|
| 插件式扩展 | 有查找开销 |
| 动态添加 | 生命周期复杂 |
2⃣ Configuration Files(配置文件)
原文:
Configuration files are intended to set parameters at startup.
理解
通过配置文件,在启动时绑定行为。
属于:
B i n d i n g T i m e = S t a r t u p BindingTime = Startup BindingTime=Startup
示例
// 从配置文件读取参数
int main() {
Config config("config.json");
int threadCount = config.getInt("thread_count");
Server server(threadCount); // 启动时决定行为
server.start();
}
优点:
- 不改代码
- 易部署
缺点: - 只能启动时修改
- 无法运行时动态改变
3⃣ Polymorphism(多态,晚绑定)
原文:
Polymorphism allows late binding of method calls.
理解
多态允许:
C a l l S i t e → R u n t i m e D i s p a t c h CallSite \rightarrow RuntimeDispatch CallSite→RuntimeDispatch
也就是说:
方法调用的目标在运行时决定。
示例
class Shape {
public:
virtual void draw() = 0; // 虚函数
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Draw Circle\n";
}
};
class Square : public Shape {
public:
void draw() override {
std::cout << "Draw Square\n";
}
};
void render(Shape* shape) {
shape->draw(); // 运行时绑定
}
优点:
- 易扩展
- 开闭原则
缺点: - 虚函数开销
- 增加间接层
4⃣ Component Replacement(组件替换)
原文:
Component replacement allows load time binding.
理解
在加载时决定使用哪个组件。
比如:
- 动态库替换
- 插件替换
属于:
B i n d i n g T i m e = L o a d T i m e BindingTime = LoadTime BindingTime=LoadTime
示例(Linux 动态库)
// 使用 dlopen 动态加载组件
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*Func)();
Func func = (Func)dlsym(handle, "process");
func(); // 调用替换后的组件
优点:
- 可替换模块
- 无需重编译主程序
缺点: - ABI 兼容问题
- 调试困难
5⃣ Defined Protocols(协议绑定)
原文:
Adherence to defined protocols allows runtime binding of independent processes.
理解
只要遵守协议:
不同进程可以运行时绑定。
例如:
- HTTP
- REST
- gRPC
只要协议一致:
S y s t e m A ↔ P r o t o c o l ↔ S y s t e m B SystemA \leftrightarrow Protocol \leftrightarrow SystemB SystemA↔Protocol↔SystemB
无需耦合实现。
二、SVG 图整体结构讲解(可修改性模型)
中心思想:
Changes Arrive → Modifiability → Changes Made within Time & Budget
我们可以形式化表达:
M o d i f i a b i l i t y = f ( C h a n g e C o s t , C h a n g e T i m e , R i s k ) Modifiability = f(ChangeCost, ChangeTime, Risk) Modifiability=f(ChangeCost,ChangeTime,Risk)
系统目标:
M i n ( C h a n g e C o s t + C h a n g e T i m e + R i p p l e E f f e c t ) Min(ChangeCost + ChangeTime + RippleEffect) Min(ChangeCost+ChangeTime+RippleEffect)
三个大类战术
1⃣ Localize Changes(局部化变更)
核心思想:
M i n i m i z e ( A f f e c t e d M o d u l e s ) Minimize(AffectedModules) Minimize(AffectedModules)
具体战术
| 战术 | 含义 |
|---|---|
| Semantic Coherence | 模块语义一致 |
| Anticipate Expected Changes | 预判变化 |
| Generalize Module | 泛化模块 |
| Limit Possible Options | 限制变化范围 |
| Abstract Common Services | 抽象公共服务 |
示例:语义内聚
// 不好的设计
class OrderManager {
public:
void createOrder();
void sendEmail(); // 不相关
void calculateTax(); // 不相关
};
// 好的设计(高内聚)
class OrderService {
public:
void createOrder();
void cancelOrder();
};
2⃣ Prevention of Ripple Effect(防止涟漪效应)
核心目标:
R i p p l e E f f e c t ≈ 0 RippleEffect \approx 0 RippleEffect≈0
战术:
- 信息隐藏
- 保持接口不变
- 限制通信路径
- 使用中介者
示例:接口稳定
// 稳定接口
class PaymentService {
public:
virtual void pay(double amount) = 0;
};
内部实现怎么改都不影响调用方。
3⃣ Defer Binding Time(延迟绑定时间)
越晚绑定:
F l e x i b i l i t y ↑ Flexibility \uparrow Flexibility↑
但:
P e r f o r m a n c e ↓ C o m p l e x i t y ↑ Performance \downarrow Complexity \uparrow Performance↓Complexity↑
这是典型权衡。
三、架构层面的数学化理解
假设:
- B B B = 绑定时间
- C C C = 修改成本
- F F F = 灵活性
通常:
F ∝ B F \propto B F∝B
C ∝ 1 B C \propto \frac{1}{B} C∝B1
但:
O v e r h e a d ∝ B Overhead \propto B Overhead∝B
因此架构设计本质是:
Optimize F − O v e r h e a d \text{Optimize} \quad F - Overhead OptimizeF−Overhead
四、总结对照表
| 战术 | 绑定时间 | 灵活性 | 性能 |
|---|---|---|---|
| 配置文件 | 启动时 | 中 | 高 |
| 多态 | 运行时 | 高 | 中 |
| 运行时注册 | 运行时 | 很高 | 较低 |
| 组件替换 | 加载时 | 高 | 中 |
| 协议绑定 | 运行时 | 很高 | 取决网络 |
五、本质思想
可修改性不是“方便改代码”,而是:
控制变化的传播范围 + 延迟决策时间 + 降低变更成本
可以抽象为:
G o o d A r c h i t e c t u r e = M i n ( C h a n g e I m p a c t ) GoodArchitecture = Min(ChangeImpact) GoodArchitecture=Min(ChangeImpact)
“四原则、八技巧、注意节奏”进行系统的理解,并结合架构实践和代码示例详细说明。内容核心围绕 产品化架构与可修改性设计。
一、四大原则
1⃣ 功能与技术分离
- 核心思想:业务逻辑和技术实现(如通信、存储、硬件接口)要解耦。
- 目的:
- 便于业务逻辑独立演进
- 技术选型变化不影响业务
图示抽象:
S y s t e m = B u s i n e s s L o g i c ⊕ T e c h n i c a l I n f r a s t r u c t u r e System = BusinessLogic \oplus TechnicalInfrastructure System=BusinessLogic⊕TechnicalInfrastructure
示例代码(C++):
// 业务逻辑层
class OrderService {
public:
void createOrder();
};
// 技术基础设施层
class Database {
public:
void saveOrder(const Order& order);
};
// 解耦方式:依赖注入
class OrderServiceWithDI {
Database* db;
public:
OrderServiceWithDI(Database* db) : db(db) {}
void createOrder() {
Order o;
db->saveOrder(o);
}
};
2⃣ 分层架构
- 思想:按照职责划分系统层次,例如:
- 表现层(UI/接口)
- 应用层(业务逻辑)
- 领域层(核心业务模型)
- 基础设施层(数据库、硬件接口、网络)
好处:
- 屏蔽层间变化
- 局部改动不影响其他层
3⃣ 基础设施/Infrastructure
- 定义:提供技术支撑,如:
- 消息队列
- 数据库
- 日志、缓存
- 硬件驱动
- 原则:
- 独立、可替换
- 不污染业务逻辑
4⃣ 注意架构节奏
- 架构设计要适应变化节奏:
- 技术变化可能快 → 设计隔离层
- 业务变化可能慢 → 稳定领域层
- 公式化:
I m p a c t ( C h a n g e ) ∝ 1 L a y e r I s o l a t i o n Impact(Change) \propto \frac{1}{LayerIsolation} Impact(Change)∝LayerIsolation1
隔离越好,变化传播越少。
二、八大技巧
A. 隔离硬件变化
- 屏蔽式封装(Encapsulation)
- 将硬件细节封装到模块内
- 对外只提供统一接口
class MotorDriver {
public:
void setSpeed(int rpm); // 屏蔽具体电机类型
private:
void writePWM(int pwmValue);
};
- 增强式封装
- 除了屏蔽,还增加功能(如安全检查、校验)
- 例如封装硬件接口同时提供异常保护
void MotorDriver::setSpeed(int rpm) {
if (rpm < 0 || rpm > maxRpm) throw std::runtime_error("RPM超限");
writePWM(rpm * pwmFactor);
}
B. 隔离领域变化
- 领域服务(Domain Service)
- 提供跨对象、跨聚合的业务操作
- 保持领域模型纯净
class PaymentService {
public:
void transfer(Account& from, Account& to, double amount);
};
- 领域模型(Domain Model)
- 核心业务对象及规则
- 稳定,尽量不被技术变化污染
- 功能聚合(Feature Aggregation)
- 将相关功能组合成模块/聚合
- 便于复用和变更管理
- 垂直切片(Vertical Slicing)
- 按业务场景划分完整功能切片
- 每个切片包含:
- UI
- 业务逻辑
- 数据访问
F e a t u r e S l i c e = U I + A p p l i c a t i o n + D o m a i n + I n f r a s t r u c t u r e FeatureSlice = UI + Application + Domain + Infrastructure FeatureSlice=UI+Application+Domain+Infrastructure
一、切片(Slice)
切片本意就是把整体“切开成片”,在软件中指:
- 按某种维度将系统拆分成可独立理解和开发的模块
- 目的:便于开发、测试、部署或复用
类型:
- 水平切片(Horizontal Slice)
- 沿着技术层次切分
- 每个切片只覆盖系统的一层,例如:
- UI层切片
- 业务逻辑层切片
- 数据库访问层切片
- 特点:
- 模块职责单一
- 更偏向技术维度
- 示例:
UI Layer Slice: 负责登录界面
Business Layer Slice: 处理用户认证逻辑
Data Layer Slice: 查询用户表
二、垂直切片(Vertical Slice)
垂直切片 = 按业务功能切分整个技术栈
- 一个垂直切片包含从 UI → 应用逻辑 → 领域模型 → 数据/基础设施 的完整实现
- 目的是让一个功能模块能“端到端工作”
- 支撑快速交付、业务驱动开发
公式化表示:
假设系统分四层:
S y s t e m = U I + A p p l i c a t i o n + D o m a i n + I n f r a s t r u c t u r e System = UI + Application + Domain + Infrastructure System=UI+Application+Domain+Infrastructure
一个垂直切片:
V e r t i c a l S l i c e i = U I i + A p p l i c a t i o n i + D o m a i n i + I n f r a s t r u c t u r e i VerticalSlice_i = UI_i + Application_i + Domain_i + Infrastructure_i VerticalSlicei=UIi+Applicationi+Domaini+Infrastructurei
每个垂直切片是完整的、可独立运行的功能单元。
示例
假设系统是“订单管理系统”,有功能:
- 下单
- 查询订单
- 取消订单
垂直切片示意
| 功能 | 垂直切片内容 |
|---|---|
| 下单 | UI(下单界面)+ Application(下单逻辑)+ Domain(Order对象)+ Infra(数据库持久化) |
| 查询订单 | UI(查询界面)+ Application(查询逻辑)+ Domain(Order对象)+ Infra(数据库访问) |
| 取消订单 | UI(取消界面)+ Application(取消逻辑)+ Domain(Order对象)+ Infra(数据库访问) |
每个功能独立切片,开发和测试可以并行进行,不依赖其他垂直切片
水平切片 vs 垂直切片对比
| 维度 | 水平切片 | 垂直切片 |
|---|---|---|
| 切分方向 | 技术层 | 功能/业务 |
| 组成部分 | 单层模块 | 端到端完整功能 |
| 优点 | 便于技术专精 | 独立可交付,快速验证业务价值 |
| 缺点 | 需要跨层集成才能运行 | 可能重复技术实现(需复用基础设施) |
在产品化架构中的作用
- 垂直切片:
- 每个产品功能就是一个可独立交付单元
- 方便 需求变化管理
- 与“功能聚合”/“模块化”结合
- 水平切片:
- 保持技术一致性和可复用性
- 避免业务逻辑被硬件、基础设施污染
总结
- 切片 = 将系统拆分成可管理的部分
- 垂直切片 = 按业务功能拆分,从前端到后端完整覆盖,端到端可运行
- 垂直切片是敏捷、模块化和可修改性架构的核心思想之一
如果你需要,我可以画一张 水平切片 vs 垂直切片的图,用图形直观表示各层之间的差异,让概念更清晰。
你希望我画吗?
C. 框架化/平台化
- 提炼Framework(抽象框架)
- 将可复用模式提炼成框架
- 如消息处理框架、任务调度框架
- 框架中引入元模型(Metamodel)
- 用抽象模型描述框架内部结构
- 支持生成/配置不同产品变体
// 示例:元模型描述任务
struct TaskMeta {
std::string name;
int priority;
bool isPeriodic;
};
- 产品化时,通过元模型生成具体任务实例
- 避免重复实现,提高可修改性
三、总结逻辑
整体可以抽象成如下公式化关系:
- 变化隔离原则:
Δ H a r d w a r e ∩ Δ B u s i n e s s = ∅ \Delta_{Hardware} \cap \Delta_{Business} = \varnothing ΔHardware∩ΔBusiness=∅
- 硬件变更不影响业务逻辑
- 业务变更不影响基础设施
- 功能切片化:
S y s t e m = ⋃ i = 1 n F e a t u r e S l i c e i System = \bigcup_{i=1}^{n} FeatureSlice_i System=i=1⋃nFeatureSlicei - 可复用框架:
P r o d u c t i = F r a m e w o r k + C o n f i g i Product_i = Framework + Config_i Producti=Framework+Configi - 变化传播最小化:
I m p a c t ( C h a n g e ) = f ( I s o l a t i o n , E n c a p s u l a t i o n , V e r t i c a l S l i c i n g ) Impact(Change) = f(Isolation, Encapsulation, VerticalSlicing) Impact(Change)=f(Isolation,Encapsulation,VerticalSlicing)
四、落地要点
- 每个层/模块都有明确职责
- 尽量屏蔽变化来源(硬件、领域)
- 抽象通用服务,提高复用
- 通过元模型和框架生成具体产品
- 控制变化传播节奏,保证可修改性和可维护性
Crazyflie 早期版本主控板(可能是 Rev.A 或初代原型)
一、硬件概览
Crazyflie 是 掌心大小的微型四轴飞行器(Nano Quadcopter),早期版本主控板特点如下:
- 形状:十字形 PCB,四角分别安装微型电机
- 颜色:绿色 PCB,布线清晰
- 尺寸:约掌心大小
- 用途:科研、教育、开源飞控实验
整体架构可抽象为 飞行器硬件分层:
S y s t e m = MainProcessor + Sensors + Communication + Actuators + DebugInterface System = \text{MainProcessor} + \text{Sensors} + \text{Communication} + \text{Actuators} + \text{DebugInterface} System=MainProcessor+Sensors+Communication+Actuators+DebugInterface
二、主要硬件元件及作用
| 元件 | 位置 | 功能 | 备注 |
|---|---|---|---|
| Cortex-M3 主处理器 | 中央偏上 | 飞行控制、传感器数据融合、算法执行 | STM32 系列,Crazyflie 1.0时代使用 Cortex-M3,后升级 Cortex-M4 |
| 3 Axis Accelerometer(三轴加速度计) | 主处理器附近 | 感知线性加速度、姿态检测 | 支撑 PID 控制、姿态稳定 |
| 2 Axis X-Y Gyro(X-Y双轴陀螺仪) | 下方 | 测量 X/Y 方向角速度 | 与 Z 轴陀螺仪组合形成完整三轴角速度测量 |
| 1 Axis Z Gyro(Z轴单轴陀螺仪) | 单独标注 | 测量 Z 方向角速度 | 支撑航向角控制 |
| 2.4GHz Radio(无线模块) | PCB 一侧细长矩形 | 无线通信 | 连接遥控器或电脑,实现远程控制 |
| JTAG/Serial Connector(调试接口) | PCB 顶部 | 烧录程序、调试、串口通信 | 便于开发和调试 |
| 电机 + 螺旋桨 | 四角 | 产生升力与姿态控制 | 双叶螺旋桨,微型无刷电机 |
三、硬件架构理解
可抽象为分层结构:
- 控制层(Control Layer)
- Cortex-M3 主处理器
- PID 控制器或四轴姿态控制算法
- 感知层(Sensing Layer)
- 三轴加速度计
- 三轴陀螺仪(XY + Z)
- 通信层(Communication Layer)
- 2.4GHz 无线模块
- 数据传输、遥控信号接收
- 执行层(Actuation Layer)
- 四个电机 + 螺旋桨
- 将控制指令转化为实际运动
- 调试与开发接口(Debug/Programming Layer)
- JTAG / Serial
- 支持固件烧录与实时调试
用公式表示:
F l i g h t C o n t r o l l e r = f ( S e n s o r D a t a , C o n t r o l A l g o r i t h m , C o m m u n i c a t i o n , A c t u a t o r s ) FlightController = f( SensorData, ControlAlgorithm, Communication, Actuators ) FlightController=f(SensorData,ControlAlgorithm,Communication,Actuators)
四、PCB设计特点
- 小型化:整个 PCB 尺寸极小,四轴电机四角均衡布局
- 模块化:
- 主处理器 + 传感器模块
- 通信模块(射频)
- 接口模块(JTAG/Serial)
- 贴片元件密集:电阻、电容等分布合理
- 丝印标记:版本信息(Crazyflie Rev.A)、标识符(C28、EAT)
这种模块化设计为后续版本升级(Cortex-M4、增强传感器等)提供了可替换空间
五、软件与硬件接口关系
硬件抽象层(HAL, Hardware Abstraction Layer)示意:
class Motor {
public:
Motor(int id);
void setSpeed(float pwm); // 控制电机转速
};
class Sensor {
public:
virtual Vector3 read() = 0; // 返回三维数据
};
class Accelerometer : public Sensor {
public:
Vector3 read() override;
};
class Gyroscope : public Sensor {
public:
Vector3 read() override;
};
// 飞控控制逻辑
class FlightController {
Motor motors[4];
Accelerometer accel;
Gyroscope gyro;
public:
void update(float dt);
};
- 垂直切片示例:
- 每个功能模块(姿态控制、无线通信)是一个垂直切片
- 包含硬件接口 + 控制逻辑 + 数据处理
六、设计特点总结
- 模块化:
- 处理器、传感器、通信、执行器分明
- 可扩展:
- 后续升级传感器或处理器无需改变整体布局
- 可调试:
- JTAG/Serial 接口保证固件调试可行
- 轻量化:
- PCB 尺寸极小,电机布置优化
- 开源/可替换:
- Crazyflie 硬件、固件开源
- 支持社区自定义模块
总结公式化理解
- 系统功能抽象:
F l i g h t S y s t e m = C o n t r o l L a y e r + S e n s i n g L a y e r + C o m m u n i c a t i o n L a y e r + A c t u a t i o n L a y e r + D e b u g I n t e r f a c e FlightSystem = ControlLayer + SensingLayer + CommunicationLayer + ActuationLayer + DebugInterface FlightSystem=ControlLayer+SensingLayer+CommunicationLayer+ActuationLayer+DebugInterface - 数据流:
S e n s o r D a t a → F u s i o n C o n t r o l A l g o r i t h m → P W M M o t o r s → M o t i o n F l i g h t D y n a m i c s SensorData \xrightarrow{Fusion} ControlAlgorithm \xrightarrow{PWM} Motors \xrightarrow{Motion} FlightDynamics SensorDataFusionControlAlgorithmPWMMotorsMotionFlightDynamics - 模块化原则:
C h a n g e I m p a c t ∝ 1 M o d u l e I s o l a t i o n ChangeImpact \propto \frac{1}{ModuleIsolation} ChangeImpact∝ModuleIsolation1
- 硬件变化隔离良好 → 软件稳定
- 软件升级不依赖硬件微改动
一、嵌入式软件架构大类别解析
表格内容主要从 架构模型、操作系统、硬件、编程语言、代码量 五个维度对比三类嵌入式/软硬件架构。
| 项目 | 1 | 2 | 3 |
|---|---|---|---|
| 架构 | Time-Slice Polling + ISR | 多任务 + ISR | 多进程 + 多线程 |
| OS | 无OS | RTOS | Linux、Windows |
| 硬件 | MCU | MCU | CPU / 工控机 / 专用机 |
| 语言 | C | C | C++ |
| 代码量 | 几万 ~ 十几万行代码 | 几万 ~ 几十万行代码 | 几十万 ~ 几千万行代码 |
二、架构类别详解
1⃣ Time-Slice Polling + ISR(无OS,MCU)
特点:
- 架构模式:
- 时间片轮询(Time-Slice Polling)+ 中断服务程序(ISR)
- 主循环负责按时间片处理任务
- ISR 用于处理紧急事件/外部中断
- 操作系统:无操作系统(Bare-Metal)
- 硬件:低成本 MCU(如 STM32、AVR)
- 语言:C 语言
- 代码量:几万 ~ 十几万行代码
- 适用场景:
- 微型控制器
- 轻量级传感器、简单电机控制
- 响应实时性要求不高的任务
公式表示:
T a s k i ( t ) = PollingLoop ( t ) + ∑ j I S R j ( t ) Task_i(t) = \text{PollingLoop}(t) + \sum_j ISR_j(t) Taski(t)=PollingLoop(t)+j∑ISRj(t)
示例代码:
void main_loop() {
while (1) {
read_sensors();
update_actuators();
delay_ms(10); // 时间片轮询
}
}
// 中断服务
void TIMER_ISR() {
// 处理紧急事件
}
2⃣ 多任务 + ISR(RTOS)
特点:
- 架构模式:
- 多任务调度,任务间切换由 RTOS 管理
- ISR 仍然处理高优先级事件
- 操作系统:实时操作系统(FreeRTOS, μC/OS)
- 硬件:MCU
- 语言:C
- 代码量:几万 ~ 几十万行
- 适用场景:
- 工业控制
- 机器人控制器
- 需要严格实时调度(任务优先级)的场景
公式表示:
C P U t i m e = ∑ i T a s k i ⋅ P r i o r i t y i + ∑ j I S R j CPU_{time} = \sum_i Task_i \cdot Priority_i + \sum_j ISR_j CPUtime=i∑Taski⋅Priorityi+j∑ISRj
示例代码(FreeRTOS):
void TaskA(void *pvParameters) {
while(1) {
read_sensor_A();
vTaskDelay(10); // RTOS延时
}
}
void TaskB(void *pvParameters) {
while(1) {
control_motor();
vTaskDelay(5);
}
}
int main() {
xTaskCreate(TaskA, "TaskA", 128, NULL, 2, NULL);
xTaskCreate(TaskB, "TaskB", 128, NULL, 1, NULL);
vTaskStartScheduler(); // 启动RTOS调度
}
3⃣ 多进程 + 多线程(Linux / Windows,CPU 或工控机)
特点:
- 架构模式:
- 高级操作系统,支持多进程、多线程
- 进程间通信(IPC)、线程同步机制丰富
- 操作系统:Linux、Windows
- 硬件:CPU、工业 PC、专用计算机
- 语言:C++(支持面向对象)
- 代码量:几十万 ~ 几千万行
- 适用场景:
- 智能机器人
- 高级自动化控制
- 数据密集型处理、复杂图像处理或机器学习
- 需要跨硬件/软件平台集成
数据流公式:
P r o c e s s i ↔ I P C P r o c e s s j , T h r e a d k ∈ P r o c e s s i Process_i \xleftrightarrow{IPC} Process_j, \quad Thread_k \in Process_i ProcessiIPC Processj,Threadk∈Processi
示例代码(C++ + Linux 多线程):
#include <thread>
#include <vector>
void readSensor(int id) {
// 读取传感器数据
}
void controlMotor(int id) {
// 控制电机
}
int main() {
std::vector<std::thread> threads;
threads.push_back(std::thread(readSensor, 1));
threads.push_back(std::thread(controlMotor, 1));
for(auto &t : threads) t.join();
}
三、比较与分析
| 特性 | 1. Time-Slice + ISR | 2. 多任务 + ISR | 3. 多进程 + 多线程 |
|---|---|---|---|
| 硬件成本 | 低 | 中 | 高 |
| 复杂度 | 低 | 中 | 高 |
| 实时性 | 一般 | 高 | 中/高(依赖OS) |
| 可维护性 | 较低 | 中 | 高 |
| 开发语言 | C | C | C++ |
| 适用场景 | MCU 简单控制 | 工业 MCU 控制 | 智能机器人/PC |
四、总结理解
- 架构选择 → 与硬件资源、实时性、任务复杂度相关
- RTOS 提供多任务调度,提高可扩展性和实时性
- 多进程 + 多线程适合复杂嵌入式系统或工控平台
- 代码量随系统复杂度显著增加
嵌入式系统选择架构时,需权衡 硬件成本、任务复杂性、实时性需求、软件可维护性。
功能与技术分离架构图
一、整体概念
功能与技术分离(Functional vs Technical Separation)是一种架构原则:
- 功能层(FUNC):关注业务逻辑、服务、功能实现。
- 技术层(HAL、Driver、Lib):关注底层硬件访问、驱动、基础库支持。
- Common / 通用层:提供跨层可复用的公共模块。
架构目标是 解耦业务与技术实现,便于维护、扩展和移植。
二、架构层次解析
1⃣ Init层(Program Entry层)
- 位置:最上层蓝色矩形
Init层 或叫 Program Entry层 - 职责:
- 系统启动和初始化
- 调用功能层(FUNC)和技术层(HAL/Driver/Lib)
- 示例代码(C风格伪代码):
int main() {
System_Init(); // 系统初始化
FUNC_Init(); // 功能层初始化
HAL_Init(); // HAL初始化
while(1) {
FUNC_Run(); // 调用功能服务
}
}
2⃣ FUNC(功能层)
- 位置:黄色矩形
- 子模块:
- 显示服务(Display Service):界面显示、LED、屏幕刷新
- 通信服务(Communication Service):串口、CAN、无线通信
- 存储服务(Storage Service):EEPROM、Flash、SD卡存取
- 职责:
- 提供业务功能接口
- 不直接操作硬件,而通过 HAL/Driver 层
- 示意伪代码:
void DisplayService_ShowStatus(Status s) {
HAL_Display_Write(s);
}
void CommService_SendData(Data d) {
HAL_Comm_Send(d);
}
3⃣ HAL(硬件抽象层)
- 位置:绿色矩形
- 职责:
- 提供硬件抽象接口
- 屏蔽底层硬件差异
- 示例:
// HAL层示意
void HAL_Display_Write(Status s) {
Driver_Display_Write(s); // 调用底层驱动
}
4⃣ Driver(驱动层)
- 位置:绿色矩形下方
- 职责:
- 直接操作硬件寄存器
- 提供 HAL 上层调用接口
- 示例(伪代码):
void Driver_Display_Write(Status s) {
*(volatile uint32_t*)0x40001000 = s; // 寄存器写入
}
5⃣ Lib(基础库)
- 位置:灰色矩形
- 职责:
- 提供通用函数库
- 算法、数据结构、工具函数
- 示例:
int max(int a, int b) {
return (a > b) ? a : b;
}
6⃣ Common(通用/共享模块)
- 位置:右侧灰色竖条
- 职责:
- 提供跨层共享资源
- 常量定义、日志、工具函数
- 示例:
#define LED_OK 1
#define LED_ERROR 0
void Log(const char* msg);
三、层次关系总结
说明:
- 功能层 只依赖 HAL/Driver,不直接操作硬件
- 技术层(HAL/Driver/Lib)独立,可替换硬件或库实现
- Common 提供跨层复用,避免重复实现
四、架构优点
- 可维护性高:业务逻辑与硬件驱动分离
- 可移植性强:更换 MCU 或操作系统时,仅改 HAL/Driver 层
- 可扩展性好:新增功能服务,不影响硬件层
- 测试友好:可单独对功能层、HAL、Driver 进行单元测试
隔离硬件变化架构图
一、核心概念
隔离硬件变化(Hardware Variation Isolation)是一种软件设计策略:
- 目的:屏蔽上层功能与不同硬件实现的差异,提高可移植性和可维护性。
- 核心手段:
- 屏蔽式封装(Shielded Encapsulation)
- 对硬件接口进行封装,隐藏底层细节
- 上层只调用统一接口
- 增强式封装(Enhanced Encapsulation)
- 对硬件服务进行高级封装,提供更多功能或安全保证
- 可在不同硬件间复用
- 屏蔽式封装(Shielded Encapsulation)
二、架构层次解析
整体以 子系统 为边界,分为三层:
1⃣ Modules(功能模块层)
- 位置:上层,黄色矩形
- 职责:
- 负责功能逻辑和配置
- 不直接操作硬件
- 示例文件:
comm.c:负责通信配置crtp.c:实现收发服务(低层协议逻辑)crtp.h:提供函数指针结构体接口
- 特点:
- 依赖 HAL/Driver 提供的接口
- 可以跨硬件版本复用
// comm.c 伪代码示意
void Comm_Init() {
HAL_Comm_Config(); // 调用 HAL 层配置接口
}
2⃣ HAL(硬件抽象层)
- 位置:中层,蓝色矩形
- 职责:
- 将硬件差异屏蔽给上层
- 提供统一接口调用
- 示例文件:
nrf24link.c(Crazyflie 1.0)eskylink.c(Crazyflie 1.0)radiolink.c(Crazyflie 2.0)
// HAL层接口
void HAL_SendPacket(uint8_t* data, size_t len) {
Radio_Write(data, len); // 具体驱动实现
}
3⃣ Driver(驱动层)
- 位置:底层,绿色矩形
- 职责:
- 直接操作硬件寄存器和外设
- 提供 HAL 调用接口
- 示例文件:
nrf24l01.c(1.0版本无线芯片驱动)uart_syslink.c(2.0版本串口驱动)
// Driver 层伪代码
void Radio_Write(uint8_t* data, size_t len) {
SPI_WriteBuffer(RADIO_REG_TX, data, len);
}
三、封装策略说明
1⃣ 屏蔽式封装(Shielded Encapsulation)
- 用途:隐藏硬件接口变化
- 上层模块无需关心底层实现
- 在图中:
crtp.h 中的函数指针结构体
2⃣ 增强式封装(Enhanced Encapsulation)
- 用途:提供更高级功能接口(如收发服务、协议处理等)
- 在图中:
crtp.c 实现收发服务
四、版本兼容性示例
| 层级 | CF 1.0 实现 | CF 2.0 实现 |
|---|---|---|
| HAL | nrf24link.c |
radiolink.c |
| Driver | nrf24l01.c |
uart_syslink.c |
| Modules | comm.c、crtp.c |
复用部分模块 |
说明:上层 Modules 不直接依赖底层实现,通过 HAL 接口完成硬件隔离,支持不同版本无线硬件替换而不影响业务逻辑。
五、架构总结
- 模块化层次清晰:
- Modules → HAL → Drivers
- 高层业务逻辑与底层硬件完全解耦
- 硬件变化隔离:
- 更换无线芯片、MCU 或总线方式时,只需修改 HAL/Driver
- 封装策略:
- 屏蔽式封装:隐藏接口变化
- 增强式封装:提供额外功能和安全接口
六、示意伪代码结构
Modules
|
|-- comm.c // 配置
|-- crtp.c // 收发服务
|-- crtp.h // 函数指针接口
|
v
HAL
|
|-- nrf24link.c / eskylink.c / radiolink.c
|
v
Driver
|
|-- nrf24l01.c / uart_syslink.c
完整的小例子
├── main.c # 入口
├── comm.c # modules层:配置链路
├── crtp.c / crtp.h # modules层:收发服务 + 函数指针结构体
├── nrf24link.c # hal层:cf 1.0 适配
├── radiolink.c # hal层:cf 2.0 适配
├── nrf24l01.c/h # drivers层:SPI 驱动(cf 1.0)
├── uart_syslink.c/h # drivers层:UART 驱动(cf 2.0)
└── Makefile
架构分三层:
modules层:comm.c+crtp.chal层:nrf24link.c/eskylink.c/radiolink.cdrivers层:nrf24l01.c/uart_syslink.c
crtp.h — 核心函数指针结构体(屏蔽式封装)
// crtp.h
#ifndef CRTP_H
#define CRTP_H
#include <stdint.h>
// CRTP 数据包
typedef struct {
uint8_t port;
uint8_t channel;
uint8_t data[30];
uint8_t size;
} CRTPPacket;
// 函数指针结构体 —— 屏蔽底层实现
typedef struct {
int (*init)(void);
int (*sendPacket)(CRTPPacket *pk);
int (*receivePacket)(CRTPPacket *pk);
int (*isConnected)(void);
} CRTPLink;
// 全局链路接口(由 comm.c 配置)
extern CRTPLink *crtpLink;
void crtpInit(void);
int crtpSendPacket(CRTPPacket *pk);
int crtpReceivePacket(CRTPPacket *pk);
#endif
crtp.c — 收发服务实现
// crtp.c
#include <stdio.h>
#include "crtp.h"
CRTPLink *crtpLink = NULL; // 由 comm.c 注入
void crtpInit(void) {
if (crtpLink && crtpLink->init) {
crtpLink->init();
printf("[crtp] 初始化完成,链路已就绪\n");
}
}
int crtpSendPacket(CRTPPacket *pk) {
if (!crtpLink) return -1;
printf("[crtp] 发送数据包: port=%d, size=%d\n", pk->port, pk->size);
return crtpLink->sendPacket(pk);
}
int crtpReceivePacket(CRTPPacket *pk) {
if (!crtpLink) return -1;
int ret = crtpLink->receivePacket(pk);
if (ret == 0)
printf("[crtp] 收到数据包: port=%d, size=%d\n", pk->port, pk->size);
return ret;
}
nrf24l01.c — drivers 层驱动(cf 1.0)
// nrf24l01.c
#include <stdio.h>
void nrf24l01_init(void) {
printf("[nrf24l01] SPI 驱动初始化\n");
}
int nrf24l01_send(const uint8_t *buf, int len) {
printf("[nrf24l01] 通过 SPI 发送 %d 字节\n", len);
return 0;
}
int nrf24l01_receive(uint8_t *buf, int *len) {
printf("[nrf24l01] 通过 SPI 接收数据\n");
*len = 4;
return 0;
}
uart_syslink.c — drivers 层驱动(cf 2.0)
// uart_syslink.c
#include <stdio.h>
void uart_syslink_init(void) {
printf("[uart_syslink] UART 驱动初始化\n");
}
int uart_syslink_send(const uint8_t *buf, int len) {
printf("[uart_syslink] 通过 UART 发送 %d 字节\n", len);
return 0;
}
int uart_syslink_receive(uint8_t *buf, int *len) {
printf("[uart_syslink] 通过 UART 接收数据\n");
*len = 6;
return 0;
}
nrf24link.c — hal 层(cf 1.0,增强式封装)
// nrf24link.c
#include <stdio.h>
#include <stdint.h>
#include "crtp.h"
#include "nrf24l01.h" // 实际项目中用头文件
static int nrf24link_init(void) {
printf("[nrf24link] HAL 初始化\n");
nrf24l01_init();
return 0;
}
static int nrf24link_send(CRTPPacket *pk) {
return nrf24l01_send(pk->data, pk->size);
}
static int nrf24link_receive(CRTPPacket *pk) {
int len = 0;
int ret = nrf24l01_receive(pk->data, &len);
pk->size = len;
return ret;
}
static int nrf24link_isConnected(void) { return 1; }
// 暴露给 comm.c 的接口
CRTPLink nrf24LinkVTable = {
.init = nrf24link_init,
.sendPacket = nrf24link_send,
.receivePacket = nrf24link_receive,
.isConnected = nrf24link_isConnected,
};
radiolink.c — hal 层(cf 2.0,增强式封装)
// radiolink.c
#include <stdio.h>
#include <stdint.h>
#include "crtp.h"
#include "uart_syslink.h" // 实际项目中用头文件
static int radiolink_init(void) {
printf("[radiolink] HAL 初始化\n");
uart_syslink_init();
return 0;
}
static int radiolink_send(CRTPPacket *pk) {
return uart_syslink_send(pk->data, pk->size);
}
static int radiolink_receive(CRTPPacket *pk) {
int len = 0;
int ret = uart_syslink_receive(pk->data, &len);
pk->size = len;
return ret;
}
static int radiolink_isConnected(void) { return 1; }
CRTPLink radioLinkVTable = {
.init = radiolink_init,
.sendPacket = radiolink_send,
.receivePacket = radiolink_receive,
.isConnected = radiolink_isConnected,
};
comm.c — 负责配置,选择链路注入
// comm.c
#include <stdio.h>
#include "crtp.h"
// 声明各 HAL 层的 vtable
extern CRTPLink nrf24LinkVTable; // cf 1.0
extern CRTPLink radioLinkVTable; // cf 2.0
#define USE_CF2 // 切换这个宏来选择硬件版本
void commInit(void) {
printf("[comm] 配置通信链路...\n");
#ifdef USE_CF2
crtpLink = &radioLinkVTable; // cf 2.0: radiolink -> uart_syslink
printf("[comm] 使用 cf 2.0 链路: radiolink\n");
#else
crtpLink = &nrf24LinkVTable; // cf 1.0: nrf24link -> nrf24l01
printf("[comm] 使用 cf 1.0 链路: nrf24link\n");
#endif
crtpInit();
}
main.c — 入口,串联整个调用链
// main.c
#include <stdio.h>
#include "crtp.h"
extern void commInit(void);
int main(void) {
printf("=== Crazyflie 通信子系统启动 ===\n\n");
// 1. comm.c 负责配置链路
commInit();
printf("\n--- 发送测试包 ---\n");
// 2. 上层业务通过 crtp.c 的服务收发,不感知底层
CRTPPacket pk = {
.port = 0,
.channel = 0,
.data = {0xDE, 0xAD, 0xBE, 0xEF},
.size = 4
};
crtpSendPacket(&pk);
printf("\n--- 接收测试包 ---\n");
CRTPPacket rx = {0};
crtpReceivePacket(&rx);
printf("\n=== 完成 ===\n");
return 0;
}
编译运行:
gcc main.c crtp.c comm.c nrf24link.c radiolink.c \
nrf24l01.c uart_syslink.c -o demo && ./demo
https://godbolt.org/z/1d7cs7x8x
输出(USE_CF2 模式):
=== Crazyflie 通信子系统启动 ===
[comm] 配置通信链路...
[comm] 使用 cf 2.0 链路: radiolink
[radiolink] HAL 初始化
[uart_syslink] UART 驱动初始化
[crtp] 初始化完成,链路已就绪
--- 发送测试包 ---
[crtp] 发送数据包: port=0, size=4
[uart_syslink] 通过 UART 发送 4 字节
--- 接收测试包 ---
[uart_syslink] 通过 UART 接收数据
[crtp] 收到数据包: port=0, size=6
=== 完成 ===
核心设计要点:
| 层 | 文件 | 作用 |
|---|---|---|
| modules | comm.c |
选择硬件版本,注入链路 |
| modules | crtp.c |
统一收发接口,不感知底层 |
| hal | nrf24link / radiolink |
适配不同硬件的增强封装 |
| drivers | nrf24l01 / uart_syslink |
最底层寄存器/外设操作 |
切换 #define USE_CF2 即可在 cf 1.0 和 cf 2.0 之间无缝切换,上层代码零修改。
SMT(表面贴装技术)生产线流程
一、SMT 生产线核心概念
**SMT(Surface Mount Technology,表面贴装技术)**是一种电子组装工艺,将电子元件直接安装在 PCB(Printed Circuit Board,印刷电路板)表面,而不是通过传统的插针穿孔。
- 目标:提高 PCB 组装密度、可靠性,适应小型化电子产品需求。
- 关键点:自动化、高精度、高速度、可检测。
二、生产线流程解析
流程顺序如下(从左到右、上到下):
1⃣ 送板(Loader)
- 功能:自动将裸 PCB 板送入 SMT 生产线
- 技术要点:
- 自动对齐 PCB
- 送入链条或输送带
- 目标:保证生产连续性,提高效率
- 示意注释:
PCB → Loader → SMT生产线
2⃣ 锡膏印刷(Solder Paste Printing)
- 功能:通过钢网(Stencil)将锡膏精准印刷到 PCB 焊盘上
- 技术要点:
- 锡膏厚度和体积严格控制
- 对应焊盘位置精准定位
- 印刷质量直接影响焊接可靠性
- 常用设备:
- 半自动或全自动锡膏印刷机
- 伪代码/逻辑表示:
for each PCB:
align PCB with stencil
print solder paste on pads
3⃣ SPI(Solder Paste Inspection,锡膏检测)
- 功能:检查锡膏质量,包括:
- 体积(Volume)
- 面积(Area)
- 高度(Height)
- 偏移(Offset)
- 缺失(Missing)
- 技术特点:
- 3D检测常用激光或摄像头
- 输出良品/缺陷反馈到生产线
- 目的:减少焊接缺陷,提高可靠性
- 示意注释:
if solder_paste_quality < threshold:
alert operator
remove PCB from line
4⃣ 贴片(Pick and Place)
- 功能:高速、精确地将表面贴装元件放置到锡膏上
- 技术要点:
- 高速机械手/真空吸嘴
- CCD 摄像头定位
- 贴装精度可达 ±0.05mm
- 示意流程:
for each component in BOM:
pick component from feeder
align component
place on PCB pad
5⃣ 回流焊(Reflow Oven)
- 功能:通过温度曲线使锡膏熔化,实现元件与 PCB 的焊接固化
- 关键步骤:
- 预热(Preheat)
- 恒温段(Soak)
- 回流段(Reflow)
- 冷却段(Cooling)
- 注意点:
- 温度曲线需匹配元件特性
- 避免焊接缺陷(虚焊、锡球、翘件)
6⃣ AOI(Automated Optical Inspection,自动光学检测)
- 功能:检测焊接后的缺陷,如:
- 缺件(Missing)
- 偏移(Misalignment)
- 锡桥(Solder Bridge)
- 立碑(Tombstoning)
- 虚焊(Cold Joint)
- 技术特点:
- CCD摄像头 + 图像处理算法
- 实时反馈,可剔除或标记问题板
7⃣ 人工目检(Manual Inspection)
- 功能:人工复核 AOI 检测或补充细节
- 重要性:
- 对小缺陷或光学难以检测的异常进行确认
- 最终保证出厂 PCB 质量
三、流程总结与特性
- 自动化程度高:Loader、SPI、贴片、回流焊、AOI
- 质量控制多级:SPI → AOI → 人工
- 闭环反馈:
- SPI 或 AOI 检测出缺陷时,可以停止生产或报警
- 效率与精度:
- SMT 自动化流水线速度可达每小时数千板
- 贴装精度可达 ±0.05mm
四、流程示意伪代码
for each PCB in production_line:
Loader.send_board(PCB)
SolderPrinter.print_paste(PCB)
if SPI.inspect(PCB) == NG:
remove PCB
continue
PickPlace.place_components(PCB)
ReflowOven.reflow(PCB)
if AOI.inspect(PCB) == NG:
mark PCB
ManualCheck.inspect(PCB)
五、注意点
- 锡膏印刷质量:直接影响回流焊焊点
- 贴片精度:影响焊接可靠性
- 温度曲线匹配:避免元件损伤
- 多级检测:减少返工与报废率
嵌软架构大类别
功能与技术分离
该系统从上到下共分为以下五个主要层级:
- 基础设施层 (Infrastructure Layer - Top):位于最顶部,包含 UI框架 和 Socket框架。这一层直接与外部实体(如用户、OS鼠标/屏幕管理、OS网卡驱动及远程GUI)进行交互。
- 展现层 (Presentation Layer):包含核心的 UI 模块,并与右侧的 通信协议 及 适配层 平行。它负责处理用户输入并通过
OnCmd()向下传递,同时接收Update()进行状态更新。 - 应用层 (Application Layer):核心处理单元为 应用层 CmdProcess()。该层作为中间枢纽,协调展现层与领域层之间的逻辑,通过
DmService()调用服务并接收Notify()异步通知。 - 领域层 (Domain Layer):图中展示了两个垂直排布的领域层级,内部包含多个 领域服务层(标注有 SA/PI 接口)和 领域模型层。该层是系统的核心业务逻辑所在,并能通过 RPC 或 Pub/Sub 模式与右侧的 其他组件 进行通信。
- 适配层 (Adaptation Layer):位于底层,包含 DRV/HAL封装、DAO(数据访问对象)和 FileIO。它将领域层的逻辑指令转化为对具体基础设施的调用。
- 基础设施层 (Infrastructure Layer - Bottom):底部的基础设施支撑,包含 三方库、DB访问框架 和 File/Log框架。它们最终连接到最底层的物理或系统资源:硬件(通过总线/串口)、DB/IMDB(通过DBMS接口)以及 File/Log(通过OS File接口)。
典型嵌入式/实时系统的分层架构
1⃣ 外部实体
- 用户(User):系统最终使用者,输入命令、查看输出。
- 远程GUI(RemoteGUI):远程操作界面,通过网络与系统交互。
注:这里体现了系统的“边界”,用户和远程GUI是系统外部依赖。
2⃣ 顶部基础设施层(Top Infrastructure)
- UI框架:处理用户输入、事件管理、显示更新。
- Socket框架:处理网络通信、数据传输协议。
特点:
- 面向外部接口,屏蔽底层复杂性。
- 提供统一的 API 给展现层使用。
- 层级高,直接与外部实体交互。
3⃣ 展现层 / 适配层(Presentation Layer)
- UI:展示核心内容,接收用户命令。
- 通信协议:管理上下行数据格式和协议逻辑。
- 适配层(Adaptor):桥接上层 UI/协议与应用层逻辑。
交互方式:
- 用户操作 →
OnCmd()→ 应用层- 应用层状态更新 →
Update()→ UI 展示- Protocol 处理网络事件并传递到 App_Core
注释:
UI_Comp -->|"OnCmd()"| App_Core
App_Core -->|"Update()"| UI_Comp
Protocol -->|"OnCmd()"| App_Core
App_Core -->|"Send()"| Protocol
4⃣ 应用层(Application Layer)
- 核心功能:业务指令处理、事件调度。
- 作用:
- 协调展现层与领域层
- 通过
DmService()调用领域服务 - 接收
Notify()异步通知
伪逻辑:
5⃣ 领域层(Domain Layer)
- 多个领域块(DS1~DS5)表示系统不同业务功能模块。
- 领域服务层(Service):
- 提供业务逻辑接口(SA/PI)
- 可被应用层调用,也能进行 RPC / Pub/Sub 与组件通信
- 领域模型层(Model):
- 管理业务数据结构和状态
- 提供业务约束和规则
特点:
- 系统核心业务逻辑集中于此
- 与具体技术(硬件/数据库/IO)解耦
- 支持垂直切片和模块化服务
交互示例:
App_Core -->|"DmService()"| DomainLayer1
DomainLayer1 -->|"Notify()"| App_Core
DomainLayer1 <==>|"RPC / Pub/Sub"| Component_Block
6⃣ 底层适配层(Adaptation Layer)
- DRV/HAL:硬件抽象层,屏蔽硬件变化。
- DAO:数据访问对象,屏蔽数据库访问细节。
- FileIO:文件/日志操作接口。
作用:将领域逻辑与底层技术(硬件/文件/数据库)分离,实现“技术封装”。
DomainLayer2 --> DRV_HAL
DomainLayer2 --> DAO
DomainLayer2 --> FileIO
DRV_HAL --> Libs
DAO --> DB_Framework
FileIO --> Log_Framework
7⃣ 底部基础设施层(Bottom Infrastructure)
- 提供底层资源访问能力:
- 三方库 → 硬件接口(总线/串口)
- 数据库访问框架 → DB/IMDB
- 文件/日志框架 → File/Log
- 最终物理层:
注释:
领域层不直接操作硬件/数据库/文件,所有操作通过适配层和底层框架完成,实现功能与技术分离。
8⃣ 关键设计原则
- 功能与技术分离
- 业务逻辑在领域层,技术细节封装在适配层/基础设施层。
- 分层架构
- 清晰分离 UI、应用、领域、适配、底层。
- 模块化与组件化
- 支持 RPC / Pub/Sub,实现松耦合。
- 可扩展性和可维护性
- 增加新功能只改领域层或新增领域块
- 底层技术升级无需修改领域逻辑
9⃣ 总结
- 上层(UI/应用):面向用户和业务,技术透明
- 中层(领域):业务核心,技术无感知
- 下层(适配/基础设施):屏蔽硬件和技术差异,提供稳定接口
- 思想:垂直切片 + 层次分离 + 功能与技术解耦
可视化效果:
- 黄色:应用逻辑
- 蓝色:技术适配
- 红色:用户/外部交互
- 粉色:领域服务
隔离领域变化 — 解析
这个图展示了一个 SMT 或制造类系统的领域建模思路,核心思想是 将业务逻辑抽象为领域对象与领域服务,通过 服务层调用对象,从而隔离具体功能变化对整体系统的影响。
1⃣ 领域服务子域 — 维护功能
« 领域服务子域 »:维护功能
- 制程管理
- 调整参数
理解
- 作用:封装系统中维护相关的业务逻辑,如工艺制程管理、参数调整等。
- 特点:
- 对外提供统一接口
- 内部实现可以随需求变化而修改,而不影响其他子域
- 伪代码示意:
class MaintenanceService {
public:
void ProcessStepManagement(); // 制程管理逻辑
void AdjustParameters(); // 调整参数逻辑
};
这里体现了 “隔离领域变化” 的原则:维护功能变化只影响该服务内部实现,不影响控制功能或其他领域对象。
2⃣ 领域服务子域 — 控制功能
« 领域服务子域 »:控制功能
- 制程总控
- 搬运 / 定位 / 印刷 / 擦拭
理解
- 作用:负责生产线核心操作逻辑(如 SMT 贴片流程)。
- 内部封装为多个动作/操作:
- 搬运(Move)
- 定位(Position)
- 印刷(Print)
- 擦拭(Wipe)
- 特点:
- 控制功能变化集中在此服务
- 上层只调用接口,不关心内部实现
- 伪代码示意:
class ControlService {
public:
void TotalControl(); // 总控流程
void MoveComponent(); // 搬运逻辑
void PositionComponent(); // 定位逻辑
void PrintComponent(); // 印刷逻辑
void WipeComponent(); // 擦拭逻辑
};
通过服务调用对象,控制逻辑变化不会直接影响其他子域,实现业务逻辑隔离。
3⃣ 领域对象 — 业务实体
« 领域对象 »:
- 制程定义
- 搬运器件
- 定位器件
- 印刷器件
- 擦拭器件
理解
- 作用:抽象生产线中核心业务元素
- 特点:
- 每个对象封装自身属性和状态
- 对象变化(增加新属性、修改方法)只影响对应领域服务
- 伪代码示意:
class ProcessDefinition { // 制程定义
string processName;
map<string, double> parameters;
};
class TransportDevice { // 搬运器件
vector<string> track;
string clamp;
};
class PositionDevice { // 定位器件
string lens;
};
class PrintDevice { // 印刷器件
string printHead;
string stencil;
};
class WipeDevice { // 擦拭器件
string brush;
string cleaningPad;
};
关键点:领域对象和服务分离,使 控制逻辑(服务) 和 数据结构(对象) 可以独立变化。
4⃣ 隔离领域变化的设计思想
- 服务层封装操作逻辑
- 所有变化都通过服务接口暴露,内部可以自由调整实现
- 对象层封装数据状态
- 新增对象或修改对象属性,不会破坏其他子域逻辑
- 接口调用隔离
- 上层调用者只依赖接口,不依赖内部实现
- 示意代码:
void RunProcess(ControlService& ctrl, ProcessDefinition& process) {
ctrl.TotalControl();
ctrl.MoveComponent();
ctrl.PositionComponent();
ctrl.PrintComponent();
ctrl.WipeComponent();
}
如果未来“印刷”流程需要升级,只修改
PrintComponent()内部实现,上层和其他流程不受影响。
5⃣ 图中视觉意义
- 粉色块:领域服务子域
- 蓝色块:领域对象
- 白色矩形/多边形:具体操作或属性
- 排布方式:服务在上、对象在下 → “服务驱动对象”
核心理念:服务定义行为,对象保存状态,通过分层和接口实现领域变化隔离。
总结
- **领域服务(Domain Service)**负责封装操作和流程逻辑
- **领域对象(Domain Object)**负责封装数据和状态
- 服务调用对象 → 上层调用接口,不关心内部实现
- 变化隔离:服务内部逻辑变化或对象属性增加不会破坏整体系统
- 代码映射:
领域服务子域 (Service) <--> 控制功能 / 维护功能
领域对象 (Object) <--> 制程定义 / 搬运器件 / 定位器件 / 印刷器件 / 擦拭器件
这就是经典的 领域驱动设计(DDD) + 隔离领域变化 的实现方法。
如何通过“领域建模 + 子域划分 + 器件抽象”来隔离领域变化
一、什么是“隔离领域变化”
软件复杂的根源不是代码,而是:
业务在不断变化
在你这个印刷设备系统中,变化来源主要有:
- 新增一种器件
- 更换硬件品牌
- 新增自动流程
- 修改节拍计算方式
- 新增一种人工操作
- 改变控制策略
如果变化直接影响所有模块,那系统会迅速失控。
所以核心目标是:
变化影响范围 → 局部化 变化影响范围 \to 局部化 变化影响范围→局部化
我们希望:
系统影响半径 = 最小 系统影响半径 = 最小 系统影响半径=最小
二、从你的图看“变化分层”
我们按层级分析:
第一层:领域服务子域(行为层)
例如:
- 制程管理
- 手动操作
- 自动印刷
- 器件控制
这一层负责“行为编排”
但不直接操作硬件细节。
第二层:领域对象(模型层)
例如:
- 制程定义
- 搬运器件
- 定位器件
- 印刷器件
- 擦拭器件
这一层负责“业务概念表达”
它是:
稳定抽象层
三、变化从哪里来?
我们拆解一下变化源:
1⃣ 器件变化(硬件变化)
例如:
- 轨道换型号
- 镜头升级
- 钢网结构改变
- 印刷头改为双头
这是:
设备变化
2⃣ 流程变化
例如:
- 自动adjust策略变化
- 增加清洗步骤
- 新的节拍算法
这是:
业务规则变化
四、如何隔离器件变化
核心思想:
用抽象接口屏蔽具体器件
示例:搬运器件抽象
// 搬运器件抽象接口
class ITransportDevice
{
public:
virtual ~ITransportDevice() = default;
// 搬运到指定位置
virtual void MoveTo(double position) = 0;
// 夹板打开
virtual void OpenClamp() = 0;
// 夹板关闭
virtual void CloseClamp() = 0;
};
具体实现(轨道版本)
// 轨道实现
class RailTransport : public ITransportDevice
{
public:
void MoveTo(double position) override
{
// 调用轨道控制器驱动
}
void OpenClamp() override
{
// 控制轨道夹板气缸
}
void CloseClamp() override
{
// 控制轨道夹板气缸
}
};
如果未来改成皮带式搬运?
只需要:
class BeltTransport : public ITransportDevice
{
public:
void MoveTo(double position) override
{
// 皮带控制逻辑
}
void OpenClamp() override
{
// 皮带夹持结构
}
void CloseClamp() override
{
}
};
而:
自动印刷模块完全不用改
这就是:
变化封闭原则 变化封闭原则 变化封闭原则
五、隔离“流程变化”
例如节拍计算:
节拍本质是:
T = T 搬运 + T 定位 + T 印刷 + T 擦拭 T = T_{搬运} + T_{定位} + T_{印刷} + T_{擦拭} T=T搬运+T定位+T印刷+T擦拭
如果未来增加AOI检测:
T = T 搬运 + T 定位 + T 印刷 + T 擦拭 + T A O I T = T_{搬运} + T_{定位} + T_{印刷} + T_{擦拭} + T_{AOI} T=T搬运+T定位+T印刷+T擦拭+TAOI
我们不能把公式写死在控制逻辑中。
正确做法:策略模式
// 节拍策略接口
class ICycleStrategy
{
public:
virtual double Calculate() = 0;
};
普通模式
class NormalCycleStrategy : public ICycleStrategy
{
public:
double Calculate() override
{
// 固定计算方式
return transport + align + print + wipe;
}
};
加入AOI模式
class AOICycleStrategy : public ICycleStrategy
{
public:
double Calculate() override
{
return transport + align + print + wipe + aoi;
}
};
自动印刷只依赖接口:
class AutoPrint
{
public:
AutoPrint(std::shared_ptr<ICycleStrategy> strategy)
: strategy_(strategy)
{}
void Run()
{
double cycle = strategy_->Calculate(); // 不关心具体算法
}
private:
std::shared_ptr<ICycleStrategy> strategy_;
};
六、为什么领域对象必须独立?
你的图里:
- 搬运器件
- 定位器件
- 印刷器件
- 擦拭器件
是领域对象
它们不应该: - 知道数据库
- 知道UI
- 知道PLC协议
否则:
领域模型污染 = 变化放大 领域模型污染 = 变化放大 领域模型污染=变化放大
七、功能聚合 vs 器件聚合
你图中其实已经在做两种切分:
横向:按职责
- 制程管理
- 手动操作
- 自动印刷
这是:
行为切片
纵向:按器件
- 搬运
- 定位
- 印刷
- 擦拭
这是:
垂直切片
每个切片拥有:
- 自己的模型
- 自己的控制逻辑
- 自己的硬件适配
这叫:
高内聚 高内聚 高内聚
数学上可理解为:
耦合度 → 最小 耦合度 \to 最小 耦合度→最小
内聚度 → 最大 内聚度 \to 最大 内聚度→最大
八、领域隔离的真正本质
领域隔离不是分文件夹。
是:
让不同变化源落在不同模块
我们可以抽象成一个函数:
系统 = f ( 设备变化 , 流程变化 , 算法变化 ) 系统 = f(设备变化, 流程变化, 算法变化) 系统=f(设备变化,流程变化,算法变化)
目标是:
∂ 系统 ∂ 单一变化 → 局部 \frac{\partial 系统}{\partial 单一变化} \to 局部 ∂单一变化∂系统→局部
意思是:
某个变化只影响一个小区域
九、结合你图的最终架构模型
建议分层:
UI层
↓
应用服务层
↓
领域服务层(制程/自动/手动)
↓
领域模型层(器件抽象)
↓
基础设施层(驱动/PLC/IO)
十、总结成一句话
你这张图本质在表达:
通过“领域对象抽象 + 子域分离 + 器件封装 + 策略解耦”,把变化源拆开,让变化不扩散。
完整的领域分层 + 子域划分 + 对象依赖结构图
用“领域子域”组织行为,用“领域对象”承载状态,用依赖箭头表达变化传导方向。
一、整体结构理解
图可以抽象成三层结构:
第一层:领域服务子域(上半部分)
- 制程管理
- 手动操作
- 自动印刷
- 器件控制
- 设备监控
- 系统管理
这是 行为组织层
它负责: - 编排流程
- 协调对象
- 定义业务动作
但它不应该包含: - 具体硬件实现
- 数据库存取细节
第二层:领域对象(下半部分蓝色)
- 制程定义
- 搬运器件
- 定位器件
- 印刷器件
- 擦拭器件
- 权限定义
- 日志数据
- 产量数据
这是 业务模型层
它负责: - 承载状态
- 定义规则
- 约束业务
第三层:依赖箭头(蓝色箭线)
箭头从“领域服务”指向“领域对象”。
这表达一个非常重要的原则:
领域服务 → 依赖 → 领域对象 领域服务 \to 依赖 \to 领域对象 领域服务→依赖→领域对象
而不是反过来。
这叫:
依赖方向单向化
二、变化传播模型(数学视角)
假设系统函数为:
S y s t e m = f ( S , D ) System = f(S, D) System=f(S,D)
其中:
- S S S = 领域服务
- D D D = 领域对象
变化来源: - Δ S \Delta S ΔS :流程变化
- Δ D \Delta D ΔD :业务规则变化
我们希望:
∂ S ∂ D ≠ 0 \frac{\partial S}{\partial D} \neq 0 ∂D∂S=0
但:
∂ D ∂ S = 0 \frac{\partial D}{\partial S} = 0 ∂S∂D=0
意思是: - 服务可以依赖对象
- 对象不能依赖服务
这保证了:
核心业务模型稳定
三、每个子域的职责本质
1⃣ 制程管理
负责:
- 导入
- 导出
本质是:
制程定义的生命周期管理
示例代码:
// 制程定义领域对象
class ProcessDefinition
{
public:
void AddStep(const std::string& step)
{
steps_.push_back(step); // 添加工艺步骤
}
const std::vector<std::string>& Steps() const
{
return steps_;
}
private:
std::vector<std::string> steps_;
};
// 制程管理领域服务
class ProcessManagementService
{
public:
void Import(ProcessDefinition& def)
{
// 从文件解析
// 更新领域对象
}
void Export(const ProcessDefinition& def)
{
// 序列化领域对象
}
};
2⃣ 自动印刷
包含:
- 运行模式
- 暂停
- 节拍
- 制程总控
节拍公式可以抽象为:
T = ∑ i = 1 n T i T = \sum_{i=1}^{n} T_i T=i=1∑nTi
其中: - T i T_i Ti 是每个器件执行时间
例如:
T = T 搬运 + T 定位 + T 印刷 + T 擦拭 T = T_{搬运} + T_{定位} + T_{印刷} + T_{擦拭} T=T搬运+T定位+T印刷+T擦拭
代码示例:
class CycleCalculator
{
public:
double Calculate(double transport,
double align,
double print,
double wipe)
{
return transport + align + print + wipe;
}
};
3⃣ 器件控制
这一块非常关键。
它是:
行为层与器件模型之间的桥梁
例如:
class TransportDevice
{
public:
void MoveTo(double pos)
{
position_ = pos; // 更新状态
}
double Position() const
{
return position_;
}
private:
double position_{0.0};
};
class DeviceControlService
{
public:
DeviceControlService(TransportDevice& device)
: device_(device)
{}
void ExecuteTransport(double target)
{
device_.MoveTo(target); // 调用领域对象
}
private:
TransportDevice& device_;
};
四、监控与系统管理的意义
图里新增两个非常重要的子域:
设备监控
包含:
- 实时状态
- 实时诊断
- 实时告警
- 生产计件
这里的关键思想是:
监控也是领域,不是工具
例如产量统计:
Y i e l d = G o o d T o t a l Yield = \frac{Good}{Total} Yield=TotalGood
class ProductionData
{
public:
void AddGood() { good_++; total_++; }
void AddNG() { total_++; }
double Yield() const
{
if (total_ == 0) return 0.0;
return static_cast<double>(good_) / total_;
}
private:
int good_{0};
int total_{0};
};
系统管理
包含:
- 权限
- 日志
- 性能分析
- 产量分析
这些是:
支撑性子域
但仍然独立成领域。
例如权限模型:
class PermissionDefinition
{
public:
void AddPermission(const std::string& name)
{
permissions_.insert(name);
}
bool HasPermission(const std::string& name) const
{
return permissions_.count(name) > 0;
}
private:
std::set<std::string> permissions_;
};
五、这张图真正厉害的地方
它做了三件成熟的事情:
1⃣ 领域切分清晰
每个子域:
- 职责单一
- 边界清晰
- 变化来源单一
2⃣ 依赖方向正确
服务依赖对象
对象不依赖服务
满足:
低耦合 低耦合 低耦合
3⃣ 变化隔离成功
例如:
- 新增器件 → 只影响对应领域对象
- 新增监控指标 → 只影响监控子域
- 新增分析算法 → 只影响系统管理
不会波及自动印刷逻辑。
六、这张图已经达到什么水平?
它已经具备:
- DDD子域划分
- 垂直切片思想
- 行为与模型分离
- 依赖方向控制
- 可平台化结构
再往上走就是:
把器件控制抽象成插件式框架
把领域对象抽象成元模型
把子域抽象成可组合模块
七、最终总结
这张图表达的架构核心可以用一句数学表达式总结:
系统稳定性 ∝ 1 耦合度 系统稳定性 \propto \frac{1}{耦合度} 系统稳定性∝耦合度1
你现在的结构:
- 子域清晰
- 依赖单向
- 模型集中
- 行为隔离
已经非常接近一个:
可演进的工业控制平台架构
控制系统的“领域模型分层 + 垂直切片 + 逻辑物理分离”的架构思想
一、整体结构:三大行为子域 + 五类领域对象
上层:领域服务子域(行为层)
- 维护功能
- 配置
- 初始化
- 日志功能
- 控制功能
- 运行模式
- 自动 / 手动
- 监控功能
- 状态诊断
- 性能
- 告警
这是 行为层(Behavior Layer)
下层:领域对象(模型层)
- 配置数据 / 参数数据 / 日志数据
- 外部命令 / 外部信号
- 逻辑部件 / 逻辑资源
- 物理部件 / 物理资源
- 部件状态
这是 模型层(Model Layer)
右侧:垂直切片
它表达的是:
一个功能的完整闭环,应该从上到下贯穿
这才是你这张图最关键的思想。
二、逻辑部件 vs 物理部件 —— 领域隔离的核心
这是整张图最重要的设计点。
为什么要分逻辑和物理?
现实世界:
- 有电机
- 有气缸
- 有IO点
- 有传感器
但业务不应该直接操作“IO地址”。
我们希望:
业务 ≠ I O 地址 业务 \neq IO地址 业务=IO地址
抽象模型
可以理解为:
逻辑部件 = f ( 物理部件 ) 逻辑部件 = f(物理部件) 逻辑部件=f(物理部件)
逻辑层是对物理层的抽象映射。
示例代码
物理部件(贴近硬件)
// 物理电机(直接控制驱动器)
class PhysicalMotor
{
public:
void SetSpeed(double rpm)
{
// 发送PLC指令或驱动命令
}
void Stop()
{
// 停止电机
}
};
逻辑部件(业务语义)
// 逻辑搬运单元(屏蔽物理实现)
class TransportUnit
{
public:
TransportUnit(PhysicalMotor& motor)
: motor_(motor)
{}
void MoveForward()
{
motor_.SetSpeed(1200); // 业务层不知道PLC地址
}
void Stop()
{
motor_.Stop();
}
private:
PhysicalMotor& motor_;
};
如果将来:
- 电机品牌换了
- 通讯协议换了
- PLC升级了
只需要改 PhysicalMotor。
这就是:
隔离硬件变化 隔离硬件变化 隔离硬件变化
三、控制功能的数学模型
自动 / 手动其实是:
状态机系统
系统状态集合:
S = I d l e , A u t o , M a n u a l , A l a r m S = {Idle, Auto, Manual, Alarm} S=Idle,Auto,Manual,Alarm
状态转移函数:
S n e x t = f ( S c u r r e n t , C o m m a n d ) S_{next} = f(S_{current}, Command) Snext=f(Scurrent,Command)
简化代码示例
enum class RunMode
{
Idle,
Auto,
Manual,
Alarm
};
class Controller
{
public:
void SwitchTo(RunMode mode)
{
mode_ = mode; // 状态切换
}
RunMode Current() const
{
return mode_;
}
private:
RunMode mode_{RunMode::Idle};
};
四、监控功能的设计本质
监控不是“读取变量”。
监控是:
状态函数 + 判定规则 状态函数 + 判定规则 状态函数+判定规则
例如告警触发条件:
A l a r m = ( T e m p e r a t u r e > T m a x ) Alarm = (Temperature > T_{max}) Alarm=(Temperature>Tmax)
示例代码
class ComponentState
{
public:
void SetTemperature(double t)
{
temperature_ = t;
}
bool IsOverheat() const
{
return temperature_ > maxTemp_;
}
private:
double temperature_{0.0};
double maxTemp_{80.0};
};
监控层只是调用:
if (state.IsOverheat())
{
// 触发告警
}
五、垂直切片真正的含义
右侧“垂直切片”代表:
一个完整功能应包含:
行为 + 模型 + 状态 + 资源
例如“搬运功能”完整切片:
- 控制功能(行为)
- 外部命令
- 逻辑搬运单元
- 物理电机
- 状态数据
这叫:
F e a t u r e = B e h a v i o r + M o d e l + R e s o u r c e Feature = Behavior + Model + Resource Feature=Behavior+Model+Resource
六、整个系统的数学抽象
可以抽象成:
S y s t e m = f ( C o n f i g , C o m m a n d , L o g i c , P h y s i c a l , S t a t e ) System = f(Config, Command, Logic, Physical, State) System=f(Config,Command,Logic,Physical,State)
其中:
- Config → 配置数据
- Command → 外部命令
- Logic → 逻辑部件
- Physical → 物理资源
- State → 部件状态
我们希望:
∂ L o g i c ∂ P h y s i c a l → 最小 \frac{\partial Logic}{\partial Physical} \to 最小 ∂Physical∂Logic→最小
即逻辑对物理变化敏感度最小。
七、这张图体现的架构成熟度
这张图已经具备:
- 行为分域
- 模型分层
- 逻辑物理分离
- 状态独立
- 垂直切片思想
再往上升级就是: - 插件式逻辑资源
- 元模型驱动逻辑层
- 流程引擎驱动控制层
八、核心总结
这张图表达的核心架构思想可以用一句话总结:
通过“逻辑-物理分离 + 行为子域划分 + 垂直切片贯通”,把控制系统变成可演进的软件系统,而不是硬件脚本集合。
用数学表达:
系统稳定性 ∝ 1 耦合度 系统稳定性 \propto \frac{1}{耦合度} 系统稳定性∝耦合度1
演进能力 ∝ 抽象层次 演进能力 \propto 抽象层次 演进能力∝抽象层次
你现在这套结构,已经是:
工业控制系统向平台化架构演进的关键一步。
完整SMT印刷系统的例子
SMT 印刷系统
SMT 印刷系统 C++ 示例
基于领域驱动设计(DDD)的完整控制系统
语言:C++17
构建:GCC / Clang
架构:领域驱动设计(DDD) + 分层架构
目标:变化隔离、高内聚低耦合、易扩展
项目简介
这是一个真实可运行的 SMT 印刷机控制系统 C++ 示例,完全对应典型 SMT 印刷工艺的六大领域服务子域与五大核心领域对象。
通过 DDD 原则实现:
- 领域对象(Entity/Value Object)承载状态与不变业务规则
- 领域服务(Domain Service)负责编排复杂行为
- 依赖方向严格单向:服务 → 对象,对象不依赖服务
核心设计目标
✓ 变化来源单向、影响范围局部化
✓ 支持运行时热切换印刷节拍策略
✓ 细粒度权限控制 + 全局日志 + 生产数据统计
文件结构
smt_full/
├── main.cpp # 应用层入口,串联所有子域
├── Makefile
└── domain/
├── objects/ # 领域对象(状态 + 规则)
│ ├── ProcessDefinition.h
│ ├── Devices.h
│ └── DataObjects.h
└── services/ # 领域服务(行为编排)
├── CycleStrategy.h
├── ProcessManagementService.h
├── ManualOperationService.h
├── DeviceControlService.h
├── AutoPrintService.h
├── MonitorService.h
└── SystemManagementService.h
架构对应关系
| 架构图子域 | 代码文件 | 主要领域对象 |
|---|---|---|
| 制程管理 | ProcessManagementService.h | ProcessDefinition |
| 手动操作 | ManualOperationService.h | Devices, PermissionDefinition |
| 自动印刷 | AutoPrintService.h | ProductionData, ICycleStrategy |
| 器件控制 | DeviceControlService.h | TransportDevice / AlignDevice / PrintDevice / WipeDevice |
| 设备监控 | MonitorService.h | ProductionData, LogData |
| 系统管理 | SystemManagementService.h | PermissionDefinition, LogData, ProductionData |
预期输出(模拟日志)
程序将顺序演示完整生产周期:
- [系统管理] 工程师角色登录,权限配置
- [制程管理] 导入 proc_001.json
- [手动操作] 开机 → 添加锡膏 → 自动对准 → 调参
- [自动印刷] AOI 模式,连续生产 5 块 PCB
- [节拍切换] 运行中暂停 → 切换双刮刀策略 → 恢复
- [设备监控] 实时状态、诊断、告警、生产计件
- [手动操作] 人工清洗 → 关机
- [系统管理] 故障/性能/产量分析 + 日志汇总
模块详解
领域对象(核心实体)
| 对象类 | 主要职责 | 关键字段示例 |
|---|---|---|
| ProcessDefinition | 制程步骤与参数定义 | name, version, steps[], params |
| TransportDevice | 搬运轨道 + 夹板状态机 | position, clamped, State{Idle,Moving,…} |
| AlignDevice | 光学定位与纠偏 | captureCount, lastOffset{dx,dy,dtheta} |
| PrintDevice | 印刷头控制与计数 | pressure, speed, bladeDown, printCount |
| WipeDevice | 自动/人工擦拭计数 | wipeCount, cleanCount |
| ProductionData | 产量 & 良率统计 | total, good, ng, ngReasons[] |
| PermissionDefinition | 权限 + 角色 | perms(set), role{Operator,Engineer,Admin} |
| LogData | 分级日志存储 | entries[]{level, module, message} |
领域服务一览
- ProcessManagementService:制程导入/导出/查询
- ManualOperationService:开机/关机/调参/加锡膏/自动校准/人工清洗
- DeviceControlService:底层器件动作执行(搬运、对位、印刷、擦拭)
- AutoPrintService:自动生产核心(模式切换、策略注入、批量运行、暂停恢复)
- MonitorService:状态报告、诊断、告警、生产报表
- SystemManagementService:权限管理、升级、备份、分析(故障/性能/产量)
节拍策略(策略模式)
三种策略实现 ICycleStrategy 接口,支持运行时热切换:
| 策略类 | 说明 | 计算公式 |
|---|---|---|
| NormalCycleStrategy | 普通模式 | T = 搬运 + 定位 + 印刷 + 擦拭 |
| AOICycleStrategy | 含 AOI 检测模式 | T = 搬运 + 定位 + 印刷 + 擦拭 + AOI |
| DualBladeCycleStrategy | 双刮刀高效模式 | T = 搬运 + 定位 + 印刷×0.5 + 擦拭 |
切换示例(零侵入):
// 普通模式
auto s1 = std::make_shared<NormalCycleStrategy>(3.0, 1.5, 4.0, 0.5);
autoPrint.SetStrategy(s1);
// 运行中切换双刮刀
autoPrint.Pause();
auto s2 = std::make_shared<DualBladeCycleStrategy>(3.0, 1.5, 4.0, 0.5);
autoPrint.SetStrategy(s2);
autoPrint.Resume();
权限系统
| 角色 | adjust_param | system_upgrade | 适用场景 |
|---|---|---|---|
| Operator | ✗ | ✗ | 日常生产 |
| Engineer | ✓ | ✗ | 工艺调试、参数调整 |
| Admin | ✓ | ✓ | 系统升级、完整管理 |
设计原则亮点
- 依赖单向:服务依赖对象,对象零服务依赖
- 策略模式 + 依赖注入
- 状态机(TransportDevice)
- 观察者回调(事件通知)
- 权限守卫(每个敏感操作检查)
- 全局分级日志贯穿全系统
扩展指引
- 新增器件:Devices.h 加类 → DeviceControlService 注入 → 加 ExecuteXXX 方法
- 新增策略:继承 ICycleStrategy → main.cpp 中 SetStrategy 切换
- 新增权限:SetupPermissions 加字符串 → 服务方法加 Has() 检查
常见问题 & 调试提示
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 编译报 C++17 错误 | GCC 版本过低 | 升级 GCC 9+ 或加 -std=c++17 |
| 对位 WARN 日志频繁 | 模拟偏差固定 0.03 > threshold | 修改 AlignDevice::Capture() 返回值 |
| 想测试 NG 流程 | 模拟良率 100% | RunOnePCB 中强制 alignOk = false |
| 想丰富 NG 分类 | 当前只有良/NG | AddNG(reason) 传入更多分类字符串 |
https://godbolt.org/z/oheTMr8PM
╔════════════════════════════════════════════╗
║ SMT 印刷系统 —— 完整领域架构 C++ 示例 ║
╚════════════════════════════════════════════╝
══════════ ① 系统管理:权限配置 ══════════
[LOG][INFO ][系统管理] 设置权限角色: 工程师
[系统管理] 权限配置 → 角色: 工程师
adjust_param: ✓
system_upgrade: ✗
══════════ ② 制程管理:导入 ══════════════
[LOG][INFO ][制程管理] 导入制程文件: proc_001.json
[制程管理] 导入成功:
制程名: SMT_PROC_001 v2.3.1
步骤: 搬运 (3s)
步骤: 定位 (1.5s)
步骤: 印刷 (4s)
步骤: 擦拭 (0.5s)
参数: align_threshold = 0.05
参数: print_pressure = 35
参数: print_speed = 80
参数: wipe_interval = 3
══════════ ③ 手动操作:开机准备 ══════════
[LOG][INFO ][手动操作] 系统开机初始化
[手动操作] ▶ 开机处理
检查气压... OK
检查锡膏余量... OK
各轴回零点...
[LOG][INFO ][器件控制] 搬运PCB到 0mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 0 → 0 mm
[搬运器件] 夹板夹紧
开机完成。
[LOG][INFO ][手动操作] 添加锡膏 500g
[手动操作] 添加锡膏 500g,搅拌均匀
[LOG][INFO ][手动操作] 执行自动adjust校准
[手动操作] 自动adjust: 钢网与PCB自动对准校准
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #1,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[手动操作] adjust完成
[LOG][INFO ][手动操作] 调整参数 print_pressure = 38.000000
[手动操作] ✓ 参数调整: print_pressure = 38
[LOG][INFO ][手动操作] 调整参数 print_speed = 75.000000
[手动操作] ✓ 参数调整: print_speed = 75
══════════ ④ 自动印刷:连续生产 ══════════
[LOG][INFO ][自动印刷] 切换运行模式: 连续
[自动印刷] 运行模式: 连续
[LOG][INFO ][自动印刷] 批量启动,共 5 块
[自动印刷] === 批量启动 制程:SMT_PROC_001 数量:5 ===
── PCB #1 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#1
[LOG][INFO ][器件控制] 搬运PCB到 100mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 0 → 100 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #2,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[节拍] AOI模式 搬运3+定位1.5+印刷4+擦拭0.5+AOI2 = 11s
[应用层通知] OK PCB#1
── PCB #2 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#2
[LOG][INFO ][器件控制] 搬运PCB到 200mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 100 → 200 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #3,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[节拍] AOI模式 搬运3+定位1.5+印刷4+擦拭0.5+AOI2 = 11s
[应用层通知] OK PCB#2
── PCB #3 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#3
[LOG][INFO ][器件控制] 搬运PCB到 300mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 200 → 300 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #4,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[LOG][INFO ][器件控制] 触发自动擦拭
[擦拭器件] 自动擦拭钢网底部 #1
[节拍] AOI模式 搬运3+定位1.5+印刷4+擦拭0.5+AOI2 = 11s
[应用层通知] OK PCB#3
── PCB #4 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#4
[LOG][INFO ][器件控制] 搬运PCB到 400mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 300 → 400 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #5,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[节拍] AOI模式 搬运3+定位1.5+印刷4+擦拭0.5+AOI2 = 11s
[应用层通知] OK PCB#4
── PCB #5 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#5
[LOG][INFO ][器件控制] 搬运PCB到 500mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 400 → 500 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #6,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[节拍] AOI模式 搬运3+定位1.5+印刷4+擦拭0.5+AOI2 = 11s
[应用层通知] OK PCB#5
[自动印刷] === 批量结束,累计时间 55s ===
┌─ 产量报告 ─────────────────
│ 总计=5 良品=5 NG=0 良率=100%
└──────────────────────────────
══════════ ⑤ 运行中切换节拍策略 ══════════
[LOG][INFO ][自动印刷] 暂停
[自动印刷] ⏸ 暂停
[LOG][INFO ][自动印刷] 切换节拍策略: 双刮刀模式
[自动印刷] 节拍策略切换为: 双刮刀模式
[LOG][INFO ][自动印刷] 恢复
[自动印刷] ▶ 恢复
[LOG][INFO ][自动印刷] 批量启动,共 3 块
[自动印刷] === 批量启动 制程:SMT_PROC_001 数量:3 ===
── PCB #1 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#1
[LOG][INFO ][器件控制] 搬运PCB到 100mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 500 → 100 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #7,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[节拍] 双刮刀模式 印刷时间减半,T=7s
[应用层通知] OK PCB#1
── PCB #2 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#2
[LOG][INFO ][器件控制] 搬运PCB到 200mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 100 → 200 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #8,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[节拍] 双刮刀模式 印刷时间减半,T=7s
[应用层通知] OK PCB#2
── PCB #3 ──────────────────
[LOG][INFO ][自动印刷] 开始处理 PCB#3
[LOG][INFO ][器件控制] 搬运PCB到 300mm
[搬运器件] 夹板打开
[搬运器件] 轨道移动: 200 → 300 mm
[搬运器件] 夹板夹紧
[LOG][INFO ][器件控制] 执行光学对位
[定位器件] 镜头拍照 #9,计算偏差
[定位器件] 纠偏 dx=0.012 dy=-0.018 dθ=0.08
[LOG][INFO ][器件控制] 执行印刷
[印刷器件] 刮刀下降,压力=38 kPa,速度=75 mm/s
[印刷器件] 锡膏印刷中...
[印刷器件] 刮刀抬起,脱模
[LOG][INFO ][器件控制] 触发自动擦拭
[擦拭器件] 自动擦拭钢网底部 #2
[节拍] 双刮刀模式 印刷时间减半,T=7s
[应用层通知] OK PCB#3
[自动印刷] === 批量结束,累计时间 76s ===
┌─ 产量报告 ─────────────────
│ 总计=8 良品=8 NG=0 良率=100%
└──────────────────────────────
══════════ ⑥ 设备监控 ════════════════════
[设备监控] ── 实时状态 ──────────────────
轨道位置 : 300 mm
夹板状态 : 夹紧
轨道状态 : 已夹紧
擦拭次数 : 2
人工清洗 : 0 次
拍照次数 : 9
最后对位偏差: dx=0.012 dy=-0.018
[设备监控] ── 实时诊断 ──────────────────
✓ 搬运器件: 正常
✓ 定位器件: 正常
✓ 印刷器件: 正常
✓ 擦拭器件: 正常
[设备监控] ── 告警检查 ──────────────────
✓ 无告警
[设备监控] ── 生产计件 ──────────────────
┌─ 产量报告 ─────────────────
│ 总计=8 良品=8 NG=0 良率=100%
└──────────────────────────────
══════════ ⑦ 手动操作:关机 ══════════════
[LOG][INFO ][手动操作] 操作员执行人工清洗
[手动操作] 人工清洗钢网
[LOG][INFO ][器件控制] 操作员触发人工清洗
[擦拭器件] 人工深度清洗,用溶剂擦拭钢网
[LOG][INFO ][手动操作] 系统关机
[手动操作] ■ 关机处理
保存当前制程参数...
各轴退至安全位置...
关机完成。
══════════ ⑧ 系统管理:分析报告 ══════════
[系统管理] ── 故障分析 ──────────────────
无NG记录
[系统管理] ── 性能分析 ──────────────────
总生产时间 : 76 s
平均节拍 : 9.5 s/块
理论UPH : 378 块/时
[系统管理] ── 产量分析 ──────────────────
┌─ 产量报告 ─────────────────
│ 总计=8 良品=8 NG=0 良率=100%
└──────────────────────────────
质量等级 : 优秀
[系统管理] ── 日志汇总 ──────────────────
总日志条数 : 52
INFO 条数 : 52
WARN 条数 : 0
ERROR 条数 : 0
[LOG][INFO ][系统管理] 备份到 /backup/20240101/
[系统管理] 备份数据库和制程库 → /backup/20240101/
── 工程师尝试升级 ──
[LOG][WARN ][系统管理] 升级被拒绝,权限不足
[系统管理] ✗ 升级失败:权限不足
[LOG][INFO ][系统管理] 设置权限角色: 管理员
[系统管理] 权限配置 → 角色: 管理员
adjust_param: ✓
system_upgrade: ✓
[LOG][INFO ][系统管理] 系统升级至 v2.0.0
[系统管理] ▶ 升级至 v2.0.0 ...
下载固件... OK
校验校验和... OK
重启应用... OK
[LOG][INFO ][制程管理] 导出制程: SMT_PROC_001 → proc_001_updated.json
[制程管理] 导出 SMT_PROC_001 v2.3.1 → proc_001_updated.json
╔════════════════════════════════════════════╗
║ 程序正常结束 ║
╚════════════════════════════════════════════╝
例子2 SMT C++ 示例项目 · 基于 DDD 领域驱动设计
基于领域驱动设计(DDD)的 C++ SMT 锡膏印刷机控制系统
项目简介
本项目是一套用于 SMT(Surface Mount Technology,表面贴装技术)生产线的锡膏印刷机控制系统,采用 C++17 编写,遵循领域驱动设计(Domain-Driven Design, DDD) 架构原则,将业务逻辑与基础设施解耦,具备高可维护性与可扩展性。
技术栈
| 类别 | 技术 |
|---|---|
| 语言 | C++17 |
| 构建系统 | CMake 3.16+ |
| 单元测试 | Google Test |
| 日志 | spdlog |
| 序列化 | nlohmann/json |
| 通信协议 | TCP/IP, Modbus RTU |
架构设计
本系统严格遵循 DDD 分层架构:
┌─────────────────────────────────────┐
│ Presentation Layer │ UI / CLI / HMI 接口
├─────────────────────────────────────┤
│ Application Layer │ 应用服务、用例编排
├─────────────────────────────────────┤
│ Domain Layer │ 核心业务逻辑、领域模型
├─────────────────────────────────────┤
│ Infrastructure Layer │ 数据库、硬件驱动、通信
└─────────────────────────────────────┘
核心领域模型
- PrintJob(印刷任务) — 聚合根,管理一次完整印刷作业的生命周期
- SqueegeeController(刮刀控制器) — 负责刮刀压力、速度、角度控制
- StencilManager(钢网管理器) — 钢网对位、清洁周期管理
- PCBAlignment(PCB 对位) — 视觉对位与坐标校正
- PasteMonitor(锡膏监控) — 锡膏量检测与补充提醒
目录结构
smt-printing-system/
├── CMakeLists.txt
├── README.md
├── src/
│ ├── domain/ # 领域层
│ │ ├── entities/ # 实体
│ │ │ ├── PrintJob.h
│ │ │ ├── PrintJob.cpp
│ │ │ └── PCBAlignment.h
│ │ ├── value_objects/ # 值对象
│ │ │ ├── SqueegeeParams.h
│ │ │ └── PrintProfile.h
│ │ ├── repositories/ # 仓储接口
│ │ │ └── IPrintJobRepository.h
│ │ └── services/ # 领域服务
│ │ └── PrintDomainService.h
│ ├── application/ # 应用层
│ │ ├── commands/
│ │ │ └── StartPrintCommand.h
│ │ ├── queries/
│ │ │ └── GetJobStatusQuery.h
│ │ └── services/
│ │ └── PrintApplicationService.cpp
│ ├── infrastructure/ # 基础设施层
│ │ ├── hardware/
│ │ │ ├── SqueegeeDriver.cpp
│ │ │ └── VisionSystem.cpp
│ │ ├── persistence/
│ │ │ └── SqlitePrintJobRepository.cpp
│ │ └── communication/
│ │ └── ModbusClient.cpp
│ └── presentation/ # 表现层
│ ├── cli/
│ │ └── CliHandler.cpp
│ └── hmi/
│ └── HmiInterface.cpp
├── tests/
│ ├── domain/
│ │ └── PrintJobTest.cpp
│ └── application/
│ └── PrintServiceTest.cpp
└── docs/
└── architecture.md
快速开始
启动系统
./smt_printer --config config/machine.json
核心代码示例
领域实体 — PrintJob
// domain/entities/PrintJob.h
// ─── PrintJob — 聚合根 ────────────────────────────────────────────────────────
class PrintJob {
public:
PrintJob(JobId id, PrintProfile profile, PCBSpec pcb);
// 状态机操作
void start();
void pause();
void resume();
void complete();
void abort(const std::string& reason);
// 印刷计数
void incrementPrintCount();
bool needsStencilClean() const;
// Getters
const JobId& id() const { return m_id; }
JobStatus status() const { return m_status; }
const PrintProfile& profile() const { return m_profile; }
const PCBSpec& pcbSpec() const { return m_pcb; }
int printCount() const { return m_print_count; }
const std::vector<DomainEvent>& events() const { return m_events; }
void clearEvents() { m_events.clear(); }
const std::string& abortReason() const { return m_abortReason; }
private:
JobId m_id;
JobStatus m_status;
PrintProfile m_profile;
PCBSpec m_pcb;
std::string m_abortReason;
int m_print_count = 0;
std::vector<DomainEvent> m_events;
};
值对象 — SqueegeeParams
// domain/value_objects/SqueegeeParams.h
struct SqueegeeParams {
float pressure_kg; // 刮刀压力 (kg)
float speed_mm_s; // 印刷速度 (mm/s)
float angle_deg; // 刮刀角度 (°)
bool operator==(const SqueegeeParams& other) const;
};
仓储接口
// domain/repositories/IPrintJobRepository.h
class IPrintJobRepository {
public:
virtual ~IPrintJobRepository() = default;
virtual void save(const PrintJob& job) = 0;
virtual std::optional<PrintJob> findById(const JobId& id) = 0;
virtual std::vector<PrintJob> findByStatus(JobStatus status) = 0;
};
领域驱动设计要点
| DDD 概念 | 本项目对应实现 |
|---|---|
| 聚合根 (Aggregate Root) | PrintJob |
| 值对象 (Value Object) | SqueegeeParams, PrintProfile |
| 领域服务 (Domain Service) | PrintDomainService |
| 仓储 (Repository) | IPrintJobRepository → SqlitePrintJobRepository |
| 领域事件 (Domain Event) | PrintStartedEvent, PrintCompletedEvent |
| 应用服务 (Application Service) | PrintApplicationService |
| 工厂 (Factory) | PrintJobFactory |
配置文件示例
{
"machine": {
"id": "SMT-001",
"model": "DEK Horizon"
},
"squeegee": {
"default_pressure_kg": 8.0,
"default_speed_mm_s": 60.0,
"default_angle_deg": 60.0
},
"vision": {
"camera_resolution": "5MP",
"alignment_tolerance_um": 25
},
"communication": {
"modbus_port": "/dev/ttyUSB0",
"modbus_baud": 9600
}
}
test
https://godbolt.org/z/sxszW4Wxx
CLI 命令测试
https://godbolt.org/z/9zbzo61Pj
电梯控制系统的领域驱动架构(DDD 思想)+ 垂直切片结构。
它不是简单的“模块图”,而是:
- 上层:领域服务子域(Service Subdomain)
- 下层:领域对象(Domain Object)
- 右侧:物理控制资源
- 中间:控制核心逻辑
- 整体:围绕“运行模式”形成一个完整的垂直业务闭环
我分层给你做一次系统级拆解,并穿插代码示例。
一、整体架构理解
图可以分为五大块:
- 维护类服务子域(参数、初始化)
- 控制核心子域(运行模式 + 调度 + 运动控制)
- 监控子域(状态显示、告警)
- 领域对象(队列、信号、指令)
- 物理执行子域(电机、电梯实体)
它符合典型的:
输入 → 规则判断 → 调度 → 执行 → 反馈 → 显示 输入 → 规则判断 → 调度 → 执行 → 反馈 → 显示 输入→规则判断→调度→执行→反馈→显示
这就是一个标准闭环控制系统。
二、运行模式核心 —— 架构中心
图上方有两条运行分类:
- 检修运行 / 对接操作
- 正常运行 / 紧急电动 / 消防运行
这意味着系统存在多模式状态机。
可以抽象为:
M o d e ∈ M a i n t a i n , D o c k i n g , N o r m a l , E m e r g e n c y , F i r e Mode \in {Maintain, Docking, Normal, Emergency, Fire} Mode∈Maintain,Docking,Normal,Emergency,Fire
代码示例:
// 运行模式枚举
enum class RunMode
{
Maintain, // 检修
Docking, // 对接操作
Normal, // 正常运行
Emergency, // 紧急电动
Fire // 消防运行
};
// 当前模式
RunMode currentMode;
// 模式判断
void EvaluateMode(const AdminCommand& cmd)
{
switch (cmd.type)
{
case AdminCommandType::SwitchToMaintain:
currentMode = RunMode::Maintain;
break;
case AdminCommandType::SwitchToFire:
currentMode = RunMode::Fire;
break;
default:
break;
}
}
三、控制核心子域(系统大脑)
中间区域是:
- 运行模式判断
- 运行控制
- 使能禁止控制
- 指令调度
- 轿厢移动控制
本质上是一个三层控制结构:
控制 = 模式决策 + 调度算法 + 运动控制 控制 = 模式决策 + 调度算法 + 运动控制 控制=模式决策+调度算法+运动控制
1⃣ 调度算法(电梯核心)
电梯调度是一个典型的队列调度问题。
上行请求队列 + 下行请求队列。
设:
- Q u p Q_{up} Qup 上行请求
- Q d o w n Q_{down} Qdown 下行请求
- F F F 当前楼层
调度目标:
m i n ∑ ∣ F − F i ∣ min \sum |F - F_i| min∑∣F−Fi∣
代码示例:
class RequestQueue
{
public:
std::set<int> upQueue; // 上行楼层集合
std::set<int> downQueue; // 下行楼层集合
void AddUpRequest(int floor)
{
upQueue.insert(floor);
}
void AddDownRequest(int floor)
{
downQueue.insert(floor);
}
};
2⃣ 运行控制
class ElevatorController
{
public:
void Control()
{
switch (currentMode)
{
case RunMode::Normal:
NormalRun();
break;
case RunMode::Maintain:
MaintainRun();
break;
case RunMode::Fire:
FireRun();
break;
default:
break;
}
}
};
这就是典型的状态机驱动架构。
四、领域对象(业务数据核心)
图中下方是:
- 用户指令
- 管理员指令
- 井道信号
- 控制指令队列
这些不是“模块”,而是:
纯业务数据模型
例如:
struct UserCommand
{
int floor; // 请求楼层
bool isUp; // 上行还是下行
};
struct ShaftSignal
{
bool doorLocked; // 门锁信号
bool limitSwitch; // 限位信号
bool leveling; // 平层信号
};
领域对象不包含控制逻辑。
这就是 DDD 的核心原则。
五、物理资源层(底层执行)
最右侧是:
- 电机控制
- 速度检测
- 超重检测
- 电机
- 轿厢
这是典型的:
控制输出 → P W M / I O → 电机 控制输出 → PWM / IO → 电机 控制输出→PWM/IO→电机
运动学关系:
速度:
v = d x d t v = \frac{dx}{dt} v=dtdx
加速度:
a = d v d t a = \frac{dv}{dt} a=dtdv
控制代码示例:
class Motor
{
public:
void SetSpeed(float speed)
{
// 输出PWM控制电机转速
}
float GetSpeed() const
{
return currentSpeed;
}
private:
float currentSpeed;
};
六、完整控制闭环
整个系统的控制闭环可以抽象为:
输入 ( 用户 / 信号 ) → 状态机 → 调度算法 → 电机控制 → 位置变化 → 信号反馈 输入(用户/信号) → 状态机 → 调度算法 → 电机控制 → 位置变化 → 信号反馈 输入(用户/信号)→状态机→调度算法→电机控制→位置变化→信号反馈
用代码结构表示:
void MainLoop()
{
ReadSignals(); // 采集井道信号
ReadCommands(); // 读取用户/管理员指令
controller.Control(); // 模式判断 + 调度 + 控制
UpdateMotor(); // 输出控制
UpdateDisplay(); // 状态显示
}
七、这是“垂直切片架构”
为什么?
因为:
每一种运行模式是完整链路:
- 输入
- 判断
- 调度
- 控制
- 执行
- 反馈
例如:
消防运行是一个完整切片: - 忽略普通召唤
- 强制回基站
- 锁定楼层
- 限制速度
它不是一个“模块”,而是一个完整业务流。
这就是垂直切片。
八、架构类型判断
从图看,这是:
- MCU / RTOS 架构
- C 或 C++
- 事件驱动 + 状态机
- 分层 DDD 思想
属于嵌入式中等复杂控制系统架构。
九、总结
这张图本质是:
一个基于领域驱动设计的电梯控制系统架构
核心思想:
- 上层:业务逻辑
- 中层:控制算法
- 下层:物理执行
- 数据与行为分离
- 模式驱动控制
- 垂直切片组织业务
它体现的是:
高内聚 + 低耦合 + 可扩展 高内聚 + 低耦合 + 可扩展 高内聚+低耦合+可扩展
更完整、更清晰的“电梯系统垂直切片架构”,相比上一张,它更强调:
- 每一列都是一个完整的业务闭环(垂直切片)
- 每个切片内部包含:领域服务子域 + 领域对象
- 控制核心与物理执行层是一个完整业务流
我按“架构思想 → 控制模型 → 数学模型 → 代码结构”四层给你完整拆解。
一、什么是“垂直切片”
图中每一个大框都标注:
<< 垂直切片 >>
含义是:
每一个切片都是一个“可独立运行的完整业务链”
比如:
1⃣ 维护切片
2⃣ 控制切片
3⃣ 数据与指令切片
4⃣ 显示切片
5⃣ 告警切片
6⃣ 执行机构切片
垂直切片的数学抽象
如果传统分层是:
U I → S e r v i c e → D o m a i n → I n f r a UI → Service → Domain → Infra UI→Service→Domain→Infra
那么垂直切片是:
S l i c e = 输入 + 规则 + 数据 + 执行 Slice = 输入 + 规则 + 数据 + 执行 Slice=输入+规则+数据+执行
每个切片是:
S i = ( I i , R i , D i , O i ) S_i = (I_i, R_i, D_i, O_i) Si=(Ii,Ri,Di,Oi)
- I I I 输入
- R R R 规则
- D D D 数据
- O O O 输出
二、左侧切片:维护子域
包含:
- 参数设置
- 初始化
- 手动检测
- 配置数据
- 参数
- 规则
本质是:
系统运行前的配置层
1⃣ 配置数据模型
// 领域对象:配置规则
struct Rule
{
int maxSpeed; // 最大速度
int maxLoad; // 最大载重
int floorCount; // 楼层数
};
// 领域对象:配置数据
struct ConfigData
{
Rule rule;
int defaultFloor;
};
2⃣ 初始化流程
系统启动过程可抽象为:
I n i t = L o a d ( C o n f i g ) + C h e c k ( H a r d w a r e ) + R e s e t ( S t a t e ) Init = Load(Config) + Check(Hardware) + Reset(State) Init=Load(Config)+Check(Hardware)+Reset(State)
class SystemInitializer
{
public:
void Initialize()
{
LoadConfig(); // 读取配置
CheckHardware(); // 硬件自检
ResetState(); // 状态清零
}
};
三、中间大切片:控制核心(系统大脑)
这是系统的最核心部分。
包含:
- 运行模式判断
- 运行控制
- 调度
- 移动控制
四、运行模式状态机
运行模式集合:
M o d e ∈ M a i n t a i n , D o c k i n g , N o r m a l , E m e r g e n c y , F i r e Mode \in {Maintain, Docking, Normal, Emergency, Fire} Mode∈Maintain,Docking,Normal,Emergency,Fire
状态机实现
enum class RunMode
{
Maintain,
Docking,
Normal,
Emergency,
Fire
};
class ModeManager
{
public:
void SwitchMode(RunMode newMode)
{
currentMode = newMode;
}
RunMode GetMode() const
{
return currentMode;
}
private:
RunMode currentMode = RunMode::Normal;
};
五、调度算法(电梯核心算法)
存在:
- 上行请求队列
- 下行请求队列
目标是最小化移动代价:
C o s t = ∑ ∣ F − F i ∣ Cost = \sum |F - F_i| Cost=∑∣F−Fi∣
其中: - F F F 当前楼层
- F i F_i Fi 请求楼层
队列模型
#include <set>
// 领域对象:请求队列
class RequestQueue
{
public:
void AddUp(int floor)
{
upQueue.insert(floor); // 升序集合
}
void AddDown(int floor)
{
downQueue.insert(floor);
}
int NextUp()
{
return *upQueue.begin(); // 最近上行目标
}
private:
std::set<int> upQueue;
std::set<int> downQueue;
};
六、控制逻辑(运行控制)
控制是:
C o n t r o l = M o d e + S c h e d u l e + M o t i o n Control = Mode + Schedule + Motion Control=Mode+Schedule+Motion
class ElevatorController
{
public:
void Run()
{
switch(modeManager.GetMode())
{
case RunMode::Normal:
HandleNormal();
break;
case RunMode::Maintain:
HandleMaintain();
break;
case RunMode::Fire:
HandleFire();
break;
default:
break;
}
}
private:
ModeManager modeManager;
};
七、运动控制(物理模型)
电机运动遵循经典运动学:
速度:
v = d x d t v = \frac{dx}{dt} v=dtdx
加速度:
a = d v d t a = \frac{dv}{dt} a=dtdv
位移:
x = x 0 + v t + 1 2 a t 2 x = x_0 + vt + \frac{1}{2}at^2 x=x0+vt+21at2
电机控制模型
class Motor
{
public:
void SetAcceleration(float a)
{
acceleration = a;
}
void Update(float dt)
{
// v = v + a * dt
velocity += acceleration * dt;
// x = x + v * dt
position += velocity * dt;
}
float GetPosition() const
{
return position;
}
private:
float position = 0.0f;
float velocity = 0.0f;
float acceleration = 0.0f;
};
八、井道信号与安全约束
井道信号:
- 门锁信号
- 限位信号
- 平层信号
- 限速器信号
安全控制模型:
I f D o o r U n l o c k e d → S t o p If\ DoorUnlocked → Stop If DoorUnlocked→Stop
I f O v e r s p e e d → B r a k e If\ Overspeed → Brake If Overspeed→Brake
class SafetyChecker
{
public:
bool CanMove(const ShaftSignal& s)
{
if(!s.doorLocked)
return false;
if(s.limitSwitch)
return false;
return true;
}
};
九、右侧两个切片:显示与告警
显示:
D i s p l a y = f ( S y s t e m S t a t e ) Display = f(SystemState) Display=f(SystemState)
告警:
A l a r m = g ( S a f e t y C o n d i t i o n ) Alarm = g(SafetyCondition) Alarm=g(SafetyCondition)
class AlarmService
{
public:
void CheckOverload(float weight, float maxLoad)
{
if(weight > maxLoad)
{
TriggerAlarm("超重");
}
}
};
十、最右下切片:执行机构
电机 + 轿厢 = 实体模型
领域对象:
- 电梯
- 电机
- 轿厢
这是 DDD 的 Aggregate。
class Elevator
{
public:
void MoveTo(float targetFloor)
{
// 控制电机实现移动
}
private:
Motor motor;
};
十一、完整闭环模型
整个系统闭环为:
用户输入 → 调度 → 运动控制 → 位置变化 → 传感器反馈 → 再决策 用户输入 → 调度 → 运动控制 → 位置变化 → 传感器反馈 → 再决策 用户输入→调度→运动控制→位置变化→传感器反馈→再决策
伪代码主循环:
void MainLoop()
{
ReadUserCommand(); // 读取召唤
ReadAdminCommand(); // 读取管理员操作
ReadShaftSignal(); // 读取井道信号
controller.Run(); // 模式 + 调度 + 控制
motor.Update(0.01f); // 更新运动学
UpdateDisplay(); // 状态显示
CheckAlarm(); // 告警检查
}
十二、这张图的本质
它体现的是:
✓ DDD 架构
✓ 垂直切片组织方式
✓ 控制系统状态机
✓ 实体与服务分离
✓ 安全闭环控制
它不是简单的“模块图”,而是:
面向业务流的结构图
十三、架构等级判断
这种复杂度一般属于:
- MCU + RTOS
- C / C++
- 中大型嵌入式控制系统
- 电梯 / 工控 / 自动化行业典型结构
完整电梯控制系统项目代码示例(使用 C++17 + 领域驱动设计 DDD + 垂直切片 Vertical Slice 架构)。
项目设计说明(对应 SVG 图的核心思想)
SVG 图展示了典型的垂直切片(Vertical Slice) 结构,而不是传统的水平分层(按技术分层):
- 每个垂直切片(<< 垂直切片 >>)代表一个业务能力/功能区域,内部包含:
- <<领域服务子域>>(行为、协调逻辑)
- <<领域对象>>(实体、值对象、聚合根,承载状态 + 业务规则)
主要切片:
- 参数设置 / 初始化 / 手动检测 切片(配置 + 手动模式)
- 运行模式判断 / 运行控制 / 使能禁止 / 指令调度 / 轿厢移动 切片(核心运行逻辑)
- 队列管理 切片(上行/下行请求、控制指令、井道信号)
- 用户/管理员指令 切片
- 状态显示 / 状态告警 / 状态查询 切片(监控)
- 电机控制 / 速度检测 / 超重检测 切片(物理层抽象)
项目采用垂直切片 + DDD:
- 每个切片是一个文件夹(或命名空间),包含自己的服务 + 实体
- 切片之间通过接口/事件松耦合(避免大泥球)
- 核心聚合根:
Elevator(轿厢)、Motor等
项目结构(垂直切片风格)
elevator-control-ddd/
├── CMakeLists.txt
├── main.cpp
└── domain/
├── common/ # 共享值对象、接口
│ ├── Direction.h
│ ├── Floor.h
│ ├── Request.h
│ └── Queue.h
├── config_slice/ # 左侧第一个切片:参数 & 手动
│ ├── ConfigService.h/cpp
│ └── ConfigAggregate.h
├── run_slice/ # 核心运行切片
│ ├── RunModeService.h/cpp
│ ├── DispatchService.h/cpp
│ └── ElevatorAggregate.h # 核心聚合根
├── queue_slice/ # 队列管理切片
│ ├── RequestQueueService.h/cpp
│ └── RequestQueues.h
├── command_slice/ # 用户/管理员指令
│ ├── CommandService.h/cpp
└── monitor_slice/ # 状态显示/告警
├── MonitorService.h/cpp
└── ElevatorStatus.h
核心代码实现(简化但完整可编译)
很抱歉,在这个纯文本对话环境中,我无法直接生成并提供可下载的 .zip 文件(没有文件系统上传/下载能力)。
但我可以给你两种实用方案,让你快速得到完整的项目文件夹 + zip:
方案1:最快方式(推荐)—— 复制下面内容到本地,一键创建项目 + zip
- 在你的电脑上新建一个空文件夹,例如叫
elevator-control-ddd - 在里面新建所有文件(用 VS Code / Notepad++ / 任何编辑器)
- 把下面我提供的完整文件内容逐个复制进去
- 完成后右键文件夹 → 发送到 → 压缩(zipped)文件夹 即可得到
elevator-control-ddd.zip
下面是所有文件的完整内容(已精简但可编译通过,包含 CMakeLists.txt):
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(elevator-control-ddd LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 收集所有源文件
file(GLOB_RECURSE SOURCES
"domain/*.cpp"
"*.cpp"
)
add_executable(elevator ${SOURCES})
target_include_directories(elevator PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/)
# 如果有 pthread(Linux/macOS)
if(UNIX)
target_link_libraries(elevator PRIVATE pthread)
endif()
main.cpp
#include "domain/run_slice/RunModeService.h"
#include "domain/monitor_slice/MonitorService.h"
#include "domain/run_slice/ElevatorAggregate.h"
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
int main() {
auto elevator = std::make_shared<ElevatorAggregate>(1, 1, 20);
auto runService = std::make_shared<RunModeService>(elevator);
auto monitor = std::make_shared<MonitorService>(elevator);
std::cout << "=== 电梯控制系统启动 (DDD + Vertical Slice) ===\n\n";
// 模拟一些操作
runService->HandleHallRequest({5, Direction::Up});
runService->HandleHallRequest({3, Direction::Down});
runService->HandleCarRequest(12);
std::cout << "开始模拟运行 20 个周期...\n\n";
for (int i = 1; i <= 20; ++i) {
runService->Tick();
std::cout << "[周期 " << i << "] ";
monitor->DisplayStatus();
monitor->CheckAlarms();
std::this_thread::sleep_for(std::chrono::milliseconds(600));
}
std::cout << "\n模拟结束。欢迎扩展真实硬件接口!\n";
return 0;
}
domain/common/Direction.h
#pragma once
enum class Direction { Up, Down, Idle };
domain/common/Floor.h
#pragma once
#include <cstdint>
using FloorNumber = std::int16_t;
constexpr FloorNumber INVALID_FLOOR = -1;
domain/common/Request.h
#pragma once
#include "Direction.h"
#include "Floor.h"
struct HallRequest {
FloorNumber floor;
Direction direction;
};
struct CarRequest {
FloorNumber targetFloor;
};
domain/common/Queue.h
#pragma once
#include "Request.h"
#include <deque>
#include <algorithm>
template<typename T>
class SimpleQueue {
public:
void push(const T& item) { queue_.push_back(item); }
bool pop(T& out) {
if (queue_.empty()) return false;
out = queue_.front();
queue_.pop_front();
return true;
}
bool empty() const { return queue_.empty(); }
size_t size() const { return queue_.size(); }
private:
std::deque<T> queue_;
};
domain/run_slice/ElevatorAggregate.h
#pragma once
#include "../common/Direction.h"
#include "../common/Floor.h"
#include <vector>
#include <iostream>
class ElevatorAggregate {
public:
ElevatorAggregate(FloorNumber current = 1, FloorNumber minF = 1, FloorNumber maxF = 20)
: currentFloor_(current),
minFloor_(minF),
maxFloor_(maxF),
direction_(Direction::Idle),
doorOpen_(false),
overload_(false) {}
bool CanMoveTo(FloorNumber target) const;
void MoveStep();
void OpenDoor() {
doorOpen_ = true;
std::cout << "[电梯] 门打开\n";
}
void CloseDoor() {
doorOpen_ = false;
std::cout << "[电梯] 门关闭\n";
}
void SetOverload(bool ov) { overload_ = ov; }
FloorNumber CurrentFloor() const { return currentFloor_; }
Direction CurrentDirection() const { return direction_; }
bool IsDoorOpen() const { return doorOpen_; }
bool IsOverloaded() const { return overload_; }
void SetDirection(Direction Dir) { direction_ = Dir; }
private:
FloorNumber currentFloor_, minFloor_, maxFloor_;
Direction direction_;
bool doorOpen_;
bool overload_;
};
domain/run_slice/ElevatorAggregate.cpp
#include "ElevatorAggregate.h"
bool ElevatorAggregate::CanMoveTo(FloorNumber target) const {
if (overload_) return false;
return target >= minFloor_ && target <= maxFloor_;
}
void ElevatorAggregate::MoveStep() {
if (direction_ == Direction::Up && currentFloor_ < maxFloor_) {
++currentFloor_;
std::cout << "[电梯] ↑ 移动到 " << currentFloor_ << "\n";
} else if (direction_ == Direction::Down && currentFloor_ > minFloor_) {
--currentFloor_;
std::cout << "[电梯] ↓ 移动到 " << currentFloor_ << "\n";
}
}
domain/run_slice/RunModeService.h
#pragma once
#include "ElevatorAggregate.h"
#include "../common/Request.h"
#include <memory>
enum class RunMode { Normal, Inspection, Fire, Emergency };
class RunModeService {
public:
explicit RunModeService(std::shared_ptr<ElevatorAggregate> elev);
void SwitchMode(RunMode mode);
void HandleHallRequest(const HallRequest& req);
void HandleCarRequest(FloorNumber target);
void Tick(); // 主循环每周期调用
private:
std::shared_ptr<ElevatorAggregate> elevator_;
RunMode currentMode_ = RunMode::Normal;
};
domain/run_slice/RunModeService.cpp
#include "RunModeService.h"
#include <iostream>
RunModeService::RunModeService(std::shared_ptr<ElevatorAggregate> elev) : elevator_(elev) {}
void RunModeService::SwitchMode(RunMode mode) {
currentMode_ = mode;
std::cout << "[运行模式] 切换到 ";
switch (mode) {
case RunMode::Normal:
std::cout << "正常模式\n";
break;
case RunMode::Inspection:
std::cout << "检修模式\n";
break;
case RunMode::Fire:
std::cout << "消防模式\n";
break;
default:
std::cout << "其他模式\n";
}
}
void RunModeService::HandleHallRequest(const HallRequest& req) {
std::cout << "[请求] 厅呼 " << req.floor << (req.direction == Direction::Up ? " ↑" : " ↓")
<< "\n";
// 简单逻辑:直接设置方向
if (req.direction == Direction::Up && elevator_->CurrentFloor() < req.floor) {
elevator_->SetDirection(Direction::Up); // 简化,实际应入队列
} else if (req.direction == Direction::Down && elevator_->CurrentFloor() > req.floor) {
elevator_->SetDirection(Direction::Down);
}
}
void RunModeService::HandleCarRequest(FloorNumber target) {
std::cout << "[请求] 轿厢内目标 " << target << "\n";
if (elevator_->CanMoveTo(target)) {
// 简化:直接朝向目标
elevator_->SetDirection((target > elevator_->CurrentFloor()) ? Direction::Up
: Direction::Down);
}
}
void RunModeService::Tick() {
if (currentMode_ == RunMode::Normal && !elevator_->IsOverloaded()) {
elevator_->MoveStep();
if (elevator_->IsDoorOpen())
elevator_->CloseDoor();
else // if (/* 到达目标 */)
elevator_->OpenDoor();
}
}
domain/monitor_slice/MonitorService.h
#pragma once
#include "../run_slice/ElevatorAggregate.h"
#include <iostream>
class MonitorService {
public:
explicit MonitorService(std::shared_ptr<ElevatorAggregate> elev) : elevator_(elev) {}
void DisplayStatus() const;
void CheckAlarms() const;
private:
std::shared_ptr<ElevatorAggregate> elevator_;
};
domain/monitor_slice/MonitorService.cpp
#include "MonitorService.h"
void MonitorService::DisplayStatus() const {
std::cout << "楼层:" << elevator_->CurrentFloor()
<< " 方向:" << (elevator_->CurrentDirection() == Direction::Up ? "↑" :
elevator_->CurrentDirection() == Direction::Down ? "↓" : "-")
<< " 门:" << (elevator_->IsDoorOpen() ? "开" : "关")
<< " 超重:" << (elevator_->IsOverloaded() ? "是" : "否") << " ";
}
void MonitorService::CheckAlarms() const {
if (elevator_->IsOverloaded()) {
std::cout << "【告警】超重!停止运行\n";
}
}
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)