From 7122cf9deb9ca0653dc6911e7cdce06ac3ab60d6 Mon Sep 17 00:00:00 2001 From: Artem Darius Weber Date: Fri, 19 Jul 2024 10:41:09 +0300 Subject: [PATCH] init --- .idea/.gitignore | 8 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/teleoperator.iml | 8 + main.py | 7 + rmd_dx8/__init__.py | 0 rmd_dx8/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 161 bytes rmd_dx8/__pycache__/rmd_x8.cpython-38.pyc | Bin 0 -> 14458 bytes rmd_dx8/rmd_x8.py | 481 ++++++++++++++++++ 10 files changed, 525 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/teleoperator.iml create mode 100644 main.py create mode 100644 rmd_dx8/__init__.py create mode 100644 rmd_dx8/__pycache__/__init__.cpython-38.pyc create mode 100644 rmd_dx8/__pycache__/rmd_x8.cpython-38.pyc create mode 100644 rmd_dx8/rmd_x8.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1314114 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1b87838 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/teleoperator.iml b/.idea/teleoperator.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/teleoperator.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..b4ddd85 --- /dev/null +++ b/main.py @@ -0,0 +1,7 @@ +from rmd_dx8.rmd_x8 import RMD_X8 + +# Setup a new RMD_X8 motor with its identifier. +robot = RMD_X8(0x141) + +# Read the motor's current PID parameters. +robot.read_pid() \ No newline at end of file diff --git a/rmd_dx8/__init__.py b/rmd_dx8/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rmd_dx8/__pycache__/__init__.cpython-38.pyc b/rmd_dx8/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7db7848449221bab6df550e62c4d0fed0697c184 GIT binary patch literal 161 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 F{{zLRa@+s_ literal 0 HcmV?d00001 diff --git a/rmd_dx8/rmd_x8.py b/rmd_dx8/rmd_x8.py new file mode 100644 index 0000000..19a1c5f --- /dev/null +++ b/rmd_dx8/rmd_x8.py @@ -0,0 +1,481 @@ +# RMD-X8 Python Library +# Copyright 2022 Sanjay Sunil + +import can +import os +import time + + +class RMD_X8: + """ + A class to read and write on the RMD-X8 motor. + + ... + + Attributes + ---------- + bus : type + the can bus channel used to communicate with the motor + identifier : type + the motor's identifier on the can bus + + Methods + ------- + setup(): + Setup the can bus connection. + send_cmd(data, delay): + Send a frame data to the motor. + read_pid(): + Read the motor's current PID parameters. + write_pid_ram(data): + Write PID parameters to the RAM. + write_pid_rom(data): + Write PID parameters to the ROM. + read_acceleration(): + Read the motor's acceleration data. + write_acceleration_ram(data): + Write the acceleration to the RAM of the motor. + read_encoder(): + Read the current position of the encoder. + write_encoder_offset(data): + Set the motor's encoder offset. + write_motor_zero_rom(): + Write the current position of the motor to the ROM + as the motor zero position. + read_multi_turns_angle(): + Read the multi-turn angle of the motor. + read_single_turn_angle(): + Read the single circle angle of the motor. + motor_off(): + Turn off the motor, while clearing the motor operating + status and previously received control commands. + motor_stop(): + Stop the motor, but do not clear the operating state and + previously received control commands. + motor_running(): + Resume motor operation from the motor stop command. + read_motor_status_1(): + Reads the motor's error status, voltage, temperature and + other information. + read_motor_status_2(): + Reads the motor temperature, voltage, speed and encoder + position. + read_motor_status_3(): + Reads the phase current status data of the motor. + clear_motor_error_flag(): + Clears the error status of the currrent motor. + torque_closed_loop(data): + Control torque current output of the motor. + speed_closed_loop(data): + Control the speed of the motor. + position_closed_loop_1(data): + Control the position of the motor (multi-turn angle). + position_closed_loop_2(data): + Control the position of the motor (multi-turn angle). + position_closed_loop_3(data): + Control the position of the motor (single-turn angle). + position_closed_loop_4(data): + Control the position of the motor (single-turn angle). + """ + + def __init__(self, identifier): + """ + Constructs all the necessary attributes for the RMDX8 object. + """ + self.bus = None + self.identifier = identifier + + def setup(self): + """ + Setup the can bus connection. + + Returns + ------- + self.bus : type + The bus used to communicate with the motor. + """ + try: + os.system("sudo /sbin/ip link set can0 up type can bitrate 1000000") + time.sleep(0.1) + except Exception as e: + print(e) + + try: + bus = can.interface.Bus(bustype='socketcan_native', channel='can0') + except OSError: + print('err: PiCAN board was not found.') + exit() + except Exception as e: + print(e) + + self.bus = bus + return self.bus + + def send_cmd(self, data, delay): + """ + Send frame data to the motor. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + delay : int/float + The time to wait after sending data to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = can.Message(arbitration_id=self.identifier, + data=data, extended_id=False) + self.bus.send(message) + time.sleep(delay) + received_message = self.bus.recv() + return received_message + + def read_pid(self): + """ + Read the motor's current PID parameters. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def write_pid_ram(self, data): + """ + Write PID parameters to the RAM. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x31, 0x00, data[0], data[1], + data[2], data[3], data[4], data[5]] + return self.send_cmd(message, 0.01) + + def write_pid_rom(self, data): + """ + Write PID parameters to the ROM. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x32, 0x00, data[0], data[1], + data[2], data[3], data[4], data[5]] + return self.send_cmd(message, 0.01) + + def read_acceleration(self): + """ + Read the motor's acceleration data. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def write_acceleration_ram(self, data): + """ + Write the acceleration to the RAM of the motor. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x34, 0x00, 0x00, 0x00, + data[0], data[1], data[2], data[3]] + return self.send_cmd(message, 0.01) + + def read_encoder(self): + """ + Read the current position of the encoder. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def write_encoder_offset(self, data): + """ + Set the motor's encoder offset. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x91, 0x00, 0x00, 0x00, + 0x00, 0x00, data[0], data[1]] + return self.send_cmd(message, 0.01) + + def write_motor_zero_rom(self): + """ + Write the current position of the motor to the ROM as the motor zero position. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def read_multi_turns_angle(self): + """ + Read the multi-turn angle of the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x92, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def read_single_turn_angle(self): + """ + Read the single circle angle of the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x94, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def motor_off(self): + """ + Turn off the motor, while clearing the motor operating status and previously received control commands. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def motor_stop(self): + """ + Stop the motor, but do not clear the operating state and previously received control commands. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x81, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def motor_run(self): + """ + Resume motor operation from the motor stop command. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def read_motor_status_1(self): + """ + Reads the motor's error status, voltage, temperature and other information. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x9A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def read_motor_status_2(self): + """ + Reads the motor temperature, voltage, speed and encoder position. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x9C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def read_motor_status_3(self): + """ + Reads the phase current status data of the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x9D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def clear_motor_error_flag(self): + """ + Clears the error status of the currrent motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0x9B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def torque_closed_loop(self, data): + """ + Control torque current output of the motor. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0xA1, 0x00, 0x00, 0x00, + data[0], data[1], 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def speed_closed_loop(self, data): + """ + Control the speed of the motor. + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0xA2, 0x00, 0x00, 0x00, + data[0], data[1], data[2], data[3]] + return self.send_cmd(message, 0.01) + + def position_closed_loop_1(self, data): + """ + Control the position of the motor (multi-turn angle). + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0xA3, 0x00, 0x00, 0x00, + data[0], data[1], data[2], data[3]] + return self.send_cmd(message, 0.01) + + def position_closed_loop_2(self, data): + """ + Control the position of the motor (multi-turn angle). + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0xA4, 0x00, data[0], data[1], + data[2], data[3], data[4], data[5]] + return self.send_cmd(message, 0.01) + + def position_closed_loop_3(self, data): + """ + Control the position of the motor (single-turn angle). + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0xA5, data[0], 0x00, 0x00, + data[1], data[2], 0x00, 0x00] + return self.send_cmd(message, 0.01) + + def position_closed_loop_4(self, data): + """ + Control the position of the motor (single-turn angle). + + Parameters + ---------- + data : list + The frame data to be sent to the motor. + + Returns + ------- + received_message : list + Frame data received from the motor after receiving the command. + """ + message = [0xA6, data[0], data[1], data[2], + data[3], data[4], 0x00, 0x00] + return self.send_cmd(message, 0.01) \ No newline at end of file