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:
- Motion control
- Relative-angle and absolute-angle movement of the left / right arm
- Synchronized relative and absolute movement of both arms (the left and right arms are guaranteed to move in sync)
- Reset the left arm / right arm / both arms to the default position (perpendicular to the ground)
- Stop the motion of the left arm / right arm / both arms
- Status queries
- Query the current position of the left / right arm (angle, motor position, speed)
- Query whether the arms can move (whether arm swinging is enabled)
- Real-time monitoring
- Register / unregister a real-time arm position listener to continuously receive position changes
Key points:
- All motion APIs return their results asynchronously through the
CommandListenercallbackresult == Definition.RESULT_SUCCEEDandmessage == Definition.SUCCEEDmeans 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 value30; behavior is not guaranteed for values outside this range - Resource usage:
armLeftXxxusesRESOURCE_ARM_LEFT,armRightXxxusesRESOURCE_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 IDangle: int, relative angle (unit: degrees; positive is forward, negative is backward; for example,50moves 50° forward and-50moves 50° backward). The target position (current angle +angle) must be within-30° ~ +130°; otherwise it is clipped to the limitsspeed: int, speed (1~60; passing 0 uses the default of 30)listener:CommandListenercallback; can benull
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 IDangle: int, target absolute angle (unit: degrees; valid range-30° ~ +130°; values outside it are clipped). For example,45moves to 45° and-30moves to -30°speed: int, speed (1~60; passing 0 uses the default of 30)listener:CommandListenercallback; can benull
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 IDangle: 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 limitsspeed: int, speed (1~60; passing 0 uses the default of 30)listener:CommandListenercallback; can benull
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 IDangle: int, target absolute angle (unit: degrees; valid range-30° ~ +130°; values outside it are clipped). Mirroring for the right arm is handled automaticallyspeed: int, speed (1~60; passing 0 uses the default of 30)listener:CommandListenercallback; can benull
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 IDleftAngle: 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:CommandListenercallback; can benull
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 IDleftAngle: 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:CommandListenercallback; can benull
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 IDlistener:CommandListenercallback
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 IDlistener:CommandListenercallback
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 IDlistener:CommandListenercallback
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 IDlistener:CommandListenercallback, which returns a JSON string inextraData: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 IDlistener:CommandListenercallback, which returns a JSON string inextraDatain the same format as the left arm query;armis 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 IDlistener:CommandListenercallback, which returns a JSON string inextraData:{"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:StatusListenerstatus listeneronStatusUpdate(String type, String data):datais a JSON string with the same fields asgetLeftArmPosition/getRightArmPosition:arm,angle,speed
Description:
- After registration, you keep receiving position changes for the left and right arms until you call
unregisterArmPositionListenerto unregister - To unregister, just pass the same
listenerinstance; 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 sameStatusListenerinstance 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
| API | Resources used |
armLeftRelMove / armLeftAbsMove / resetLeftArmToDefault / armLeftStop | RESOURCE_ARM_LEFT |
armRightRelMove / armRightAbsMove / resetRightArmToDefault / armRightStop | RESOURCE_ARM_RIGHT |
armDualRelMove / armDualAbsMove / resetBothArmsToDefault / armDualStop | RESOURCE_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.onResultcallback), 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.