Skip to content Skip to main navigation Skip to footer

Robotic Arm Control

1. Introduction

This document describes how to use the robotic arm APIs in RobotApi. These APIs provide motion control, position queries and real-time monitoring for the robot’s left arm, right arm and both arms.

Supported models: All arm control APIs covered in this document are supported only on GreetingBot Pro. On other models, calls to these APIs will not work properly.

Capabilities:

  1. Motion control
    1. Relative-angle and absolute-angle movement of the left / right arm
    2. Synchronized relative and absolute movement of both arms (the left and right arms are guaranteed to move in sync)
    3. Reset the left arm / right arm / both arms to the default position (perpendicular to the ground)
    4. Stop the motion of the left arm / right arm / both arms
  2. Status queries
    1. Query the current position of the left / right arm (angle, motor position, speed)
    2. Query whether the arms can move (whether arm swinging is enabled)
  3. Real-time monitoring
    1. Register / unregister a real-time arm position listener to continuously receive position changes

Key points:

  • All motion APIs return their results asynchronously through the CommandListener callback
    • result == Definition.RESULT_SUCCEED and message == Definition.SUCCEED means success
    • Anything else is a failure, including timeouts, hardware faults and so on
  • Angle unit: degrees; positive values are forward and negative values are backward; 0° means the arm is perpendicular to the ground
  • Valid angle range: -30° ~ +130° (at most 30° backward and at most 130° forward); target angles outside this range are clipped to the limits
  • Default position (the target position of the reset APIs): 0° (perpendicular to the ground)
  • Speed parameter range: 1 ~ 60; if you pass 0, the arm moves at the default value 30; behavior is not guaranteed for values outside this range
  • Resource usage: armLeftXxx uses RESOURCE_ARM_LEFT, armRightXxx uses RESOURCE_ARM_RIGHT, and the dual-arm APIs use both the left and right arm resources. If a resource is still held by a previous command that has not finished, the new command returns a resource conflict error. If your app can make sure that calls do not overlap, no extra handling is needed; if you cannot control when calls are made (for example, when the user taps quickly several times), we recommend calling the corresponding stop API before each motion (armLeftStop / armRightStop / armDualStop). This effectively avoids conflicts.

2. Motion Control APIs

2.1 Move the Left Arm by a Relative Angle

Method name: armLeftRelMove

How to call:

RobotApi.getInstance().armLeftRelMove(reqId, angle, speed, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Success
        } else {
            // Failure (including timeouts, hardware faults, etc.)
        }
    }
});

Parameters:

  • reqId: int, command ID
  • angle: int, relative angle (unit: degrees; positive is forward, negative is backward; for example, 50 moves 50° forward and -50 moves 50° backward). The target position (current angle + angle) must be within -30° ~ +130°; otherwise it is clipped to the limits
  • speed: int, speed (1~60; passing 0 uses the default of 30)
  • listener: CommandListener callback; can be null

2.2 Move the Left Arm to an Absolute Angle

Method name: armLeftAbsMove

How to call:

RobotApi.getInstance().armLeftAbsMove(reqId, angle, speed, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Moved to the target angle successfully
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • angle: int, target absolute angle (unit: degrees; valid range -30° ~ +130°; values outside it are clipped). For example, 45 moves to 45° and -30 moves to -30°
  • speed: int, speed (1~60; passing 0 uses the default of 30)
  • listener: CommandListener callback; can be null

2.3 Move the Right Arm by a Relative Angle

Method name: armRightRelMove

How to call:

RobotApi.getInstance().armRightRelMove(reqId, angle, speed, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Success
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • angle: int, relative angle (unit: degrees; positive is forward, negative is backward; mirroring for the right arm is handled automatically). The target position must be within -30° ~ +130°; otherwise it is clipped to the limits
  • speed: int, speed (1~60; passing 0 uses the default of 30)
  • listener: CommandListener callback; can be null

2.4 Move the Right Arm to an Absolute Angle

Method name: armRightAbsMove

How to call:

RobotApi.getInstance().armRightAbsMove(reqId, angle, speed, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Success
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • angle: int, target absolute angle (unit: degrees; valid range -30° ~ +130°; values outside it are clipped). Mirroring for the right arm is handled automatically
  • speed: int, speed (1~60; passing 0 uses the default of 30)
  • listener: CommandListener callback; can be null

2.5 Move Both Arms by Relative Angles (Synchronized)

Method name: armDualRelMove

How to call:

RobotApi.getInstance().armDualRelMove(reqId, leftAngle, rightAngle, speed, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Success
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • leftAngle: int, relative angle of the left arm (positive is forward, negative is backward). The target position must be within -30° ~ +130°
  • rightAngle: int, relative angle of the right arm (positive is forward, negative is backward). The target position must be within -30° ~ +130°
  • speed: int, speed (1~60; passing 0 uses the default of 30)
  • listener: CommandListener callback; can be null

Description: This API guarantees that the left and right arms move in sync, which is more reliable than calling the left and right arm APIs separately.


2.6 Move Both Arms to Absolute Angles (Synchronized)

Method name: armDualAbsMove

How to call:

RobotApi.getInstance().armDualAbsMove(reqId, leftAngle, rightAngle, speed, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Success
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • leftAngle: int, target absolute angle of the left arm (valid range -30° ~ +130°)
  • rightAngle: int, target absolute angle of the right arm (valid range -30° ~ +130°)
  • speed: int, speed (1~60; passing 0 uses the default of 30)
  • listener: CommandListener callback; can be null

2.7 Reset the Left Arm to the Default Position

Method name: resetLeftArmToDefault

How to call:

RobotApi.getInstance().resetLeftArmToDefault(reqId, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Reset succeeded
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • listener: CommandListener callback

Description: The default position is the left arm perpendicular to the ground (that is, 0°).


2.8 Reset the Right Arm to the Default Position

Method name: resetRightArmToDefault

How to call:

RobotApi.getInstance().resetRightArmToDefault(reqId, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Reset succeeded
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • listener: CommandListener callback

Description: The default position is the right arm perpendicular to the ground (that is, 0°).


2.9 Reset Both Arms to the Default Position (Synchronized)

Method name: resetBothArmsToDefault

How to call:

RobotApi.getInstance().resetBothArmsToDefault(reqId, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Reset succeeded
        } else {
            // Failure
        }
    }
});

Parameters:

  • reqId: int, command ID
  • listener: CommandListener callback

Description: Resets both the left and right arms to the default position at the same time. Compared with calling resetLeftArmToDefault and resetRightArmToDefault separately, this API guarantees that both arms move in sync.


3. Stop Control APIs

3.1 Stop Left Arm Motion

Method name: armLeftStop

How to call:

RobotApi.getInstance().armLeftStop(reqId);

Parameters:

  • reqId: int, command ID

Description: This API also releases the RESOURCE_ARM_LEFT resource. There is no callback.


3.2 Stop Right Arm Motion

Method name: armRightStop

How to call:

RobotApi.getInstance().armRightStop(reqId);

Parameters:

  • reqId: int, command ID

Description: This API also releases the RESOURCE_ARM_RIGHT resource. There is no callback.


3.3 Stop Both Arms

Method name: armDualStop

How to call:

RobotApi.getInstance().armDualStop(reqId);

Parameters:

  • reqId: int, command ID

Description: Stops the motion of both the left and right arms and releases the corresponding resources. There is no callback.


4. Status Query APIs

4.1 Query the Left Arm Position

Method name: getLeftArmPosition

How to call:

RobotApi.getInstance().getLeftArmPosition(reqId, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            try {
                JSONObject json = new JSONObject(extraData);
                String arm = json.optString(Definition.JSON_ARM);     // Definition.ARM_LEFT
                int angle = json.optInt(Definition.JSON_ARM_ANGLE);   // angle (unit: degrees)
                int speed = json.optInt(Definition.JSON_ARM_SPEED);   // current speed
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
});

Parameters:

  • reqId: int, command ID
  • listener: CommandListener callback, which returns a JSON string in extraData:
    • arm: arm identifier, always "left" (Definition.ARM_LEFT)
    • angle: current angle (degrees)
    • speed: current speed

4.2 Query the Right Arm Position

Method name: getRightArmPosition

How to call:

RobotApi.getInstance().getRightArmPosition(reqId, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            try {
                JSONObject json = new JSONObject(extraData);
                String arm = json.optString(Definition.JSON_ARM);     // Definition.ARM_RIGHT
                int angle = json.optInt(Definition.JSON_ARM_ANGLE);
                int speed = json.optInt(Definition.JSON_ARM_SPEED);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
});

Parameters:

  • reqId: int, command ID
  • listener: CommandListener callback, which returns a JSON string in extraData in the same format as the left arm query; arm is always "right" (Definition.ARM_RIGHT)

4.3 Query Whether the Arms Can Move

Method name: isArmMovable

How to call:

RobotApi.getInstance().isArmMovable(reqId, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            try {
                JSONObject json = new JSONObject(extraData);
                boolean movable = json.optBoolean(Definition.JSON_ARM_MOVABLE);
                // movable == true means arm swinging is currently enabled and arm motion can be performed
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
});

Parameters:

  • reqId: int, command ID
  • listener: CommandListener callback, which returns a JSON string in extraData: {"movable": true/false}

Description:

  • On models that support robotic arms, if peripherals such as a chest-mounted large screen are also connected, the system dynamically decides whether arm swinging is allowed based on the current state. This switch can only be changed on the system settings page and has three modes:
    • Smart detection (default): automatically detects whether a screen is connected. The arms do not swing when a screen is connected and swing normally when it is not.
    • Swing off: the arms never swing. Use this when an external screen is connected, to keep the arms from hitting the screen.
    • Swing on: the arms can always swing. This suits interactive scenarios such as guiding guests to their seats or dancing; we recommend using it only when no external screen is connected.
  • This API returns the final result of “whether the arms can actually swing right now”, taking both the system setting and the external screen state into account.
  • When movable == false, calls to the arm motion APIs are not actually executed; they only return a failure result and cause no other side effects.
  • If your app needs to tell the user that the arms are currently unavailable, call this API once when entering the relevant page; there is no need to check repeatedly afterwards.

5. Real-Time Monitoring APIs

5.1 Register an Arm Position Listener

Method name: registerArmPositionListener

How to call:

StatusListener listener = new StatusListener() {
    @Override
    public void onStatusUpdate(String type, String data) {
        try {
            JSONObject json = new JSONObject(data);
            String arm = json.optString(Definition.JSON_ARM);     // Definition.ARM_LEFT or Definition.ARM_RIGHT
            int angle = json.optInt(Definition.JSON_ARM_ANGLE);
            int speed = json.optInt(Definition.JSON_ARM_SPEED);
            if (Definition.ARM_LEFT.equals(arm)) {
                // Left arm position updated
            } else if (Definition.ARM_RIGHT.equals(arm)) {
                // Right arm position updated
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
};

RobotApi.getInstance().registerArmPositionListener(listener);

Parameters:

  • listener: StatusListener status listener
    • onStatusUpdate(String type, String data): data is a JSON string with the same fields as getLeftArmPosition / getRightArmPosition: arm, angle, speed

Description:

  • After registration, you keep receiving position changes for the left and right arms until you call unregisterArmPositionListener to unregister
  • To unregister, just pass the same listener instance; you do not need to keep a return value

5.2 Unregister an Arm Position Listener

Method name: unregisterArmPositionListener

How to call:

RobotApi.getInstance().unregisterArmPositionListener(listener);

Parameters:

  • listener: the same StatusListener instance used when registering

Note: Be sure to unregister when the Activity / Fragment is destroyed to avoid memory leaks.


6. Appendix

6.1 Template for Checking Callback Results

For all motion control APIs, we recommend checking the result as follows:

new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        if (result == Definition.RESULT_SUCCEED && Definition.SUCCEED.equals(message)) {
            // Success
        } else {
            // Failure: result may be an error code such as a timeout or hardware fault
            // message describes the specific cause of the failure
        }
    }
}

6.2 Resource Usage

APIResources used
armLeftRelMove / armLeftAbsMove / resetLeftArmToDefault / armLeftStopRESOURCE_ARM_LEFT
armRightRelMove / armRightAbsMove / resetRightArmToDefault / armRightStopRESOURCE_ARM_RIGHT
armDualRelMove / armDualAbsMove / resetBothArmsToDefault / armDualStopRESOURCE_ARM_LEFT + RESOURCE_ARM_RIGHT

When a resource is in use (for example, the left arm is performing another action), a new command returns a resource conflict error code. You need to stop the conflicting command first or wait for it to finish.

How to avoid conflicts:

  • Your app guarantees the call order: if your app can make sure the next arm command is only sent after the previous one has finished (for example, by waiting for the CommandListener.onResult callback), no extra handling is needed.
  • When call timing cannot be controlled: for example, when the user taps repeatedly or calls are driven by external events, call the corresponding stop API before each motion and then start the new action. This effectively avoids resource conflicts.
// Example: stop the left arm before starting a left arm motion
RobotApi.getInstance().armLeftStop(0);
RobotApi.getInstance().armLeftAbsMove(0, 90, 30, new CommandListener() {
    @Override
    public void onResult(int result, String message, String extraData) {
        // Handle the result
    }
});

// The same applies to both arms
RobotApi.getInstance().armDualStop(0);
RobotApi.getInstance().armDualAbsMove(0, 0, 0, 30, listener);

6.3 Complete Example

For a complete example, see the following class in the RobotSample project: com.ainirobot.robotos.fragment.ArmControlFragment

This sample covers all the API calls listed in this document.