yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
DOFBOT-Ultra📖
1. Get Started Quickly
  • 0.Unboxing Video
  • 1. Get Started Quickly
  • 2. Access the Terminal
  • 3. Calibration offset(Depth Camera)
  • 4. Calibration offset(USB Camera)
2. Assembly Course
  • 1. Installation Steps
  • 2. Calibrate the Robotic Arm
  • 3. Install the Map
3. Remote Control Course
  • 1. Mobile Phone Control
  • 2. USB Handle Control
  • 3. PC Web Page Programming
4. Preparation
  • 1. Manage Web control services
  • 2. Manage JupyterLab services
  • 3. Manage OLED services
5. 2D Visual Tracking Course
  • 1. Color block positioning
  • 2. Color block tracking
  • 3. Face positioning
  • 4. Face tracking
  • 5. Label code positioning
  • 6. Label code tracking
6. 2D Visual Grasping Course
  • 1. Identify grab color block
  • 2. Catch game
  • 3. Color sorting
  • 4. Color stacking
  • 5. Label code sorting
  • 6. Label code stacking
  • 7. Gesture grasping color blocks
  • 8. Gesture stacking color blocks
  • 9. Single garbage sorting
  • 10. Garbage sorting
  • 11. Tracking game
7. 2D Mediapipe Course
  • 1. Hand detection
  • 2. Posture detection
  • 3. Overall detection
  • 4. Facial Detection
  • 5. Face Detection
  • 6. Face effects
  • 7. 3D Object Recognition
  • 8. Paintbrush
  • 9. Finger control
  • 10. Gesture Recognition
  • 11. Gesture control robotic arm action group
  • 12. Arm posture control robotic arm
  • 13. Palm Targeting
  • 14. Robotic arm tracks the palm
  • 15. Fingertip trajectory recognition
  • 16. Fingertip trajectory control robotic arm
  • 17. Gesture Grasp and Release Objects
  • 18. Finger Control Robot Arm
8. AI Large Model Voice Module (Must Read)
  • 1. Introduction to AI Large Model Voice Module
  • 2. Flash the firmware (Must read)
  • 3. Modify the wake-up word and command word
9. 2D Voice Control Course
  • 1. Simple Voice Control
  • 2. Smart Voice Control
  • 3. Voice Action Control
  • 4. Memory Learning Mode
  • 5. Color Recognition with Voice Broadcast
  • 6. Color Recognition and Grasping with Voice Broadcast
  • 7. Color Recognition and Sorting with Voice Broadcast
  • 8. Voice Recognition Target Tracking
  • 9. Voice Control Stacking
  • 10. Voice Control Sorting
  • 11. Garbage Recognition with Voice Broadcast
  • 12. Garbage Sorting with Voice Broadcast
10. 3D Depth Camera Course
  • 1. Dabai_DCW2 camera
  • 2. Deep pseudo-color image
  • 3. Depth camera ranging
  • 4. Color block volume measurement
11. 3D Robotic Arm Control and Inverse Kinematics
  • 1. Robotic arm ROS control
  • 2. Inverse solution
12. 3D Sorting and Grasping Course
  • 1. AprilTag ID sorting
  • 2. AprilTag abnormal height sorting
  • 3. MediaPipe Gesture-AprilTag ID sorting
  • 4. MediaPipe Gesture-AprilTag height sorting
  • 5. MediaPipe Gesture-AprilTag distance sorting
  • 6. Block color sorting
  • 7. Block abnormal height sorting
  • 8. Block different shape sorting
  • 9. Block shape abnormal height sorting
  • 10. Yolov26 garbage sorting
13. 3D Tracking and Grasping Course
  • 1. AprilTag Tracking and Grasping
  • 2. Color Block Tracking and Grasping
  • 3. KCF Object Tracking and Grasping
14. 3D Voice Control for Grasping Course (Sorting & Tracking)
  • 1. Voice Control AprilTag ID Sorting
  • 2. Voice Control Remove Height Abnormal AprilTag
  • 3. Voice Control Tracking and Grasping AprilTag
  • 4. Voice Control Color Block Sorting
  • 5. Voice Control Remove Height Abnormal Color Block
  • 6. Voice Control Tracking and Grasping Color Block
  • 7. Voice Control Yolov26 Garbage Recognition Sorting
  • 8. Voice Control Object Tracking and Grasping
15. AI Model Basics
  • 1. AI Large Model Types and Principles
  • 2. RAG Retrieval Enhancement and Model Training Samples
  • 3. Embodied Intelligent Robot System Architecture
  • 4. Embodied Intelligent Functions Core Source Code
