Otomasyon

Robot Kol Programlama: ROS 2 ile Pick-and-Place

Fatih Algül
12.03.2026 200 görüntülenme

Pick-and-Place Nedir ve Neden ROS 2?

Pick-and-place, endüstriyel robotların en temel ve yaygın görevlerinden biridir. Bir nesneyi belirli bir konumdan alıp başka bir konuma yerleştirme işlemi, üretim hatlarından lojistik merkezlerine kadar pek çok alanda kullanılır. ROS 2 (Robot Operating System 2), bu tür görevleri programlamak için güçlü bir ekosistem sunar. MoveIt 2 entegrasyonu, gerçek zamanlı kontrol desteği ve DDS tabanlı iletişim altyapısı sayesinde ROS 2, endüstriyel seviyede robot kol programlama için ideal bir platformdur.

Gerekli Yazılım ve Donanım Bileşenleri

Yazılım Gereksinimleri

  • ROS 2 Humble veya Iron: LTS sürümü tercih edilmelidir
  • MoveIt 2: Hareket planlama ve kinematik çözümleme için
  • Gazebo (Ignition): Simülasyon ortamı
  • ros2_control: Donanım soyutlama katmanı
  • Python 3 veya C++: Node geliştirme dilleri

Donanım Gereksinimleri

  • 6 DOF (Degree of Freedom) robot kol (ör. UR5e, Franka Emika Panda, xArm6)
  • Gripper (paralel jaw veya vakum tipi)
  • Derinlik kamerası (isteğe bağlı, ör. Intel RealSense D435)
  • Bilgisayar: En az 8 GB RAM, çok çekirdekli işlemci

Çalışma Ortamının Kurulumu

Öncelikle ROS 2 ve MoveIt 2 kurulumunu gerçekleştirelim. Ubuntu 22.04 üzerinde Humble sürümünü kullanacağız:

# ROS 2 Humble kurulumu sonrası MoveIt 2 yüklemesi
sudo apt install ros-humble-moveit ros-humble-moveit-setup-assistant
sudo apt install ros-humble-ros2-control ros-humble-ros2-controllers
sudo apt install ros-humble-gazebo-ros2-control

# Workspace oluşturma
mkdir -p ~/pick_place_ws/src
cd ~/pick_place_ws/src

# MoveIt 2 konfigürasyon paketini oluşturma
ros2 pkg create --build-type ament_python pick_place_demo \
  --dependencies rclpy moveit_msgs geometry_msgs

MoveIt 2 ile Hareket Planlama Mimarisi

Pick-and-place işlemi temelde dört aşamadan oluşur:

  1. Yaklaşma (Approach): Robot kolu nesnenin üzerine yaklaşır
  2. Kavrama (Grasp): Gripper kapanarak nesneyi kavrar
  3. Taşıma (Transport): Nesne hedef konuma taşınır
  4. Bırakma (Release): Gripper açılarak nesne bırakılır

MoveIt 2, bu aşamaların her biri için trajectory planlama, çarpışma kontrolü ve kinematik çözümleme sağlar. Arka planda OMPL (Open Motion Planning Library) kullanarak RRT*, PRM gibi algoritmalarla çarpışmasız yollar hesaplar.

Python ile Pick-and-Place Node Geliştirme

Aşağıda MoveIt 2 Python API'sini kullanarak temel bir pick-and-place node'u yer almaktadır:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Pose, PoseStamped
from moveit_msgs.msg import CollisionObject, Grasp
from shape_msgs.msg import SolidPrimitive
from moveit.planning import MoveItPy
from moveit.core.robot_state import RobotState
import numpy as np


