yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
MicroROS V2 Robot Car📖
01.Introduction
  • 0.Unboxing Video
  • 1. Readme Learning Route
  • 2. Must Read Before Use
  • 3. Introduction to MicroROS V2 Control Board
  • 4. Introduction to LiDAR
  • 5. Introduction to ESP32 Camera Module
  • 6. FAQ
  • 7. About Charging
02.Assembly course
  • Assembly Video
03.Preparation
  • 1. Quickly Start
  • 2. Write Firmware
  • 3. Install VM and Start System
  • 4. Configure VM and Connect Robot
  • 5. Connect to MicroROS Agent
04.Quick start control course
  • 1. VM Keyboard Remote Control
  • 2. VM Handle Remote Control
  • 3. USB Handle Control
05.Robot control course
  • 1. Robot Information Release
  • 2. Robot Keyboard Control
  • 3. Robot Handle Control
  • 4. Robot State Estimation
  • 5. Linear Speed Calibration
  • 6. Angular Velocity Calibration
  • 7. Robot URDF Model
06.Lidar course
  • 1. Precautions for LiDAR Mapping and Navigation
  • 2. Lidar Avoid
  • 3. Lidar Following
  • 4. Lidar Guard
  • 5. Lidar Patrol
  • 6. Gmapping Mapping
  • 7. Cartographer Mapping
  • 8. Cartographer Quickly Repositioning Navigation
  • 9. Slam Toolx Mapping
  • 10. Navigation2 Single Point Navigation
  • 11. Navigation2 Multi Point Navigation
  • 12. Ros Robot App Mapping And Navigation
07.Road network planning
  • 1. Introduction to Road Network Planning and Quick Start
  • 2. Build a Pose Map
  • 3. Road Network Annotation
  • 4. Planning and Navigation Between Road Networks
  • 5. Road Network Navigation in Any Pose
  • 6. Path Replanning
08.Vision basic course
OpenCV Image Processing
  • 1. Introduction to OpenCV Source CV
  • 2. Open Source CV Geometric Transformation
  • 3. Open Source CV Image Processing and Drawing Text Line Segments
  • 4. Open Source CV Image Beautification
09.Visual General Course (For Standard&Superior version)
4. Yolov11&26 Visual Recognition
  • 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
1. ESP32 Visual Recognition Module Configuration
  • 1. Serial Port Configuration WiFi Instructions
  • 2. Use ESP32 Camera Module in Robot Car
  • 3. Servo PTZ Calibration
2. OpenCV Visual Recognition
  • 1. Human Pose Estimation
  • 2. Object Detection
  • 3. AR Vision
  • 4. QR Code
  • 5. Color Recognition
  • 6. Facial Recognition
  • 7. AprilTag Machine Code Recognition
3. Mediapipe Visual Recognition
  • 1. Hand Detection
  • 2. Pose Detection
  • 3. Holistic Detection
  • 4. Facial Detection
  • 5. Facial Recognition
  • 6. Facial Special Effects
  • 7. 3D Object Recognition
  • 8. Brush
  • 9. Gesture Recognition
  • 10. Palm Target Localization
  • 11. Fingertip Trajectory Recognition
10.Visual Advanced Course (Just for Standard version)
1. Visual Control
  • 1. QR Code Recognition Control Action
  • 2. AprilTag Control Car Action Group
  • 3. Gesture Control Car Action Group
  • 4. Visual Line Tracking
2. Visual Tracking
  • 1. Color Tracking
  • 2. Facial Tracking
  • 3. QR Code Tracking
  • 4. Object Recognition And Tracking
  • 5. Mediapipe Palm Tracking
  • 6. Mediapipe Gesture Tracking
  • 7. Mediapipe Human Pose Tracking
  • 8. KCF Object Tracking
  • 9. AprilTag Machine Code Tracking
3. Visual Following
  • 1. Color Follow
  • 2. Face Follow
  • 3. QR Code Follow
  • 4. Mediapipe Palm Follow
  • 5. Mediapipe Gesture Follow
  • 6. Mediapipe Human Posture Follow
  • 7. AprilTag Machine Code Follow
  • 8. Object Follow
11.Visual Advanced Course (Just for Superior version)
1. Visual Control
  • 1. QR Code Recognition Control Action
  • 2. AprilTag Control Car Action Group
  • 3. Gesture Control Car Action Group
  • 4. Visual Line Tracking
2. Visual Tracking
  • 1. Color Tracking
  • 2. Facial Tracking
  • 3. QR Code Tracking
  • 4. YOLO Object Recognition and Tracking
  • 5. Mediapipe Palm Tracking
  • 6. Mediapipe Gesture Tracking
  • 7. Mediapipe Human Pose Tracking
  • 8. KCF Object Tracking
  • 9. AprilTag Machine Code Tracking
3. Visual Follow
  • 1. Color Follow
  • 2. Face Follow
  • 3. QR Code Follow
  • 4. Mediapipe Palm Follow
  • 5. Mediapipe Gesture Follow
  • 6. Mediapipe Human Pose Follow
  • 7. AprilTag Machine Code Follow
  • 8. Object Follow
12.AI Large Model Basic and Applications Course
1. AI Large Model Preparation
  • 1.AI Large Model Preparation
2. AI Large Model Basic Course
  • 1. AI Large Model Types and Principles
  • 2. RAG Retrieval Augmented and Training Samples
  • 3. Embodied Intelligent System Architecture
  • 4. Embodied Intelligence Core Source Code
3. AI Large Model Development
  • 1. Register Model Service Account
  • 2. Configure API KEY
  • 3. Dify Introduction
  • 4. Dify Basic Features
  • 5. RAG Knowledge Base Deployment
  • 6. AI Application Development Chatbot
  • 7. RAG Knowledge Base and Chatbot
  • 8. AI Agent Workflow
4. AI Large Model (Text Version)
  • 1. Semantic Understanding and Instruction Following
  • 2. Multimodal Visual Understanding
  • 3. Multimodal Visual Understanding + Basic Control
  • 4. Multimodal Visual Understanding + Object Following
  • 5. Multimodal Visual Understanding + Visual Line Following
  • 6. Multimodal Visual Understanding + SLAM Navigation
  • 7. Multimodal Visual Understanding + SLAM Navigation + Visual Line Following
  • 8. Multimodal Visual Understanding + SLAM Navigation + Object Following
  • 9. Intent Inference + Multimodal Visual Understanding + SLAM Navigation
13.Openclaw Basics Course
  • 1. Openclaw Introduction
  • 2. Openclaw Core Concepts
  • 3. Openclaw Api-Key Configuration
  • 4. Openclaw Switching Models
  • 5. Openclaw System Prompt
  • 6. Openclaw Common CLI Command
14. Openclaw Interaction Course
  • 1. Openclaw Webchat Interaction
  • 2. Openclaw Tui Interaction
  • 3. Openclaw Whatsapp Interaction
15.Openclaw Development Course
  • 1. Openclaw Robot Mcp Service Integration
  • 2. Robot CLI Command Tools
  • 3. Robot-Control Plugin Tools
  • 4. Openclaw Skills Development
  • 5. Openclaw Scheduled Tasks
  • 6. Openclaw Multi Agent System
  • 7. Openclaw Proactive Interaction
  • 8. Openclaw Vector Memory
16.Openclaw Embodied Intelligence Course
  • 1. OpenClaw Natural Interaction + Basic Control
  • 2. OpenClaw Natural Interaction + Slam Navigation
  • 3. OpenClaw Natural Interaction + Multimodal Visual Understand
  • 4. OpenClaw Natural Interaction + Multimodal Visual Understand + Slam Navigation + Visual Follow
  • 5. OpenClaw Natural Interaction + Multimodal Visual Understand + Slam Navigation + Visual Track
17.Multi-machine Course
  • 1. Multi Machine Handle Control
  • 2. Multi Machine Keyboard Control
  • 3. Multi Machine Navigation
