Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Servo PDO communication problem #157

Open
ssusardnz opened this issue Sep 24, 2024 · 5 comments
Open

Servo PDO communication problem #157

ssusardnz opened this issue Sep 24, 2024 · 5 comments
Labels
help wanted Extra attention is needed

Comments

@ssusardnz
Copy link

I am new about python and ethercat. I use delta A2 servo.

https://www.deltaww.com/en-US/products/Servo-Systems-AC-Servo-Motors-and-Drives/23

Servo only run "DC_synchronous" mode. I managed to change state of servo to OP mode.
I want to read position of motor.
But read TxPDO data from servo always 0.

Code is here.

import time
import pysoem
import ctypes
import struct

from apscheduler.schedulers.background import BackgroundScheduler

master = pysoem.Master()
eth_ID =  '\\Device\\NPF_{0C56BDEC-CD3C-4041-B243-8505216C48B9}'
master.open(eth_ID)

if master.config_init() > 0:
    for device in master.slaves:
        print(f'Found Device {device.name}')


def startup(slv):
    slv.sdo_write(0x1C12, 0, bytes(ctypes.c_uint8(0x00)))  # clear sm pdos (0x1C12)
    slv.sdo_write(0x1C13, 0, bytes(ctypes.c_uint8(0x00)))  # clear sm pdos (0x1C13)

    slv.sdo_write(0x1A00, 0, bytes(ctypes.c_uint8(0x00)))           # clear pdo 0x1A00 entries
    slv.sdo_write(0x1A00, 1, bytes(ctypes.c_uint32(0x60410010)))    # download pdo 0x1A00 entry
    slv.sdo_write(0x1A00, 2, bytes(ctypes.c_uint32(0x60640020)))    # download pdo 0x1A00 entry
    slv.sdo_write(0x1A00, 3, bytes(ctypes.c_uint32(0x606C0020)))    # download pdo 0x1A00 entry
    slv.sdo_write(0x1A00, 4, bytes(ctypes.c_uint32(0x60770010)))    # download pdo 0x1A00 entry
    slv.sdo_write(0x1A00, 5, bytes(ctypes.c_uint32(0x60610008)))    # download pdo 0x1A00 entry
    slv.sdo_write(0x1A00, 0, bytes(ctypes.c_uint8(0x05)))           # download pdo 0x1A00 entry count

    slv.sdo_write(0x1A01, 0, bytes(ctypes.c_uint8(0x00)))           # clear pdo 0x1A01 entries
    slv.sdo_write(0x1A01, 1, bytes(ctypes.c_uint32(0x60410010)))    # download pdo 0x1A01 entry
    slv.sdo_write(0x1A01, 2, bytes(ctypes.c_uint32(0x60640020)))    # download pdo 0x1A01 entry
    slv.sdo_write(0x1A01, 0, bytes(ctypes.c_uint8(0x02)))           # download pdo 0x1A01 entry count

    slv.sdo_write(0x1A02, 0, bytes(ctypes.c_uint8(0x00)))
    slv.sdo_write(0x1A02, 1, bytes(ctypes.c_uint32(0x60410010)))
    slv.sdo_write(0x1A02, 2, bytes(ctypes.c_uint32(0x60640020)))
    slv.sdo_write(0x1A02, 3, bytes(ctypes.c_uint32(0x606C0020)))
    slv.sdo_write(0x1A02, 0, bytes(ctypes.c_uint8(0x03)))

    slv.sdo_write(0x1A03, 0, bytes(ctypes.c_uint8(0x00)))
    slv.sdo_write(0x1A03, 1, bytes(ctypes.c_uint32(0x60410010)))
    slv.sdo_write(0x1A03, 2, bytes(ctypes.c_uint32(0x60640020)))
    slv.sdo_write(0x1A03, 3, bytes(ctypes.c_uint32(0x60770010)))
    slv.sdo_write(0x1A03, 0, bytes(ctypes.c_uint8(0x03)))

    slv.sdo_write(0x1600, 0, bytes(ctypes.c_uint8(0x00)))
    slv.sdo_write(0x1600, 1, bytes(ctypes.c_uint32(0x60400010)))
    slv.sdo_write(0x1600, 2, bytes(ctypes.c_uint32(0x607A0020)))
    slv.sdo_write(0x1600, 3, bytes(ctypes.c_uint32(0x60FF0020)))
    slv.sdo_write(0x1600, 4, bytes(ctypes.c_uint32(0x60710010)))
    slv.sdo_write(0x1600, 5, bytes(ctypes.c_uint32(0x60600008)))
    slv.sdo_write(0x1600, 0, bytes(ctypes.c_uint8(0x05)))

    slv.sdo_write(0x1601, 0, bytes(ctypes.c_uint8(0x00)))
    slv.sdo_write(0x1601, 1, bytes(ctypes.c_uint32(0x60400010)))
    slv.sdo_write(0x1601, 2, bytes(ctypes.c_uint32(0x607A0020)))
    slv.sdo_write(0x1601, 0, bytes(ctypes.c_uint8(0x02)))

    slv.sdo_write(0x1602, 0, bytes(ctypes.c_uint8(0x00)))
    slv.sdo_write(0x1602, 1, bytes(ctypes.c_uint32(0x60400010)))
    slv.sdo_write(0x1602, 2, bytes(ctypes.c_uint32(0x60FF0020)))
    slv.sdo_write(0x1602, 0, bytes(ctypes.c_uint8(0x02)))

    slv.sdo_write(0x1603, 0, bytes(ctypes.c_uint8(0x00)))
    slv.sdo_write(0x1603, 1, bytes(ctypes.c_uint32(0x60400010)))
    slv.sdo_write(0x1603, 2, bytes(ctypes.c_uint32(0x60710010)))
    slv.sdo_write(0x1603, 0, bytes(ctypes.c_uint8(0x02)))

    slv.sdo_write(0x1C12, 1, bytes(ctypes.c_uint16(0x1601)))
    slv.sdo_write(0x1C12, 0, bytes(ctypes.c_uint8(0x01)))

    slv.sdo_write(0x1C13, 1, bytes(ctypes.c_uint16(0x1A01)))
    slv.sdo_write(0x1C13, 0, bytes(ctypes.c_uint8(0x01)))

    slv.sdo_write(0x6060, 0, bytes(ctypes.c_int8(8)))
    slv.sdo_write(0x60C2, 1, bytes(ctypes.c_uint8(Interpolation_time_period)))
    slv.sdo_write(0x60C2, 2, bytes(ctypes.c_int8(-3)))

