C++运动控制系统:分层架构与工厂模式实践总结(望大家看完后,有想法可以提出批评指正)
一,设计思路
设计思路通过分层抽象和工厂模式构建“硬件与软件结合” 的架构,目标是实现 “更换运动控制硬件时,下层软件无需修改”
物料搬运实现 ← 最上层:实现具体的物料搬运业务流程(如 “取料→转移→
│ 放料” 的循环逻辑),只关心 “做什么”,不关心 “硬件如何做”
▼ , 只依赖HAL 完全不接触底层运动卡的具体实现
硬件抽象层 ← 抽象层,提供统一的硬件操作接口,HAL 的接口和逻辑
│ 轴映射是 “与硬件无关的抽象”,更换运动卡时,只需确保
▼ 新卡的物理轴能通过映射对应到逻辑轴,HAL 的代码无需改动
运动卡管理类 ← 实例管理层,管理类只认 “运动卡基类” 的接口,无论新增 /
│ 更换哪种卡(只要继承基类),管理类的 “管理逻辑”
▼ (如批量初始化、切换卡)都无需改变
运动卡基类 ← 抽象接口层,被具体运动卡实现继承
│
┌────────────────┐
▼ ▼
运动卡A实现 运动卡B实现 ← 具体实现层,并列关系,继承自基类
└────────┬───── ─┘
│
▼
运动卡工厂类 ← 实例创建层,负责创建运动卡A/B的实例(返回基类指针)
|
运动 实现 |
MaterialHandlingSystem |
物料搬运流程(取料 / 传递 / 放料) |
依赖HardwareAbstraction的接口 |
|
硬件 抽象 |
HardwareAbstraction |
逻辑轴→物理轴转换,屏蔽硬件差异 |
依赖CMotionControllerManager |
|
卡管 理层 |
CMotionControllerManager |
管理多个控制器(创建 / 切换 / 初始化) |
依赖CMotionControllerBase及其派生类 |
|
控制 器层 |
派生类(固高,研控等) |
具体硬件的运动控制(固高 / 研控 / 模拟) |
继承CMotionControllerBase接口 |
|
工厂层 |
CMotionControllerFactory |
创建控制器实例,统一管理生命周期 |
依赖所有控制器派生类 |
二,设计过程
我们已经有运动控制管理器、工厂类和基类,现在需要实现一个物料搬运系统,使用状态机和任务队列的混合方案。
由于我们已经有管理类,我们可以直接使用它来管理多个控制卡。但是注意,题目要求使用一个卡完成控制,但切换卡后另一张也能支持控制。
因此,我们需要构建一个硬件抽象层,将具体的轴映射到逻辑轴,这样无论底层使用哪个控制卡,我们都可以用相同的逻辑轴编号来控制。
步骤:
设计硬件抽象层,将逻辑轴映射到物理轴(控制卡号+轴号)。
设计物料搬运系统,使用状态机和任务队列。
在对话框中集成物料搬运系统,并处理控制卡切换。

