The Classy Colonoscopy Magnetic Capsule Robotic Arms Navigation System

Supplementary Digital Appendix for our System.

Members & Roles

CAD & 3D Design

  • Harry Marsden
  • Andy Wills

Mathematics & Ethics

  • Ebony Cope
  • Imogen Dolan
  • Jiaoxiao WU

Arduino & Poster

  • Ali Waseem

MATLAB & Website

  • Yecheng WANG

1. 3D Design Walk Around Video

3D Design Walk Around Video

The Arms 1 length is 250 and Arms 2 length is 200.

2. Trajectory Simulation

MATLAB Kinematics Record

Real-time execution of the Equidistant Linear Resampling algorithm enforcing Elbow-down configuration. Based on our design, the offset of Motor in Colon Map is (-60,-60) and Arms' Length is (250,200). Current origin of the coordinate system is at the Motor.

Farthest Reachability Check

Verification of Inverse Kinematics at the extremity of the physical envelope (Max extension verification). The programme pinpointed the Unreachable Points in the workspace with red markers and skipped them for Physical Continuity.

3. Interpolation Methodology

Due to space constraints on the physical poster, the specific logic behind our Equidistant Linear Resampling and Interpolation for Constant Velocity Trajectory Generation is detailed below.

Spatial Discretization Strategy

Unlike previous segmented interpolation, our current algorithm computes the Euclidean distance dist = √[Δx² + Δy²] between each pair of the 87 original waypoints. It dynamically assigns a resampling density N = ⌈dist/Δs⌉ to maintain a uniform spatial resolution across the entire trajectory.

Constant Velocity Profile (Jerk Mitigation)

By maintaining a fixed spatial displacement between discrete steps, the system achieves a constant velocity profile when executed by the Arduino's fixed-frequency timer. This eliminates velocity spikes and minimizes mechanical jerk, which is critical for magnetic navigation.

4. Bill of Materials (BOM)

Part No. Component Name Category Quantity
n/a M3 Pan head 10mm BS 10
n/a M3 Pan head 16mm BS 8
n/a M3 Pan head 20mm BS 5
n/a M3 Pan head 25mm BS 10
n/a M3 Pan head 30mm BS 4
n/a M3 hex nut BS 38
n/a M3 washer BS 2
9 ASBS 220mm arm piece 2 S 1
12 AR6 arm connection piece 60mm S 1
10 AR6 arm connection piece 150mm S 1
11 ASBS 400mm arm piece 1 S 2
15 motor 2 hold plate S 1
1 Magic Pen Gripper 3D 1
2 Rod holder 3D 6
5 Motor housing lid 3D 1
6 Motor housing 3D 1
7 Motor 2 housing 3D 1
8 Motor 2 housing lid 3D 1
n/a 919D23 (belt) G 1
n/a 919D12 (pulley) G 2
n/a GFM-0608-04 Push in bearing G 4
n/a clamp collar G 1
n/a Rigid Coupling G 1
Category Legend: BS: Standard Fasteners (Bolts/Screws) S: Structural / Stock Parts 3D: Custom 3D Printed Components G: Gears, Bearings & Couplings

5. Codes Appendix

%% MECH2636 Robotic Arm Trajectory Simulation
% Team 4
% Author: Yecheng 'Geoffrey' WANG
% Version: 004 | Note: Global Constant Step-Size Interpolation (Velocity Optimized)
clear; clc; close all;

%% 1. Parameters
L1 = 250;           % Arm 1 Length in mm
L2 = 200;           % Arm 2 Length in mm
x_base_map = -60;   % Motor 1 X-offset in Colon Map
y_base_map = -60;   % Motor 1 Y-offset in Colon Map

% --- Gear Ratios ---
gear1 = 1.0;        % Arm 1 & Motor 1 ratio
gear2 = 1.0;        % Arm 2 & Motor 2 ratio

% --- Global Interpolation Parameter ---
% Target distance between each point in mm. 
% Smaller = Higher resolution/Slower speed. 
% Larger = Lower resolution/Faster speed.
target_step_size = 0.5; 

% --- Simulation Update Rate ---
sim_pause = 0.01; % (seconds)

%% 2. Data Loading & Constant Step-Size Interpolation
data = readtable('colon_coordinates.csv');
x_orig = data.x - x_base_map; 
y_orig = data.y - y_base_map;

% Process interpolation to maintain constant travel speed
x_robot = [];
y_robot = [];