18.MicroROS-V2 Control Board Development Environment
  • 1. Set Up the ESP32-IDF Development Environment
  • 2. ESP32-IDF Configuration Tool
  • 3. ESP Flash Tool Flashing Firmware
  • 4. Install ESP32 Microros Components
  • 5. Install And Start Microros Agent
19.MicroROS-V2 Control Board ESP32 Basic Course
  • 1. Led Light Up
  • 2. Button Function
  • 3. Drive Buzzer
  • 4. Oled
  • 5. Usb Host Function
  • 6. Serial Communication
  • 7. Battery Voltage Detection
  • 8. Drive Pwm Servo
  • 9. Drive Motors
  • 10. Read Motor Encoder Data
  • 11. Pid Motor Speed Control
  • 12. Robot Kinematics Analysis Theory
  • 13. Read Imu Data
  • 14. Read Lidar Data
  • 15. Flash Data Storage
  • 16. Custom Partition Table
  • 17. Bluetooth Communication
  • 18. Wifi Connection
  • 19. ESP32 Visual Recognition Module
20.MicroROS-V2 Control Board Micro_ros Basic Course
  • 1. Publish Topics
  • 2. Subscribe To Topics
  • 3. Multi Topic Subscription And Publishing
  • 4. Subscribe To Buzzer Topic
  • 5. Subscribe To Pwm Servo Topic
  • 6. Subscribe To Speed Control Topic
  • 7. Publish Odometry Topic
  • 8. Publish Imu Data Topic
  • 9. Publish Lidar Data Topic
21. Linux System Course
  • 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.Static IP and hotspot mode
  • 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
22.Docker Course
  • 1.Docker overview and docker installation
  • 2.Common commands for docker image containers
  • 3.Docker images deeply understand and publish images
23.ROS 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
Download
  • Mapping_APP(iOS_Android)
  • Code
  • Software_Tools
  • VM_System_File
  • 3D_Model_File
  • ESP32_Firmware
  • Dify LLM Base
  • Instruction_Manual
  • VMware
Download ZIP

Welcome to MicroROS V2 Robot Car repository

2. OpenClaw Natural Interaction + Slam Navigation

OpenClaw Natural Interaction + SLAM Navigation

OpenClaw Natural Interaction + SLAM Navigation1. Course ContentLearning Objectives2. Preparation3. Record the Map Mapping File4. Case Demonstration: OpenClaw with Regular Navigation5. Case Demonstration: OpenClaw with Road Network Planning Navigation6. Source Code Analysis6.1 Navigation — Navigation Function6.2 PreciseAdjustPosition — Precise Position Adjustment7. Parameter Debugging

1. Course Content

  • Use OpenClaw to control the robot to perform semantic navigation in the map combined with map mapping, so that the robot can go to a specified location to perform tasks

Learning Objectives

Basic

  • Learn to use the RecordMapLocation tool to record the map mapping file and establish the correspondence between place names and coordinates
  • Learn to use OpenClaw to issue a task to the robot to go to the target point

Advanced

  • Understand how to switch between Road Network Navigation and regular navigation (Nav2)

  • Master the PID control principle and parameter tuning methods of PreciseAdjustPosition for precise position adjustment

     

2. Preparation

[!IMPORTANT]

  • OpenClaw combined with the regular navigation feature requires building a grid map and a pose map first. Refer to the tutorial on LiDAR Gameplay / slam_toolx Mapping.
  • OpenClaw combined with the road network navigation feature requires building a grid map, a pose map, and annotating the road network file first. Refer to the tutorial on Road Network Planning Navigation — Building Pose Maps, Road Network Annotation.

 

xxxxxxxxxx
ros2 launch microros_v2_bringup openclaw_service.launch.py use_nav:=true

image-20260724163522005

  • Use the 2D Pose Estimate tool to initialize the robot's position in the map

image-20260724163801960

 

 

3. Record the Map Mapping File

[!TIP]

  • You can use the robot_control CLI tool, or directly ask OpenClaw to record positions and symbols during interaction with OpenClaw. The following demonstrates recording a map mapping file using the CLI tool as an example
ToolParametersFunction
RecordMapLocation--name, --symbolSave the robot's current position in the map to the map mapping file

 

  • You can use the robot_control CLI tool to record the position of the robot chassis base_link in the map map
xxxxxxxxxx
robot_control call-tool RecordMapLocation --name 发货区 --symbol B
  • --name specifies the name of the recorded point
  • --symbol specifies the symbol of the recorded point, so that OpenClaw can identify and distinguish it

image-20260724164246272

  • If the recording succeeds, the content shown in the figure above will be prompted. If a symbol with the same name already exists in the map mapping file, the previous symbol content will be automatically overwritten
  • map_mapping.yaml, the file path is:
xxxxxxxxxx
$HOME/microros_ws/src/microros_v2_bringup/config/map_mapping.yaml

image-20260724164404846

 

4. Case Demonstration: OpenClaw with Regular Navigation

[!TIP]

  • You can write the commands according to your own needs. The following is a case demonstration. You can choose any interaction method; here we demonstrate with Web Chat on the web page as an example
  • Navigation function tools
ToolParametersFunction
GetMapMappingNoneGet the mapping relationship between all place names and their corresponding identifiers
Navigation--locationNavigate to the target position via the target point identifier

image-20260506203450670

image-20260724164743550

 

5. Case Demonstration: OpenClaw with Road Network Planning Navigation

xxxxxxxxxx
ros2 launch microros_v2_bringup openclaw_service.launch.py use_nav:=true nav_mode:=roadnet

image-20260724165032736

  • Here we demonstrate navigating to the Shipping Area as an example

image-20260506203450670

  • The robot will navigate along the path in the road network to the target point and adjust its pose

image-20260724165334516

image-20260724170154245

 

6. Source Code Analysis

  • Source Code Path
xxxxxxxxxx
$HOME/microros_ws/src/multi_brains_pre/multi_brains_pre/action_controller.py

6.1 Navigation — Navigation Function

Navigation() is the navigation entry function in action_controller.py. It determines the navigation mode based on the startup parameters use_nav and nav_mode. The overall flow is divided into three steps: read the map mapping file → construct the target pose → dispatch to the specific navigation implementation.

  1. Read the map mapping file: Opens map_mapping.yaml, iterates over all symbol entries, matches the location parameter (supports symbol matching or name matching), and finds the target coordinate data

  2. Construct the target pose: Fills the matched position (x, y, z) and orientation (quaternion) into the PoseStamped message, with the coordinate frame set to map

  3. Navigation mode dispatch:

    • nav_mode == 'nav2': calls _nav2_navigation() to perform regular navigation through Nav2's NavigateToPose Action service
    • nav_mode == 'roadnet': calls _roadnet_navigation() to perform road network navigation through the RoadNetToGoal Action service
  4. Precise position adjustment: Regardless of the navigation mode, after reaching the target, PreciseAdjustPosition() is called to fine-tune the pose and improve the final docking accuracy

_nav2_navigation(): Constructs NavigateToPose.Goal() and sends it through nav_to_pose_client, waits asynchronously for the execution result, and calls the precise position adjustment upon completion

_roadnet_navigation(): Constructs RoadNetToGoal.Goal(), sets use_poses=True to use coordinates directly, sends it through roadnet_nav_client, and determines the navigation result based on result.success

​x
    def Navigation(self, location: str) -> list[bool,str]:
        '''Navigate to target location. Mode depends on use_nav and nav_mode parameters.'''
        if not self.use_nav:
            return [False, "Navigation is not enabled ,Please add the startup parameter `use_nav:=true` when starting the OpenClaw service.This step should only be performed by the user."]
​
        location = location.strip("'\"")
        try:
            with open(self.map_mapping_file, 'r', encoding='utf-8') as file:
                map_data = yaml.safe_load(file) or {}
​
            target_data = None
            for symbol, data in map_data.items():
                if symbol == location or (isinstance(data, dict) and data.get('name') == location):
                    target_data = data
                    break
            if target_data is None:
                error_info = f"The map mapping file has no coordinate record for '{location}'."
                self.get_logger().error(error_info)
                return [False,error_info]
        except Exception as e:
            self.get_logger().error(str(e))
            return [False,str(e)]
​
        goal_msg = PoseStamped()
        goal_msg.header.frame_id = "map"
        goal_msg.header.stamp = self.get_clock().now().to_msg()
        position = target_data.get('position', {})
        goal_msg.pose.position.x = float(position.get('x', 0.0))
        goal_msg.pose.position.y = float(position.get('y', 0.0))
        goal_msg.pose.position.z = float(position.get('z', 0.0))
        orientation = target_data.get('orientation', {})
        goal_msg.pose.orientation.x = float(orientation.get('x', 0.0))
        goal_msg.pose.orientation.y = float(orientation.get('y', 0.0))
        goal_msg.pose.orientation.z = float(orientation.get('z', 0.0))
        goal_msg.pose.orientation.w = float(orientation.get('w', 1.0))
​
        if self.nav_mode == 'nav2':
            return self._nav2_navigation(goal_msg, position, orientation, location)
        elif self.nav_mode == 'roadnet':
            return self._roadnet_navigation(goal_msg, location)
        else:
            error=f"Unknown nav_mode: '{self.nav_mode}'. Please check the nav_mode startup parameters."
            self.get_logger().error(error)
            return [False, error]
​
    def _nav2_navigation(self, goal_msg: PoseStamped, position: dict, orientation: dict, location: str) -> list[bool,str]:
        '''Navigate using nav2 NavigateToPose action'''
        if not self.nav_to_pose_client.wait_for_server(timeout_sec=10.0):
            return [False, "Nav2 action server not available,Users should check if the navigation nodes are functioning correctly"]
        goal = NavigateToPose.Goal()
        goal.pose.header.frame_id = "map"
        goal.pose.header.stamp = self.get_clock().now().to_msg()
        goal.pose.pose.position.x = float(position.get('x', 0.0))
        goal.pose.pose.position.y = float(position.get('y', 0.0))
        goal.pose.pose.position.z = float(position.get('z', 0.0))
        goal.pose.pose.orientation.x = float(orientation.get('x', 0.0))
        goal.pose.pose.orientation.y = float(orientation.get('y', 0.0))
        goal.pose.pose.orientation.z = float(orientation.get('z', 0.0))
        goal.pose.pose.orientation.w = float(orientation.get('w', 1.0))
​
        send_goal_future = self.nav_to_pose_client.send_goal_async(goal)
        while rclpy.ok() and not send_goal_future.done():
            time.sleep(0.5)
        
        if not send_goal_future.done():
            return [False, "Failed to send navigation goal"]
        
        goal_handle = send_goal_future.result()
        if not goal_handle.accepted:
            return [False, "Navigation goal rejected"]
        
        result_future = goal_handle.get_result_async()
        
        while rclpy.ok() and not result_future.done():
            time.sleep(0.5)
        
        if result_future.done():
            result = result_future.result().result
            self.PreciseAdjustPosition(goal_msg.pose)
            return [True, "Navigation succeeded"]
        else:
            return [False, "Navigation failed"]
​
    def _roadnet_navigation(self, goal_msg: PoseStamped, location: str) -> list[bool,str]:
        '''Navigate using roadnet action'''
        if not self.roadnet_nav_client.wait_for_server(timeout_sec=10.0):
            return [False, "RoadNet action server not available,Users should check if the navigation nodes are functioning correctly."]
​
        goal = RoadNetToGoal.Goal()
        goal.use_start = False
        goal.use_poses = True
        goal.goal = goal_msg
​
        self.get_logger().info(f"Sending roadnet navigation goal to '{location}'")
​
        send_goal_future = self.roadnet_nav_client.send_goal_async(goal)
        while rclpy.ok() and not send_goal_future.done():
            time.sleep(0.5)
​
        if not send_goal_future.done():
            return [False, "Failed to send roadnet navigation goal"]
​
        goal_handle = send_goal_future.result()
        if not goal_handle.accepted:
            return [False, "Roadnet navigation goal rejected"]
​
        result_future = goal_handle.get_result_async()
​
        while rclpy.ok() and not result_future.done():
            time.sleep(0.5)
​
        if result_future.done():
            result = result_future.result().result
            if result.success:
                self.PreciseAdjustPosition(goal_msg.pose)
                return [True, f"Roadnet navigation to '{location}' succeeded"]
            else:
                self.get_logger().warn(
                    Fore.YELLOW + f"Roadnet navigation to '{location}' failed: "f"error_code={result.error_code}, {result.error_msg}" + Fore.RESET)
                return [False, result.error_msg]
        else:
            return [False, "Roadnet navigation failed"]
​
    def PreciseAdjustPosition(self, target_pose: Pose) -> list[bool, str]:
        '''Request precise pose adjustment via PreciseAdjustPose action service'''
        if not self.precise_adjust_client.wait_for_server(timeout_sec=10.0):
            return [False, "PreciseAdjustPose action server not available"]
​
        goal = PreciseAdjustPose.Goal()
        goal.target_pose = target_pose
​
        send_goal_future = self.precise_adjust_client.send_goal_async(goal)
        while rclpy.ok() and not send_goal_future.done():
            time.sleep(0.5)
​
        if not send_goal_future.done():
            return [False, "Failed to send PreciseAdjustPose goal"]
​
        goal_handle = send_goal_future.result()
        result_future = goal_handle.get_result_async()
        while rclpy.ok() and not result_future.done():
            time.sleep(0.5)
​
        if not result_future.done():
            return [False, "PreciseAdjustPose result timeout"]
​
        result = result_future.result().result
        if result.success:
            return [True, result.message]
        return [False, result.message]

 

6.2 PreciseAdjustPosition — Precise Position Adjustment

_preciseAdjustActionCallback() is the PID-based precise pose adjustment implementation in base_driver.py. It runs as the callback function of the PreciseAdjustPose Action server. It adopts a three-stage state machine strategy and gradually converges to the target pose through visual/TF localization feedback

Three-stage state machine:

  1. AIM (aiming stage): Rotates in place to point the front of the vehicle toward the target point; when bearing_error < aim_tol (about 5.7°), it transitions to the DRIVE stage
  2. DRIVE (forward stage): Drives straight toward the target point while correcting the heading deviation with angular velocity; if bearing_error > realign_tol (about 20°) during driving, it returns to the AIM stage to re-aim
  3. FINAL_YAW (final heading stage): After reaching the target position, rotates in place to adjust to the target yaw angle; if the position deviation exceeds reenter_dist, it returns to the AIM stage and re-enters

Control logic:

  1. Error calculation: Obtains the current pose in the map coordinate frame through _getMapPose(), and calculates the position error pos_error, heading error yaw_error, and bearing error bearing_error
  2. P control: Uses pure proportional control. xy_kp maps the distance error to forward speed, and theta_kp maps the angle error to angular speed, both limited within the maximum/minimum speed ranges
  3. Completion check: Only when both the position error and the angle error are less than the tolerance and the condition is satisfied 5 consecutive times (required_success=5) is it considered successful, avoiding misjudgment from a single jitter
xxxxxxxxxx
    def _preciseAdjustActionCallback(self, goal_handle):
​
        goal = goal_handle.request
        target_x = goal.target_pose.position.x
        target_y = goal.target_pose.position.y
        target_yaw = self._getYawFromQuaternion(goal.target_pose.orientation)
​
        self.get_logger().info(
            f"PreciseAdjust goal: x={target_x:.3f}, y={target_y:.3f}, yaw={math.degrees(target_yaw):.1f}°")
​
        # Gains
        xy_kp = self.precise_adjust_xy_pid[0]        # distance -> forward speed
        theta_kp = self.precise_adjust_theta_pid[0]  # heading error -> angular speed