16. AI Model Development
  • 0. DofbotUltra Large Model Development Quick Start Guide
  • 1. Register a Model Service Provider Account
  • 2. Configure API-KEY
  • 3. Introduction to Dify
  • 4. Basic Dify Features
  • 5. AI Large Model Development - chatbot
  • 6. Deploy the RAG knowledge base
  • 7. RAG knowledge base + chatbot
  • 8. AI agent workflow
17. AI Model – Text Version
  • 1. Semantic understand instruction following
  • 2. Multimodal visual understand
  • 3. Text Large Model + robotic arm control
  • 4. Multimodal Large Model + Video Understanding
  • 5. Multimodal Large Model + Robotic Arm Gripping
  • 6. Multimodal Large Model + Robotic Arm Tracking
  • 7. Multimodal Large Model + Robotic Arm Sorting
  • 8. Multimodal Large Model + Robotic Arm Handling
  • 9. Multimodal Large Model + Color Blocks Back In Place
  • 10. Multimodal Large Model + Intent Prediction + Robotic Arm Gripping
  • 11. Text Large Model + YOLOv26 + Intelligent Garbage Classification
18. AI Model – Voice Version
  • 1. Semantic understand instruction following
  • 2. Multimodal visual understand
  • 3. Text Large Model + robotic arm control
  • 4. Multimodal Large Model + Video Understanding
  • 5. Multimodal Large Model + Robotic Arm Gripping
  • 6. Multimodal Large Model + Robotic Arm Tracking
  • 7. Multimodal Large Model + Robotic Arm Sorting
  • 8. Multimodal Large Model + Robotic Arm Handling
  • 9. Multimodal Large Model + Color Blocks Back In Place
  • 10. Multimodal Large Model + Intent Prediction + Robotic Arm Gripping
  • 11. Text Large Model + YOLOv26 + Intelligent Garbage Classification
19. AI Model – Fun Applications
  • 1. AI Butler-Intent Inference
  • 2. AI Encyclopedia-Arithmetic Master
  • 3. AI Encyclopedia-You asked I Pointed
  • 4. AI Encyclopedia-Nutritional Analysis
  • 5. AI Encyclopedia-Problem Solving Expert
  • 6. AI Encyclopedia-English Spelling
  • 7. AI Butler-Garbage Sorting
  • 8. AI Top Chef
20. MoveIt Course
  • 1. MoveIt Configuration
  • 2. MoveIt Usage
  • 3. MoveIt Drive Real Robot
  • 4. MoveIt Random Movement
  • 5. MoveIt Forward Kinematics Design
  • 6. MoveIt Inverse Kinematics Design
  • 7. MoveIt Cartesian Path
  • 8. MoveIt Scene Design
  • 9. MoveIt Trajectory Planning
  • 10. MoveIt Collision Detection
21. YOLOv11 Development Course
PI 5
  • 1. YOLO Environment Setup
  • 2. CLI Usage
  • 3. Object Detection
  • 4. Instance Segmentation
  • 5. Pose Estimation
  • 6. Image Classification
  • 7. Oriented Bounding Boxes Object Detection
  • 8. Model Conversion
  • 9. Dataset Annotation
  • 10. Model Training and Conversion
  • 11. Model Prediction
RDK X5
  • 1. YOLO Environment Setup
  • 2. CLI Usage
  • 3. Object Detection
  • 4. Instance Segmentation
  • 5. Pose Estimation
  • 6. Image Classification
  • 7. Oriented Bounding Boxes Object Detection
  • 8. Model Prediction
  • 9. Model Conversion
22. YOLOv26 Development Course
PI 5
  • 6. Image Classification
  • 1. YOLO Environment Setup
  • 2. CLI Usage
  • 3. Object Detection
  • 4. Instance Segmentation
  • 5. Pose Estimation
  • 6. Image Classification
  • 7. Oriented Bounding Boxes Object Detection
  • 8. Model Conversion
  • 9. Dataset Annotation
  • 10. Model Training and Conversion
  • 11. Model Prediction
RDK X5
  • 1. YOLO Environment Setup
  • 2. CLI Usage
  • 3. Object Detection
  • 4. Instance Segmentation
  • 5. Pose Estimation
  • 7. Oriented Bounding Boxes Object Detection
  • 8. Model Conversion
  • 9. Dataset Annotation
  • 10. Model Training and Conversion
  • 11. Model Prediction
23.Linux Operating System
  • 1.Introduction to Linux system
  • 2.Ubuntu file system
  • 3.Ubuntu common commands
  • 4.Ubuntu common editors
  • 5.Ubuntu software operation commands
  • 6.Virtual machine installation
  • 7.SSH remote control
  • 8.VNC remote control
  • 9.Transfer files remotely
  • 10.Driver library and communication
  • 11.Capacity expansion and resource allocation
  • 12.Bind device ID
  • 13.Capacity expansion and resource allocation
  • 14.Update system software sources
  • 15.Set root user password
  • 16.sudo free password
  • 17.Connect to WiFi network
  • 18.View system version
  • 19.Customized service management
  • 20.Back up system image