def state_change(target_state):
    timeout = 10000000
    master.state = target_state
    master.write_state()
    read_state = master.state_check(target_state,timeout)
    time.sleep(0.5)
    if read_state == target_state:
        return True, read_state
    else:
        return False, read_state

def to_init_state():
    degisti, read_state = state_change(pysoem.INIT_STATE)
    if degisti == True:
        set_mailbox()

def set_mailbox():
    servo.amend_mbx(mailbox='out', start_address=0x1000, size=0x0080)
    servo.amend_mbx(mailbox='in', start_address=0x10C0, size=0x0080)

    sm0_mail_out_data = struct.pack('Q', 0x0001003600801000)
    servo._fpwr(0x0800, sm0_mail_out_data)

    sm1_mail_in_data = struct.pack('Q', 0x00010032008010C0)
    servo._fpwr(0x0808, sm1_mail_in_data)


    fmmu2_out_data = struct.pack('QQ', 0x000000010100080D, 0x0000000109000000)
    servo._fpwr(0x0620, fmmu2_out_data)

    from_init_to_preop()

def from_init_to_preop():
    degisti, read_state = state_change(pysoem.PREOP_STATE)
    if degisti == True:
        set_in_out_box()


def set_in_out_box():
    sync_cycle: int = Interpolation_time_period * 1000000
    servo.dc_sync(act=True, sync0_cycle_time=sync_cycle, sync0_shift_time=0, sync1_cycle_time=None)

    master.config_dc()
    master.config_map()

    sm2_out_data = struct.pack('Q', 0x0001002400061180)
    servo._fpwr(0x0810, sm2_out_data)

    sm3_in_data = struct.pack('Q', 0x0001000000061480)
    servo._fpwr(0x0818, sm3_in_data)

    fmmu0_out_data = struct.pack('QQ', 0x0000000102001180, 0x0700000601000000)
    servo._fpwr(0x0600, fmmu0_out_data)

    fmmu1_out_data = struct.pack('QQ', 0x0000000101001480, 0x0700000601000000)
    servo._fpwr(0x0610, fmmu1_out_data)


    from_preop_to_safeop()

def from_preop_to_safeop():
    degisti, read_state = state_change(pysoem.SAFEOP_STATE)
    if degisti == True:
        from_safeop_to_op()
        thread_calis()
        time.sleep(1)

def from_safeop_to_op():
    global send_basla
    degisti, read_state = state_change(pysoem.OP_STATE)
    if degisti == True:
        send_basla = True
        d =5


def gecen_sureyi_yaz(yaz):
    global onceki_time
    gecen_sure = time.perf_counter() - onceki_time
    onceki_time = time.perf_counter()
    print(gecen_sure*1000   ,   yaz)


def thread_calis():
    sched = BackgroundScheduler()
    sched.add_job(process_data, 'interval', seconds=ETH_task_periyod)
    sched.add_job(plc, 'interval', seconds=plc_periyod)
    sched.start()


def process_data():
    global send_basla
    if send_basla == True:
        master.send_processdata()
    master.receive_processdata(10000)
    if debug == True:
        gecen_sureyi_yaz(1 * 1000)

def plc():
    global a
    a = a + 1

    c = servo.input
    data = struct.unpack('iH', c)
    t = servo.output
    d= c + data



debug = True
send_basla = False

servo = master.slaves[0]

time.sleep(0.1)
PUU_to_puls_carpani = 1280000

Interpolation_time_period = 2 #ms
ETH_task_periyod = Interpolation_time_period/1000


onceki_time = 0.0

#ethercat_task_ms = 10
#ETH_task_periyod = ethercat_task_ms/1000

plc_task_ms = 1000
plc_periyod = plc_task_ms/1000


#startup(servo)
to_init_state()
a = 6

while True:
    time.sleep(1)
@bnjmnp
Copy link
Owner

bnjmnp commented Oct 21, 2024

Hi @ssusardnz. Did you get any further on this? I was not able to support lately.

@ssusardnz
Copy link
Author

Yes. master.config_map() command make some changes in servo FMMU settings. Physical start address is set "0x0000" after command. But this address is servo device information register, I cant use this address.

Before config_map() command,
image

Aftrer config_map() command,
image

@bnjmnp
Copy link
Owner

bnjmnp commented Oct 22, 2024

Interesting, I never heard about something like this before. Do you think this is a problem on the PySOEM/SOEM side? Or is this a problem with the servo drive?

@ssusardnz
Copy link
Author

I can use and control the servo with beckhoff twincat and codesys. It works normally.
I dont have much experience with ethercat and pysoem. May can be, I couldnt configure code correctly.
Is there any sample project which controlled with DC_synchronous mode?

@bnjmnp
Copy link
Owner

bnjmnp commented Oct 27, 2024

In some issues, there are code examples on how to operate a servo drive that is based on DS402 drive profile:

The basic_example.py demonstrates how DC is enabled.

@bnjmnp bnjmnp added the help wanted Extra attention is needed label Oct 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants