feat: Connect4 board movement
This commit is contained in:
@@ -9,9 +9,14 @@
|
||||
class JointPublisherNode: public rclcpp::Node {
|
||||
public:
|
||||
JointPublisherNode(): Node("ik_node") {
|
||||
L0_ = this->declare_parameter<double>("L0", 0.1105); // Base height to L2 joint
|
||||
L1_ = this->declare_parameter<double>("L1", 0.18);
|
||||
L2_ = this->declare_parameter<double>("L2", 0.0435);
|
||||
// PAROL6 link lengths from DH parameters (in meters)
|
||||
a1_ = this->declare_parameter<double>("a1", 0.11050); // Base height
|
||||
a2_ = this->declare_parameter<double>("a2", 0.02342); // Base offset
|
||||
a3_ = this->declare_parameter<double>("a3", 0.18000); // Shoulder to elbow
|
||||
a4_ = this->declare_parameter<double>("a4", 0.04350); // Elbow offset
|
||||
a5_ = this->declare_parameter<double>("a5", 0.17635); // Elbow to wrist
|
||||
a6_ = this->declare_parameter<double>("a6", 0.06280); // Wrist offset
|
||||
a7_ = this->declare_parameter<double>("a7", 0.04525); // End effector length
|
||||
target_x_ = this->declare_parameter<double>("target_x", 0.0);
|
||||
target_y_ = this->declare_parameter<double>("target_y", 0.0);
|
||||
target_z_ = this->declare_parameter<double>("target_z", 0.0);
|
||||
@@ -27,9 +32,57 @@ public:
|
||||
tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
|
||||
|
||||
// Set up parameter callback for dynamic reconfiguration
|
||||
param_callback_handle_ = this->add_on_set_parameters_callback(
|
||||
std::bind(&JointPublisherNode::parametersCallback, this, std::placeholders::_1)
|
||||
);
|
||||
// param_callback_handle_ = this->add_on_set_parameters_callback(
|
||||
// std::bind(&JointPublisherNode::parametersCallback, this, std::placeholders::_1)
|
||||
// );
|
||||
|
||||
// Connect 4 board parameters
|
||||
// Board is at x=0.6, y=0.0, z=0.0 (from BoardLocationPublisher)
|
||||
// Board offset from base_conn_4 to board_conn_4: x=0.281, y=0.031, z=0.006
|
||||
double board_base_x = 0.60;
|
||||
double board_base_y = 0.00;
|
||||
double board_base_z = 0.00;
|
||||
double board_offset_x = 0.281;
|
||||
double board_offset_y = 0.031;
|
||||
double board_offset_z = 0.006;
|
||||
|
||||
// Connect 4 is 7 columns x 6 rows
|
||||
int cols = 7;
|
||||
int rows = 6;
|
||||
double cell_spacing_x = 0.0; // Horizontal spacing between columns
|
||||
double cell_spacing_y = 0.015; // 15mm spacing between columns
|
||||
double cell_spacing_z = 0.015; // 15mm spacing between rows
|
||||
|
||||
// Starting position for top-left cell
|
||||
double board_start_x = board_base_x + board_offset_x;
|
||||
double board_start_y = board_base_y + board_offset_y - (cols - 1) * cell_spacing_y / 2.0;
|
||||
double board_start_z = board_base_z + board_offset_z + 0.10; // Above the board
|
||||
|
||||
int current_col = 0;
|
||||
int current_row = 0;
|
||||
|
||||
while(true) {
|
||||
// Calculate target position for current cell
|
||||
target_x_ = board_start_x + current_row * cell_spacing_x;
|
||||
target_y_ = board_start_y + current_col * cell_spacing_y;
|
||||
target_z_ = board_start_z - current_row * cell_spacing_z;
|
||||
|
||||
// Move to position and update
|
||||
calculateInverseKinematics(case_number_);
|
||||
broadcastJointState();
|
||||
|
||||
// Move to next cell
|
||||
current_col++;
|
||||
if (current_col >= cols) {
|
||||
current_col = 0;
|
||||
current_row++;
|
||||
if (current_row >= rows) {
|
||||
current_row = 0; // Start over from top-left
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Pause at each cell
|
||||
}
|
||||
|
||||
RCLCPP_INFO(this->get_logger(),
|
||||
"Case %d: Joint states (%f %f %f %f %f %f) were calculated and sent to the system.",
|
||||
@@ -54,9 +107,14 @@ private:
|
||||
double target_x_;
|
||||
double target_y_;
|
||||
double target_z_;
|
||||
double L0_; // Base height
|
||||
double L1_;
|
||||
double L2_;
|
||||
// PAROL6 DH parameters (link lengths in meters)
|
||||
double a1_; // Base height (110.5mm)
|
||||
double a2_; // Base offset (23.42mm)
|
||||
double a3_; // Shoulder to elbow (180mm)
|
||||
double a4_; // Elbow offset (43.5mm)
|
||||
double a5_; // Elbow to wrist (176.35mm)
|
||||
double a6_; // Wrist offset (62.8mm)
|
||||
double a7_; // End effector length (45.25mm)
|
||||
int case_number_;
|
||||
|
||||
double theta_1_;
|
||||
@@ -87,18 +145,18 @@ private:
|
||||
target_z_ = param.as_double();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated target_z to %f", target_z_);
|
||||
} else if (param.get_name() == "L0") {
|
||||
L0_ = param.as_double();
|
||||
} else if (param.get_name() == "a1") {
|
||||
a1_ = param.as_double();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated L0 to %f", L0_);
|
||||
} else if (param.get_name() == "L1") {
|
||||
L1_ = param.as_double();
|
||||
RCLCPP_INFO(this->get_logger(), "Updated a1 to %f", a1_);
|
||||
} else if (param.get_name() == "a3") {
|
||||
a3_ = param.as_double();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated L1 to %f", L1_);
|
||||
} else if (param.get_name() == "L2") {
|
||||
L2_ = param.as_double();
|
||||
RCLCPP_INFO(this->get_logger(), "Updated a3 to %f", a3_);
|
||||
} else if (param.get_name() == "a5") {
|
||||
a5_ = param.as_double();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated L2 to %f", L2_);
|
||||
RCLCPP_INFO(this->get_logger(), "Updated a5 to %f", a5_);
|
||||
} else if (param.get_name() == "case_number") {
|
||||
case_number_ = param.as_int();
|
||||
recalculate = true;
|
||||
@@ -138,31 +196,32 @@ private:
|
||||
z = target_z_;
|
||||
RCLCPP_INFO(this->get_logger(), "Using explicit target position");
|
||||
} else {
|
||||
// Default test positions for different cases
|
||||
switch(case_number) {
|
||||
case 1:
|
||||
x = 0.20;
|
||||
y = 0.10;
|
||||
z = 0.05;
|
||||
z = 0.15;
|
||||
break;
|
||||
case 2:
|
||||
x = 0.15;
|
||||
y = 0.15;
|
||||
z = 0.08;
|
||||
z = 0.20;
|
||||
break;
|
||||
case 3:
|
||||
x = 0.18;
|
||||
y = 0.05;
|
||||
z = 0.10;
|
||||
z = 0.18;
|
||||
break;
|
||||
case 4:
|
||||
x = 0.12;
|
||||
y = 0.12;
|
||||
z = 0.06;
|
||||
z = 0.15;
|
||||
break;
|
||||
default:
|
||||
x = 0.15;
|
||||
y = 0.10;
|
||||
z = 0.05;
|
||||
z = 0.15;
|
||||
break;
|
||||
}
|
||||
RCLCPP_INFO(this->get_logger(), "Using case %d position", case_number);
|
||||
@@ -170,50 +229,73 @@ private:
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Calculating IK for target position: x=%f, y=%f, z=%f", x, y, z);
|
||||
|
||||
// Adjust z for base height (z is measured from world, but we need z from L2 joint)
|
||||
double z_from_base = z - L0_;
|
||||
// Calculate the horizontal reach needed (r) and account for vertical component
|
||||
double r = std::sqrt(x*x + y*y); // horizontal distance from base
|
||||
// PAROL6 Inverse Kinematics Solution
|
||||
// Based on DH parameters and kinematic structure
|
||||
|
||||
// theta_2 (law of cosines) - using r and z_from_base for 2D arm planning
|
||||
double reach_distance = std::sqrt(r*r + z_from_base*z_from_base);
|
||||
double cos_theta_2 = (reach_distance*reach_distance - L1_*L1_ - L2_*L2_) / (2 * L1_ * L2_);
|
||||
// Joint 1 (Base rotation) - simple atan2 for rotation around Z axis
|
||||
theta_1_ = std::atan2(y, x);
|
||||
|
||||
// Check if solution is valid
|
||||
if (cos_theta_2 < -1.0 || cos_theta_2 > 1.0) {
|
||||
RCLCPP_WARN(this->get_logger(), "target position (r=%f, z=%f) is out of reach, using default angles", r, z_from_base);
|
||||
theta_1_ = 0.0;
|
||||
theta_2_ = 0.0;
|
||||
theta_3_ = 0.0;
|
||||
theta_4_ = 0.0;
|
||||
theta_5_ = 0.0;
|
||||
theta_6_ = 0.0091;
|
||||
return;
|
||||
// Calculate wrist center position (subtract end effector)
|
||||
// The wrist center is located before the last 3 joints (wrist assembly)
|
||||
double r = std::sqrt(x*x + y*y); // Radial distance from base
|
||||
|
||||
// Account for base offset a2 in the radial direction
|
||||
double r_adjusted = r - a2_;
|
||||
|
||||
// Height from shoulder joint (which is at height a1)
|
||||
double z_from_shoulder = z - a1_;
|
||||
|
||||
// Distance from shoulder to wrist center in the 2D plane (r, z)
|
||||
// Subtract the wrist assembly length a5 from the reach
|
||||
double wrist_r = r_adjusted;
|
||||
double wrist_z = z_from_shoulder;
|
||||
|
||||
// Distance from shoulder to wrist position
|
||||
double d = std::sqrt(wrist_r*wrist_r + wrist_z*wrist_z);
|
||||
|
||||
// Check if target is reachable
|
||||
// Add some tolerance and account for offsets (a4, a6, a7)
|
||||
double max_reach = a3_ + a5_ + a4_ + a7_; // Full extension plus offsets
|
||||
double min_reach = std::abs(a3_ - a5_) * 0.5; // More lenient minimum
|
||||
|
||||
if (d > max_reach) {
|
||||
RCLCPP_WARN(this->get_logger(),
|
||||
"Target position (d=%f) is out of reach (max=%f), clamping to max reach",
|
||||
d, max_reach);
|
||||
// Scale down to max reach instead of failing
|
||||
double scale = max_reach / d;
|
||||
wrist_r *= scale;
|
||||
wrist_z *= scale;
|
||||
d = max_reach;
|
||||
} else if (d < min_reach) {
|
||||
RCLCPP_WARN(this->get_logger(),
|
||||
"Target position (d=%f) too close (min=%f), using minimum reach",
|
||||
d, min_reach);
|
||||
d = min_reach;
|
||||
}
|
||||
|
||||
double sin_theta_2 = std::sqrt(1 - cos_theta_2 * cos_theta_2);
|
||||
double theta_2 = std::atan2(sin_theta_2, cos_theta_2);
|
||||
// Joint 3 (Elbow) - law of cosines
|
||||
// Use negative angle for "elbow down" configuration to allow arm extension
|
||||
double cos_theta_3 = (d*d - a3_*a3_ - a5_*a5_) / (2 * a3_ * a5_);
|
||||
cos_theta_3 = std::max(-1.0, std::min(1.0, cos_theta_3)); // Clamp to [-1, 1]
|
||||
theta_3_ = -std::acos(cos_theta_3); // Negative for elbow-down configuration
|
||||
|
||||
double k1 = L1_ + L2_ * cos_theta_2;
|
||||
double k2 = L2_ * sin_theta_2;
|
||||
// Joint 2 (Shoulder) - geometric solution
|
||||
double alpha = std::atan2(wrist_z, wrist_r); // Angle to target
|
||||
double beta = std::atan2(a5_ * std::sin(-theta_3_), a3_ + a5_ * std::cos(-theta_3_));
|
||||
theta_2_ = alpha - beta;
|
||||
|
||||
// Calculate the angle to the target in the r-z plane
|
||||
double alpha = std::atan2(z_from_base, r);
|
||||
double gamma = std::atan2(k2, k1);
|
||||
double theta_1 = alpha - gamma;
|
||||
// Wrist joints - position gripper downward for dropping pieces
|
||||
// theta_4: Wrist pitch - point down
|
||||
// theta_5: Wrist roll - keep level
|
||||
// theta_6: Gripper rotation - keep aligned
|
||||
theta_4_ = -M_PI / 2; // Point gripper down
|
||||
theta_5_ = 0.0;
|
||||
theta_6_ = 0.0;
|
||||
|
||||
// Base rotation to point towards (x, y)
|
||||
double theta_0 = std::atan2(y, x);
|
||||
|
||||
// Convert to robot-specific joint angles
|
||||
// theta_1_ = theta_0; // L1 joint - base rotation
|
||||
theta_2_ = theta_1 - M_PI / 2; // L2 joint - shoulder
|
||||
theta_3_ = theta_2; // L3 joint - elbow
|
||||
theta_4_ = -theta_1 - theta_2 - M_PI / 2; // L4 joint - wrist to point down
|
||||
theta_5_ = theta_0; // L5 joint - wrist rotation
|
||||
theta_6_ = 0.0091; // L6 joint (fixed)
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Calculated angles - base: %f, theta_1: %f, theta_2: %f", theta_0, theta_1, theta_2);
|
||||
RCLCPP_INFO(this->get_logger(),
|
||||
"Calculated angles - J1(base): %f, J2(shoulder): %f, J3(elbow): %f",
|
||||
theta_1_, theta_2_, theta_3_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user