Skip to main content

Overview

The RoboticServices class is the main orchestrator for the robotic arm controller. It coordinates all hardware modules, manages service execution, and handles communication with the Raspberry Pi host. Location: arm_controller/src/main.py:294

Class Definition

main.py
class RoboticServices:
    def __init__(self):
        # Initialize modules
        self.sensor_module = SensorModule()
        self.perception_module = PerceptionModule(self.sensor_module)
        self.mapping_module = MappingModule()
        self.control_module = ControlModule()
        self.safety_module = SafetyModule(self.sensor_module, self.control_module)
        self.communication = CommunicationManager()

Motor Control

Motor Configuration

The ControlModule manages four motors for arm manipulation:
main.py
class ControlModule:
    def __init__(self):
        self.base_motor = Motor(Ports.PORT1, True)
        self.shoulder_motor = Motor(Ports.PORT2, True)
        self.elbow_motor = Motor(Ports.PORT3, True)
        self.gripper_motor = Motor(Ports.PORT4, True)

Base Motor

Purpose: 360° rotation for environmental scanning
# Rotate base for scanning
self.control_module.rotate_motor_forward(self.control_module.base_motor, 20)
Base motor rotation speed is set to 20 RPM during scanning to ensure accurate object detection and mapping.

Shoulder Motor

Purpose: Vertical arm positioning and height adjustment
# Raise shoulder
self.control_module.rotate_motor_forward(self.control_module.shoulder_motor, 60)

# Lower shoulder on bumper collision
self.control_module.rotate_motor_reverse(self.control_module.shoulder_motor, 10)
Safety Integration: Bumper sensor monitors for collisions and triggers automatic reversal.

Elbow Motor

Purpose: Arm extension and retraction
# Controlled via ControlModule
self.control_module.elbow_motor.spin(FORWARD, speed, RPM)

Gripper Motor

Purpose: Object grasping with current-based grip detection
# Activate gripper
self.control_module.rotate_motor_forward(self.control_module.gripper_motor, 20)

# Check grip force via current draw
gripper_current = self.control_module.get_current_motor(self.control_module.gripper_motor)
if gripper_current > 0.3:  # Object gripped
    self.control_module.general_stop()
Gripper current threshold is set at 0.3A. Exceeding this value indicates successful object grip or motor stall.

Sensor Integration

Distance Sensors

Base Distance Sensor (PORT11)

Purpose: Object detection during 360° scan
main.py
# Get base distance measurement
base_distance = self.sensor_module.get_base_distance()  # Returns distance in MM

# Detection zone: 50-300mm
if 50 <= base_distance <= 300:
    object_size = self.sensor_module.get_base_object_size()
    self.sensor_module.set_color(LEDColors.OBJECT_DETECTED)
Detection Parameters:
  • Range: 50-300mm
  • Purpose: Primary object detection
  • Returns: Distance in millimeters and raw object size

Gripper Distance Sensor (PORT7)

Purpose: Close-range object detection for grasping
main.py
gripper_distance = self.sensor_module.get_gripper_distance()  # Returns distance in MM

Bumper Sensor (PORT12)

Purpose: Collision detection for shoulder safety
main.py
if self.sensor_module.is_bumper_pressed():
    self.control_module.general_stop()
    self.sensor_module.set_color(LEDColors.ERROR)
    # Reverse shoulder motor
    self.control_module.rotate_motor_reverse(self.control_module.shoulder_motor, 10)

Inertial Sensor

Purpose: Track rotation angle during scanning
main.py
# Calibrate on startup
self.sensor_module.calibrate_intertial_sensor()

# Get current heading (0-360°)
current_angle = self.sensor_module.get_angle()

# Calculate rotation delta
delta = current_angle - self.last_angle
if delta > 180: delta -= 360
elif delta < -180: delta += 360
self.accumulated_rotation += abs(delta)

TouchLED Sensor (PORT10)

Purpose: Visual status indicator
main.py
# Set LED color
self.sensor_module.set_color(LEDColors.READY)  # Green
self.sensor_module.set_color(LEDColors.ERROR)  # Red
self.sensor_module.set_color(LEDColors.RUNNING)  # Blue

Service Operations

Check Service

Purpose: Validate all hardware components before operation
main.py
def check_service(self) -> bool:
    if self.safety_module.check_sensors() and self.safety_module.check_motors():
        self.sensor_module.set_color(LEDColors.READY)
        return True
    else:
        self.sensor_module.set_color(LEDColors.ERROR)
        return False
Validation Checks:
  • All four motors installed and responsive
  • All three distance sensors operational
  • Bumper sensor functional
Response Message:
{
    "type": "check_service",
    "data": {
        "state": "approved"
    }
}
Or on error:
{
    "type": "check_service",
    "data": {
        "error": "Sensors or motors not installed"
    }
}

Safety State Service

Purpose: Sequential safety validation for shoulder and gripper mechanisms
main.py
def safety_state_service(self) -> bool:
    while self.safety_service_loop == True:
        if not self.safety_shoulder:
            safety_shoulder = self.safety_module.check_shoulder_safety()
            if safety_shoulder:
                self.safety_shoulder = True
        else:
            if not self.safety_gripper:
                safety_gripper = self.safety_module.check_gripper_safety()
                if safety_gripper:
                    self.safety_gripper = True
                    self.safety_service_loop = False
                    self.safety_mode_active = True
                    self.sensor_module.set_color(LEDColors.READY)
                    return True
    return False
