4. PLC读取LinuxCNC坐标位置
任务目标
在LinuxCNC运行过程中,PLC需要实时获取机床各轴坐标(如X、Y、Z轴位置)。本案例通过LinuxCNC的DBus接口实现坐标读取,并在PLC侧通过Modbus TCP协议暴露坐标值,使外部PLC主站(或HMI)能够读取当前坐标。
实现方案
LinuxCNC本身不直接提供PLC通信模块,但可以通过其DBus接口获取轴位置。本案例采用以下链路:
- LinuxCNC通过DBus发布轴坐标(需启用
dbus模块); - Python脚本订阅DBus信号,周期读取坐标;
- 脚本作为Modbus TCP从站,将坐标映射到保持寄存器;
- PLC作为Modbus主站读取这些寄存器。
环境依赖
- LinuxCNC(已启用DBus模块,通常在
.ini中配置DBUS = 1) - Python 3 +
dbus-python、pymodbus库
sudo apt install python3-dbus python3-pip
pip3 install pymodbus==2.5.3
关键完整代码
以下Python脚本实现坐标读取与Modbus TCP从站功能:
#!/usr/bin/env python3
import dbus
import threading
import time
from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSlaveContext, ModbusDataBlock
from pymodbus.datastore import ModbusServerContext
# ---------- LinuxCNC DBus 坐标读取 ----------
class AxisReader:
def __init__(self):
self.bus = dbus.SessionBus()
# LinuxCNC DBus 服务名与路径(需与hal配置一致)
self.obj = self.bus.get_object('org.linuxcnc', '/org/linuxcnc/axis')
self.iface = dbus.Interface(self.obj, 'org.linuxcnc.Axis')
self.coords = [0.0, 0.0, 0.0] # X, Y, Z
def update(self):
try:
# 获取当前坐标(单位:mm)
self.coords[0] = self.iface.GetAxisPosition(0) # X轴
self.coords[1] = self.iface.GetAxisPosition(1) # Y轴
self.coords[2] = self.iface.GetAxisPosition(2) # Z轴
except Exception as e:
print(f"DBus读取失败: {e}")
# ---------- Modbus 寄存器映射 ----------
# 保持寄存器地址规划:
# 0-1: X轴坐标(float32,大端)
# 2-3: Y轴坐标
# 4-5: Z轴坐标
# 6: 数据有效标志(1=有效)
class ModbusDataStore:
def __init__(self, reader):
self.reader = reader
# 初始化寄存器块(保持寄存器,共7个)
self.store = ModbusSlaveContext(
di=ModbusDataBlock.create([0]*10),
co=ModbusDataBlock.create([0]*10),
hr=ModbusDataBlock.create([0]*7),
ir=ModbusDataBlock.create([0]*10)
)
def update_registers(self):
# 将float32拆分为两个16位寄存器(大端序)
for idx, val in enumerate(self.reader.coords):
import struct
packed = struct.pack('>f', val) # 大端float32
reg1, reg2 = struct.unpack('>HH', packed)
self.store.setValues(3, idx*2, [reg1, reg2]) # 3=保持寄存器
# 设置有效标志
self.store.setValues(3, 6, [1])
# ---------- 主程序 ----------
def main():
reader = AxisReader()
datastore = ModbusDataStore(reader)
context = ModbusServerContext(slaves=datastore, single=True)
# 启动Modbus TCP服务(端口5020,避免权限问题)
t = threading.Thread(target=StartTcpServer, args=(context,), kwargs={'address': ('0.0.0.0', 5020)})
t.daemon = True
t.start()
print("Modbus TCP 从站已启动,端口5020")
# 周期更新坐标(10Hz)
while True:
reader.update()
datastore.update_registers()
time.sleep(0.1)
if __name__ == '__main__':
main()
运行与验证
- 启动LinuxCNC并加载包含DBus支持的配置(如模拟器配置
sim/axis.ini)。 - 运行上述脚本:
python3 axis_to_modbus.py - PLC侧配置:作为Modbus TCP主站,连接IP为LinuxCNC主机IP,端口5020。
- 读取寄存器:地址0-5为X/Y/Z坐标(float32大端),地址6为有效标志。
验证方法:在LinuxCNC中手动移动轴(如MDI输入G0 X50),观察PLC读取到的X坐标值是否同步变化。若PLC显示值与LinuxCNC界面坐标一致(误差在运动精度范围内),且有效标志为1,则判定通过。
常见问题
- DBus连接失败:确认LinuxCNC已启用DBus(检查
.ini中[DBUS]节),且运行在同一用户会话。 - 寄存器字节序:本实现使用大端序(网络字节序),PLC侧需匹配。若PLC使用小端,可修改
struct.pack('<f', val)。 - 坐标单位:LinuxCNC默认返回毫米(mm),若使用英寸配置需在PLC侧换算。