24. Basic Control Course
  • 1. Control RGB Light
  • 2. Control Buzzer
  • 3. Control Single Servo
  • 4. Read Servo Angle
  • 5. Control All Servos
  • 6. DOFBOT Up Down Left Right Movement
  • 7. DOFBOT Dancing
  • 8. DOFBOT Memory Actions
  • 9. DOFBOT Grab Blocks
  • 10. Porter
  • 11. Stack Blocks
  • 12. Two DOFBOT Synchronized Actions
25.RDKx5 Board Basic Course
  • 1.Introduction to RDK X5 board
  • 2.Write system file
  • 3.Hardware interface description
  • 4.Install case and other accessories
  • 5.Use 7-inch display
  • 6.SD card expansion
  • 7.Network configuration
  • 8.Remote login
  • 9.Update system
  • 10.srpi-config user guide
  • 11.config. txt configuration file
  • 12.Self-startup setting
  • 13.RDK exclusive command usage
  • 14.Linux command usage
  • 15.GPIO readwrite operation
  • 16.Image classification algorithm
  • 17.Use USB camera
  • 18.Use MIPI camera
  • 19.Binocular camera depth algorithm
26.PI5 Board Basic Course
  • 1.Introduction to Raspberry PI 5
  • 2.Raspberry PI system installation and backup
  • 3.Powering the Raspberry PI 5
  • 4.Startup of Raspberry PI 5
  • 5.Installation and introduction of shell and active radiator
  • 6.GPIO and 40-pin pins
  • 7.Tips for using Raspberry PI 5
  • 8.Raspberry PI PCIe connector
  • 9.MIPI CSI DSI connector
  • 10.UART connector
  • 11.Real-time clock
  • 12.Power over Ethernet (PoE) connector
  • 13.Update and upgrade operating system
  • 14.Introduction to raspi-config tool
  • 15.config.txt file description
  • 16.Network Configuration
  • 17.remote access
  • 18.Transfer files remotely
  • 19.Set display resolution and rotation
  • 20.Set screen to sleep
  • 21.Get real-time temperature of Raspberry Pi
  • 22.Play audio and video
  • 23.Using USB camera
  • 24.Using MIPI camera
27. Visual Basics Course
  • 1. Introduction to Open Source CV
  • 2. Image Reading and Display
  • 3. Image Writing
  • 4. Image Quality
  • 5. Pixel Operation
  • 6. Image Zoom
  • 7. Picture Cut
  • 8. Picture Pan
  • 9. Picture Mirroring
  • 10. Affine Transformation
  • 11. Picture Rotation
  • 12. Perspective Transformation
  • 13. Grayscale Processing
  • 14. Binary Image
  • 15. Edge Detection
  • 16. Line Segment Drawing
  • 17. Rectangular Circle Drawing
  • 18. Text and Picture Drawing
28. ROS2 Basic Course
  • 1.Introduction to ROS2
  • 2.ROS2 install Humble
  • 3.ROS2 development environment
  • 4.ROS2 workspace
  • 5.ROS2 function package
  • 6.ROS2 node
  • 7.ROS2 topic communication
  • 8.ROS2 service communication
  • 9.ROS2 action communication
  • 10.ROS2 custom interface message
  • 11.ROS2 parameter service case
  • 12.ROS2 meta-function package
  • 13.ROS2 distributed communication
  • 14.ROS2 DDS
  • 15.ROS2 time related API
  • 16.ROS2 common command tools
  • 17.ROS2 rviz2 use
  • 18.ROS2 rqt toolbox
  • 19.ROS2 Launch startup file configuration
  • 20.ROS2 recording and playback tool
  • 21.ROS2 URDF model
  • 22.ROS2 Gazebo simulation platform
  • 23.ROS2 TF2 coordinate transformation
29. Jupyter Lab Usage Course
  • 1. Read the document before operation
  • 2. Jupyter lab environment construction tutorial
  • 3. Basic tutorial of Jupyter lab
  • 4. Jupyter widgets configuration tutorial
  • 5. Getting Started Tutorial for JupyterWidgets Controls
Download
  • System_File
  • VM_File
  • Instruction Manual
  • Code
  • Software Tools
  • Communication_Protocols
  • Hardware_Info
Download ZIP

Welcome to DOFBOT-Ultra repository

1. AprilTag ID sorting

AprilTag ID Sorting

Please ensure that the web control program backend process is not running in the background. To close it, refer to the [Get Started Quickly - 2. Access the Terminal] section in [Disable the Auto-Start Web Control Backend Process]. After closing, please restart DofbotUltra!!!

Before starting this function, the web control program backend process must be closed.

1. Function Description

After the program starts, the camera detects AprilTags, outlines them in the image, and prints the corresponding ID value. Press the space bar to start the sorting program. The robotic arm will grasp the detected AprilTag block and place it at the designated position based on the detected ID.

If the grip is inaccurate, please recalibrate.

The vision and grasping logic in this tutorial automatically switches based on CAMERA_TYPE, but the camera startup command must be selected according to the actual camera type:

  • DaBai Depth Camera: Use dabai_dcw2.launch.py

  • Standard USB Camera: Use usb_cam.launch.py

2. Startup and Operation

2.1. Startup Commands

Open a terminal and enter the following commands (open multiple terminals; the system will automatically enter the Docker environment). For RDK, run directly on the host machine:

​x
# DaBai Depth Camera
ros2 launch orbbec_camera dabai_dcw2.launch.py

# Standard USB Camera
ros2 launch dofbot_ultra_info usb_cam.launch.py

# Start the inverse solution program
ros2 run dofbot_ultra_info kinematics_dofbotultra
# Start the low-level control:
ros2 run dofbot_ultra_driver arm_driver
# AprilTag detection
ros2 run dofbot_ultra_driver apriltag_detect
# Grasping
ros2 run dofbot_ultra_driver grasp

# Set exposure
ros2 service call /camera/set_color_exposure orbbec_camera_msgs/srv/SetInt32 '{data: 30}'
ros2 service call /camera/set_depth_auto_exposure std_srvs/srv/SetBool '{data: true}'

Notes:

  1. The camera startup command must be selected according to the actual camera type.

  2. Except for the camera startup command, the startup method for other nodes is the same.

image-20260521104002840

Can be hand-held or placed on a horizontal tabletop.

2.2. Operation

Use a 3x3 square block (30mm x 30mm x 30mm). Click on the image frame (to give focus to the image GUI), then press the space bar. The robotic arm will automatically select the coordinate calculation method based on the current CAMERA_TYPE:

  • Depth Camera Mode: Calculates the grasping point based on the AprilTag center coordinates and the center point depth value.

  • Standard USB Camera Mode: Calculates the grasping point based on the AprilTag center pixel coordinates and the completed planar calibration results.

The robotic arm will then lower its gripper to grasp the detected AprilTag block. After grasping, it places the block at the designated position based on the detected ID. After placement, it returns to the detection posture to detect the next AprilTag.

Information displayed in the grasping terminal:

image-20260202111118367

Output data analysis:

System Initialization Phase

xxxxxxxxxx
[DofbotConfig] Loaded offsets from .../offset_value.yaml
Bias: x=-0.015, y=0.0, z=-0.015
Comp: x=0.07, z=-0.08
slope_x: -0.0621524570603543, slope_y: -0.03930853176041961

Loaded 4 calibration configuration files:

  • Fixed Offset: Systematic error in visual detection

  • Arm Bias: Physical error in robotic arm movement (-1.5cm in X/Z directions)

  • Plane Tilt Correction: Worktable tilt compensation coefficients

  • Height Dynamic Compensation: Dynamically adjusts X/Z compensation based on height

xxxxxxxxxx
Current_End_Pose: [0.1198, 0.0006, 0.0914, 3.124, 1.047, 0.000]

Initial End-Effector Pose: Current robotic arm position (X=11.98cm, Y=0.06cm, Z=9.14cm)

Visual Detection

xxxxxxxxxx
xyz id : 1 pos : 351.748, 372.711, 0.188

Detected AprilTag ID=1:

  • Pixel coordinates: (351.7, 372.7)

  • Distance: 18.8cm

xxxxxxxxxx
Coord Raw: x=0.165, y=-0.012, z=0.004

Raw World Coordinates (uncorrected): Object position (16.5cm, -1.2cm, 0.4cm)

Coordinate Correction Detailed Process

1️⃣ Plane Tilt Correction
xxxxxxxxxx
Tilt Correction: SlopeX=-0.0622, SlopeY=-0.0393 -> Correction=-0.0098

Compensation: Due to table tilt (possible) and visual errors (inevitable), the position X=16.5cm, Y=-1.2cm requires +0.98cm height compensation.

2️⃣ Height Dynamic Compensation
xxxxxxxxxx
Height Comp: Z_Comp_X=0.07 * 0.0035 = 0.0002
Height Comp: Z_Comp_Z=-0.08 * 0.0035 = -0.0003

Behavior: When grasping at a higher position, the end-effector tends to extend forward 0.02mm and droop 0.03mm, requiring compensation.

3️⃣ Final Coordinate Calculation
xxxxxxxxxx
Final X: 0.1651 + 0.0165(Offset) + (-0.0150)(Bias) - 0.0002 = 0.1663
Final Z: 0.0035 + 0.0008 + (-0.0150) - (-0.0098) - (-0.0003) = -0.0006