1

Shoulder Safety Check

Raises shoulder until bumper sensor is pressed, then reverses to safe position
2

Gripper Safety Check

Closes gripper until current threshold is reached, confirming grip strength
3

Service Completion

Returns approval message and sets system to READY state
Reset Method:
main.py
def reset_safety_sequence(self):
    self.safety_mode_active = False
    self.safety_shoulder = False
    self.safety_gripper = False
    self.safety_service_loop = True

Scan Service

Purpose: Execute 360° rotation while detecting and mapping objects
main.py
def scan_service(self):
    while not self.scan_complete:
        if self.scan_start:
            scan_init = self.start_scan_service()
            if scan_init:
                self.scan_start = False
                self.scan_update = True
        elif self.scan_update:
            scan_finish = self.update_scan_service()
            if scan_finish:
                self.scan_update = False
                self.scan_mode_active = False
                self.scan_complete = True
                return True
    return self.scan_complete

Start Scan

main.py
def start_scan_service(self):
    self.scan_mode_active = True
    self.scan_start_time = time.time()
    self.last_angle = self.sensor_module.get_angle()
    self.control_module.rotate_motor_forward(self.control_module.base_motor, 20)
    self.sensor_module.set_color(LEDColors.RUNNING)
    return True

Update Scan

main.py
def update_scan_service(self):
    current_angle = self.sensor_module.get_angle()
    
    # Calculate rotation
    delta = current_angle - self.last_angle
    if delta > 180: delta -= 360
    elif delta < -180: delta += 360
    
    self.accumulated_rotation += abs(delta)
    self.last_angle = current_angle
    
    # Process sensor data
    sensor_data = self.perception_module.process_sensor_data()
    self.mapping_module.process_object_detection(current_angle, sensor_data['size'], sensor_data['distance']) 
            
    # Check if scan is complete
    if self.accumulated_rotation >= 360 or time.time() - self.scan_start_time > self.scan_timeout:
        self.control_module.general_stop()
        return True
    
    return False
Completion Conditions:
  • Accumulated rotation reaches 360°
  • Timeout exceeded (30 seconds)
Response Message:
{
    "type": "scan_service",
    "data": {
        "state": "complete",
        "objects": [
            {
                "center_angle": 45.5,
                "width": 12.3,
                "distance": 150,
                "max_size": 85
            }
        ]
    }
}

Real Code Examples

Complete Service Workflow

main.py
def run(self):
    self.communication.initialize()
    while True:
        try:
            # Process incoming messages
            message = self.communication.read_message()
            
            if message:
                self.sensor_module.print_screen("rx: {}".format(message), 1, 15)
                self.process_raspberry_message(message)
            
            # Execute check service
            if self.check_mode_active:
                check_service = self.check_service()
                if check_service:
                    data = {'state': 'approved'}
                    self.communication.send_message('check_service', data)
                    self.check_mode_active = False
                else:
                    data = {'error': 'Sensors or motors not installed'}
                    self.communication.send_message('check_service', data)
                    self.check_mode_active = False
            
            # Execute safety service
            if self.safety_mode_active:
                safety_state = self.safety_state_service()
                if safety_state:
                    data = {'state': 'approved'}
                    self.communication.send_message('safety_service', data)
                    self.safety_mode_active = False
            
            # Execute scan service
            if self.scan_mode_active:
                scan_complete = self.scan_service()
                if scan_complete:
                    objects_map = self.mapping_module.get_objects_map()
                    data = {'state': 'complete', 'objects': objects_map}
                    self.communication.send_message('scan_service', data)
                    self.scan_mode_active = False
                    
        except Exception as e:
            self.sensor_module.print_screen("Error: {}".format(str(e)[:20]), 1, 95)
            self.control_module.general_stop()

Message Processing

main.py
def process_raspberry_message(self, message:dict):
    if not message or 'type' not in message:
        return
        
    msg_type = message['type'].lower()
    msg_data = message.get('data', {})
        
    if msg_type == 'check_service':
        self.check_mode_active = True
        
    elif msg_type == 'safety_service':
        self.reset_safety_sequence()
        self.safety_mode_active = True
        
    elif msg_type == 'scan_service':
        self.reset_scan_sequence()
        self.scan_mode_active = True

Error Handling

main.py
try:
    # Service operations
except Exception as e:
    self.sensor_module.print_screen("Error: {}".format(str(e)[:20]), 1, 95)
    self.control_module.general_stop()
All exceptions trigger emergency stop via general_stop() to prevent hardware damage.

Best Practices

Service Sequencing

Always run check_service before safety_service, and safety_service before scan_service

Emergency Stop

Use general_stop() to halt all motors immediately in error conditions

State Reset

Call reset methods before restarting services to clear previous state

LED Feedback

Monitor TouchLED color for real-time system status during operations

Serial Communication

Message protocol and connection setup

Safety System

Detailed safety check procedures

Overview

System architecture and components

Build docs developers (and LLMs) love