Control a Dobot Magician with Python APIs in 20 Minutes

Send commands, move joints, pick objects, and automate tasks on the Dobot Magician robotic arm using Python and the official DobotDllType API.

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) or libDobotDll.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 / .so file isn't in the same directory as DobotDllType.py. Check the path in DobotDllType.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) after SetQueuedCmdStartExec before polling — the index takes a moment to increment.
  • lastIndex is -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
  • Done printed 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=1 and poll GetQueuedCmdCurrentIndex to sequence moves reliably
  • Suction requires both enableCtrl=1 and suck=1, plus a short sleep to build pressure
  • Coordinates are absolute in mm — always verify they fall within your arm's physical reach before running

Limitations:

  • DobotDllType.py is Windows-first; Linux support varies by SDK version (test with your hardware)
  • The queue index can wrap around on long sessions — restart SetQueuedCmdClear between 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