Final Target: [16.63cm, 0.21cm, -0.06cm]

xxxxxxxxxx
✅ Z=-0.06cm >= -8cm This is a safety limit to prevent the arm from grasping too low.

Inverse Kinematics Solution

xxxxxxxxxx
Grasping target: x=0.166, y=0.002, z=-0.001
Target Orientation: r=0.000, p=-1.570, y=0.013

Grasp Command:

  • Position: Approaching the tabletop grasping posture

  • Orientation: Yaw adapts to the target angle

xxxxxxxxxx
calcutelate_response: joint1=90.5°, joint2=129.4°, joint3=156.4°, joint4=153.5°, joint5=95.8°
[90.5, 50.6, 23.6, 26.5, 95.8, 30]

After successful inverse kinematics solution, 6 joint angles are obtained, and the robotic arm starts moving.

Grasp Sequence Execution

xxxxxxxxxx
self.cur_tagId 1 → self.down_joint [155.0, 35, 70, 5, 60.0, 142]
Grasp sequence completed.

Complete Grasp Workflow:

  1. ✅ Move to target (1 second)

  2. ✅ Close gripper (1.5 seconds)

  3. ✅ Raise arm (1.8 seconds)

  4. ✅ Move to Tag1 drop-off position

  5. ✅ Release gripper

  6. ✅ Return to initial position

3. Program Flowchart

apriltag_detect.py

image-20240924151909138

grasp.py

image-20240926113504611

4. Core Code Analysis

4.1. apriltag_detect.py

Code path:

xxxxxxxxxx
Raspberry Pi:/home/yahboom/dofbot_ultra_ws/src/dofbot_ultra_driver/dofbot_ultra_driver/apriltag_detect.py
RDK:/home/sunrise/dofbot_ultra_ws/src/core/dofbot_ultra_driver/dofbot_ultra_driver/apriltag_detect.py

Import necessary library files:

xxxxxxxxxx
import rclpy
from rclpy.node import Node
import cv2 
import numpy as np
from sensor_msgs.msg import Image
from message_filters import ApproximateTimeSynchronizer, Subscriber
from std_msgs.msg import Float32, Bool
from cv_bridge import CvBridge
import cv2 as cv
from dt_apriltags import Detector
import threading
# Import AprilTag drawing library
from dofbot_ultra_driver.vutils import draw_tags
# Import custom service data type
from dofbot_ultra_interface.srv import Kinematics
# Import custom message data types
from dofbot_ultra_interface.msg import *
import pyzbar.pyzbar as pyzbar
from std_msgs.msg import Float32,Bool
import time
import queue

Program parameter initialization, creating publishers and subscribers:

xxxxxxxxxx
def __init__(self):
    rospy.init_node('apriltag_detect')
    #self.init_joints = [90.0, 120, 0, 0.0, 90, 90]
    # Publish the initial posture of the arm, which is also the detection posture
    self.init_joints = [90.0, 120, 0.0, 0.0, 90, 30]
    # Create two subscribers for color image and depth image topics
    self.depth_image_sub = Subscriber(self, Image, '/camera/depth/image_raw')
    self.rgb_image_sub = Subscriber(self, Image, '/camera/color/image_raw')
    # Create a publisher for AprilTag information
    self.pos_info_pub = self.create_publisher(AprilTagInfo, "PosInfo", qos_profile=10)
    # Create a publisher for the arm's target angle
    self.pubPoint = self.create_publisher(ArmJoint, "TargetAngle", qos_profile=1)
    # Time-synchronize the color and depth image subscriptions
    self.TimeSynchronizer = message_filters.ApproximateTimeSynchronizer([self.rgb_image_sub,self.depth_image_sub],1,0.5)
    # Create a subscriber for grasping results
    self.subscription = self.create_subscription(Bool,'grasp_done',self.GraspStatusCallback,qos_profile=1)
    # Register the TagDetect callback for synchronized messages
    self.ts.registerCallback(self.TagDetect)
    # Create bridges to convert image topic messages to image data
    self.rgb_bridge = CvBridge()
    self.depth_bridge = CvBridge()
    # Flag for publishing AprilTag info, publishes /TagInfo topic data when True
    self.pubPos_flag = False
    # Create the AprilTag detector object with parameters:
    '''
    searchpath: Path to look for tag models.
    families: Tag family to use, e.g. 'tag36h11'.
    nthreads: Number of parallel processing threads for faster detection.
    quad_decimate: Downscale factor for the input image to reduce computation.
    quad_sigma: Standard deviation for Gaussian blur in image preprocessing.
    refine_edges: Whether to refine edges for higher detection accuracy.
    decode_sharpening: Sharpening parameter for decoding, enhances tag contrast.
    debug: Debug mode switch for viewing detection process information.
    '''
    self.at_detector = Detector(searchpath=['apriltags'], 
                                families='tag36h11',
                                nthreads=8,
                                quad_decimate=2.0,
                                quad_sigma=0.0,
                                refine_edges=1,
                                decode_sharpening=0.25,
                                debug=0)
   

