⚙ Development Guide
> SDK
> API
Usage Examples#
Initialize the client AirbotClient.__init__(host: str = "localhost", port: int = 50051)
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
def main():
client_arm = AirbotClient(host="localhost", port=50051) # Initializing AirbotClient automatically connects to the robotic arm server at localhost:50051
client_arm.close() # Release the connection and exit safely
if __name__ == "__main__":
main()
Get current robotic arm joint data get_arm_joint_state() -> Optional[ArmJointState]
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
try:
while True:
state = arm.get_arm_joint_state() # Get joint state
if state:
print(state)
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[INFO] Interrupted by user, exiting...")
if __name__ == "__main__":
main()
Get current robotic arm motor data get_arm_motor_state() -> Optional[ArmMotorState]
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
try:
while True:
state = arm.get_arm_motor_state() # Get motor state
if state:
print(state)
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[INFO] Interrupted by user, exiting...")
if __name__ == "__main__":
main()
Get current end-effector joint data get_eef_joint_state() -> Optional[EEFJointState]
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
try:
while True:
state = arm.get_eef_joint_state() # Get end-effector state
if state:
print(state)
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[INFO] Interrupted by user, exiting...")
if __name__ == "__main__":
main()
Get current end-effector motor data get_eef_motor_state() -> Optional[EEFMotorState]
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
try:
while True:
state = arm.get_eef_motor_state() # Get end-effector motor state
if state:
print(state)
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[INFO] Interrupted by user, exiting...")
if __name__ == "__main__":
main()
Get the current Cartesian-space pose of the robotic arm tool end get_end_pose() -> Optional[CartesianPose]
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
try:
while True:
state = arm.get_end_pose() # Get tool-end pose
if state:
print(state)
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[INFO] Interrupted by user, exiting...")
if __name__ == "__main__":
main()
Get the current operating state of the robotic arm server get_service_state() -> Optional[ServiceState]
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
try:
while True:
state = arm.get_service_state() # Get server data
if state:
print(state)
time.sleep(0.01)
except KeyboardInterrupt:
print("\n[INFO] Interrupted by user, exiting...")
if __name__ == "__main__":
main()
Get current robotic arm firmware information get_firmware_info() -> Optional[ArmFirmwareInfo]
from arm_sdk import AirbotClient, ArmControlOptions, Controller
def main():
with AirbotClient(port=50051) as arm: # Using a context manager for the Client lifecycle is recommended
try:
fw_info = arm.get_firmware_info() # Get firmware information
print(fw_info)
except KeyboardInterrupt:
pass
finally:
arm.close()
if __name__ == "__main__":
main()
Set the maximum robotic arm joint velocity and maximum motion percentage set_arm_speed(arm_speed: list[float]) -> bool
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
if __name__ == "__main__":
main()
Set the maximum end-effector velocity and maximum motion percentage set_eef_speed(eef_speed: list[float]) -> bool
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.set_eef_speed(
1.0 * math.pi
) # Set the maximum motor velocity to 1.0 * pi
if __name__ == "__main__":
main()
Set the robotic arm controller switch_controller(controller: Controller, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller # Import AirbotClient and the Controller enum from the arm_sdk package
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.switch_controller(
Controller.servo_control
) # Switch to the servo controller
if __name__ == "__main__":
main()
Enter gravity-compensation mode enter_gravity_compensation_mode(timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient # Import the AirbotClient class from the arm_sdk package
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.enter_gravity_compensation_mode() # Enter gravity-compensation mode
if __name__ == "__main__":
main()
Control the robotic arm in joint space move_joint(pos: float, options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(Controller.direct_control) # Switch to the forward controller
arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
pos = [1.0, -1.0, 1.0, 0.0, 0.0, 0.0] # Set the target position
options.eff = [8.0, 8.0, 8.0, 8.0, 8.0, 8.0] # Set the motor current threshold
arm.move_joint(pos, options) # Send the control command
if __name__ == "__main__":
main()
💡
optionsparameters required by each controller for robotic arm joint-space control
Controller Required optionsParameterNotes Controller.direct_controloptions.effMotor current threshold options.eef_effEnd-effector torque Controller.servo_controloptions.effMotor current threshold options.eef_posTarget end-effector position (if an end effector is present) options.eef_effEnd-effector current threshold (if an end effector is present) Controller.planning_control
See Appendix 4.3 for detailsoptions.effMotor current threshold options.sampling_timeTrajectory sampling period (seconds) options.allow_planning_timeMaximum planning time options.velocity_scaling_factorVelocity scaling factor options.acceleration_scaling_factorAcceleration scaling factor options.force_calc_linWhether to enable the interpolation strategy to force planning after linear planning fails options.lin_interpolate_numNumber of interpolation points after linear planning fails options.lin_hard_thresholdHard velocity threshold for linear-planning failure options.circ_is_centerWhether the circular-trajectory path point is the center of the circle options.allow_blend_failAllow blending failure options.motion_typePlanning mode; accepted strings are PTP and RRT_CONNECT Controller.mit_controloptions.kpMotor kp options.kdMotor kd options.torqueMotor torque options.eef_torqueEnd-effector torque options.eef_kpEnd-effector kp options.eef_kdEnd-effector kd
Control the end effector in joint space move_eef(pos: float, options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(Controller.direct_control) # Switch to the forward controller
arm.set_eef_speed(
math.pi
) # Set the maximum motor velocity to 1.0 * pi
pos = 0.07 # Set the target position
options.eef_eff = 6.0 # Set the motor current threshold
arm.move_eef(pos, options) # Send the control command
if __name__ == "__main__":
main()
Control the robotic arm in Cartesian space move_end_pose(pos: CartesianPose, options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions, CartesianPose
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(Controller.direct_control) # Switch to the forward controller
arm.set_arm_speed(
[math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
pos = CartesianPose((0.4, 0.0, 0.3), (0.0, 0.0, 0.0, 1.0)) # Set the target pose
options.eff = [8.0, 8.0, 8.0, 8.0, 8.0, 8.0] # Set the motor current threshold
arm.move_end_pose(pos, options) # Send the control command
if __name__ == "__main__":
main()
Plan and control a Cartesian-space linear trajectory move_end_pose_linear(start: CartesianPose, target: CartesianPose, options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions, CartesianPose
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(
Controller.planning_control
) # Switch to the joint_trajectory controller
arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
start = CartesianPose((0.4, 0.0, 0.3), (0.0, 0.0, 0.0, -1.0)) # Set the start pose
target = CartesianPose((0.4, 0.1, 0.3), (0.0, 0.0, 0.0, -1.0)) # Set the target pose
options.eff = [8.0, 8.0, 8.0, 8.0, 8.0, 8.0] # Set the motor current threshold
options.sampling_time = 0.01 # Set the trajectory sampling period
options.allow_planning_time = 5.0 # Set the maximum planning time
options.velocity_scaling_factor = 0.1 # Set the velocity scaling factor
options.acceleration_scaling_factor = 1.0 # Set the acceleration scaling factor
options.force_calc_lin = False # Set force_calc_lin
options.lin_hard_threshold = 10.0 # Set the hard velocity threshold at singularities
options.lin_interpolate_num = 5 # Set the number of linear interpolation points at singularities
arm.move_end_pose_linear(start, target, options) # Send the control command
if __name__ == "__main__":
main()
Plan and control a Cartesian-space circular trajectory move_end_pose_circle(start: CartesianPose, path: CartesianPose, target: CartesianPose, options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions, CartesianPose
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(
Controller.planning_control
) # Switch to the joint_trajectory controller
arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
start = CartesianPose((0.4, -0.2, 0.2), (0.0, 0.0, 0.0, 1.0)) # Set the start pose
path = CartesianPose((0.6, 0.0, 0.2), (0.0, 0.0, 0.0, 1.0)) # Set the path-point pose
target = CartesianPose((0.2, 0.3, 0.1), (0.0, 0.0, 0.0, 1.0)) # Set the target pose
options.eff = [8.0, 8.0, 8.0, 8.0, 8.0, 8.0] # Set the motor current threshold
options.sampling_time = 0.01 # Set the trajectory sampling period
options.allow_planning_time = 5.0 # Set the maximum planning time
options.velocity_scaling_factor = 0.1 # Set the velocity scaling factor
options.acceleration_scaling_factor = 1.0 # Set the acceleration scaling factor
options.circ_is_center = False # Set whether path in CIRC is the center of the arc's circle
arm.move_end_pose_circle(start, path, target, options) # Send the control command
if __name__ == "__main__":
main()
Plan and control multiple robotic arm joint-space waypoints move_joint_waypoints(waypoints: list[list[float]], options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions, CartesianPose
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(
Controller.planning_control
) # Switch to the joint_trajectory controller
arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
waypoints = []
waypoints.append([1.0, -1.0, 1.0, 0.0, 0.0, 0.0]) # Set waypoints1
waypoints.append([0.0, -1.0, 1.0, 0.0, 0.0, 0.0]) # Set waypoints2
options.min_blend_radius = 0.001 # Set the minimum blending radius
options.sampling_time = 0.01 # Set the trajectory sampling period
options.allow_planning_time = 5.0 # Set the maximum planning time
options.velocity_scaling_factor = 0.1 # Set the velocity scaling factor
options.acceleration_scaling_factor = 1.0 # Set the acceleration scaling factor
options.allow_blend_fail = True # Set allow_blend_fail
arm.move_joint_waypoints(waypoints, options)
if __name__ == "__main__":
main()
Plan and control multiple robotic arm Cartesian-space waypoints move_end_pose_waypoints(waypoints: list[CartesianPose], options: ArmControlOptions, timeout_ms: int = 1000) -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions, CartesianPose
import math
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
options = ArmControlOptions() # Initialize a default ArmControlOptions data structure
arm.switch_controller(
Controller.planning_control
) # Switch to the joint_trajectory controller
arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum motor velocity to 1.0 * pi
waypoints = []
waypoints.append(
CartesianPose(position=(0.4, 0.0, 0.3), orientation=(0.0, 0.0, 0.0, 1.0))
) # Set waypoints1
waypoints.append(
CartesianPose(position=(0.4, 0.0, 0.1), orientation=(0.0, 0.0, 0.0, 1.0))
) # Set waypoints2
waypoints.append(
CartesianPose(position=(0.4, 0.0, 0.5), orientation=(0.0, 0.0, 0.0, 1.0))
) # Set waypoints3
options.min_blend_radius = 0.005 # Set the minimum blending radius
options.sampling_time = 0.01 # Set the trajectory sampling period
options.allow_planning_time = 5.0 # Set the maximum planning time
options.velocity_scaling_factor = 0.1 # Set the velocity scaling factor
options.acceleration_scaling_factor = 1.0 # Set the acceleration scaling factor
options.allow_blend_fail = True # Set allow_blend_fail
arm.move_end_pose_waypoints(waypoints, options)
if __name__ == "__main__":
main()
Return the robotic arm to zero return_zero() -> bool
from arm_sdk import AirbotClient, Controller, ArmControlOptions, CartesianPose
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Acquire control ownership
arm.return_zero() # Send the return-to-zero command
if __name__ == "__main__":
main()
Emergency-stop and recover the robotic arm set_arm_emergency_stop(mode: bool) -> bool
from arm_sdk import AirbotClient
import time
def main():
with AirbotClient() as arm: # Using a context manager for the Client lifecycle is recommended
arm.acquire_control() # Emergency stop requires control ownership
arm.set_arm_emergency_stop(mode=True) # Emergency stop
time.sleep(5.0)
arm.set_arm_emergency_stop(mode=False) # Recover control
if __name__ == "__main__":
main()
Clear robotic arm motor error codes clear_arm_motor_err() -> bool
from arm_sdk import AirbotClient, Controller
import time
def main():
client = AirbotClient(port=50051) # Initializing AirbotClient automatically connects to the robotic arm server; the default host is localhost and the server address and port are localhost:50051
client.acquire_control() # Control ownership is required
client.clear_arm_motor_err() # Clear robotic arm motor error codes
if __name__ == "__main__":
main()
Clear end-effector motor error codes clear_eef_motor_err() -> bool
from arm_sdk import AirbotClient, Controller
import time
def main():
client = AirbotClient(port=50051) # Initializing AirbotClient automatically connects to the robotic arm server; the default host is localhost and the server address and port are localhost:50051
client.acquire_control() # Control ownership is required
client.clear_eef_motor_err() # Clear end-effector motor error codes
if __name__ == "__main__":
main()
Use a utility function for one-to-one control
from arm_sdk import AirbotClient, Controller, ArmControlOptions, utilities
import time
def main():
port_play: int = 50051,
port_replay: int = 50052,
pendant_model: str = "REPLAY",
gripper_model: str = "G2",
eff: tuple[float, float, float, float, float, float] = (
8.0,
8.0,
8.0,
8.0,
8.0,
8.0,
),
hz: float = 50.0,
duration_s: float = 0.0,
client_arm = AirbotClient(port=port_play)
client_replay = AirbotClient(port=port_replay)
dt = 1.0 / max(hz, 1.0)
try:
ok = client_arm.acquire_control()
if not ok:
raise RuntimeError(
"Failed to acquire control for PLAY (another client may be controlling)."
)
client_arm.switch_controller(
Controller.servo_control
) # Switch to the servo_control controller
client_arm.set_arm_speed(
[1.0 * math.pi] * 6
) # Set the maximum robotic arm motor velocity to 1.0 * pi
client_arm.set_eef_speed(
1.0 * math.pi
) # Set the maximum end-effector motor velocity to 1.0 * pi
options_arm = ArmControlOptions()
options_arm.eff = list(eff)
t0 = time.time()
while True:
if duration_s > 0.0 and (time.time() - t0) >= duration_s:
break
states = client_replay.get_arm_joint_state()
pos = states.angles
states_eef = client_replay.get_eef_joint_state()
pendant_pos = states_eef.eef_pos
gripper_pos = utilities.map_pendant_to_gripper(
pos=pendant_pos,
from_pendant=pendant_model,
to_gripper=gripper_model,
)
print("Gripper pos: ", gripper_pos)
print("EEF pos: ", pendant_pos)
options_arm.eef_pos = gripper_pos
options_arm.eef_eff = 10.0
client_arm.move_joint(list(pos), options_arm)
time.sleep(dt)
except KeyboardInterrupt:
pass
finally:
client_arm.close()
client_replay.close()
if __name__ == "__main__":
main()