​
        consecutive_success = 0
        required_success = 5
​
        # Heading thresholds (rad): must face the target within aim_tol before driving;
        # while driving, fall back to AIM if the bearing drifts beyond realign_tol.
        aim_tol = 0.10           # ~5.7 deg
        realign_tol = 0.35       # ~20 deg
        # Leave FINAL_YAW back to AIM only after clearly drifting out of tolerance.
        reenter_dist = self.precise_adjust_pos_tolerance * 1.5
​
        phase = self._PHASE_AIM
        pos_error = 0.0
        yaw_error = 0.0
​
        for _ in range(self.precise_adjust_max_iteration):
            # Cancel check
            if goal_handle.is_cancel_requested:
                self._publishCmdVel(0.0, 0.0)
                goal_handle.canceled()
                result = PreciseAdjustPose.Result()
                result.success = False
                result.message = "Precise adjustment cancelled"
                return result
​
            pose = self._getMapPose()
            if pose is None:
                self.get_logger().error("Failed to get current pose from TF")
                self._publishCmdVel(0.0, 0.0)
                goal_handle.abort()
                result = PreciseAdjustPose.Result()
                result.success = False
                result.message = "Failed to get current pose from TF"
                return result
​
            current_x, current_y, current_yaw = pose
​
            # Position/orientation error in the map frame
            dx = target_x - current_x
            dy = target_y - current_y
            pos_error = math.hypot(dx, dy)
            yaw_error = self._normalizeAngle(target_yaw - current_yaw)
            bearing_error = self._normalizeAngle(math.atan2(dy, dx) - current_yaw)
​
            pos_reached = pos_error < self.precise_adjust_pos_tolerance
            angle_reached = abs(yaw_error) < self.precise_adjust_angle_tolerance
​
            # Publish feedback
            feedback = PreciseAdjustPose.Feedback()
            feedback.position_error = float(pos_error)
            feedback.angle_error = float(math.degrees(yaw_error))
            goal_handle.publish_feedback(feedback)
​
            if pos_reached and angle_reached:
                consecutive_success += 1
                if consecutive_success >= required_success:
                    self._publishCmdVel(0.0, 0.0)
                    msg = (f"Precise adjustment completed: pos_error={pos_error:.3f}m, "
                           f"angle_error={math.degrees(yaw_error):.1f}°")
                    self.get_logger().info(msg)
                    goal_handle.succeed()
                    result = PreciseAdjustPose.Result()
                    result.success = True
                    result.message = msg
                    return result
            else:
                consecutive_success = 0
​
            # ---- Phase transitions (with hysteresis) ----
            if pos_reached:
                phase = self._PHASE_FINAL_YAW
            elif phase == self._PHASE_FINAL_YAW:
                # Was aligning yaw but drifted off the point: re-aim and drive back in.
                if pos_error > reenter_dist:
                    phase = self._PHASE_AIM
            elif phase == self._PHASE_AIM:
                if abs(bearing_error) < aim_tol:
                    phase = self._PHASE_DRIVE
            elif phase == self._PHASE_DRIVE:
                if abs(bearing_error) > realign_tol:
                    phase = self._PHASE_AIM
​
            # ---- Per-phase velocity command ----
            if phase == self._PHASE_DRIVE:
                # Drive forward toward the target; heading correction keeps it on bearing.
                vx = xy_kp * pos_error * max(0.0, math.cos(bearing_error))
                vx = max(0.0, min(self.precise_adjust_max_linear_vel, vx))
                if vx > 1e-6 and vx < self.min_linear_speed:
                    vx = self.min_linear_speed
                omega = theta_kp * bearing_error
                omega = max(-self.precise_adjust_max_angular_vel,
                            min(self.precise_adjust_max_angular_vel, omega))
            else:
                # In-place rotation: AIM faces the target point, FINAL_YAW the target yaw.
                if phase == self._PHASE_FINAL_YAW:
                    heading_error = yaw_error
                    reached = angle_reached
                else:
                    heading_error = bearing_error
                    reached = abs(bearing_error) < aim_tol
                vx = 0.0
                omega = theta_kp * heading_error
                omega = max(-self.precise_adjust_max_angular_vel,
                            min(self.precise_adjust_max_angular_vel, omega))
                if reached:
                    omega = 0.0
                elif abs(omega) < self.min_angular_speed:
                    # Overcome the chassis static-friction dead-zone so it actually turns.
                    omega = self.min_angular_speed if omega > 0 else -self.min_angular_speed
