一、产品化成就头部企业 —— 核心逻辑理解

你这套资料本质是在表达一个核心思想:

通过“分层控制平台 + 多型号覆盖 + 典型场景适配”,形成完整产品矩阵,从而实现规模化与头部地位。

产品分级体系


型号 定位 说明
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} ω=LvRvL
    其中:
  • 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} Iphase3 ×Ibus
    比如:
    母线 35A
    35 × 1.732 ≈ 60 A 35 \times 1.732 \approx 60A 35×1.73260A
    而表中相电流 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增加
  • 通信接口增加
  • 控制轴数增加
    向下:
  • 成本下降
  • 功能减少
  • 面向单一场景

九、产品化成功的本质

真正的“头部企业”不是靠单一爆款,而是:

  1. 全功率段覆盖
  2. 全车型覆盖
  3. 全场景覆盖
  4. 控制算法平台统一
  5. 模块化设计
  6. 规模化生产

十、技术总结

这份资料核心表达的是:

一套从小功率两轮车控制器,到工业AGV控制系统的完整产品体系
技术维度包括:

  • 电机驱动技术
  • 功率电子设计
  • 运动学建模
  • 安全控制
  • 系统集成
  • 平台化架构

杭州海康威视数字技术有限公司 为主线,系统性解读其“做器件 → 做整机 → 做系统 → 做平台 → 做总包”的升级路径。

一个典型的 技术纵向整合 + 平台化 + 生态化 演进模型。

一、总体战略路径理解

企业能力升级路径可以抽象为:

核心算法 → 硬件产品 → 系统解决方案 → 平台架构 → 行业生态 → 城市级底座

本质上是三次“控制权升级”:

  1. 控制技术
  2. 控制产品
  3. 控制数据与系统架构

二、阶段一:技术与器件起步期(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 + 云 + 城市级底座

九、核心启示

这条路径说明:

  1. 技术起家是基础
  2. 产品规模化是跳板
  3. AI是转折点
  4. 平台化是长期壁垒
  5. 城市级底座是终极形态
    本质上,这是从“硬件利润”升级为“系统价值”的过程。

安川电机 的事业变迁路径,并用“做器件 → 做整机 → 做系统 → 做总包”的战略模型来理解。

这是一条比海康更“工业底层”的升级路线,本质是:

电机技术 → 控制技术 → 机电一体化 → 工业机器人 → 行业解决方案

一、总体战略主线

可以抽象为:

电动机 → 电机系统 → 控制器 → 伺服系统 → 机电一体化 → 工业机器人 → 行业总包

这是典型的“工业底层技术向上整合”。

二、创立期:电动机时代(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)+Kie(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) θ=f1(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 ChangeArchitecture 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 FlexibilityFaster 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 PerformanceCPUFrequency×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 StrategyArchitectureProduct 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 PerformanceHardwareCapacity
    例如:
    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=1nTaski
    必须满足:
    T o t a l L o a d ≤ C P U M a x TotalLoad \leq CPU_{Max} TotalLoadCPUMax

三、中间层:接口需求

接口需求处在中间,是架构的关键。
图中包括:

  • 通信协议
  • 软控接口
  • 界面原型
  • 菜单导航
  • 界面转换流
    这是“系统耦合边界”。
    可以表示为:
    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 CouplingInterfaceComplexity

通信协议细化

例如:

  • 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=1nWeighti×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} ChangeCostArchitectureFlexibility1
架构越灵活,变更成本越低。

九、本质理解总结

这张图表达的是:

需求变化并不是简单功能增加,而是三维变化:

  1. 部署变化
  2. 硬件变化
  3. 功能变化
    而产品化架构必须承载:
    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),重点在于:

如何让系统在“变化到来”时,能够在时间和预算约束下完成修改、测试和部署。

Modifiability Changes Arrive Changes Made, Tested, and Deployed Within Time and Budget Localize Changes Semantic Coherence Anticipate Expected Changes Generalize Module Limit Possible Options Abstract Common Services Prevention of Ripple Effect Hide Information Maintain Existing Interface Restrict Communication Paths Use an Intermediary Defer Binding Time Runtime Registration Configuration Files Polymorphism Component Replacement Adherence to Defined Protocols

