Code Monkey home page Code Monkey logo

Comments (20)

tom9672 avatar tom9672 commented on August 16, 2024

Hi @bnjmnp
I find my ESI esi , could you please have a look.
And give a guidance about how to edit my inputPDO and outPDO.

best regards
Tom

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024
                                    <RxPdo Fixed="0" Sm="2">
					<Index>#x1600</Index>
					<Name>RPDO</Name>
					<Entry>
						<Index>#x6040</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>Controlword</Name>
						<DataType>UINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6060</Index>
						<SubIndex>0</SubIndex>
						<BitLen>8</BitLen>
						<Name>ModeOperation</Name>
						<DataType>SINT</DataType>
					</Entry>
					<Entry>
						<Index>#x0000</Index>
						<SubIndex>0</SubIndex>
						<BitLen>8</BitLen>
						<Name>Complement</Name>
						<DataType>SINT</DataType>
					</Entry>
					<Entry>
						<Index>#x607A</Index>
						<SubIndex>0</SubIndex>
						<BitLen>32</BitLen>
						<Name>TargetPosition</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6042</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>VITargVelocity</Name>
						<DataType>INT</DataType>
					</Entry>
					<Entry>
						<Index>#x6071</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>TargetTorque</Name>
						<DataType>INT</DataType>
					</Entry>
					<Entry>
						<Index>#x60C1</Index>
						<SubIndex>1</SubIndex>
						<BitLen>32</BitLen>
						<Name>FstSetpoint</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x60FF</Index>
						<SubIndex>0</SubIndex>
						<BitLen>32</BitLen>
						<Name>TargetVelocity</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x2031</Index>
						<SubIndex>02</SubIndex>
						<BitLen>16</BitLen>
						<Name>DoStateCommSet</Name>
						<DataType>UINT</DataType>
					</Entry>
				</RxPdo>

outputPDO

class OutputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('Controlword', ctypes.c_uint16),
        ('ModeOperation', ctypes.c_int8),
        ('Complement', ctypes.c_int8),
        ('TargetPosition', ctypes.c_int32),
        ('VITargVelocity', ctypes.c_int16),
        ('TargetTorque', ctypes.c_int16),
        ('FstSetpoint', ctypes.c_int32),
        ('TargetVelocity', ctypes.c_int32),
        ('DoStateCommSet', ctypes.c_uint16),
    ]
				<TxPdo Fixed="0" Sm="3">
					<Index>#x1a00</Index>
					<Name>TPDO0</Name>
					<Entry>
						<Index>#x603F</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>ErrorCode</Name>
						<DataType>UINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6041</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>Statusword</Name>
						<DataType>UINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6061</Index>
						<SubIndex>0</SubIndex>
						<BitLen>8</BitLen>
						<Name>Modes of operation display</Name>
						<DataType>SINT</DataType>
					</Entry>
					<Entry>
						<Index>#x0000</Index>
						<SubIndex>0</SubIndex>
						<BitLen>8</BitLen>
						<Name>Complement</Name>
						<DataType>SINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6064</Index>
						<SubIndex>0</SubIndex>
						<BitLen>32</BitLen>
						<Name>Position actual value</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6063</Index>
						<SubIndex>0</SubIndex>
						<BitLen>32</BitLen>
						<Name>PosActualValue</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6069</Index>
						<SubIndex>0</SubIndex>
						<BitLen>32</BitLen>
						<Name>VlSensorActualValu</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x606C</Index>
						<SubIndex>0</SubIndex>
						<BitLen>32</BitLen>
						<Name>VlActualValue</Name>
						<DataType>DINT</DataType>
					</Entry>
					<Entry>
						<Index>#x6077</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>TorqActualValu</Name>
						<DataType>INT</DataType>
					</Entry>
					<Entry>
						<Index>#x6078</Index>
						<SubIndex>0</SubIndex>
						<BitLen>16</BitLen>
						<Name>CurentActlValu</Name>
						<DataType>INT</DataType>
					</Entry>
					<Entry>
						<Index>#x200B</Index>
						<SubIndex>05</SubIndex>
						<BitLen>16</BitLen>
						<Name>MonitoredDiStates</Name>
						<DataType>UINT</DataType>
					</Entry>
				</TxPdo>

inputPDO

class InputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('ErrorCode', ctypes.c_uint16),
        ('Statusword', ctypes.c_uint16),
        ('Modes_of_operation_display', ctypes.c_int8),
        ('Complement', ctypes.c_int8),
        ('Position_actual_value', ctypes.c_int32),
        ('PosActualValue', ctypes.c_int32),
        ('VlSensorActualValu', ctypes.c_int32),
        ('VlActualValue', ctypes.c_int32),
        ('TorqActualValu', ctypes.c_int16),
        ('CurentActlValu', ctypes.c_int16),
        ('MonitoredDiStates', ctypes.c_uint16),
    ]
				<Dc>
					<OpMode>
						<Name>Synchron</Name>
						<Desc>SM-Synchron</Desc>
						<AssignActivate>#x0</AssignActivate>
						<CycleTimeSync0 Factor="1">0</CycleTimeSync0>
						<ShiftTimeSync0>0</ShiftTimeSync0>
						<CycleTimeSync1 Factor="1">0</CycleTimeSync1>
					</OpMode>
					<OpMode>
						<Name>DC</Name>
						<Desc>DC-Synchron</Desc>
						<AssignActivate>#x300</AssignActivate>
						<CycleTimeSync0 Factor="1">0</CycleTimeSync0>
						<ShiftTimeSync0>0</ShiftTimeSync0>
						<CycleTimeSync1 Factor="1">0</CycleTimeSync1>
					</OpMode>
				</Dc>

Could you have a look @bnjmnp , Is it right? I follow the esi, copy the name and datatype field.

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Now my code and its prints.

import pysoem
import time
import ctypes
import sys

pmm60 = None


class OutputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('Controlword', ctypes.c_uint16),
        ('ModeOperation', ctypes.c_int8),
        ('Complement', ctypes.c_int8),
        ('TargetPosition', ctypes.c_int32),
        ('VITargVelocity', ctypes.c_int16),
        ('TargetTorque', ctypes.c_int16),
        ('FstSetpoint', ctypes.c_int32),
        ('TargetVelocity', ctypes.c_int32),
        ('DoStateCommSet', ctypes.c_uint16),
    ]



class InputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('ErrorCode', ctypes.c_uint16),
        ('Statusword', ctypes.c_uint16),
        ('Modes_of_operation_display', ctypes.c_int8),
        ('Complement', ctypes.c_int8),
        ('Position_actual_value', ctypes.c_int32),
        ('PosActualValue', ctypes.c_int32),
        ('VlSensorActualValu', ctypes.c_int32),
        ('VlActualValue', ctypes.c_int32),
        ('TorqActualValu', ctypes.c_int16),
        ('CurentActlValu', ctypes.c_int16),
        ('MonitoredDiStates', ctypes.c_uint16),
    ]




