{"id":971,"date":"2026-11-03T11:23:59","date_gmt":"2026-11-03T03:23:59","guid":{"rendered":"https:\/\/doc.orionstar.com\/en\/knowledge-base\/robotic-arm-control\/"},"modified":"2026-11-03T11:28:20","modified_gmt":"2026-11-03T03:28:20","slug":"robotic-arm-control","status":"publish","type":"lsvr_kba","link":"https:\/\/doc.orionstar.com\/en\/knowledge-base\/robotic-arm-control\/","title":{"rendered":"Robotic Arm Control"},"content":{"rendered":"\n<h1>1. Introduction<\/h1>\n\n\n\n<p>This document describes how to use the robotic arm APIs in <code>RobotApi<\/code>. These APIs provide motion control, position queries and real-time monitoring for the robot&#8217;s left arm, right arm and both arms.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p><strong>Supported models:<\/strong> All arm control APIs covered in this document <strong>are supported only on <\/strong><strong>GreetingBot<\/strong><strong> Pro<\/strong>. On other models, calls to these APIs will not work properly.<\/p>\n<\/blockquote>\n\n\n\n<p><strong>Capabilities:<\/strong><\/p>\n\n\n\n<ol start=\"1\">\n<li><strong>Motion control<\/strong>\n<ol>\n<li>Relative-angle and absolute-angle movement of the left \/ right arm<\/li>\n\n\n\n<li>Synchronized relative and absolute movement of both arms (the left and right arms are guaranteed to move in sync)<\/li>\n\n\n\n<li>Reset the left arm \/ right arm \/ both arms to the default position (perpendicular to the ground)<\/li>\n\n\n\n<li>Stop the motion of the left arm \/ right arm \/ both arms<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li><strong>Status queries<\/strong>\n<ol>\n<li>Query the current position of the left \/ right arm (angle, motor position, speed)<\/li>\n\n\n\n<li>Query whether the arms can move (whether arm swinging is enabled)<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li><strong>Real-time monitoring<\/strong>\n<ol>\n<li>Register \/ unregister a real-time arm position listener to continuously receive position changes<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>Key points:<\/strong><\/p>\n\n\n\n<ul>\n<li>All motion APIs return their results asynchronously through the <code>CommandListener<\/code> callback\n<ul>\n<li><code>result == Definition.RESULT_SUCCEED<\/code> and <code>message == Definition.SUCCEED<\/code> means success<\/li>\n\n\n\n<li>Anything else is a failure, including timeouts, hardware faults and so on<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Angle unit: <strong>degrees<\/strong>; positive values are forward and negative values are backward; <code>0\u00b0<\/code> means the arm is perpendicular to the ground<\/li>\n\n\n\n<li>Valid angle range: <strong><code>-30\u00b0<\/code><\/strong><strong> ~ <\/strong><strong><code>+130\u00b0<\/code><\/strong> (at most 30\u00b0 backward and at most 130\u00b0 forward); target angles outside this range are clipped to the limits<\/li>\n\n\n\n<li>Default position (the target position of the reset APIs): <code>0\u00b0<\/code> (perpendicular to the ground)<\/li>\n\n\n\n<li>Speed parameter range: <strong>1 ~ 60<\/strong>; if you pass <code>0<\/code>, the arm moves at the default value <code>30<\/code>; behavior is not guaranteed for values outside this range<\/li>\n\n\n\n<li>Resource usage: <code>armLeftXxx<\/code> uses <code>RESOURCE_ARM_LEFT<\/code>, <code>armRightXxx<\/code> uses <code>RESOURCE_ARM_RIGHT<\/code>, 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 (<code>armLeftStop<\/code> \/ <code>armRightStop<\/code> \/ <code>armDualStop<\/code>). This effectively avoids conflicts.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1>2. Motion Control APIs<\/h1>\n\n\n\n<h2>2.1 Move the Left Arm by a Relative Angle<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armLeftRelMove<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armLeftRelMove(reqId, angle, speed, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Success\n        } else {\n            \/\/ Failure (including timeouts, hardware faults, etc.)\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>angle<\/code>: int, relative angle (unit: degrees; positive is forward, negative is backward; for example, <code>50<\/code> moves 50\u00b0 forward and <code>-50<\/code> moves 50\u00b0 backward). The target position (current angle + <code>angle<\/code>) must be within <code>-30\u00b0 ~ +130\u00b0<\/code>; otherwise it is clipped to the limits<\/li>\n\n\n\n<li><code>speed<\/code>: int, speed (1~60; passing 0 uses the default of 30)<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback; can be <code>null<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.2 Move the Left Arm to an Absolute Angle<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armLeftAbsMove<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armLeftAbsMove(reqId, angle, speed, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Moved to the target angle successfully\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>angle<\/code>: int, target absolute angle (unit: degrees; valid range <code>-30\u00b0 ~ +130\u00b0<\/code>; values outside it are clipped). For example, <code>45<\/code> moves to 45\u00b0 and <code>-30<\/code> moves to -30\u00b0<\/li>\n\n\n\n<li><code>speed<\/code>: int, speed (1~60; passing 0 uses the default of 30)<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback; can be <code>null<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.3 Move the Right Arm by a Relative Angle<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armRightRelMove<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armRightRelMove(reqId, angle, speed, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Success\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>angle<\/code>: 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 <code>-30\u00b0 ~ +130\u00b0<\/code>; otherwise it is clipped to the limits<\/li>\n\n\n\n<li><code>speed<\/code>: int, speed (1~60; passing 0 uses the default of 30)<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback; can be <code>null<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.4 Move the Right Arm to an Absolute Angle<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armRightAbsMove<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armRightAbsMove(reqId, angle, speed, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Success\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>angle<\/code>: int, target absolute angle (unit: degrees; valid range <code>-30\u00b0 ~ +130\u00b0<\/code>; values outside it are clipped). Mirroring for the right arm is handled automatically<\/li>\n\n\n\n<li><code>speed<\/code>: int, speed (1~60; passing 0 uses the default of 30)<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback; can be <code>null<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.5 Move Both Arms by Relative Angles (Synchronized)<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armDualRelMove<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armDualRelMove(reqId, leftAngle, rightAngle, speed, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Success\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>leftAngle<\/code>: int, relative angle of the left arm (positive is forward, negative is backward). The target position must be within <code>-30\u00b0 ~ +130\u00b0<\/code><\/li>\n\n\n\n<li><code>rightAngle<\/code>: int, relative angle of the right arm (positive is forward, negative is backward). The target position must be within <code>-30\u00b0 ~ +130\u00b0<\/code><\/li>\n\n\n\n<li><code>speed<\/code>: int, speed (1~60; passing 0 uses the default of 30)<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback; can be <code>null<\/code><\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> 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.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.6 Move Both Arms to Absolute Angles (Synchronized)<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armDualAbsMove<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armDualAbsMove(reqId, leftAngle, rightAngle, speed, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Success\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>leftAngle<\/code>: int, target absolute angle of the left arm (valid range <code>-30\u00b0 ~ +130\u00b0<\/code>)<\/li>\n\n\n\n<li><code>rightAngle<\/code>: int, target absolute angle of the right arm (valid range <code>-30\u00b0 ~ +130\u00b0<\/code>)<\/li>\n\n\n\n<li><code>speed<\/code>: int, speed (1~60; passing 0 uses the default of 30)<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback; can be <code>null<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.7 Reset the Left Arm to the Default Position<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>resetLeftArmToDefault<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().resetLeftArmToDefault(reqId, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Reset succeeded\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> The default position is the left arm perpendicular to the ground (that is, <code>0\u00b0<\/code>).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.8 Reset the Right Arm to the Default Position<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>resetRightArmToDefault<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().resetRightArmToDefault(reqId, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Reset succeeded\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> The default position is the right arm perpendicular to the ground (that is, <code>0\u00b0<\/code>).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>2.9 Reset Both Arms to the Default Position (Synchronized)<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>resetBothArmsToDefault<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().resetBothArmsToDefault(reqId, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Reset succeeded\n        } else {\n            \/\/ Failure\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> Resets both the left and right arms to the default position at the same time. Compared with calling <code>resetLeftArmToDefault<\/code> and <code>resetRightArmToDefault<\/code> separately, this API guarantees that both arms move in sync.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1>3. Stop Control APIs<\/h1>\n\n\n\n<h2>3.1 Stop Left Arm Motion<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armLeftStop<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armLeftStop(reqId);<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> This API also releases the <code>RESOURCE_ARM_LEFT<\/code> resource. There is no callback.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>3.2 Stop Right Arm Motion<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armRightStop<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armRightStop(reqId);<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> This API also releases the <code>RESOURCE_ARM_RIGHT<\/code> resource. There is no callback.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>3.3 Stop Both Arms<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>armDualStop<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().armDualStop(reqId);<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong> Stops the motion of both the left and right arms and releases the corresponding resources. There is no callback.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1>4. Status Query APIs<\/h1>\n\n\n\n<h2>4.1 Query the Left Arm Position<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>getLeftArmPosition<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().getLeftArmPosition(reqId, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            try {\n                JSONObject json = new JSONObject(extraData);\n                String arm = json.optString(Definition.JSON_ARM);     \/\/ Definition.ARM_LEFT\n                int angle = json.optInt(Definition.JSON_ARM_ANGLE);   \/\/ angle (unit: degrees)\n                int speed = json.optInt(Definition.JSON_ARM_SPEED);   \/\/ current speed\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback, which returns a JSON string in <code>extraData<\/code>:\n<ul>\n<li><code>arm<\/code>: arm identifier, always <code>\"left\"<\/code> (<code>Definition.ARM_LEFT<\/code>)<\/li>\n\n\n\n<li><code>angle<\/code>: current angle (degrees)<\/li>\n\n\n\n<li><code>speed<\/code>: current speed<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>4.2 Query the Right Arm Position<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>getRightArmPosition<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().getRightArmPosition(reqId, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            try {\n                JSONObject json = new JSONObject(extraData);\n                String arm = json.optString(Definition.JSON_ARM);     \/\/ Definition.ARM_RIGHT\n                int angle = json.optInt(Definition.JSON_ARM_ANGLE);\n                int speed = json.optInt(Definition.JSON_ARM_SPEED);\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback, which returns a JSON string in <code>extraData<\/code> in the same format as the left arm query; <code>arm<\/code> is always <code>\"right\"<\/code> (<code>Definition.ARM_RIGHT<\/code>)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>4.3 Query Whether the Arms Can Move<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>isArmMovable<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().isArmMovable(reqId, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            try {\n                JSONObject json = new JSONObject(extraData);\n                boolean movable = json.optBoolean(Definition.JSON_ARM_MOVABLE);\n                \/\/ movable == true means arm swinging is currently enabled and arm motion can be performed\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n});<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>reqId<\/code>: int, command ID<\/li>\n\n\n\n<li><code>listener<\/code>: <code>CommandListener<\/code> callback, which returns a JSON string in <code>extraData<\/code>: <code>{\"movable\": true\/false}<\/code><\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<ul>\n<li>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:\n<ul>\n<li><strong>Smart detection<\/strong> (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.<\/li>\n\n\n\n<li><strong>Swing off<\/strong>: the arms never swing. Use this when an external screen is connected, to keep the arms from hitting the screen.<\/li>\n\n\n\n<li><strong>Swing on<\/strong>: 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.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>This API returns the final result of &#8220;whether the arms can actually swing right now&#8221;, taking both the system setting and the external screen state into account.<\/li>\n\n\n\n<li>When <code>movable == false<\/code>, calls to the arm motion APIs are not actually executed; they only return a failure result and cause no other side effects.<\/li>\n\n\n\n<li>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.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1>5. Real-Time Monitoring APIs<\/h1>\n\n\n\n<h2>5.1 Register an Arm Position Listener<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>registerArmPositionListener<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">StatusListener listener = new StatusListener() {\n    @Override\n    public void onStatusUpdate(String type, String data) {\n        try {\n            JSONObject json = new JSONObject(data);\n            String arm = json.optString(Definition.JSON_ARM);     \/\/ Definition.ARM_LEFT or Definition.ARM_RIGHT\n            int angle = json.optInt(Definition.JSON_ARM_ANGLE);\n            int speed = json.optInt(Definition.JSON_ARM_SPEED);\n            if (Definition.ARM_LEFT.equals(arm)) {\n                \/\/ Left arm position updated\n            } else if (Definition.ARM_RIGHT.equals(arm)) {\n                \/\/ Right arm position updated\n            }\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n    }\n};\n\nRobotApi.getInstance().registerArmPositionListener(listener);<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>listener<\/code>: <code>StatusListener<\/code> status listener\n<ul>\n<li><code>onStatusUpdate(String type, String data)<\/code>: <code>data<\/code> is a JSON string with the same fields as <code>getLeftArmPosition<\/code> \/ <code>getRightArmPosition<\/code>: <code>arm<\/code>, <code>angle<\/code>, <code>speed<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>Description:<\/strong><\/p>\n\n\n\n<ul>\n<li>After registration, you keep receiving position changes for the left and right arms until you call <code>unregisterArmPositionListener<\/code> to unregister<\/li>\n\n\n\n<li>To unregister, just pass the same <code>listener<\/code> instance; you do not need to keep a return value<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2>5.2 Unregister an Arm Position Listener<\/h2>\n\n\n\n<p><strong>Method name: <\/strong><code>unregisterArmPositionListener<\/code><\/p>\n\n\n\n<p><strong>How to call:<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RobotApi.getInstance().unregisterArmPositionListener(listener);<\/pre>\n\n\n\n<p><strong>Parameters:<\/strong><\/p>\n\n\n\n<ul>\n<li><code>listener<\/code>: the same <code>StatusListener<\/code> instance used when registering<\/li>\n<\/ul>\n\n\n\n<p><strong>Note:<\/strong> Be sure to unregister when the Activity \/ Fragment is destroyed to avoid memory leaks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1>6. Appendix<\/h1>\n\n\n\n<h2>6.1 Template for Checking Callback Results<\/h2>\n\n\n\n<p>For all motion control APIs, we recommend checking the result as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        if (result == Definition.RESULT_SUCCEED &amp;&amp; Definition.SUCCEED.equals(message)) {\n            \/\/ Success\n        } else {\n            \/\/ Failure: result may be an error code such as a timeout or hardware fault\n            \/\/ message describes the specific cause of the failure\n        }\n    }\n}<\/pre>\n\n\n\n<h2>6.2 Resource Usage<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>API<\/td><td>Resources used<\/td><\/tr><tr><td><code>armLeftRelMove<\/code> \/ <code>armLeftAbsMove<\/code> \/ <code>resetLeftArmToDefault<\/code> \/ <code>armLeftStop<\/code><\/td><td><code>RESOURCE_ARM_LEFT<\/code><\/td><\/tr><tr><td><code>armRightRelMove<\/code> \/ <code>armRightAbsMove<\/code> \/ <code>resetRightArmToDefault<\/code> \/ <code>armRightStop<\/code><\/td><td><code>RESOURCE_ARM_RIGHT<\/code><\/td><\/tr><tr><td><code>armDualRelMove<\/code> \/ <code>armDualAbsMove<\/code> \/ <code>resetBothArmsToDefault<\/code> \/ <code>armDualStop<\/code><\/td><td><code>RESOURCE_ARM_LEFT<\/code> + <code>RESOURCE_ARM_RIGHT<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>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.<\/p>\n\n\n\n<p><strong>How to avoid conflicts:<\/strong><\/p>\n\n\n\n<ul>\n<li><strong>Your app guarantees the call order<\/strong>: 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 <code>CommandListener.onResult<\/code> callback), no extra handling is needed.<\/li>\n\n\n\n<li><strong>When call timing cannot be controlled<\/strong>: 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.<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Example: stop the left arm before starting a left arm motion\nRobotApi.getInstance().armLeftStop(0);\nRobotApi.getInstance().armLeftAbsMove(0, 90, 30, new CommandListener() {\n    @Override\n    public void onResult(int result, String message, String extraData) {\n        \/\/ Handle the result\n    }\n});\n\n\/\/ The same applies to both arms\nRobotApi.getInstance().armDualStop(0);\nRobotApi.getInstance().armDualAbsMove(0, 0, 0, 30, listener);<\/pre>\n\n\n\n<h2>6.3 Complete Example<\/h2>\n\n\n\n<p>For a complete example, see the following class in the RobotSample project: <code>com.ainirobot.robotos.fragment.ArmControlFragment<\/code><\/p>\n\n\n\n<p>This sample covers all the API calls listed in this document.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8217;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 [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"closed","template":"","format":"standard","meta":[],"lsvr_kba_cat":[6],"lsvr_kba_tag":[],"_links":{"self":[{"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/lsvr_kba\/971"}],"collection":[{"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/lsvr_kba"}],"about":[{"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/types\/lsvr_kba"}],"author":[{"embeddable":true,"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/comments?post=971"}],"version-history":[{"count":1,"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/lsvr_kba\/971\/revisions"}],"predecessor-version":[{"id":973,"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/lsvr_kba\/971\/revisions\/973"}],"wp:attachment":[{"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/media?parent=971"}],"wp:term":[{"taxonomy":"lsvr_kba_cat","embeddable":true,"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/lsvr_kba_cat?post=971"},{"taxonomy":"lsvr_kba_tag","embeddable":true,"href":"https:\/\/doc.orionstar.com\/en\/wp-json\/wp\/v2\/lsvr_kba_tag?post=971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}