这套思想来自经典架构著作 Software Architecture in Practice
下面我会:

  1. 先解释“绑定时间”相关战术
  2. 再整体解读图中的“可修改性结构”
  3. 用代码示例说明每个战术
  4. 给出形式化理解(含公式)

一、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 CallSiteRuntimeDispatch
也就是说:
方法调用的目标在运行时决定。

示例

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 SystemAProtocolSystemB
    无需耦合实现。

二、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 RippleEffect0
战术:

  • 信息隐藏
  • 保持接口不变
  • 限制通信路径
  • 使用中介者

示例:接口稳定

// 稳定接口
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 PerformanceComplexity
这是典型权衡。

三、架构层面的数学化理解

假设:

  • B B B = 绑定时间
  • C C C = 修改成本
  • F F F = 灵活性
    通常:
    F ∝ B F \propto B FB
    C ∝ 1 B C \propto \frac{1}{B} CB1
    但:
    O v e r h e a d ∝ B Overhead \propto B OverheadB
    因此架构设计本质是:
    Optimize F − O v e r h e a d \text{Optimize} \quad F - Overhead OptimizeFOverhead

四、总结对照表


战术 绑定时间 灵活性 性能
配置文件 启动时
多态 运行时
运行时注册 运行时 很高 较低
组件替换 加载时
协议绑定 运行时 很高 取决网络

五、本质思想

可修改性不是“方便改代码”,而是:

控制变化的传播范围 + 延迟决策时间 + 降低变更成本
可以抽象为:
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=BusinessLogicTechnicalInfrastructure

示例代码(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⃣ 分层架构

  • 思想:按照职责划分系统层次,例如:
    1. 表现层(UI/接口)
    2. 应用层(业务逻辑)
    3. 领域层(核心业务模型)
    4. 基础设施层(数据库、硬件接口、网络)

好处:

  • 屏蔽层间变化
  • 局部改动不影响其他层

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. 隔离硬件变化

  1. 屏蔽式封装(Encapsulation)
  • 将硬件细节封装到模块内
  • 对外只提供统一接口
class MotorDriver {
public:
    void setSpeed(int rpm); // 屏蔽具体电机类型
private:
    void writePWM(int pwmValue);
};
  1. 增强式封装
  • 除了屏蔽,还增加功能(如安全检查、校验)
  • 例如封装硬件接口同时提供异常保护
void MotorDriver::setSpeed(int rpm) {
    if (rpm < 0 || rpm > maxRpm) throw std::runtime_error("RPM超限");
    writePWM(rpm * pwmFactor);
}

B. 隔离领域变化

  1. 领域服务(Domain Service)
  • 提供跨对象、跨聚合的业务操作
  • 保持领域模型纯净
class PaymentService {
public:
    void transfer(Account& from, Account& to, double amount);
};
  1. 领域模型(Domain Model)
  • 核心业务对象及规则
  • 稳定,尽量不被技术变化污染
  1. 功能聚合(Feature Aggregation)
  • 将相关功能组合成模块/聚合
  • 便于复用和变更管理
  1. 垂直切片(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)

切片本意就是把整体“切开成片”,在软件中指:

  • 按某种维度将系统拆分成可独立理解和开发的模块
  • 目的:便于开发、测试、部署或复用
    类型
  1. 水平切片(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 垂直切片对比


维度 水平切片 垂直切片
切分方向 技术层 功能/业务
组成部分 单层模块 端到端完整功能
优点 便于技术专精 独立可交付,快速验证业务价值
缺点 需要跨层集成才能运行 可能重复技术实现(需复用基础设施)

在产品化架构中的作用

  1. 垂直切片
  • 每个产品功能就是一个可独立交付单元
  • 方便 需求变化管理
  • 与“功能聚合”/“模块化”结合
  1. 水平切片
  • 保持技术一致性和可复用性
  • 避免业务逻辑被硬件、基础设施污染

总结

  • 切片 = 将系统拆分成可管理的部分
  • 垂直切片 = 按业务功能拆分,从前端到后端完整覆盖,端到端可运行
  • 垂直切片是敏捷、模块化和可修改性架构的核心思想之一
    如果你需要,我可以画一张 水平切片 vs 垂直切片的图,用图形直观表示各层之间的差异,让概念更清晰。
    你希望我画吗?

C. 框架化/平台化

  1. 提炼Framework(抽象框架)
  • 将可复用模式提炼成框架
  • 如消息处理框架、任务调度框架
  1. 框架中引入元模型(Metamodel)
  • 用抽象模型描述框架内部结构
  • 支持生成/配置不同产品变体
// 示例:元模型描述任务
struct TaskMeta {
    std::string name;
    int priority;
    bool isPeriodic;
};
  • 产品化时,通过元模型生成具体任务实例
  • 避免重复实现,提高可修改性

三、总结逻辑

整体可以抽象成如下公式化关系:

  1. 变化隔离原则
    Δ H a r d w a r e ∩ Δ B u s i n e s s = ∅ \Delta_{Hardware} \cap \Delta_{Business} = \varnothing ΔHardwareΔBusiness=
  • 硬件变更不影响业务逻辑
  • 业务变更不影响基础设施
  1. 功能切片化
    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=1nFeatureSlicei
  2. 可复用框架
    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
  3. 变化传播最小化
    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 顶部 烧录程序、调试、串口通信 便于开发和调试
电机 + 螺旋桨 四角 产生升力与姿态控制 双叶螺旋桨,微型无刷电机

三、硬件架构理解

可抽象为分层结构

  1. 控制层(Control Layer)
    • Cortex-M3 主处理器
    • PID 控制器或四轴姿态控制算法
  2. 感知层(Sensing Layer)
    • 三轴加速度计
    • 三轴陀螺仪(XY + Z)
  3. 通信层(Communication Layer)
    • 2.4GHz 无线模块
    • 数据传输、遥控信号接收
  4. 执行层(Actuation Layer)
    • 四个电机 + 螺旋桨
    • 将控制指令转化为实际运动
  5. 调试与开发接口(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);
};
  • 垂直切片示例
    • 每个功能模块(姿态控制、无线通信)是一个垂直切片
    • 包含硬件接口 + 控制逻辑 + 数据处理

六、设计特点总结

  1. 模块化
    • 处理器、传感器、通信、执行器分明
  2. 可扩展
    • 后续升级传感器或处理器无需改变整体布局
  3. 可调试
    • JTAG/Serial 接口保证固件调试可行
  4. 轻量化
    • PCB 尺寸极小,电机布置优化
  5. 开源/可替换
    • Crazyflie 硬件、固件开源
    • 支持社区自定义模块

总结公式化理解

  1. 系统功能抽象:
    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
  2. 数据流:
    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 SensorDataFusion ControlAlgorithmPWM MotorsMotion FlightDynamics
  3. 模块化原则:
    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} ChangeImpactModuleIsolation1
  • 硬件变化隔离良好 → 软件稳定
  • 软件升级不依赖硬件微改动

一、嵌入式软件架构大类别解析

表格内容主要从 架构模型、操作系统、硬件、编程语言、代码量 五个维度对比三类嵌入式/软硬件架构。