Key focus on the TagDetect callback function:

xxxxxxxxxx
def TagDetect(self,color_frame,depth_frame):
    #rgb_image
    # Receive color image topic message, convert message data to image data
    rgb_image = self.rgb_bridge.imgmsg_to_cv2(color_frame,'rgb8')
    result_image = np.copy(rgb_image)
    #depth_image
    # Receive depth image topic message, convert message data to image data
    depth_image = self.depth_bridge.imgmsg_to_cv2(depth_frame, encoding[1])
    # Convert depth image to pseudo-color image
    depth_to_color_image = cv.applyColorMap(cv.convertScaleAbs(depth_image, alpha=0.03), cv.COLORMAP_JET)
    frame = cv.resize(depth_image, (640, 480))
    depth_image_info = frame.astype(np.float32)
    # Call the detect function with parameters:
    '''
    cv2.cvtColor(rgb_image, cv2.COLOR_RGB2GRAY): Convert RGB image to grayscale for tag detection.
    False: Do not estimate tag pose.
    None: No camera parameters provided, possibly for simple detection only.
    0.025: Tag size (in meters) to assist the detection algorithm.
    Returns detection results including each tag's position, ID, and bounding box.
    '''
    tags = self.at_detector.detect(cv2.cvtColor(rgb_image, cv2.COLOR_RGB2GRAY), False, None, 0.025)
    # Sort the detected tags (optional step)
    tags = sorted(tags, key=lambda tag: tag.tag_id) 
    # Call draw_tags to draw detected AprilTag info on the color image (corners, center, ID)
    draw_tags(result_image, tags, corners_color=(0, 0, 255), center_color=(0, 255, 0))
    # Wait for keyboard input, 32 = space bar, changes self.pubPos_flag to allow publishing tag info
    key = cv2.waitKey(10)
    if key == 32:
        self.pubPos_flag = True
    # Check if tags are detected (len > 0)
    if len(tags) > 0 :
        # Iterate through detected tags
        for i in range(len(tags)):
            if self.pubPos_flag == True:
                # Get the center of the detected tag
                center_x, center_y = tags[i].center
                # Mark the center point on the pseudo-color image
                cv.circle(result_image, (int(center_x),int(center_y)), 10, (0,210,255), thickness=-1)
                # Create AprilTag info message
                tag = AprilTagInfo()
                # Assign values: id = tag ID, x and y = center coordinates, z = depth value at center (scaled down by 1000, in meters)
                tag.id = tags[i].tag_id
                tag.x = center_x
                tag.y = center_y
                tag.z = depth_image_info[int(center_y),int(center_x)]/1000
                # Print detected tag info
                print("tag_id: ",tags[i].tag_id)
                print("center_x, center_y: ",center_x, center_y)
                print("depth: ",depth_image_info[int(center_y),int(center_x)]/1000)
                # If tag distance > 0, it is valid data; publish the tag info message
                if tag.z>0:
                    self.tag_info_pub.publish(tag)
                    # Change self.pubPos_flag to prevent multiple publishes; wait for grasp completion to reset
                    self.pubPos_flag = False
                else:
                    print("Invalid distance.")
    # Convert color image from RGB to BGR
    result_image = cv2.cvtColor(result_image, cv2.COLOR_RGB2BGR)
    # Display images
    cv2.imshow("result_image", result_image)
    cv2.imshow("depth_image", depth_to_color_image)
    key = cv2.waitKey(1)

Grasp status callback function:

xxxxxxxxxx
def GraspStatusCallback(self,msg):
    # If received message data is True, grasping is complete; set self.pubPos_flag to allow publishing next frame
    if msg.data == True:
        self.pubPos_flag = True

4.2. grasp.py

Code path:

xxxxxxxxxx
Raspberry Pi:/home/yahboom/dofbot_ultra_ws/src/dofbot_ultra_driver/dofbot_ultra_driver/grasp.py
RDK:/home/sunrise/dofbot_ultra_ws/src/core/dofbot_ultra_driver/dofbot_ultra_driver/grasp.py

Import necessary library files:

