-
Notifications
You must be signed in to change notification settings - Fork 11
Description
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:
- Connects to dashboard (29999), motion (30003), and feedback (30004) ports.
- Clears errors, enables the robot, and sets user coordinate to 0.
- Starts a feedback thread to monitor robot state.
- Sends
MovJcommands via the motion client. - 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 # TimeoutResponse 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
- Connected to robot dashboard (port 29999), motion (port 30003), and feedback (port 30004).
- Cleared errors, enabled robot, set user coordinate to 0.
- Started feedback thread to monitor robot state.
- Sent
MovJcommands via the motion client. - Attempted to wait for motion completion using feedback (robot mode and command ID).
- 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
MovJcommand 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=0explicitly to theMovJcommand 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