Closed Xubunt closed 11 months ago
Manual: https://en.estun.com/static/upload/file/20220719/1658199238719685.pdf
@bnjmnp would appreciate any guidance
I ran minimal example.py
and got the following status code:
1 slaves found and configured
Drive did not reach SAFEOP state
al status code 0x1d (Invalid output configuration)
not all slaves reached SAFEOP state
This was my modified code for minimal example.py
Manual: https://en.estun.com/static/upload/file/20220719/1658199238719685.pdf
@bnjmnp would appreciate any guidance
This site dose not use https, which is strange.
Does the vendor also provide a ESI XML file for this device? There one might find what needs to go into the ed3l_setup()
function to get rid of the "Invalid output configuration" error.
Where you able to operate the device with any other EtherCAT master system?
Here is the manual.
ESTUN_ED3L_V1_00B2-软件.txt Here is the .xml file I got from the vendor. I am not sure I know enough about Ethercat to identify what would need to be added for the correct configuration.
To get a motor running in PV mode you could try adapting your first code with this:
def ed3l_config_func(slave_pos):
global ed3l
map_1c12_bytes = struct.pack('BxH', 1, 0x1600)
ed3l.sdo_write(0x1c12, 0, map_1c12_bytes, True)
map_1c13_bytes = struct.pack('BxH', 1, 0x1A00)
ed3l.sdo_write(0x1c13, 0, map_1c13_bytes, True)
class OutputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('controlword', ctypes.c_int16),
('target_velocity', ctypes.c_int32),
]
class InputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('statusword', ctypes.c_uint16),
('velocity_actual_value', ctypes.c_int32),
('torque_actual_value', ctypes.c_int16),
]
...
if master.state == pysoem.OP_STATE:
output_data = OutputPdo()
output_data.target_velocity = 500 # RPM
for control_cmd in [6, 7, 15]:
output_data.controlword = control_cmd
ed3l.output = bytes(output_data) # that is the actual change of the PDO output data
master.send_processdata()
master.receive_processdata(1_000)
time.sleep(0.01)
try:
while 1:
master.send_processdata()
master.receive_processdata(1_000)
print(convert_input_data(ed3l.input).statusword)
time.sleep(0.01)
except KeyboardInterrupt:
print('stopped')
# zero everything
ed3l.output = bytes(len(ed3l.output))
master.send_processdata()
master.receive_processdata(1_000)
else:
print('failed to got to op state')
...
After running the code with your changes, I get ValueError: Buffer size too small (6 instead of at least 8 bytes)
It seems this is an issue with specifying the size of the bytes when using the struct module?
I did a slight update of my code from the last comment. Not sure if it helps. Could you please post the complete stack trace of the error?
Re-ran and still got same error.
When I run the code while the motor is hooked up to ESView, I get this error:
That is strange, you could try to cut the input PDO struct to 6 byte:
class InputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('statusword', ctypes.c_uint16),
('velocity_actual_value', ctypes.c_int32),
]
According to the ESI file it should have 8 byte.
That got rid of the buffer error, thanks! However, I now have another issue. When I run the code with the process data loop, I get this error:
incorrect wkc
incorrect wkc
incorrect wkc
1560
incorrect wkc
1560
incorrect wkc
1560
incorrect wkc
1560
incorrect wkc
1560
incorrect wkc
1560
When I run the code without the process data loop, I get this error:
failed to got to op state
For reference, the code with the process data loop is in my original post, the first code. The code without the process data loop is below:
import struct
import pysoem
import time
import ctypes
import threading
ed3l = None
class InputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('statusword', ctypes.c_uint16),
('velocity_actual_value', ctypes.c_int32),
]
class OutputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('controlword', ctypes.c_int16),
('target_velocity', ctypes.c_int32),
]
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)
pd_thread_stop_event = threading.Event()
master = pysoem.Master()
master.open('eth1')
actual_wkc = 0
def ed3l_config_func(slave_pos):
global ed3l
map_1c12_bytes = struct.pack('BxH', 1, 0x1600)
ed3l.sdo_write(0x1c12, 0, map_1c12_bytes, True)
map_1c13_bytes = struct.pack('BxH', 1, 0x1A00)
ed3l.sdo_write(0x1c13, 0, map_1c13_bytes, True)
# 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.01)
def main():
global ed3l
master = pysoem.Master()
master.open('eth1') # someting like '\\Device\\NPF_{B4B7A38F-7DEB-43AF-B787B7-EABADE43978EA}' under Windows
if master.config_init() > 0:
ed3l = master.slaves[0]
ed3l.config_func = ed3l_config_func
master.config_map()
if master.state_check(pysoem.SAFEOP_STATE, 50_000) == pysoem.SAFEOP_STATE:
master.state = pysoem.OP_STATE
# proc_thread = threading.Thread(target=processdata_thread)
# proc_thread.start()
#
# master.send_processdata() # this is actually done in the "processdata_thread" - maybe it is not needed here
# master.receive_processdata(
# 2000) # this is actually done in the "processdata_thread" - maybe it is not needed here
master.write_state()
master.state_check(pysoem.OP_STATE, 5_000_000)
if master.state == pysoem.OP_STATE:
output_data = OutputPdo()
output_data.target_velocity = 500 # RPM
for control_cmd in [6, 7, 15]:
output_data.controlword = control_cmd
ed3l.output = bytes(output_data) # that is the actual change of the PDO output data
master.send_processdata()
master.receive_processdata(1_000)
time.sleep(0.01)
try:
while 1:
master.send_processdata()
master.receive_processdata(1_000)
print(convert_input_data(ed3l.input).statusword)
time.sleep(0.01)
except KeyboardInterrupt:
print('stopped')
# zero everything
ed3l.output = bytes(len(ed3l.output))
master.send_processdata()
master.receive_processdata(1_000)
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()
Could be timing issues and maybe the drive you are using checks if process data is coming in precisely. You should also check in the main loop if the drive stays in OP_STATE or falls back to SAFEOP_STATE.
The thing is also that your device needs process data communication running (in parallel) wile going to OP_STATE, thus you cannot really get rid of the processdata_thread().
One thing you could try is, to increase the timeout given to receive_processdata(), maybe 100000 instead of 10000.
I upped to timeout, but still get the error:
Tried adding
I realize the numbers are the statuswords. What I am confused about it that the system is in OP state and the statusword is printing to terminal, but the motor is not moving. And it is because of the Overtime Watchdog error. Not sure if I can add a time.sleep somewhere to help the system catch up with the code or not - otherwise I am unsure of what to do
Looks like your device requires a more rigorous/precise real time system. You could rather try to decrease the sleep of 0.01 after the master.receive_processdata(1_000)
to 0.001 or removing it. You should also remove the print of the statusword, this slows down the loop a lot.
Another thing you could try is to increase the watchdog timeouts with the set_watchdog() function. Check out #71, maybe that helps.
I did a few tweaks to the sleep and the watchdog error is gone. I get no alarms for when I run the script, but now the motor has an overtravel status when I run the script and it does not move. I have the force stop the script otherwise it keeps running. Here is the stack trace:
'
@bnjmnp any guidance is appreciated
'
Hello, I am trying to move my motor using modified code from issue #30. I am using the code below: