6. Quick Start for Secondary Development
6. Quick Start for Secondary Development
Section titled “6. Quick Start for Secondary Development”This chapter will demonstrate how to quickly run your first secondary development program through a few simple example programs, providing a basic understanding.
6.1 Prerequisites
Section titled “6.1 Prerequisites”Secondary development programs are recommended to be deployed on a personal PC or an additional computing board. The system requirements are Ubuntu 22.04 installed with ROS2 Humble and Fastdds. Connect via Ethernet directly to the robot’s debugging port (192.168.2.50) or the reserved to_orin port (192.168.100.100). In this tutorial, we will use the example of a personal PC connected via Ethernet to the debugging port (192.168.2.50). If the network port is different, simply modify the IP address accordingly.
6.1.1 Wired Connection to Debugging Port
Section titled “6.1.1 Wired Connection to Debugging Port”First, set the IP address of the PC’s wired network port to a static IP of 192.168.2.150, then connect one end of the Ethernet cable to the PC’s wired network port and the other end to the robot’s right debugging port at the back.
After the robot is powered on, you should be able to ping 192.168.2.50 from your personal PC.
ping 192.168.2.506.1.2 Adding DDS Configuration Whitelist
Section titled “6.1.2 Adding DDS Configuration Whitelist”First, SSH into the robot, back up the relevant configuration files, and then modify them.
ssh agi@192.168.2.50cd /agibot/software/v0/entry/bin/cfg/cp ros_dds_configuration.xml ros_dds_configuration.xml.originalcp privileged_ros_dds_configuration.xml privileged_ros_dds_configuration.xml.originalThen use vim to modify the above two configuration files.
vim ros_dds_configuration.xmlvim privileged_ros_dds_configuration.xmlHere is only the modified segment (the same for both files):
<interfaceWhiteList> <address>127.0.0.1</address> <address>10.42.0.100</address> <address>192.168.2.50</address> </interfaceWhiteList>After modifying, restart the robot. Once the robot has completed initialization, execute the following command on your personal PC. You should be able to see many topics and echo the corresponding topics successfully.
source /opt/ros/humble/setup.bashexport ROS_DOMAIN_ID=232export ROS_LOCALHOST_ONLY=0ros2 topic list --no-daemonros2 topic echo /motion/control/arm_joint_state6.2 Example of Running a Battery Level Retrieval Program
Section titled “6.2 Example of Running a Battery Level Retrieval Program”This example uses an HTTP request to get the robot’s BMS status, relying only on the wired connection described in Section 5.1.1. Additionally, if the robot and PC are connected to the same local network, you can access the service using the robot’s IP address in the local network.
Run the following script directly on your personal PC:
curl -i -H 'content-type:application/json' \ -H 'timeout: 1000' -X POST 'http://192.168.2.50:56421/rpc/aimdk.protocol.HalBmsService/GetBmsState' \ -d '{}'The returned result should be as follows (the specific field meanings are explained in detail in the subsequent interface section):
HTTP/1.1 200 OKAccess-Control-Allow-Origin: *Content-Type: application/jsonContent-Length: 709
{"data":{"ver":{"hardware_major":49,"hardware_minor":48,"hardware_revision":49,"software_major":53,"software_minor":48,"software_revision":49},"voltage":54200,"current":28,"power":0,"temperature":285,"capacity":14090,"charge":100,"power_supply_health":"PowerSupplyHealth_GOOD","power_supply_status":"PowerSupplyStatus_IDEL","cycles_num":100,"cycles_capacity":10,"abnormal_state":"PowerAbnormalStatus_OVERVOLTAGE","charger_state":"ChargerConnected","bms_state":"BatteryStatus_Connected","max_current":29568,"battery_firmware_type":"BatteryFirmwareType_OLD","battery_key_state":"BatteryKeyStatus_SHORT_CURCUIT","battery_pack_state":"BatteryPackStatus_NORMAL","battery_comm_state":"BatteryCommunication_NORMAL"}}6.3 Example of Retrieving Robot Upper Limb Joint Status
Section titled “6.3 Example of Retrieving Robot Upper Limb Joint Status”This example uses a ROS2 program to subscribe to the corresponding topic on the robot, relying on all the prerequisites in Section 5.1. It is not recommended to use a local wireless network to obtain ROS2 topics and other data (due to high and uncontrollable latency, and potential interference issues with multiple robots, which significantly reduces security).
Save the following code on your PC as joint_state.py.
#!/usr/bin/env python3import rclpyfrom rclpy.node import Nodefrom rclpy.qos import QoSHistoryPolicy, QoSProfile, QoSReliabilityPolicyfrom sensor_msgs.msg import JointState
class JointStateSubscriber(Node): def __init__(self, topic_name: str): super().__init__("joint_state_subscriber")
self.topic_name = topic_name
qos_profile = QoSProfile( history=QoSHistoryPolicy.KEEP_LAST, depth=10, reliability=QoSReliabilityPolicy.BEST_EFFORT )
self.subscription = self.create_subscription(JointState, topic_name, self.listener_callback, qos_profile)
def listener_callback(self, msg: JointState): self.get_logger().info(f"=== Received {self.topic_name} ===") self.get_logger().info(f" header: {msg.header}") self.get_logger().info(f" name: {msg.name}") self.get_logger().info(f" position: {msg.position}") self.get_logger().info(f" velocity: {msg.velocity}") self.get_logger().info(f" effort: {msg.effort}")
def main(args=None): rclpy.init(args=args) # Arm Joint Status: /motion/control/arm_joint_state # Neck Joint Status: /motion/control/neck_joint_state joint_state_node = JointStateSubscriber("/motion/control/arm_joint_state") try: rclpy.spin(joint_state_node) except KeyboardInterrupt: pass finally: joint_state_node.destroy_node() rclpy.shutdown()
if __name__ == "__main__": main()Then execute the following command:
source /opt/ros/humble/setup.bashexport ROS_DOMAIN_ID=232export ROS_LOCALHOST_ONLY=0python3 joint_state.py6.4 Example of Controlling Head Shaking Interface
Section titled “6.4 Example of Controlling Head Shaking Interface”We provide both HTTP and ROS2 interfaces for head control.
6.4.1 Head Shaking HTTP Example
Section titled “6.4.1 Head Shaking HTTP Example”Using the HTTP interface for control is relatively simple. Here is the script:
#!/usr/bin/env python3
## Function: Set head joint values
import requestsfrom datetime import datetime
# Generate request headerdef create_header(): now = datetime.utcnow() header = { "timestamp": { "seconds": int(now.timestamp()), "nanos": now.microsecond * 1000, "ms_since_epoch": int(now.timestamp() * 1000), }, "control_source": "ControlSource_SAFE", } return header
def create_neck_data(): neck_data = { "shake": { "name": "idx27_head_joint1", "position": 0.5, "velocity": 0.0, "effort": 0.0, }, "nod": { "name": "idx28_head_joint2", "position": 0.5, "velocity": 0.0, "effort": 0.0, }, }
return neck_data
def service_call(): # Send service request
url = ( "http://192.168.2.50:56322/rpc/aimdk.protocol.McMotionService/SetNeckCommand" ) headers = {"Content-Type": "application/json", "timeout": "60000"}
payload = {"header": create_header(), "data": create_neck_data()}
response = requests.Session().post(url, headers=headers, json=payload)
return response
# Main program entry pointdef main(): response = service_call() print(response.text) requests.Session().close()
if __name__ == "__main__": main()Note that idx28_head_joint2 is invalid but must be retained. The Lite model robot has only one degree of freedom for head shaking, where velocity and effort are invalid, and only position will take effect.
6.4.2 Head Shaking ROS2 Example
Section titled “6.4.2 Head Shaking ROS2 Example”Using the ROS2 interface to call the head shaking program requires some prerequisites:
-
First, switch the state machine of the motion control module to the RL_LOCOMOTION_ARM_EXT_JOINT_SERVO state. This can be done on AimMaster, and the detailed interface explanation will also cover how to switch using program interfaces.
-
Call the following command to release the control of the upper body joints by the action playback module.
Terminal window curl -i \-H 'content-type:application/json' \-H 'timeout: 1000' \-X POST http://192.168.2.50:56444/rpc/aimdk.protocol.MotionCommandService/DisableMotionPlayer \-d '{}'
Next, save the following script as neck.py:
#!/usr/bin/env python3import rclpyfrom rclpy.node import Nodefrom rclpy.qos import QoSHistoryPolicy, QoSProfile, QoSReliabilityPolicyfrom sensor_msgs.msg import JointState
class NeckPublisher(Node): def __init__(self, neck_topic_name: str): super().__init__("neck_publisher")
qos_profile = QoSProfile( history=QoSHistoryPolicy.KEEP_LAST, depth=10, reliability=QoSReliabilityPolicy.BEST_EFFORT, )
self.publisher = self.create_publisher(JointState, neck_topic_name, qos_profile)
timer_period = 0.02 self.timer = self.create_timer(timer_period, self.timer_callback)
def timer_callback(self): msg = JointState() msg.header.stamp = self.get_clock().now().to_msg()
# Position control only is supported. # The first value represents head yaw, ranging from -0.785 to 0.785. # The second value is unused but still needs to be provided. msg.name = ["idx27_head_joint1", "idx28_head_joint2"] msg.position = [0.5, 0.5]
# The following fields have no actual effect but must be set; otherwise, they may cause the motion controller to crash msg.velocity = [0.0, 0.0] msg.effort = [0.0, 0.0]
self.publisher.publish(msg)
def main(args=None): rclpy.init(args=args) neck_publisher = NeckPublisher("/motion/control/neck_joint_command") try: rclpy.spin(neck_publisher) except KeyboardInterrupt: pass finally: neck_publisher.destroy_node() rclpy.shutdown()
if __name__ == "__main__": main()Then run:
source /opt/ros/humble/setup.bashexport ROS_DOMAIN_ID=232export ROS_LOCALHOST_ONLY=0python3 neck.py