Skip to main content
The Innex1 Rover’s material handling system provides action-based control for excavation and deposition operations during lunar mining missions.

Material Action Server

Location: src/lunabot_control/lunabot_control/material_action_server.py The MaterialActionServer node provides ROS 2 action interfaces for:
  • Excavation - Collecting regolith from the mining zone
  • Deposition - Dumping collected material into the construction zone

Architecture

class MaterialActionServer(Node):
    def __init__(self):
        self._excavate_server = ActionServer(
            self,
            Excavate,
            "/mission/excavate",
            execute_callback=self.execute_excavate,
            callback_group=self._callback_group,
        )
        self._deposit_server = ActionServer(
            self,
            Deposit,
            "/mission/deposit",
            execute_callback=self.execute_deposit,
            callback_group=self._callback_group,
        )
Both servers use a ReentrantCallbackGroup to allow concurrent goal handling.

Excavation Action

Parameters

ParameterDefaultDescription
excavate_nominal_duration_s8.0Expected excavation duration
loop_period_s0.2Feedback publishing rate (5 Hz)
force_failure_action""Test parameter to simulate failures

Execution Phases

The excavation action progresses through distinct phases:
1

PHASE_PRECHECK (0-20%)

Initial safety checks and system validation
2

PHASE_SPINUP (20-35%)

Excavation motor ramp-up to operational speed
3

PHASE_DIGGING (35-85%)

Active regolith collection with motor current monitoring:
feedback.excavation_motor_current_a = float(8.0 + 6.0 * progress)
Current ranges from 8A to 14A based on load.
4

PHASE_RETRACT (85-100%)

Withdraw excavation mechanism and secure material

Feedback Message

feedback.phase = Excavate.Feedback.PHASE_DIGGING
feedback.elapsed_s = float(elapsed)
feedback.fill_fraction_estimate = float(progress)
feedback.excavation_motor_current_a = float(8.0 + 6.0 * progress)
feedback.jam_detected = False
feedback.estop_active = False

Result Codes

Excavate.Result.REASON_SUCCESS          # Successful collection
Excavate.Result.REASON_CANCELED         # Client canceled goal
Excavate.Result.REASON_TIMEOUT          # Exceeded timeout_s
Excavate.Result.REASON_FORCED_FAILURE   # Test mode failure
Excavate.Result.REASON_SHUTDOWN         # Node shutdown during execution

Success Result

result.success = True
result.collected_mass_kg_estimate = 12.5  # Estimated regolith mass
result.duration_s = elapsed
The collected_mass_kg_estimate of 12.5 kg represents typical excavation yield. Actual hardware implementation should measure fill level via sensors.

Deposition Action

Parameters

ParameterDefaultDescription
deposit_nominal_duration_s5.0Expected deposition duration
loop_period_s0.2Feedback publishing rate (5 Hz)

Execution Phases

1

PHASE_PRECHECK (0-20%)

Verify bin position and door actuators
2

PHASE_OPENING (20-45%)

Open collection bin door
feedback.door_open = True
3

PHASE_RAISING (45-65%)

Raise bin to dumping angle
feedback.bed_raised = True
feedback.door_open = True
4

PHASE_DUMPING (65-85%)

Hold position while material empties
dump_duration = goal_handle.request.dump_duration_s
nominal_duration = max(nominal_duration, dump_duration + 2.0)
5

PHASE_CLOSING (85-100%)

Lower bin and close door

Actuator Monitoring

feedback.actuator_current_a = float(3.5 + 2.5 * progress)
Current ranges from 3.5A to 6.0A during operation.

Result Message

result.success = True
result.residual_fill_fraction_estimate = 0.05  # ~5% material remains
result.duration_s = elapsed
The residual_fill_fraction_estimate indicates material still in the bin after dumping. Values above 0.1 may indicate a clogged door or incomplete dump.

Goal Policies

Acceptance

def goal_callback(self, _goal_request):
    return GoalResponse.ACCEPT
All goals are accepted immediately for testing flexibility.

Cancellation

def cancel_callback(self, _goal_handle):
    return CancelResponse.ACCEPT
Clients can cancel long-running operations at any time.

Timeout Handling

Both actions support client-specified timeouts:
timeout_s = float(goal_handle.request.timeout_s)
if timeout_s > 0.0 and elapsed >= timeout_s:
    goal_handle.abort()
    return result(success=False, reason_code=REASON_TIMEOUT)
Set timeout_s to 0.0 to disable timeout enforcement.

Integration Example

import rclpy
from rclpy.action import ActionClient
from lunabot_interfaces.action import Excavate

class MissionController:
    def __init__(self):
        self._excavate_client = ActionClient(
            self, Excavate, '/mission/excavate'
        )

    def start_excavation(self):
        goal = Excavate.Goal()
        goal.timeout_s = 15.0
        
        self._excavate_client.send_goal_async(
            goal,
            feedback_callback=self.excavate_feedback
        )

    def excavate_feedback(self, feedback_msg):
        fb = feedback_msg.feedback
        print(f"Phase: {fb.phase}, Fill: {fb.fill_fraction_estimate:.1%}")

Running the Server

1

Launch the material action server

ros2 run lunabot_control material_action_server
2

Verify action availability

ros2 action list
# Should show:
# /mission/excavate
# /mission/deposit
3

Test excavation action

ros2 action send_goal /mission/excavate lunabot_interfaces/action/Excavate "{timeout_s: 10.0}"

Testing Failures

Simulate failures for integration testing:
ros2 run lunabot_control material_action_server --ros-args \
  -p force_failure_action:="excavate"
This causes all excavation goals to abort with REASON_FORCED_FAILURE.
The action server uses a MultiThreadedExecutor to handle concurrent excavation and deposition goals without blocking.

Build docs developers (and LLMs) love