Skip to content

MovJ / MovL always move to x=0.0000 on Dobot Nova 5 with TCP-IP-Python-V4 #5

@n7729697

Description

@n7729697

Robot Verification Issue: Motion Timeout Despite Correct Movement

Problem Description

I am running a verification script (verify_robot.py) for my Dobot Nova 5 robot. The script connects, enables the robot, sets the user coordinate, and sends MovJ commands to test motion. Note: The robot reaches the exact commanded pose (as shown by GetPose()), but the script times out waiting for motion completion due to issues with feedback parsing and command ID matching.

Verification Script Details

The verify_robot.py script performs the following key steps:

  1. Connects to dashboard (29999), motion (30003), and feedback (30004) ports.
  2. Clears errors, enables the robot, and sets user coordinate to 0.
  3. Starts a feedback thread to monitor robot state.
  4. Sends MovJ commands via the motion client.
  5. Waits for motion completion using feedback.

Key Code Snippets

Motion Testing:

def test_motion(self, x, y, z, rx=180.0, ry=0.0, rz=180.0):
    resp = self.motion.MovJ(x, y, z, rx, ry, rz, 0)  # Send to motion port
    self.get_current_pose()  # Print pose after command
    return self.wait_for_motion(resp)

Waiting for Motion:

def wait_for_motion(self, command_response, timeout=10.0):
    parsed = self.parse_result_id(command_response)
    if not parsed or parsed[0] != 0:
        return False
    command_id = parsed[1] if len(parsed) > 1 else None
    start_time = time.time()
    while time.time() - start_time < timeout:
        if self.feed_item.robot_mode == 5 and self.feed_item.current_command_id == command_id:
            return True
        time.sleep(0.1)
    return False  # Timeout

Response Parsing:

def parse_result_id(self, response):
    m = re.match(r"^\s*(-?\d+)\s*(?:,\s*(-?\d+))?", response)
    if not m:
        return None
    return [int(m.group(1)), int(m.group(2))] if m.group(2) else [int(m.group(1))]

The script uses DobotApiDashboard for both dashboard and motion connections, but motion commands must be sent to the motion port client. The waiting logic in wait_for_motion() is not detecting motion completion.

Steps Taken

  1. Connected to robot dashboard (port 29999), motion (port 30003), and feedback (port 30004).
  2. Cleared errors, enabled robot, set user coordinate to 0.
  3. Started feedback thread to monitor robot state.
  4. Sent MovJ commands via the motion client.
  5. Attempted to wait for motion completion using feedback (robot mode and command ID).
  6. Printed current pose after timeout.

Output Logs

First Run (Commanded: x=150.0, y=-350.0, z=300.0, rx=92.0, ry=67.0, rz=107.0)

[17:31:00] Starting robot verification...
[17:31:00] Connecting to robot...
[17:31:00] Connected successfully
[17:31:00] Errors cleared successfully
[17:31:02] Robot enabled successfully
[17:31:02] User coordinate set to 0
[17:31:02] Feedback thread started
[17:31:03] Get status failed: list index out of range
[17:31:03] Testing motion to: x=150.0, y=-350.0, z=300.0, rx=92.0, ry=67.0, rz=107.0
[17:31:03] MovJ response: 0,{},MovJ(pose={150.000000,-350.000000,300.000000,92.000000,67.000000,107.000000});
[17:31:13] Motion timeout
[17:31:13] Current pose: 0,{-0.000000,-350.000000,300.000000,92.000000,67.000000,107.000000},GetPose();
[17:31:13] Motion test FAILED

❌ VERIFICATION FAILED: Check logs above for issues.
[17:31:13] Robot disabled
[17:31:13] Verification complete

Second Run (Commanded: x=200.0, y=-400.0, z=200.0, rx=180.0, ry=0.0, rz=180.0)

[17:31:24] Starting robot verification...
[17:31:24] Connecting to robot...
[17:31:25] Connected successfully
[17:31:25] Errors cleared successfully
[17:31:26] Robot enabled successfully
[17:31:26] User coordinate set to 0
[17:31:26] Feedback thread started
[17:31:27] Get status failed: list index out of range
[17:31:27] Testing motion to: x=200.0, y=-400.0, z=200.0, rx=180.0, ry=0.0, rz=180.0
[17:31:27] MovJ response: 0,{},MovJ(pose={200.000000,-400.000000,200.000000,180.000000,0.000000,180.000000});
[17:31:37] Motion timeout
[17:31:37] Current pose: 0,{-0.000000,-400.000000,200.000000,180.000000,-0.000000,180.000000},GetPose();
[17:31:37] Motion test FAILED

❌ VERIFICATION FAILED: Check logs above for issues.
[17:31:37] Robot disabled
[17:31:37] Verification complete

Analysis

  • The MovJ command is sent with the correct X value (e.g., 150.0 or 200.0), as shown in the response: MovJ(pose={150.000000,...}).
  • However, the robot's actual pose after motion shows X=0.0000, while Y, Z, RX, RY, RZ match exactly.
  • Setting user coordinate to 0 and tool coordinate to 0 before motion does not resolve the issue.
  • Passing user=0, tool=0 explicitly to the MovJ command does not resolve the issue.
  • The motion command appears to be formatted correctly by dobot_api.py, but the robot ignores or misinterprets the X coordinate.
  • This suggests a bug in the Dobot Nova 5 firmware/API handling of X coordinates in TCP/IP commands, or an incorrect coordinate transformation.
MovJ response: 0,{},MovJ(pose={150.000000,-350.000000,300.000000,92.000000,67.000000,107.000000});
Current pose: 0,{-0.000000,-350.000000,300.000000,92.000000,67.000000,107.000000},GetPose();

MovJ response: 0,{},MovJ(pose={200.000000,-400.000000,200.000000,180.000000,0.000000,180.000000});
Current pose: 0,{-0.000000,-400.000000,200.000000,180.000000,-0.000000,180.000000},GetPose();

Question

Why does the robot move correctly to the commanded positions, but the verification script times out waiting for motion completion? How can I fix the wait_for_motion() logic to properly detect when the robot has finished moving, especially when no command ID is returned in the response?

Code Snippet (verify_robot.py)

#!/usr/bin/env python3
"""
Dobot Robot Verification Script
Tests basic robot functionality: connection, enabling, feedback, and motion commands.
"""

import threading
import time
import re
from dobot_api import DobotApiDashboard, DobotApiFeedBack

