Team: Sanghera Davina | Laroussi Rihab | Nwafor Solomon Chibuzo
Course: IFRosLab - ELTE University
Robot: Silvanus (AgileX Scout 2.0 + xArm6)
This project develops an autonomous fruit recycling system that detects, classifies, and sorts recyclable materials using the Silvanus robot platform.
The current implementation focuses on a finite state machine (FSM) in recycle_node_simple_multi.py that coordinates fruit detection and robotic manipulation.
This repository contains a submodule (yolo-seg-fruit) for fruit detection. To clone with all submodules:
git clone --recursive https://github.com/IFRoS-ELTE/autonomous_recycling_robot_25.git
cd autonomous_recycling_robot_25If you've already cloned the repository without submodules, initialize them with:
git submodule update --init --recursiveThe repository is organized into several main directories:
autonomous_recycling_robot_25/
βββ demos/ # Demonstration videos and GIFs
β βββ IFRoS_Lab2.gif # Main demonstration video (GIF)
β βββ IFRoS_Lab2.mp4 # Main demonstration video (MP4)
β
βββ docker_setup/ # Docker configuration files
β βββ compose.yaml # Docker Compose configuration
β βββ melodic.Dockerfile # ROS Melodic Docker image
β βββ docker_guide.md # Docker setup instructions
β βββ build_melodic.sh # Build script for Docker image
β βββ run_melodic.sh # Run script for Docker container
β βββ zshrc # Shell configuration
β
βββ moveit_config/ # MoveIt motion planning configuration
β βββ dh_robotics_ag95_model/ # DH Robotics AG95 gripper model
β β βββ launch/ # Gripper visualization launch files
β β βββ urdf/ # Gripper URDF/Xacro files
β β βββ meshes/ # Gripper 3D mesh files
β β
β βββ scout_xarm/ # Scout + xArm6 combined configuration
β βββ scout_xarm_base/ # Base robot configuration
β βββ scout_xarm_description/ # Robot description (URDF/Xacro)
β β βββ scout_urdf/ # Scout mobile base URDF
β β βββ xarm6_urdf/ # xArm6 manipulator URDF
β β βββ xarm6_meshes/ # xArm6 3D mesh files
β β
β βββ scout_xarm_moveit_config/ # MoveIt planning configuration
β βββ config/ # Planning parameters (OMPL, CHOMP, etc.)
β βββ launch/ # MoveIt launch files
β
βββ recycle/ # Main recycling robot package
β βββ scripts/ # Python scripts
β β βββ recycle_node_simple_multi.py # Main FSM controller
β β βββ grasp_planner_multi.py # Grasp planning and execution
β β βββ aruco_goal_publisher.py # ArUco marker detection
β β βββ region_filter_bridge.py # Camera-to-odom transform bridge
β β βββ restamp_pointcloud.py # LiDAR point cloud timestamp fix
β β βββ odom_tf_republisher.py # TF timestamp republisher
β β
β βββ launch/ # ROS launch files
β β βββ bringup_minimal_1.launch # Main system bringup
β β
β βββ config/ # Configuration files
β β βββ controllers.yaml # Robot controller configuration
β β βββ costmap_*.yaml # Navigation costmap parameters
β β βββ *_planner_params.yaml # Local planner parameters
β β
β βββ rviz/ # RViz visualization configurations
β β βββ scout_xarm_bringup.rviz # Main RViz config
β β
β βββ urdf/ # Robot description files
β β βββ scout_v2.xacro # Scout mobile base
β β βββ scout_xarm6.xacro # Combined Scout + xArm6
β β
β βββ src/ # C++ source files
β β βββ scout_base_node.cpp # Scout base control node
β β βββ scout_messenger.cpp # Scout communication interface
β β
β βββ include/ # C++ header files
β β βββ recycle/
β β βββ scout_messenger.hpp
β β
β βββ GRIPPER_SYSTEM.md # Gripper system documentation
β βββ TESTING_GUIDE.md # Testing instructions
β βββ CMakeLists.txt # CMake build configuration
β βββ package.xml # ROS package manifest
β
βββ scout_ros/ # Scout mobile base ROS packages
β βββ scout_base/ # Base control package
β β βββ launch/ # Base launch files
β β βββ src/ # Base control source code
β β
β βββ scout_bringup/ # System bringup package
β β βββ launch/ # Bringup launch files
β β βββ scripts/ # Setup scripts
β β
β βββ scout_description/ # Robot description package
β β βββ urdf/ # URDF/Xacro files
β β βββ launch/ # Display launch files
β β βββ rviz/ # RViz configurations
β β
β βββ scout_msgs/ # Custom message definitions
β βββ msg/ # Message files
β
βββ yolo-seg-fruit/ # Fruit detection submodule
β βββ catkin_ws/ # ROS workspace
β β βββ src/
β β βββ integration/
β β βββ scripts/
β β βββ detect_fruit_depth_improved.py # Main detection script
β β
β βββ docker/ # Docker setup for detection
β β βββ compose.yaml
β β βββ melodic.Dockerfile
β β
β βββ scripts/ # Utility scripts
β β βββ build.sh # Build Docker image
β β βββ start.sh # Start container
β β βββ detect.sh # Run detection
β β βββ stop.sh # Stop container
β β
β βββ README.md # Detection system documentation
β βββ SETUP_INSTRUCTIONS.md # Setup guide
β βββ REQUIREMENTS.txt # Python dependencies
β
βββ README.md # This file
Key Files:
recycle/scripts/recycle_node_simple_multi.py- Main FSM controllerrecycle/launch/bringup_minimal_1.launch- System bringuprecycle/GRIPPER_SYSTEM.md- Gripper documentationyolo-seg-fruit/- Fruit detection submodulerecycle/urdf/scout_xarm6.xacro- Combined robot modelrecycle/rviz/scout_xarm_bringup.rviz- RViz visualization
Take a look at our project slideshow outlining the complete waste recycling system:
https://docs.google.com/presentation/d/1JIryyOf5k8tIjwnNDefBi-1vyTzC3IGpH2LB7fFdm4U/edit?usp=sharing
Below are the video demonstrations of the system:
- Fruit / waste detection using RGB-D data (
detect_fruit_depth_improved.py) - Classification of detected items (e.g fruit classes)
- Robotic arm manipulation to pick and place items
- ArUco marker-based bin identification
- FSM-based task execution inside
recycle_node_simple_multi.py
- Mobile Base: AgileX Scout 2.0 (4WD differential drive)
- Manipulator: xArm6 robotic arm with DH Robotics AG95 gripper
- See Gripper System Documentation for detailed gripper control, force parameters, and grasping workflow
- Sensors:
- Intel RealSense D435 RGB-D camera (mounted on end-effector)
- Compute: Onboard computer running ROS Melodic
- ROS Framework: ROS Melodic
- Development Environment: Docker containers for distributed development
- Perception: Depth-aware fruit/waste detection (
detect_fruit_depth_improved.py) - Manipulation: MoveIt-based arm motion planning (called from
recycle_node_simple_multi.py) - Control Logic: Custom FSM in
recycle_node_simple_multi.pycoordinating perception and manipulation
-
Fruit detection (
detect_fruit_depth_improved.py)- Subscribes to RGB (
/camera/color/image_raw), depth (/camera/aligned_depth_to_color/image_raw), and camera info. - Runs YOLO models to detect fruits (carrot, orange, apple) in the RGB image.
- For each detection:
- Refines the object shape using HSV color segmentation.
- Computes an oriented bounding box and its yaw angle (object rotation).
- Uses the depth image and camera intrinsics to recover a 3D point in the camera frame.
- Transforms this point into the odom frame and keeps only detections inside a fixed workspace region in front of the robot.
- Publishes a
PoseStamped(position + orientation from the yaw angle) for each valid detection on:/carrot_pose_camera,/orange_pose_camera,/apple_pose_camera(plus corresponding visualization markers).
- Subscribes to RGB (
-
Target selection and 3D pose handling (FSM)
RecycleNodesubscribes to/carrot_pose_camera,/orange_pose_camera,/apple_pose_camera,/ball_pose_camera.- While scanning for ArUco markers, object detections are ignored to avoid interference.
- After the scan finishes and markers are stored, object detection is enabled and stabilized for 2 seconds.
- The FSM then selects the next object to process with priority:
- orange β carrot β apple β ball.
- The selected
PoseStamped(camera frame) and its type are stored as the current target.
-
Grasp planning and execution with xArm6
- In
STATE_PICK_OBJECT, the current object pose in camera frame is adjusted by fixed calibration offsets (xβ0.04 m, y+0.03 m) and transformed into the MoveIt base frame (dummy_base_link). - For carrots, the end-effector (joint 6) is rotated to align the gripper with the object's orientation; round objects (orange, apple, ball) skip this step.
GraspPlanner.pick_object():- Builds a preβgrasp pose above the object (same x,y, higher z) and moves there via a smooth Cartesian path.
- Moves straight down by a small offset to the final grasp pose.
- Closes the gripper to grasp the object and then returns the arm to the stored start joint configuration.
- A gripper state check detects if the gripper closed empty; in that case, the FSM fetches a fresh pose and retries the pick up to a configured maximum.
- Detailed Documentation: See Gripper System Documentation for complete workflow, force parameters, state machine, and retry logic.
- In
-
Bin selection using ArUco markers
- During
STATE_RUN_SCAN, the arm performs a 4βstep sweep (leftβcenterβrightβcenter), while an ArUco node (aruco_goal_publisher_v2.py) detects markers and publishes their poses (/aruco_marker_pose) and TF frames (aruco_marker_<id>). RecycleNodematches each detected pose to the corresponding TF frame to determine its ID and stores markers:- ID 0 β
marker_0_pose(ball bin) - ID 1 β
marker_1_pose(carrot bin) - ID 2 β
marker_2_pose(orange bin) - ID 3 β
marker_3_pose(apple bin)
- ID 0 β
STATE_WAIT_SCAN_RESULTSparses the scan results and enables object detection once the scan is complete.
- During
-
Move to dropβoff pose and release
- In
STATE_DROP_OBJECT, the FSM chooses the bin based on the object type:- ball β marker ID 0, carrot β ID 1, orange β ID 2, apple β ID 3.
- The selected marker pose is transformed into the base frame and passed to
GraspPlanner.drop_object():- The planner computes a drop pose for the endβeffector about 25 cm above the marker (compensating for the gripper tip offset).
- The arm follows a Cartesian path to this pose.
- The gripper opens to release the object into the bin.
- In
-
Return to home pose and wait for next item
- After dropping,
STATE_RETURN_STARTcommands the arm back to the initial joint configuration stored at startup. - All stored object poses and the current object type are cleared.
- The FSM returns to
STATE_WAIT_OBJECTand waits for the next valid fruit detection fromdetect_fruit_depth_improved.py, repeating the cycle.
- After dropping,
The following behavior tree diagram illustrates the complete FSM implementation in recycle_node_simple_multi.py:
graph TD
Start([START]) --> Root[Root: Recycle Sequence]
Root --> ScanSeq[Sequence: ArUco Scan]
ScanSeq --> InitPos[Action: Check Initial Position]
InitPos --> SetSlowSpeed[Action: Set Slow Speed 5%]
SetSlowSpeed --> ScanSteps[Sequence: 4-Step Scan]
ScanSteps --> Step1[Step 1: Move LEFT +180Β°]
Step1 --> Wait1[Wait 2s for ArUco]
Wait1 --> Step2[Step 2: Return CENTER 0Β°]
Step2 --> Step3[Step 3: Move RIGHT -180Β°]
Step3 --> Wait2[Wait 2s for ArUco]
Wait2 --> Step4[Step 4: Return CENTER 0Β°]
Step4 --> RestoreSpeed[Action: Restore Speed]
RestoreSpeed --> PublishResults[Action: Publish Scan Results]
Root --> WaitScan[Wait: Scan Results]
WaitScan --> ProcessMarkers[Action: Store Markers 0,1,2,3]
ProcessMarkers --> EnableDetection[Action: Enable Object Detection]
EnableDetection --> WaitStable[Wait: 2s Stabilization]
WaitStable --> ObjectSelector[Selector: Check Object Priority]
ObjectSelector --> CheckOrange{Orange<br/>detected?}
CheckOrange -->|Yes| SelectOrange[Set: orange]
CheckOrange -->|No| CheckCarrot{Carrot<br/>detected?}
CheckCarrot -->|Yes| SelectCarrot[Set: carrot]
CheckCarrot -->|No| CheckApple{Apple<br/>detected?}
CheckApple -->|Yes| SelectApple[Set: apple]
CheckApple -->|No| CheckBall{Ball<br/>detected?}
CheckBall -->|Yes| SelectBall[Set: ball]
CheckBall -->|No| WaitMore[Wait: Continue waiting]
WaitMore --> ObjectSelector
SelectOrange --> PickSeq[Sequence: Pick Object]
SelectCarrot --> PickSeq
SelectApple --> PickSeq
SelectBall --> PickSeq
PickSeq --> ApplyCalib[Action: Apply Calibration<br/>x-0.04m, y+0.03m]
ApplyCalib --> TransformBase[Action: Transform to Base Frame]
TransformBase --> RotateGripper{Object type?}
RotateGripper -->|Carrot| AlignGripper[Action: Rotate Gripper<br/>to Match Orientation]
RotateGripper -->|Round| SkipRotate[Skip Rotation]
AlignGripper --> ExecutePick[Action: Execute Pick<br/>MoveIt Planning]
SkipRotate --> ExecutePick
ExecutePick --> CheckGripper{Gripper<br/>Empty?}
CheckGripper -->|Yes| CheckRetries{Retries<br/>< 5?}
CheckRetries -->|Yes| GetFreshPose[Action: Get Fresh Pose]
GetFreshPose --> IncrementRetry[Action: Increment Counter]
IncrementRetry --> PickSeq
CheckRetries -->|No| PickFailed[Failure: Max Retries]
CheckGripper -->|No| PickSuccess[Success: Object Grasped]
PickSuccess --> DropSeq[Sequence: Drop Object]
PickFailed --> FailedState[State: FAILED]
DropSeq --> SelectMarker{Object Type?}
SelectMarker -->|Ball| Marker0[Select: Marker ID 0]
SelectMarker -->|Carrot| Marker1[Select: Marker ID 1]
SelectMarker -->|Orange| Marker2[Select: Marker ID 2]
SelectMarker -->|Apple| Marker3[Select: Marker ID 3]
Marker0 --> CheckMarker0{Marker 0<br/>Available?}
Marker1 --> CheckMarker1{Marker 1<br/>Available?}
Marker2 --> CheckMarker2{Marker 2<br/>Available?}
Marker3 --> CheckMarker3{Marker 3<br/>Available?}
CheckMarker0 -->|No| DropFailed[Failure: Marker Missing]
CheckMarker1 -->|No| DropFailed
CheckMarker2 -->|No| DropFailed
CheckMarker3 -->|No| DropFailed
CheckMarker0 -->|Yes| TransformDrop[Action: Transform Marker<br/>to Base Frame]
CheckMarker1 -->|Yes| TransformDrop
CheckMarker2 -->|Yes| TransformDrop
CheckMarker3 -->|Yes| TransformDrop
TransformDrop --> ExecuteDrop[Action: Execute Drop<br/>25cm Above Marker]
ExecuteDrop --> OpenGripper[Action: Open Gripper]
OpenGripper --> DropSuccess[Success: Object Dropped]
DropSuccess --> ReturnSeq[Sequence: Return to Start]
DropFailed --> FailedState
ReturnSeq --> ReturnHome[Action: Return to Initial<br/>Joint Configuration]
ReturnHome --> ClearPoses[Action: Clear All Object Poses]
ClearPoses --> LoopBack[Loop: Back to Wait Object]
LoopBack --> ObjectSelector
FailedState --> End([END])
style Start fill:#90EE90,stroke:#2d5016,stroke-width:2px,color:#000
style End fill:#FFB6C1,stroke:#8b3a5c,stroke-width:2px,color:#000
style FailedState fill:#FF6B6B,stroke:#8b0000,stroke-width:2px,color:#fff
style PickSuccess fill:#90EE90,stroke:#2d5016,stroke-width:2px,color:#000
style DropSuccess fill:#90EE90,stroke:#2d5016,stroke-width:2px,color:#000
style Root fill:#E6F3FF,stroke:#0066CC,stroke-width:2px,color:#000
style ScanSeq fill:#F0E6FF,stroke:#6B46C1,stroke-width:2px,color:#000
style PickSeq fill:#FFF9E6,stroke:#CC9900,stroke-width:2px,color:#000
style DropSeq fill:#FFE6E6,stroke:#CC3333,stroke-width:2px,color:#000
style ReturnSeq fill:#E6FFF0,stroke:#009966,stroke-width:2px,color:#000
style ObjectSelector fill:#E6F7FF,stroke:#006699,stroke-width:2px,color:#000
Key Features:
- Sequences: Actions executed in order (Scan, Pick, Drop, Return)
- Selectors: Priority-based object selection (orange β carrot β apple β ball)
- Conditions: Decision points for gripper state, retries, and marker availability
- Retry Logic: Automatic retry up to 5 times if gripper closes empty
- Continuous Loop: Returns to object detection after each successful cycle
The following demonstrations showcase different aspects of the recycling pipeline:
The following video demonstrates the complete recycling pipeline in action, showing the robot successfully detecting, picking, and sorting carrot, apple, and orange into their respective bins:
The demonstration showcases the integrated system: YOLO-based fruit detection with color segmentation for precise orientation, MoveIt-based grasping with orientation-aware manipulation for elongated objects (carrots), and ArUco marker-based bin localization for accurate sorting.
Future work may re-enable mobile navigation around the environment.
| Team Member | Primary Focus | Current Tasks |
|---|---|---|
| Davina Sanghera | Perception & Detection | Fruit detection, depth integration |
| Nwafor Solomon Chibuzo | Bringup + Manipulation & Sorting | Bringup launch and setup,MoveIt configuration, grasping & execution with xArm6 pipeline, FSM integration |
| Rihab Laroussi | Bin Localization & Drop Pipeline | ArUco-based bin scan and ID mapping, object-to-bin assignment, and drop motion. |



