diff --git a/crane_plus_examples/CMakeLists.txt b/crane_plus_examples/CMakeLists.txt
index 421bb58..7f4906f 100644
--- a/crane_plus_examples/CMakeLists.txt
+++ b/crane_plus_examples/CMakeLists.txt
@@ -17,12 +17,15 @@ endif()
# find dependencies
find_package(ament_cmake REQUIRED)
+find_package(angles REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(geometry_msgs REQUIRED)
+find_package(image_geometry REQUIRED)
+find_package(image_transport REQUIRED)
+find_package(message_filters REQUIRED)
find_package(moveit_ros_planning_interface REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
-find_package(image_geometry REQUIRED)
# Build and install node executables
set(executable_list
@@ -37,13 +40,16 @@ set(executable_list
foreach(loop_var IN LISTS executable_list)
add_executable(${loop_var} src/${loop_var}.cpp)
ament_target_dependencies(${loop_var}
+ angles
cv_bridge
geometry_msgs
+ image_geometry
+ image_transport
+ message_filters
moveit_ros_planning_interface
OpenCV
rclcpp
tf2_geometry_msgs
- image_geometry
)
install(TARGETS
diff --git a/crane_plus_examples/package.xml b/crane_plus_examples/package.xml
index 1232d80..9cbcde9 100644
--- a/crane_plus_examples/package.xml
+++ b/crane_plus_examples/package.xml
@@ -13,17 +13,20 @@
ament_cmake
+ angles
crane_plus_control
crane_plus_description
crane_plus_moveit_config
cv_bridge
geometry_msgs
+ image_geometry
+ image_transport
libopencv-dev
+ message_filters
moveit_ros_planning_interface
rclcpp
tf2_geometry_msgs
usb_cam
- image_geometry
ament_lint_auto
ament_lint_common
diff --git a/crane_plus_examples/src/aruco_detection.cpp b/crane_plus_examples/src/aruco_detection.cpp
index 77ca11d..dbc290f 100644
--- a/crane_plus_examples/src/aruco_detection.cpp
+++ b/crane_plus_examples/src/aruco_detection.cpp
@@ -16,7 +16,6 @@
// https://docs.opencv.org/4.2.0/d5/dae/tutorial_aruco_detection.html
// https://docs.ros.org/en/humble/Tutorials/Intermediate/Tf2/Writing-A-Tf2-Broadcaster-Cpp.html
-#include
#include
#include
#include
@@ -27,11 +26,13 @@
#include "sensor_msgs/msg/image.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/aruco.hpp"
+#include "opencv2/core/quaternion.hpp"
#include "cv_bridge/cv_bridge.hpp"
-#include "tf2/LinearMath/Quaternion.hpp"
-#include "tf2/LinearMath/Matrix3x3.hpp"
#include "tf2_ros/transform_broadcaster.h"
+#include "image_transport/image_transport.hpp"
+#include "image_transport/camera_subscriber.hpp"
using std::placeholders::_1;
+using std::placeholders::_2;
class ImageSubscriber : public rclcpp::Node
{
@@ -39,88 +40,71 @@ class ImageSubscriber : public rclcpp::Node
ImageSubscriber()
: Node("aruco_detection")
{
- image_subscription_ = this->create_subscription(
- "image_raw", 10, std::bind(&ImageSubscriber::image_callback, this, _1));
+ camera_subscription_ = image_transport::create_camera_subscription(
+ this,
+ "image_raw",
+ std::bind(&ImageSubscriber::camera_callback, this, _1, _2),
+ "raw");
- camera_info_subscription_ = this->create_subscription(
- "camera_info", 10, std::bind(&ImageSubscriber::camera_info_callback, this, _1));
+ // ArUcoマーカのデータセットを読み込む
+ // DICT_6x6_50は6x6ビットのマーカが50個収録されたもの
+ marker_dict_ = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_50);
tf_broadcaster_ =
std::make_unique(*this);
}
private:
- rclcpp::Subscription::SharedPtr image_subscription_;
- rclcpp::Subscription::SharedPtr camera_info_subscription_;
- sensor_msgs::msg::CameraInfo::SharedPtr camera_info_;
+ image_transport::CameraSubscriber camera_subscription_;
std::unique_ptr tf_broadcaster_;
+ cv::Ptr marker_dict_;
- void image_callback(const sensor_msgs::msg::Image::SharedPtr msg)
+ void camera_callback(
+ const sensor_msgs::msg::Image::ConstSharedPtr & img_msg,
+ const sensor_msgs::msg::CameraInfo::ConstSharedPtr & info_msg)
{
- auto cv_img = cv_bridge::toCvShare(msg, msg->encoding);
+ auto cv_img = cv_bridge::toCvShare(img_msg, img_msg->encoding);
cv::cvtColor(cv_img->image, cv_img->image, cv::COLOR_RGB2BGR);
- if (camera_info_) {
- // ArUcoマーカのデータセットを読み込む
- // DICT_6x6_50は6x6ビットのマーカが50個収録されたもの
- const auto MARKER_DICT = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_50);
- // マーカID
- std::vector ids;
- // 画像座標系上のマーカ頂点位置
- std::vector> corners;
- // マーカの検出
- cv::aruco::detectMarkers(cv_img->image, MARKER_DICT, corners, ids);
- // マーカの検出数
- int n_markers = ids.size();
- // カメラパラメータ
- const auto CAMERA_MATRIX = cv::Mat(3, 3, CV_64F, camera_info_->k.data());
- const auto DIST_COEFFS = cv::Mat(1, 5, CV_64F, camera_info_->d.data());
- // マーカ一辺の長さ 0.04 [m]
- const float MARKER_LENGTH = 0.04;
+ // マーカID
+ std::vector ids;
+ // 画像座標系上のマーカ頂点位置
+ std::vector> corners;
+ // マーカの検出
+ cv::aruco::detectMarkers(cv_img->image, marker_dict_, corners, ids);
+ // マーカの検出数
+ int n_markers = ids.size();
- // マーカが一つ以上検出された場合、マーカの位置姿勢をtfで配信
- if (n_markers > 0) {
- for (int i = 0; i < n_markers; i++) {
- // マーカの回転ベクトルと位置ベクトル
- std::vector rvecs, tvecs;
- // 画像座標系上のマーカ位置を三次元のカメラ座標系に変換
- cv::aruco::estimatePoseSingleMarkers(
- corners, MARKER_LENGTH, CAMERA_MATRIX, DIST_COEFFS, rvecs, tvecs);
-
- // tfの配信
- geometry_msgs::msg::TransformStamped t;
- t.header = msg->header;
- t.child_frame_id = "target_" + std::to_string(ids[i]);
- t.transform.translation.x = tvecs[i][0];
- t.transform.translation.y = tvecs[i][1];
- t.transform.translation.z = tvecs[i][2];
- tf2::Quaternion q;
- cv::Mat cv_rotation_matrix;
- cv::Rodrigues(rvecs[i], cv_rotation_matrix);
- tf2::Matrix3x3 tf2_rotation_matrix = tf2::Matrix3x3(
- cv_rotation_matrix.at(0, 0),
- cv_rotation_matrix.at(0, 1),
- cv_rotation_matrix.at(0, 2),
- cv_rotation_matrix.at(1, 0),
- cv_rotation_matrix.at(1, 1),
- cv_rotation_matrix.at(1, 2),
- cv_rotation_matrix.at(2, 0),
- cv_rotation_matrix.at(2, 1),
- cv_rotation_matrix.at(2, 2));
- tf2_rotation_matrix.getRotation(q);
- t.transform.rotation.x = q.x();
- t.transform.rotation.y = q.y();
- t.transform.rotation.z = q.z();
- t.transform.rotation.w = q.w();
- tf_broadcaster_->sendTransform(t);
- }
- }
+ if (n_markers <= 0) {
+ return;
}
- }
- void camera_info_callback(const sensor_msgs::msg::CameraInfo::SharedPtr msg)
- {
- camera_info_ = msg;
+ // カメラパラメータの読み込み
+ const auto CAMERA_MATRIX = cv::Mat(3, 3, CV_64F, const_cast(info_msg->k.data()));
+ const auto DIST_COEFFS = cv::Mat(1, 5, CV_64F, const_cast(info_msg->d.data()));
+ // マーカ一辺の長さ 0.04 [m]
+ const float MARKER_LENGTH = 0.04;
+ // マーカの回転ベクトルと位置ベクトル
+ std::vector rvecs, tvecs;
+ // 画像座標系上のマーカ位置を三次元のカメラ座標系に変換
+ cv::aruco::estimatePoseSingleMarkers(
+ corners, MARKER_LENGTH, CAMERA_MATRIX, DIST_COEFFS, rvecs, tvecs);
+
+ // tfの配信
+ for (int i = 0; i < n_markers; i++) {
+ geometry_msgs::msg::TransformStamped t;
+ t.header = img_msg->header;
+ t.child_frame_id = "target_" + std::to_string(ids[i]);
+ t.transform.translation.x = tvecs[i][0];
+ t.transform.translation.y = tvecs[i][1];
+ t.transform.translation.z = tvecs[i][2];
+ cv::Quatd cv_q = cv::Quatd::createFromRvec(rvecs[i]);
+ t.transform.rotation.x = cv_q.x;
+ t.transform.rotation.y = cv_q.y;
+ t.transform.rotation.z = cv_q.z;
+ t.transform.rotation.w = cv_q.w;
+ tf_broadcaster_->sendTransform(t);
+ }
}
};
diff --git a/crane_plus_examples/src/color_detection.cpp b/crane_plus_examples/src/color_detection.cpp
index 33e7389..d22ff0f 100644
--- a/crane_plus_examples/src/color_detection.cpp
+++ b/crane_plus_examples/src/color_detection.cpp
@@ -15,23 +15,25 @@
// Reference:
// https://www.opencv-srf.com/2010/09/object-detection-using-color-seperation.html
-#include
-#include
-#include
#include
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/transform_stamped.hpp"
#include "sensor_msgs/msg/camera_info.hpp"
#include "sensor_msgs/msg/image.hpp"
-#include "tf2/LinearMath/Quaternion.hpp"
-#include "tf2/LinearMath/Matrix3x3.hpp"
#include "tf2_ros/transform_broadcaster.h"
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "cv_bridge/cv_bridge.hpp"
#include "image_geometry/pinhole_camera_model.hpp"
+#include "image_transport/image_transport.hpp"
+#include "image_transport/subscriber_filter.hpp"
+#include "message_filters/subscriber.h"
+#include "message_filters/synchronizer.h"
+#include "message_filters/sync_policies/exact_time.h"
+
using std::placeholders::_1;
+using std::placeholders::_2;
class ImageSubscriber : public rclcpp::Node
{
@@ -39,11 +41,12 @@ class ImageSubscriber : public rclcpp::Node
ImageSubscriber()
: Node("color_detection")
{
- image_subscription_ = this->create_subscription(
- "image_raw", 10, std::bind(&ImageSubscriber::image_callback, this, _1));
+ color_sub_.subscribe(this, "image_raw", "raw");
+ info_sub_.subscribe(this, "camera_info");
- camera_info_subscription_ = this->create_subscription(
- "camera_info", 10, std::bind(&ImageSubscriber::camera_info_callback, this, _1));
+ sync_ = std::make_unique>(
+ ExactPolicy(10), color_sub_, info_sub_);
+ sync_->registerCallback(std::bind(&ImageSubscriber::camera_callback, this, _1, _2));
image_thresholded_publisher_ =
this->create_publisher("image_thresholded", 10);
@@ -53,115 +56,114 @@ class ImageSubscriber : public rclcpp::Node
}
private:
- rclcpp::Subscription::SharedPtr image_subscription_;
- rclcpp::Subscription::SharedPtr camera_info_subscription_;
+ using ExactPolicy = message_filters::sync_policies::ExactTime<
+ sensor_msgs::msg::Image,
+ sensor_msgs::msg::CameraInfo>;
+ image_transport::SubscriberFilter color_sub_;
+ message_filters::Subscriber info_sub_;
+ std::unique_ptr> sync_;
rclcpp::Publisher::SharedPtr image_thresholded_publisher_;
- sensor_msgs::msg::CameraInfo::SharedPtr camera_info_;
std::unique_ptr tf_broadcaster_;
- void image_callback(const sensor_msgs::msg::Image::SharedPtr msg)
+ void camera_callback(
+ const sensor_msgs::msg::Image::ConstSharedPtr & img_msg,
+ const sensor_msgs::msg::CameraInfo::ConstSharedPtr & info_msg)
{
- // カメラのパラメータを取得してから処理を行う
- if (camera_info_) {
- // 赤い物体を検出するようにHSVの範囲を設定
- // 周囲の明るさ等の動作環境に合わせて調整
- const int LOW_H_1 = 0, HIGH_H_1 = 10;
- const int LOW_H_2 = 170, HIGH_H_2 = 179;
- const int LOW_S = 100, HIGH_S = 255;
- const int LOW_V = 100, HIGH_V = 255;
-
- // ウェブカメラの画像を受け取る
- auto cv_img = cv_bridge::toCvShare(msg, msg->encoding);
-
- // 画像をRGBからHSVに変換(取得したカメラ画像にフォーマットを合わせる)
- cv::cvtColor(cv_img->image, cv_img->image, cv::COLOR_RGB2HSV);
-
- // 画像処理用の変数を用意
- cv::Mat img_mask_1;
- cv::Mat img_mask_2;
- cv::Mat img_thresholded;
-
- // 画像の二値化
- cv::inRange(
- cv_img->image,
- cv::Scalar(LOW_H_1, LOW_S, LOW_V),
- cv::Scalar(HIGH_H_1, HIGH_S, HIGH_V),
- img_mask_1);
- cv::inRange(
- cv_img->image,
- cv::Scalar(LOW_H_2, LOW_S, LOW_V),
- cv::Scalar(HIGH_H_2, HIGH_S, HIGH_V),
- img_mask_2);
-
- // マスク画像の合成
- img_thresholded = img_mask_1 | img_mask_2;
-
- // ノイズ除去の処理
- cv::morphologyEx(
- img_thresholded,
- img_thresholded,
- cv::MORPH_OPEN,
- cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)));
-
- // 穴埋めの処理
- cv::morphologyEx(
- img_thresholded,
- img_thresholded,
- cv::MORPH_CLOSE,
- cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)));
-
- // 画像の検出領域におけるモーメントを計算
- cv::Moments moment = moments(img_thresholded);
- double d_m01 = moment.m01;
- double d_m10 = moment.m10;
- double d_area = moment.m00;
-
- // 検出した領域のピクセル数が10000より大きい場合
- if (d_area > 10000) {
- // カメラモデル作成
- image_geometry::PinholeCameraModel camera_model;
-
- // カメラのパラメータを設定
- camera_model.fromCameraInfo(camera_info_);
-
- // 画像座標系における把持対象物の位置(2D)
- const double pixel_x = d_m10 / d_area;
- const double pixel_y = d_m01 / d_area;
- const cv::Point2d point(pixel_x, pixel_y);
-
- // 補正後の画像座標系における把持対象物の位置を取得(2D)
- const cv::Point2d rect_point = camera_model.rectifyPoint(point);
-
- // カメラ座標系から見た把持対象物の方向(Ray)を取得する
- const cv::Point3d ray = camera_model.projectPixelTo3dRay(rect_point);
-
- // カメラの高さを0.44[m]として把持対象物の位置を計算
- const double CAMERA_HEIGHT = 0.46;
- cv::Point3d object_position(
- ray.x * CAMERA_HEIGHT,
- ray.y * CAMERA_HEIGHT,
- ray.z * CAMERA_HEIGHT);
-
- // 把持対象物の位置をTFに配信
- geometry_msgs::msg::TransformStamped t;
- t.header = msg->header;
- t.child_frame_id = "target_0";
- t.transform.translation.x = object_position.x;
- t.transform.translation.y = object_position.y;
- t.transform.translation.z = object_position.z;
- tf_broadcaster_->sendTransform(t);
- }
-
- // 閾値による二値化画像を配信
- sensor_msgs::msg::Image::SharedPtr img_thresholded_msg =
- cv_bridge::CvImage(msg->header, "mono8", img_thresholded).toImageMsg();
- image_thresholded_publisher_->publish(*img_thresholded_msg);
+ // 赤い物体を検出するようにHSVの範囲を設定
+ // 周囲の明るさ等の動作環境に合わせて調整
+ const int LOW_H_1 = 0, HIGH_H_1 = 10;
+ const int LOW_H_2 = 170, HIGH_H_2 = 179;
+ const int LOW_S = 100, HIGH_S = 255;
+ const int LOW_V = 100, HIGH_V = 255;
+
+ // カメラ画像を受け取る
+ auto cv_img = cv_bridge::toCvShare(img_msg, img_msg->encoding);
+
+ // 画像をRGBからHSVに変換
+ cv::cvtColor(cv_img->image, cv_img->image, cv::COLOR_RGB2HSV);
+
+ // 画像処理用の変数を用意
+ cv::Mat img_mask_1;
+ cv::Mat img_mask_2;
+ cv::Mat img_thresholded;
+
+ // 画像の二値化
+ cv::inRange(
+ cv_img->image,
+ cv::Scalar(LOW_H_1, LOW_S, LOW_V),
+ cv::Scalar(HIGH_H_1, HIGH_S, HIGH_V),
+ img_mask_1);
+ cv::inRange(
+ cv_img->image,
+ cv::Scalar(LOW_H_2, LOW_S, LOW_V),
+ cv::Scalar(HIGH_H_2, HIGH_S, HIGH_V),
+ img_mask_2);
+
+ // マスク画像の合成
+ img_thresholded = img_mask_1 | img_mask_2;
+
+ // ノイズ除去の処理
+ cv::morphologyEx(
+ img_thresholded,
+ img_thresholded,
+ cv::MORPH_OPEN,
+ cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)));
+
+ // 穴埋めの処理
+ cv::morphologyEx(
+ img_thresholded,
+ img_thresholded,
+ cv::MORPH_CLOSE,
+ cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)));
+
+ // 画像の検出領域におけるモーメントを計算
+ cv::Moments moment = moments(img_thresholded);
+ double d_m01 = moment.m01;
+ double d_m10 = moment.m10;
+ double d_area = moment.m00;
+
+ // 閾値による二値化画像を配信
+ sensor_msgs::msg::Image::SharedPtr img_thresholded_msg =
+ cv_bridge::CvImage(img_msg->header, "mono8", img_thresholded).toImageMsg();
+ image_thresholded_publisher_->publish(*img_thresholded_msg);
+
+ // 検出した領域のピクセル数が10000より大きい場合に把持位置を配信
+ if (d_area <= 10000) {
+ return;
}
- }
- void camera_info_callback(const sensor_msgs::msg::CameraInfo::SharedPtr msg)
- {
- camera_info_ = msg;
+ // カメラモデル作成
+ image_geometry::PinholeCameraModel camera_model;
+
+ // カメラのパラメータを設定
+ camera_model.fromCameraInfo(*info_msg);
+
+ // 画像座標系における把持対象物の位置(2D)
+ const double pixel_x = d_m10 / d_area;
+ const double pixel_y = d_m01 / d_area;
+ const cv::Point2d point(pixel_x, pixel_y);
+
+ // 補正後の画像座標系における把持対象物の位置を取得(2D)
+ const cv::Point2d rect_point = camera_model.rectifyPoint(point);
+
+ // カメラ座標系から見た把持対象物の方向(Ray)を取得する
+ const cv::Point3d ray = camera_model.projectPixelTo3dRay(rect_point);
+
+ // カメラの高さを0.46[m]として把持対象物の位置を計算
+ const double CAMERA_HEIGHT = 0.46;
+ cv::Point3d object_position(
+ ray.x * CAMERA_HEIGHT,
+ ray.y * CAMERA_HEIGHT,
+ ray.z * CAMERA_HEIGHT);
+
+ // 把持対象物の位置をTFに配信
+ geometry_msgs::msg::TransformStamped t;
+ t.header = img_msg->header;
+ t.child_frame_id = "target_0";
+ t.transform.translation.x = object_position.x;
+ t.transform.translation.y = object_position.y;
+ t.transform.translation.z = object_position.z;
+ tf_broadcaster_->sendTransform(t);
}
};
diff --git a/crane_plus_examples/src/gripper_control.cpp b/crane_plus_examples/src/gripper_control.cpp
index 66cea24..5216326 100644
--- a/crane_plus_examples/src/gripper_control.cpp
+++ b/crane_plus_examples/src/gripper_control.cpp
@@ -16,49 +16,66 @@
// https://github.com/ros-planning/moveit2/blob/main/moveit_demo_nodes
// /run_move_group/src/run_move_group.cpp
-#include
+#include
+#include "angles/angles.h"
#include "moveit/move_group_interface/move_group_interface.hpp"
#include "rclcpp/rclcpp.hpp"
using MoveGroupInterface = moveit::planning_interface::MoveGroupInterface;
-static const rclcpp::Logger LOGGER = rclcpp::get_logger("gripper_control");
-
-double to_radians(const double deg_angle)
+class GripperControl
{
- return deg_angle * M_PI / 180.0;
-}
+public:
+ // グリッパの開閉角度
+ inline static const double GRIPPER_DEFAULT = 0.0;
+ inline static const double GRIPPER_OPEN = angles::from_degrees(-30.0);
+ inline static const double GRIPPER_CLOSE = angles::from_degrees(30.0);
+
+ // ノードを受け取り、グリッパのMoveGroupInterfaceを初期化する
+ explicit GripperControl(rclcpp::Node::SharedPtr node)
+ {
+ move_group_gripper_ = std::make_shared(node, "gripper");
+ move_group_gripper_->setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
+ move_group_gripper_->setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ }
+
+ // グリッパを角度[rad]を指定して開閉する
+ void move_gripper_angle(const double angle)
+ {
+ auto joint_values = move_group_gripper_->getCurrentJointValues();
+ joint_values[0] = angle;
+ move_group_gripper_->setJointValueTarget(joint_values);
+ move_group_gripper_->move();
+ }
+
+private:
+ std::shared_ptr move_group_gripper_;
+};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
- auto move_group_gripper_node = rclcpp::Node::make_shared("move_group_gripper_node", node_options);
- // For current state monitor
- rclcpp::executors::SingleThreadedExecutor executor;
- executor.add_node(move_group_gripper_node);
- std::thread([&executor]() {executor.spin();}).detach();
+ auto node = rclcpp::Node::make_shared("gripper_control", node_options);
- MoveGroupInterface move_group_gripper(move_group_gripper_node, "gripper");
- move_group_gripper.setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
- move_group_gripper.setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ // MoveGroupInterfaceのデッドロックを防ぐため、スピン処理を別スレッドで走らせる
+ std::thread spin_thread([node]() {rclcpp::spin(node);});
- auto gripper_joint_values = move_group_gripper.getCurrentJointValues();
+ GripperControl controller(node);
- gripper_joint_values[0] = to_radians(30);
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
+ // グリッパを閉じる
+ controller.move_gripper_angle(GripperControl::GRIPPER_CLOSE);
- gripper_joint_values[0] = to_radians(-30);
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
+ // グリッパを開く
+ controller.move_gripper_angle(GripperControl::GRIPPER_OPEN);
- gripper_joint_values[0] = to_radians(0);
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
+ // グリッパを0度にする
+ controller.move_gripper_angle(GripperControl::GRIPPER_DEFAULT);
+ // 終了処理: rclcppを終了したのち、バックグラウンドスレッドを安全に回収する
rclcpp::shutdown();
+ spin_thread.join();
return 0;
}
diff --git a/crane_plus_examples/src/joint_values.cpp b/crane_plus_examples/src/joint_values.cpp
index 609bc4b..61fff47 100644
--- a/crane_plus_examples/src/joint_values.cpp
+++ b/crane_plus_examples/src/joint_values.cpp
@@ -16,49 +16,78 @@
// https://github.com/ros-planning/moveit2/blob/main/moveit_demo_nodes
// /run_move_group/src/run_move_group.cpp
-#include
+#include
+#include
+#include "angles/angles.h"
#include "moveit/move_group_interface/move_group_interface.hpp"
#include "rclcpp/rclcpp.hpp"
using MoveGroupInterface = moveit::planning_interface::MoveGroupInterface;
-static const rclcpp::Logger LOGGER = rclcpp::get_logger("joint_values");
-
-double to_radians(const double deg_angle)
+class JointValues
{
- return deg_angle * M_PI / 180.0;
-}
+public:
+ // ノードを受け取り、アームのMoveGroupInterfaceを初期化する
+ explicit JointValues(rclcpp::Node::SharedPtr node)
+ {
+ move_group_arm_ = std::make_shared(node, "arm_tcp");
+ move_group_arm_->setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
+ move_group_arm_->setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ }
+
+ // SRDFに定義された姿勢名でアームを動かす
+ void move_arm_to_named_pose(const std::string & name)
+ {
+ move_group_arm_->setNamedTarget(name);
+ move_group_arm_->move();
+ }
+
+ // 各ジョイント角度[rad]を指定してアームを動かす
+ void move_arm_joint_values(const std::vector & joint_values)
+ {
+ move_group_arm_->setJointValueTarget(joint_values);
+ move_group_arm_->move();
+ }
+
+ // アームの現在のジョイント角度を取得する
+ std::vector get_current_arm_joint_values()
+ {
+ return move_group_arm_->getCurrentJointValues();
+ }
+
+private:
+ std::shared_ptr move_group_arm_;
+};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
- auto move_group_node = rclcpp::Node::make_shared("joint_values", node_options);
- // For current state monitor
- rclcpp::executors::SingleThreadedExecutor executor;
- executor.add_node(move_group_node);
- std::thread([&executor]() {executor.spin();}).detach();
+ auto node = rclcpp::Node::make_shared("joint_values", node_options);
+
+ // MoveGroupInterfaceのデッドロックを防ぐため、スピン処理を別スレッドで走らせる
+ std::thread spin_thread([node]() {rclcpp::spin(node);});
- MoveGroupInterface move_group_arm(move_group_node, "arm_tcp");
- move_group_arm.setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
- move_group_arm.setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ JointValues controller(node);
- move_group_arm.setNamedTarget("vertical");
- move_group_arm.move();
+ // verticalの姿勢から開始
+ controller.move_arm_to_named_pose("vertical");
- auto joint_values = move_group_arm.getCurrentJointValues();
- double target_joint_value = to_radians(45.0);
+ // 各関節を順番に45度に動かす
+ const double TARGET_ANGLE = angles::from_degrees(45.0);
+ auto joint_values = controller.get_current_arm_joint_values();
for (size_t i = 0; i < joint_values.size(); i++) {
- joint_values[i] = target_joint_value;
- move_group_arm.setJointValueTarget(joint_values);
- move_group_arm.move();
+ joint_values[i] = TARGET_ANGLE;
+ controller.move_arm_joint_values(joint_values);
}
- move_group_arm.setNamedTarget("vertical");
- move_group_arm.move();
+ // verticalの姿勢に戻る
+ controller.move_arm_to_named_pose("vertical");
+ // 終了処理: rclcppを終了したのち、バックグラウンドスレッドを安全に回収する
rclcpp::shutdown();
+ spin_thread.join();
return 0;
}
diff --git a/crane_plus_examples/src/pick_and_place.cpp b/crane_plus_examples/src/pick_and_place.cpp
index c5d5116..f9de2b6 100644
--- a/crane_plus_examples/src/pick_and_place.cpp
+++ b/crane_plus_examples/src/pick_and_place.cpp
@@ -16,124 +16,157 @@
// https://github.com/ros-planning/moveit2/blob/main/moveit_demo_nodes
// /run_move_group/src/run_move_group.cpp
-#include
+#include
+#include "angles/angles.h"
#include "geometry_msgs/msg/pose.hpp"
-#include "geometry_msgs/msg/quaternion.hpp"
#include "moveit/move_group_interface/move_group_interface.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
using MoveGroupInterface = moveit::planning_interface::MoveGroupInterface;
-static const rclcpp::Logger LOGGER = rclcpp::get_logger("pick_and_place");
-
-double to_radians(const double deg_angle)
+class PickAndPlace
{
- return deg_angle * M_PI / 180.0;
-}
+public:
+ // グリッパの開閉角度
+ inline static const double GRIPPER_DEFAULT = 0.0;
+ inline static const double GRIPPER_OPEN = angles::from_degrees(-30.0);
+ inline static const double GRIPPER_CLOSE = angles::from_degrees(10.0);
+
+ // ノードを受け取り、アーム・グリッパのMoveGroupInterfaceを初期化する
+ explicit PickAndPlace(rclcpp::Node::SharedPtr node)
+ {
+ move_group_arm_ = std::make_shared(node, "arm_tcp");
+ move_group_arm_->setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
+ move_group_arm_->setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ // IKの成功率を向上させるため、目標位置姿勢の許容範囲を小さく設定
+ move_group_arm_->setGoalPositionTolerance(1e-5);
+ move_group_arm_->setGoalOrientationTolerance(1e-4);
+
+ move_group_gripper_ = std::make_shared(node, "gripper");
+ move_group_gripper_->setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
+ move_group_gripper_->setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ }
+
+ // SRDFに定義された姿勢名でアームを動かす
+ void move_arm_to_named_pose(const std::string & name)
+ {
+ move_group_arm_->setNamedTarget(name);
+ move_group_arm_->move();
+ }
+
+ // アームを目標位置・姿勢(Pose)に動かす
+ void move_arm_to_pose(const geometry_msgs::msg::Pose & pose)
+ {
+ move_group_arm_->setPoseTarget(pose);
+ move_group_arm_->move();
+ }
+
+ // アームを目標位置(x, y, z [m])・姿勢(roll, pitch, yaw [deg])に動かす
+ void control_arm(
+ const double x, const double y, const double z,
+ const double roll, const double pitch, const double yaw)
+ {
+ geometry_msgs::msg::Pose target_pose;
+ tf2::Quaternion q;
+ target_pose.position.x = x;
+ target_pose.position.y = y;
+ target_pose.position.z = z;
+ q.setRPY(angles::from_degrees(roll), angles::from_degrees(pitch), angles::from_degrees(yaw));
+ target_pose.orientation = tf2::toMsg(q);
+ move_arm_to_pose(target_pose);
+ }
+
+ // グリッパを角度[rad]を指定して開閉する
+ void move_gripper_angle(const double angle)
+ {
+ auto joint_values = move_group_gripper_->getCurrentJointValues();
+ joint_values[0] = angle;
+ move_group_gripper_->setJointValueTarget(joint_values);
+ move_group_gripper_->move();
+ }
+
+private:
+ std::shared_ptr move_group_arm_;
+ std::shared_ptr move_group_gripper_;
+};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
- auto move_group_arm_node = rclcpp::Node::make_shared("move_group_arm_node", node_options);
- auto move_group_gripper_node = rclcpp::Node::make_shared("move_group_gripper_node", node_options);
- // For current state monitor
- rclcpp::executors::SingleThreadedExecutor executor;
- executor.add_node(move_group_arm_node);
- executor.add_node(move_group_gripper_node);
- std::thread([&executor]() {executor.spin();}).detach();
-
- MoveGroupInterface move_group_arm(move_group_arm_node, "arm_tcp");
- move_group_arm.setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
- move_group_arm.setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
-
- MoveGroupInterface move_group_gripper(move_group_gripper_node, "gripper");
- move_group_gripper.setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
- move_group_gripper.setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
- auto gripper_joint_values = move_group_gripper.getCurrentJointValues();
- double GRIPPER_DEFAULT = 0.0;
- double GRIPPER_OPEN = to_radians(-30);
- double GRIPPER_CLOSE = to_radians(10);
-
- // Set goal tolerances to improve IK success rate
- move_group_arm.setGoalPositionTolerance(1e-5);
- move_group_arm.setGoalOrientationTolerance(1e-4);
-
- move_group_arm.setNamedTarget("vertical");
- move_group_arm.move();
-
- gripper_joint_values[0] = GRIPPER_DEFAULT;
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
-
- // ----- Picking Preparation -----
- move_group_arm.setNamedTarget("home");
- move_group_arm.move();
-
- gripper_joint_values[0] = GRIPPER_OPEN;
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
-
- geometry_msgs::msg::Pose target_pose;
- tf2::Quaternion q;
- target_pose.position.x = 0.0;
- target_pose.position.y = -0.21;
- target_pose.position.z = 0.17;
- q.setRPY(to_radians(0), to_radians(90), to_radians(-90));
- target_pose.orientation = tf2::toMsg(q);
- move_group_arm.setPoseTarget(target_pose);
- move_group_arm.move();
-
- target_pose.position.y = -0.1;
- target_pose.position.z = 0.05;
- q.setRPY(to_radians(0), to_radians(180), to_radians(-90));
- target_pose.orientation = tf2::toMsg(q);
- move_group_arm.setPoseTarget(target_pose);
- move_group_arm.move();
-
- target_pose.position.z = 0.02;
- move_group_arm.setPoseTarget(target_pose);
- move_group_arm.move();
-
- // Grasp
- gripper_joint_values[0] = GRIPPER_CLOSE;
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
-
- target_pose.position.z = 0.05;
- move_group_arm.setPoseTarget(target_pose);
- move_group_arm.move();
-
- // ----- Placing Preparation -----
- move_group_arm.setNamedTarget("home");
- move_group_arm.move();
-
- target_pose.position.x = 0.25;
- target_pose.position.y = 0.0;
- target_pose.position.z = 0.06;
- q.setRPY(to_radians(0), to_radians(90), to_radians(0));
- target_pose.orientation = tf2::toMsg(q);
- move_group_arm.setPoseTarget(target_pose);
- move_group_arm.move();
-
- // Release
- gripper_joint_values[0] = GRIPPER_OPEN;
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
-
- // Return to home and vertical pose
- move_group_arm.setNamedTarget("home");
- move_group_arm.move();
- move_group_arm.setNamedTarget("vertical");
- move_group_arm.move();
-
- gripper_joint_values[0] = GRIPPER_DEFAULT;
- move_group_gripper.setJointValueTarget(gripper_joint_values);
- move_group_gripper.move();
-
+ auto node = rclcpp::Node::make_shared("pick_and_place", node_options);
+
+ // MoveGroupInterfaceのデッドロックを防ぐため、スピン処理を別スレッドで走らせる
+ std::thread spin_thread([node]() {rclcpp::spin(node);});
+
+ PickAndPlace controller(node);
+
+ // 物体上方のアプローチ位置のXYZ[m]とRPY[deg]
+ const double APPROACH_X = 0.0;
+ const double APPROACH_Y = -0.21;
+ const double APPROACH_Z = 0.17;
+ const double APPROACH_ROLL = 0.0;
+ const double APPROACH_PITCH = 90.0;
+ const double APPROACH_YAW = -90.0;
+
+ // アプローチ・退避時の高さ
+ const double LIFTING_HEIGHT = 0.05;
+
+ // 掴む位置(ピック位置)のXYZ[m]とRPY[deg]
+ const double PICK_X = 0.0;
+ const double PICK_Y = -0.1;
+ const double PICK_Z = 0.02;
+ const double PICK_ROLL = 0.0;
+ const double PICK_PITCH = 180.0;
+ const double PICK_YAW = -90.0;
+
+ // 置く位置(プレース位置)のXYZ[m]とRPY[deg]
+ const double PLACE_X = 0.25;
+ const double PLACE_Y = 0.0;
+ const double PLACE_Z = 0.06;
+ const double PLACE_ROLL = 0.0;
+ const double PLACE_PITCH = 90.0;
+ const double PLACE_YAW = 0.0;
+
+ // 初期姿勢
+ controller.move_arm_to_named_pose("vertical");
+ controller.move_gripper_angle(PickAndPlace::GRIPPER_DEFAULT);
+
+ // ピック準備
+ controller.move_arm_to_named_pose("home");
+ controller.move_gripper_angle(PickAndPlace::GRIPPER_OPEN);
+ // 物体上方へ移動
+ controller.control_arm(APPROACH_X, APPROACH_Y, APPROACH_Z, APPROACH_ROLL, APPROACH_PITCH,
+ APPROACH_YAW);
+ // 物体直上まで降りる
+ controller.control_arm(PICK_X, PICK_Y, LIFTING_HEIGHT, PICK_ROLL, PICK_PITCH, PICK_YAW);
+
+ // ピック動作
+ // 掴む位置まで降りる
+ controller.control_arm(PICK_X, PICK_Y, PICK_Z, PICK_ROLL, PICK_PITCH, PICK_YAW);
+ // 掴む
+ controller.move_gripper_angle(PickAndPlace::GRIPPER_CLOSE);
+ // 持ち上げる
+ controller.control_arm(PICK_X, PICK_Y, LIFTING_HEIGHT, PICK_ROLL, PICK_PITCH, PICK_YAW);
+
+ // プレース動作
+ // homeを経由してプレース位置へ移動
+ controller.move_arm_to_named_pose("home");
+ // 置く位置まで降ろす
+ controller.control_arm(PLACE_X, PLACE_Y, PLACE_Z, PLACE_ROLL, PLACE_PITCH, PLACE_YAW);
+ // 離す
+ controller.move_gripper_angle(PickAndPlace::GRIPPER_OPEN);
+
+ // 終了動作
+ controller.move_arm_to_named_pose("home");
+ controller.move_arm_to_named_pose("vertical");
+ controller.move_gripper_angle(PickAndPlace::GRIPPER_DEFAULT);
+
+ // 終了処理: rclcppを終了したのち、バックグラウンドスレッドを安全に回収する
rclcpp::shutdown();
+ spin_thread.join();
return 0;
}
diff --git a/crane_plus_examples/src/pick_and_place_tf.cpp b/crane_plus_examples/src/pick_and_place_tf.cpp
index f62dd58..fcff462 100644
--- a/crane_plus_examples/src/pick_and_place_tf.cpp
+++ b/crane_plus_examples/src/pick_and_place_tf.cpp
@@ -24,29 +24,52 @@
#include "angles/angles.h"
#include "geometry_msgs/msg/pose.hpp"
-#include "geometry_msgs/msg/quaternion.hpp"
#include "geometry_msgs/msg/transform_stamped.hpp"
-#include "geometry_msgs/msg/twist.hpp"
#include "moveit/move_group_interface/move_group_interface.hpp"
#include "rclcpp/rclcpp.hpp"
-#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include "tf2/convert.hpp"
#include "tf2/exceptions.hpp"
-#include "tf2_ros/transform_listener.h"
+#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
#include "tf2_ros/buffer.h"
-#include "std_msgs/msg/string.hpp"
+#include "tf2_ros/transform_listener.h"
+
using namespace std::chrono_literals;
using MoveGroupInterface = moveit::planning_interface::MoveGroupInterface;
class PickAndPlaceTf : public rclcpp::Node
{
public:
+ // 位置姿勢パラメータ(x, y, z [m], roll, pitch, yaw [deg])
+ struct PoseParams
+ {
+ double x, y, z, roll, pitch, yaw;
+ };
+
+ // グリッパの開閉角度
+ inline static const double GRIPPER_DEFAULT = 0.0;
+ inline static const double GRIPPER_OPEN = angles::from_degrees(-30.0);
+ inline static const double GRIPPER_CLOSE = angles::from_degrees(10.0);
+
+ // 待機姿勢の位置姿勢(x, y, z [m], roll, pitch, yaw [deg])
+ inline static const PoseParams STANDBY = {0.0, 0.0, 0.3, 0.0, 0.0, 0.0};
+
+ // 把持アプローチ時の高さおよびピッチ角
+ inline static const double GRASP_HEIGHT = 0.04;
+ inline static const double GRASP_PITCH = 90.0;
+
+ // 搬送時の中間姿勢
+ inline static const PoseParams TRANSIT_1 = {0.12, 0.0, 0.17, 0.0, 90.0, 0.0};
+ inline static const PoseParams TRANSIT_2 = {0.0, -0.12, 0.17, 0.0, 90.0, -90.0};
+
+ // 置く位置(プレース位置)とその退避姿勢
+ inline static const PoseParams PLACE = {0.0, -0.25, 0.05, 0.0, 90.0, -90.0};
+ inline static const PoseParams PLACE_RETRACT = {0.0, -0.25, 0.10, 0.0, 90.0, -90.0};
+
PickAndPlaceTf(
rclcpp::Node::SharedPtr move_group_arm_node,
rclcpp::Node::SharedPtr move_group_gripper_node)
: Node("pick_and_place_tf_node")
{
- using namespace std::placeholders;
move_group_arm_ = std::make_shared(move_group_arm_node, "arm_tcp");
move_group_arm_->setMaxVelocityScalingFactor(1.0);
move_group_arm_->setMaxAccelerationScalingFactor(1.0);
@@ -59,11 +82,69 @@ class PickAndPlaceTf : public rclcpp::Node
move_group_arm_->setGoalPositionTolerance(1e-5);
move_group_arm_->setGoalOrientationTolerance(1e-4);
- // SRDFに定義されている"home"の姿勢にする
- move_group_arm_->setNamedTarget("home");
+ // SRDFに定義されている "home" の姿勢に移動
+ move_arm_to_named_pose("home");
+
+ // アームの可動範囲制限を設定してから待機姿勢に移動する
+ set_constraints();
+ control_arm(STANDBY);
+
+ tf_buffer_ = std::make_unique(this->get_clock());
+ tf_listener_ = std::make_shared(*tf_buffer_);
+
+ // 0.5秒ごとにon_timerを呼び出すタイマーを作成
+ timer_ = this->create_wall_timer(500ms, std::bind(&PickAndPlaceTf::on_timer, this));
+ }
+
+ // グリッパを角度[rad]を指定して開閉する
+ void move_gripper_angle(const double angle)
+ {
+ auto joint_values = move_group_gripper_->getCurrentJointValues();
+ joint_values[0] = angle;
+ move_group_gripper_->setJointValueTarget(joint_values);
+ move_group_gripper_->move();
+ }
+
+ // アームを目標位置・姿勢(Pose)に動かす
+ void move_arm_to_pose(const geometry_msgs::msg::Pose & pose)
+ {
+ move_group_arm_->setPoseTarget(pose);
+ move_group_arm_->move();
+ }
+
+ // アームを目標位置(x, y, z [m])・姿勢(roll, pitch, yaw [deg])に動かす
+ bool control_arm(
+ const double x, const double y, const double z,
+ const double roll, const double pitch, const double yaw)
+ {
+ geometry_msgs::msg::Pose target_pose;
+ tf2::Quaternion q;
+ target_pose.position.x = x;
+ target_pose.position.y = y;
+ target_pose.position.z = z;
+ q.setRPY(angles::from_degrees(roll), angles::from_degrees(pitch), angles::from_degrees(yaw));
+ target_pose.orientation = tf2::toMsg(q);
+ move_group_arm_->setPoseTarget(target_pose);
+ moveit::core::MoveItErrorCode result = move_group_arm_->move();
+ return result.val == moveit::core::MoveItErrorCode::SUCCESS;
+ }
+
+ // 位置姿勢パラメータ(PoseParams)を使ってアームを動かす
+ bool control_arm(const PoseParams & pose)
+ {
+ return control_arm(pose.x, pose.y, pose.z, pose.roll, pose.pitch, pose.yaw);
+ }
+
+ // SRDFに定義された姿勢名でアームを動かす
+ void move_arm_to_named_pose(const std::string & name)
+ {
+ move_group_arm_->setNamedTarget(name);
move_group_arm_->move();
+ }
- // 可動範囲を制限する
+ // アームの関節の一部に可動制限を設定する
+ void set_constraints()
+ {
moveit_msgs::msg::Constraints constraints;
constraints.name = "arm_constraints";
@@ -83,23 +164,18 @@ class PickAndPlaceTf : public rclcpp::Node
constraints.joint_constraints.push_back(joint_constraint);
move_group_arm_->setPathConstraints(constraints);
+ }
- // 待機姿勢
- control_arm(0.0, 0.0, 0.3, 0, 0, 0);
-
- tf_buffer_ =
- std::make_unique(this->get_clock());
- tf_listener_ =
- std::make_shared(*tf_buffer_);
-
- timer_ = this->create_wall_timer(
- 500ms, std::bind(&PickAndPlaceTf::on_timer, this));
+ // 設定された関節可動制限をクリアする
+ void clear_constraints()
+ {
+ move_group_arm_->clearPathConstraints();
}
private:
void on_timer()
{
- // target_0のtf位置姿勢を取得
+ // target_0(把持対象)のTFを取得
geometry_msgs::msg::TransformStamped tf_msg;
try {
@@ -114,104 +190,74 @@ class PickAndPlaceTf : public rclcpp::Node
}
rclcpp::Time now = this->get_clock()->now();
- const std::chrono::nanoseconds FILTERING_TIME = 2s;
- const std::chrono::nanoseconds STOP_TIME_THRESHOLD = 3s;
- const float DISTANCE_THRESHOLD = 0.01;
- tf2::Stamped tf;
- tf2::convert(tf_msg, tf);
- const auto TF_ELAPSED_TIME = now.nanoseconds() - tf.stamp_.time_since_epoch().count();
- const auto TF_STOP_TIME = now.nanoseconds() - tf_past_.stamp_.time_since_epoch().count();
+ const auto FILTERING_TIME = rclcpp::Duration(2s);
+ const auto STOP_TIME_THRESHOLD = rclcpp::Duration(3s);
+ const double DISTANCE_THRESHOLD = 0.01;
+ const double TARGET_Z_MIN_LIMIT = 0.04;
+
+ tf2::Stamped tf_current;
+ tf2::convert(tf_msg, tf_current);
+
+ const auto tf_elapsed_time = now - rclcpp::Time(tf_msg.header.stamp, RCL_ROS_TIME);
+ const auto tf_stop_time =
+ now - rclcpp::Time(tf_past_.stamp_.time_since_epoch().count(), RCL_ROS_TIME);
// 現在時刻から2秒以内に受け取ったtfを使用
- if (TF_ELAPSED_TIME < FILTERING_TIME.count()) {
- double tf_diff = (tf_past_.getOrigin() - tf.getOrigin()).length();
- // 把持対象の位置が停止していることを判定
- if (tf_diff < DISTANCE_THRESHOLD) {
- // 把持対象が3秒以上停止している場合ピッキング動作開始
- if (TF_STOP_TIME > STOP_TIME_THRESHOLD.count()) {
- picking(tf.getOrigin());
- }
- } else {
- tf_past_ = tf;
- }
+ if (tf_elapsed_time > FILTERING_TIME) {
+ return;
}
+
+ double tf_diff = (tf_past_.getOrigin() - tf_current.getOrigin()).length();
+
+ // 把持対象の位置が停止していることを判定
+ if (tf_diff > DISTANCE_THRESHOLD) {
+ tf_past_ = tf_current;
+ return;
+ }
+
+ // 把持対象が3秒以上停止している場合ピッキング動作開始
+ if (tf_stop_time < STOP_TIME_THRESHOLD) {
+ return;
+ }
+
+ // 把持対象が低すぎる場合は把持位置を調整
+ if (tf_current.getOrigin().z() < TARGET_Z_MIN_LIMIT) {
+ tf_current.getOrigin().setZ(TARGET_Z_MIN_LIMIT);
+ }
+
+ picking(tf_current.getOrigin());
}
void picking(tf2::Vector3 target_position)
{
- const double GRIPPER_DEFAULT = 0.0;
- const double GRIPPER_OPEN = angles::from_degrees(-30.0);
- const double GRIPPER_CLOSE = angles::from_degrees(10.0);
-
- // 何かを掴んでいた時のためにハンドを開く
- control_gripper(GRIPPER_OPEN);
+ // 何かを掴んでいた時のためにハンドを開閉
+ move_gripper_angle(GRIPPER_OPEN);
- // ロボット座標系(2D)の原点から見た把持対象物への角度を計算
double x = target_position.x();
double y = target_position.y();
- double theta_rad = std::atan2(y, x);
- double theta_deg = theta_rad * 180.0 / 3.1415926535;
+ double theta_deg = std::atan2(y, x) * 180.0 / M_PI;
- // 把持対象物に正対する
- control_arm(0.0, 0.0, 0.3, 0, 0, theta_deg);
+ // ターゲットの正面に向ける
+ control_arm(0.0, 0.0, STANDBY.z, 0, 0, theta_deg);
- // 掴みに行く
- if (!control_arm(x, y, 0.04, 0, 90, theta_deg)) {
- // アーム動作に失敗した時はpick_and_placeを中断して待機姿勢に戻る
- control_arm(0.0, 0.0, 0.3, 0, 0, 0);
+ // ピック動作(掴みに行く)
+ if (!control_arm(x, y, GRASP_HEIGHT, 0, GRASP_PITCH, theta_deg)) {
+ // アーム動作に失敗した場合は待機姿勢に戻る
+ control_arm(STANDBY);
return;
}
+ move_gripper_angle(GRIPPER_CLOSE);
- // ハンドを閉じる
- control_gripper(GRIPPER_CLOSE);
-
- // 移動する
- control_arm(0.12, 0.0, 0.17, 0, 90, 0);
-
- // 横を向く
- control_arm(0.0, -0.12, 0.17, 0, 90, -90);
-
- // 下ろす
- control_arm(0.0, -0.25, 0.05, 0, 90, -90);
-
- // ハンドを開く
- control_gripper(GRIPPER_OPEN);
-
- // 少しだけハンドを持ち上げる
- control_arm(0.0, -0.25, 0.10, 0, 90, -90);
+ // プレース動作(移動して置く)
+ control_arm(TRANSIT_1);
+ control_arm(TRANSIT_2);
+ control_arm(PLACE);
+ move_gripper_angle(GRIPPER_OPEN);
// 待機姿勢に戻る
- control_arm(0.0, 0.0, 0.3, 0, 0, 0);
-
- // ハンドを閉じる
- control_gripper(GRIPPER_DEFAULT);
- }
-
- // グリッパ制御
- void control_gripper(const double angle)
- {
- auto joint_values = move_group_gripper_->getCurrentJointValues();
- joint_values[0] = angle;
- move_group_gripper_->setJointValueTarget(joint_values);
- move_group_gripper_->move();
- }
-
- // アーム制御
- bool control_arm(
- const double x, const double y, const double z,
- const double roll, const double pitch, const double yaw)
- {
- geometry_msgs::msg::Pose target_pose;
- tf2::Quaternion q;
- target_pose.position.x = x;
- target_pose.position.y = y;
- target_pose.position.z = z;
- q.setRPY(angles::from_degrees(roll), angles::from_degrees(pitch), angles::from_degrees(yaw));
- target_pose.orientation = tf2::toMsg(q);
- move_group_arm_->setPoseTarget(target_pose);
- // アーム動作の成否を取得
- moveit::core::MoveItErrorCode result = move_group_arm_->move();
- return result.val == moveit::core::MoveItErrorCode::SUCCESS;
+ control_arm(PLACE_RETRACT);
+ control_arm(STANDBY);
+ move_gripper_angle(GRIPPER_DEFAULT);
}
std::shared_ptr move_group_arm_;
@@ -230,14 +276,18 @@ int main(int argc, char ** argv)
auto move_group_arm_node = rclcpp::Node::make_shared("move_group_arm_node", node_options);
auto move_group_gripper_node = rclcpp::Node::make_shared("move_group_gripper_node", node_options);
+ // タイマーとTFリスナーを持つため、MultiThreadedExecutorを使用する
rclcpp::executors::MultiThreadedExecutor exec;
auto pick_and_place_tf_node = std::make_shared(
move_group_arm_node,
move_group_gripper_node);
+
exec.add_node(pick_and_place_tf_node);
exec.add_node(move_group_arm_node);
exec.add_node(move_group_gripper_node);
exec.spin();
+
+ pick_and_place_tf_node->clear_constraints();
rclcpp::shutdown();
return 0;
}
diff --git a/crane_plus_examples/src/pose_groupstate.cpp b/crane_plus_examples/src/pose_groupstate.cpp
index e5d4c79..0e4bb75 100644
--- a/crane_plus_examples/src/pose_groupstate.cpp
+++ b/crane_plus_examples/src/pose_groupstate.cpp
@@ -16,37 +16,54 @@
// https://github.com/ros-planning/moveit2/blob/main/moveit_demo_nodes
// /run_move_group/src/run_move_group.cpp
+#include
+
#include "moveit/move_group_interface/move_group_interface.hpp"
#include "rclcpp/rclcpp.hpp"
using MoveGroupInterface = moveit::planning_interface::MoveGroupInterface;
-static const rclcpp::Logger LOGGER = rclcpp::get_logger("pose_groupstate");
+class PoseGroupstate
+{
+public:
+ // ノードを受け取り、アームのMoveGroupInterfaceを初期化する
+ explicit PoseGroupstate(rclcpp::Node::SharedPtr node)
+ {
+ move_group_arm_ = std::make_shared(node, "arm_tcp");
+ move_group_arm_->setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
+ move_group_arm_->setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ }
+
+ // SRDFに定義された姿勢名でアームを動かす
+ void move_arm_to_named_pose(const std::string & name)
+ {
+ move_group_arm_->setNamedTarget(name);
+ move_group_arm_->move();
+ }
+
+private:
+ std::shared_ptr move_group_arm_;
+};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
- auto move_group_arm_node = rclcpp::Node::make_shared("move_group_arm_node", node_options);
- // For current state monitor
- rclcpp::executors::SingleThreadedExecutor executor;
- executor.add_node(move_group_arm_node);
- std::thread([&executor]() {executor.spin();}).detach();
-
- MoveGroupInterface move_group_arm(move_group_arm_node, "arm_tcp");
- move_group_arm.setMaxVelocityScalingFactor(1.0); // Set 0.0 ~ 1.0
- move_group_arm.setMaxAccelerationScalingFactor(1.0); // Set 0.0 ~ 1.0
+ auto node = rclcpp::Node::make_shared("pose_groupstate", node_options);
- move_group_arm.setNamedTarget("home");
- move_group_arm.move();
+ // MoveGroupInterfaceのデッドロックを防ぐため、スピン処理を別スレッドで走らせる
+ std::thread spin_thread([node]() {rclcpp::spin(node);});
- move_group_arm.setNamedTarget("vertical");
- move_group_arm.move();
+ PoseGroupstate controller(node);
- move_group_arm.setNamedTarget("home");
- move_group_arm.move();
+ // SRDFに定義されている名前付き姿勢を順番に実行する
+ controller.move_arm_to_named_pose("home");
+ controller.move_arm_to_named_pose("vertical");
+ controller.move_arm_to_named_pose("home");
+ // 終了処理: rclcppを終了したのち、バックグラウンドスレッドを安全に回収する
rclcpp::shutdown();
+ spin_thread.join();
return 0;
}
diff --git a/crane_plus_examples_py/crane_plus_examples_py/aruco_detection.py b/crane_plus_examples_py/crane_plus_examples_py/aruco_detection.py
index b43f77b..c2d6cde 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/aruco_detection.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/aruco_detection.py
@@ -16,6 +16,7 @@
from cv2 import aruco
from cv_bridge import CvBridge
from geometry_msgs.msg import TransformStamped
+import message_filters
import numpy as np
import rclpy
from rclpy.node import Node
@@ -27,44 +28,42 @@
class ImageSubscriber(Node):
def __init__(self):
super().__init__('aruco_detection')
- self.image_subscription = self.create_subscription(
- Image, 'image_raw', self.image_callback, 10
- )
- self.camera_info_subscription = self.create_subscription(
- CameraInfo, 'camera_info', self.camera_info_callback, 10
- )
- self.tf_broadcaster = TransformBroadcaster(self)
+ self.image_sub = message_filters.Subscriber(self, Image, 'image_raw')
+ self.info_sub = message_filters.Subscriber(self, CameraInfo, 'camera_info')
+ self.ts = message_filters.TimeSynchronizer([self.image_sub, self.info_sub], 10)
+ self.ts.registerCallback(self.camera_callback)
- self.camera_info = None
+ # ArUcoマーカのデータセットを読み込む
+ # DICT_6x6_50は6x6ビットのマーカが50個収録されたもの
+ self.marker_dict = aruco.getPredefinedDictionary(aruco.DICT_6X6_50)
+ self.tf_broadcaster = TransformBroadcaster(self)
self.bridge = CvBridge()
- def image_callback(self, msg):
+ def camera_callback(self, img_msg, info_msg):
# 画像データをROSのメッセージからOpenCVの配列に変換
- cv_img = self.bridge.imgmsg_to_cv2(msg, desired_encoding=msg.encoding)
+ cv_img = self.bridge.imgmsg_to_cv2(img_msg, desired_encoding=img_msg.encoding)
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_RGB2BGR)
- if not self.camera_info:
- return
-
- # ArUcoマーカのデータセットを読み込む
- # DICT_6x6_50は6x6ビットのマーカが50個収録されたもの
- MARKER_DICT = aruco.getPredefinedDictionary(aruco.DICT_6X6_50)
- # マーカID
- ids = []
# 画像座標系上のマーカ頂点位置
corners = []
+ # マーカID
+ ids = []
# マーカの検出
- corners, ids, _ = aruco.detectMarkers(cv_img, MARKER_DICT)
+ corners, ids, _ = aruco.detectMarkers(cv_img, self.marker_dict)
+
if ids is None:
return
# マーカの検出数
n_markers = len(ids)
+
# カメラパラメータ
- CAMERA_MATRIX = np.array(self.camera_info.k).reshape(3, 3)
- DIST_COEFFS = np.array(self.camera_info.d).reshape(1, 5)
+ CAMERA_MATRIX = np.array(info_msg.k).reshape(3, 3)
+ DIST_COEFFS = np.array(info_msg.d).reshape(1, 5)
+
# マーカ一辺の長さ 0.04 [m]
MARKER_LENGTH = 0.04
+
# 画像座標系上のマーカ位置を三次元のカメラ座標系に変換
rvecs, tvecs, _ = cv2.aruco.estimatePoseSingleMarkers(
corners, MARKER_LENGTH, CAMERA_MATRIX, DIST_COEFFS
@@ -72,13 +71,13 @@ def image_callback(self, msg):
# マーカの位置姿勢をtfで配信
for i in range(n_markers):
- # tfの配信
t = TransformStamped()
- t.header = msg.header
+ t.header = img_msg.header
t.child_frame_id = 'target_' + str(ids[i][0])
t.transform.translation.x = tvecs[i][0][0]
t.transform.translation.y = tvecs[i][0][1]
t.transform.translation.z = tvecs[i][0][2]
+
# 回転ベクトルをクォータニオンに変換
marker_orientation_rot = Rotation.from_rotvec(rvecs[i][0])
marker_orientation_quat = marker_orientation_rot.as_quat()
@@ -86,10 +85,8 @@ def image_callback(self, msg):
t.transform.rotation.y = marker_orientation_quat[1]
t.transform.rotation.z = marker_orientation_quat[2]
t.transform.rotation.w = marker_orientation_quat[3]
- self.tf_broadcaster.sendTransform(t)
- def camera_info_callback(self, msg):
- self.camera_info = msg
+ self.tf_broadcaster.sendTransform(t)
def main(args=None):
diff --git a/crane_plus_examples_py/crane_plus_examples_py/color_detection.py b/crane_plus_examples_py/crane_plus_examples_py/color_detection.py
index bf36f63..6fd88e3 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/color_detection.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/color_detection.py
@@ -16,6 +16,7 @@
from cv_bridge import CvBridge
from geometry_msgs.msg import TransformStamped
from image_geometry import PinholeCameraModel
+import message_filters
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import CameraInfo, Image
@@ -25,22 +26,16 @@
class ImageSubscriber(Node):
def __init__(self):
super().__init__('color_detection')
- self.image_subscription = self.create_subscription(
- Image, 'image_raw', self.image_callback, 10
- )
- self.camera_info_subscription = self.create_subscription(
- CameraInfo, 'camera_info', self.camera_info_callback, 10
- )
+ self.color_sub = message_filters.Subscriber(self, Image, 'image_raw')
+ self.info_sub = message_filters.Subscriber(self, CameraInfo, 'camera_info')
+ self.sync = message_filters.TimeSynchronizer([self.color_sub, self.info_sub], 10)
+ self.sync.registerCallback(self.camera_callback)
+
self.image_thresholded_publisher = self.create_publisher(Image, 'image_thresholded', 10)
self.tf_broadcaster = TransformBroadcaster(self)
- self.camera_info = None
self.bridge = CvBridge()
- def image_callback(self, msg):
- # カメラのパラメータを取得してから処理を行う
- if not self.camera_info:
- return
-
+ def camera_callback(self, img_msg, info_msg):
# 赤い物体を検出するようにHSVの範囲を設定
# 周囲の明るさ等の動作環境に合わせて調整
LOW_H_1 = 0
@@ -52,10 +47,10 @@ def image_callback(self, msg):
LOW_V = 100
HIGH_V = 255
- # ウェブカメラの画像を受け取る
- cv_img = self.bridge.imgmsg_to_cv2(msg, desired_encoding=msg.encoding)
+ # カメラ画像を受け取る
+ cv_img = self.bridge.imgmsg_to_cv2(img_msg, desired_encoding=img_msg.encoding)
- # 画像をRGBからHSVに変換(取得したカメラ画像にフォーマットを合わせる)
+ # 画像をRGBからHSVに変換
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_RGB2HSV)
# 画像の二値化
@@ -78,48 +73,47 @@ def image_callback(self, msg):
d_m10 = moment['m10']
d_area = moment['m00']
- # 検出した領域のピクセル数が10000より大きい場合
- if d_area > 10000:
- # カメラモデル作成
- camera_model = PinholeCameraModel()
-
- # カメラのパラメータを設定
- camera_model.fromCameraInfo(self.camera_info)
-
- # 画像座標系における把持対象物の位置(2D)
- pixel_x = d_m10 / d_area
- pixel_y = d_m01 / d_area
- point = (pixel_x, pixel_y)
-
- # 補正後の画像座標系における把持対象物の位置を取得(2D)
- rect_point = camera_model.rectifyPoint(point)
-
- # カメラ座標系から見た把持対象物の方向(Ray)を取得する
- ray = camera_model.projectPixelTo3dRay(rect_point)
-
- # カメラの高さを0.46[m]として把持対象物の位置を計算
- CAMERA_HEIGHT = 0.46
- object_position = [
- ray[0] * CAMERA_HEIGHT,
- ray[1] * CAMERA_HEIGHT,
- ray[2] * CAMERA_HEIGHT,
- ]
-
- # 把持対象物の位置をTFに配信
- t = TransformStamped()
- t.header = msg.header
- t.child_frame_id = 'target_0'
- t.transform.translation.x = object_position[0]
- t.transform.translation.y = object_position[1]
- t.transform.translation.z = object_position[2]
- self.tf_broadcaster.sendTransform(t)
-
# 閾値による二値化画像を配信
img_thresholded_msg = self.bridge.cv2_to_imgmsg(img_thresholded, encoding='mono8')
self.image_thresholded_publisher.publish(img_thresholded_msg)
- def camera_info_callback(self, msg):
- self.camera_info = msg
+ # 検出した領域のピクセル数が10000より大きい場合に把持位置を配信
+ if d_area <= 10000:
+ return
+
+ # カメラモデル作成
+ camera_model = PinholeCameraModel()
+
+ # カメラのパラメータを設定
+ camera_model.fromCameraInfo(info_msg)
+
+ # 画像座標系における把持対象物の位置(2D)
+ pixel_x = d_m10 / d_area
+ pixel_y = d_m01 / d_area
+ point = (pixel_x, pixel_y)
+
+ # 補正後の画像座標系における把持対象物の位置を取得(2D)
+ rect_point = camera_model.rectifyPoint(point)
+
+ # カメラ座標系から見た把持対象物の方向(Ray)を取得する
+ ray = camera_model.projectPixelTo3dRay(rect_point)
+
+ # カメラの高さを0.46[m]として把持対象物の位置を計算
+ CAMERA_HEIGHT = 0.46
+ object_position = [
+ ray[0] * CAMERA_HEIGHT,
+ ray[1] * CAMERA_HEIGHT,
+ ray[2] * CAMERA_HEIGHT,
+ ]
+
+ # 把持対象物の位置をTFに配信
+ t = TransformStamped()
+ t.header = img_msg.header
+ t.child_frame_id = 'target_0'
+ t.transform.translation.x = object_position[0]
+ t.transform.translation.y = object_position[1]
+ t.transform.translation.z = object_position[2]
+ self.tf_broadcaster.sendTransform(t)
def main(args=None):
diff --git a/crane_plus_examples_py/crane_plus_examples_py/gripper_control.py b/crane_plus_examples_py/crane_plus_examples_py/gripper_control.py
index 3886700..9c2ef0d 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/gripper_control.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/gripper_control.py
@@ -26,64 +26,52 @@
from rclpy.logging import get_logger
+class GripperControl:
+ # グリッパの開閉角度
+ GRIPPER_DEFAULT = 0.0
+ GRIPPER_OPEN = math.radians(-30.0)
+ GRIPPER_CLOSE = math.radians(30.0)
+
+ def __init__(self):
+ # MoveItPyのインスタンスを生成し、planning componentを取得
+ self.crane_plus = MoveItPy(node_name='gripper_control')
+ self.gripper = self.crane_plus.get_planning_component('gripper')
+ self.robot_model = self.crane_plus.get_robot_model()
+
+ # プランニングの設定(動作プランナーと速度・加速度スケール)
+ self.plan_request_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+
+ self.logger = get_logger('gripper_control')
+
+ def move_gripper_angle(self, angle):
+ # グリッパを角度[rad]を指定して開閉する
+ self.gripper.set_start_state_to_current_state()
+ robot_state = RobotState(self.robot_model)
+ robot_state.set_joint_group_positions('gripper', [angle])
+ self.gripper.set_goal_state(robot_state=robot_state)
+ plan_and_execute(
+ self.crane_plus,
+ self.gripper,
+ self.logger,
+ single_plan_parameters=self.plan_request_params,
+ )
+
+
def main(args=None):
rclpy.init(args=args)
- logger = get_logger('gripper_control')
-
- # instantiate MoveItPy instance and get planning component
- crane_plus = MoveItPy(node_name='gripper_control')
- logger.info('MoveItPy instance created')
-
- # グリッパ制御用 planning component
- gripper = crane_plus.get_planning_component('gripper')
-
- # instantiate a RobotState instance using the current robot model
- robot_model = crane_plus.get_robot_model()
-
- plan_request_params = PlanRequestParameters(
- crane_plus,
- 'ompl_rrtc',
- )
-
- # 動作速度の調整
- plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
-
- # gripperを閉じる
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [math.radians(30.0)])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=plan_request_params,
- )
-
- # gripperを開く
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [math.radians(-30.0)])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=plan_request_params,
- )
-
- # gripperを0度にする
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [math.radians(0.0)])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=plan_request_params,
- )
+
+ controller = GripperControl()
+
+ # グリッパを閉じる
+ controller.move_gripper_angle(GripperControl.GRIPPER_CLOSE)
+
+ # グリッパを開く
+ controller.move_gripper_angle(GripperControl.GRIPPER_OPEN)
+
+ # グリッパを0度にする
+ controller.move_gripper_angle(GripperControl.GRIPPER_DEFAULT)
# Finish with error. Related Issue
# https://github.com/moveit/moveit2/issues/2693
diff --git a/crane_plus_examples_py/crane_plus_examples_py/joint_values.py b/crane_plus_examples_py/crane_plus_examples_py/joint_values.py
index 50eb734..4b8fed8 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/joint_values.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/joint_values.py
@@ -27,35 +27,56 @@
from rclpy.logging import get_logger
-def main(args=None):
- rclpy.init(args=args)
- logger = get_logger('joint_values')
-
- # instantiate MoveItPy instance and get planning component
- crane_plus = MoveItPy(node_name='joint_values')
- logger.info('MoveItPy instance created')
+class JointValues:
+ def __init__(self):
+ # MoveItPyのインスタンスを生成し、planning componentを取得
+ self.crane_plus = MoveItPy(node_name='joint_values')
+ self.arm = self.crane_plus.get_planning_component('arm_tcp')
+ self.robot_model = self.crane_plus.get_robot_model()
+ self.robot_state = RobotState(self.robot_model)
+
+ # プランニングの設定(動作プランナーと速度・加速度スケール)
+ self.plan_request_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+
+ self.logger = get_logger('joint_values')
+
+ def move_arm_to_named_pose(self, name):
+ # SRDFに定義されている名前付きの姿勢に移動する
+ self.arm.set_start_state_to_current_state()
+ self.arm.set_goal_state(configuration_name=name)
+ plan_and_execute(
+ self.crane_plus, self.arm, self.logger,
+ single_plan_parameters=self.plan_request_params,
+ )
- # アーム制御用 planning component
- arm = crane_plus.get_planning_component('arm_tcp')
+ def move_arm_joint_values(self, joint_values_dict):
+ # 関節角度の辞書を指定してアームを動かす
+ self.robot_state.joint_positions = joint_values_dict
+ joint_constraint = construct_joint_constraint(
+ robot_state=self.robot_state,
+ joint_model_group=self.crane_plus.get_robot_model().get_joint_model_group('arm_tcp'),
+ )
+ self.arm.set_start_state_to_current_state()
+ self.arm.set_goal_state(motion_plan_constraints=[joint_constraint])
+ plan_and_execute(
+ self.crane_plus, self.arm, self.logger,
+ single_plan_parameters=self.plan_request_params,
+ )
- # instantiate a RobotState instance using the current robot model
- robot_model = crane_plus.get_robot_model()
- robot_state = RobotState(robot_model)
+ def get_current_arm_joint_values(self, joint_names):
+ # 現在の関節角度を名前付き辞書で取得する
+ self.arm.set_start_state_to_current_state()
+ current_state = self.arm.get_start_state()
+ positions = current_state.get_joint_group_positions('arm_tcp')
+ return dict(zip(joint_names, positions))
- plan_request_params = PlanRequestParameters(
- crane_plus,
- 'ompl_rrtc',
- )
- # 動作速度の調整
- # Set 0.0 ~ 1.0
- plan_request_params.max_acceleration_scaling_factor = 1.0
- plan_request_params.max_velocity_scaling_factor = 1.0
+def main(args=None):
+ rclpy.init(args=args)
- # SRDF内に定義されている'vertical'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='vertical')
- plan_and_execute(crane_plus, arm, logger, single_plan_parameters=plan_request_params)
+ controller = JointValues()
joint_names = [
'crane_plus_joint1',
@@ -63,35 +84,19 @@ def main(args=None):
'crane_plus_joint3',
'crane_plus_joint4',
]
- target_joint_value = math.radians(45)
-
- # 現在角度をベースに、目標角度を作成する
- current_state = arm.get_start_state()
- joint_values = current_state.get_joint_group_positions('arm_tcp')
+ target_joint_value = math.radians(45.0)
- # jointのリストを辞書型の形式に変換する
- joint_values_dict = dict(zip(joint_names, joint_values))
+ # verticalの姿勢から開始
+ controller.move_arm_to_named_pose('vertical')
- # 各関節角度を順番に-45[deg]に動かす
+ # 各関節を順番に45度に動かす
+ joint_values_dict = controller.get_current_arm_joint_values(joint_names)
for joint_name in joint_names:
- # 対象のjointに目標値を設定する
joint_values_dict[joint_name] = target_joint_value
- robot_state.joint_positions = joint_values_dict
-
- joint_constraint = construct_joint_constraint(
- robot_state=robot_state,
- joint_model_group=crane_plus.get_robot_model().get_joint_model_group('arm_tcp'),
- )
-
- arm.set_start_state_to_current_state()
- arm.set_goal_state(motion_plan_constraints=[joint_constraint])
-
- plan_and_execute(crane_plus, arm, logger, single_plan_parameters=plan_request_params)
+ controller.move_arm_joint_values(joint_values_dict)
- # SRDF内に定義されている'vertical'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='vertical')
- plan_and_execute(crane_plus, arm, logger, single_plan_parameters=plan_request_params)
+ # verticalの姿勢に戻る
+ controller.move_arm_to_named_pose('vertical')
# Finish with error. Related Issue
# https://github.com/moveit/moveit2/issues/2693
diff --git a/crane_plus_examples_py/crane_plus_examples_py/pick_and_place.py b/crane_plus_examples_py/crane_plus_examples_py/pick_and_place.py
index ec33167..433932c 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/pick_and_place.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/pick_and_place.py
@@ -12,12 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import copy
import math
from crane_plus_examples_py.utils import plan_and_execute
-from geometry_msgs.msg import Point, Pose, PoseStamped, Quaternion
+from geometry_msgs.msg import Pose, PoseStamped
from moveit.core.robot_state import RobotState
from moveit.planning import (
@@ -32,270 +31,174 @@
from shape_msgs.msg import SolidPrimitive
-def set_goal_constraints(pose):
- # 位置姿勢の許容誤差
- POSITION_TOLERANCE = 0.00001
- ORIENTATION_TOLERANCE = 0.0001
-
- # 目標位置姿勢
- target_pose = PoseStamped()
- target_pose.header.frame_id = 'crane_plus_base'
- target_pose.pose = pose
-
- # 目標位置姿勢の制約設定
- goal_constraints = Constraints()
- goal_constraints.name = 'tolerance_goal'
-
- # 位置の制約設定
- position_constraint = PositionConstraint()
- position_constraint.header.frame_id = 'crane_plus_base'
- position_constraint.link_name = 'crane_plus_link_tcp'
- tolerance_region = BoundingVolume()
- primitive = SolidPrimitive()
- primitive.type = SolidPrimitive.SPHERE
- primitive.dimensions = [POSITION_TOLERANCE]
- tolerance_region.primitives.append(primitive)
- tolerance_region.primitive_poses.append(target_pose.pose)
- position_constraint.constraint_region = tolerance_region
- position_constraint.weight = 1.0
-
- # 姿勢の制約設定
- orientation_constraint = OrientationConstraint()
- orientation_constraint.header.frame_id = 'crane_plus_base'
- orientation_constraint.link_name = 'crane_plus_link_tcp'
- orientation_constraint.orientation = target_pose.pose.orientation
- orientation_constraint.absolute_x_axis_tolerance = ORIENTATION_TOLERANCE
- orientation_constraint.absolute_y_axis_tolerance = ORIENTATION_TOLERANCE
- orientation_constraint.absolute_z_axis_tolerance = ORIENTATION_TOLERANCE
- orientation_constraint.weight = 1.0
-
- goal_constraints.position_constraints.append(position_constraint)
- goal_constraints.orientation_constraints.append(orientation_constraint)
-
- # 制約設定を返す
- return goal_constraints
-
-
-def main(args=None):
- rclpy.init(args=args)
- logger = get_logger('pick_and_place')
-
- # instantiate MoveItPy instance and get planning component
- crane_plus = MoveItPy(node_name='pick_and_place')
- logger.info('MoveItPy instance created')
-
- # アーム制御用 planning component
- arm = crane_plus.get_planning_component('arm_tcp')
- # グリッパ制御用 planning component
- gripper = crane_plus.get_planning_component('gripper')
-
- robot_model = crane_plus.get_robot_model()
-
- arm_plan_request_params = PlanRequestParameters(
- crane_plus,
- 'ompl_rrtc',
- )
- gripper_plan_request_params = PlanRequestParameters(
- crane_plus,
- 'ompl_rrtc',
- )
-
- # 動作速度の調整
- arm_plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- arm_plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
-
- gripper_plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- gripper_plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
-
- # グリッパの開閉角
+class PickAndPlace:
+ # グリッパの開閉角度
GRIPPER_DEFAULT = 0.0
GRIPPER_OPEN = math.radians(-30.0)
GRIPPER_CLOSE = math.radians(10.0)
- # 物体を持ち上げる高さ
- LIFTING_HEIGHT = 0.03
- # 物体の頭上の位置
- gripper_quat = Rotation.from_euler('xyz', [0.0, 90.0, -90.0], degrees=True).as_quat()
- gripper_quat_msg = Quaternion(
- x=gripper_quat[0], y=gripper_quat[1], z=gripper_quat[2], w=gripper_quat[3]
- )
- ABOVE_POSE = Pose(position=Point(x=0.0, y=-0.21, z=0.17), orientation=gripper_quat_msg)
- # 物体を掴む位置
- gripper_quat = Rotation.from_euler('xyz', [0.0, 180.0, -90.0], degrees=True).as_quat()
- gripper_quat_msg = Quaternion(
- x=gripper_quat[0], y=gripper_quat[1], z=gripper_quat[2], w=gripper_quat[3]
- )
- GRASP_POSE = Pose(position=Point(x=0.0, y=-0.1, z=0.02), orientation=gripper_quat_msg)
- PRE_AND_POST_GRASP_POSE = copy.deepcopy(GRASP_POSE)
- PRE_AND_POST_GRASP_POSE.position.z = LIFTING_HEIGHT
- # 物体を置く位置
- gripper_quat = Rotation.from_euler('xyz', [0.0, 90.0, 0.0], degrees=True).as_quat()
- gripper_quat_msg = Quaternion(
- x=gripper_quat[0], y=gripper_quat[1], z=gripper_quat[2], w=gripper_quat[3]
- )
- RELEASE_POSE = Pose(position=Point(x=0.25, y=0.0, z=0.06), orientation=gripper_quat_msg)
-
- # SRDF内に定義されている'vertical'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='vertical')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
-
- # gripperの開閉角を0度にする
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [GRIPPER_DEFAULT])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=gripper_plan_request_params,
- )
-
- # SRDF内に定義されている'home'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='home')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
- # gripperを開く
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [GRIPPER_OPEN])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=gripper_plan_request_params,
- )
-
- # 物体の上に腕を伸ばす
- arm.set_start_state_to_current_state()
- goal_constraints = set_goal_constraints(ABOVE_POSE)
- arm.set_goal_state(motion_plan_constraints=[goal_constraints])
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
- arm.set_start_state_to_current_state()
- goal_constraints = set_goal_constraints(PRE_AND_POST_GRASP_POSE)
- arm.set_goal_state(motion_plan_constraints=[goal_constraints])
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
+ def __init__(self):
+ # MoveItPyのインスタンスを生成し、planning componentを取得
+ self.crane_plus = MoveItPy(node_name='pick_and_place')
+ self.arm = self.crane_plus.get_planning_component('arm_tcp')
+ self.gripper = self.crane_plus.get_planning_component('gripper')
+ self.robot_model = self.crane_plus.get_robot_model()
+
+ # プランニングの設定(動作プランナーと速度・加速度スケール)
+ self.arm_plan_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.arm_plan_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.arm_plan_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+
+ self.gripper_plan_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.gripper_plan_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.gripper_plan_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+
+ self.logger = get_logger('pick_and_place')
+
+ def move_arm_to_named_pose(self, name):
+ # SRDFに定義されている名前付きの姿勢に移動する
+ self.arm.set_start_state_to_current_state()
+ self.arm.set_goal_state(configuration_name=name)
+ plan_and_execute(
+ self.crane_plus, self.arm, self.logger,
+ single_plan_parameters=self.arm_plan_params,
+ )
+
+ def move_arm_to_pose(self, pose):
+ # 位置姿勢の制約を設定してアームを動かす
+ # (IKの精度を向上させるため、位置・姿勢の許容範囲を設定)
+ POSITION_TOLERANCE = 0.00001
+ ORIENTATION_TOLERANCE = 0.0001
+
+ target_pose = PoseStamped()
+ target_pose.header.frame_id = 'crane_plus_base'
+ target_pose.pose = pose
+
+ goal_constraints = Constraints()
+ goal_constraints.name = 'tolerance_goal'
+
+ position_constraint = PositionConstraint()
+ position_constraint.header.frame_id = 'crane_plus_base'
+ position_constraint.link_name = 'crane_plus_link_tcp'
+ tolerance_region = BoundingVolume()
+ primitive = SolidPrimitive()
+ primitive.type = SolidPrimitive.SPHERE
+ primitive.dimensions = [POSITION_TOLERANCE]
+ tolerance_region.primitives.append(primitive)
+ tolerance_region.primitive_poses.append(target_pose.pose)
+ position_constraint.constraint_region = tolerance_region
+ position_constraint.weight = 1.0
+
+ orientation_constraint = OrientationConstraint()
+ orientation_constraint.header.frame_id = 'crane_plus_base'
+ orientation_constraint.link_name = 'crane_plus_link_tcp'
+ orientation_constraint.orientation = target_pose.pose.orientation
+ orientation_constraint.absolute_x_axis_tolerance = ORIENTATION_TOLERANCE
+ orientation_constraint.absolute_y_axis_tolerance = ORIENTATION_TOLERANCE
+ orientation_constraint.absolute_z_axis_tolerance = ORIENTATION_TOLERANCE
+ orientation_constraint.weight = 1.0
+
+ goal_constraints.position_constraints.append(position_constraint)
+ goal_constraints.orientation_constraints.append(orientation_constraint)
+
+ self.arm.set_start_state_to_current_state()
+ self.arm.set_goal_state(motion_plan_constraints=[goal_constraints])
+ plan_and_execute(
+ self.crane_plus, self.arm, self.logger,
+ single_plan_parameters=self.arm_plan_params,
+ )
+
+ def control_arm(self, x, y, z, roll, pitch, yaw):
+ # アームを目標位置(x, y, z [m])・姿勢(roll, pitch, yaw [deg])に動かす
+ pose = Pose()
+ pose.position.x = x
+ pose.position.y = y
+ pose.position.z = z
+ quat = Rotation.from_euler('xyz', [roll, pitch, yaw], degrees=True).as_quat()
+ pose.orientation.x = quat[0]
+ pose.orientation.y = quat[1]
+ pose.orientation.z = quat[2]
+ pose.orientation.w = quat[3]
+ self.move_arm_to_pose(pose)
+
+ def move_gripper_angle(self, angle):
+ # グリッパを角度[rad]を指定して開閉する
+ self.gripper.set_start_state_to_current_state()
+ robot_state = RobotState(self.robot_model)
+ robot_state.set_joint_group_positions('gripper', [angle])
+ self.gripper.set_goal_state(robot_state=robot_state)
+ plan_and_execute(
+ self.crane_plus, self.gripper, self.logger,
+ single_plan_parameters=self.gripper_plan_params,
+ )
- # 掴みに行く
- arm.set_start_state_to_current_state()
- goal_constraints = set_goal_constraints(GRASP_POSE)
- arm.set_goal_state(motion_plan_constraints=[goal_constraints])
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
- # ハンドを閉じる
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [GRIPPER_CLOSE])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=gripper_plan_request_params,
- )
+def main(args=None):
+ rclpy.init(args=args)
+ controller = PickAndPlace()
+
+ # 物体上方のアプローチ位置のXYZ[m]とRPY[deg]
+ APPROACH_X = 0.0
+ APPROACH_Y = -0.21
+ APPROACH_Z = 0.17
+ APPROACH_ROLL = 0.0
+ APPROACH_PITCH = 90.0
+ APPROACH_YAW = -90.0
+
+ # アプローチ・退避時の高さ
+ LIFTING_HEIGHT = 0.05
+
+ # 掴む位置(ピック位置)のXYZ[m]とRPY[deg]
+ PICK_X = 0.0
+ PICK_Y = -0.1
+ PICK_Z = 0.02
+ PICK_ROLL = 0.0
+ PICK_PITCH = 180.0
+ PICK_YAW = -90.0
+
+ # 置く位置(プレース位置)のXYZ[m]とRPY[deg]
+ PLACE_X = 0.25
+ PLACE_Y = 0.0
+ PLACE_Z = 0.06
+ PLACE_ROLL = 0.0
+ PLACE_PITCH = 90.0
+ PLACE_YAW = 0.0
+
+ # 初期姿勢
+ controller.move_arm_to_named_pose('vertical')
+ controller.move_gripper_angle(PickAndPlace.GRIPPER_DEFAULT)
+
+ # ピック準備
+ controller.move_arm_to_named_pose('home')
+ controller.move_gripper_angle(PickAndPlace.GRIPPER_OPEN)
+ # 物体上方へ移動
+ controller.control_arm(
+ APPROACH_X, APPROACH_Y, APPROACH_Z, APPROACH_ROLL, APPROACH_PITCH, APPROACH_YAW
+ )
+ # 物体直上まで降りる
+ controller.control_arm(PICK_X, PICK_Y, LIFTING_HEIGHT, PICK_ROLL, PICK_PITCH, PICK_YAW)
+
+ # ピック動作
+ # 掴む位置まで降りる
+ controller.control_arm(PICK_X, PICK_Y, PICK_Z, PICK_ROLL, PICK_PITCH, PICK_YAW)
+ # 掴む
+ controller.move_gripper_angle(PickAndPlace.GRIPPER_CLOSE)
# 持ち上げる
- arm.set_start_state_to_current_state()
- goal_constraints = set_goal_constraints(PRE_AND_POST_GRASP_POSE)
- arm.set_goal_state(motion_plan_constraints=[goal_constraints])
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
-
- # SRDF内に定義されている'home'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='home')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
-
- # 下ろす
- arm.set_start_state_to_current_state()
- goal_constraints = set_goal_constraints(RELEASE_POSE)
- arm.set_goal_state(motion_plan_constraints=[goal_constraints])
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
-
- # ハンドを開く
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [GRIPPER_OPEN])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=gripper_plan_request_params,
- )
-
- # SRDF内に定義されている'home'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='home')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
-
- # SRDF内に定義されている'vertical'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='vertical')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=arm_plan_request_params,
- )
-
- # gripperの開閉角を0度にする
- gripper.set_start_state_to_current_state()
- robot_state = RobotState(robot_model)
- robot_state.set_joint_group_positions('gripper', [GRIPPER_DEFAULT])
- gripper.set_goal_state(robot_state=robot_state)
- plan_and_execute(
- crane_plus,
- gripper,
- logger,
- single_plan_parameters=gripper_plan_request_params,
- )
+ controller.control_arm(PICK_X, PICK_Y, LIFTING_HEIGHT, PICK_ROLL, PICK_PITCH, PICK_YAW)
+
+ # プレース動作
+ # homeを経由してプレース位置へ移動
+ controller.move_arm_to_named_pose('home')
+ # 置く位置まで降ろす
+ controller.control_arm(PLACE_X, PLACE_Y, PLACE_Z, PLACE_ROLL, PLACE_PITCH, PLACE_YAW)
+ # 離す
+ controller.move_gripper_angle(PickAndPlace.GRIPPER_OPEN)
+
+ # 終了動作
+ controller.move_arm_to_named_pose('home')
+ controller.move_arm_to_named_pose('vertical')
+ controller.move_gripper_angle(PickAndPlace.GRIPPER_DEFAULT)
# Finish with error. Related Issue
# https://github.com/moveit/moveit2/issues/2693
diff --git a/crane_plus_examples_py/crane_plus_examples_py/pick_and_place_tf.py b/crane_plus_examples_py/crane_plus_examples_py/pick_and_place_tf.py
index 6dd60be..ec879fd 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/pick_and_place_tf.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/pick_and_place_tf.py
@@ -42,79 +42,68 @@
class PickAndPlaceTf(Node):
+ # グリッパの開閉角度
+ GRIPPER_DEFAULT = 0.0
+ GRIPPER_OPEN = math.radians(-30.0)
+ GRIPPER_CLOSE = math.radians(10.0)
+
+ # 待機姿勢の位置姿勢(x, y, z [m], roll, pitch, yaw [deg])
+ STANDBY = (0.0, 0.0, 0.3, 0.0, 0.0, 0.0)
+
+ # 把持アプローチ時の高さおよびピッチ角
+ GRASP_HEIGHT = 0.04
+ GRASP_PITCH = 90.0
+
+ # 搬送時の中間姿勢
+ TRANSIT_1 = (0.12, 0.0, 0.17, 0.0, 90.0, 0.0)
+ TRANSIT_2 = (0.0, -0.12, 0.17, 0.0, 90.0, -90.0)
+
+ # 置く位置(プレース位置)とその退避姿勢
+ PLACE = (0.0, -0.25, 0.05, 0.0, 90.0, -90.0)
+ PLACE_RETRACT = (0.0, -0.25, 0.10, 0.0, 90.0, -90.0)
+
def __init__(self):
super().__init__('pick_and_place_tf')
self.logger = self.get_logger()
- # tf
+ # TFリスナーの初期化
self.tf_buffer = Buffer()
self.tf_listener = TransformListener(self.tf_buffer, self)
self.tf_past = TransformStamped()
- # instantiate MoveItPy instance and get planning component
+ # MoveItPyのインスタンスを生成し、planning componentを取得
self.crane_plus = MoveItPy(node_name='moveit_py')
self.logger.info('MoveItPy instance created')
- # アーム制御用 planning component
+ # アーム・グリッパ制御用 planning component
self.arm = self.crane_plus.get_planning_component('arm_tcp')
- # グリッパ制御用 planning component
self.gripper = self.crane_plus.get_planning_component('gripper')
- # instantiate a RobotState instance using the current robot model
+ # ロボットモデルの取得(ジョイント目標値の設定に使用)
self.robot_model = self.crane_plus.get_robot_model()
- self.arm_plan_request_params = PlanRequestParameters(
- self.crane_plus,
- 'ompl_rrtc',
- )
- self.gripper_plan_request_params = PlanRequestParameters(
- self.crane_plus,
- 'ompl_rrtc',
- )
+ # プランニングの設定(動作プランナーと速度・加速度スケール)
+ self.arm_plan_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.arm_plan_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.arm_plan_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- # 動作速度の調整
- # Set 0.0 ~ 1.0
- self.arm_plan_request_params.max_velocity_scaling_factor = 1.0
- self.arm_plan_request_params.max_acceleration_scaling_factor = 1.0
+ self.gripper_plan_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.gripper_plan_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.gripper_plan_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- self.gripper_plan_request_params.max_velocity_scaling_factor = 1.0
- self.gripper_plan_request_params.max_acceleration_scaling_factor = 1.0
-
- # SRDFに定義されている'home'の姿勢にする
+ # SRDFに定義されている "home" の姿勢に移動
self.arm.set_start_state_to_current_state()
self.arm.set_goal_state(configuration_name='home')
plan_and_execute(
- self.crane_plus,
- self.arm,
- self.logger,
- single_plan_parameters=self.arm_plan_request_params,
+ self.crane_plus, self.arm, self.logger,
+ single_plan_parameters=self.arm_plan_params,
)
- # 可動範囲を制限する
- constraints = Constraints()
- constraints.name = 'arm_constraints'
-
- jointConstraint = JointConstraint()
- jointConstraint.joint_name = 'crane_plus_joint1'
- jointConstraint.position = 0.0
- jointConstraint.tolerance_above = math.radians(100)
- jointConstraint.tolerance_below = math.radians(100)
- jointConstraint.weight = 1.0
- constraints.joint_constraints.append(jointConstraint)
-
- jointConstraint.joint_name = 'crane_plus_joint3'
- jointConstraint.position = 0.0
- jointConstraint.tolerance_above = math.radians(0)
- jointConstraint.tolerance_below = math.radians(180)
- jointConstraint.weight = 1.0
- constraints.joint_constraints.append(jointConstraint)
-
- self.arm.set_path_constraints(constraints)
-
- # 待機姿勢
- self._control_arm(0.0, 0.0, 0.3, 0, 0, 0.0)
+ # アームの可動範囲制限を設定してから待機姿勢に移動する
+ self.set_constraints()
+ self.control_arm(*self.STANDBY)
- # Call on_timer function every second
+ # 0.5秒ごとにon_timerを呼び出すタイマーを作成
self.timer = self.create_timer(0.5, self.on_timer)
def on_timer(self):
@@ -132,98 +121,81 @@ def on_timer(self):
STOP_TIME_THRESHOLD = rclpy.duration.Duration(seconds=3)
DISTANCE_THRESHOLD = 0.01
- # 経過時間と停止時間を計算(nsec)
- # 経過時間
tf_time = rclpy.time.Time.from_msg(tf_msg.header.stamp)
- TF_ELAPSED_TIME = now_time - tf_time
- # 停止時間
+ tf_elapsed_time = now_time - tf_time
tf_past_time = rclpy.time.Time.from_msg(self.tf_past.header.stamp)
- TF_STOP_TIME = now_time - tf_past_time
+ tf_stop_time = now_time - tf_past_time
# 現在時刻から2秒以内に受け取ったtfを使用
- if TF_ELAPSED_TIME < FILTERING_TIME:
- tf_diff = np.linalg.norm(
- [
- self.tf_past.transform.translation.x - tf_msg.transform.translation.x,
- self.tf_past.transform.translation.y - tf_msg.transform.translation.y,
- self.tf_past.transform.translation.z - tf_msg.transform.translation.z,
- ]
- )
- # 把持対象の位置が停止していることを判定
- if tf_diff < DISTANCE_THRESHOLD:
- # 把持対象が3秒以上停止している場合ピッキング動作開始
- if TF_STOP_TIME > STOP_TIME_THRESHOLD:
- self._picking(tf_msg.transform.translation)
- else:
- self.tf_past = tf_msg
-
- def _picking(self, target_position):
- GRIPPER_DEFAULT = 0.0
- GRIPPER_OPEN = math.radians(-30.0)
- GRIPPER_CLOSE = math.radians(10.0)
-
- # 何かを掴んでいた時のためにハンドを開く
- self._control_gripper(GRIPPER_OPEN)
-
- # ロボット座標系(2D)の原点から見た把持対象物への角度を計算
- x = target_position.x
- y = target_position.y
- theta_rad = math.atan2(y, x)
- theta_deg = math.degrees(theta_rad)
+ if tf_elapsed_time > FILTERING_TIME:
+ return
- # 把持対象物に正対する
- self._control_arm(0.0, 0.0, 0.3, 0, 0, theta_deg)
+ tf_diff = np.linalg.norm(
+ [
+ self.tf_past.transform.translation.x - tf_msg.transform.translation.x,
+ self.tf_past.transform.translation.y - tf_msg.transform.translation.y,
+ self.tf_past.transform.translation.z - tf_msg.transform.translation.z,
+ ]
+ )
- # 掴みに行く
- if not self._control_arm(x, y, 0.04, 0, 90, theta_deg):
- # アーム動作に失敗した時はpick_and_placeを中断して待機姿勢に戻る
- self._control_arm(0.0, 0.0, 0.3, 0, 0, 0)
+ # 把持対象の位置が停止していることを判定
+ if tf_diff > DISTANCE_THRESHOLD:
+ self.tf_past = tf_msg
return
- # ハンドを閉じる
- self._control_gripper(GRIPPER_CLOSE)
+ # 把持対象が3秒以上停止している場合ピッキング動作開始
+ if tf_stop_time < STOP_TIME_THRESHOLD:
+ return
- # 移動する
- self._control_arm(0.12, 0.0, 0.17, 0, 90, 0)
+ self.picking(tf_msg.transform.translation)
- # 横へ移動する
- self._control_arm(0.0, -0.12, 0.17, 0, 90, -90)
+ def picking(self, target_position):
+ # 何かを掴んでいた時のためにハンドを開閉
+ self.move_gripper_angle(self.GRIPPER_OPEN)
- # 下ろす
- self._control_arm(0.0, -0.25, 0.05, 0, 90, -90)
+ x = target_position.x
+ y = target_position.y
+ theta_deg = math.degrees(math.atan2(y, x))
- # ハンドを開く
- self._control_gripper(GRIPPER_OPEN)
+ # ターゲットの正面に向ける
+ self.control_arm(0.0, 0.0, self.STANDBY[2], 0, 0, theta_deg)
- # 少しだけハンドを持ち上げる
- self._control_arm(0.0, -0.25, 0.10, 0, 90, -90)
+ # ピック動作(掴みに行く)
+ if not self.control_arm(x, y, self.GRASP_HEIGHT, 0, self.GRASP_PITCH, theta_deg):
+ # アーム動作に失敗した場合は待機姿勢に戻る
+ self.control_arm(*self.STANDBY)
+ return
- # 待機姿勢に戻る
- self._control_arm(0.0, 0.0, 0.3, 0, 0, 0)
+ self.move_gripper_angle(self.GRIPPER_CLOSE)
+
+ # プレース動作(移動して置く)
+ self.control_arm(*self.TRANSIT_1)
+ self.control_arm(*self.TRANSIT_2)
+ self.control_arm(*self.PLACE)
+ self.move_gripper_angle(self.GRIPPER_OPEN)
- # ハンドを閉じる
- self._control_gripper(GRIPPER_DEFAULT)
+ # 待機姿勢に戻る
+ self.control_arm(*self.PLACE_RETRACT)
+ self.control_arm(*self.STANDBY)
+ self.move_gripper_angle(self.GRIPPER_DEFAULT)
- # グリッパ制御
- def _control_gripper(self, angle):
+ def move_gripper_angle(self, angle):
+ # グリッパを角度[rad]を指定して開閉する
self.gripper.set_start_state_to_current_state()
robot_state = RobotState(self.robot_model)
robot_state.set_joint_group_positions('gripper', [angle])
self.gripper.set_goal_state(robot_state=robot_state)
plan_and_execute(
- self.crane_plus,
- self.gripper,
- self.logger,
- single_plan_parameters=self.gripper_plan_request_params,
+ self.crane_plus, self.gripper, self.logger,
+ single_plan_parameters=self.gripper_plan_params,
)
- # アーム制御
- def _control_arm(self, x, y, z, roll, pitch, yaw):
- # 位置姿勢の許容誤差
+ def control_arm(self, x, y, z, roll, pitch, yaw):
+ # アームを目標位置(x, y, z [m])・姿勢(roll, pitch, yaw [deg])に動かす
+ # (IKの精度を向上させるため、位置・姿勢の許容範囲を設定)
POSITION_TOLERANCE = 0.00001
ORIENTATION_TOLERANCE = 0.0001
- # 目標位置姿勢
target_pose = PoseStamped()
target_pose.header.frame_id = 'crane_plus_base'
target_pose.pose.position.x = x
@@ -236,11 +208,9 @@ def _control_arm(self, x, y, z, roll, pitch, yaw):
target_pose.pose.orientation.z = quat[2]
target_pose.pose.orientation.w = quat[3]
- # 目標位置姿勢の制約設定
goal_constraints = Constraints()
goal_constraints.name = 'tolerance_goal'
- # 位置の制約設定
position_constraint = PositionConstraint()
position_constraint.header.frame_id = 'crane_plus_base'
position_constraint.link_name = 'crane_plus_link_tcp'
@@ -253,7 +223,6 @@ def _control_arm(self, x, y, z, roll, pitch, yaw):
position_constraint.constraint_region = tolerance_region
position_constraint.weight = 1.0
- # 姿勢の制約設定
orientation_constraint = OrientationConstraint()
orientation_constraint.header.frame_id = 'crane_plus_base'
orientation_constraint.link_name = 'crane_plus_link_tcp'
@@ -268,23 +237,49 @@ def _control_arm(self, x, y, z, roll, pitch, yaw):
self.arm.set_start_state_to_current_state()
self.arm.set_goal_state(motion_plan_constraints=[goal_constraints])
- result = plan_and_execute(
- self.crane_plus,
- self.arm,
- self.logger,
- single_plan_parameters=self.arm_plan_request_params,
+
+ return plan_and_execute(
+ self.crane_plus, self.arm, self.logger,
+ single_plan_parameters=self.arm_plan_params,
)
- return result
+
+ def set_constraints(self):
+ # アームの関節の一部に可動制限を設定する
+ constraints = Constraints()
+ constraints.name = 'arm_constraints'
+
+ joint_constraint = JointConstraint()
+ joint_constraint.joint_name = 'crane_plus_joint1'
+ joint_constraint.position = 0.0
+ joint_constraint.tolerance_above = math.radians(100)
+ joint_constraint.tolerance_below = math.radians(100)
+ joint_constraint.weight = 1.0
+ constraints.joint_constraints.append(joint_constraint)
+
+ joint_constraint = JointConstraint()
+ joint_constraint.joint_name = 'crane_plus_joint3'
+ joint_constraint.position = 0.0
+ joint_constraint.tolerance_above = math.radians(0)
+ joint_constraint.tolerance_below = math.radians(180)
+ joint_constraint.weight = 1.0
+ constraints.joint_constraints.append(joint_constraint)
+
+ self.arm.set_path_constraints(constraints)
+
+ def clear_constraints(self):
+ # 設定された関節可動制限をクリアする
+ self.arm.clear_path_constraints()
def main(args=None):
- # ros2の初期化
rclpy.init(args=args)
pick_and_place_tf_node = PickAndPlaceTf()
rclpy.spin(pick_and_place_tf_node)
+ pick_and_place_tf_node.clear_constraints()
+
# Finish with error. Related Issue
# https://github.com/moveit/moveit2/issues/2693
pick_and_place_tf_node.destroy_node()
diff --git a/crane_plus_examples_py/crane_plus_examples_py/pose_groupstate.py b/crane_plus_examples_py/crane_plus_examples_py/pose_groupstate.py
index cef4555..478bd77 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/pose_groupstate.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/pose_groupstate.py
@@ -23,55 +23,40 @@
from rclpy.logging import get_logger
-def main(args=None):
- rclpy.init(args=args)
- logger = get_logger('pose_groupstate')
+class PoseGroupstateController:
+ def __init__(self):
+ # MoveItPyのインスタンスを生成し、planning componentを取得
+ self.crane_plus = MoveItPy(node_name='pose_groupstate')
+ self.arm = self.crane_plus.get_planning_component('arm_tcp')
- # instantiate MoveItPy instance and get planning component
- crane_plus = MoveItPy(node_name='pose_groupstate')
- logger.info('MoveItPy instance created')
+ # プランニングパラメータの設定
+ self.plan_request_params = PlanRequestParameters(self.crane_plus, 'ompl_rrtc')
+ self.plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
+ self.plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- # アーム制御用 planning component
- arm = crane_plus.get_planning_component('arm_tcp')
+ self.logger = get_logger('pose_groupstate')
- plan_request_params = PlanRequestParameters(
- crane_plus,
- 'ompl_rrtc',
- )
+ def move_arm_to_named_pose(self, name):
+ # SRDFに定義されている名前付きの姿勢に移動する
+ self.arm.set_start_state_to_current_state()
+ self.arm.set_goal_state(configuration_name=name)
+ plan_and_execute(
+ self.crane_plus,
+ self.arm,
+ self.logger,
+ single_plan_parameters=self.plan_request_params,
+ )
- # 動作速度の調整
- plan_request_params.max_acceleration_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- plan_request_params.max_velocity_scaling_factor = 1.0 # Set 0.0 ~ 1.0
- # SRDF内に定義されている'home'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='home')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=plan_request_params,
- )
+def main(args=None):
+ rclpy.init(args=args)
- # SRDF内に定義されている'vertical'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='vertical')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=plan_request_params,
- )
+ controller = PoseGroupstateController()
- # SRDF内に定義されている'home'の姿勢にする
- arm.set_start_state_to_current_state()
- arm.set_goal_state(configuration_name='home')
- plan_and_execute(
- crane_plus,
- arm,
- logger,
- single_plan_parameters=plan_request_params,
- )
+ # SRDFに定義されている名前付き姿勢を順番に実行する
+ controller.move_arm_to_named_pose('home')
+ controller.move_arm_to_named_pose('vertical')
+ controller.move_arm_to_named_pose('home')
# Finish with error. Related Issue
# https://github.com/moveit/moveit2/issues/2693
diff --git a/crane_plus_examples_py/crane_plus_examples_py/utils.py b/crane_plus_examples_py/crane_plus_examples_py/utils.py
index e97753a..35a9e47 100644
--- a/crane_plus_examples_py/crane_plus_examples_py/utils.py
+++ b/crane_plus_examples_py/crane_plus_examples_py/utils.py
@@ -1,8 +1,11 @@
# Copyright 2025 RT Corporation
+#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
+#
# http://www.apache.org/licenses/LICENSE-2.0
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,7 +23,7 @@ def plan_and_execute(
multi_plan_parameters=None,
sleep_time=0.0,
):
- # plan to goal
+ # 軌道を計画する
logger.info('Planning trajectory')
if multi_plan_parameters is not None:
plan_result = planning_component.plan(multi_plan_parameters=multi_plan_parameters)
@@ -29,7 +32,7 @@ def plan_and_execute(
else:
plan_result = planning_component.plan()
- # execute the plan
+ # 計画した軌道を実行する
if plan_result:
logger.info('Executing plan')
robot_trajectory = plan_result.trajectory
diff --git a/crane_plus_examples_py/package.xml b/crane_plus_examples_py/package.xml
index 5b37710..5bc535f 100644
--- a/crane_plus_examples_py/package.xml
+++ b/crane_plus_examples_py/package.xml
@@ -11,12 +11,19 @@
Atsushi Kuwagata
Yusuke Kato
- rclpy
- std_msgs
+ cv_bridge
+ geometry_msgs
+ image_geometry
+ message_filters
moveit
moveit_py
python3-numpy
+ python3-opencv
python3-scipy
+ rclpy
+ sensor_msgs
+ std_msgs
+ tf2_ros
ament_copyright
ament_flake8