class RobotVerifier:
    def __init__(self, ip="192.168.0.37"):
        self.ip = ip
        self.dashboard_port = 29999
        self.motion_port = 30003
        self.feedback_port = 30004
        self.dashboard = None
        self.motion = None
        self.feedback = None
        self.feed_data = None
        self.connected = False

        # Feedback data structure
        class FeedItem:
            def __init__(self):
                self.robot_mode = -1
                self.current_command_id = 0
                self.digital_inputs = -1
                self.digital_outputs = -1

        self.feed_item = FeedItem()

    def log(self, message):
        """Simple logging function"""
        timestamp = time.strftime("%H:%M:%S")
        print(f"[{timestamp}] {message}")

    def parse_result_id(self, response):
        """Parse the response to extract error code and command ID"""
        if "Not Tcp" in response:
            self.log("ERROR: Control Mode Is Not Tcp")
            return None
        matches = re.findall(r'-?\d+', response)
        if matches:
            return [int(num) for num in matches]
        return None

    def connect_robot(self):
        """Connect to robot dashboard and feedback"""
        try:
            self.log("Connecting to robot...")
            self.dashboard = DobotApiDashboard(self.ip, self.dashboard_port)
            self.motion = DobotApiDashboard(self.ip, self.motion_port)
            self.feedback = DobotApiFeedBack(self.ip, self.feedback_port)
            self.connected = True
            self.log("Connected successfully")
            return True
        except Exception as e:
            self.log(f"Connection failed: {str(e)}")
            return False

    def clear_errors(self):
        """Clear any robot errors"""
        if not self.connected:
            return False
        try:
            resp = self.dashboard.ClearError()
            parsed = self.parse_result_id(resp)
            if parsed and parsed[0] == 0:
                self.log("Errors cleared successfully")
                return True
            else:
                self.log(f"Failed to clear errors: {resp}")
                return False
        except Exception as e:
            self.log(f"Clear errors failed: {str(e)}")
            return False

    def enable_robot(self):
        """Enable the robot"""
        if not self.connected:
            return False
        try:
            resp = self.dashboard.EnableRobot()
            parsed = self.parse_result_id(resp)
            if parsed and parsed[0] == 0:
                self.log("Robot enabled successfully")
                return True
            else:
                self.log(f"Failed to enable robot: {resp}")
                return False
        except Exception as e:
            self.log(f"Enable robot failed: {str(e)}")
            return False

    def set_user_coordinate(self, user=0):
        """Set the user coordinate system"""
        if not self.connected:
            return False
        try:
            resp = self.dashboard.User(user)
            parsed = self.parse_result_id(resp)
            if parsed and parsed[0] == 0:
                self.log(f"User coordinate set to {user}")
                return True
            else:
                self.log(f"Failed to set user coordinate: {resp}")
                return False
        except Exception as e:
            self.log(f"Set user coordinate failed: {str(e)}")
            return False

    def start_feedback_thread(self):
        """Start the feedback monitoring thread"""
        def feedback_loop():
            while self.connected:
                try:
                    feed_info = self.feedback.feedBackData()
                    if feed_info:
                        if hex((feed_info['TestValue'][0])) == '0x123456789abcdef':
                            self.feed_item.robot_mode = feed_info['RobotMode'][0]
                            self.feed_item.digital_inputs = feed_info['DigitalInputs'][0]
                            self.feed_item.digital_outputs = feed_info['DigitalOutputs'][0]
                            self.feed_item.current_command_id = feed_info['CurrentCommandId'][0]
                except Exception as e:
                    self.log(f"Feedback error: {str(e)}")
                time.sleep(0.1)

        thread = threading.Thread(target=feedback_loop, daemon=True)
        thread.start()
        self.log("Feedback thread started")

    def wait_for_motion(self, command_response, timeout=10.0):
        """Wait for motion command to complete"""
        parsed = self.parse_result_id(command_response)
        if not parsed or parsed[0] != 0:
            self.log(f"Motion command failed: {command_response}")
            return False

        command_id = parsed[1]
        start_time = time.time()

        while time.time() - start_time < timeout:
            if self.feed_item.robot_mode == 5 and self.feed_item.current_command_id == command_id:
                self.log("Motion completed successfully")
                return True
            time.sleep(0.1)

        self.log("Motion timeout")
        return False

    def test_motion(self, x, y, z, rx=180.0, ry=0.0, rz=180.0):
        """Test a simple motion command"""
        if not self.connected:
            return False

        self.log(f"Testing motion to: x={x}, y={y}, z={z}, rx={rx}, ry={ry}, rz={rz}")

        # Send MovJ command (coordinateMode=0 for pose)
        resp = self.motion.MovJ(x, y, z, rx, ry, rz, 0)
        self.log(f"MovJ response: {resp}")

        # Wait for completion
        return self.wait_for_motion(resp)

    def get_current_pose(self):
        """Get and print the current robot pose"""
        if not self.connected:
            self.log("Not connected")
            return None
        try:
            resp = self.dashboard.GetPose()
            self.log(f"Current pose: {resp}")
            return resp
        except Exception as e:
            self.log(f"Get pose failed: {str(e)}")
            return None

    def get_robot_status(self):
        """Get current robot status"""
        if not self.connected:
            return None

        try:
            mode_resp = self.dashboard.RobotMode()
            error_resp = self.dashboard.GetErrorID()
            mode_parsed = self.parse_result_id(mode_resp)
            error_parsed = self.parse_result_id(error_resp)

            status = {
                'robot_mode': mode_parsed[1] if mode_parsed else -1,
                'error_id': error_parsed[1] if error_parsed else -1,
                'feed_robot_mode': self.feed_item.robot_mode,
                'feed_command_id': self.feed_item.current_command_id
            }
            return status
        except Exception as e:
            self.log(f"Get status failed: {str(e)}")
            return None

    def run_verification(self):
        """Run the complete verification sequence"""
        self.log("Starting robot verification...")

        # Step 1: Connect
        if not self.connect_robot():
            return False

        # Step 2: Clear errors
        if not self.clear_errors():
            return False

        # Step 3: Enable robot
        if not self.enable_robot():
            return False

        # Step 4: Set user coordinate
        if not self.set_user_coordinate(0):
            return False

        # Step 5: Start feedback
        self.start_feedback_thread()
        time.sleep(1)  # Let feedback initialize

        # Step 6: Get initial status
        status = self.get_robot_status()
        if status:
            self.log(f"Initial status: {status}")

        # Step 7: Test motion to a safe position
        # Using a position similar to the demo but safe
        test_pos = [200.0,-400.0,200.0,180.0,0.0,180.0]  # Safe test position
        test_pos2 = [150.0, -350.0, 300.0, 92.0, 67.0, 107.0]
        if self.test_motion(*test_pos):
            # Print current pose before waiting
            self.get_current_pose()
            self.log("Motion test PASSED")
        else:
            # Print current pose before waiting
            self.get_current_pose()
            self.log("Motion test FAILED")
            return False

        # Step 8: Test another position
        if self.test_motion(*test_pos2):
            self.log("Second motion test PASSED")
        else:
            self.log("Second motion test FAILED")
            return False

        self.log("All verification tests PASSED!")
        return True

    def cleanup(self):
        """Clean up connections"""
        self.connected = False
        if self.dashboard:
            try:
                self.dashboard.DisableRobot()
                self.log("Robot disabled")
            except:
                pass
        self.log("Verification complete")

def main():
    verifier = RobotVerifier()

    try:
        success = verifier.run_verification()
        if success:
            print("\n✅ VERIFICATION SUCCESSFUL: Robot is working correctly!")
        else:
            print("\n❌ VERIFICATION FAILED: Check logs above for issues.")
    except KeyboardInterrupt:
        print("\nInterrupted by user")
    except Exception as e:
        print(f"\nUnexpected error: {str(e)}")
    finally:
        verifier.cleanup()

if __name__ == "__main__":
    main()

Environment

  • Robot: Dobot Nova 5
  • API: Custom dobot_api.py
  • Python 3
  • OS: Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions