Files
cnc_wams/项目分析/5axis-xyzbc-trt-sim执行过程分析.md
2026-07-05 22:13:40 -04:00

883 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 5axis-xyzbc-trt-sim 执行过程分析
本文分析当前 LinuxCNC 源码树中 `5axis-xyzbc-trt-sim` 对应的仿真配置。实际入口配置文件为:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
```
该配置是一个 `axis` GUI + `basic_sim.tcl` 仿真 HAL + `xyzbc-trt-kins` 可切换运动学 + PyVCP/halui 控制面板 + Vismach 三维模型的五轴转台仿真。
## 1. 启动入口
如果从项目中已有快捷方式启动,入口文件是:
```text
linuxcnc-rtcp-5axis-shortcuts/table-rotary-tilting/xyzbc-trt.desktop
```
其中的执行命令为:
```bash
/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`
总体启动顺序如下:
1. 启动 `linuxcncsvr`,创建和持有 NML 通道。
2. 启动 realtime/HAL。
3. 加载 `tpmod``homemod`
4. 启动 `[TASK] TASK = milltask`
5. 启动 `[HAL] HALUI = halui`
6. 执行 `[HAL] HALFILE = LIB:basic_sim.tcl`
7. 执行 INI 中所有 `[HAL] HALCMD = ...`
8. 执行 `halcmd start`,启动实时线程。
9. 启动 `[DISPLAY] DISPLAY = axis`
## 2. INI 核心配置
目标配置文件:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
```
关键内容:
```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` -> X
- `joint.1` -> Y
- `joint.2` -> Z
- `joint.3` -> B
- `joint.4` -> C
`sparm=identityfirst` 是本配置的重要细节。它改变了 `xyzbc-trt-kins` 的默认类型顺序,使启动时的 `switchkins-type 0` 是 identity kinematics。
## 3. basic_sim.tcl 建立仿真 HAL
INI 中:
```ini
[HAL]
HALFILE = LIB:basic_sim.tcl
```
对应文件:
```text
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`,主要完成:
1. 调用 `setup_kins` 加载 `[KINS] KINEMATICS` 指定的运动学模块。
2. 加载 `motmod`
3.`motion-command-handler``motion-controller` 加入 `servo-thread`
4. 为每个 joint 创建 `pid`
5. 为每个 joint 创建 `mux2`
6.`joint.N.motor-pos-cmd` 经由 `pid``mux2` 接回 `joint.N.motor-pos-fb`,形成理想伺服仿真闭环。
7. 加载仿真回零、仿真主轴、手动换刀等用户态/实时组件。
运行后可生成等效 HAL 文件:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt_cmds.hal
```
其中关键 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 的典型连接形态为:
```hal
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 初始化
运动学源码入口:
```text
src/emc/kinematics/xyzbc-trt-kins.c
src/emc/kinematics/switchkins.c
src/emc/kinematics/trtfuncs.c
```
`xyzbc-trt-kins.c``switchkinsSetup()` 根据 `sparm` 配置三套运动学函数。
因为 INI 使用:
```ini
KINEMATICS = xyzbc-trt-kins sparm=identityfirst
```
所以实际类型顺序为:
- `switchkins-type 0`identity kinematics
- `switchkins-type 1`xyzbc TRT kinematics
- `switchkins-type 2`userk kinematics
如果没有 `identityfirst`,默认 type 0 才是 `xyzbc-trt-kins`
`switchkins.c` 提供统一包装函数:
- `kinematicsForward()`
- `kinematicsInverse()`
- `kinematicsSwitch()`
- `kinematicsSwitchable()`
并创建状态 HAL pin
- `kinstype.is-0`
- `kinstype.is-1`
- `kinstype.is-2`
启动时 `switchkins_type = 0`,随后调用 `kinematicsSwitch(0)`,因此本配置启动后处于 identity kinematics。
## 5. xyzbc TRT 几何参数
`trtKinematicsSetup()` 位于:
```text
src/emc/kinematics/trtfuncs.c
```
它为 TRT 运动学创建 HAL 输入:
- `xyzbc-trt-kins.x-rot-point`
- `xyzbc-trt-kins.y-rot-point`
- `xyzbc-trt-kins.z-rot-point`
- `xyzbc-trt-kins.x-offset`
- `xyzbc-trt-kins.y-offset`
- `xyzbc-trt-kins.z-offset`
- `xyzbc-trt-kins.tool-offset`
- `xyzbc-trt-kins.conventional-directions`
INI 中对这些参数的设置和连接为:
```hal
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
```
因此实际计算中:
```text
dx = x-offset = -20
dz = z-offset + tool-offset
```
`tool-offset` 来自 `motion.tooloffset.z`,也就是 LinuxCNC 当前刀长补偿的 Z 分量。
## 6. xyzbc 正/逆运动学
`xyzbcKinematicsForward()` 位于:
```text
src/emc/kinematics/trtfuncs.c
```
它从 joint 坐标计算当前笛卡尔位姿:
```text
joints[X/Y/Z/B/C] -> EmcPose XYZBC
```
`xyzbcKinematicsInverse()` 从程序位姿计算 joint 目标:
```text
EmcPose XYZBC -> joints[X/Y/Z/B/C]
```
关键输入包括:
- X/Y/Z/B/C 指令位姿
- B 轴角度
- C 轴角度
- `x-offset`
- `z-offset`
- `tool-offset`
- 旋转中心 `x/y/z-rot-point`
- `conventional-directions`
在 coordinated motion 中motion 控制循环调用当前运动学的 inverse将轨迹规划器输出的笛卡尔目标位置转换为 joint 目标。
路径为:
```text
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
```text
joint feedback/cmd
-> kinematicsForward()
-> xyzbcKinematicsForward()
-> 当前 XYZBC 位姿
```
## 7. Vismach 可视化模型
INI 中启动 Vismach
```hal
loadusr -W xyzbc-trt-gui
```
对应文件:
```text
bin/xyzbc-trt-gui
src/hal/user_comps/vismach/xyzbc-trt-gui.py
```
`xyzbc-trt-gui.py` 创建 HAL 用户组件:
```python
c = hal.component("xyzbc-trt-gui")
```
并创建输入 pin
- `table-x`
- `saddle-y`
- `spindle-z`
- `tilt-b`
- `rotate-c`
- `z-offset`
- `x-offset`
- `tool-offset`
INI 中连接为:
```hal
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
```hal
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 根据:
```ini
[DISPLAY]
PYVCP = ./xyzbc-trt.xml
```
加载:
```text
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 组件后,再执行:
```ini
[HAL]
POSTGUI_HALFILE = switchkins_postgui.hal
```
对应文件:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal
```
其中连接:
```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]` 配置为:
```ini
MDI_COMMAND = M429
MDI_COMMAND = M428
MDI_COMMAND = M430
```
所以按钮和运动学类型的实际关系是:
- `IDENTITY` -> `halui.mdi-command-00` -> `M429` -> type 0
- `TCP:XYZBC` -> `halui.mdi-command-01` -> `M428` -> type 1
- `userk` -> `halui.mdi-command-02` -> `M430` -> type 2
## 9. M428/M429/M430 切换链路
remap 子程序位于:
```text
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`identity
- `M428``#<kinstype> = 1`xyzbc TRT
- `M430``#<kinstype> = 2`userk
`M428` 为例,`428remap.ngc` 中核心语句为:
```ngc
#<kinstype> = 1
#<SWITCHKINS_PIN> = 3
M68 E#<SWITCHKINS_PIN> Q#<kinstype>
M66 E0 L0
```
实际等价于:
```ngc
M68 E3 Q1
M66 E0 L0
```
`M68 E3 Q1` 设置 `motion.analog-out-03 = 1`
INI 已连接:
```hal
net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type
```
因此完整切换路径为:
```text
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 控制循环位于:
```text
src/emc/motion/control.c
```
每个 servo cycle 中会执行:
```c
read_homing_in_pins(ALL_JOINTS);
handle_kinematicsSwitch();
process_inputs();
do_forward_kins();
...
get_pos_cmds(period);
...
output_to_hal();
```
`handle_kinematicsSwitch()` 的逻辑:
1. 如果当前运动学不可切换,直接返回。
2. 读取 `motion.switchkins-type`
3. 如果值没变,直接返回。
4. 如果值变化,调用 `kinematicsSwitch(new_type)`
5. 用当前 joint 位置做一次 forward kinematics。
6. 更新 `emcmotStatus->carte_pos_cmd`
7. 更新 trajectory planner 当前位姿。
这一步很重要切换运动学时LinuxCNC 会用当前 joint 位置重新计算新的笛卡尔位置,避免轨迹规划器还停留在旧运动学解释下的位置。
## 11. M68 到 motion analog output 的路径
`M68` 在解释器中被转换为设置 analog output 的 canonical command。
源码路径:
```text
src/emc/rs274ngc/interp_convert.cc
src/emc/task/emccanon.cc
src/emc/task/taskintf.cc
src/emc/motion/command.c
```
核心过程:
1. `interp_convert.cc` 识别 `M68 E... Q...`
2. 调用 `SET_AUX_OUTPUT_VALUE(index, value)`
3. `emccanon.cc` 生成 `EMC_MOTION_SET_AOUT`
4. task 层调用 `emcMotionSetAout()`
5. motion 收到 `EMCMOT_SET_AOUT`
6. `emcmotAioWrite(index, value)` 写入 `motion.analog-out-XX`
本配置中 index 为 3所以写入
```text
motion.analog-out-03
```
再通过 HAL net 传给:
```text
motion.switchkins-type
```
## 12. 演示 G-code 执行过程
AXIS 启动后自动打开:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc
```
该文件内容很短:
```ngc
o<xyzbc_switchkins_sub> call [10] [5] [10][1000][3][0][20][45][20]
m2
```
它调用:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzbc_switchkins_sub.ngc
```
参数含义:
- `zmax = 10`
- `zmin = 5`
- `r = 10`
- `frate = 1000`
- `n = 3`
- `a = 0`
- `b = 20`
- `c = 45`
- `dist = 20`
`xyzbc_switchkins_sub.ngc` 在四个象限重复类似流程:
```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 [...]
```
也就是:
1.`M429` 切到 identity。
2.`G53` 在机床坐标中移动到安全位置。
3.`G10 L20 P0` 重设当前工件坐标系。
4. 移动到当前象限的加工中心。
5. 调用 `helix_bc`
`helix_bc.ngc` 的核心流程为:
```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
```
含义:
1. 先切回 identity方便做直观的定位和坐标系设置。
2. 调整 X 到圆弧起点。
3. 重设 G54。
4. `M428` 切换到 xyzbc TRT 运动学。
5. 移动 B/C 到指定角度。
6. 执行带 Z 下降的 `G2 ... P#<n>` 螺旋插补。
7. 切回 identity回到安全位置。
8. 最后再次切到 xyzbc保持显示/演示状态。
## 13. 运行时数据流总览
启动阶段:
```text
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
```
切换运动学:
```text
PyVCP button 或 G-code M428/M429/M430
-> remap ngc
-> M68 E3 Q<type>
-> motion.analog-out-03
-> motion.switchkins-type
-> handle_kinematicsSwitch()
-> kinematicsSwitch(type)
```
加工运动:
```text
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 显示
```
反馈显示:
```text
joint.N.pos-fb
-> xyzbc-trt-gui table/saddle/spindle/tilt/rotate pins
-> Vismach 三维模型
kinstype.is-N
-> pyvcp.multilabel
-> SWITCHKINS 面板显示当前类型
```
## 14. 关键结论
该仿真不是简单的五个独立轴显示程序,而是一个完整的 switchkins 示例:
1. 启动默认是 identity kinematics因为 `sparm=identityfirst`
2. `M429` 选择 identity`switchkins-type 0`
3. `M428` 选择 xyzbc TRT`switchkins-type 1`
4. `M430` 选择 userk`switchkins-type 2`
5. 运动学切换不是按钮直接写运动学模块,而是经过 `halui -> MDI -> remap -> M68 -> motion.analog-out-03 -> motion.switchkins-type`
6. `xyzbcKinematicsInverse()` 是 RTCP/TCP 行为的核心,它根据 XYZBC 指令、B/C 角度、转台偏置和刀长补偿计算 joint 目标。
7. Vismach 使用 joint feedback 和同一组几何偏置显示机床模型,因此可视化结果跟运动学参数保持一致。
8. 演示程序通过 identity 与 xyzbc TRT 之间反复切换,展示了在普通机床坐标定位和五轴 TCP 加工之间切换的完整过程。
## 15. 主要相关文件清单
配置入口:
```text
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
```
HAL/GUI
```text
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
```text
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
```
运动学源码:
```text
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 相关:
```text
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 库:
```text
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` 决定系统启动时要加载的主要对象:
```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`。实际路径是:
```text
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 子程序:
```ngc
M68 E3 Q1
M66 E0 L0
```
解释器把 `M68 E3 Q1` 转换为 canonical motion output command最终由 task 发送给 motion
```text
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 执行为:
```text
motion.analog-out-03 = 1
```
然后 HAL 连接把这个值传给:
```text
motion.switchkins-type = 1
```
### 16.5 HAL 是实时信号网络
本配置最关键的 HAL net 是:
```hal
net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type
```
它的本质是让两个 HAL pin 共享同一个 signal 值:
```text
writer: motion.analog-out-03
signal: :kinstype-select
reader: motion.switchkins-type
```
servo-thread 中 `handle_kinematicsSwitch()` 读取 `motion.switchkins-type`,发现从 0 变成 1 后调用:
```text
kinematicsSwitch(1)
```
于是当前运动学从 identity 切换到 xyzbc TRT。
### 16.6 Vismach 是 HAL 数据消费者
Vismach 模型不参与 NML也不参与 motion command 调度。它作为 HAL 用户组件读取 pin
```hal
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 该配置的数据链总图
```text
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 显示状态
```
详细通用原理见:
```text
项目分析/LinuxCNC数据系统核心原理.md
```