项目 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)+jISRj(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=iTaskiPriorityi+jISRj

示例代码(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,ThreadkProcessi

示例代码(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

四、总结理解

  1. 架构选择 → 与硬件资源、实时性、任务复杂度相关
  2. RTOS 提供多任务调度,提高可扩展性和实时性
  3. 多进程 + 多线程适合复杂嵌入式系统或工控平台
  4. 代码量随系统复杂度显著增加

嵌入式系统选择架构时,需权衡 硬件成本、任务复杂性、实时性需求、软件可维护性

功能与技术分离架构图

Init层 或叫 Program Entry层 FUNC 显示服务 通信服务 存储服务 HAL Driver Lib Common

一、整体概念

功能与技术分离(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);

三、层次关系总结

Init层 / Program Entry层

FUNC功能层

显示服务

通信服务

存储服务

HAL硬件抽象层

Driver驱动层

Lib基础库

Common通用模块

说明

  • 功能层 只依赖 HAL/Driver,不直接操作硬件
  • 技术层(HAL/Driver/Lib)独立,可替换硬件或库实现
  • Common 提供跨层复用,避免重复实现

四、架构优点

  1. 可维护性高:业务逻辑与硬件驱动分离
  2. 可移植性强:更换 MCU 或操作系统时,仅改 HAL/Driver 层
  3. 可扩展性好:新增功能服务,不影响硬件层
  4. 测试友好:可单独对功能层、HAL、Driver 进行单元测试

隔离硬件变化架构图

子系统 modules hal drivers comm.c 负责 配置 crtp.c 实现收发服务 crtp.h 中的函数指针结构体 《cf 1.0》 nrf24link.c 《cf 1.0》 eskylink.c 《cf 2.0》 radiolink.c 《cf 1.0》 nrf24l01.c 《cf 2.0》 uart_syslink.c 增强式封装 屏蔽式封装 负责配置

一、核心概念

隔离硬件变化(Hardware Variation Isolation)是一种软件设计策略:

  • 目的:屏蔽上层功能与不同硬件实现的差异,提高可移植性和可维护性。
  • 核心手段:
    1. 屏蔽式封装(Shielded Encapsulation)
      • 对硬件接口进行封装,隐藏底层细节
      • 上层只调用统一接口
    2. 增强式封装(Enhanced 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.ccrtp.c 复用部分模块

说明:上层 Modules 不直接依赖底层实现,通过 HAL 接口完成硬件隔离,支持不同版本无线硬件替换而不影响业务逻辑。

五、架构总结

  1. 模块化层次清晰
    • Modules → HAL → Drivers
    • 高层业务逻辑与底层硬件完全解耦
  2. 硬件变化隔离
    • 更换无线芯片、MCU 或总线方式时,只需修改 HAL/Driver
  3. 封装策略
    • 屏蔽式封装:隐藏接口变化
    • 增强式封装:提供额外功能和安全接口

六、示意伪代码结构

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.c
  • hal 层:nrf24link.c / eskylink.c / radiolink.c
  • drivers 层: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 的焊接固化
  • 关键步骤
    1. 预热(Preheat)
    2. 恒温段(Soak)
    3. 回流段(Reflow)
    4. 冷却段(Cooling)
  • 注意点
    • 温度曲线需匹配元件特性
    • 避免焊接缺陷(虚焊、锡球、翘件)

6⃣ AOI(Automated Optical Inspection,自动光学检测)

  • 功能:检测焊接后的缺陷,如:
    • 缺件(Missing)
    • 偏移(Misalignment)
    • 锡桥(Solder Bridge)
    • 立碑(Tombstoning)
    • 虚焊(Cold Joint)
  • 技术特点
    • CCD摄像头 + 图像处理算法
    • 实时反馈,可剔除或标记问题板

7⃣ 人工目检(Manual Inspection)

  • 功能:人工复核 AOI 检测或补充细节
  • 重要性
    • 对小缺陷或光学难以检测的异常进行确认
    • 最终保证出厂 PCB 质量

三、流程总结与特性

  1. 自动化程度高:Loader、SPI、贴片、回流焊、AOI
  2. 质量控制多级:SPI → AOI → 人工
  3. 闭环反馈
    • SPI 或 AOI 检测出缺陷时,可以停止生产或报警
  4. 效率与精度
    • 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)

五、注意点

  • 锡膏印刷质量:直接影响回流焊焊点
  • 贴片精度:影响焊接可靠性
  • 温度曲线匹配:避免元件损伤
  • 多级检测:减少返工与报废率
    嵌软架构大类别
    功能与技术分离

基础设施层

适配层

应用层

展现层 / 适配层

基础设施层

OS鼠标/屏幕管理

OS网卡驱动

OnCmd()

Update()

OnCmd()

Send()

总线/串口

DBMS接口

OS File接口

DmService()

Notify()

DmService()

Notify()

RPC / Pub/Sub

RPC / Pub/Sub

Notify()

领域层

领域块 E

领域服务层 SA/PI

领域模型层

领域块 D

领域服务层 SA/PI

领域模型层

领域块 C

领域服务层 SA/PI

领域模型层

领域层

领域块 B

领域服务层 SA/PI

领域模型层

领域块 A

领域服务层 SA/PI

领域模型层

用户

远程GUI

UI框架

Socket框架

UI

通信协议

适配层

应用层 CmdProcess()

组件化后
其他组件

DRV/HAL封装

DAO

FileIO

三方库

DB访问框架

File/Log框架

硬件

DB、IMDB

File、Log

该系统从上到下共分为以下五个主要层级:

  • 基础设施层 (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⃣ 关键设计原则

  1. 功能与技术分离
    • 业务逻辑在领域层,技术细节封装在适配层/基础设施层。
  2. 分层架构
    • 清晰分离 UI、应用、领域、适配、底层。
  3. 模块化与组件化
    • 支持 RPC / Pub/Sub,实现松耦合。
  4. 可扩展性和可维护性
    • 增加新功能只改领域层或新增领域块
    • 底层技术升级无需修改领域逻辑

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⃣ 隔离领域变化的设计思想

  1. 服务层封装操作逻辑
    • 所有变化都通过服务接口暴露,内部可以自由调整实现
  2. 对象层封装数据状态
    • 新增对象或修改对象属性,不会破坏其他子域逻辑
  3. 接口调用隔离
    • 上层调用者只依赖接口,不依赖内部实现
  4. 示意代码
void RunProcess(ControlService& ctrl, ProcessDefinition& process) {
    ctrl.TotalControl();
    ctrl.MoveComponent();
    ctrl.PositionComponent();
    ctrl.PrintComponent();
    ctrl.WipeComponent();
}

如果未来“印刷”流程需要升级,只修改 PrintComponent() 内部实现,上层和其他流程不受影响。

5⃣ 图中视觉意义

  • 粉色块:领域服务子域
  • 蓝色块:领域对象
  • 白色矩形/多边形:具体操作或属性
  • 排布方式:服务在上、对象在下 → “服务驱动对象”

核心理念:服务定义行为,对象保存状态,通过分层和接口实现领域变化隔离。

总结

  1. **领域服务(Domain Service)**负责封装操作和流程逻辑
  2. **领域对象(Domain Object)**负责封装数据和状态
  3. 服务调用对象 → 上层调用接口,不关心内部实现
  4. 变化隔离:服务内部逻辑变化或对象属性增加不会破坏整体系统
  5. 代码映射
领域服务子域 (Service)  <-->  控制功能 / 维护功能
领域对象 (Object)       <-->  制程定义 / 搬运器件 / 定位器件 / 印刷器件 / 擦拭器件

这就是经典的 领域驱动设计(DDD) + 隔离领域变化 的实现方法。

如何通过“领域建模 + 子域划分 + 器件抽象”来隔离领域变化

« 领域服务子域 » 制程管理 导入 导出 « 领域服务子域 » 手动操作 开机处理 关机处理 手动调参 添加锡膏 自动adjust 人工清洗 « 领域服务子域 » 自动印刷 节拍 制程总控 « 领域服务子域 » 器件控制 搬运 定位 印刷 擦拭 « 领域对象 » 制程定义 制程定义 « 领域对象 » 搬运器件 轨道 夹板 « 领域对象 » 定位器件 光学镜头 « 领域对象 » 印刷器件 印刷头 钢网 « 领域对象 » 擦拭器件

一、什么是“隔离领域变化”

软件复杂的根源不是代码,而是:

业务在不断变化
在你这个印刷设备系统中,变化来源主要有:

  • 新增一种器件
  • 更换硬件品牌
  • 新增自动流程
  • 修改节拍计算方式
  • 新增一种人工操作
  • 改变控制策略
    如果变化直接影响所有模块,那系统会迅速失控。
    所以核心目标是:
    变化影响范围 → 局部化 变化影响范围 \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)

十、总结成一句话

你这张图本质在表达:

通过“领域对象抽象 + 子域分离 + 器件封装 + 策略解耦”,把变化源拆开,让变化不扩散。

完整的领域分层 + 子域划分 + 对象依赖结构图

<< 领域服务子域 >> 制程管理 导入 导出 << 领域服务子域 >> 手动操作 开机处理 关机处理 手动调参 添加锡膏 自动adjust 人工清洗 << 领域服务子域 >> 自动印刷 运行模式 暂停 节拍 制程总控 << 领域服务子域 >> 器件控制 搬运 定位 印刷 擦拭 << 领域服务子域 >> 设备监控 实时状态 实时诊断 实时告警 生产计件 << 领域服务子域 >> 系统管理 权限管理 升级/备份 日志管理 故障分析 性能分析 产量分析 << 领域对象 >> 制程定义 制程定义 << 领域对象 >> 搬运器件 轨道 夹板 << 领域对象 >> 定位器件 光学镜头 << 领域对象 >> 印刷器件 印刷头 << 领域对象 >> 擦拭器件 钢网 << 领域对象 >> 权限定义 日志数据 产量数据

用“领域子域”组织行为,用“领域对象”承载状态,用依赖箭头表达变化传导方向。

一、整体结构理解

图可以抽象成三层结构:

第一层:领域服务子域(上半部分)

  • 制程管理
  • 手动操作
  • 自动印刷
  • 器件控制
  • 设备监控
  • 系统管理
    这是 行为组织层
    它负责:
  • 编排流程
  • 协调对象
  • 定义业务动作
    但它不应该包含:
  • 具体硬件实现
  • 数据库存取细节

第二层:领域对象(下半部分蓝色)

  • 制程定义
  • 搬运器件
  • 定位器件
  • 印刷器件
  • 擦拭器件
  • 权限定义
  • 日志数据
  • 产量数据
    这是 业务模型层
    它负责:
  • 承载状态
  • 定义规则
  • 约束业务

第三层:依赖箭头(蓝色箭线)

箭头从“领域服务”指向“领域对象”。
这表达一个非常重要的原则:
领域服务 → 依赖 → 领域对象 领域服务 \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 DS=0
    但:
    ∂ D ∂ S = 0 \frac{\partial D}{\partial S} = 0 SD=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=1nTi
    其中:
  • 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
你现在的结构:

  • 子域清晰
  • 依赖单向
  • 模型集中
  • 行为隔离
    已经非常接近一个:

可演进的工业控制平台架构

控制系统的“领域模型分层 + 垂直切片 + 逻辑物理分离”的架构思想

« 领域服务子域 » 维护功能 配置、初始化、日志功能 « 领域服务子域 » 控制功能 运行模式、自动手动 « 领域服务子域 » 监控功能 状态诊断、性能、告警 « 领域对象 » 配置数据 参数数据 日志数据 « 领域对象 » 外部命令 外部信号 « 领域对象 » 逻辑部件 逻辑资源 « 领域对象 » 物理部件 物理资源 « 领域对象 » 部件状态 « 垂直切片 »

一、整体结构:三大行为子域 + 五类领域对象

上层:领域服务子域(行为层)

  1. 维护功能
    • 配置
    • 初始化
    • 日志功能
  2. 控制功能
    • 运行模式
    • 自动 / 手动
  3. 监控功能
    • 状态诊断
    • 性能
    • 告警
      这是 行为层(Behavior Layer)

下层:领域对象(模型层)

  1. 配置数据 / 参数数据 / 日志数据
  2. 外部命令 / 外部信号
  3. 逻辑部件 / 逻辑资源
  4. 物理部件 / 物理资源
  5. 部件状态
    这是 模型层(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 最小 PhysicalLogic最小
    即逻辑对物理变化敏感度最小。

七、这张图体现的架构成熟度

这张图已经具备:

  • 行为分域
  • 模型分层
  • 逻辑物理分离
  • 状态独立
  • 垂直切片思想
    再往上升级就是:
  • 插件式逻辑资源
  • 元模型驱动逻辑层
  • 流程引擎驱动控制层

八、核心总结

这张图表达的核心架构思想可以用一句话总结:

通过“逻辑-物理分离 + 行为子域划分 + 垂直切片贯通”,把控制系统变成可演进的软件系统,而不是硬件脚本集合。
用数学表达:
系统稳定性 ∝ 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

预期输出(模拟日志)

程序将顺序演示完整生产周期:

  1. [系统管理] 工程师角色登录,权限配置
  2. [制程管理] 导入 proc_001.json
  3. [手动操作] 开机 → 添加锡膏 → 自动对准 → 调参
  4. [自动印刷] AOI 模式,连续生产 5 块 PCB
  5. [节拍切换] 运行中暂停 → 切换双刮刀策略 → 恢复
  6. [设备监控] 实时状态、诊断、告警、生产计件
  7. [手动操作] 人工清洗 → 关机
  8. [系统管理] 故障/性能/产量分析 + 日志汇总

模块详解

领域对象(核心实体)


对象类 主要职责 关键字段示例
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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 00 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 0100 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 100200 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 200300 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 300400 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 400500 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 500100 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 100200 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
  [搬运器件] 夹板打开
  [搬运器件] 轨道移动: 200300 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) IPrintJobRepositorySqlitePrintJobRepository
领域事件 (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)
  • 右侧:物理控制资源
  • 中间:控制核心逻辑
  • 整体:围绕“运行模式”形成一个完整的垂直业务闭环
    我分层给你做一次系统级拆解,并穿插代码示例。

一、整体架构理解

图可以分为五大块:

  1. 维护类服务子域(参数、初始化)
  2. 控制核心子域(运行模式 + 调度 + 运动控制)
  3. 监控子域(状态显示、告警)
  4. 领域对象(队列、信号、指令)
  5. 物理执行子域(电机、电梯实体)
    它符合典型的:
    输入 → 规则判断 → 调度 → 执行 → 反馈 → 显示 输入 → 规则判断 → 调度 → 执行 → 反馈 → 显示 输入规则判断调度执行反馈显示
    这就是一个标准闭环控制系统。

二、运行模式核心 —— 架构中心

图上方有两条运行分类:

  • 检修运行 / 对接操作
  • 正常运行 / 紧急电动 / 消防运行
    这意味着系统存在多模式状态机
    可以抽象为:
    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} ModeMaintain,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| minFFi
    代码示例:
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 UIServiceDomainInfra
那么垂直切片是:
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} ModeMaintain,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=FFi
    其中:
  • 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 DoorUnlockedStop
    I f   O v e r s p e e d → B r a k e If\ Overspeed → Brake If OverspeedBrake
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) 结构,而不是传统的水平分层(按技术分层):

  • 每个垂直切片(<< 垂直切片 >>)代表一个业务能力/功能区域,内部包含:
    • <<领域服务子域>>(行为、协调逻辑)
    • <<领域对象>>(实体、值对象、聚合根,承载状态 + 业务规则)
      主要切片:
  1. 参数设置 / 初始化 / 手动检测 切片(配置 + 手动模式)
  2. 运行模式判断 / 运行控制 / 使能禁止 / 指令调度 / 轿厢移动 切片(核心运行逻辑)
  3. 队列管理 切片(上行/下行请求、控制指令、井道信号)
  4. 用户/管理员指令 切片
  5. 状态显示 / 状态告警 / 状态查询 切片(监控)
  6. 电机控制 / 速度检测 / 超重检测 切片(物理层抽象)
    项目采用垂直切片 + 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

  1. 在你的电脑上新建一个空文件夹,例如叫 elevator-control-ddd
  2. 在里面新建所有文件(用 VS Code / Notepad++ / 任何编辑器)
  3. 把下面我提供的完整文件内容逐个复制进去
  4. 完成后右键文件夹 → 发送到 → 压缩(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";
    }
}

https://godbolt.org/z/nzvWhnv3o

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