Problem: Sending Commands to a Dobot Magician from Python
You have a Dobot Magician robotic arm and want to control it programmatically — move it to specific coordinates, trigger the suction cup, or automate pick-and-place sequences — but Dobot's official software only goes so far.
You'll learn:
- How to connect to the Dobot via Python using the official DLL/shared library API
- How to move the arm to Cartesian and joint-space coordinates
- How to control the end-effector (suction cup or gripper)
- How to queue and execute motion commands reliably
Time: 20 min | Level: Intermediate
Why This Happens
The Dobot Magician exposes its control interface through a native shared library (DobotDll.dll on Windows, libDobotDll.so on Linux). Dobot provides a Python wrapper called DobotDllType.py that wraps this library with ctypes. Once connected over USB, you can send movement commands, manage the motion queue, and read arm state — all from Python.
Common gotchas:
- The DLL path must be set correctly or the library silently fails to load
- Commands are queued asynchronously — you must wait for execution or the arm skips steps
- Coordinate units are millimeters and degrees, not meters or radians
Solution
Step 1: Install Dependencies and Get the SDK
Download the Dobot SDK from the official Dobot developer portal under "Secondary Development Resources." Extract it and locate:
DobotDll.dll(Windows) orlibDobotDll.so(Linux/Mac)DobotDllType.py— the Python ctypes wrapper
Place both in your project directory, then connect the Dobot via USB and note which port it appears on (COM3 on Windows, /dev/ttyUSB0 on Linux).
# Verify the device appears (Linux/Mac)
ls /dev/ttyUSB*
# Windows: check Device Manager for the COM port
No pip install is needed — the SDK is file-based.
Step 2: Connect to the Arm
import DobotDllType as dType
import time
# Load the shared library from the current directory
api = dType.load()
# Connect to the Dobot (empty string = auto-detect port)
# Returns (errorCode,) — 0 means success
state = dType.ConnectDobot(api, "", 115200)
print("Connect state:", state[0]) # 0 = DobotConnect_NoError
if state[0] != dType.DobotConnect.DobotConnect_NoError:
raise RuntimeError("Could not connect to Dobot — check USB and port")
# Clear any leftover commands from a previous session
dType.SetQueuedCmdClear(api)
# Set home position parameters (tweak these to match your workspace)
dType.SetHOMEParams(api, 250, 0, 50, 0, isQueued=0)
Expected: No exception, state[0] prints 0.
If it fails:
OSError: cannot open shared object file: The.dll/.sofile isn't in the same directory asDobotDllType.py. Check the path inDobotDllType.py(line ~10) and update it.state[0]is non-zero: Try specifying the port explicitly:dType.ConnectDobot(api, "COM3", 115200).
Step 3: Move to a Cartesian Position
# Move to an absolute (X, Y, Z, R) position in mm and degrees
# MODE_PTP_MOVL_XYZ = linear move in Cartesian space
lastIndex = dType.SetPTPCmd(
api,
dType.PTPMode.PTPMOVLXYZMode, # Linear move
x=250, y=0, z=50, rHead=0, # Target position (mm, mm, mm, deg)
isQueued=1 # Add to queue (don't execute immediately)
)[0]
# Start executing the queue
dType.SetQueuedCmdStartExec(api)
# Wait until the arm finishes this command before sending the next one
while lastIndex > dType.GetQueuedCmdCurrentIndex(api)[0]:
dType.dSleep(100) # Poll every 100ms
print("Arm reached target position")
Why isQueued=1 matters: Without queuing, rapid sequential commands overwrite each other and the arm skips moves. Always queue and wait.
If it fails:
- Arm moves but stops early: Add a
time.sleep(0.5)afterSetQueuedCmdStartExecbefore polling — the index takes a moment to increment. lastIndexis -1: The command was rejected. Check that coordinates are within the arm's reach (roughly X: 150–320mm, Y: ±150mm, Z: –50–150mm).
Step 4: Control the Suction Cup End-Effector
def set_suction(api, enable: bool):
"""Turn the suction cup on or off."""
# EnableCtrl=1 to activate, SuctionCup=1 to apply suction
lastIndex = dType.SetEndEffectorSuctionCup(
api,
enableCtrl=1,
suck=1 if enable else 0,
isQueued=1
)[0]
dType.SetQueuedCmdStartExec(api)
while lastIndex > dType.GetQueuedCmdCurrentIndex(api)[0]:
dType.dSleep(50)
# Pick: move down, activate suction, lift up
def pick(api, x, y, z_approach, z_grasp):
# Approach above the object
move_to(api, x, y, z_approach)
# Descend to grasp height
move_to(api, x, y, z_grasp)
# Activate suction
set_suction(api, True)
time.sleep(0.3) # Give suction time to grip
# Lift back up
move_to(api, x, y, z_approach)
Step 5: A Complete Pick-and-Place Sequence
def move_to(api, x, y, z, r=0):
"""Move arm to Cartesian position and wait for completion."""
lastIndex = dType.SetPTPCmd(
api,
dType.PTPMode.PTPMOVLXYZMode,
x=x, y=y, z=z, rHead=r,
isQueued=1
)[0]
dType.SetQueuedCmdStartExec(api)
while lastIndex > dType.GetQueuedCmdCurrentIndex(api)[0]:
dType.dSleep(100)
# Define pick and place positions
PICK_POS = (250, 80, 50, 30) # x, y, z_approach, z_grasp
PLACE_POS = (250, -80, 50, 30)
# --- Run pick-and-place ---
dType.SetQueuedCmdClear(api)
dType.SetQueuedCmdStartExec(api)
# Pick
move_to(api, PICK_POS[0], PICK_POS[1], PICK_POS[2])
move_to(api, PICK_POS[0], PICK_POS[1], PICK_POS[3]) # Descend
set_suction(api, True)
# Place
move_to(api, PICK_POS[0], PICK_POS[1], PICK_POS[2]) # Lift
move_to(api, PLACE_POS[0], PLACE_POS[1], PLACE_POS[2]) # Travel
move_to(api, PLACE_POS[0], PLACE_POS[1], PLACE_POS[3]) # Descend
set_suction(api, False)
move_to(api, PLACE_POS[0], PLACE_POS[1], PLACE_POS[2]) # Retreat
# Disconnect cleanly
dType.DisconnectDobot(api)
print("Done")
Verification
Run the script with the arm powered on and connected:
python pick_and_place.py
You should see:
Connect state: 0- The arm moves to the pick position, descends, lifts the object, travels to the place position, and releases it
Doneprinted with no exceptions
If the arm moves erratically: Reduce speed with dType.SetPTPCommonParams(api, velocityRatio=30, accelerationRatio=30, isQueued=0) before issuing move commands. Default speed can be too fast for certain payloads.
What You Learned
- The Dobot Python API wraps a native DLL with
ctypes— no pip package required - Always use
isQueued=1and pollGetQueuedCmdCurrentIndexto sequence moves reliably - Suction requires both
enableCtrl=1andsuck=1, plus a shortsleepto build pressure - Coordinates are absolute in mm — always verify they fall within your arm's physical reach before running
Limitations:
DobotDllType.pyis Windows-first; Linux support varies by SDK version (test with your hardware)- The queue index can wrap around on long sessions — restart
SetQueuedCmdClearbetween task cycles - Joint-space moves (
PTPMOVJANGLEMode) are faster for large sweeps but harder to reason about spatially
Tested on Dobot Magician firmware 3.8.x, SDK v2.0, Python 3.11, Windows 11 and Ubuntu 22.04