物料搬运系统
├── 运动控制器管理层 (MotionControllerManager)
│ ├── 左臂控制卡 (卡0)
│ └── 右臂控制卡 (卡0)
├── 状态机引擎
│ ├── 取料状态
│ ├── 传递状态
│ └── 放料状态
└── 运动执行层
├── 6个核心运动函数
└── 同步协调机制
三,使用方法,各层解释
状态机是一种 “用状态描述行为、用规则控制转换” 的模型,通过抽象复杂逻辑,让系统行为更可控、可维护。它的核心思想是:“在什么状态下,发生什么事件,会转到什么新状态,以及执行什么动作
状态(State):对象在某一时刻的稳定形态,是行为的基础。 例如:电梯的 “停止”“上升”“下降”;交通信号灯的 “红灯”“黄灯”“绿灯”。(注意:状态是 “有限的”—— 数量固定且可枚举)0
1事件(Event):触发状态转换的外部或内部条件。例如:电梯收到 “5 楼按钮信号”(外部事件);交通信号灯的 “30 秒计时结束”(内部事件)。
转换(Transition):当某个事件发生时,从当前状态切换到另一个状态的过程。例如:交通信号灯 “绿灯” 时,若 “30 秒计时结束”(事件),则转换到 “黄灯”(新状态)。
动作(Action):状态转换时或处于某状态时执行的操作。例如:电梯从 “停止” 转换到 “上升” 时,启动电机(动作);处于 “红灯” 状态时,禁止车辆通行(持续动作)。
任务队列模式是一种用于管理和调度任务执行的设计模式,核心思想是将待执行的任务(工作单元)存储在一个 “队列” 中,由专门的 “消费者” 按顺序或规则从队列中取出任务并执行,从而实现任务的异步处理、解耦生产者与消费者、平衡系统负载。
任务(Task):需要执行的具体工作单元,可包含执行逻辑、参数、优先级等信息(例如 “打印文件”“发送邮件”“控制机械臂移动到坐标 X”)。
任务队列(Queue):存储任务的数据结构(通常是先进先出 FIFO,也可支持优先级排序),作为生产者和消费者之间的 “缓冲层”。
生产者(Producer):创建任务并将其加入队列的角色(例如用户点击 “打印” 按钮、传感器触发 “采集数据” 指令)。
消费者(Consumer):从队列中取出任务并执行的角色(可以是单个线程、多个线程池,甚至分布式节点)。
工作流程
生产任务:生产者根据业务需求创建任务(如解析用户请求、检测到设备状态变化),并将任务添加到队列中。
存储任务:队列按规则(如 FIFO、优先级)暂存任务,等待被消费。
消费任务:消费者持续从队列中获取任务(通常是 “取一个执行一个” 的循环),执行任务逻辑(如调用函数、运行脚本、控制硬件)。
完成 / 反馈:任务执行完成后,可能返回结果(如执行状态、输出数据),或通过回调、日志等方式通知生产者。
硬件抽象层和运动卡管理类有什么区别
|
维度 |
硬件抽象层(HAL) |
运动卡管理类 |
|
范围 |
覆盖所有硬件(运动卡、传感器、执行器等) |
仅针对运动卡一种硬件 |
|
核心目标 |
统一接口,屏蔽不同硬件的差异 |
封装特定运动卡的操作,实现其功能 |
|
抽象程度 |
高(只定义 “做什么”,不涉及硬件细节) |
低(需了解运动卡的协议、驱动细节) |
|
依赖关系 |
依赖底层硬件的管理类(包括运动卡管理类) |
依赖具体运动卡的驱动或协议 |
HardwareAbstraction(硬件抽象层)的完整实现,是连接上层业务逻辑(如物料搬运系统)与底层运动控制器的 “桥梁”。它通过逻辑轴与物理轴的映射、统一的硬件操作接口和错误处理
构造函数:绑定运动控制器管理器
HardwareAbstraction::HardwareAbstraction(CMotionControllerManager& manager)
: m_motionManager(manager){}
功能:通过初始化列表绑定 m_motionManager(运动控制器管理器的引用),获取底层运动控制器的管理能力。
设计意义:采用 “依赖注入” 模式,HAL 不直接创建控制器,而是依赖外部传入的管理器,确保层间解耦
MaterialHandlingSystem(物料搬运系统)
定位:最上层,实现业务逻辑(取料、传递、放料流程)。
职责:通过控制逻辑轴的运动,完成物料搬运的完整流程(多轴协同、阶段切换、超时检测等)。
依赖:通过构造函数接收HardwareAbstraction的引用(m_hardware),依赖其提供的轴操作接口。
CMotionControllerManager(运动控制器管理器)
定位:最底层,直接管理各类运动控制器(固高、研控、模拟)。
职责:创建 / 删除控制器、初始化 / 关闭控制器、切换当前控制器、统一执行急停 / 使能等操作。
持有对象:内部维护多个CMotionControllerBase(运动控制器基类)的派生实例(CGTSMotionController、CYKMotionController等)。
数据的传递(引用+成员变量)
初始化时的关联
在CmachinemotionDlg(对话框类)中,三者的创建顺序是:CMotionControllerManager m_motionManager(先创建)→HardwareAbstraction m_hardware(m_motionManager)(通过构造函数传入管理器引用)→MaterialHandlingSystem m_materialSystem(m_hardware)(通过构造函数传入硬件抽象层引用)。
最终形成:m_materialSystem → m_hardware → m_motionManager的传递
运行时的数据传递
上层(物料系统)需要控制轴运动时,通过m_hardware调用硬件抽象层的方法(MoveAxis等),传递 “逻辑轴号” 和 “目标位置”。
硬件抽象层将 “逻辑轴号” 转换为 “物理轴号” 后,通过m_motionManager获取当前控制器(GetCurrentController),调用控制器的具体运动方法(如AbsoluteMove)。
控制器执行后,状态(如当前位置、运动是否完成)通过硬件抽象层回传给物料系统(如GetAxisPosition、IsMotionComplete)。
核心函数(按层级划分)
1. CMotionControllerManager(底层控制器管理)
|
CreateController |
创建指定类型的控制器(固高 / 研控 / 模拟),存入内部容器m_controllers。 |
|
InitializeSystem |
初始化所有已创建的控制器(调用每个控制器的Initialize)。 |
|
GetCurrentController |
获取当前激活的控制器实例(m_pCurrentController),供上层调用。 |
|
EmergencyStopAll |
对所有控制器执行急停(调用每个控制器的EmergencyStop)。 |
|
SwitchController |
切换当前控制器(用于多卡场景)。 |
2. HardwareAbstraction(中层硬件抽象)
|
MoveAxis |
移动单个逻辑轴(转换为物理轴后调用控制器的AbsoluteMove)。 |
|
MoveAxes |
同时移动多个逻辑轴(批量转换后调用控制器的运动方法)。 |
|
GetAxisPosition |
获取逻辑轴当前位置(转换为物理轴后调用控制器的GetCurrentPosition)。 |
|
LogicalToPhysical |
逻辑轴→物理轴映射(核心转换逻辑,如左 Y 轴 0→物理轴 0)。 |
|
WaitForMotionComplete |
等待逻辑轴运动完成(轮询控制器的IsMotionComplete)。 |
3. MaterialHandlingSystem(上层业务逻辑)
|
StartProcess |
启动物料搬运流程(创建工作线程WorkingThread)。 |
|
RunProcess |
流程主循环(按状态机切换取料 / 传递 / 放料阶段)。 |
|
ExecuteLoadPhase/ExecuteTransferPhase/ExecuteUnloadPhase |
取料 / 传递 / 放料阶段的具体实现(调用硬件抽象层控制轴运动)。 |
|
EmergencyStop |
紧急停止(调用硬件抽象层的EmergencyStop,终止所有运动)。 |
卡关系
CMotionControllerFactory、CGTSMotionController、CYKMotionController)是运动控制器的核心实现,通过 “工厂模式 + 多态” 实现了不同硬件控制卡(固高、研控)的统一管理和操作
一、类之间的关系:工厂模式 + 继承多态
三者通过 “基类抽象 + 派生类实现 + 工厂创建” 的模式协作,核心是 “屏蔽硬件差异,提供统一接口”。
工厂模式(Factory )是一种创建型设计模式,核心作用是封装对象的创建过程,通过专门的 “工厂类” 来负责对象的实例化,而不是让上层代码直接使用new关键字创建对象。
核心结构
1.产品接口定义所有具体产品的通用接口(抽象类或纯虚类),规定了产品必须实现的方法。如:运动控制器的基类CMotionControllerBase,定义了Initialize、AbsoluteMove等接口。
2.具体产品实现产品接口的具体类,对应不同的 “产品实例”。例如:CGTSMotionController(固高控制器)、CYKMotionController(研控控制器),它们都继承自CMotionControllerBase。
3.工厂类提供创建产品的方法(静态),根据输入参数(如类型)创建对应的具体产品实例,并返回产品接口的指针。例如:CMotionControllerFactory,通过CreateController方法根据MotionControllerType创建固高或研控控制器。
1. 继承关系:基类定义接口,派生类实现具体硬件逻辑
基类CMotionControllerBase(未直接提供代码,但从上下文可知):定义了所有运动控制器的通用接口(纯虚函数),如Initialize(初始化)、AbsoluteMove(绝对运动)、EnableAxis(使能轴)等,是所有具体控制器的 “标准模板”。
派生类CGTSMotionController和CYKMotionController:分别继承自CMotionControllerBase,实现了基类的所有纯虚函数,针对 “固高控制卡” 和 “研控控制卡” 的硬件特性编写具体逻辑(如固高的GT_XXX函数、研控的YK_XXX函数)。例如:CGTSMotionController::AbsoluteMove实现固高卡的绝对运动,CYKMotionController::AbsoluteMove实现研控卡的绝对运动,但对外接口完全一致。
2. 依赖关系:工厂类负责创建具体控制器实例
CMotionControllerFactory(工厂类):依赖CGTSMotionController和CYKMotionController,通过CreateController方法根据类型(MotionControllerType)创建对应的控制器实例,并返回基类指针(CMotionControllerBase*)。作用:封装控制器的创建过程,上层代码无需直接new具体控制器,只需调用工厂方法,降低耦合。
从创建到操作的完整步骤
- 运动卡管理类通过调用工厂的CreateController方法,传入控制器类型和卡号,获取基类指针
管理类使用
CMotionControllerBase* pController = CMotionControllerFactory::CreateController(MC_GTS, 0); // 创建固高卡(卡号0)
工厂内部创建
case MC_GTS:
return new CGTSMotionController(cardNo); // 传入卡号0
- 初始化控制器
通过基类指针调用Initialize方法,初始化硬件(不同控制器的初始化逻辑不同,但接口一致)
管理器中的使用
pController->Initialize();
- 操作控制器(使能轴、运动控制等)
通过基类指针调用通用接口,实际执行的是派生类的具体实现(多态特性)
轴使能
pController->EnableAxis(0);
轴运动
pController->AbsoluteMove(0, 50.0);
- 查询状态(如当前位置、是否使能)
通过基类指针获取控制器状态,内部由派生类实现具体的查询逻辑
// 获取轴0当前位置
double pos = pController->GetCurrentPosition(0);
// 检查轴0是否使能
BOOL isEnabled = pController->IsAxisEnabled(0);
- 销毁控制器
通过工厂的DestroyController方法释放资源(避免上层直接调用delete,统一管理)
CMotionControllerFactory::DestroyController(pController);
四,整体框架解释
以 “物料系统执行取料阶段,控制左 Y 轴(逻辑轴 0)移动到取料位置” 为例
1:运动系统发起流程
MaterialHandlingSystem通过工作线程启动取料流程,调用ExecuteLoadPhase:
// MaterialHandlingSystem::StartProcess() 启动线程
CWinThread* pThread = AfxBeginThread(WorkingThread, this);
// 线程函数调用RunProcess,根据状态执行取料阶段
void MaterialHandlingSystem::RunProcess() {
if (m_currentState == SystemState::LOADING) {
ExecuteLoadPhase(); // 执行取料
}}
然后在ExecuteLoadPhase中,调用硬件抽象层中的MoveAxis,传递轴号和位置
if (!m_hardware.MoveAxis(0, m_systemPositions.load_left_y))
return FALSE;
2:抽象层转换轴号并调用管理器
HardwareAbstraction::MoveAxis接收逻辑轴号,转换为物理轴号,再通过管理器获取当前控制器:
BOOL HardwareAbstraction::MoveAxis(int logical_axis, double position) {
// 1. 逻辑轴→物理轴转换(核心:解耦业务与硬件轴号)
int physical_axis = LogicalToPhysical(logical_axis);
// 例如:逻辑轴0(左Y轴)→ 物理轴m_axisMapping.left_y_axis(如0)
// 2. 通过管理器获取当前激活的控制器
CMotionControllerBase* pController = m_motionManager.GetCurrentController();
if (!pController) return FALSE;
// 3. 调用控制器的绝对运动方法(传递物理轴和位置)
return pController->AbsoluteMove(physical_axis, position); }
具体转换逻辑
int HardwareAbstraction::LogicalToPhysical(int logical_axis) {
switch (logical_axis) {
case 0: return m_axisMapping.left_y_axis; // 左Y轴→物理轴(如0)
case 1: return m_axisMapping.left_z_axis; // 左Z轴→物理轴(如1)
}}
3:管理层提供控制器实例
CMotionControllerManager管理多个控制器,GetCurrentController返回当前激活的控制器:
CMotionControllerBase* CMotionControllerManager::GetCurrentController() {
return m_pCurrentController; // 指向当前选中的控制器(如固高卡)}
控制器的创建由管理器通过工厂完成(初始化时)
BOOL CMotionControllerManager::CreateController(MotionControllerType type, int cardNo) {
// 调用工厂创建控制器实例
tionControllerBase*pController =CMotionControllerFactory::CreateController(type, cardNo);
if (pController) {
m_controllers[cardNo] = pController; // 存入map管理
m_pCurrentController = pController; // 设置为当前控制器
return TRUE;
}}
4:控制器层执行硬件操作
CGTSMotionController实现AbsoluteMove,执行固高卡的绝对运动逻辑:
BOOL CGTSMotionController::AbsoluteMove(int axis, double position)
5:状态回传(从控制器到业务层)
物料系统等待运动完成,调用WaitForMotionComplete,逐层查询状态:
// MaterialHandlingSystem::WaitForMotionCompletewhile (true) {
for (int axis : axes) {
if (!m_hardware.IsMotionComplete(axis)) { // 调用抽象层
all_complete = FALSE; break;
}
}
if (all_complete) return TRUE;
// ...超时检测}
抽象层查询控制器状态:
BOOL HardwareAbstraction::IsMotionComplete(int logical_axis) {
int physical_axis = LogicalToPhysical(logical_axis);
auto pController = m_motionManager.GetCurrentController();
return pController ? pController->IsMotionComplete(physical_axis) : FALSE;}
控制器返回运动状态(以固高为例)
BOOL CGTSMotionController::IsMotionComplete(int axis)
五,总结
数据传递:
运动系统(逻辑轴、位置)→ 抽象层(转物理轴)→ 管理器(获取控制器)→ 控制器(执行硬件操作)→ 状态沿原路返回。
核心函数:
运动系统层:StartProcess/ExecuteLoadPhase(发起流程)。
抽象层:MoveAxis/LogicalToPhysical(转换与转发)。
管理层:CreateController/GetCurrentController(管理实例)。
控制器:AbsoluteMove/IsMotionComplete(硬件操作与状态查询)。
简单工厂模式:控制器创建的“统一入口”
应用:多控制器派生类的实例化(固高、研控、模拟)
核心:上层(管理层)无需直接new具体控制器,只需调用工厂方法并传入类型,新增控制器时仅需修改工厂类,符合“开闭原则”
体现:CMotionControllerFactory::CreateController()通过switch-case匹配类型,返回对应实例
多态:实现的具体差异
应用:不同控制器的统一调用(如AbsoluteMove)
核心:管理层通过CMotionControllerBase*指针调用接口,实际执行派生类的具体实现,无需区分硬件类型
体现:pController->AbsoluteMove(...),指针指向固高则执行CGTS的实现,指向研控则执行CYK的实现
分层思想:分层控制,方便管理
每层仅关注自身职责,修改某一层不影响其他层:
修改物料搬运流程(业务层),无需改动硬件操作代码
新增运动控制卡品牌(控制器层),仅需新增派生类并扩展工厂,上层无感知
调整逻辑轴映射规则(抽象层),业务层无需修改调用方式
维护使用:
可扩展性:新增控制卡时,仅需:① 新增继承基类的派生类;② 在工厂类中添加case分支,上层代码无改动
可维护性:硬件相关代码集中在控制器层,业务逻辑集中在业务层,定位问题时可快速锁定层级
实用性:通过模拟控制器(CSimulationMotionController)支持离线调试,降低硬件依赖,提升开发效率
六,待优化和思考问题
待优化与思考
1.目前工厂类为简单工厂,新增控制器需修改工厂代码,可升级为工厂方法模式进一步优化
2.线程安全:工作线程与UI线程的交互未做同步处理,实际应用中需通过临界区或信号量优化
3.错误处理:可引入日志系统,将m_lastOperation的操作记录持久化,便于问题追溯
更多推荐


所有评论(0)