diff --git a/CMakeLists.txt b/CMakeLists.txt index 655de58..ba9da6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,10 +7,46 @@ endif() # find dependencies find_package(ament_cmake REQUIRED) -# uncomment the following section in order to fill in -# further dependencies manually. -# find_package( REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_srvs REQUIRED) +find_package(gz-cmake3 REQUIRED) +find_package(gz-plugin2 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +find_package(gz-common5 REQUIRED COMPONENTS profiler) +set(GZ_COMMON_VER ${gz-common5_VERSION_MAJOR}) +# Harmonic +if("$ENV{GZ_VERSION}" STREQUAL "harmonic") + find_package(gz-sim8 REQUIRED) + set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) + message(STATUS "Compiling against Gazebo Harmonic") +# Default to Garden +else() + find_package(gz-sim7 REQUIRED) + set(GZ_SIM_VER ${gz-sim7_VERSION_MAJOR}) + message(STATUS "Compiling against Gazebo Garden") +endif() + +add_library(VacuumGripper + SHARED + src/VacuumGripper.cpp +) + +include_directories(include) + +target_link_libraries(VacuumGripper + gz-sim${GZ_SIM_VER}::gz-sim${GZ_SIM_VER} +) + +ament_target_dependencies(VacuumGripper + rclcpp + std_srvs +) + +install( + TARGETS VacuumGripper + DESTINATION lib/${PROJECT_NAME} +) install(DIRECTORY launch urdf config world assets DESTINATION share/${PROJECT_NAME}) diff --git a/include/rbs_mill_assist/VacuumGripper.hpp b/include/rbs_mill_assist/VacuumGripper.hpp new file mode 100644 index 0000000..f49cb27 --- /dev/null +++ b/include/rbs_mill_assist/VacuumGripper.hpp @@ -0,0 +1,46 @@ +#ifndef RBS_MILL_ASSIST__FULL_SYSTEM_HH_ +#define RBS_MILL_ASSIST__FULL_SYSTEM_HH_ + +// The only required include in the header is this one. +// All others will depend on what your plugin does. +#include "gz/sim/components/Factory.hh" +#include +#include + +#include "std_srvs/srv/set_bool.hpp" +#include +#include + +namespace rbs_mill_assist { + +class VacuumGripperPrivate; + +class VacuumGripper : public gz::sim::System, + public gz::sim::ISystemConfigure, + public gz::sim::ISystemPreUpdate { +public: + VacuumGripper(); + ~VacuumGripper() override; + void Configure(const gz::sim::Entity &_entity, + const std::shared_ptr &_element, + gz::sim::EntityComponentManager &_ecm, + gz::sim::EventManager &_eventManager) override; + + void PreUpdate(const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) override; + + bool AttachLink(gz::sim::EntityComponentManager &_ecm, + const gz::sim::Entity &gripperEntity, + const gz::sim::Entity &objectEntity); + + bool DetachLink(gz::sim::EntityComponentManager &_ecm); + + void Toggle(const std::shared_ptr request, + std::shared_ptr response); + +private: + std::unique_ptr dataPtr; + rclcpp::Service::SharedPtr toggle_service_; +}; +} // namespace rbs_mill_assist +#endif diff --git a/launch/simulation.launch.py b/launch/simulation.launch.py index ffc6da6..a447f37 100644 --- a/launch/simulation.launch.py +++ b/launch/simulation.launch.py @@ -143,7 +143,7 @@ def launch_setup(context, *args, **kwargs): PythonLaunchDescriptionSource( [os.path.join(get_package_share_directory('ros_gz_sim'), 'launch', 'gz_sim.launch.py')]), - launch_arguments=[('gz_args', [' -r ', gazebo_world])], + launch_arguments=[('gz_args', [' -r -v 4 ', gazebo_world])], ) gazebo_spawn_robot = Node( diff --git a/package.xml b/package.xml index 24ca05b..ad98c48 100644 --- a/package.xml +++ b/package.xml @@ -11,6 +11,7 @@ rbs_bringup rbs_arm + std_srvs cartesian_compliance_controller cartesian_controller_base cartesian_controller_handles diff --git a/src/VacuumGripper.cpp b/src/VacuumGripper.cpp new file mode 100644 index 0000000..d97d1d9 --- /dev/null +++ b/src/VacuumGripper.cpp @@ -0,0 +1,237 @@ +#include "gz/sim/components/Name.hh" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rbs_mill_assist/VacuumGripper.hpp" + +GZ_ADD_PLUGIN(rbs_mill_assist::VacuumGripper, gz::sim::System, + rbs_mill_assist::VacuumGripper::ISystemConfigure, + rbs_mill_assist::VacuumGripper::ISystemPreUpdate) + +namespace rbs_mill_assist { + +class VacuumGripperPrivate { +public: + std::string gripper_link_name; + bool attach_request; + bool detach_request; + bool enabled; + gz::sim::EntityComponentManager _ecm; + + gz::sim::components::Joint joint; + gz::sim::Entity contactedEntity = gz::sim::kNullEntity; + gz::sim::Entity gripperEntity = gz::sim::kNullEntity; + std::unordered_map detachableJoints; + + std::shared_ptr node_{nullptr}; + std::thread thread_executor_spin_; + rclcpp::executors::MultiThreadedExecutor::SharedPtr executor_; +}; + +VacuumGripper::VacuumGripper() + : dataPtr(std::make_unique()) {} + +VacuumGripper::~VacuumGripper() { + this->dataPtr->executor_->cancel(); + this->dataPtr->thread_executor_spin_.join(); +} + +void VacuumGripper::Toggle( + const std::shared_ptr request, + std::shared_ptr response) { + this->dataPtr->enabled = request->data; + response->success = true; + gzdbg << "Vacuum Gripper " << (request->data ? "Activated" : "Deactivated") + << std::endl; +} + +bool VacuumGripper::AttachLink(gz::sim::EntityComponentManager &_ecm, + const gz::sim::Entity &gripperEntity, + const gz::sim::Entity &objectEntity) { + + if (gripperEntity == gz::sim::kNullEntity || + objectEntity == gz::sim::kNullEntity) { + gzerr << "Invalid gripper or object entity!" << std::endl; + return false; + } + + auto jointEntity = _ecm.CreateEntity(); + + gz::sim::components::DetachableJointInfo jointInfo; + jointInfo.parentLink = gripperEntity; + jointInfo.childLink = objectEntity; + jointInfo.jointType = "fixed"; + + _ecm.CreateComponent(jointEntity, + gz::sim::components::DetachableJoint(jointInfo)); + + std::string key = + std::to_string(gripperEntity) + "_" + std::to_string(objectEntity); + this->dataPtr->detachableJoints[key] = jointEntity; + + gzdbg << "Attached object " << objectEntity << " to gripper " << gripperEntity + << std::endl; + + this->dataPtr->contactedEntity = objectEntity; + + return true; +} + +bool VacuumGripper::DetachLink(gz::sim::EntityComponentManager &_ecm) { + if (this->dataPtr->contactedEntity == gz::sim::kNullEntity) { + gzdbg << "No object is currently attached to the gripper." << std::endl; + return false; + } + + std::string key = std::to_string(this->dataPtr->gripperEntity) + "_" + + std::to_string(this->dataPtr->contactedEntity); + + auto it = this->dataPtr->detachableJoints.find(key); + if (it == this->dataPtr->detachableJoints.end()) { + gzerr << "Failed to find detachable joint for object " + << this->dataPtr->contactedEntity << std::endl; + return false; + } + + auto jointEntity = it->second; + _ecm.RequestRemoveEntity(jointEntity); + + this->dataPtr->contactedEntity = gz::sim::kNullEntity; + this->dataPtr->detachableJoints.erase(it); + + return true; +} + +void VacuumGripper::Configure( + const gz::sim::Entity &_entity, + const std::shared_ptr &_element, + gz::sim::EntityComponentManager &_ecm, + gz::sim::EventManager &_eventManager) { + + rclcpp::Logger logger = rclcpp::get_logger("VacuumGripperGazeboPlugin"); + const auto model = gz::sim::Model(_entity); + + if (!model.Valid(_ecm)) { + RCLCPP_ERROR(logger, + "[Vacuum Gripper Plugin] Failed to initialize because [%s] " + "(Entity=%lu)] is not a model." + "Please make sure that Vacuum Gripper Plugin is attached to a " + "valid model.", + model.Name(_ecm).c_str(), _entity); + return; + } + + auto sdfPtr = const_cast(_element.get()); + + if (!sdfPtr->HasElement("link_name")) { + RCLCPP_ERROR(logger, "VacuumGripper link has not defined"); + return; + } + + this->dataPtr->gripper_link_name = + sdfPtr->GetElement("link_name")->Get(); + + _ecm.Each( + [&](const gz::sim::Entity &entity, + const gz::sim::components::Name *nameComp) -> bool { + if (nameComp->Data() == this->dataPtr->gripper_link_name) { + this->dataPtr->gripperEntity = entity; + gzdbg << "Found gripper link: " << this->dataPtr->gripper_link_name + << " (Entity ID: " << entity << ")" << std::endl; + return false; + } + return true; + }); + + if (this->dataPtr->gripperEntity == gz::sim::kNullEntity) { + RCLCPP_ERROR(logger, "Gripper link '%s' not found!", + this->dataPtr->gripper_link_name.c_str()); + return; + } + + std::string node_name = "gz_ros2_vacuum_gripper_plugin"; + + this->dataPtr->node_ = rclcpp::Node::make_shared(node_name); + + this->dataPtr->executor_ = + std::make_shared(); + this->dataPtr->executor_->add_node(this->dataPtr->node_); + auto spin = [this]() { this->dataPtr->executor_->spin(); }; + this->dataPtr->thread_executor_spin_ = std::thread(spin); + + toggle_service_ = + this->dataPtr->node_->create_service( + node_name + "/toggle", + std::bind(&VacuumGripper::Toggle, this, std::placeholders::_1, + std::placeholders::_2)); + + gzdbg << "rbs_mill_assist::VacuumGripper::Configure on entity: " << _entity + << std::endl; +} + +void VacuumGripper::PreUpdate(const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) { + if (!this->dataPtr->enabled) { + if (this->dataPtr->contactedEntity != gz::sim::kNullEntity) { + gzdbg << "Detaching object..." << std::endl; + if (this->DetachLink(_ecm)) { + gzdbg << "Object detached successfully!" << std::endl; + } else { + gzerr << "Failed to detach object!" << std::endl; + } + } + return; + } + + _ecm.Each( + [&](const gz::sim::Entity &entity, + const gz::sim::components::ContactSensorData *contactData) -> bool { + if (contactData->Data().contact_size() == 0) { + return true; + } + + auto msgEntity = contactData->Data().contact(0).collision2(); + gz::sim::Entity contactedCollision(msgEntity.id()); + + auto parentLink = _ecm.Component( + contactedCollision); + if (!parentLink) { + return true; + } + + if (this->dataPtr->contactedEntity != gz::sim::kNullEntity) { + return true; + } + + gz::sim::Entity gripperLink = this->dataPtr->gripperEntity; + gz::sim::Entity objectLink = parentLink->Data(); + + gzdbg << "Attaching object " << objectLink << " to gripper..." + << std::endl; + if (this->AttachLink(_ecm, gripperLink, objectLink)) { + this->dataPtr->contactedEntity = objectLink; + gzdbg << "Object attached successfully!" << std::endl; + } else { + gzerr << "Failed to attach object!" << std::endl; + } + + return true; + }); +} + +} // namespace rbs_mill_assist diff --git a/urdf/rbs_arm_modular.xacro b/urdf/rbs_arm_modular.xacro index 69692aa..25c92f4 100644 --- a/urdf/rbs_arm_modular.xacro +++ b/urdf/rbs_arm_modular.xacro @@ -17,43 +17,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/urdf/rbs_arm_modular_macro.xacro b/urdf/rbs_arm_modular_macro.xacro index fc57254..2d91c3a 100644 --- a/urdf/rbs_arm_modular_macro.xacro +++ b/urdf/rbs_arm_modular_macro.xacro @@ -47,7 +47,17 @@ ${namespace} + + ee_link + + + + + ee_link_collision + + + diff --git a/world/default.sdf b/world/default.sdf index 30caa97..792b739 100644 --- a/world/default.sdf +++ b/world/default.sdf @@ -6,13 +6,22 @@ false - + 0.001 1.0 + + bullet + + + + + + gz-physics-dartsim-plugin + + + true @@ -73,6 +85,52 @@ + + 1 + + 0 0 -0.40000000000000002 0 0 0 + + 0 0 0 0 0 0 + 10 + + 4.708333333333333 + 0 + 0 + 8.6666666666666661 + 0 + 8.0416666666666661 + + + + 0 0 0 0 0 0 + + + 1.2 0.69999999999999996 0.80000000000000004 + + + + + + + + + + + + 0 0 0 0 0 0 + + + 1.2 0.69999999999999996 0.80000000000000004 + + + + 0 0 1 1 + 0 0 1 1 + + + false + +