Starter Kit
Last updated
Last updated
The Starter Kit has the fundamentals of a control system for starters. A 100 RPM brushless DC motor with an encoder and a Joystick Module makes great applications for understanding the basics of motor control.
There are 4 operation modes for motor control: PWM, Velocity, Position and Current control modes.
All these modes allow the user to discover the different ways to drive a motor, enhancing the imagination.
The Starter Kit project codes shown below with all these motor control mode explanations:
Mode 1 - PWM: Motor rotates at a duty cycle corresponding to the value received from the joystick X axis
Mode 2 - Velocity: The velocity of the motor increases or decreases according to the X and Y axis of the joystick
Mode 3 - Position: The joystick acts like the edges of a circle on a coordinate system. It makes the motor rotate at angle of the joystick.
from smd.red import *
from serial.tools.list_ports import comports
from platform import system
import math
import os
import time
import json
import logging
from datetime import datetime
logging.basicConfig(
filename=f'motor_log_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
class MotorConfig:
"""Engine configuration class"""
DEFAULT_CONFIG = {
'CPR': 6533,
'RPM': 100,
'JOYSTICK_DEADZONE': 10,
'MAX_SPEED': 100,
'MIN_SPEED': -100,
'UPDATE_INTERVAL': 0.1
}
@staticmethod
def load_config(filename='Starter Kit/motor_config.json'):
try:
with open(filename, 'r') as f:
return {**MotorConfig.DEFAULT_CONFIG, **json.load(f)}
except FileNotFoundError:
logging.warning("Configuration file not found, using default settings.")
return MotorConfig.DEFAULT_CONFIG
class MotorModes:
"""Enum-like class for engine modes"""
PWM = 0
VELOCITY = 1
POSITION = 2
TORQUE = 3
@staticmethod
def get_mode_name(mode):
modes = {
0: "PWM Mode",
1: "Speed Mode",
2: "Position Mode",
3: "Torque Mode"
}
return modes.get(mode,"Unknown Mode")
class MotorController:
"""Main engine control class"""
def __init__(self, port, motor_id=1):
self.master = Master(port)
self.id = motor_id
self.config = MotorConfig.load_config()
self.mode = 0
self.setup_motor()
self.last_update = time.time()
# Durum değişkenleri
self.motor_speed = 0
self.angle_degrees = 0
self.current_limit = 100
self.current_value = 0
self.previous_current = 0
self.torque_status = True
def setup_motor(self):
"""Engine start settings"""
try:
self.master.attach(Red(self.id))
self.master.set_shaft_cpr(self.id, self.config['CPR'])
self.master.set_shaft_rpm(self.id, self.config['RPM'])
# Kontrol parametreleri
self.master.set_control_parameters_velocity(self.id, 30.0, 5.0, 0.0)
self.master.set_control_parameters_position(self.id, 0.5, 0.0, 20.0)
self.master.set_control_parameters_torque(self.id, 3.0, 0.1, 0.0)
self.master.enable_torque(self.id, True)
logging.info("Engine started successfully")
except Exception as e:
logging.error(f"Engine start failure: {e}")
raise
def safe_joystick_read(self):
"""Secure joystick reading"""
try:
return self.master.get_joystick(self.id, 1)
except Exception as e:
logging.warning(f"Joystick reading error: {e}")
return [0, 0, 0]
def emergency_stop(self):
"""Emergency stop"""
try:
self.master.enable_torque(self.id, False)
self.master.set_duty_cycle(self.id, 0)
logging.info("Emergency stop was carried out")
except Exception as e:
logging.error(f"Emergency stop error: {e}")
def handle_pwm_mode(self, joystick_x):
"""PWM mode handler"""
self.master.set_operation_mode(self.id, OperationMode.PWM)
if abs(joystick_x) > self.config['JOYSTICK_DEADZONE']:
self.motor_speed = joystick_x
self.master.set_duty_cycle(self.id, -self.motor_speed)
else:
self.master.set_duty_cycle(self.id, 0)
self.motor_speed = 0
return {"Motor Duty Cycle": self.motor_speed}
def handle_velocity_mode(self, joystick_x, joystick_y):
"""Velocity mode handler"""
self.master.set_operation_mode(self.id, OperationMode.Velocity)
if (joystick_x > 50 or joystick_y > 50) and self.motor_speed < self.config['MAX_SPEED']:
self.motor_speed += 1
elif (joystick_x < -50 or joystick_y < -50) and self.motor_speed > self.config['MIN_SPEED']:
self.motor_speed -= 1
self.master.set_velocity(self.id, -self.motor_speed)
return {"Motor Speed": self.motor_speed}
def handle_position_mode(self, joystick_x, joystick_y):
"""Position mode handler"""
self.master.set_operation_mode(self.id, OperationMode.Position)
if abs(joystick_x) < 10 and abs(joystick_y) < 10:
try:
current_position = self.master.get_position(self.id)
self.angle_degrees = current_position * (360/self.config['CPR'])
except:
pass
else:
x = joystick_x / 100.0
y = joystick_y / 100.0
angle = math.atan2(y, x)
previous_angle = self.angle_degrees
self.angle_degrees = (math.degrees(angle) + 360) % 360
if self.angle_degrees - previous_angle > 180:
self.angle_degrees -= 360
position = self.angle_degrees * (self.config['CPR']/360)
self.master.set_position(self.id, position)
return {"Engine Angle": f"{self.angle_degrees:.2f}°"}
def handle_torque_mode(self, joystick_x, joystick_y):
"""Torque mode handler"""
self.master.set_operation_mode(self.id, OperationMode.Torque)
if joystick_x > 50 or joystick_y > 50:
self.current_limit += 1
elif joystick_x < -50 or joystick_y < -50:
self.current_limit -= 1
self.master.set_torque(self.id, self.current_limit - 50)
try:
self.previous_current = self.current_value
self.current_value = self.master.get_torque(self.id)
except:
self.current_value = self.previous_current
if self.current_value >= self.current_limit:
self.current_value = self.current_limit
return {
"Motor Current": f"{self.current_value:.2f}",
"Current Limit": self.current_limit
}
def USB_Port():
ports = list(comports())
usb_names = {
"Windows": ["USB Serial Port"],
"Linux": ["/dev/ttyUSB"],
"Darwin": [
"/dev/tty.usbserial",
"/dev/tty.usbmodem",
"/dev/tty.SLAB_USBtoUART",
"/dev/tty.wchusbserial",
"/dev/cu.usbserial",
"/dev/cu.usbmodem",
"/dev/cu.SLAB_USBtoUART",
"/dev/cu.wchusbserial",
]
}
os_name = system()
if ports:
for port, desc, hwid in sorted(ports):
if any(name in port or name in desc for name in usb_names.get(os_name, [])):
return port
print("Current ports:")
for port, desc, hwid in ports:
print(f"Port: {port}, Description: {desc}, Hardware ID: {hwid}")
else:
print("No port found")
return None
def main():
# USB port bulma
port = USB_Port()
if not port:
logging.error("No port found")
return
# Motor kontrolcüsü oluşturma
controller = MotorController(port)
while True:
try:
# Joystick durumunu oku
joystick_x, joystick_y, button = controller.safe_joystick_read()
# Mod değişimi kontrolü
if button:
controller.mode = (controller.mode + 1) % 4
logging.info(f"Chance Mode: {MotorModes.get_mode_name(controller.mode)}")
time.sleep(0.5)
logging.info(f"Current mode: {MotorModes.get_mode_name(controller.mode)}")
if controller.mode == MotorModes.PWM:
status = controller.handle_pwm_mode(joystick_x)
elif controller.mode == MotorModes.VELOCITY:
status = controller.handle_velocity_mode(joystick_x, joystick_y)
elif controller.mode == MotorModes.POSITION:
status = controller.handle_position_mode(joystick_x, joystick_y)
elif controller.mode == MotorModes.TORQUE:
status = controller.handle_torque_mode(joystick_x, joystick_y)
else:
logging.error("Invalid mode selected")
print(f"=== {MotorModes.get_mode_name(controller.mode)} ===")
print("-" * 40)
for key, value in status.items():
print(f"{key}: {value}")
print("-" * 40)
except KeyboardInterrupt:
logging.info("The program was terminated by the user")
controller.emergency_stop()
break
except Exception as e:
logging.error(f"Unexpected error: {e}")
controller.emergency_stop()
break
if __name__ == "__main__":
main()
#include <Acrome-SMD.h>
#define BAUDRATE 115200
#define CPR 6533
#define ID 1
Red master(ID, Serial, BAUDRATE);
int mode = 0; // Motor control mode (0: PWM, 1: Velocity, 2: Position, 3: Torque)
int motorSpeed = 0;
int currentLimit = 100;
bool torqueEnabled = true;
void setup() {
master.begin();
master.torqueEnable(1);
Serial.begin(115200);
}
void loop() {
start();
}
void start(){
int joystickX, joystickY, button;
joystickX = (master.getJoystickX(1))?master.getJoystickX(1):0;
joystickY = (master.getJoystickY(1))?master.getJoystickY(1):0;
button = (master.getJoystickButton(1))?master.getJoystickButton(1):0;
if (button) {
delay(3000);
mode = (mode + 1) % 4;
master.torqueEnable(0); delay(100);
master.torqueEnable(1); delay(100);
}
switch (mode) {
case 0: // PWM Control
master.setOperationMode(PWMControl);
if (abs(joystickX) > 10) {
motorSpeed = joystickX;
master.setpoint(0, -motorSpeed);
} else {
master.setpoint(0, 0);
}
Serial.println("*** MODE 1: PWM ***");
Serial.print("Motor Duty Cycle: "); Serial.println(motorSpeed);
break;
case 1: // Velocity Control
master.setOperationMode(VelocityControl);
if (joystickX > 50 || joystickY > 50) {
motorSpeed = constrain(motorSpeed + 1, -100, 100);
} else if (joystickX < -50 || joystickY < -50) {
motorSpeed = constrain(motorSpeed - 1, -100, 100);
}
master.setpoint(2, -motorSpeed);
Serial.println("*** MODE 2: Velocity ***");
Serial.print("Motor Speed: "); Serial.println(motorSpeed);
break;
case 2: // Position Control
master.setOperationMode(PositionControl);
if (abs(joystickX) > 10 || abs(joystickY) > 10) {
float angle = atan2(joystickY / 100.0, joystickX / 100.0);
float angleDegrees = fmod(degrees(angle) + 360.0, 360.0);
int position = angleDegrees * (CPR / 360.0);
master.setpoint(1, position);
Serial.println("*** MODE 3: Position ***");
Serial.print("Motor Angle: "); Serial.println(angleDegrees);
}
break;
case 3: // Torque Control
master.setOperationMode(TorqueControl);
if (joystickX > 50 || joystickY > 50) {
currentLimit++;
} else if (joystickX < -50 || joystickY < -50) {
currentLimit--;
}
master.setpoint(3, currentLimit - 50);
int current = master.getTorque();
Serial.println("*** MODE 4: Torque ***");
Serial.print("Motor Current: "); Serial.println(current);
Serial.print("Current Limit: "); Serial.println(currentLimit);
if (current >= currentLimit) {
master.torqueEnable(0);
torqueEnabled = false;
Serial.println("Motor movement disabled due to the current exceed!");
}
break;
default:
break;
}
}
Mode 4 - Current: The motor will draw a current value less than 50 mA which is set as the limit by the joystick. If something physically interrupts motor from moving, it naturally will try to draw a higher current to maintain its torque. The register becomes False
and motor operation halts.