class PickAndPlaceNode(Node):
    def __init__(self):
        super().__init__('pick_and_place_node')
        
        # MoveItPy başlatma
        self.moveit = MoveItPy(node_name="pick_place_moveit")
        self.arm = self.moveit.get_planning_component("manipulator")
        self.gripper = self.moveit.get_planning_component("gripper")
        
        # Planning sahnesine erişim
        self.planning_scene = self.moveit.get_planning_scene_monitor()
        
        self.get_logger().info("Pick-and-Place node başlatıldı.")

    def add_collision_objects(self):
        """Sahneye çarpışma nesneleri ekle (masa ve hedef nesne)."""
        with self.planning_scene.read_write() as scene:
            # Masa ekleme
            table = CollisionObject()
            table.id = "masa"
            table.header.frame_id = "world"
            
            table_primitive = SolidPrimitive()
            table_primitive.type = SolidPrimitive.BOX
            table_primitive.dimensions = [0.8, 1.2, 0.02]
            
            table_pose = Pose()
            table_pose.position.x = 0.5
            table_pose.position.y = 0.0
            table_pose.position.z = 0.4
            
            table.primitives.append(table_primitive)
            table.primitive_poses.append(table_pose)
            scene.apply_collision_object(table)
            
            # Kavranacak nesne ekleme
            obj = CollisionObject()
            obj.id = "kutu"
            obj.header.frame_id = "world"
            
            obj_primitive = SolidPrimitive()
            obj_primitive.type = SolidPrimitive.BOX
            obj_primitive.dimensions = [0.04, 0.04, 0.10]
            
            obj_pose = Pose()
            obj_pose.position.x = 0.5
            obj_pose.position.y = 0.2
            obj_pose.position.z = 0.46
            
            obj.primitives.append(obj_primitive)
            obj.primitive_poses.append(obj_pose)
            scene.apply_collision_object(obj)

    def move_to_pose(self, target_pose: Pose) -> bool:
        """Kolu belirtilen poza hareket ettir."""
        pose_stamped = PoseStamped()
        pose_stamped.header.frame_id = "world"
        pose_stamped.pose = target_pose
        
        self.arm.set_goal_state(pose_stamped_msg=pose_stamped)
        
        plan_result = self.arm.plan()
        if plan_result:
            self.get_logger().info("Plan başarılı, hareket yürütülüyor...")
            self.arm.execute()
            return True
        else:
            self.get_logger().error("Hareket planlaması başarısız!")
            return False

    def open_gripper(self):
        """Gripper'ı aç."""
        self.gripper.set_goal_state(configuration_name="open")
        plan = self.gripper.plan()
        if plan:
            self.gripper.execute()

    def close_gripper(self):
        """Gripper'ı kapat."""
        self.gripper.set_goal_state(configuration_name="close")
        plan = self.gripper.plan()
        if plan:
            self.gripper.execute()

    def attach_object(self, object_id: str):
        """Nesneyi end-effector'a bağla."""
        with self.planning_scene.read_write() as scene:
            scene.attach_object(object_id, "gripper_link")

    def detach_object(self, object_id: str):
        """Nesneyi end-effector'dan ayır."""
        with self.planning_scene.read_write() as scene:
            scene.detach_object(object_id)

    def execute_pick_and_place(self):
        """Tam pick-and-place döngüsünü çalıştır."""
        self.add_collision_objects()
        
        # 1. Gripper'ı aç
        self.get_logger().info("Adım 1: Gripper açılıyor...")
        self.open_gripper()
        
        # 2. Nesnenin üzerine yaklaş (pre-grasp pozisyon)
        self.get_logger().info("Adım 2: Pre-grasp pozisyona hareket...")
        pre_grasp = Pose()
        pre_grasp.position.x = 0.5
        pre_grasp.position.y = 0.2
        pre_grasp.position.z = 0.60  # Nesnenin 10cm üstü
        pre_grasp.orientation.x = 1.0  # Aşağı bakan yön
        pre_grasp.orientation.w = 0.0
        
        if not self.move_to_pose(pre_grasp):
            return False
        
        # 3. Kavrama pozisyonuna in
        self.get_logger().info("Adım 3: Kavrama pozisyonuna iniş...")
        grasp_pose = Pose()
        grasp_pose.position.x = 0.5
        grasp_pose.position.y = 0.2
        grasp_pose.position.z = 0.50
        grasp_pose.orientation.x = 1.0
        grasp_pose.orientation.w = 0.0
        
        if not self.move_to_pose(grasp_pose):
            return False
        
        # 4. Gripper'ı kapat ve nesneyi bağla
        self.get_logger().info("Adım 4: Nesne kavranıyor...")
        self.close_gripper()
        self.attach_object("kutu")
        
        # 5. Nesneyi yukarı kaldır
        self.get_logger().info("Adım 5: Nesne kaldırılıyor...")
        lift_pose = Pose()
        lift_pose.position.x = 0.5
        lift_pose.position.y = 0.2
        lift_pose.position.z = 0.65
        lift_pose.orientation.x = 1.0
        lift_pose.orientation.w = 0.0
        
        if not self.move_to_pose(lift_pose):
            return False
        
        # 6. Hedef konuma taşı
        self.get_logger().info("Adım 6: Hedef konuma taşınıyor...")
        place_pose = Pose()
        place_pose.position.x = 0.5
        place_pose.position.y = -0.2
        place_pose.position.z = 0.50
        place_pose.orientation.x = 1.0
        place_pose.orientation.w = 0.0
        
        if not self.move_to_pose(place_pose):
            return False
        
        # 7. Nesneyi bırak
        self.get_logger().info("Adım 7: Nesne bırakılıyor...")
        self.open_gripper()
        self.detach_object("kutu")
        
        # 8. Güvenli pozisyona geri dön
        self.get_logger().info("Adım 8: Başlangıç pozisyonuna dönüş...")
        self.arm.set_goal_state(configuration_name="home")
        plan = self.arm.plan()
        if plan:
            self.arm.execute()
        
        self.get_logger().info("Pick-and-place işlemi tamamlandı!")
        return True


