21 KiB
5axis-xyzbc-trt-sim 执行过程分析
本文分析当前 LinuxCNC 源码树中 5axis-xyzbc-trt-sim 对应的仿真配置。实际入口配置文件为:
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
该配置是一个 axis GUI + basic_sim.tcl 仿真 HAL + xyzbc-trt-kins 可切换运动学 + PyVCP/halui 控制面板 + Vismach 三维模型的五轴转台仿真。
1. 启动入口
如果从项目中已有快捷方式启动,入口文件是:
linuxcnc-rtcp-5axis-shortcuts/table-rotary-tilting/xyzbc-trt.desktop
其中的执行命令为:
/home/mes123456/linuxcnc-master/scripts/rip-environment linuxcnc /home/mes123456/linuxcnc-master/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
rip-environment 负责设置 RIP 开发环境,然后调用 linuxcnc 脚本并传入 xyzbc-trt.ini。
scripts/linuxcnc 会读取 INI 中的关键段:
[TASK] TASK = milltask[HAL] HALUI = halui[HAL] HALFILE = LIB:basic_sim.tcl[DISPLAY] DISPLAY = axis[KINS] KINEMATICS = xyzbc-trt-kins sparm=identityfirst[TRAJ] COORDINATES = XYZBC
总体启动顺序如下:
- 启动
linuxcncsvr,创建和持有 NML 通道。 - 启动 realtime/HAL。
- 加载
tpmod、homemod。 - 启动
[TASK] TASK = milltask。 - 启动
[HAL] HALUI = halui。 - 执行
[HAL] HALFILE = LIB:basic_sim.tcl。 - 执行 INI 中所有
[HAL] HALCMD = ...。 - 执行
halcmd start,启动实时线程。 - 启动
[DISPLAY] DISPLAY = axis。
2. INI 核心配置
目标配置文件:
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
关键内容:
[EMC]
MACHINE = sim-xyzbc-trt-kins (switchkins)
[DISPLAY]
GEOMETRY = XYZB
OPEN_FILE = ./demos/xyzbc_switchkins.ngc
PYVCP = ./xyzbc-trt.xml
JOG_AXES = XYZC
DISPLAY = axis
[RS274NGC]
SUBROUTINE_PATH = ./remap_subs
HAL_PIN_VARS = 1
REMAP = M428 modalgroup=10 ngc=428remap
REMAP = M429 modalgroup=10 ngc=429remap
REMAP = M430 modalgroup=10 ngc=430remap
PARAMETER_FILE = xyzbc.var
[KINS]
KINEMATICS = xyzbc-trt-kins sparm=identityfirst
JOINTS = 5
[TRAJ]
COORDINATES = XYZBC
LINEAR_UNITS = mm
ANGULAR_UNITS = deg
COORDINATES = XYZBC 表示该配置使用 5 个坐标字母,对应 5 个 joint:
joint.0-> Xjoint.1-> Yjoint.2-> Zjoint.3-> Bjoint.4-> C
sparm=identityfirst 是本配置的重要细节。它改变了 xyzbc-trt-kins 的默认类型顺序,使启动时的 switchkins-type 0 是 identity kinematics。
3. basic_sim.tcl 建立仿真 HAL
INI 中:
[HAL]
HALFILE = LIB:basic_sim.tcl
对应文件:
lib/hallib/basic_sim.tcl
lib/hallib/sim_lib.tcl
basic_sim.tcl 读取 INI 中的坐标、joint 数、servo period,然后调用 core_sim。
core_sim 位于 lib/hallib/sim_lib.tcl,主要完成:
- 调用
setup_kins加载[KINS] KINEMATICS指定的运动学模块。 - 加载
motmod。 - 将
motion-command-handler和motion-controller加入servo-thread。 - 为每个 joint 创建
pid。 - 为每个 joint 创建
mux2。 - 将
joint.N.motor-pos-cmd经由pid、mux2接回joint.N.motor-pos-fb,形成理想伺服仿真闭环。 - 加载仿真回零、仿真主轴、手动换刀等用户态/实时组件。
运行后可生成等效 HAL 文件:
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt_cmds.hal
其中关键 HAL 命令为:
loadrt xyzbc-trt-kins sparm=identityfirst
loadrt motmod base_period_nsec=0 servo_period_nsec=1000000 num_joints=5
loadrt pid names=J0_pid,J1_pid,J2_pid,J3_pid,J4_pid
loadrt mux2 names=J0_mux,J1_mux,J2_mux,J3_mux,J4_mux
addf motion-command-handler servo-thread
addf motion-controller servo-thread
每个 joint 的典型连接形态为:
net J0:pos-cmd joint.0.motor-pos-cmd => J0_pid.command
net J0:on-pos J0_pid.output => J0_mux.in1
net J0:pos-fb J0_mux.out => joint.0.motor-pos-fb
这说明该仿真没有真实驱动器,反馈位置由仿真 HAL 直接产生。
4. switchkins 初始化
运动学源码入口:
src/emc/kinematics/xyzbc-trt-kins.c
src/emc/kinematics/switchkins.c
src/emc/kinematics/trtfuncs.c
xyzbc-trt-kins.c 的 switchkinsSetup() 根据 sparm 配置三套运动学函数。
因为 INI 使用:
KINEMATICS = xyzbc-trt-kins sparm=identityfirst
所以实际类型顺序为:
switchkins-type 0:identity kinematicsswitchkins-type 1:xyzbc TRT kinematicsswitchkins-type 2:userk kinematics
如果没有 identityfirst,默认 type 0 才是 xyzbc-trt-kins。
switchkins.c 提供统一包装函数:
kinematicsForward()kinematicsInverse()kinematicsSwitch()kinematicsSwitchable()
并创建状态 HAL pin:
kinstype.is-0kinstype.is-1kinstype.is-2
启动时 switchkins_type = 0,随后调用 kinematicsSwitch(0),因此本配置启动后处于 identity kinematics。
5. xyzbc TRT 几何参数
trtKinematicsSetup() 位于:
src/emc/kinematics/trtfuncs.c
它为 TRT 运动学创建 HAL 输入:
xyzbc-trt-kins.x-rot-pointxyzbc-trt-kins.y-rot-pointxyzbc-trt-kins.z-rot-pointxyzbc-trt-kins.x-offsetxyzbc-trt-kins.y-offsetxyzbc-trt-kins.z-offsetxyzbc-trt-kins.tool-offsetxyzbc-trt-kins.conventional-directions
INI 中对这些参数的设置和连接为:
net :tool-offset motion.tooloffset.z
net :tool-offset xyzbc-trt-kins.tool-offset xyzbc-trt-gui.tool-offset
net :x-offset xyzbc-trt-kins.x-offset xyzbc-trt-gui.x-offset
net :z-offset xyzbc-trt-kins.z-offset xyzbc-trt-gui.z-offset
sets :x-offset -20
sets :z-offset -15
setp xyzbc-trt-kins.x-rot-point 0
setp xyzbc-trt-kins.y-rot-point 0
setp xyzbc-trt-kins.z-rot-point 0
setp xyzbc-trt-kins.conventional-directions 0
因此实际计算中:
dx = x-offset = -20
dz = z-offset + tool-offset
tool-offset 来自 motion.tooloffset.z,也就是 LinuxCNC 当前刀长补偿的 Z 分量。
6. xyzbc 正/逆运动学
xyzbcKinematicsForward() 位于:
src/emc/kinematics/trtfuncs.c
它从 joint 坐标计算当前笛卡尔位姿:
joints[X/Y/Z/B/C] -> EmcPose XYZBC
xyzbcKinematicsInverse() 从程序位姿计算 joint 目标:
EmcPose XYZBC -> joints[X/Y/Z/B/C]
关键输入包括:
- X/Y/Z/B/C 指令位姿
- B 轴角度
- C 轴角度
x-offsetz-offsettool-offset- 旋转中心
x/y/z-rot-point conventional-directions
在 coordinated motion 中,motion 控制循环调用当前运动学的 inverse,将轨迹规划器输出的笛卡尔目标位置转换为 joint 目标。
路径为:
trajectory planner
-> emcmotStatus->carte_pos_cmd
-> kinematicsInverse()
-> xyzbcKinematicsInverse()
-> joint.N.coarse_pos
-> joint.N.motor-pos-cmd
-> 仿真 PID/mux
-> joint.N.motor-pos-fb
反馈/状态更新时则通过 forward:
joint feedback/cmd
-> kinematicsForward()
-> xyzbcKinematicsForward()
-> 当前 XYZBC 位姿
7. Vismach 可视化模型
INI 中启动 Vismach:
loadusr -W xyzbc-trt-gui
对应文件:
bin/xyzbc-trt-gui
src/hal/user_comps/vismach/xyzbc-trt-gui.py
xyzbc-trt-gui.py 创建 HAL 用户组件:
c = hal.component("xyzbc-trt-gui")
并创建输入 pin:
table-xsaddle-yspindle-ztilt-brotate-cz-offsetx-offsettool-offset
INI 中连接为:
net :table-x joint.0.pos-fb xyzbc-trt-gui.table-x
net :saddle-y joint.1.pos-fb xyzbc-trt-gui.saddle-y
net :spindle-z joint.2.pos-fb xyzbc-trt-gui.spindle-z
net :tilt-b joint.3.pos-fb xyzbc-trt-gui.tilt-b
net :rotate-c joint.4.pos-fb xyzbc-trt-gui.rotate-c
因此 Vismach 显示的是 joint 反馈,不是直接显示 G-code 坐标。
偏置和刀长补偿也传给 Vismach:
net :tool-offset xyzbc-trt-kins.tool-offset xyzbc-trt-gui.tool-offset
net :x-offset xyzbc-trt-kins.x-offset xyzbc-trt-gui.x-offset
net :z-offset xyzbc-trt-kins.z-offset xyzbc-trt-gui.z-offset
这样三维模型的几何位置和运动学计算使用同一组偏置。
8. PyVCP 面板与按钮
AXIS 根据:
[DISPLAY]
PYVCP = ./xyzbc-trt.xml
加载:
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.xml
该 XML 创建一个 SWITCHKINS 面板,包含:
- 多状态标签:
0:IDENTITY、1: XYZBC、2: USERK - 按钮
IDENTITY - 按钮
TCP:XYZBC - 按钮
userk - 按钮
vismach-clear
AXIS 创建 PyVCP 组件后,再执行:
[HAL]
POSTGUI_HALFILE = switchkins_postgui.hal
对应文件:
configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal
其中连接:
net :kinstype.is-0 <= kinstype.is-0 => pyvcp.multilabel.0.legend0
net :kinstype.is-1 <= kinstype.is-1 => pyvcp.multilabel.0.legend1
net :kinstype.is-2 <= kinstype.is-2 => pyvcp.multilabel.0.legend2
net :type0-button <= pyvcp.type0-button => halui.mdi-command-00
net :type1-button <= pyvcp.type1-button => halui.mdi-command-01
net :type2-button <= pyvcp.type2-button => halui.mdi-command-02
INI 中 [HALUI] 配置为:
MDI_COMMAND = M429
MDI_COMMAND = M428
MDI_COMMAND = M430
所以按钮和运动学类型的实际关系是:
IDENTITY->halui.mdi-command-00->M429-> type 0TCP:XYZBC->halui.mdi-command-01->M428-> type 1userk->halui.mdi-command-02->M430-> type 2
9. M428/M429/M430 切换链路
remap 子程序位于:
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc
由于本配置使用 sparm=identityfirst,实际映射为:
M429:#<kinstype> = 0,identityM428:#<kinstype> = 1,xyzbc TRTM430:#<kinstype> = 2,userk
以 M428 为例,428remap.ngc 中核心语句为:
#<kinstype> = 1
#<SWITCHKINS_PIN> = 3
M68 E#<SWITCHKINS_PIN> Q#<kinstype>
M66 E0 L0
实际等价于:
M68 E3 Q1
M66 E0 L0
M68 E3 Q1 设置 motion.analog-out-03 = 1。
INI 已连接:
net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type
因此完整切换路径为:
PyVCP button
-> halui.mdi-command-01
-> M428
-> 428remap.ngc
-> M68 E3 Q1
-> motion.analog-out-03 = 1
-> motion.switchkins-type = 1
-> motion servo thread 检测到变化
-> handle_kinematicsSwitch()
-> kinematicsSwitch(1)
-> 激活 xyzbc TRT kinematics
M66 E0 L0 用于强制解释器和 motion 同步,确保后续 G-code 在切换后的运动学类型下执行。
10. motion 伺服周期中的 switchkins 处理
motion 控制循环位于:
src/emc/motion/control.c
每个 servo cycle 中会执行:
read_homing_in_pins(ALL_JOINTS);
handle_kinematicsSwitch();
process_inputs();
do_forward_kins();
...
get_pos_cmds(period);
...
output_to_hal();
handle_kinematicsSwitch() 的逻辑:
- 如果当前运动学不可切换,直接返回。
- 读取
motion.switchkins-type。 - 如果值没变,直接返回。
- 如果值变化,调用
kinematicsSwitch(new_type)。 - 用当前 joint 位置做一次 forward kinematics。
- 更新
emcmotStatus->carte_pos_cmd。 - 更新 trajectory planner 当前位姿。
这一步很重要:切换运动学时,LinuxCNC 会用当前 joint 位置重新计算新的笛卡尔位置,避免轨迹规划器还停留在旧运动学解释下的位置。
11. M68 到 motion analog output 的路径
M68 在解释器中被转换为设置 analog output 的 canonical command。
源码路径:
src/emc/rs274ngc/interp_convert.cc
src/emc/task/emccanon.cc
src/emc/task/taskintf.cc
src/emc/motion/command.c
核心过程:
interp_convert.cc识别M68 E... Q...。- 调用
SET_AUX_OUTPUT_VALUE(index, value)。 emccanon.cc生成EMC_MOTION_SET_AOUT。- task 层调用
emcMotionSetAout()。 - motion 收到
EMCMOT_SET_AOUT。 emcmotAioWrite(index, value)写入motion.analog-out-XX。
本配置中 index 为 3,所以写入:
motion.analog-out-03
再通过 HAL net 传给:
motion.switchkins-type
12. 演示 G-code 执行过程
AXIS 启动后自动打开:
configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc
该文件内容很短:
o<xyzbc_switchkins_sub> call [10] [5] [10][1000][3][0][20][45][20]
m2
它调用:
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzbc_switchkins_sub.ngc
参数含义:
zmax = 10zmin = 5r = 10frate = 1000n = 3a = 0b = 20c = 45dist = 20
xyzbc_switchkins_sub.ngc 在四个象限重复类似流程:
M429
G53 G0 X0 Y0 Z#<zmax> B0 C0
G10 L20 P0 X0 Y0 Z#<zmax> B0 C0
G0 X... Y... Z#<zmax>
o<helix_bc> call [...]
也就是:
- 用
M429切到 identity。 - 用
G53在机床坐标中移动到安全位置。 - 用
G10 L20 P0重设当前工件坐标系。 - 移动到当前象限的加工中心。
- 调用
helix_bc。
helix_bc.ngc 的核心流程为:
M429
G0 X[#<_x> - #<r>]
G10 L20 P0 X0 Y0 Z#<zmax> B0 C0
M428
G0 B#<b> C#<c>
F#<frate> G2 I#<r> Z#<zmin> P#<n>
M429
G0 X0 Y0 Z#<zmax> B0 C0
G0 X[#<_x> + #<r>]
M428
含义:
- 先切回 identity,方便做直观的定位和坐标系设置。
- 调整 X 到圆弧起点。
- 重设 G54。
M428切换到 xyzbc TRT 运动学。- 移动 B/C 到指定角度。
- 执行带 Z 下降的
G2 ... P#<n>螺旋插补。 - 切回 identity,回到安全位置。
- 最后再次切到 xyzbc,保持显示/演示状态。
13. 运行时数据流总览
启动阶段:
desktop/rip-environment
-> scripts/linuxcnc
-> linuxcncsvr
-> realtime/HAL
-> milltask
-> halui
-> basic_sim.tcl
-> loadrt xyzbc-trt-kins sparm=identityfirst
-> loadrt motmod num_joints=5
-> HALCMD loadusr -W xyzbc-trt-gui
-> axis GUI
-> PyVCP
-> switchkins_postgui.hal
切换运动学:
PyVCP button 或 G-code M428/M429/M430
-> remap ngc
-> M68 E3 Q<type>
-> motion.analog-out-03
-> motion.switchkins-type
-> handle_kinematicsSwitch()
-> kinematicsSwitch(type)
加工运动:
G-code XYZBC
-> interpreter
-> task
-> trajectory planner
-> emcmotStatus->carte_pos_cmd
-> kinematicsInverse()
-> xyzbcKinematicsInverse() 或 identityKinematicsInverse()
-> joint.N.motor-pos-cmd
-> pid/mux 仿真闭环
-> joint.N.motor-pos-fb
-> Vismach 显示
反馈显示:
joint.N.pos-fb
-> xyzbc-trt-gui table/saddle/spindle/tilt/rotate pins
-> Vismach 三维模型
kinstype.is-N
-> pyvcp.multilabel
-> SWITCHKINS 面板显示当前类型
14. 关键结论
该仿真不是简单的五个独立轴显示程序,而是一个完整的 switchkins 示例:
- 启动默认是 identity kinematics,因为
sparm=identityfirst。 M429选择 identity,即switchkins-type 0。M428选择 xyzbc TRT,即switchkins-type 1。M430选择 userk,即switchkins-type 2。- 运动学切换不是按钮直接写运动学模块,而是经过
halui -> MDI -> remap -> M68 -> motion.analog-out-03 -> motion.switchkins-type。 xyzbcKinematicsInverse()是 RTCP/TCP 行为的核心,它根据 XYZBC 指令、B/C 角度、转台偏置和刀长补偿计算 joint 目标。- Vismach 使用 joint feedback 和同一组几何偏置显示机床模型,因此可视化结果跟运动学参数保持一致。
- 演示程序通过 identity 与 xyzbc TRT 之间反复切换,展示了在普通机床坐标定位和五轴 TCP 加工之间切换的完整过程。
15. 主要相关文件清单
配置入口:
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
HAL/GUI:
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.xml
configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt_cmds.hal
src/hal/user_comps/vismach/xyzbc-trt-gui.py
bin/xyzbc-trt-gui
G-code/remap:
configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzbc_switchkins_sub.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_bc.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc
运动学源码:
src/emc/kinematics/xyzbc-trt-kins.c
src/emc/kinematics/trtfuncs.c
src/emc/kinematics/switchkins.c
src/emc/kinematics/userkfuncs.c
include/kinematics.h
motion/task/interpreter 相关:
src/emc/motion/motion.c
src/emc/motion/control.c
src/emc/motion/command.c
src/emc/rs274ngc/interp_convert.cc
src/emc/task/emccanon.cc
src/emc/task/taskintf.cc
启动脚本和 HAL 库:
scripts/linuxcnc
scripts/rip-environment
lib/hallib/basic_sim.tcl
lib/hallib/sim_lib.tcl
16. 与 LinuxCNC 数据系统核心原理的对应关系
xyzbc-trt 仿真可以看作 LinuxCNC 数据系统的一次完整穿透:从 INI 装配,到 NML 命令,到 task/interpreter,到 motion 实时共享内存,到 HAL pin/signal,再到 Vismach 显示。
16.1 INI 是装配入口
xyzbc-trt.ini 决定系统启动时要加载的主要对象:
[DISPLAY] DISPLAY = axis
[DISPLAY] PYVCP = ./xyzbc-trt.xml
[DISPLAY] OPEN_FILE = ./demos/xyzbc_switchkins.ngc
[KINS] KINEMATICS = xyzbc-trt-kins sparm=identityfirst
[KINS] JOINTS = 5
[TRAJ] COORDINATES = XYZBC
[HAL] HALFILE = LIB:basic_sim.tcl
[HAL] POSTGUI_HALFILE = switchkins_postgui.hal
INI 本身不做实时控制,它描述“系统应该如何装配”。真正的运行时数据交换由 NML、motion shared memory 和 HAL 完成。
16.2 NML 负责 GUI/halui/task 命令与状态
当用户点击 PyVCP 按钮时,按钮并不直接写 motion.switchkins-type。实际路径是:
PyVCP button
-> halui.mdi-command-N
-> halui 通过 NML 发送 MDI 命令
-> milltask 接收命令
-> interpreter 执行 M428/M429/M430 remap
这体现了 LinuxCNC 的基本原则:GUI 和 halui 表达操作意图,task 负责调度和合法性,motion 负责实时执行。
16.3 Interpreter/Canonical 层把 M428 转成 motion 能执行的动作
M428 并不是 motion 原生命令,而是 remap 子程序:
M68 E3 Q1
M66 E0 L0
解释器把 M68 E3 Q1 转换为 canonical motion output command,最终由 task 发送给 motion:
M68 E3 Q1
-> SET_AUX_OUTPUT_VALUE(3, 1)
-> EMC_MOTION_SET_AOUT
-> emcMotionSetAout()
-> usrmotWriteEmcmotCommand()
-> realtime motion command shared memory
这说明 G-code 的作用不是直接改 HAL,而是通过解释器和 task 进入 motion。
16.4 Motion shared memory 是 task 和实时 motion 的边界
task 写入 emcmot_command_t,实时 motion 读取并执行。motion 每个 servo cycle 更新 emcmot_status_t,task 再把它汇总到 EMC_STAT。
在 xyzbc-trt 中,M68 E3 Q1 最终被 motion 执行为:
motion.analog-out-03 = 1
然后 HAL 连接把这个值传给:
motion.switchkins-type = 1
16.5 HAL 是实时信号网络
本配置最关键的 HAL net 是:
net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type
它的本质是让两个 HAL pin 共享同一个 signal 值:
writer: motion.analog-out-03
signal: :kinstype-select
reader: motion.switchkins-type
servo-thread 中 handle_kinematicsSwitch() 读取 motion.switchkins-type,发现从 0 变成 1 后调用:
kinematicsSwitch(1)
于是当前运动学从 identity 切换到 xyzbc TRT。
16.6 Vismach 是 HAL 数据消费者
Vismach 模型不参与 NML,也不参与 motion command 调度。它作为 HAL 用户组件读取 pin:
joint.0.pos-fb -> xyzbc-trt-gui.table-x
joint.1.pos-fb -> xyzbc-trt-gui.saddle-y
joint.2.pos-fb -> xyzbc-trt-gui.spindle-z
joint.3.pos-fb -> xyzbc-trt-gui.tilt-b
joint.4.pos-fb -> xyzbc-trt-gui.rotate-c
因此 Vismach 显示的是 HAL 中的 joint feedback 快照,而不是直接读取 G-code 或 motion 内部轨迹队列。
16.7 该配置的数据链总图
INI 装配
-> basic_sim.tcl 加载 motion/k 等 HAL 模块
-> AXIS 创建 PyVCP
-> PyVCP 按钮触发 halui MDI
-> NML command 到 milltask
-> interpreter 执行 M428/M429/M430 remap
-> M68 变成 EMC_MOTION_SET_AOUT
-> task 写 emcmot_command_t
-> realtime motion 写 motion.analog-out-03
-> HAL net 传给 motion.switchkins-type
-> servo-thread 切换 kinematics
-> inverse kinematics 生成 joint 目标
-> 仿真 PID/mux 生成 joint feedback
-> Vismach 读取 HAL feedback 显示机床
-> motion status 汇总到 EMC_STAT
-> AXIS/halui 显示状态
详细通用原理见:
项目分析/LinuxCNC数据系统核心原理.md