Supplementary Digital Appendix for our System.
The Arms 1 length is 250 and Arms 2 length is 200.
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.
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.
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.
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.
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.
| 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 |
%% 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
[1] The university of Leeds (2026). MECH Classy Colonoscopy assignment [Powerpoint slides] School of Mechanical Engineering, Minerva.