def main(args=None):
    rclpy.init(args=args)
    node = PickAndPlaceNode()
    
    node.execute_pick_and_place()
    
    node.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

Gazebo Simülasyonunda Test Etme

Kodu gerçek robota göndermeden önce Gazebo simülasyonunda test etmek kritik önem taşır. Simülasyon ortamını başlatmak için:

# Terminal 1: Simülasyon ortamını başlat
ros2 launch pick_place_demo gazebo_sim.launch.py

# Terminal 2: MoveIt 2 ve RViz başlat
ros2 launch pick_place_demo moveit_demo.launch.py

# Terminal 3: Pick-and-place node'unu çalıştır
ros2 run pick_place_demo pick_and_place_node

Cartesian Path Planning ile Hassas Kontrol

Bazı pick-and-place senaryolarında düz çizgisel hareketler gerekir. Özellikle nesneye yaklaşırken ve bırakırken Cartesian yol planlama kullanmak daha güvenilir sonuçlar verir:

def compute_cartesian_path(self, waypoints, max_step=0.01):
    """Cartesian yol planla (düz çizgisel hareket)."""
    robot_state = self.arm.get_start_state()
    
    # Cartesian path hesaplama
    trajectory, fraction = robot_state.compute_cartesian_path(
        waypoints,
        max_step,       # Adım büyüklüğü (metre)
        0.0,            # Jump threshold
        avoid_collisions=True
    )
    
    if fraction < 0.95:
        self.get_logger().warn(
            f"Cartesian yolun yalnızca %{fraction*100:.1f}'i planlanabildi."
        )
        return None
    
    return trajectory

Gripper Kontrolü ve ros2_control Entegrasyonu

Gerçek donanımda gripper kontrolü ros2_control framework'ü üzerinden yapılır. YAML konfigürasyon dosyasında gripper controller'ını tanımlamak gerekir:

# gripper_controllers.yaml
controller_manager:
  ros__parameters:
    update_rate: 100
    
    gripper_controller:
      type: position_controllers/GripperActionController

gripper_controller:
  ros__parameters:
    joint: gripper_finger_joint
    goal_tolerance: 0.01
    max_effort: 50.0
    stall_velocity_threshold: 0.001
    stall_timeout: 1.0

Görüntü İşleme ile Dinamik Nesne Algılama

Statik koordinatlar yerine kamera ile nesne konumunu dinamik olarak belirlemek üretim ortamlarında zorunludur. Derinlik kamerası kullanarak nesne pozisyonunu TF2 frame'ine dönüştüren basit bir yaklaşım:

from tf2_ros import Buffer, TransformListener
from sensor_msgs.msg import PointCloud2

