bnjmnp / pysoem

Cython wrapper for the Simple Open EtherCAT Master Library
MIT License
96 stars 37 forks source link

Motion Control Leadshine CS3E D1008 #76

Closed DHavron closed 2 years ago

DHavron commented 2 years ago

Hey, I am trying to drive a Leadshine CS3E D1008. I have already tried with both SDO and PDO. With the help of SDO, I was able to write data to the controller. I am using this code:

    import pysoem
    import ctypes
    import time
    import threading

    pd_thread_stop_event = threading.Event()
    master = pysoem.Master()
    master.open('NPF_{04128CF2-766E-4511-8571-9B0F477AEB1B}')
    actual_wkc = 0

    def config_func():
        global device
        device.sdo_write(0x607D, 1, bytes(ctypes.c_int32(0)))
        device.sdo_write(0x607D, 2, bytes(ctypes.c_int32(100000)))

    def processdata_thread():
        global master  # not sure if this is necessary
        global pd_thread_stop_event  # not sure if this is necessary
        global actual_wkc  # not sure if this is necessary
        while not pd_thread_stop_event.is_set():
            master.send_processdata()
            actual_wkc = master.receive_processdata(10000)
            if not actual_wkc == master.expected_wkc:
                print('incorrect wkc')
            time.sleep(0.02)

    if master.config_init() > 0:
        device = master.slaves[0]
        device.config_func = config_func()
        master.config_map()

        if master.state_check(pysoem.SAFEOP_STATE, 500000) == pysoem.SAFEOP_STATE:

            master.state = pysoem.OP_STATE

            proc_thread = threading.Thread(target=processdata_thread)
            proc_thread.start()

            # this is actually done in the "processdata_thread" - maybe it is not needed here
            master.send_processdata()
            # this is actually done in the "processdata_thread" - maybe it is not needed here
            master.receive_processdata(10000)

            master.write_state()
            master.state_check(pysoem.OP_STATE, 5000000)
            print('al status code {} ({})'.format(hex(device.al_status),
                                                  pysoem.al_status_code_to_string(device.al_status)))
            if master.state == pysoem.OP_STATE:
                # Modes of operation - Profile position mode (pp)
                device.sdo_write(0x6060, 0, bytes(ctypes.c_int8(1)))
                device.sdo_write(0x607A, 0, bytes(
                    ctypes.c_int32(1000)))  # Target position

                for control_cmd in [0, 6, 7, 15]:
                    # update the controlword
                    device.sdo_write(0x6040, 0, bytes(
                        ctypes.c_uint16(control_cmd)))
                    # print(int.from_bytes(device.sdo_read(0x6041, 0), byteorder='little', signed=False))
                    print(ctypes.c_uint16.from_buffer_copy(
                        device.sdo_read(0x6041, 0)).value)
                    time.sleep(0.01)

                try:
                    while 1:
                        time.sleep(0.01)

                except KeyboardInterrupt:
                    print('stopped')

                # zero everything
                device.output = bytes(len(device.output))
                master.send_processdata()
                master.receive_processdata(10000)

                pd_thread_stop_event.set()
                proc_thread.join()

            else:
                print('failed to got to OP_STATE')

        else:
            print('failed to got to safeop state')
        master.state = pysoem.PREOP_STATE
        master.write_state()
    else:
        print('no device found')

    master.close()

I get an answer: 592 561 624 624