modes_of_operation = {
    'No mode': 0,
    'Profile position mode': 1,
    'Velocity mode':2,
    '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 pmm60_config_func(slave_pos):
    global pmm60
    # All default config


def main():
    global pmm60
    master = pysoem.Master()
    master.open(sys.argv[1])  
    if master.config_init() > 0:
        pmm60 = master.slaves[0]
        pmm60.config_func = pmm60_config_func
        master.config_map()
        if master.state_check(pysoem.SAFEOP_STATE, 50_000) == pysoem.SAFEOP_STATE:
            master.state = pysoem.OP_STATE
            
            master.send_processdata()
            master.receive_processdata(1_000)
            
            master.write_state()
            master.state_check(pysoem.OP_STATE, 5_000_000)
            if master.state == pysoem.OP_STATE:
                print('IN OP STATE')
                output_data = OutputPdo()
                output_data.modes_of_operation = modes_of_operation['Profile velocity mode']
                output_data.TargetVelocity = 500 
                for control_cmd in [6, 7, 15]:
                    output_data.controlword = control_cmd
                    pmm60.output = bytes(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(
                              'Statusword',convert_input_data(pmm60.input).Statusword,
                              'TorqActualValu',convert_input_data(pmm60.input).TorqActualValu,
                              'CurentActlValu',convert_input_data(pmm60.input).CurentActlValu)
                        time.sleep(0.01)
                except KeyboardInterrupt:
                    print('stopped')
                # zero everything
                pmm60.output = bytes(len(pmm60.output))
                master.send_processdata()
                master.receive_processdata(1_000)
            else:
                print('al status code {} ({})'.format(hex(pmm60.al_status), pysoem.al_status_code_to_string(pmm60.al_status)))
                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()





pi@raspberrypi:~ $ sudo python3 Desktop/1018/pdo.py eth0
IN OP STATE
Statusword 608 TorqActualValu 1 CurentActlValu 1
Statusword 608 TorqActualValu -1 CurentActlValu -1
Statusword 608 TorqActualValu 1 CurentActlValu 1
Statusword 608 TorqActualValu 0 CurentActlValu 0
Statusword 608 TorqActualValu 2 CurentActlValu 2
Statusword 608 TorqActualValu 3 CurentActlValu 3
Statusword 608 TorqActualValu 0 CurentActlValu 0
Statusword 608 TorqActualValu 5 CurentActlValu 5
Statusword 608 TorqActualValu 0 CurentActlValu 0
Statusword 608 TorqActualValu 0 CurentActlValu 0
Statusword 608 TorqActualValu 2 CurentActlValu 2
Statusword 608 TorqActualValu 0 CurentActlValu 0
Statusword 608 TorqActualValu 1 CurentActlValu 1
Statusword 608 TorqActualValu 4 CurentActlValu 4
Statusword 608 TorqActualValu 3 CurentActlValu 3

The ErrorCode, Complement, VlSensorActualValu, VlActualValue are all 0, and the position is a fixed value. So, the motor not move at all.

Could someone let me know how to let my device move...
@bnjmnp @zchen24 @galch @JeremyShubert
Thank you so much!
best regards
Tom

from pysoem.

bnjmnp avatar bnjmnp commented on August 16, 2024

Looks all good at the first glance, tough I cannot find a user manual for this drive on the net.
Maybe you need to deactivate limit switches.

from pysoem.

bnjmnp avatar bnjmnp commented on August 16, 2024

You may also check the meaning of bits in the "Statusword", and compare this with the 608 you get.

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Hi @bnjmnp

Hope you have a good day.

I still can't make my device to work...

Statusword            /PDS FSA state
xxxx xxxx x0xx 0000b /Not ready to switch on
xxxx xxxx x1xx 0000b /Switch on disabled
xxxx xxxx x01x 0001b /Ready to switch on
xxxx xxxx x01x 0011b /Switched on
xxxx xxxx x01x 0111b /Operation enabled
xxxx xxxx x00x 0111b /Quick stop active
xxxx xxxx x0xx 1111b /Fault reaction active
xxxx xxxx x0xx 1000b /Fault
15 13  11 10  9   8  7  6    5   4    3  2    1
14 12
ms oms ila tr rm ms w sod qs ve f oe so rtso
MSB 
LSB


0 ready to switch on
1 switched on
2 operation enabled
3 fault
4 voltage enabled
5 quick stop
6 switch on disabled
7 warning
8 manufacturer-specific
9 remote
10 target reached
11 internal limit active
12-13 operation mode specific
14-15 manufacturer-specific

not sure why, I run the above code on a windows pc, the 'statusword' now became 0. Is the system delay (may over 20ms or more) will make the pdo unable run well, lead to the motor can't move.

Another question is should i follow the exact order of the esi file to write the output and input?

My code still can't make the motor move. but I tried to test the device with a software the manufacturers gave me.
The device works well on this software..
It have a db file to config the params, including supply voltage, max speed, max torque....
the db file like this:

  "ParamNO" varchar (10) NOT NULL,
  "GroupNO" varchar (10) NOT NULL,
  "CANopenIndex" varchar(10),
  "ParamIndex" VARCHAR (20),
  "ParamName" varchar (20) NOT NULL,
  "CurValue" int (11) DEFAULT NULL,
  "DefValue" int (11) DEFAULT NULL,
  "MinValue" int (11) DEFAULT NULL,
  "MaxValue" int (11) DEFAULT NULL,
  "Unit" varchar (10) DEFAULT NULL,
  "BitWidth" int (11) DEFAULT NULL,
  "HasSign" int (11) DEFAULT NULL,
  "IsPublic" INT (11) DEFAULT (1),
  "InputType" int (11) DEFAULT NULL,
  "AccessType" int (11) DEFAULT NULL,
  "EffectTime" int (11) DEFAULT NULL,
  "PDOMapping" int(11) DEFAULT 0,
  "SelectNO" varchar(10),
  PRIMARY KEY ("ParamNO"),
  UNIQUE ("ParamIndex" ASC)

and a json file to config the pdo mapping:

{
    "RPDO1": {
        "Enabled": true,
        "Map Param": [
            "I6040",
            "I6060",
			"I0000",
            "I607A",
            "I6042",
            "I6071",
            "I60C1-1",
            "I60FF",
            "I6072",
            "I607F",
            "I6080"
        ]
    },
    "TPDO1": {
        "Enabled": true,
        "Map Param": [
            "I606B",
            "I6041",
            "I6061",
			"I0000",
            "I60FC",
            "I6063",
            "I6069",
            "I606C",
            "I6077",
            "I6078"
        ]
    }
}

I find the items and order of this is slight different with the rxpdo1 and txpdo1 in ESI file.

I changed my code:

import pysoem
import time
import ctypes

pmm60 = None


class OutputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ =[
        ('Control_Word',ctypes.c_uint16),
        ('Mode_Operation',ctypes.c_int8),
        ('Complement',ctypes.c_int8),
        ('Target_Position',ctypes.c_int32),
        ('Vi_Target_V',ctypes.c_int16),
        ('Target_Torque',ctypes.c_int16),
        ('Tnterpolation_Data_Record',ctypes.c_int32),
        ('Target_Velocity',ctypes.c_int32),
        ('Max_Torque',ctypes.c_uint16),
        ('Max_Profile_Velocity',ctypes.c_uint32),
        ('Max_Motor_Speed',ctypes.c_uint32)
    ]
    # _fields_ = [
    #     ('Controlword', ctypes.c_uint16),
    #     ('ModeOperation', ctypes.c_int8),
    #     ('Complement', ctypes.c_int8),
    #     ('TargetPosition', ctypes.c_int32),
    #     ('VITargVelocity', ctypes.c_int16),
    #     ('TargetTorque', ctypes.c_int16),
    #     ('FstSetpoint', ctypes.c_int32),
    #     ('TargetVelocity', ctypes.c_int32),
    #     ('DoStateCommSet', ctypes.c_uint16),
    # ]



class InputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ('Velocity_demand_value', ctypes.c_int32),
        ('Status_word', ctypes.c_uint16),
        ('Modes_of_operation_display', ctypes.c_int8),
        ('complement',ctypes.c_int8),
        ('Position_demand_internal_value', ctypes.c_int32),
        ('Position_actual_encoder_value',  ctypes.c_int32),
        ('Velocity_sensor_actual_value', ctypes.c_int32),
        ('Velocity_actual_value', ctypes.c_int32),
        ('Torque_actual_value',ctypes.c_int16),
        ('Current_actual_value',ctypes.c_int16)
    ]
    # _fields_ = [
    #     ('ErrorCode', ctypes.c_uint16),
    #     ('Statusword', ctypes.c_uint16),
    #     ('Modes_of_operation_display', ctypes.c_int8),
    #     ('Complement', ctypes.c_int8),
    #     ('Position_actual_value', ctypes.c_int32),
    #     ('PosActualValue', ctypes.c_int32),
    #     ('VlSensorActualValu', ctypes.c_int32),
    #     ('VlActualValue', ctypes.c_int32),
    #     ('TorqActualValu', ctypes.c_int16),
    #     ('CurentActlValu', ctypes.c_int16),
    #     ('MonitoredDiStates', ctypes.c_uint16),
    # ]




modes_of_operation = {
    'No mode': 0,
    'Profile position mode': 1,
    'Velocity mode':2,
    '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 pmm60_config_func(slave_pos):
    global pmm60
    # All default config
    pmm60.sdo_write(0x6060,0,bytes(ctypes.c_int8(2)))


def main():
    global pmm60
    master = pysoem.Master()
    master.open('\\Device\\NPF_{267091CF-E38D-4EED-AAC8-1A0692194D59}') 
    time.sleep(1) 
    if master.config_init() > 0:
        pmm60 = master.slaves[0]
        pmm60.config_func = pmm60_config_func
        master.config_map()
        if master.state_check(pysoem.SAFEOP_STATE, 50_000) == pysoem.SAFEOP_STATE:
            master.state = pysoem.OP_STATE
            
            master.send_processdata()
            master.receive_processdata(1_000)
            
            master.write_state()
            master.state_check(pysoem.OP_STATE, 5_000_000)
            if master.state == pysoem.OP_STATE:
                print('IN OP STATE')
                output_data = OutputPdo()
                output_data.modes_of_operation = modes_of_operation['Velocity mode']
                output_data.TargetVelocity = 500 
                for control_cmd in [6, 7, 15]:
                    output_data.controlword = control_cmd
                    pmm60.output = bytes(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(
                            'Status_word:',convert_input_data(pmm60.input).Status_word,'/',
                            'Modes_of_operation_display:',convert_input_data(pmm60.input).Modes_of_operation_display,
                            
                            )
                        time.sleep(0.01)
                except KeyboardInterrupt:
                    print('stopped')
                # zero everything
                pmm60.output = bytes(len(pmm60.output))
                master.send_processdata()
                master.receive_processdata(1_000)
            else:
                print('al status code {} ({})'.format(hex(pmm60.al_status), pysoem.al_status_code_to_string(pmm60.al_status)))
                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()

The prints:

IN OP STATE
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0
Status_word 4704 / Modes_of_operation_display 0

the 4740 here is decimal right? its binary is 1001010000100,
As the top, I'm confused what the meaning of it.
Also, I use both sdo_writed and output to config the mode of operation.
But why the prints of Modes_of_operation_display is 0.
(I also checked Velocity_demand_value, it is 0 too), it seems the outputpdo not success..

If i use the commented input and output, The prints:

IN OP STATE
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96
Status_word: 0 / Modes_of_operation_display: 96

Both of them, the motor not move. I'm confused...

Thank you so much if you could give me some suggestions
best regards
Tom

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

update:

I find this in the handbook:

screenshot

Then i changed my inputPDO and outputPDO again

class OutputPdo(ctypes.Structure):
pack = 1
fields = [
('Control_Word',ctypes.c_uint16),
('Mode_Operation',ctypes.c_int8),
('Target_Position',ctypes.c_int32),
('Vi_Target_V',ctypes.c_int16),
('Target_Torque',ctypes.c_int16),
('Tnterpolation_Data_Record',ctypes.c_int32),
('Target_Velocity',ctypes.c_int32),
('Max_Torque',ctypes.c_uint16),
('Max_Profile_Velocity',ctypes.c_uint32),
('Max_Motor_Speed',ctypes.c_uint32),
('VDI_virtuallevelCommSet',ctypes.c_uint16),
]

class InputPdo(ctypes.Structure):
pack = 1
fields = [
('ErrorCode', ctypes.c_uint16),
('Status_word', ctypes.c_uint16),
('Modes_of_operation_display', ctypes.c_int8),
('Position_actual_user_value', ctypes.c_int32),
('Position_actual_encoder_value', ctypes.c_int32),
('Velocity_sensor_actual_value', ctypes.c_int32),
('Velocity_actual_value', ctypes.c_int32),
('Torque_actual_value',ctypes.c_int16),
('Current_actual_value',ctypes.c_int16),
('MoinitoedDIStates',ctypes.c_uint16),
]

I find a spelling mistake in the previous code and I changed to this:
output_data.Mode_Operation = modes_of_operation['Velocity mode']

By now, if I print mode of operation display, it shows the corresponding number of the selected mode.

However, the motor still not move.
As the handbook, the VM mode needs target speed, and Acceleration and deceleration. The default value of Acceleration and deceleration are 500/s. I set the target v to 1000.
Seems everything all good. But not move at all now.

Another update:

# Vi_Target_V: 6042:0, RW , VM mode target velocity
output_data.Vi_Target_V = 1000 
# this print is 0     
print(ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x6042,0)).value) 

it seems the VM mode target velocity not write successfully or been replaced by 0.

Then I tried to config the 6042h at config_func step.

def pmm60_config_func(slave_pos):
    global pmm60
    pmm60.sdo_write(0x6042,0,bytes(ctypes.c_int16(1000)))
print(ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x6042,0)).value) #print 1000
for control_cmd in [0x6,0x7,0x7F]:
    output_data.Control_Word = control_cmd
    pmm60.output = bytes(output_data)
    master.send_processdata()
    master.receive_processdata(1_000)
    time.sleep(0.01)

 print(ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x6042,0)).value) #print 0