class ObjectDetector(Node):
    def __init__(self):
        super().__init__('object_detector')
        self.tf_buffer = Buffer()
        self.tf_listener = TransformListener(self.tf_buffer, self)
        
        self.subscription = self.create_subscription(
            PointCloud2,
            '/camera/depth/points',
            self.pointcloud_callback,
            10
        )
    
    def pointcloud_callback(self, msg):
        # Nokta bulutu işleme ve nesne segmentasyonu
        # Algılanan nesne pozisyonunu base_link frame'ine dönüştür
        try:
            transform = self.tf_buffer.lookup_transform(
                'base_link',
                msg.header.frame_id,
                rclpy.time.Time()
            )
            # Dönüştürülmüş koordinatları kullan
        except Exception as e:
            self.get_logger().warn(f"TF dönüşümü başarısız: {e}")

Hata Yönetimi ve Güvenlik Önlemleri

Endüstriyel ortamlarda güvenlik kritik öneme sahiptir. Dikkat edilmesi gereken noktalar:

  • Hız sınırlama: max_velocity_scaling_factor ve max_acceleration_scaling_factor parametrelerini 0.1-0.3 aralığında tutarak yavaş başlayın
  • Çarpışma kontrolü: Planning scene'e tüm statik engelleri ekleyin; self-collision kontrolünü asla devre dışı bırakmayın
  • Kavrama doğrulama: Gripper sensör verilerini okuyarak nesnenin gerçekten kavranıp kavranmadığını kontrol edin
  • Acil durdurma: E-stop entegrasyonunu mutlaka yapın ve yazılımsal olarak da /emergency_stop topic'ini dinleyin
  • Eklem limitleri: URDF/SRDF dosyalarındaki eklem limitlerinin doğru tanımlandığından emin olun
  • Zaman aşımı: Her planlama ve yürütme adımı için makul timeout değerleri belirleyin

Performans Optimizasyonu İpuçları

Üretim ortamlarında çevrim süresini minimize etmek önemlidir. Aşağıdaki stratejiler performansı artırır:

  • Pipeline planlama: Bir hareket yürütülürken sonraki hareketi arka planda planlayın
  • Planlayıcı seçimi: OMPL'nin RRTConnect algoritması genellikle en hızlı sonucu verir; PRM* ise tekrarlayan görevlerde daha verimlidir
  • Planlama süresi sınırlama: planning_time parametresini 1-5 saniye aralığında tutun
  • Waypoint azaltma: Gereksiz ara noktalardan kaçının; MoveIt trajectory post-processing otomatik olarak yolu düzeltir
  • Paralel planlama: MoveIt 2'nin paralel planlama özelliğini etkinleştirerek birden fazla planlayıcıyı eşzamanlı çalıştırın ve en iyi sonucu seçin

Simülasyondan Gerçek Robota Geçiş

Simülasyonda başarılı olan kodun gerçek robota aktarımında şu adımları izleyin:

  1. Robot üreticisinin ROS 2 driver paketini kurun (ör. ros-humble-ur-robot-driver)
  2. ros2_control konfigürasyonunu simülasyondan gerçek donanım interface'ine geçirin
  3. Hız ve ivme ölçekleme faktörlerini düşük değerlerle başlatın (0.05-0.1)
  4. İlk testleri teach pendant üzerinden E-stop'a hazır şekilde yapın
  5. Gripper kuvvet parametrelerini nesneye uygun şekilde kalibre edin
  6. TF frame kalibrasyonunu doğrulayın; kamera-robot ilişkisini hand-eye kalibrasyon ile belirleyin

Sonuç

ROS 2 ve MoveIt 2 kombinasyonu, pick-and-place uygulamalarını geliştirmek için güçlü ve esnek bir platform sunar. Bu yazıda ele aldığımız temel bileşenler — hareket planlama, gripper kontrolü, çarpışma yönetimi ve simülasyon — endüstriyel seviyede bir pick-and-place sistemi kurmanın yapı taşlarıdır. Simülasyonda kapsamlı testler yaparak ve güvenlik önlemlerini ihmal etmeyerek, gerçek dünya uygulamalarına güvenle geçiş yapabilirsiniz. ROS 2 ekosisteminin sürekli büyüyen paket deposu ve topluluk desteği, karmaşık robotik görevleri giderek daha erişilebilir hale getirmektedir.

Yazar Hakkında
Fatih Algül
TechSoft Solutions
Proje mi var?

Yazılım, IoT veya otomasyon konularında destek almak ister misiniz?

İletişime Geç