xxxxxxxxxx
import rclpy
from rclpy.node import Node
import numpy as np
from std_msgs.msg import Float32, Bool
import time
import math
from dofbot_ultra_interface.msg import *      
from dofbot_ultra_interface.srv import Kinematics
# Import transforms3d library for handling 3D transformations, quaternions, rotation matrices, and Euler angles
import transforms3d as tfs
# Import transformations library for 3D transformations including quaternions and Euler angles
import tf_transformations as tf         # ROS2 uses tf_transformations
import threading
from ament_index_python import get_package_share_directory
import yaml
import os
from Arm_Lib import Arm_Device

Open the offset parameter file:

xxxxxxxxxx
pkg_path = get_package_share_directory('dofbot_ultra_driver')
offset_file = os.path.join(pkg_path,'config', 'offset_value.yaml')

with open(offset_file, 'r') as file:
    offset_config = yaml.safe_load(file)
print(offset_config)
print("----------------------------")
print("x_offset: ",offset_config.get('x_offset'))
print("y_offset: ",offset_config.get('y_offset'))
print("z_offset: ",offset_config.get('z_offset'))

Program parameter initialization, creating publishers, subscribers, and clients:

xxxxxxxxxx
def __init__(self):
    super().__init__('color_grap')
    # Create a subscriber for the TagInfo topic to receive AprilTag information
    self.sub = self.create_subscription(AprilTagInfo,'PosInfo',self.pos_callback,1)
    # Create a publisher for the servo target angle topic to control the arm
    self.pub_point = self.create_publisher(ArmJoint, 'TargetAngle',1)
    # Create a publisher for the grasping result topic
    self.pubGraspStatus = self.create_publisher(Bool, 'grasp_done',1)
    # Create a client for requesting the inverse solution service to calculate end-effector pose and joint values
    self.client = self.create_client(Kinematics, 'dofbot_kinematics')
    # Initial grasping flag, True = can grasp, False = cannot grasp
    self.grasp_flag = True
    # Arm initial posture
    self.init_joints = [90.0, 120, 0.0, 0.0, 90, 90]
    self.down_joint = [130.0, 55.0, 34.0, 16.0, 90.0,125]
    self.gripper_joint = 90
    # Initialize current end-effector position and pose (x, y, z, roll, pitch, yaw)
    self.CurEndPos = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
    # Depth camera intrinsic parameters
    self.camera_info_K = [477.57421875, 0.0, 319.3820495605469, 0.0, 477.55718994140625, 238.64108276367188, 0.0, 0.0, 1.0]
    # Rotation/transformation matrix between arm end and camera, describing relative position and pose
    self.EndToCamMat = np.array([[1.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00],
                                 [0.00000000e+00,7.96326711e-04,9.99999683e-01,-9.90000000e-02],
                                 [0.00000000e+00,-9.99999683e-01,7.96326711e-04,4.90000000e-02],
                                 [0.00000000e+00,0.00000000e+00,0.00000000e+00,1.00000000e+00]])
    # Get the current end-effector position and pose, updates self.CurEndPos
    self.get_current_end_pos()
    # Define the current tag ID, used for placing the block at the corresponding position
    self.cur_tagId = 0
    # Read offset parameter file and assign to offset variables
    self.x_offset = offset_config.get('x_offset')
    self.y_offset = offset_config.get('y_offset')
    self.z_offset = offset_config.get('z_offset')
    # Print current end-effector position and pose
    print("Current_End_Pose: ",self.CurEndPos)
    print("Init Done")

AprilTag info callback function tag_info_callback:

xxxxxxxxxx
def tag_info_callback(self,msg):
    pos_x = msg.x
    pos_y = msg.y
    pos_z = msg.z
    self.cur_tagId = msg.id
    # Check if the received center depth is > 0, indicating valid data
    if pos_z!=0.0:
        print("xyz id : ",pos_x,pos_y,pos_z,self.cur_tagId)
        # First coordinate transformation: pixel coordinates to camera coordinates
        camera_location = self.pixel_to_camera_depth((pos_x,pos_y),pos_z)
        #print("camera_location: ",camera_location)
        # Second coordinate transformation: camera coordinates to arm end coordinates
        PoseEndMat = np.matmul(self.EndToCamMat, self.xyz_euler_to_mat(camera_location, (0, 0, 0)))
        #PoseEndMat = np.matmul(self.xyz_euler_to_mat(camera_location, (0, 0, 0)),self.EndToCamMat)
        EndPointMat = self.get_end_point_mat()
        # Third coordinate transformation: arm end coordinates to base coordinates
        # The resulting WorldPose (rotation matrix) is the AprilTag center's position and pose relative to the arm base
        WorldPose = np.matmul(EndPointMat, PoseEndMat) 
        #WorldPose = np.matmul(PoseEndMat,EndPointMat)
        # Convert rotation matrix to xyz and Euler angles
        pose_T, pose_R = self.mat_to_xyz_euler(WorldPose)
        # Add offset parameters to compensate for servo value discrepancies
        pose_T[0] = pose_T[0] + self.x_offset
        pose_T[1] = pose_T[1] + self.y_offset
        pose_T[2] = pose_T[2] + self.z_offset
        print("pose_T: ",pose_T)
        # Check grasping flag, True = proceed with grasping
    if self.grasp_flag == True :
        print("Take it now.")
        # Change the flag to prevent re-grasping during the current grasp operation
        self.grasp_flag = False
        # Start a thread to execute the grasp program with the calculated pose_T (xyz values)
        grasp = threading.Thread(target=self.grasp, args=(pose_T,))
        # Start the thread
        grasp.start()
        grasp.join()