The first print is 1000, second is 0!
Why? the first 6042 target V is 1000 as the sdo write config. BUT after the send process step. 6042 go back to 0.

It seems the reason why the motor not move, cause its Velocity Mode's target value become 0

Do you know how to solve this issue?

Thanks soooo much.

BEST REGARDS,
Tom

third update:

print('IN OP STATE')
                output_data = OutputPdo()
                output_data.Mode_Operation = modes_of_operation['Velocity mode']
                output_data.Vi_Target_V = 3000

                print(ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x6042,0)).value)

                for control_cmd in [0x6,0x7,0x7F]:
                    
                    output_data.Control_Word = control_cmd
                    pmm60.output = bytes(output_data)  
                    master.send_processdata()
                    master.receive_processdata(1_000)
                    time.sleep(0.01)
               
                print(ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x6042,0)).value)

print:

IN OP STATE
0
11

but why, output_data.Vi_Target_V is mapping to 6042 (which is v mode targer v), i tried to print it, it is different and not reach to target(3000). Althrough it is 11, but the motor not move, i print it's position and actual V, a stable value and a 0.

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

code

print('IN OP STATE')
                output_data = OutputPdo()
                output_data.Mode_Operation = modes_of_operation['Velocity mode']
                output_data.Vi_Target_V = 1000
                print('Status_word before start:',convert_input_data(pmm60.input).Status_word)
                for control_cmd in [0x6,0x7,0x7F]:
                    
                    output_data.Control_Word = control_cmd
                    pmm60.output = bytes(output_data)  
                    master.send_processdata()
                    master.receive_processdata(1_000)
                    time.sleep(0.01)
                    
                print('Status_word after start:',convert_input_data(pmm60.input).Status_word)
                try:
                    while 1:
                        

                        master.send_processdata()
                        master.receive_processdata(1_000)
                        
                        print('Status_word in loop:',convert_input_data(pmm60.input).Status_word)
                        time.sleep(0.01)
                except KeyboardInterrupt:
                    print('stopped')

