From 35abb286dfd6a93aa9a388b7788490e2bdad537f Mon Sep 17 00:00:00 2001 From: li-shihao-code <2469171725@qq.com> Date: Thu, 26 Feb 2026 13:52:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=AE=8C=E5=96=84=E4=BA=86=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E6=A0=87=E5=AE=9A=E8=BD=A6=E9=97=B4=E7=9A=84=E7=8E=AF?= =?UTF-8?q?=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agv_calib_brain/CMakeLists.txt | 49 ++++++++++++++++ agv_calib_brain/package.xml | 18 ++++++ .../proto}/agv_calib_chassis.proto | 0 .../proto}/agv_calib_control.proto | 0 .../proto}/agv_calib_sensor.proto | 0 agv_calib_brain/src/brain_node.cpp | 57 +++++++++++++++++++ agv_calib_eu/main.cpp | 47 +++++++++++++++ 7 files changed, 171 insertions(+) create mode 100644 agv_calib_brain/CMakeLists.txt create mode 100644 agv_calib_brain/package.xml rename {msg => agv_calib_brain/proto}/agv_calib_chassis.proto (100%) rename {msg => agv_calib_brain/proto}/agv_calib_control.proto (100%) rename {msg => agv_calib_brain/proto}/agv_calib_sensor.proto (100%) create mode 100644 agv_calib_brain/src/brain_node.cpp create mode 100644 agv_calib_eu/main.cpp diff --git a/agv_calib_brain/CMakeLists.txt b/agv_calib_brain/CMakeLists.txt new file mode 100644 index 0000000..21abe94 --- /dev/null +++ b/agv_calib_brain/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.8) +project(agv_calib_brain) + +# 1. 寻找 ROS 2 依赖 +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) + +# 2. 寻找 gRPC 和 Protobuf 依赖 +find_package(Protobuf REQUIRED) +find_package(gRPC REQUIRED) + +# 3. 设置 Proto 文件路径与自动生成的源码输出路径 +set(PROTO_DIR "${CMAKE_CURRENT_SOURCE_DIR}/proto") +set(PROTO_FILE "${PROTO_DIR}/agv_calib_control.proto") +set(PROTO_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/grpc_gen") +file(MAKE_DIRECTORY ${PROTO_OUT_DIR}) + +# 4. 自动生成 gRPC C++ 源码 (每次 colcon build 自动执行) +find_program(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) +add_custom_command( + OUTPUT "${PROTO_OUT_DIR}/agv_calib_control.pb.cc" + "${PROTO_OUT_DIR}/agv_calib_control.pb.h" + "${PROTO_OUT_DIR}/agv_calib_control.grpc.pb.cc" + "${PROTO_OUT_DIR}/agv_calib_control.grpc.pb.h" + COMMAND protoc + ARGS --proto_path="${PROTO_DIR}" + --cpp_out="${PROTO_OUT_DIR}" + --grpc_out="${PROTO_OUT_DIR}" + --plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN_EXECUTABLE}" + "${PROTO_FILE}" + DEPENDS "${PROTO_FILE}" +) + +# 5. 将生成的网络源码打包成独立的 C++ 库 +add_library(agv_grpc_proto_lib SHARED + "${PROTO_OUT_DIR}/agv_calib_control.pb.cc" + "${PROTO_OUT_DIR}/agv_calib_control.grpc.pb.cc" +) +target_include_directories(agv_grpc_proto_lib PUBLIC "${PROTO_OUT_DIR}") +target_link_libraries(agv_grpc_proto_lib gRPC::grpc++ protobuf::libprotobuf) + +# 6. 编译您的 ROS 2 节点主程序 +add_executable(brain_node src/brain_node.cpp) +ament_target_dependencies(brain_node rclcpp) +# 🚨 必须链接刚才生成的 gRPC 库 +target_link_libraries(brain_node agv_grpc_proto_lib) + +install(TARGETS brain_node DESTINATION lib/${PROJECT_NAME}) +ament_package() \ No newline at end of file diff --git a/agv_calib_brain/package.xml b/agv_calib_brain/package.xml new file mode 100644 index 0000000..3b8a58d --- /dev/null +++ b/agv_calib_brain/package.xml @@ -0,0 +1,18 @@ + + + + agv_calib_brain + 0.0.0 + TODO: Package description + nvidia + TODO: License declaration + + ament_cmake + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/msg/agv_calib_chassis.proto b/agv_calib_brain/proto/agv_calib_chassis.proto similarity index 100% rename from msg/agv_calib_chassis.proto rename to agv_calib_brain/proto/agv_calib_chassis.proto diff --git a/msg/agv_calib_control.proto b/agv_calib_brain/proto/agv_calib_control.proto similarity index 100% rename from msg/agv_calib_control.proto rename to agv_calib_brain/proto/agv_calib_control.proto diff --git a/msg/agv_calib_sensor.proto b/agv_calib_brain/proto/agv_calib_sensor.proto similarity index 100% rename from msg/agv_calib_sensor.proto rename to agv_calib_brain/proto/agv_calib_sensor.proto diff --git a/agv_calib_brain/src/brain_node.cpp b/agv_calib_brain/src/brain_node.cpp new file mode 100644 index 0000000..d6a9e1f --- /dev/null +++ b/agv_calib_brain/src/brain_node.cpp @@ -0,0 +1,57 @@ +#include +#include +// 包含刚才 CMake 自动生成的头文件 +#include "agv_calib_control.grpc.pb.h" + +using namespace agv::calibration::control; + +class BrainNode : public rclcpp::Node { +public: + BrainNode() : Node("calib_brain_node") { + // 1. 拨号连接到 Windows 车端 (请替换为车端在 Wi-Fi 6 下的真实 IP) + std::string target_ip = "192.168.1.100:50051"; + + // 🚨 Wi-Fi 防断联核心:强制开启 gRPC 保活机制 (KeepAlive) + grpc::ChannelArguments args; + args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 2000); // 每 2 秒发一次 TCP 心跳探活 + args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 1000); // 1 秒没回音即认为掉线 + args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1); + + auto channel = grpc::CreateCustomChannel(target_ip, grpc::InsecureChannelCredentials(), args); + stub_ = AgvCalibControlService::NewStub(channel); + + RCLCPP_INFO(this->get_logger(), "连接车端 %s 中...", target_ip.c_str()); + + // 2. 发起夺权指令测试 + send_control_mode_request(); + } + +private: + std::unique_ptr stub_; + + void send_control_mode_request() { + ModeRequest request; + request.set_target_mode(ModeRequest::OPEN_LOOP_MODE); // 下发开环提线木偶模式 + + StandardResponse response; + grpc::ClientContext context; + + // 【网络防卡死】设置单次通信的超时时间为 2 秒,防止死锁 ROS 2 主线程 + context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(2)); + + grpc::Status status = stub_->SetControlMode(&context, request, &response); + + if (status.ok() && response.success()) { + RCLCPP_INFO(this->get_logger(), "✅ 夺权成功!车端回复: %s", response.message().c_str()); + } else { + RCLCPP_ERROR(this->get_logger(), "❌ 调用失败: %d - %s", status.error_code(), status.error_message().c_str()); + } + } +}; + +int main(int argc, char **argv) { + rclcpp::init(argc, argv); + rclcpp::spin(std::make_shared()); + rclcpp::shutdown(); + return 0; +} \ No newline at end of file diff --git a/agv_calib_eu/main.cpp b/agv_calib_eu/main.cpp new file mode 100644 index 0000000..3749ff1 --- /dev/null +++ b/agv_calib_eu/main.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +// 包含刚才手动翻译出的头文件 +#include "agv_calib_control.grpc.pb.h" + +using namespace agv::calibration::control; + +// 继承并实现定义在 .proto 里的服务契约 +class AgvCalibServiceImpl final : public AgvCalibControlService::Service { + + // 覆盖接管控制权的方法 + grpc::Status SetControlMode(grpc::ServerContext* context, + const ModeRequest* request, + StandardResponse* reply) override { + + if (request->target_mode() == ModeRequest::OPEN_LOOP_MODE) { + std::cout << "\n[Windows底层] 收到 Linux 指令: 进入开环提线木偶模式" << std::endl; + // TODO: 在这里调用 Windows 原有的底盘驱动 DLL 或 CAN 接口,切断避障算法 + + reply->set_success(true); + reply->set_message("Windows 已交出控制权,避障与运动学逆解已关闭"); + } + return grpc::Status::OK; + } + // TODO: 接着 override 实现 ExecuteOpenLoopCmd 等其他函数... +}; + +void RunServer() { + // 🚨 关键排雷:必须绑定 0.0.0.0,千万不要写 localhost 或 127.0.0.1,否则 ROS2 连不上! + std::string server_address("0.0.0.0:50051"); + AgvCalibServiceImpl service; + + grpc::ServerBuilder builder; + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "🚀 Windows 车端 gRPC 代理已启动,监听外部请求: " << server_address << std::endl; + server->Wait(); +} + +int main() { + RunServer(); + return 0; +} \ No newline at end of file