Arm grasping function grasp:

xxxxxxxxxx
def grasp(self,pose_T):
    print("------------------------------------------------")
    print("pose_T: ",pose_T)
    # Call the inverse solution service (IK), assign the required request parameters
    request = kinematicsRequest()
    # Target x position of the arm end, in meters
    request.tar_x = pose_T[0] -0.01
    # Target y position of the arm end, in meters
    request.tar_y = pose_T[1] 
    # Target z position of the arm end, in meters; 0.2 is a scaling factor, adjust as needed
    request.tar_z = pose_T[2] +  (math.sqrt(request.tar_y**2+request.tar_x**2)-0.181)*0.2
    # Set the service type to ik
    request.kin_name = "ik"
    # Target Roll of the arm end, in radians; uses the current arm end roll value
    request.Roll = self.CurEndPos[3] 
    print("calcutelate_request: ",request)
    try:
        
        future = self.client.call_async(request)
        rclpy.spin_until_future_complete(self, future, timeout_sec=5.0) 
        response = future.result()
        #print("calcutelate_response: ",response)
        joints = [0.0, 0.0, 0.0, 0.0, 0.0,0.0]
        # Assign the joint1-joint6 values returned by the service
        joints[0] = response.joint1 #response.joint1
        joints[1] = response.joint2
        joints[2] = response.joint3
        if response.joint4>90:
            joints[3] = 90
        else:
            joints[3] = response.joint4
            joints[4] = 90 
            joints[5] = 30
        print("compute_joints: ",joints)
        # Call pubTargetArm with the calculated joint values
        self.pubTargetArm(joints)
        time.sleep(3.5)
        # Call move to place the block based on the detected tag ID
        self.move()

        except Exception:
            rospy.loginfo("run error")

Function to publish arm target angles pubTargetArm:

xxxxxxxxxx
def pubTargetArm(self, joints, id=6, angle=180.0, runtime=2000):
        print(joints)      self.Arm.Arm_serial_servo_write6(joints[0],joints[1],joints[2],joints[3],joints[4],joints[5],2000)

Grasping and placement function move:

def move(self):
    print("self.gripper_joint = ",self.gripper_joint)
    self.pubArm([],5, self.gripper_joint, 2000)
    time.sleep(2.5)
    self.pubArm([],6, 140, 2000)
    time.sleep(2.5)
    self.pubArm([],2, 120, 2000)
    time.sleep(2.5)
    # Change self.down_joint based on tag ID, representing the placement position
    if self.cur_tagId == 1:
        self.down_joint = [150.0, 30, 70, 5, 90.0,140]
    elif self.cur_tagId == 2:
        self.down_joint = [180.0, 35, 60, 0, 90.0,140]
    elif self.cur_tagId == 3:
        self.down_joint = [28.0, 30, 70, 2, 90.0,140]
    elif self.cur_tagId == 4:
        self.down_joint = [0.0, 43, 48, 6, 90.0,140]
    self.pubArm(self.down_joint)
    time.sleep(2.5)
    self.pubArm([],6, 90, 2000)
    time.sleep(2.5)
    self.pubArm([],2, 90, 2000)
    time.sleep(2.5)
    # After placement, return to the initial position
    self.pubArm(self.init_joints)
    time.sleep(5)
    # Wait for the arm to return to its initial position, then publish grasp completion message
    self.grasp_flag = True
    grasp_done = Bool()
    grasp_done.data = True
    self.pubGraspStatus.publish(grasp_done)
Inverse solution
AprilTag abnormal height sorting

COMPANY

  • About us
  • Team Style
  • Blog

LEARNING & EXPLORATION

  • Tutorials
  • Edge AI hardware
  • Educational robots
  • Embedded intelligent hardware

WHERE TO BUY

  • Yahboom Online Store
  • Amazon
  • Alibaba
  • AliExpress
  • Mercado

TECHNOLOGY SUPPORT

  • Email: support@yahboom.com

BE A DISTRIBUTOR

  • Email: sales@yahboom.com
QR Code
QR Code

Copyright © 2017-2026 Yahboom. All Rights Reserved.

EN/中文