prints:

IN OP STATE
Status_word before start: 4704
Status_word after start: 4657
Status_word in loop: 4659
Status_word in loop: 4704
Status_word in loop: 4704
Status_word in loop: 4704
Status_word in loop: 4704
Status_word in loop: 4704
Status_word in loop: 4704

statusword table:

Statusword            /PDS FSA state
xxxx xxxx x0xx 0000b /Not ready to switch on
xxxx xxxx x1xx 0000b /Switch on disabled
xxxx xxxx x01x 0001b /Ready to switch on
xxxx xxxx x01x 0011b /Switched on
xxxx xxxx x01x 0111b /Operation enabled
xxxx xxxx x00x 0111b /Quick stop active
xxxx xxxx x0xx 1111b /Fault reaction active
xxxx xxxx x0xx 1000b /Fault

4704: 1001001100000, switched on disabled
4657: 1001000110001, ready to switch on
4659: 1001000110011, switched on
4704: 1001001100000, switched on disabled

So, the device not in the operation enabled state...

BUT:
every first time i turn on the device. the statusword is 5687, which is 1011000110111, means operation enabled. BUT the same, not move.
The software of testing the device shows statuswords of 1591 when the motor moving. which is 11000110111.

Status_word in loop: 5687
Status_word in loop: 5687
Status_word in loop: 5687
Status_word in loop: 5687
Status_word in loop: 5687
Status_word in loop: 5687