for k = 1:length(x_orig)-1
    % Calculate the Euclidean distance of this segment
    dist_segment = sqrt((x_orig(k+1) - x_orig(k))^2 + (y_orig(k+1) - y_orig(k))^2);
    
    % Determine number of steps needed to maintain constant step size
    % Ensure at least 1 step even for very short segments
    num_steps = max(1, round(dist_segment / target_step_size));
    
    % Generate points for this segment
    segment_x = linspace(x_orig(k), x_orig(k+1), num_steps + 1);
    segment_y = linspace(y_orig(k), y_orig(k+1), num_steps + 1);
    
    % Append points (omitting the last point of segment to avoid duplicates)
    x_robot = [x_robot, segment_x(1:end-1)];
    y_robot = [y_robot, segment_y(1:end-1)];
end

% Append the very last point from the CSV
x_robot = [x_robot, x_orig(end)];
y_robot = [y_robot, y_orig(end)];

num_points = length(x_robot);
theta1_list = zeros(num_points, 1);
theta2_list = zeros(num_points, 1);
reachable = true(num_points, 1);

%% 3. IK Solver (Elbow-down)
for i = 1:num_points
    x = x_robot(i); y = y_robot(i);
    dist_sq = x^2 + y^2;
    
    % Reachability check
    if dist_sq > (L1 + L2)^2 || dist_sq < abs(L1 - L2)^2
        reachable(i) = false;
        theta1_list(i) = NaN;
        theta2_list(i) = NaN;
        continue;
    end
    
    cos_theta2 = (dist_sq - L1^2 - L2^2) / (2 * L1 * L2);
    
    % --- Elbow-down Configuration ---
    theta2 = acos(cos_theta2); 
    
    % Calculate theta1
    k1 = L1 + L2 * cos(theta2);
    k2 = L2 * sin(theta2);
    theta1 = atan2(y, x) - atan2(k2, k1);
    
    theta1_list(i) = theta1;
    theta2_list(i) = theta2;
end

%% 4. Data Export to XLSX
output_table = table((1:num_points)', x_robot', y_robot', theta1_list, theta2_list, ...
    'VariableNames', {'Point_ID', 'X_Robot_mm', 'Y_Robot_mm', 'Theta1_rad', 'Theta2_rad'});

filename = 'v4_ConstantStep_Trajectory_Output.xlsx';
writetable(output_table, filename);
fprintf('Success: %d points (with constant step size of %.2fmm) exported to %s\n', ...
        num_points, target_step_size, filename);

%% 5. Visualization
figure('Color', 'w', 'Name', 'Constant Step-Size Simulation (Elbow-down)','Position',[100,100,800,1200]);
grid on; hold on; axis equal;

% --- Adjustment 3: Fixed Axis Scale ---
xlim([-50, 320]); 
ylim([-100, 320]);

xlabel('X (mm) - Robot/Motor Map','FontSize',18);
ylabel('Y (mm) - Robot/Motor Map','FontSize',18);
set(gca,'FontSize',16);

% Plot original given points from CSV
plot(x_orig, y_orig, 'ro', 'MarkerSize', 5, 'DisplayName', 'Given 87 Keypoints');
% Plot interpolated path
plot(x_robot, y_robot, 'c.', 'MarkerSize', 2, 'DisplayName', 'Interpolation Points');

% Even if no points are unreachable, this creates the legend entry
h_unreach = plot(x_robot(~reachable), y_robot(~reachable), 'mx', 'MarkerSize', 10, ...
                 'LineWidth', 1.5, 'DisplayName', 'Unreachable Points');

h_arm = plot([0,0,0], [0,0,0], '-ok', 'LineWidth', 2, 'MarkerFaceColor', 'k', ...
             'DisplayName', 'Robotics Arms'); 

h_path = plot(NaN, NaN, 'g', 'LineWidth', 1.5, 'DisplayName', 'Actual Motion');
legend('Location', 'southwest','AutoUpdate','on','FontSize',18);

% Simulation Loop
for i = 1:num_points
    if ~reachable(i)
        continue; 
    end
    
    % Forward Kinematics for visualization
    x1 = L1 * cos(theta1_list(i));
    y1 = L1 * sin(theta1_list(i));
    x2 = x1 + L2 * cos(theta1_list(i) + theta2_list(i));
    y2 = y1 + L2 * sin(theta1_list(i) + theta2_list(i));
        
    % Update the green path showing progress
    set(h_path, 'XData', x_robot(1:i), 'YData', y_robot(1:i));

    % Update arm segments
    set(h_arm, 'XData', [0, x1, x2], 'YData', [0, y1, y2]);
    
    % Update Title
    title(sprintf('Elbow-Down | Point: %d/%d | Step: %.1fmm | Delay: %.3fs', ...
          i, num_points, target_step_size, sim_pause), 'FontSize',18);
    
    drawnow;
    pause(sim_pause); 
end

6. Reference

[1] The university of Leeds (2026). MECH Classy Colonoscopy assignment [Powerpoint slides] School of Mechanical Engineering, Minerva.