feat: First kinematics version
This commit is contained in:
@@ -1,10 +1,27 @@
|
||||
<launch>
|
||||
<let name="urdf_path" value="$(find-pkg-share wt-assign1-2025-4-parol6-static-pkg)/urdf/PAROL6.urdf.xacro"/>
|
||||
<let name="rviz_config_path" value="$(find-pkg-share wt-assign1-2025-4-parol6-static-pkg)/rviz/urdf_config.rviz"/>
|
||||
|
||||
<!-- Declare case number argument with default value -->
|
||||
<arg name="L1" default="0.18" description="Length of first link"/>
|
||||
<arg name="L2" default="0.0435" description="Length of second link"/>
|
||||
<arg name="target_x" default="0.0" description="Target x coordinate"/>
|
||||
<arg name="target_y" default="0.0" description="Target y coordinate"/>
|
||||
<arg name="case_number" default="2" description="Case number for inverse kinematics test"/>
|
||||
|
||||
<node pkg="robot_state_publisher" exec="robot_state_publisher">
|
||||
<param name="robot_description" value="$(command 'xacro $(var urdf_path)')" />
|
||||
</node>
|
||||
<node pkg="wt-assign3-2025-5-joint-state-publisher-pkg" exec="ik_node" />
|
||||
|
||||
<node pkg="wt-assign3-2025-5-joint-state-publisher-pkg" exec="ik_node">
|
||||
<param name="L1" value="$(var L1)"/>
|
||||
<param name="L2" value="$(var L2)"/>
|
||||
<param name="target_x" value="$(var target_x)"/>
|
||||
<param name="target_y" value="$(var target_y)"/>
|
||||
<param name="case_number" value="$(var case_number)"/>
|
||||
</node>
|
||||
|
||||
<!--<node pkg="joint_state_publisher_gui" exec="joint_state_publisher_gui" />-->
|
||||
|
||||
<node pkg="rviz2" exec="rviz2" output="screen" args="-d $(var rviz_config_path)" />
|
||||
</launch>
|
||||
</launch>
|
||||
|
||||
@@ -1,78 +1,198 @@
|
||||
#include <cmath>
|
||||
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
|
||||
#include <sensor_msgs/msg/joint_state.hpp>
|
||||
|
||||
#include <tf2_ros/transform_listener.h>
|
||||
|
||||
#include <tf2_ros/buffer.h>
|
||||
|
||||
#include <sensor_msgs/msg/joint_state.hpp>
|
||||
#include <geometry_msgs/msg/transform_stamped.hpp>
|
||||
|
||||
class JointPublisherNode: public rclcpp::Node {
|
||||
public: JointPublisherNode(): Node("ik_node") {
|
||||
//Declare and get parameters
|
||||
this -> declare_parameter < double > ("theta_1", 0.0);
|
||||
this -> declare_parameter < double > ("theta_2", 0.0);
|
||||
this -> declare_parameter < double > ("theta_3", 0.0);
|
||||
this -> declare_parameter < double > ("theta_4", 0.0);
|
||||
this -> declare_parameter < double > ("theta_5", 0.0);
|
||||
this -> declare_parameter < double > ("theta_6", 0.0);
|
||||
theta_1_ = this -> get_parameter("theta_1").as_double();
|
||||
theta_2_ = this -> get_parameter("theta_2").as_double();
|
||||
theta_3_ = this -> get_parameter("theta_3").as_double();
|
||||
theta_4_ = this -> get_parameter("theta_4").as_double();
|
||||
theta_5_ = this -> get_parameter("theta_5").as_double();
|
||||
theta_6_ = this -> get_parameter("theta_6").as_double();
|
||||
// Initialize the joint state publisher
|
||||
joint_state_publisher_ = this -> create_publisher < sensor_msgs::msg::JointState > ("joint_states", 10);
|
||||
// Initialize the transform buffer and listener
|
||||
tf_buffer_ = std::make_shared < tf2_ros::Buffer > (this -> get_clock());
|
||||
tf_listener_ = std::make_shared < tf2_ros::TransformListener > ( * tf_buffer_);
|
||||
RCLCPP_INFO(this -> get_logger(), "Joint states (%f %f %f %f %f %f) were sent to the system.", theta_1_, theta_2_, theta_3_, theta_4_, theta_5_, theta_6_);
|
||||
// Set up a timer to periodically check for the transform and broadcast it
|
||||
timer_ = this -> create_wall_timer(
|
||||
std::chrono::milliseconds(100),
|
||||
std::bind( & JointPublisherNode::broadcastJointState, this));
|
||||
public:
|
||||
JointPublisherNode(): Node("ik_node") {
|
||||
L1_ = this->declare_parameter<double>("L1", 0.18);
|
||||
L2_ = this->declare_parameter<double>("L2", 0.0435);
|
||||
target_x_ = this->declare_parameter<double>("target_x", 0.0);
|
||||
target_y_ = this->declare_parameter<double>("target_y", 0.0);
|
||||
case_number_ = this->declare_parameter<int>("case_number", 0);
|
||||
|
||||
calculateInverseKinematics(case_number_);
|
||||
|
||||
joint_state_publisher_ =
|
||||
this->create_publisher<sensor_msgs::msg::JointState>("joint_states", 10);
|
||||
|
||||
// transform buffer and listener
|
||||
tf_buffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
|
||||
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)
|
||||
);
|
||||
|
||||
RCLCPP_INFO(this->get_logger(),
|
||||
"Case %d: Joint states (%f %f %f %f %f %f) were calculated and sent to the system.",
|
||||
case_number_, theta_1_, theta_2_, theta_3_, theta_4_, theta_5_, theta_6_
|
||||
);
|
||||
|
||||
// periodically broadcast joint state
|
||||
timer_ = this->create_wall_timer(
|
||||
std::chrono::milliseconds(100),
|
||||
std::bind(&JointPublisherNode::broadcastJointState, this)
|
||||
);
|
||||
}
|
||||
private: void broadcastJointState() {
|
||||
// Create a JointState message
|
||||
auto joint_state = sensor_msgs::msg::JointState();
|
||||
joint_state.header.stamp = this -> get_clock() -> now();
|
||||
joint_state.name = {
|
||||
"L1", // x
|
||||
"L2", // x
|
||||
"L3", // x
|
||||
"L4",
|
||||
"L5", // x
|
||||
"L6"
|
||||
};
|
||||
joint_state.position = {
|
||||
theta_1_,
|
||||
theta_2_,
|
||||
theta_3_,
|
||||
theta_4_,
|
||||
theta_5_,
|
||||
theta_6_
|
||||
};
|
||||
// Publish the joint state
|
||||
joint_state_publisher_ -> publish(joint_state);
|
||||
}
|
||||
double theta_1_;
|
||||
double theta_2_;
|
||||
double theta_3_;
|
||||
double theta_4_;
|
||||
double theta_5_;
|
||||
double theta_6_;
|
||||
rclcpp::Publisher < sensor_msgs::msg::JointState > ::SharedPtr
|
||||
joint_state_publisher_;
|
||||
std::shared_ptr < tf2_ros::Buffer > tf_buffer_;
|
||||
std::shared_ptr < tf2_ros::TransformListener > tf_listener_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
};
|
||||
int main(int argc, char ** argv) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared < JointPublisherNode > ();
|
||||
rclcpp::spin(node);
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr joint_state_publisher_;
|
||||
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
|
||||
std::shared_ptr<tf2_ros::TransformListener> tf_listener_;
|
||||
OnSetParametersCallbackHandle::SharedPtr param_callback_handle_;
|
||||
|
||||
double target_x_;
|
||||
double target_y_;
|
||||
double L1_;
|
||||
double L2_;
|
||||
int case_number_;
|
||||
|
||||
double theta_1_;
|
||||
double theta_2_;
|
||||
double theta_3_;
|
||||
double theta_4_;
|
||||
double theta_5_;
|
||||
double theta_6_;
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult parametersCallback(
|
||||
const std::vector<rclcpp::Parameter> ¶meters) {
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
|
||||
bool recalculate = false;
|
||||
|
||||
for (const auto ¶m : parameters) {
|
||||
if (param.get_name() == "target_x") {
|
||||
target_x_ = param.as_double();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated target_x to %f", target_x_);
|
||||
} else if (param.get_name() == "target_y") {
|
||||
target_y_ = param.as_double();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated target_y to %f", target_y_);
|
||||
} else if (param.get_name() == "L1") {
|
||||
L1_ = 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();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated L2 to %f", L2_);
|
||||
} else if (param.get_name() == "case_number") {
|
||||
case_number_ = param.as_int();
|
||||
recalculate = true;
|
||||
RCLCPP_INFO(this->get_logger(), "Updated case_number to %d", case_number_);
|
||||
}
|
||||
}
|
||||
|
||||
if (recalculate) {
|
||||
calculateInverseKinematics(case_number_);
|
||||
RCLCPP_INFO(this->get_logger(),
|
||||
"Recalculated joint states: (%f %f %f %f %f %f)",
|
||||
theta_1_, theta_2_, theta_3_, theta_4_, theta_5_, theta_6_);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void broadcastJointState() {
|
||||
auto joint_state = sensor_msgs::msg::JointState();
|
||||
joint_state.header.stamp = this->get_clock()->now();
|
||||
joint_state.name = { "L1", "L2", "L3", "L4", "L5", "L6" };
|
||||
joint_state.position = { theta_1_, theta_2_, theta_3_, theta_4_, theta_5_, theta_6_ };
|
||||
|
||||
joint_state_publisher_->publish(joint_state);
|
||||
}
|
||||
|
||||
void calculateInverseKinematics(int case_number) {
|
||||
double x, y;
|
||||
|
||||
// Use target_x_ and target_y_ if explicitly set via parameters
|
||||
// Otherwise use predefined case positions
|
||||
bool use_target_params = (target_x_ != 0.0 || target_y_ != 0.0);
|
||||
|
||||
if (use_target_params) {
|
||||
x = target_x_;
|
||||
y = target_y_;
|
||||
RCLCPP_INFO(this->get_logger(), "Using explicit target position");
|
||||
} else {
|
||||
switch(case_number) {
|
||||
case 1:
|
||||
x = 0.20;
|
||||
y = 0.10;
|
||||
break;
|
||||
case 2:
|
||||
x = 0.15;
|
||||
y = 0.15;
|
||||
break;
|
||||
case 3:
|
||||
x = 0.18;
|
||||
y = 0.05;
|
||||
break;
|
||||
case 4:
|
||||
x = 0.12;
|
||||
y = 0.12;
|
||||
break;
|
||||
default:
|
||||
x = 0.15;
|
||||
y = 0.10;
|
||||
break;
|
||||
}
|
||||
RCLCPP_INFO(this->get_logger(), "Using case %d position", case_number);
|
||||
}
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Calculating IK for target position: x=%f, y=%f", x, y);
|
||||
|
||||
// theta_2 (law of cosines)
|
||||
double cos_theta_2 = (x*x + y*y - L1_*L1_ - L2_*L2_) / (2 * L1_ * L2_);
|
||||
|
||||
// Check if solution is valid
|
||||
if (cos_theta_2 < -1.0 || cos_theta_2 > 1.0) {
|
||||
RCLCPP_WARN(this->get_logger(), "target position is out of reach, using default angles");
|
||||
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;
|
||||
}
|
||||
|
||||
double sin_theta_2 = std::sqrt(1 - cos_theta_2 * cos_theta_2);
|
||||
double theta_2 = std::atan2(sin_theta_2, cos_theta_2);
|
||||
|
||||
double k1 = L1_ + L2_ * cos_theta_2;
|
||||
double k2 = L2_ * sin_theta_2;
|
||||
|
||||
double gamma = std::atan2(k2, k1);
|
||||
double theta_1 = std::atan2(y, x) - gamma;
|
||||
|
||||
// Convert to robot-specific joint angles
|
||||
double theta_0 = 0.0; // Base rotation (theta_0)
|
||||
theta_1_ = theta_0; // L1 joint
|
||||
theta_2_ = theta_1 - M_PI / 2; // L2 joint
|
||||
theta_3_ = theta_2; // L3 joint
|
||||
theta_4_ = -theta_1 - theta_2 - M_PI / 2; // L4 joint
|
||||
theta_5_ = theta_0; // L5 joint
|
||||
theta_6_ = 0.0091; // L6 joint (fixed)
|
||||
|
||||
RCLCPP_INFO(this->get_logger(), "Calculated angles - theta_1: %f, theta_2: %f", theta_1, theta_2);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
rclcpp::init(argc, argv);
|
||||
auto node = std::make_shared<JointPublisherNode>();
|
||||
rclcpp::spin(node);
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user