Is it because something not go back to init state? lead to the first time the device can go in operation enable status, second time and after it can't

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Good news for me
It runs well in VM mode
prints:

Status_word in loop: 1591 / Modes_of_operation_display: 2
Status_word in loop: 1591 / Modes_of_operation_display: 2
Status_word in loop: 1591 / Modes_of_operation_display: 2
Status_word in loop: 1591 / Modes_of_operation_display: 2
Status_word in loop: 1591 / Modes_of_operation_display: 2
stopped
PS C:\Users\Administrator\Desktop\pmm60> python .\test.py
IN OP STATE
Status_word before start: 608
Status_word after stop: 4657
Status_word in loop: 4659 / Modes_of_operation_display: 2
Status_word in loop: 4704 / Modes_of_operation_display: 2
Status_word in loop: 4704 / Modes_of_operation_display: 2
Status_word in loop: 4704 / Modes_of_operation_display: 2
Status_word in loop: 4704 / Modes_of_operation_display: 2
Status_word in loop: 4704 / Modes_of_operation_display: 2
Status_word in loop: 4704 / Modes_of_operation_display: 2

BUT, you can find the first time the motor in 1591, and the motor moves well in my setted mode and speed.
After i use ctrl+C, it stopped, the second time, it goes bad state and not move.

this is my code:

import pysoem
import time
import ctypes

pmm60 = None

class OutputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ =[
        # ('Control_Word',ctypes.c_uint16),
        # ('Mode_Operation',ctypes.c_int8),
        # ('Complement',ctypes.c_int8),
        # ('Target_Position',ctypes.c_int32),
        # ('Vi_Target_V',ctypes.c_int16),
        # ('Target_Torque',ctypes.c_int16),
        # ('Tnterpolation_Data_Record',ctypes.c_int32),
        # ('Target_Velocity',ctypes.c_int32),
        # ('Max_Torque',ctypes.c_uint16),
        # ('Max_Profile_Velocity',ctypes.c_uint32),
        # ('Max_Motor_Speed',ctypes.c_uint32),
        ('Control_Word',ctypes.c_uint16),
        ('Mode_Operation',ctypes.c_int8),
        ('Target_Position',ctypes.c_int32),
        ('Vi_Target_V',ctypes.c_int16),
        ('Target_Torque',ctypes.c_int16),
        ('Tnterpolation_Data_Record',ctypes.c_int32),
        ('Target_Velocity',ctypes.c_int32),
        ('Max_Torque',ctypes.c_uint16),
        ('Max_Profile_Velocity',ctypes.c_uint32),
        ('Max_Motor_Speed',ctypes.c_uint32),
        ('VDI_virtuallevelCommSet',ctypes.c_uint16),
    ]

class InputPdo(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        # ('Velocity_demand_value', ctypes.c_int32),
        # ('Status_word', ctypes.c_uint16),
        # ('Modes_of_operation_display', ctypes.c_int8),
        # ('complement',ctypes.c_int8),
        # ('Position_demand_internal_value', ctypes.c_int32),
        # ('Position_actual_encoder_value',  ctypes.c_int32),
        # ('Velocity_sensor_actual_value', ctypes.c_int32),
        # ('Velocity_actual_value', ctypes.c_int32),
        # ('Torque_actual_value',ctypes.c_int16),
        # ('Current_actual_value',ctypes.c_int16)
        ('ErrorCode', ctypes.c_uint16),
        ('Status_word', ctypes.c_uint16),
        ('Modes_of_operation_display', ctypes.c_int8),
        ('Position_actual_user_value', ctypes.c_int32),
        ('Position_actual_encoder_value', ctypes.c_int32),
        ('Velocity_sensor_actual_value', ctypes.c_int32),
        ('Velocity_actual_value', ctypes.c_int32),
        ('Torque_actual_value',ctypes.c_int16),
        ('Current_actual_value',ctypes.c_int16),
        ('MoinitoedDIStates',ctypes.c_uint16),
    ]

