LinuxCNC with EtherCAT¶
LinuxCNC is a free, open-source Linux software system that implements computer numerical control capability using general purpose computers to control CNC machines. It is typically bundled as an ISO file with a modified version of Debian Linux which provides the required real-time kernel. However, we can simply build LinuxCNC from source and deploy leveraging Linux distribution and real-time kernel provided by ECI. We can also leverage Ethercat master stack from ECI to control physical slave device with LinuxCNC.
For more information, refer to LinuxCNC website.
Install LinuxCNC with EtherCAT master stack¶
Do the following to prepare real-time environment, set up EtherCAT master stack and install LinuxCNC:
Follow Get Started Steps to prepare a real-time Linux environment. PREEMPT_RT kernel is recommended real-time kernel.
Install and set up IgH EtherCAT Master Stack.
Execute following step to fetch and compile LinuxCNC
# INSTALL THE BUILD PACKAGES $ sudo apt install git build-essential # CLONE THE LINUXCNC SOURCE CODE $ git clone github.com/LinuxCNC/linuxcnc.git linuxcnc-dev # MOVE TO LINUXCNC-DEV FOLDER $ cd linuxcnc-dev # CHANGE TO VERSION YOU WANT TO BUILD (FOR ME 2.9= QTDRAGON_HD) $ git checkout 2.9 # CHANGE TO DEBIAN FOLDER $ cd debian # THEN CONFIGURE $ ./configure uspace # THEN MOVE BACK TO THE LINUXCNC-DEV FOLDER $ cd .. # CHECK FOR ANY MISSING BUILD DEPENDENCIES (THER WILL BE SERVERAL PACKAGES TO INSTALL. JUST COPY SELECTION AND PASTE TO SAVE ALL THAT TYPING, OTHERWISE JUST TAKE TIME AND TYPE THEM ALL OUT) $ dpkg-checkbuilddeps # COPY/PASTE THE LIST OF BUILD DEPPENDENCIES THEN INSTALL WITH $ sudo apt-get install "LIST OF DEPS COPIED FROM DPKG-CHECKBUILDDEPS" # INSTALL ALL OF THE PACKAGES NEEDED, THEN CHECK DEPS AGAIN $ dpkg-checkbuilddeps # ONCE ALL THE DEPENDENCIES HAVE BEEN INSTALLED THEN MOVE TO THE SOURCE FOLDER $ cd src # THEN AFTER GETTING INTO SRC FOLDER $ ./autogen.sh # THEN CONFIGURE $ ./configure --with-realtime=uspace # THEN MAKE $ make # ALLOW ACCESS TO HARDWARE $ sudo make setuid # SETUP RIP ENVIRONMENT $ . ../scripts/rip-environment # START UP LINUXCNC $ linuxcnc
Change the permission of ethercat device so LinuxCNC can manipulate ethercat without root permission.
# CHANGE ETHERCAT DEVICE PERMISSION $ sudo chmod 666 /dev/EtherCAT0 # OPEN IN VIM TO GIVE ETHERCAT PORT STARTUP PERMISSION $ sudo vim /etc/udev/rules.d/99-ethercat.rules # ONCE THE FILE IS OPEN, ADD THE FOLLOWING: KERNEL=="EtherCAT[0-9]", MODE="0777" # THEN SAVE AND EXIT VIM WITH ":wq". # ONCE BACK ON THE COMMAND LINE, THEN RELOAD THE RULES $ sudo udevadm control --reload-rules
Setup LinuxCNC EtherCAT Driver
Clone the LinuxCNC EtherCAT driver repository.
$ cd ~/ $ git clone github.com/sittner/linuxcnc-ethercat.git linuxcnc-ethercat $ cd /linuxcnc-ethercat/src $ vim realtime.mk
Now overwrite the
realtime.mk
file with following code.include ../config.mk include Kbuild cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) .PHONY: all clean install ifeq ($(BUILDSYS),kbuild) module = $(patsubst %.o,%.ko,$(obj-m)) ifeq (,$(findstring -Wframe-larger-than=,$(EXTRA_CFLAGS))) EXTRA_CFLAGS += $(call cc-option,-Wframe-larger-than=2560) endif $(module): $(MAKE) EXTRA_CFLAGS="$(EXTRA_CFLAGS)" KBUILD_EXTRA_SYMBOLS="$(RTLIBDIR)/Module.symvers $(RTAIDIR)/modules/ethercat/Module.symvers" -C $(KERNELDIR) SUBDIRS=`pwd` CC=$(CC) V=0 modules clean:: rm -f $(obj-m) rm -f *.mod.c .*.cmd rm -f modules.order Module.symvers rm -rf .tmp_versions else module = $(patsubst %.o,%.so,$(obj-m)) EXTRA_CFLAGS := $(filter-out -Wframe-larger-than=%,$(EXTRA_CFLAGS)) $(module): $(lcec-objs) $(CC) -shared -o $@ $(lcec-objs) -Wl,-rpath,$(LIBDIR) -L$(LIBDIR) -llinuxcnchal -lethercat -lrt %.o: %.c $(CC) -o $@ $(EXTRA_CFLAGS) -Os -c $< endif all: $(module) clean:: rm -f $(module) rm -f $(lcec-objs) install: $(module) mkdir -p $(DESTDIR)$(RTLIBDIR) cp $(module) $(DESTDIR)$(RTLIBDIR)/
After saving, move to the LinuxCNC-EtherCAT folder, apply the following patch to source code.
diff --git a/src/lcec_generic.c b/src/lcec_generic.c index dfddf73..3fdc44a 100644 --- a/src/lcec_generic.c +++ b/src/lcec_generic.c @@ -27,6 +27,14 @@ hal_u32_t lcec_generic_read_u32(uint8_t *pd, lcec_generic_pin_t *hal_data); void lcec_generic_write_s32(uint8_t *pd, lcec_generic_pin_t *hal_data, hal_s32_t sval); void lcec_generic_write_u32(uint8_t *pd, lcec_generic_pin_t *hal_data, hal_u32_t uval); +float ecrt_read_real(const void *data) +{ + uint32_t raw = EC_READ_U32(data); + return *(float *) (const void *) &raw; +} + +#define EC_READ_REAL(DATA) ecrt_read_real(DATA) + int lcec_generic_init(int comp_id, struct lcec_slave *slave, ec_pdo_entry_reg_t *pdo_entry_regs) { lcec_master_t *master = slave->master; lcec_generic_pin_t *hal_data = (lcec_generic_pin_t *) slave->hal_data;
Then make and install.
$ cd ~/linuxcnc-ethercat $ make clean && make # IF YOU HAVE FAILED ON THE MAKE, GO BACK TO THE "linuxcnc-dev/src" FOLDER # AND DO THE ". ../scripts/rip-environment" AGAIN TO SETUP ENVIRONMENT VARIABLE, THEN RE-EXECUTE $ make install
Then add text
/usr/local/lib
below the include line in/etc/ld.so.conf
file.include /etc/ld.so.conf.d/*.conf /usr/local/lib
After saving, change to root and load the library.
$ sudo su $ ldconfig -v $ exit
Install CiA402 HAL component. CiA402 is CANopen device profile for drives and motion control. HAL is hardware abstract layer for LinuxCNC.
$ cd ~/ $ git clone https://github.com/dbraun1981/hal-cia402 $ cd hal-cia402 $ halcompile --install cia402.comp # IF YOU HAVE FAILED ON "halcompile" COMMAND, GO BACK TO THE "linuxcnc-dev/src" FOLDER # AND DO THE ". ../scripts/rip-environment" AGAIN TO SETUP ENVIRONMENT VARIABLE, THEN RE-EXECUTE
Now installation of all components is finished.
LinuxCNC Demo with Physical Axes¶
Now we will link five physical axes to the joints in LinuxCNC simulation demo by LinuxCNC-EtherCAT driver. Here we choose table rotary tilting as a standard 5-axis configuration to demonstrate the steps. Here is 3D model simulator for table rotary tilting in LinuxCNC.
All configuration files for table rotary tilting demo are under linuxcnc-dev/configs/sim/axis/vismach/5axis/table-rotary-tilting/
First we need to prepare an xml file which describes the property of servo drives as ethercat slave device. Here we use Inovance IS620N as example of five slave devices. Put below content in a new generated xml file and rename it, for example,
ethercat-conf.xml
.<masters> <master idx="0" appTimePeriod="1000000" refClockSyncCycles="1"> <slave idx="0" type="generic" vid="00100000" pid="000C0108" configPdos="true"> <dcConf assignActivate="300" sync0Cycle="*1" sync0Shift="0"/> <syncManager idx="2" dir="out"> <pdo idx="1600"> <pdoEntry idx="6040" subIdx="00" bitLen="16" halPin="cia-controlword" halType="u32"/> <pdoEntry idx="6060" subIdx="00" bitLen="8" halPin="opmode" halType="s32"/> <pdoEntry idx="607A" subIdx="00" bitLen="32" halPin="target-position" halType="s32"/> <pdoEntry idx="60FF" subIdx="00" bitLen="32" halPin="target-velocity" halType="s32"/> </pdo> </syncManager> <syncManager idx="3" dir="in"> <pdo idx="1a00"> <pdoEntry idx="6041" subIdx="00" bitLen="16" halPin="cia-statusword" halType="u32"/> <pdoEntry idx="6061" subIdx="00" bitLen="8" halPin="opmode-display" halType="s32"/> <pdoEntry idx="6064" subIdx="00" bitLen="32" halPin="actual-position" halType="s32"/> <pdoEntry idx="606C" subIdx="00" bitLen="32" halPin="actual-velocity" halType="s32"/> </pdo> </syncManager> </slave> <slave idx="1" type="generic" vid="00100000" pid="000C0108" configPdos="true"> <dcConf assignActivate="300" sync0Cycle="*1" sync0Shift="0"/> <syncManager idx="2" dir="out"> <pdo idx="1600"> <pdoEntry idx="6040" subIdx="00" bitLen="16" halPin="cia-controlword" halType="u32"/> <pdoEntry idx="6060" subIdx="00" bitLen="8" halPin="opmode" halType="s32"/> <pdoEntry idx="607A" subIdx="00" bitLen="32" halPin="target-position" halType="s32"/> <pdoEntry idx="60FF" subIdx="00" bitLen="32" halPin="target-velocity" halType="s32"/> </pdo> </syncManager> <syncManager idx="3" dir="in"> <pdo idx="1a00"> <pdoEntry idx="6041" subIdx="00" bitLen="16" halPin="cia-statusword" halType="u32"/> <pdoEntry idx="6061" subIdx="00" bitLen="8" halPin="opmode-display" halType="s32"/> <pdoEntry idx="6064" subIdx="00" bitLen="32" halPin="actual-position" halType="s32"/> <pdoEntry idx="606C" subIdx="00" bitLen="32" halPin="actual-velocity" halType="s32"/> </pdo> </syncManager> </slave> <slave idx="2" type="generic" vid="00100000" pid="000C0108" configPdos="true"> <dcConf assignActivate="300" sync0Cycle="*1" sync0Shift="0"/> <syncManager idx="2" dir="out"> <pdo idx="1600"> <pdoEntry idx="6040" subIdx="00" bitLen="16" halPin="cia-controlword" halType="u32"/> <pdoEntry idx="6060" subIdx="00" bitLen="8" halPin="opmode" halType="s32"/> <pdoEntry idx="607A" subIdx="00" bitLen="32" halPin="target-position" halType="s32"/> <pdoEntry idx="60FF" subIdx="00" bitLen="32" halPin="target-velocity" halType="s32"/> </pdo> </syncManager> <syncManager idx="3" dir="in"> <pdo idx="1a00"> <pdoEntry idx="6041" subIdx="00" bitLen="16" halPin="cia-statusword" halType="u32"/> <pdoEntry idx="6061" subIdx="00" bitLen="8" halPin="opmode-display" halType="s32"/> <pdoEntry idx="6064" subIdx="00" bitLen="32" halPin="actual-position" halType="s32"/> <pdoEntry idx="606C" subIdx="00" bitLen="32" halPin="actual-velocity" halType="s32"/> </pdo> </syncManager> </slave> <slave idx="3" type="generic" vid="00100000" pid="000C0108" configPdos="true"> <dcConf assignActivate="300" sync0Cycle="*1" sync0Shift="0"/> <syncManager idx="2" dir="out"> <pdo idx="1600"> <pdoEntry idx="6040" subIdx="00" bitLen="16" halPin="cia-controlword" halType="u32"/> <pdoEntry idx="6060" subIdx="00" bitLen="8" halPin="opmode" halType="s32"/> <pdoEntry idx="607A" subIdx="00" bitLen="32" halPin="target-position" halType="s32"/> <pdoEntry idx="60FF" subIdx="00" bitLen="32" halPin="target-velocity" halType="s32"/> </pdo> </syncManager> <syncManager idx="3" dir="in"> <pdo idx="1a00"> <pdoEntry idx="6041" subIdx="00" bitLen="16" halPin="cia-statusword" halType="u32"/> <pdoEntry idx="6061" subIdx="00" bitLen="8" halPin="opmode-display" halType="s32"/> <pdoEntry idx="6064" subIdx="00" bitLen="32" halPin="actual-position" halType="s32"/> <pdoEntry idx="606C" subIdx="00" bitLen="32" halPin="actual-velocity" halType="s32"/> </pdo> </syncManager> </slave> <slave idx="4" type="generic" vid="00100000" pid="000C0108" configPdos="true"> <dcConf assignActivate="300" sync0Cycle="*1" sync0Shift="0"/> <syncManager idx="2" dir="out"> <pdo idx="1600"> <pdoEntry idx="6040" subIdx="00" bitLen="16" halPin="cia-controlword" halType="u32"/> <pdoEntry idx="6060" subIdx="00" bitLen="8" halPin="opmode" halType="s32"/> <pdoEntry idx="607A" subIdx="00" bitLen="32" halPin="target-position" halType="s32"/> <pdoEntry idx="60FF" subIdx="00" bitLen="32" halPin="target-velocity" halType="s32"/> </pdo> </syncManager> <syncManager idx="3" dir="in"> <pdo idx="1a00"> <pdoEntry idx="6041" subIdx="00" bitLen="16" halPin="cia-statusword" halType="u32"/> <pdoEntry idx="6061" subIdx="00" bitLen="8" halPin="opmode-display" halType="s32"/> <pdoEntry idx="6064" subIdx="00" bitLen="32" halPin="actual-position" halType="s32"/> <pdoEntry idx="606C" subIdx="00" bitLen="32" halPin="actual-velocity" halType="s32"/> </pdo> </syncManager> </slave> </master> </masters>
If there is servo drive of other model, change
vid
andpid
accordingly, which represents Vendor ID and Product ID of servo drive.Add following code to .hal file of demo. HAL file contains the HAL commands executed on LinuxCNC initialization, including HAL components loading and HAL pins connection. For table rotary tilting xyzbc-trt demo,
xyzbc-trt_cmds.hal
will be generated automatically when demo first startup. ChangeHALFILE = LIB:basic_sim.tcl
toHALFILE = ./xyzbc-trt_cmds.hal
inxyzbc-trt.ini
and add following code toxyzbc-trt_cmds.hal
.################################# # Setup ################################# loadusr -W lcec_conf ethercat-conf.xml loadrt lcec loadrt cia402 count=5 setp cia402.0.csp-mode 1 setp cia402.0.pos-scale 8388608 setp cia402.1.csp-mode 1 setp cia402.1.pos-scale 8388608 setp cia402.2.csp-mode 1 setp cia402.2.pos-scale 8388608 setp cia402.3.csp-mode 1 setp cia402.3.pos-scale 8388608 setp cia402.4.csp-mode 1 setp cia402.4.pos-scale 8388608 ################################# # Functions servo-thread ################################# addf lcec.read-all servo-thread addf cia402.0.read-all servo-thread addf cia402.1.read-all servo-thread addf cia402.2.read-all servo-thread addf cia402.3.read-all servo-thread addf cia402.4.read-all servo-thread addf cia402.0.write-all servo-thread addf cia402.1.write-all servo-thread addf cia402.2.write-all servo-thread addf cia402.3.write-all servo-thread addf cia402.4.write-all servo-thread addf lcec.write-all servo-thread ######################################### # Net pins ######################################### net x-statusword lcec.0.0.cia-statusword => cia402.0.statusword net x-opmode-fb lcec.0.0.opmode-display => cia402.0.opmode-display net x-drv-act-pos lcec.0.0.actual-position => cia402.0.drv-actual-position net x-controlword cia402.0.controlword => lcec.0.0.cia-controlword net x-opmode-cmd cia402.0.opmode => lcec.0.0.opmode net x-drv-target-pos cia402.0.drv-target-position => lcec.0.0.target-position net y-statusword lcec.0.1.cia-statusword => cia402.1.statusword net y-opmode-fb lcec.0.1.opmode-display => cia402.1.opmode-display net y-drv-act-pos lcec.0.1.actual-position => cia402.1.drv-actual-position net y-controlword cia402.1.controlword => lcec.0.1.cia-controlword net y-opmode-cmd cia402.1.opmode => lcec.0.1.opmode net y-drv-target-pos cia402.1.drv-target-position => lcec.0.1.target-position net z-statusword lcec.0.2.cia-statusword => cia402.2.statusword net z-opmode-fb lcec.0.2.opmode-display => cia402.2.opmode-display net z-drv-act-pos lcec.0.2.actual-position => cia402.2.drv-actual-position net z-controlword cia402.2.controlword => lcec.0.2.cia-controlword net z-opmode-cmd cia402.2.opmode => lcec.0.2.opmode net z-drv-target-pos cia402.2.drv-target-position => lcec.0.2.target-position net b-statusword lcec.0.3.cia-statusword => cia402.3.statusword net b-opmode-fb lcec.0.3.opmode-display => cia402.3.opmode-display net b-drv-act-pos lcec.0.3.actual-position => cia402.3.drv-actual-position net b-controlword cia402.3.controlword => lcec.0.3.cia-controlword net b-opmode-cmd cia402.3.opmode => lcec.0.3.opmode net b-drv-target-pos cia402.3.drv-target-position => lcec.0.3.target-position net c-statusword lcec.0.4.cia-statusword => cia402.4.statusword net c-opmode-fb lcec.0.4.opmode-display => cia402.4.opmode-display net c-drv-act-pos lcec.0.4.actual-position => cia402.4.drv-actual-position net c-controlword cia402.4.controlword => lcec.0.4.cia-controlword net c-opmode-cmd cia402.4.opmode => lcec.0.4.opmode net c-drv-target-pos cia402.4.drv-target-position => lcec.0.4.target-position
By now we have finished the initialization of lcec (LinuxCNC-EtherCAT driver) and cia402 as HAL components and connected their pins accordingly. The final step is connecting cia402 pins of each physical axis to joint pins correctly so that motion component can send their target position to physical axis each control cycle other than the simulator and fetch actual physical axis position as feedback.
Also in
xyzbc-trt_cmds.hal
file, find below lines:net J0:enable joint.0.amp-enable-out => J0_pid.enable net J0:pos-cmd joint.0.motor-pos-cmd => J0_pid.command net J0:pos-fb J0_mux.out => J0_mux.in0 J0_switch.cur-pos J0_vel.in joint.0.motor-pos-fb net J1:enable joint.1.amp-enable-out => J1_pid.enable net J1:pos-cmd joint.1.motor-pos-cmd => J1_pid.command net J1:pos-fb J1_mux.out => J1_mux.in0 J1_switch.cur-pos J1_vel.in joint.1.motor-pos-fb net J2:enable joint.2.amp-enable-out => J2_pid.enable net J2:pos-cmd joint.2.motor-pos-cmd => J2_pid.command net J2:pos-fb J2_mux.out => J2_mux.in0 J2_switch.cur-pos J2_vel.in joint.2.motor-pos-fb net J3:enable joint.3.amp-enable-out => J3_pid.enable net J3:pos-cmd joint.3.motor-pos-cmd => J3_pid.command net J3:pos-fb J3_mux.out => J3_mux.in0 J3_switch.cur-pos J3_vel.in joint.3.motor-pos-fb net J4:enable joint.4.amp-enable-out => J4_pid.enable net J4:pos-cmd joint.4.motor-pos-cmd => J4_pid.command net J4:pos-fb J4_mux.out => J4_mux.in0 J4_switch.cur-pos J4_vel.in joint.4.motor-pos-fb
And change them to:
net J0:enable joint.0.amp-enable-out => J0_pid.enable cia402.0.enabl net J0:pos-cmd joint.0.motor-pos-cmd => J0_pid.command cia402.0.pos-cmd net J0:pos-fb cia402.0.pos-fb => J0_mux.in0 J0_switch.cur-pos J0_vel.in joint.0.motor-pos-fb net J1:enable joint.1.amp-enable-out => J1_pid.enable cia402.1.enable net J1:pos-cmd joint.1.motor-pos-cmd => J1_pid.command cia402.1.pos-cmd net J1:pos-fb cia402.1.pos-fb => J1_mux.in0 J1_switch.cur-pos J1_vel.in joint.1.motor-pos-fb net J2:enable joint.2.amp-enable-out => J2_pid.enable cia402.2.enable net J2:pos-cmd joint.2.motor-pos-cmd => J2_pid.command cia402.2.pos-cmd net J2:pos-fb cia402.2.pos-fb => J2_mux.in0 J2_switch.cur-pos J2_vel.in joint.2.motor-pos-fb net J3:enable joint.3.amp-enable-out => J3_pid.enable cia402.3.enable net J3:pos-cmd joint.3.motor-pos-cmd => J3_pid.command cia402.3.pos-cmd net J3:pos-fb cia402.3.pos-fb => J3_mux.in0 J3_switch.cur-pos J3_vel.in joint.3.motor-pos-fb net J4:enable joint.4.amp-enable-out => J4_pid.enable cia402.4.enable net J4:pos-cmd joint.4.motor-pos-cmd => J4_pid.command cia402.4.pos-cmd net J4:pos-fb cia402.4.pos-fb => J4_mux.in0 J4_switch.cur-pos J4_vel.in joint.4.motor-pos-fb
Now five joints J0-J5 in motion are connected to five physical axes. Restart LinuxCNC and execute table rotary tilting xyzbc-trt demo, then we will see that physical axes run following motion in simulator.