5. 二次开发程序部署
5. 二次开发程序部署
二次开发程序一般推荐两种部署方式,分别为本体 HDU 、 ADU 部署和三方工控机部署,本体部署硬件上较为简洁,适用于二开程序资源占用不大的情况,如需占用较多资源则需将 HDU 、 ADU 上原有的部分程序关闭释放 CPU/GPU 等资源;三方工控机部署则需使用额外的传输方式传输数据,如http传输协议,好处是原有机上软件程序功能都可以保留,同时又有相对充足的算力资源。
5.1 本体 HDU 、 ADU 部署
推荐的程序部署目录为 /agibot/data/home/agi/Desktop,机上磁盘清理模块会定时清理空间,该文件夹为白名单文件夹不会被清理。
mkdir -p /agibot/data/home/agi/Desktop
如遇权限问题,可考虑使用如下命令进行文件夹创建
sudo mkdir -p /agibot/data/home/agi/Desktop
sudo chown -R agi:agi /agibot/data/home/agi
5.1.1 Python 虚拟环境
使用 Python 语言进行二次开发只需创建虚拟环境即可,可使用 conda/venv 等工具创建虚拟环境,其中 venv 为系统自带工具,conda 安装可以参考 miniconda 安装官方文档。
5.1.2 C++ 开发部署
使用 C++ 编译型语言,若使用x86_64架构开发程序,需要在x86_64架构的本地电脑上进行编译,编译完成后将可执行文件上传到 HDU 、 ADU 上即可。若使用arm64架构开发程序,需要在arm64架构的本地电脑上进行编译型语言进行二次开发。 若在x86_64架构的本地电脑上进行编译,需要安装arm64架构的编译环境,具体步骤如下
1、安装交叉编译工具链
sudo apt install \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu \
binutils-aarch64-linux-gnu
2、在x86_64架构的本地电脑上构建目标设备的 sysrootA3u ,否则可能出现头文件或库版本不匹配,具体步骤如下
(1)可在工程目录下创建sysrootA3u目录
(2)从目标机器上拷贝/lib,/usr/lib, /usr/include ,/usr/local,/usr/share ,/opt/ros目录到 sysrootA3u 目录下
scp -r agi@172.23.20.140:/lib sysrootA3u/
scp -r agi@172.23.20.140:/usr/lib sysrootA3u/
scp -r agi@172.23.20.140:/usr/include sysrootA3u/
scp -r agi@172.23.20.140:/usr/local sysrootA3u/
scp -r agi@172.23.20.140:/usr/share sysrootA3u/
scp -r agi@172.23.20.140:/opt/ros sysrootA3u/
(3)下载目标设备上的第三方依赖库
3、下面以wake_up程序为例,展示如何在x86_64架构的本地电脑上进行交叉编译。
(1)构建工程目录
temp/
├── build-a3u/
│ └── wakeup_subscriber
├── prebuilt/
│
├── sysrootA3u/
│ ├── lib/
│ │
│ ├── opt/
│ │ └── ros/
│ └── usr/
│ ├── include/
│ ├── lib/
│ ├── local/
│ └── share/
│
├── CMakeLists.txt
├── main.cpp
└── start_wakeup_subscriber.sh
(2)依赖安装
程序依赖protobuf库,需安装protobuf编译库,拷贝的sysrootA3u中仅含有运行库,下载链接
https://deb.debian.org/debian/pool/main/p/protobuf/libprotobuf-dev_3.21.12-3+deb12u1_amd64.deb
解压到sysrootA3u指定目录,不安装到系统中
dpkg-deb -x libprotobuf-dev_3.21.12-3+deb12u1_amd64.deb /agibot/data/home/agi/Desktop/temp/sysrootA3u
(3)wake_up demo程序
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <google/protobuf/util/json_util.h>
#include <rclcpp/rclcpp.hpp>
#include <ros2_plugin_proto/msg/ros_msg_wrapper.hpp>
#include "aimdk/protocol/agent/agent_data.pb.h"
namespace
{
constexpr char kTopic[] =
"/agent/wakeup/pb_3Aaimdk_2Eprotocol_2EWakeUpResult";
}
class WakeUpSubscriber final : public rclcpp::Node
{
public:
WakeUpSubscriber()
: Node("wakeup_result_subscriber")
{
const auto qos = rclcpp::QoS(rclcpp::KeepLast(10))
.reliable()
.durability_volatile();
subscription_ =
create_subscription<ros2_plugin_proto::msg::RosMsgWrapper>(
kTopic,
qos,
[this](
const ros2_plugin_proto::msg::RosMsgWrapper::ConstSharedPtr msg) {
wakeup_callback(msg);
});
RCLCPP_INFO(get_logger(), "已开始订阅 WakeUpResult: %s", kTopic);
}
private:
void wakeup_callback(
const ros2_plugin_proto::msg::RosMsgWrapper::ConstSharedPtr &msg)
{
if (msg->serialization_type != "pb") {
RCLCPP_WARN(
get_logger(),
"收到不支持的序列化类型: %s",
msg->serialization_type.c_str());
return;
}
if (msg->data.size() >
static_cast<std::size_t>(std::numeric_limits<int>::max())) {
RCLCPP_ERROR(get_logger(), "WakeUpResult 数据长度超出解析限制");
return;
}
aimdk::protocol::WakeUpResult wakeup_result;
if (!wakeup_result.ParseFromArray(
msg->data.data(),
static_cast<int>(msg->data.size()))) {
RCLCPP_ERROR(get_logger(), "解析 WakeUpResult 数据失败");
return;
}
google::protobuf::util::JsonPrintOptions options;
options.add_whitespace = true;
options.preserve_proto_field_names = true;
std::string json;
const auto status = google::protobuf::util::MessageToJsonString(
wakeup_result,
&json,
options);
if (!status.ok()) {
RCLCPP_ERROR(
get_logger(),
"WakeUpResult 转换为 JSON 失败: %s",
status.ToString().c_str());
return;
}
RCLCPP_INFO(get_logger(), "WakeUpResult: %s", json.c_str());
}
rclcpp::Subscription<
ros2_plugin_proto::msg::RosMsgWrapper>::SharedPtr subscription_;
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
const auto node = std::make_shared<WakeUpSubscriber>();
RCLCPP_INFO(
node->get_logger(),
"正在监听 WakeUpResult,按 Ctrl+C 退出...");
rclcpp::spin(node);
RCLCPP_INFO(node->get_logger(), "退出中...");
rclcpp::shutdown();
return 0;
}
(4)CMakeLists.txt文件
cmake_minimum_required(VERSION 3.22)
# 交叉编译器必须是能在 x86 主机运行的程序,不能使用 sysroot 内的 ARM64 编译器包装程序。
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
project(wakeup_subscriber LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(PROJECT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
set(SYSROOT "${PROJECT_ROOT}/sysrootA3u")
set(AARCH64_PREBUILT "${PROJECT_ROOT}/prebuilt/ros2_plugin_proto_aarch64")
set(AARCH64_TRIPLE "aarch64-linux-gnu")
set(CMAKE_SYSROOT "${SYSROOT}")
set(CMAKE_FIND_ROOT_PATH
"${AARCH64_PREBUILT}"
"${SYSROOT}"
)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
list(PREPEND CMAKE_PREFIX_PATH
"${SYSROOT}/opt/ros/jazzy"
"${AARCH64_PREBUILT}"
)
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(ros2_plugin_proto REQUIRED)
set(Protobuf_INCLUDE_DIR "${SYSROOT}/usr/include")
set(Protobuf_LIBRARY "${SYSROOT}/usr/lib/${AARCH64_TRIPLE}/libprotobuf.so.32")
find_package(Protobuf 3.21.12 REQUIRED)
add_executable(wakeup_subscriber
main.cpp
"${PROJECT_ROOT}/prebuilt/generated/aimdk/protocol/agent/agent_data.pb.cc"
"${PROJECT_ROOT}/prebuilt/generated/aimdk/protocol/common/control_source.pb.cc"
"${PROJECT_ROOT}/prebuilt/generated/aimdk/protocol/common/header.pb.cc"
"${PROJECT_ROOT}/prebuilt/generated/aimdk/protocol/common/rpc.pb.cc"
"${PROJECT_ROOT}/prebuilt/generated/aimdk/protocol/common/timestamp.pb.cc"
)
target_include_directories(wakeup_subscriber SYSTEM PRIVATE
"${SYSROOT}/usr/include/${AARCH64_TRIPLE}"
)
target_include_directories(wakeup_subscriber PRIVATE
"${PROJECT_ROOT}/prebuilt/generated"
"${AARCH64_PREBUILT}/include"
)
target_link_libraries(wakeup_subscriber PRIVATE
protobuf::libprotobuf
rclcpp::rclcpp
ros2_plugin_proto::ros2_plugin_proto__rosidl_typesupport_cpp
)
set_target_properties(wakeup_subscriber PROPERTIES
BUILD_RPATH "${AARCH64_PREBUILT}/lib;${SYSROOT}/opt/ros/jazzy/lib;${SYSROOT}/usr/lib/${AARCH64_TRIPLE};${SYSROOT}/lib/${AARCH64_TRIPLE}"
INSTALL_RPATH "$ORIGIN/../lib"
)
install(TARGETS wakeup_subscriber
DESTINATION bin
)
(5)编译程序
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j"$(nproc)"
(6)运行程序 程序的运行依赖prebuilt/audio_msgs_proto_aarch64/lib下的库文件,可通过以下命令运行
source /agibot/software/v0/entry/env/env.sh
source /agibot/data/home/agi/Desktop/prebuilt/ros2_plugin_proto_aarch64/share/ros2_plugin_proto/local_setup.bash
./build-a3u/wakeup_subscriber
5.2 有线连接机器人设备
普通外设连接
机器人头部具有与HDU设备连接的Type-C接口,用于与HDU设备进行通信。用户可将机器人头部的Type-C接口连接到其他设备上,即可实现与HDU设备的通信。

第三方工控机连接
如若需要在有线连接机器人设备时进行 ROS2 通信,需要进行如下操作:
首先将第三方工控机连接到机器人背部调试仓网口处,修改工控机网段为10.42.10.x,然后将 HDU 上的 /agibot/software/v0/entry/cfg/ros_dds_configuration.xml 拷贝到本地电脑,在本地电脑上执行 ROS2 程序或使用 ros2 命令前执行如下命令(其中 /path/to 需要替换为真实路径)
export ROS_DOMAIN_ID=232
export FASTRTPS_DEFAULT_PROFILES_FILE=/path/to/ros_dds_configuration.xml