modes_of_operation = {
    'No mode': 0,
    'Profile position mode': 1,
    'Velocity mode':2,
    '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)

# I use this config function
def pmm60_config_func(slave_pos):
    global pmm60
    # all default config

def main():
    global pmm60
    master = pysoem.Master()
    master.open('\\Device\\NPF_{267091CF-E38D-4EED-AAC8-1A0692194D59}') 
    time.sleep(1) 
    if master.config_init() > 0:
        pmm60 = master.slaves[0]
        pmm60.config_func = pmm60_config_func
        master.config_map()
        if master.state_check(pysoem.SAFEOP_STATE, 50_000) == pysoem.SAFEOP_STATE:
            master.state = pysoem.OP_STATE
            
            master.send_processdata()
            master.receive_processdata(1_000)
            
            master.write_state()
            master.state_check(pysoem.OP_STATE, 5_000_000)
            if master.state == pysoem.OP_STATE:
                print('IN OP STATE')
                output_data = OutputPdo()
                output_data.Mode_Operation = modes_of_operation['Velocity mode']
                output_data.Vi_Target_V = 1000
                print('Status_word before start:',convert_input_data(pmm60.input).Status_word)
                for control_cmd in [6,7,127]:
                    
                    output_data.Control_Word = control_cmd
                    pmm60.output = bytes(output_data)  
                    master.send_processdata()
                    master.receive_processdata(1_000)
                    time.sleep(0.01)
                    
                print('Status_word after stop:',convert_input_data(pmm60.input).Status_word)
                try:
                    while 1:
                        

                        master.send_processdata()
                        master.receive_processdata(1_000)
                        
                        print('Status_word in loop:',convert_input_data(pmm60.input).Status_word,'/',
                        'Modes_of_operation_display:',convert_input_data(pmm60.input).Modes_of_operation_display)
                        time.sleep(0.01)
                except KeyboardInterrupt:
                    print('stopped')
                # zero everything
                pmm60.output = bytes(len(pmm60.output))
                master.send_processdata()
                master.receive_processdata(1_000)
            else:
                print('al status code {} ({})'.format(hex(pmm60.al_status), pysoem.al_status_code_to_string(pmm60.al_status)))
                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()

When I turn on the power, the device can move at the first time.
Howerever, not work on second time. The statuswords changed.
Do you have any suggestions of this stituation? @bnjmnp

Best regards
Tom

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

I add this in the config func:

pmm60.sdo_write(0x200E,0x09, bytes(ctypes.c_int32(50427137)))

all done now.
The communication warning lead to the problem, evey time i use ctrl+c to stop the communication, the device will record 'communicaion warning', the second i run the code, the device will not work because of this warning.
So i add remove communication warning in configure function.
Then it can work well.

Do you have any other suggestions?
and do you know how to let the device not cause communication warning while use ctrl+c to break the communication?

Hope you have a good day

Tom

from pysoem.

bnjmnp avatar bnjmnp commented on August 16, 2024

Nice to hear that you came this far. And thank you for sharing your code, someone else may find this very helpful.
I would suggest you to also teardown the state machine after the KerboardInterrupt:

                except KeyboardInterrupt:
                    print('stopped')
                for control_cmd in [7, ...]:
                    output_data.Control_Word = control_cmd
                    pmm60.output = bytes(output_data)  
                    master.send_processdata()
                    master.receive_processdata(1_000)
                    time.sleep(0.01)
                # zero everything
                pmm60.output = bytes(len(pmm60.output))

And in both, setup and teardown of the state machine it is better to wait for the specific transition to occur before sending the next command, instead of just using a sleep.

                for control_cmd in [...]:
                    output_data.Control_Word = control_cmd
                    pmm60.output = bytes(output_data)
                    while 1:
                        master.send_processdata()
                        master.receive_processdata(1_000)
                        time.sleep(0.01)
                        if convert_input_data(pmm60.input).Status_word == ...
                            break

In fact this should also be a implemented as a state machine.
For example if there is initially a fault, setting up with 6, 7, ... might not be enough, you also need to clear the fault first.

from pysoem.

bnjmnp avatar bnjmnp commented on August 16, 2024
    global pmm60
    # RatedCurrent
    pmm60.sdo_write(0x2000,0x09, bytes(0x0492))
    # MaximumCurrent
    pmm60.sdo_write(0x2000,0x0A, bytes(0x0924))
    # MaxCurrentDurTime
    pmm60.sdo_write(0x2000,0x0B, bytes(0x0064))

I don't understand why this stuff dose not throw an error. You send an array of 1170(0x0492), 2340, and 100 bytes to the object entries, all bytes set to zero.

Those that look like this make much more sense to me:

    # Motor revolutions
    pmm60.sdo_write(0x6091,0x01, bytes(ctypes.c_uint32(1)))

...
Ah, now I get it, you use the empty config func. This code is not called. You better remove dead code.

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Yeah, the configuration function causes error, now I removed it.
The teardown really inspired me, I swill try it later on.
Thank you so much!
Have a nice day.

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Hi @bnjmnp,

I used a function to control one motor, it works well, i just need to give it target V and time period.

def stop_prepare_go(output_data,v,t):
                    # stop and  prepare state
                    output_data.Control_Word = 7
                    pmm60.output = bytes(output_data)  
                    master.send_processdata()
                    master.receive_processdata(1_000)
                    time.sleep(0.01)
                    # set target V
                    output_data.Vi_Target_V = v
                    # start state
                    output_data.Control_Word = 127
                    pmm60.output = bytes(output_data)  
                    master.send_processdata()
                    master.receive_processdata(1_000)
                    time.sleep(0.01)
                    # go with in the setted time period
                    end_t = time.time() + t
                    while time.time() < end_t:
                        master.send_processdata()
                        master.receive_processdata(1_000)
                        #print('currentV',ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x606C,0)).value)
                        V_list.append(ctypes.c_int16.from_buffer_copy(pmm60.sdo_read(0x606C,0)).value)
                        time.sleep(0.01)

If I'd like to make the servo motor move at 300 rpm and continue 5 second:

stop_prepare_go(output_data, 300,5)

let the motor stop 2 seconds:

stop_prepare_go(output_data, 0,2)

If I have two or three motor, how can I make them work together? As I know, python can only process one thing at one time. Do you have any suggestions?
(I find in other issues about how to configure the PDO input and output for multiple motors, but no idea about how to let two or more ethercat servo motor work together)

Hope you have a nice day.

Looking for word to hear form you.

from pysoem.

bnjmnp avatar bnjmnp commented on August 16, 2024

if all (3) motors need to do the same move you could store the drives in a list:

pmm60s = master.slaves[0:3]

and then:

def stop_prepare_go(output_data, v, t):
    # stop and  prepare state
    output_data.Control_Word = 7
    for pmm60 in pmm60s:
        pmm60.output = bytes(output_data)  
    master.send_processdata()
    master.receive_processdata(1_000)
    time.sleep(0.01)
    # set target V
    output_data.Vi_Target_V = v
    # start state
    output_data.Control_Word = 127
    for pmm60 in pmm60s:
        pmm60.output = bytes(output_data)  
    master.send_processdata()
    master.receive_processdata(1_000)
    time.sleep(0.01)
    # go with in the setted time period
    end_t = time.time() + t
    while time.time() < end_t:
        master.send_processdata()
        master.receive_processdata(1_000)
        for i, pmm60 in enumerate(pmm60s)
            V_list[i].append(convert_input_data(pmm60.input).Velocity_actual_value)
        time.sleep(0.01)

Because send/receive processdata sends the data to all the drives at once, they will start nearly (but not perfectly) simultaneously.
I also removed the sdo_read() in the while loop an replaced it with PDO equivalent, so it's faster and you get the actual velocity from each drive at (almost) the same point in time.

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Amazing! Thank you so much

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Hi @bnjmnp,
Your code seems let multiple motors run together at the same velocity and time period.
My purpose is let multiple motors run at different velocity and different time period. Some times their working period may overlapping.
The previous function “stop_prepare_go()” unable to achieve overlapping, it can only let the motors run at the same period.
Actually my machine have three axis, with three servo motors. They work at different status. Sometimes, the start of one motor depends on another.
Do you have any suggestions for this.
Hope you have a good day.
Looking forward to hearing from you.
Tom

from pysoem.

tom9672 avatar tom9672 commented on August 16, 2024

Hi @bnjmnp
Could you please have a look my previous question when you are free.
Thanks so much!!!
Best regards,
Tom

from pysoem.

bnjmnp avatar bnjmnp commented on August 16, 2024

Hi @tom9672,
Sorry for the late response. I also thought I give you some time to figure this out yourself.
If you want the motors run differently, you may use threading.
First thread you would use for send_processdata() and receive_processdata(1_000) like in the basic_example.py (see the _processdata_thread()).
Then it is fully up to you, you may spin up a thread from the main context for every job a motor needs to do, or you start a thread for each motor at the beginning. You could also write you own state machine and do the switching between the motors yourself.

from pysoem.

tdemierr avatar tdemierr commented on August 16, 2024

Hello,
Sorry to up this thread again. However, I am currently interfacing this drive: http://www.rtelligent.net/upload/wenjian/Stepper/ECT%20Series%20User%20Manual.pdf
Could it be possible to share the version of your code with threading ?
Thanks

from pysoem.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.