pysoem
pysoem copied to clipboard
Failed to go to OP STATE with MR-J4-70TM-LD servo amplifier
Hello @bnjmnp We are trying to set up 4 Mitsubishi HG-KR73 motors controlled by SMC drivers (model LECSN2-T9, AC servo MR-J4 -70TM-LD), equipped with EtherCAT cards (https://www.accs.cz/Files/FA/SERVO/MR-J4-TM_Instr_Manual_EtherCAT.pdf , this is the manual we are using)
We have installed Pysoem on a Raspberry Pi4 and we have tried many different codes. We are able to write and read on the drivers and to put them in Safe Op state, but we never succeeded in movig the motors, neither we have reached the OP state.
For instance, we are trying to run the following code, which we adapted from your one in Issue #30
import pysoem
import ctypes
import time
master = pysoem.Master()
master.open('eth0')
def config_func():
global device
#software position limit
device.sdo_write(0x607D, 1, bytes(ctypes.c_int32(0)))
device.sdo_write(0x607D, 2, bytes(ctypes.c_int32(1000)))
# gear ratio
device.sdo_write(0x6091, 1, bytes(ctypes.c_uint32(1000000)))
#polarity
device.sdo_write(0x607E, 0, bytes(ctypes.c_uint8(192)))
if master.config_init() > 0:
device = master.slaves[0]
device.config_func=config_func()
master.config_map()
if master.state_check(pysoem.SAFEOP_STATE, 50_000) == pysoem.SAFEOP_STATE:
master.state = pysoem.OP_STATE
master.write_state()
master.state_check(pysoem.OP_STATE, 5_000_000)
if master.state == pysoem.OP_STATE:
device.sdo_write(0x6060, 0, bytes(ctypes.c_int8(1)))
device.sdo_write(0x607A, 0, bytes(ctypes.c_int32(100))) #target position
for control_cmd in [6, 7, 15]:
device.sdo_write(0x6040, 0, bytes(ctypes.c_uint16(control_cmd)))
master.send_processdata()
master.receive_processdata(1_000)
time.sleep(0.01)
try:
while 1:
master.send_processdata()
master.receive_processdata(1_000)
time.sleep(0.01)
except KeyboardInterrupt:
print('stopped')
# zero everything
device.output = bytes(len(device.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()
The answer we get is 'failed to got to OP_STATE'
Any help is welcomed.
Hi @EulaliaVives,
I'm sure you already know it but as long as the servo drive dosen't reach OP state, the motor will not move. So first of all you need to figure out why you don't get into OP state. For this you may try to print the "al status code", in order to get more information on why OP state is not reached.
Taken your script just add this print line in the else of the == pysoem.OP_STATE.
if master.state == pysoem.OP_STATE:
...
else:
print('al status code {} ({})'.format(hex(device.al_status), pysoem.al_status_code_to_string(device.al_status)))
print('failed to got to OP_STATE')
Other than that: Some EtherCAT device need a running process data loop prior to kicking the device into OP state. This is the case for the EL1259. In the basic_example.py is an example on how to do this unsing threading. There the _processdata_thread()
is started right before master.write_state()
which tells the all devices to go to OP state.
Hello @bnjmnp
Thank you very much for your answer. We appreciate you rhelp since we are stuck in an apparently silly question. We bought 4 rather expensive motors and we are not able to run them.
We have included the line you suggested
print('al status code {} ({})'.format(hex(device.al_status), pysoem.al_status_code_to_string(device.al_status)))
Once we run the code, the answer we get is :
al status code 0x0(No error)
Failed to got to OP_STATE
So we don't understand what is hapening.
Besides, we have also tried to add the data loop using threading, but we got sintax errors. We are not very good at Python, and we do not understand the way the variable self
works. We copied the definition
def _processdata_thread(self):
while not self._pd_thread_stop_event.is_set():
self._master.send_processdata()
self._actual_wkc = self._master.receive_processdata(10000)
if not self._actual_wkc == self._master.expected_wkc:
print('incorrect wkc')
time.sleep(0.01)
and we included the lines
check_thread = threading.Thread(target=self._check_thread)
check_thread.start()
proc_thread = threading.Thread(target=self._processdata_thread)
proc_thread.start()
# send one valid process data to make outputs in slaves happy
self._master.send_processdata()
self._master.receive_processdata(2000)
and we got
NameError: 'self' is not defined.
Could you please suggest how to write those lines without the self
syntax.
Coming from you original code, it could be solved like this. But I was not able to test it. Also check my comments.
import pysoem
import ctypes
import time
import threading
pd_thread_stop_event = threading.Event()
master = pysoem.Master()
master.open('eth0')
actual_wkc = 0
def config_func():
global device
# software position limit
device.sdo_write(0x607D, 1, bytes(ctypes.c_int32(0)))
device.sdo_write(0x607D, 2, bytes(ctypes.c_int32(1000)))
# gear ratio
device.sdo_write(0x6091, 1, bytes(ctypes.c_uint32(1000000)))
# polarity
device.sdo_write(0x607E, 0, bytes(ctypes.c_uint8(192)))
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)
if master.config_init() > 0:
device = master.slaves[0]
device.config_func=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:
device.sdo_write(0x6060, 0, bytes(ctypes.c_int8(1))) # Modes of operation - Profile position mode (pp)
device.sdo_write(0x607A, 0, bytes(ctypes.c_int32(100))) # Target position
for control_cmd in [6, 7, 15, 31]: # the 31 is necessary to update the "Target position", search you manual for "New set-point" - only required in pp mode
device.sdo_write(0x6040, 0, bytes(ctypes.c_uint16(control_cmd))) # update the controlword
time.sleep(0.2) # you may need to increase this sleep
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(1_000)
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()
Hello @bnjmnp Thank you very much for your answer and for taking your time to help us.
We have tried your code. The first time we run it (after resetting the motors) gives an error. The second time it does not crash but the motor is not moving. This is the screen output:
root@raspberrypi:/home/pi/Desktop/PYTHON# python3 resposta2-bnjmp25-8.py
Traceback (most recent call last):
File "resposta2-bnjmp25-8.py", line 54, in <module>
device.sdo_write(0x6040, 0, bytes(ctypes.c_uint16(control_cmd))) # update the controlword
File "pysoem\pysoem.pyx", line 608, in pysoem.pysoem.CdefSlave.sdo_write
File "pysoem\pysoem.pyx", line 735, in pysoem.pysoem.CdefSlave._raise_exception
Exception: unexpected error, Etype: 1
^CException ignored in: <module 'threading' from '/usr/lib/python3.7/threading.py'>
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 1281, in _shutdown
t.join()
File "/usr/lib/python3.7/threading.py", line 1032, in join
self._wait_for_tstate_lock()
File "/usr/lib/python3.7/threading.py", line 1048, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
root@raspberrypi:/home/pi/Desktop/PYTHON# python3 resposta2-bnjmp25-8.py
^Cstopped
root@raspberrypi:/home/pi/Desktop/PYTHON#
don't no if it is related to this but the last control_cmd must be a 31 instead of 32, I'll fix my code from above
The change to 31 does not work.
Maybe, the problem is related to other control_cmd values.
Usually when the motor is ready to start it gets powered by voltage and makes a characteristic noise, even if it is not moving. In this case, this is not happening
The driver has a display that, in all the cases ends in a message alternating n00 and 86.1. According to the manual this message might mean many different things: network module disconnected, network cable disconnected,error exist in controller setting, etc,,,
One good thing about your code is that it seems that we are truly in OP_State. There is a green led in the driver that before was always blinking and now, with your code, while we are in the "try" loop, is not blinking anymore.
Dear @bnjmnp
Our motors are still not moving. We have tried many things:
-
Increasing the time.sleep (0.2) in the control_cmd loop does not help
-
We have checked that, strictly speaking, the code is never able to write the control_cmd=7
-
It is true that it suceeds in writting control_cmd=6, but if we include a time.sleep, in any place before the loop, then it also fails to write control_cmd=6
-
Supressing the two lines master.send_processdata and master.receive_processdata does not make any difference
-
Independently, in all casses, an error message appears in the small display of the driver. And, the error seems to occur after the master.state_check(pysoem.OP_STATE, 5_000_000) command.
Any idea?
Is there a way to communicate with you exchanging our screen or even using a camera so that you can see what is actually happening in the motors? Do you know how can we obtain help (pysoem+raspberry+mitsubishi motors)? We are willing to spend some money for technical consulting.
Thanks a lot.
I don't have any idea for now. In the for control_cmd in [6, 7, 15, 32]:
loop you could add a print of the Statusword:
print(hex(int.from_bytes(device.sdo_read(0x6041, 0), byteorder='little', signed=False)))
So instead of the 0.2 sleep, you could print the Statusword 10 times in a loop plus wait 0.02 each cycle. Maybe this gives you a hint on what is wrong.
Did you actuall try to get this working using TwinCAT?
You can reach out to me on gitter: https://gitter.im/bnjmnp just using your GitHub login.
The options I see were you could get further help:
- Use TwinCAT and optionally ask the Mitsubishi support for help (I guess all device vendors should be able help you if you use TwinCAT) and then apply that knowledge to your Python script.
- No sure about that, but I think the maintainers of SOEM are somehow related to https://rt-labs.com, there you could get help in exchange for money. But they might only give you C code that uses SOEM.
- Try to ask the ETG for help, they might provide you with alternative technical consulting companies.
- Or write C code for SOEM yourself and create an issue SOEM GitHub, where you ask for help.
Closed due to inactivity of the OP.