​
            # Publish command (linear.y = 0 for differential drive)
            self._publishCmdVel(vx, omega)
            time.sleep(0.1)  # 10Hz
​
        # Max iterations reached
        self._publishCmdVel(0.0, 0.0)
        msg = (f"Precise adjustment reached max iterations ({self.precise_adjust_max_iteration}). "
               f"pos_error={pos_error:.3f}m, angle_error={math.degrees(yaw_error):.1f}°")
        self.get_logger().warn(msg)
        goal_handle.abort()
        result = PreciseAdjustPose.Result()
        result.success = False
        result.message = msg
        return result

 

7. Parameter Debugging

  • Parameter file path:
xxxxxxxxxx
$HOME/microros_ws/src/microros_v2_bringup/config/common.yaml

The following describes the parameters related to precise position adjustment in common.yaml:

ParameterDefaultDescription
precise_adjust_xy_pid[0.5, 0.0, 0.05]PID gains for the DRIVE stage [Kp, Ki, Kd]; Kp maps the distance error to forward speed
precise_adjust_theta_pid[1.0, 0.0, 0.3]PID gains for the turning stage [Kp, Ki, Kd]; Kp maps the heading error to angular speed
precise_adjust_max_linear_vel0.12Maximum linear speed for precise adjustment (m/s); slow down to improve convergence stability
precise_adjust_max_angular_vel0.5Maximum angular speed for precise adjustment (rad/s); ensures decisive turning
precise_adjust_pos_tolerance0.01Position tolerance for precise adjustment (m); loosen to accommodate TF/odometry noise
precise_adjust_angle_tolerance0.06Angle tolerance for precise adjustment (rad, about 3.4°)
precise_adjust_max_iteration500Maximum iteration count for precise adjustment (10Hz → about 50s); aborts on timeout

[!TIP]

  • If the robot still has obvious offset after reaching the target position, increase the Kp value of precise_adjust_xy_pid or loosen precise_adjust_pos_tolerance
  • If the robot's final heading is not accurate, increase the Kp value of precise_adjust_theta_pid or loosen precise_adjust_angle_tolerance
  • If the robot jitters/oscillates during adjustment, reduce the PID gains or decrease precise_adjust_max_linear_vel
  • If the precise adjustment takes too long, increase precise_adjust_max_iteration or appropriately loosen the tolerances
base_driver:
  ros__parameters:
    ....
    precise_adjust_xy_pid: [0.5, 0.0, 0.05]       # DRIVE stage: Kp = distance -> forward speed gain [Kp, Ki, Kd]
    precise_adjust_theta_pid: [1.0, 0.0, 0.3]     # Each turning stage: Kp = heading error -> angular speed gain [Kp, Ki, Kd]
    precise_adjust_max_linear_vel: 0.12           # Max linear speed for precise adjustment (m/s), slow down to improve convergence stability
    precise_adjust_max_angular_vel: 0.5           # Max angular speed for precise adjustment (rad/s), ensures decisive turning
    precise_adjust_pos_tolerance: 0.01            # Precise adjustment position tolerance (m), loosen to accommodate TF/odometry noise
    precise_adjust_angle_tolerance: 0.06          # Precise adjustment angle tolerance (rad, ~3.4°)
    precise_adjust_max_iteration: 500             # Precise adjustment max iterations (10Hz → ~50s)
...
OpenClaw Natural Interaction + Basic Control
OpenClaw Natural Interaction + Multimodal Visual Understand

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

Copyright © 2017-2026 Yahboom. All Rights Reserved.

EN/中文