when using this code:

    import pysoem
    import time
    import ctypes

    device = None

    class InputPdo(ctypes.Structure): #Tx
        _pack_ = 1
        _fields_ = [
            ('modes_of_operation_display', ctypes.c_int8),
            ('status_word', ctypes.c_uint16),
            ('position_demand_value', ctypes.c_int32),
            ('position_actual_value', ctypes.c_int32),
            ('velocity_demand_value', ctypes.c_int32),
            ('velocity_actual_value', ctypes.c_int32),
            ('torque_demand_value', ctypes.c_int32),
            ('torque_actual_value', ctypes.c_int32),
            ('digital_input', ctypes.c_uint32),
        ]

    class OutputPdo(ctypes.Structure): #Rx
        _pack_ = 1
        _fields_ = [
            ('modes_of_operation', ctypes.c_int8),
            ('control_word', ctypes.c_uint16),
            ('profile_target_position', ctypes.c_int32),
            ('target_velocity', ctypes.c_int32),
            ('target_torque', ctypes.c_int32),
            ('digital_output', ctypes.c_uint32),
        ]

    modes_of_operation = {
        'No mode': 0,
        'Profile position mode': 1,
        'Profile velocity mode': 3,
        'Homing mode': 6,
        'Cyclic synchronous position mode': 8,
        'Cyclic synchronous velocity mode': 9,
        'Cyclic synchronous torque mode': 10,
    }

    def convert_input_data(data):
        return InputPdo.from_buffer_copy(data)

    def main():
        global device
        master = pysoem.Master()
        master.open('NPF_{04128CF2-766E-4511-8571-9B0F477AEB1B}')
        if master.config_init() > 0:
            device = master.slaves[0]
            master.config_map()
            if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE:
                master.state = pysoem.OP_STATE
                master.write_state()
                master.state_check(pysoem.OP_STATE, 5000000)
                if master.state == pysoem.OP_STATE:
                    output_data = OutputPdo()
                    inputdata = InputPdo()
                    output_data.modes_of_operation = modes_of_operation['Profile velocity mode']
                    output_data.target_velocity = 500
                    output_data.profile_target_position = 100000
                    for control_cmd in [0,6,7,15]:
                        output_data.control_word = control_cmd
                        device.output = bytes(output_data)  
                        master.send_processdata()
                        master.receive_processdata(1000)
                        time.sleep(0.01)
                        # print(ctypes.c_uint16.from_buffer_copy(device.sdo_read(0x6041, 0)).value)
                        print(int.from_bytes(device.sdo_read(0x6041, 0), byteorder='little', signed=False))
                        # print(device.sdo_read(0x6041, 0))

                    try:
                        while 1:
                            master.send_processdata()
                            master.receive_processdata(1000)
                            time.sleep(0.01)

                    except KeyboardInterrupt:
                        print('stopped')
                    device.output = bytes(len(device.output))
                    master.send_processdata()
                    master.receive_processdata(1000)
                else:
                    print('failed to got to op state')
            else:
                print('failed to got to safeop state')
            master.state = pysoem.PREOP_STATE
            master.write_state()
        else:
            print('no device found')
        master.close()

    if __name__ == '__main__':
        main()

I get an answer: 592 592 592 592

motor does not rotate

bnjmnp commented 2 years ago

Hi, at least the status word looks familiar, so in general it dose not look too bad. I guess you barely get out of the "Switch On Disabled" state, are there some stop switches you may need to bride or to deactivate in software?

For the position mode you probably need also a transition from 15 to 31 for every target position update.

DHavron commented 2 years ago

problem solved.


import pysoem
import ctypes
import time

master = pysoem.Master()
master.open('NPF_{180CCD7E-236F-4835-98AE-A40535A0EBA0}')

def config_func():
    device.sdo_write(0x214B, 0, bytes(ctypes.c_int16(0)))
    device.sdo_write(0x6060, 0, bytes(ctypes.c_int8(1)))

if master.config_init() > 0:
    device = master.slaves[0]
    device.config_func = config_func()
    master.config_map()

    if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE:
        master.state = pysoem.OP_STATE
        master.write_state()
        master.state_check(pysoem.OP_STATE, 50000)
        print('al status code {} ({})'.format(hex(device.al_status),pysoem.al_status_code_to_string(device.al_status)))
        if master.state == pysoem.OP_STATE:
            device.sdo_write(0x60FF, 0, bytes(ctypes.c_int32(200))) #target_velocity
            device.sdo_write(0x607A, 0, bytes(ctypes.c_int32(10000))) #profile_target_position

            for control_cmd in [0, 6, 7,15,31]:
                device.sdo_write(0x6040, 0, bytes(ctypes.c_uint16(control_cmd)))
                print(ctypes.c_uint16.from_buffer_copy(device.sdo_read(0x6041, 0)).value)
            try:

                while 1:
                    time.sleep(.01)
            except KeyboardInterrupt:
                print('stopped')

            device.output = bytes(len(device.output))

        else:
            print('failed to got to OP_STATE')

    else:
        print('failed to got to safeop state')
    master.state = pysoem.PREOP_STATE
    master.write_state()
else:
    print('no device found')

master.close()