Basic Motor Position Control Application
Last updated
from smd.red import * # Import SMD Red control library
from serial.tools.list_ports import comports # For listing available serial ports
from platform import system # To detect the operating system
import time # For sleep/delay
import math # For mathematical operations
# Automatically detect and return the correct USB port connected to SMD Red
def USB_Port():
ports = list(comports())
usb_names = {
"Windows": ["USB Serial Port"],
"Linux": ["/dev/ttyUSB"],
"Darwin": ["/dev/cu."] # macOS ports usually start with /dev/cu.
}
os_name = system()
for port, desc, _ in ports:
if any(name in port or name in desc for name in usb_names.get(os_name, [])):
return port
return None
def main():
port = USB_Port()
if not port:
print("No port found.") # If no port is found, exit the program
return
master = Master(port) # Create a Master object to control SMD Red
motor_id = 1 # ID of the connected motor
CPR = 6533 # Encoder Counts Per Revolution
# Attach the motor and configure its basic parameters
master.attach(Red(motor_id))
master.set_shaft_cpr(motor_id, CPR) # Set encoder resolution
master.set_shaft_rpm(motor_id, 100) # Set nominal RPM
master.set_control_parameters_position(motor_id, 0.5, 0.0, 20.0) # Set PID gains for position control
master.set_operation_mode(motor_id, OperationMode.Position) # Enable position control mode
master.enable_torque(motor_id, True) # Enable torque so the motor can move
while True:
try:
# Get target angle from user input and keep it in [0, 360) range
angle = float(input("Enter target angle (0-360°): ")) % 360
# Convert angle in degrees to encoder counts
target = angle * (CPR / 360)
# Send position command to the motor
master.set_position(motor_id, target)
# Wait for movement to complete
time.sleep(0.5)
# Read actual encoder position
current = master.get_position(motor_id)
# Convert encoder counts back to degrees
actual = current * (360 / CPR)
# Print both target and actual angles
print(f"Target: {angle:.2f}°, Actual: {actual:.2f}°\n")
except KeyboardInterrupt:
# On user interrupt (Ctrl+C), disable torque and stop the motor
master.enable_torque(motor_id, False)
break
# Entry point of the script
if __name__ == "__main__":
main()#include <Acrome-SMD.h>
#define BAUDRATE 115200 // Serial communication speed
#define CPR 6533 // Counts per revolution of the encoder
#define ID 1 // ID of the SMD Red module
Red master(ID, Serial, BAUDRATE); // Create SMD Red object
void setup() {
Serial.begin(115200); // Start serial monitor
master.begin(); // Initialize communication with SMD Red
master.torqueEnable(1); // Enable motor torque
master.setOperationMode(PositionControl); // Set operation mode to Position Control
}
void loop() {
// Read joystick X and Y values from module 1
int joystickX = master.getJoystickX(1);
int joystickY = master.getJoystickY(1);
// If joystick is moved beyond dead zone
if (abs(joystickX) > 10 || abs(joystickY) > 10) {
// Calculate angle based on joystick direction
float angle = atan2(joystickY / 100.0, joystickX / 100.0);
float angleDegrees = fmod(degrees(angle) + 360.0, 360.0); // Normalize angle to 0–360°
// Convert angle in degrees to encoder position (CPR)
int position = angleDegrees * (CPR / 360.0);
master.setpoint(1, position); // Send position command to motor
// Debug output
Serial.print("Target Angle: "); Serial.println(angleDegrees);
Serial.print("Target CPR: "); Serial.println(position);
}
delay(100); // Small delay for stability
}