diff --git a/agv_calib_brain/src/agv_calib_core/CMakeLists.txt b/agv_calib_brain/src/agv_calib_core/CMakeLists.txt index 72631e0..ca27910 100644 --- a/agv_calib_brain/src/agv_calib_core/CMakeLists.txt +++ b/agv_calib_brain/src/agv_calib_core/CMakeLists.txt @@ -5,19 +5,47 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() -# 1. 寻找 ROS 2 和 行为树 核心依赖 find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) -find_package(behaviortree_cpp_v3 REQUIRED) +find_package(rclcpp_action REQUIRED) +find_package(rclcpp_components REQUIRED) # 🚨 核心依赖:寻找组件库 +find_package(behaviortree_cpp_v3 REQUIRED) find_package(ament_index_cpp REQUIRED) +find_package(win_ubuntu_bridge REQUIRED) -# 2. 编译主节点 -add_executable(master_node src/master_node.cpp) -target_include_directories(master_node PUBLIC src) -ament_target_dependencies(master_node rclcpp behaviortree_cpp_v3 ament_index_cpp) +# 1. 编译大脑为动态链接库 (SHARED) 组件 +add_library(brain_node SHARED src/brain_node.cpp) -# 3. 安装规则 (极其重要:把剧本和程序装到系统目录,让 ROS 2 能找到它) -install(TARGETS master_node DESTINATION lib/${PROJECT_NAME}) +# 2. 将 include 暴露给编译器 +target_include_directories(brain_node PUBLIC + "$" +) + +ament_target_dependencies(brain_node + rclcpp + rclcpp_action + rclcpp_components + behaviortree_cpp_v3 + ament_index_cpp + win_ubuntu_bridge +) + +# 3. 注册插件 +rclcpp_components_register_node(brain_node + PLUGIN "agv_calib_core::BrainNode" + EXECUTABLE brain_node_exe +) + +# 4. 安装工程中所有的核心文件夹 (不可遗漏!) +install(TARGETS brain_node brain_node_exe + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib/${PROJECT_NAME} + RUNTIME DESTINATION lib/${PROJECT_NAME} +) + +install(DIRECTORY include/ DESTINATION include) +install(DIRECTORY config/ DESTINATION share/${PROJECT_NAME}/config) +install(DIRECTORY launch/ DESTINATION share/${PROJECT_NAME}/launch) install(DIRECTORY behavior_trees/ DESTINATION share/${PROJECT_NAME}/behavior_trees) ament_package() \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/behavior_trees/main_tree.xml b/agv_calib_brain/src/agv_calib_core/behavior_trees/main_tree.xml new file mode 100644 index 0000000..3a5c5ff --- /dev/null +++ b/agv_calib_brain/src/agv_calib_core/behavior_trees/main_tree.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/config/brain_params.yaml b/agv_calib_brain/src/agv_calib_core/config/brain_params.yaml new file mode 100644 index 0000000..438ff0e --- /dev/null +++ b/agv_calib_brain/src/agv_calib_core/config/brain_params.yaml @@ -0,0 +1,6 @@ +/**: + ros__parameters: + # 动态指定要加载的 XML 剧本文件名 + tree_xml_filename: "main_tree.xml" + # 行为树的 Tick 循环频率 (毫秒) + tick_rate_ms: 50 \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/include/agv_calib_core/brain_node.hpp b/agv_calib_brain/src/agv_calib_core/include/agv_calib_core/brain_node.hpp new file mode 100644 index 0000000..a8a91c3 --- /dev/null +++ b/agv_calib_brain/src/agv_calib_core/include/agv_calib_core/brain_node.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace agv_calib_core { + +// 继承 Node,化身为标准的 ROS 2 Component +class BrainNode : public rclcpp::Node { +public: + explicit BrainNode(const rclcpp::NodeOptions & options); + ~BrainNode() override; + +private: + // 行为树专属的后台执行线程 (极其重要!绝不能阻塞 ROS 2 容器主线程) + void execute_behavior_tree(); + + std::thread bt_thread_; + std::atomic is_running_; +}; + +} // namespace agv_calib_core \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/include/agv_calib_core/bt_ros2_nodes.hpp b/agv_calib_brain/src/agv_calib_core/include/agv_calib_core/bt_ros2_nodes.hpp new file mode 100644 index 0000000..dc814d2 --- /dev/null +++ b/agv_calib_brain/src/agv_calib_core/include/agv_calib_core/bt_ros2_nodes.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include +#include + +// 引入底层 win_ubuntu_bridge 接口 +#include "win_ubuntu_bridge/srv/set_diagnostic_mode.hpp" +#include "win_ubuntu_bridge/srv/trigger_sync_capture.hpp" +#include "win_ubuntu_bridge/action/download_sensor_data.hpp" + +namespace agv_calib_core { + +class SetChassisModeNode : public BT::SyncActionNode { +public: + SetChassisModeNode(const std::string& name, const BT::NodeConfiguration& config, rclcpp::Node* node) + : BT::SyncActionNode(name, config), node_(node) { + client_ = node_->create_client("/chassis_gateway/set_diagnostic_mode"); + } + + static BT::PortsList providedPorts() { return { BT::InputPort("target_mode") }; } + + BT::NodeStatus tick() override { + int mode; if (!getInput("target_mode", mode)) return BT::NodeStatus::FAILURE; + RCLCPP_INFO(node_->get_logger(), "🌲 [BT] 下发底盘夺权指令,模式: %d", mode); + if (!client_->wait_for_service(std::chrono::seconds(2))) return BT::NodeStatus::FAILURE; + + auto req = std::make_shared(); req->target_mode = mode; + auto future = client_->async_send_request(req); + + // 🚨 这里阻塞等待完全没问题!因为外层 BT 跑在独立线程,根本不影响 ROS 2 Executor 的回调! + if (future.wait_for(std::chrono::seconds(3)) == std::future_status::ready) { + auto res = future.get(); + if (res->success) return BT::NodeStatus::SUCCESS; + } + return BT::NodeStatus::FAILURE; + } +private: + rclcpp::Node* node_; rclcpp::Client::SharedPtr client_; +}; + +class TriggerCaptureNode : public BT::SyncActionNode { +public: + TriggerCaptureNode(const std::string& name, const BT::NodeConfiguration& config, rclcpp::Node* node) + : BT::SyncActionNode(name, config), node_(node) { + client_ = node_->create_client("/sensor_gateway/trigger_sync_capture"); + } + + static BT::PortsList providedPorts() { + return { BT::InputPort("sensor_id"), BT::OutputPort("capture_code_out") }; + } + + BT::NodeStatus tick() override { + std::string sensor_id; getInput("sensor_id", sensor_id); + RCLCPP_INFO(node_->get_logger(), "📷 [BT] 冻结 %s 数据...", sensor_id.c_str()); + if (!client_->wait_for_service(std::chrono::seconds(2))) return BT::NodeStatus::FAILURE; + + auto req = std::make_shared(); req->sensor_ids.push_back(sensor_id); + auto future = client_->async_send_request(req); + if (future.wait_for(std::chrono::seconds(3)) == std::future_status::ready) { + auto res = future.get(); + if (res->success) { setOutput("capture_code_out", res->capture_timestamp_us); return BT::NodeStatus::SUCCESS; } + } + return BT::NodeStatus::FAILURE; + } +private: + rclcpp::Node* node_; rclcpp::Client::SharedPtr client_; +}; + +class DownloadDataNode : public BT::StatefulActionNode { +public: + DownloadDataNode(const std::string& name, const BT::NodeConfiguration& config, rclcpp::Node* node) + : BT::StatefulActionNode(name, config), node_(node) { + action_client_ = rclcpp_action::create_client(node_, "/sensor_gateway/download_sensor_data"); + } + + static BT::PortsList providedPorts() { + return { BT::InputPort("sensor_id"), BT::InputPort("capture_code_in"), + BT::InputPort("save_dir"), BT::OutputPort("saved_path_out") }; + } + + BT::NodeStatus onStart() override { + int64_t code; std::string sensor; std::string save_dir; + if (!getInput("capture_code_in", code) || !getInput("sensor_id", sensor) || !getInput("save_dir", save_dir)) return BT::NodeStatus::FAILURE; + + RCLCPP_INFO(node_->get_logger(), "📥 [BT] 挂起下载任务,取件码: %ld", code); + if (!action_client_->wait_for_action_server(std::chrono::seconds(2))) return BT::NodeStatus::FAILURE; + + auto goal_msg = win_ubuntu_bridge::action::DownloadSensorData::Goal(); + goal_msg.capture_timestamp_us = code; goal_msg.sensor_id = sensor; + goal_msg.data_type = win_ubuntu_bridge::action::DownloadSensorData::Goal::DATA_TYPE_IMAGE; goal_msg.save_directory = save_dir; + + auto send_goal_options = rclcpp_action::Client::SendGoalOptions(); + send_goal_options.result_callback = [this](const rclcpp_action::ClientGoalHandle::WrappedResult & result) { + if (result.code == rclcpp_action::ResultCode::SUCCEEDED && result.result->success) { + RCLCPP_INFO(node_->get_logger(), "✅ [BT] 落盘成功!路径: %s", result.result->saved_file_path.c_str()); + setOutput("saved_path_out", result.result->saved_file_path); done_ = true; success_ = true; + } else { done_ = true; success_ = false; } + }; + action_client_->async_send_goal(goal_msg, send_goal_options); done_ = false; return BT::NodeStatus::RUNNING; + } + BT::NodeStatus onRunning() override { if (done_) return success_ ? BT::NodeStatus::SUCCESS : BT::NodeStatus::FAILURE; return BT::NodeStatus::RUNNING; } + void onHalted() override { } +private: + rclcpp::Node* node_; rclcpp_action::Client::SharedPtr action_client_; + bool done_ = false; bool success_ = false; +}; + +} // namespace agv_calib_core \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/launch/brain_bringup.launch.py b/agv_calib_brain/src/agv_calib_core/launch/brain_bringup.launch.py new file mode 100644 index 0000000..c482a4f --- /dev/null +++ b/agv_calib_brain/src/agv_calib_core/launch/brain_bringup.launch.py @@ -0,0 +1,31 @@ +import os +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode + +def generate_launch_description(): + # 获取 yaml 文件的绝对路径 + config_file = os.path.join( + get_package_share_directory('agv_calib_core'), + 'config', + 'brain_params.yaml' + ) + + # 建立多线程容器加载大脑组件 (MT 代表 Multi-Threaded Executor) + container = ComposableNodeContainer( + name='brain_container', + namespace='', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[ + ComposableNode( + package='agv_calib_core', + plugin='agv_calib_core::BrainNode', + name='brain_node', + parameters=[config_file] # 🚨 动态挂载 YAML 参数表! + ) + ], + output='screen', + ) + return LaunchDescription([container]) \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/package.xml b/agv_calib_brain/src/agv_calib_core/package.xml index 89b1683..5d997ee 100644 --- a/agv_calib_brain/src/agv_calib_core/package.xml +++ b/agv_calib_brain/src/agv_calib_core/package.xml @@ -2,21 +2,21 @@ agv_calib_core - 0.0.0 - TODO: Package description + 1.0.0 + 行为树总控大脑 nvidia - TODO: License declaration + Apache-2.0 ament_cmake rclcpp + rclcpp_action + rclcpp_components behaviortree_cpp_v3 ament_index_cpp - - ament_lint_auto - ament_lint_common + win_ubuntu_bridge ament_cmake - + \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/src/brain_node.cpp b/agv_calib_brain/src/agv_calib_core/src/brain_node.cpp new file mode 100644 index 0000000..5fa85d8 --- /dev/null +++ b/agv_calib_brain/src/agv_calib_core/src/brain_node.cpp @@ -0,0 +1,87 @@ +#include "agv_calib_core/brain_node.hpp" +#include "agv_calib_core/bt_ros2_nodes.hpp" + +#include +#include +#include +#include + +namespace agv_calib_core { + +BrainNode::BrainNode(const rclcpp::NodeOptions & options) +: Node("brain_node", options), is_running_(false) { + + RCLCPP_INFO(this->get_logger(), "👑 AGV 标定中央大脑 (Component 版) 正在挂载..."); + + // 1. 从 YAML 配置文件读取动态参数!绝不硬编码! + this->declare_parameter("tree_xml_filename", "main_tree.xml"); + this->declare_parameter("tick_rate_ms", 50); + + // 2. 启动专属的后台守护线程去运行行为树。将主线程交还给容器处理网络回调! + is_running_ = true; + bt_thread_ = std::thread(&BrainNode::execute_behavior_tree, this); +} + +BrainNode::~BrainNode() { + is_running_ = false; + if (bt_thread_.joinable()) { + bt_thread_.join(); + } +} + +void BrainNode::execute_behavior_tree() { + // 稍微延时 0.5 秒,确保节点完全被容器接管,再发起 Client 寻址 + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + BT::BehaviorTreeFactory factory; + + // 注册业务积木,传递 this 裸指针给所有积木 + factory.registerBuilder("SetChassisMode", + [this](const std::string& name, const BT::NodeConfiguration& config) { + return std::make_unique(name, config, this); + }); + + factory.registerBuilder("TriggerCapture", + [this](const std::string& name, const BT::NodeConfiguration& config) { + return std::make_unique(name, config, this); + }); + + factory.registerBuilder("DownloadData", + [this](const std::string& name, const BT::NodeConfiguration& config) { + return std::make_unique(name, config, this); + }); + + try { + std::string xml_filename = this->get_parameter("tree_xml_filename").as_string(); + int tick_rate = this->get_parameter("tick_rate_ms").as_int(); + + std::string pkg_path = ament_index_cpp::get_package_share_directory("agv_calib_core"); + std::string xml_file = pkg_path + "/behavior_trees/" + xml_filename; + + auto tree = factory.createTreeFromFile(xml_file); + BT::StdCoutLogger logger_cout(tree); + + RCLCPP_INFO(this->get_logger(), "📜 XML 剧本 [%s] 加载完毕,开始全自动流水线...", xml_filename.c_str()); + + // 按照 YAML 配置的频率持续 Tick + BT::NodeStatus status = BT::NodeStatus::RUNNING; + while (rclcpp::ok() && is_running_ && status == BT::NodeStatus::RUNNING) { + status = tree.tickRoot(); + std::this_thread::sleep_for(std::chrono::milliseconds(tick_rate)); + } + + if (status == BT::NodeStatus::SUCCESS) { + RCLCPP_INFO(this->get_logger(), "🎉 标定流水线全流程完美结束!"); + } else { + RCLCPP_WARN(this->get_logger(), "⚠️ 流水线未成功完成 (可能被中止)。"); + } + + } catch (const std::exception& e) { + RCLCPP_ERROR(this->get_logger(), "❌ 行为树崩溃: %s", e.what()); + } +} + +} // namespace agv_calib_core + +// 🚨 终极一步:将该类注册为 ROS 2 Component (插件)! +RCLCPP_COMPONENTS_REGISTER_NODE(agv_calib_core::BrainNode) \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/src/bt_nodes/dummy_nodes.hpp b/agv_calib_brain/src/agv_calib_core/src/bt_nodes/dummy_nodes.hpp deleted file mode 100644 index d41935d..0000000 --- a/agv_calib_brain/src/agv_calib_core/src/bt_nodes/dummy_nodes.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once -#include -#include -#include -#include - -// 1. 假装连接车端并夺权 -class MockConnectAGV : public BT::SyncActionNode { -public: - MockConnectAGV(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "💻 [gRPC 对外] 📡 正在连接 Windows 车端... 夺权成功!" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 假装网络耗时 0.5 秒 - return BT::NodeStatus::SUCCESS; - } -}; - -// 2. 假装呼叫底盘算法 -class MockCallChassisAlgo : public BT::SyncActionNode { -public: - MockCallChassisAlgo(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "🧮 [Action 对内] 🚙 丢给底盘算法团队... 算好了!左轮径 0.098m。" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(800)); - return BT::NodeStatus::SUCCESS; - } -}; - -// 3. 假装控制车子跑S型曲线 -class MockTuneControl : public BT::SyncActionNode { -public: - MockTuneControl(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "💻 [gRPC 对外] 📈 正在下发S型曲线测试考题... 车端已跑完。" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - return BT::NodeStatus::SUCCESS; - } -}; - -// 4. 假装呼叫AI寻优算法 -class MockCallControlAlgo : public BT::SyncActionNode { -public: - MockCallControlAlgo(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "🧮 [Action 对内] 🧠 AI贝叶斯打分完毕... PID 最优参数已锁定!" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(800)); - return BT::NodeStatus::SUCCESS; - } -}; - -// 5. 假装走停拍并下载大文件 -class MockMoveAndCapture : public BT::SyncActionNode { -public: - MockMoveAndCapture(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "💻 [gRPC 对外] 🛑 刹车静止...咔嚓!5MB大文件已下载至 /tmp/cam.png" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(800)); - return BT::NodeStatus::SUCCESS; - } -}; - -// 6. 假装调用外参标定视觉算法 -class MockCallSensorAlgo : public BT::SyncActionNode { -public: - MockCallSensorAlgo(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "🧮 [Action 对内] 📷 视觉团队正在算矩阵... 拿到 4x4 外参矩阵!" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(800)); - return BT::NodeStatus::SUCCESS; - } -}; - -// 7. 假装出厂固化 -class MockCommitAllParams : public BT::SyncActionNode { -public: - MockCommitAllParams(const std::string& name) : BT::SyncActionNode(name, {}) {} - BT::NodeStatus tick() override { - std::cout << "💻 [gRPC 对外] 💾 正在把所有完美参数烧录进 AGV... 标定闭环,可以出厂!\n" << std::endl; - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - return BT::NodeStatus::SUCCESS; - } -}; \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/src/bt_nodes/real_grpc_nodes.hpp b/agv_calib_brain/src/agv_calib_core/src/bt_nodes/real_grpc_nodes.hpp deleted file mode 100644 index d082732..0000000 --- a/agv_calib_brain/src/agv_calib_core/src/bt_nodes/real_grpc_nodes.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include -#include -// 引入 CMake 自动生成的 C++ 网络契约头文件! -#include "agv_calib_control.grpc.pb.h" - -using namespace agv::calibration::control; - -// ========================================================= -// 🌟 真实网络积木:连接车端并夺取控制权 -// ========================================================= -class ConnectAGVNode : public BT::SyncActionNode { -public: - // 注意:构造函数这里多了一个 const BT::NodeConfiguration& config 参数 - ConnectAGVNode(const std::string& name, const BT::NodeConfiguration& config) : BT::SyncActionNode(name, config) { - // 1. 初始化时,拨号连接到车端 (因为我们要自己测,所以先连本机 127.0.0.1 端口) - channel_ = grpc::CreateChannel("127.0.0.1:50051", grpc::InsecureChannelCredentials()); - stub_ = AgvCalibControlService::NewStub(channel_); - } - - // 行为树必须的静态函数 (定义端口) - static BT::PortsList providedPorts() { return {}; } - - BT::NodeStatus tick() override { - std::cout << "\n💻 [行为树真节点] 正在通过 gRPC 向车端发起夺权请求..." << std::endl; - - // 2. 准备发送载荷:要求进入调优模式 - ModeRequest request; - request.set_target_mode(ModeRequest::TUNING_MODE); - - StandardResponse response; - grpc::ClientContext context; - - // 🚨 架构师防线:设置 2 秒网络超时!如果网络断了,绝不让主线程死锁卡住! - context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(2)); - - // 3. 发射真正的网络脉冲! - grpc::Status status = stub_->SetControlMode(&context, request, &response); - - // 4. 根据网络回复,决定行为树这根树枝是亮绿灯还是红灯 - if (status.ok() && response.success()) { - std::cout << "✅ [网络通信成功] 车端回执: " << response.message() << std::endl; - return BT::NodeStatus::SUCCESS; // 绿灯,允许行为树执行下一步 - } else { - std::cerr << "❌ [网络通信失败] 错误码: " << status.error_code() - << " 详情: " << status.error_message() << std::endl; - return BT::NodeStatus::FAILURE; // 红灯,触发行为树重试或报警 - } - } - -private: - std::shared_ptr channel_; - std::unique_ptr stub_; -}; \ No newline at end of file diff --git a/agv_calib_brain/src/agv_calib_core/src/master_node.cpp b/agv_calib_brain/src/agv_calib_core/src/master_node.cpp deleted file mode 100644 index 6b6db01..0000000 --- a/agv_calib_brain/src/agv_calib_core/src/master_node.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include - -// 引入刚才写的假节点 -#include "bt_nodes/dummy_nodes.hpp" - -int main(int argc, char **argv) { - rclcpp::init(argc, argv); - - std::cout << "\n=========================================" << std::endl; - std::cout << "🚀 AGV 标定车间中央大脑 [空转测试版] 启动!" << std::endl; - std::cout << "=========================================\n" << std::endl; - - BT::BehaviorTreeFactory factory; - - // 1. 把 C++ 类注册到工厂,名字必须和 XML 里的一模一样! - factory.registerNodeType("MockConnectAGV"); - factory.registerNodeType("MockCallChassisAlgo"); - factory.registerNodeType("MockTuneControl"); - factory.registerNodeType("MockCallControlAlgo"); - factory.registerNodeType("MockMoveAndCapture"); - factory.registerNodeType("MockCallSensorAlgo"); - factory.registerNodeType("MockCommitAllParams"); - - try { - // 2. 动态获取 XML 剧本的绝对路径 (防止你运行程序时路径不对找不到文件) - std::string pkg_path = ament_index_cpp::get_package_share_directory("agv_calib_core"); - std::string xml_file = pkg_path + "/behavior_trees/main_pipeline.xml"; - - auto tree = factory.createTreeFromFile(xml_file); - std::cout << "📜 行为树剧本加载完毕,开始全自动流水线...\n" << std::endl; - - // 3. 开始执行总控流! - tree.tickRoot(); - - } catch (const std::exception& e) { - std::cerr << "❌ 加载 XML 失败: " << e.what() << std::endl; - } - - std::cout << "🎉 全流程执行完毕,完美收工!\n" << std::endl; - rclcpp::shutdown(); - return 0; -} \ No newline at end of file diff --git a/agv_calib_brain/src/win_ubuntu_bridge/CMakeLists.txt b/agv_calib_brain/src/win_ubuntu_bridge/CMakeLists.txt index 38d19d6..bf62849 100644 --- a/agv_calib_brain/src/win_ubuntu_bridge/CMakeLists.txt +++ b/agv_calib_brain/src/win_ubuntu_bridge/CMakeLists.txt @@ -154,6 +154,6 @@ install(TARGETS install(DIRECTORY proto/ DESTINATION share/${PROJECT_NAME}/proto) # 若有 launch 或 behavior_trees 文件,随时解开下面这行的注释 -# install(DIRECTORY launch/ DESTINATION share/${PROJECT_NAME}/launch) +install(DIRECTORY launch/ config/ DESTINATION share/${PROJECT_NAME}/launch) ament_package() \ No newline at end of file diff --git a/agv_calib_brain/src/win_ubuntu_bridge/launch/gateways_bringup.launch.py b/agv_calib_brain/src/win_ubuntu_bridge/launch/gateways_bringup.launch.py new file mode 100644 index 0000000..652419b --- /dev/null +++ b/agv_calib_brain/src/win_ubuntu_bridge/launch/gateways_bringup.launch.py @@ -0,0 +1,47 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode + +def generate_launch_description(): + # 1. 声明一个外部参数 agv_ip,默认值是本地 + agv_ip_arg = DeclareLaunchArgument( + 'agv_ip', + default_value='127.0.0.1:50051', + description='Windows 车端在 Wi-Fi 下的 IP 和端口' + ) + + agv_ip = LaunchConfiguration('agv_ip') + + # 2. 把参数动态注入给三个网关组件 + container = ComposableNodeContainer( + name='agv_gateway_container', + namespace='', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[ + ComposableNode( + package='win_ubuntu_bridge', + plugin='win_ubuntu_bridge::ChassisGatewayNode', + name='chassis_gateway', + parameters=[{'agv_ip': agv_ip}] # 👈 动态注入真实 IP + ), + + ComposableNode( + package='win_ubuntu_bridge', + plugin='win_ubuntu_bridge::ControlGatewayNode', + name='control_gateway', + parameters=[{'agv_ip': agv_ip}] # 👈 动态注入真实 IP + ), + + ComposableNode( + package='win_ubuntu_bridge', + plugin='win_ubuntu_bridge::SensorGatewayNode', + name='sensor_gateway', + parameters=[{'agv_ip': agv_ip}] # 👈 动态注入真实 IP + ), + ], + output='screen', + ) + return LaunchDescription([agv_ip_arg, container]) \ No newline at end of file diff --git a/agv_calib_eu/scripts/agv_calib_chassis_pb2.py b/agv_calib_eu/scripts/agv_calib_chassis_pb2.py new file mode 100644 index 0000000..00db04d --- /dev/null +++ b/agv_calib_eu/scripts/agv_calib_chassis_pb2.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agv_calib_chassis.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'agv_calib_chassis.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x61gv_calib_chassis.proto\x12\x17\x61gv.calibration.chassis\"\x07\n\x05\x45mpty\"4\n\x10StandardResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x96\x01\n\x15\x44iagnosticModeRequest\x12H\n\x0btarget_mode\x18\x01 \x01(\x0e\x32\x33.agv.calibration.chassis.DiagnosticModeRequest.Mode\"3\n\x04Mode\x12\x15\n\x11NORMAL_KINEMATICS\x10\x00\x12\x14\n\x10\x44IRECT_RAW_DRIVE\x10\x01\"\xfd\x02\n\rHardwareState\x12\x1d\n\x15hardware_timestamp_us\x18\x01 \x01(\x03\x12\x18\n\x10\x65ncoder_ticks_fl\x18\x02 \x01(\x03\x12\x18\n\x10\x65ncoder_ticks_fr\x18\x03 \x01(\x03\x12\x18\n\x10\x65ncoder_ticks_rl\x18\x04 \x01(\x03\x12\x18\n\x10\x65ncoder_ticks_rr\x18\x05 \x01(\x03\x12$\n\x1c\x61\x63tual_steer_angle_front_deg\x18\x06 \x01(\x01\x12#\n\x1b\x61\x63tual_steer_angle_rear_deg\x18\x07 \x01(\x01\x12\x16\n\x0e\x63urrent_fl_amp\x18\x08 \x01(\x01\x12\x16\n\x0e\x63urrent_fr_amp\x18\t \x01(\x01\x12\x16\n\x0e\x63urrent_rl_amp\x18\n \x01(\x01\x12\x16\n\x0e\x63urrent_rr_amp\x18\x0b \x01(\x01\x12\x1f\n\x17\x63urrent_steer_front_amp\x18\x0c \x01(\x01\x12\x19\n\x11\x64river_error_code\x18\r \x01(\r\"\x95\x01\n\x0fRawDriveRequest\x12\x14\n\x0ctest_case_id\x18\x01 \x01(\t\x12\x14\n\x0c\x66l_motor_rpm\x18\x02 \x01(\x01\x12\x14\n\x0c\x66r_motor_rpm\x18\x03 \x01(\x01\x12\x14\n\x0crl_motor_rpm\x18\x04 \x01(\x01\x12\x14\n\x0crr_motor_rpm\x18\x05 \x01(\x01\x12\x14\n\x0c\x64uration_sec\x18\x06 \x01(\x01\"\xec\x01\n\x0fRawSteerRequest\x12\x14\n\x0ctest_case_id\x18\x01 \x01(\t\x12\x1d\n\x15\x66ront_steer_angle_deg\x18\x02 \x01(\x01\x12\x1c\n\x14rear_steer_angle_deg\x18\x03 \x01(\x01\x12 \n\x13sweep_amplitude_deg\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x1f\n\x12sweep_frequency_hz\x18\x05 \x01(\x01H\x01\x88\x01\x01\x12\x14\n\x0c\x64uration_sec\x18\x06 \x01(\x01\x42\x16\n\x14_sweep_amplitude_degB\x15\n\x13_sweep_frequency_hz\"\xdd\x04\n\x0fKinematicParams\x12\x1e\n\x11wheel_radius_fl_m\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x1e\n\x11wheel_radius_fr_m\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12\x1e\n\x11wheel_radius_rl_m\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12\x1e\n\x11wheel_radius_rr_m\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12(\n\x1bsteer_zero_offset_front_deg\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\'\n\x1asteer_zero_offset_rear_deg\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12$\n\x17\x65\x66\x66\x65\x63tive_track_width_m\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12#\n\x16\x65\x66\x66\x65\x63tive_wheel_base_m\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x1b\n\x0eicr_offset_x_m\x18\t \x01(\x01H\x08\x88\x01\x01\x12\x1b\n\x0eicr_offset_y_m\x18\n \x01(\x01H\t\x88\x01\x01\x42\x14\n\x12_wheel_radius_fl_mB\x14\n\x12_wheel_radius_fr_mB\x14\n\x12_wheel_radius_rl_mB\x14\n\x12_wheel_radius_rr_mB\x1e\n\x1c_steer_zero_offset_front_degB\x1d\n\x1b_steer_zero_offset_rear_degB\x1a\n\x18_effective_track_width_mB\x19\n\x17_effective_wheel_base_mB\x11\n\x0f_icr_offset_x_mB\x11\n\x0f_icr_offset_y_m2\xa2\x05\n\x16\x41gvCalibChassisService\x12n\n\x11SetDiagnosticMode\x12..agv.calibration.chassis.DiagnosticModeRequest\x1a).agv.calibration.chassis.StandardResponse\x12\x63\n\x16HardwareEmergencyBrake\x12\x1e.agv.calibration.chassis.Empty\x1a).agv.calibration.chassis.StandardResponse\x12\x63\n\x17StreamHardwareTelemetry\x12\x1e.agv.calibration.chassis.Empty\x1a&.agv.calibration.chassis.HardwareState0\x01\x12m\n\x16\x45xecuteRawDriveCommand\x12(.agv.calibration.chassis.RawDriveRequest\x1a).agv.calibration.chassis.StandardResponse\x12m\n\x16\x45xecuteRawSteerCommand\x12(.agv.calibration.chassis.RawSteerRequest\x1a).agv.calibration.chassis.StandardResponse\x12p\n\x19\x43ommitKinematicParameters\x12(.agv.calibration.chassis.KinematicParams\x1a).agv.calibration.chassis.StandardResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agv_calib_chassis_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_EMPTY']._serialized_start=52 + _globals['_EMPTY']._serialized_end=59 + _globals['_STANDARDRESPONSE']._serialized_start=61 + _globals['_STANDARDRESPONSE']._serialized_end=113 + _globals['_DIAGNOSTICMODEREQUEST']._serialized_start=116 + _globals['_DIAGNOSTICMODEREQUEST']._serialized_end=266 + _globals['_DIAGNOSTICMODEREQUEST_MODE']._serialized_start=215 + _globals['_DIAGNOSTICMODEREQUEST_MODE']._serialized_end=266 + _globals['_HARDWARESTATE']._serialized_start=269 + _globals['_HARDWARESTATE']._serialized_end=650 + _globals['_RAWDRIVEREQUEST']._serialized_start=653 + _globals['_RAWDRIVEREQUEST']._serialized_end=802 + _globals['_RAWSTEERREQUEST']._serialized_start=805 + _globals['_RAWSTEERREQUEST']._serialized_end=1041 + _globals['_KINEMATICPARAMS']._serialized_start=1044 + _globals['_KINEMATICPARAMS']._serialized_end=1649 + _globals['_AGVCALIBCHASSISSERVICE']._serialized_start=1652 + _globals['_AGVCALIBCHASSISSERVICE']._serialized_end=2326 +# @@protoc_insertion_point(module_scope) diff --git a/agv_calib_eu/scripts/agv_calib_chassis_pb2_grpc.py b/agv_calib_eu/scripts/agv_calib_chassis_pb2_grpc.py new file mode 100644 index 0000000..a53a69e --- /dev/null +++ b/agv_calib_eu/scripts/agv_calib_chassis_pb2_grpc.py @@ -0,0 +1,351 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import agv_calib_chassis_pb2 as agv__calib__chassis__pb2 + +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in agv_calib_chassis_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class AgvCalibChassisServiceStub(object): + """========================================================= + 核心服务:AGV 底盘底层硬件自诊与物理运动学标定代理服务 + [部署端 Server]:Windows 车端 (只负责听口令、转电机、报裸数据) + [调用端 Client]:Linux 车间服务器 (负责发口令、看雷达真值、算误差) + ========================================================= + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.SetDiagnosticMode = channel.unary_unary( + '/agv.calibration.chassis.AgvCalibChassisService/SetDiagnosticMode', + request_serializer=agv__calib__chassis__pb2.DiagnosticModeRequest.SerializeToString, + response_deserializer=agv__calib__chassis__pb2.StandardResponse.FromString, + _registered_method=True) + self.HardwareEmergencyBrake = channel.unary_unary( + '/agv.calibration.chassis.AgvCalibChassisService/HardwareEmergencyBrake', + request_serializer=agv__calib__chassis__pb2.Empty.SerializeToString, + response_deserializer=agv__calib__chassis__pb2.StandardResponse.FromString, + _registered_method=True) + self.StreamHardwareTelemetry = channel.unary_stream( + '/agv.calibration.chassis.AgvCalibChassisService/StreamHardwareTelemetry', + request_serializer=agv__calib__chassis__pb2.Empty.SerializeToString, + response_deserializer=agv__calib__chassis__pb2.HardwareState.FromString, + _registered_method=True) + self.ExecuteRawDriveCommand = channel.unary_unary( + '/agv.calibration.chassis.AgvCalibChassisService/ExecuteRawDriveCommand', + request_serializer=agv__calib__chassis__pb2.RawDriveRequest.SerializeToString, + response_deserializer=agv__calib__chassis__pb2.StandardResponse.FromString, + _registered_method=True) + self.ExecuteRawSteerCommand = channel.unary_unary( + '/agv.calibration.chassis.AgvCalibChassisService/ExecuteRawSteerCommand', + request_serializer=agv__calib__chassis__pb2.RawSteerRequest.SerializeToString, + response_deserializer=agv__calib__chassis__pb2.StandardResponse.FromString, + _registered_method=True) + self.CommitKinematicParameters = channel.unary_unary( + '/agv.calibration.chassis.AgvCalibChassisService/CommitKinematicParameters', + request_serializer=agv__calib__chassis__pb2.KinematicParams.SerializeToString, + response_deserializer=agv__calib__chassis__pb2.StandardResponse.FromString, + _registered_method=True) + + +class AgvCalibChassisServiceServicer(object): + """========================================================= + 核心服务:AGV 底盘底层硬件自诊与物理运动学标定代理服务 + [部署端 Server]:Windows 车端 (只负责听口令、转电机、报裸数据) + [调用端 Client]:Linux 车间服务器 (负责发口令、看雷达真值、算误差) + ========================================================= + """ + + def SetDiagnosticMode(self, request, context): + """--------------------------------------------------------- + 第一步:权限接管与安全熔断 (剥夺车端算法大脑) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:要求切断底盘运动学逆解,进入纯物理开环直驱模式 + 🚙 [Windows 返回 -> Linux]:返回接管是否成功的回执 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def HardwareEmergencyBrake(self, request, context): + """💻 [Linux 发送 -> Windows]:无视一切状态立刻抱死电机的紧急急停指令 + 🚙 [Windows 返回 -> Linux]:返回急停执行状态 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def StreamHardwareTelemetry(self, request, context): + """--------------------------------------------------------- + 第二步:打开体征监控水龙头 (数字孪生健康诊断) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:发送空请求,触发高频推流开关 + 🚙 [Windows 持续流式返回 -> Linux]:以 50Hz 频率持续不断地回传原始脉冲与电流 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExecuteRawDriveCommand(self, request, context): + """--------------------------------------------------------- + 第三步:原始物理开环考题下发 (逼迫底盘暴露机械缺陷) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:绕过算法,直接命令指定驱动轮以固定 RPM 盲跑 + 🚙 [Windows 返回 -> Linux]:返回电机是否已成功按给定 RPM 运转 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExecuteRawSteerCommand(self, request, context): + """💻 [Linux 发送 -> Windows]:直接对转向机构下发绝对物理角度 (测机械装歪的角度) + 🚙 [Windows 返回 -> Linux]:返回舵机是否已开始执行角度指令 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CommitKinematicParameters(self, request, context): + """--------------------------------------------------------- + 第四步:物理本底参数定稿写值 (标定闭环结束) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:下发 Linux 结合外部真值算出的绝对物理修正系数 + 🚙 [Windows 返回 -> Linux]:将系数覆写到本地硬盘/驱动板后,返回成功回执 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_AgvCalibChassisServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'SetDiagnosticMode': grpc.unary_unary_rpc_method_handler( + servicer.SetDiagnosticMode, + request_deserializer=agv__calib__chassis__pb2.DiagnosticModeRequest.FromString, + response_serializer=agv__calib__chassis__pb2.StandardResponse.SerializeToString, + ), + 'HardwareEmergencyBrake': grpc.unary_unary_rpc_method_handler( + servicer.HardwareEmergencyBrake, + request_deserializer=agv__calib__chassis__pb2.Empty.FromString, + response_serializer=agv__calib__chassis__pb2.StandardResponse.SerializeToString, + ), + 'StreamHardwareTelemetry': grpc.unary_stream_rpc_method_handler( + servicer.StreamHardwareTelemetry, + request_deserializer=agv__calib__chassis__pb2.Empty.FromString, + response_serializer=agv__calib__chassis__pb2.HardwareState.SerializeToString, + ), + 'ExecuteRawDriveCommand': grpc.unary_unary_rpc_method_handler( + servicer.ExecuteRawDriveCommand, + request_deserializer=agv__calib__chassis__pb2.RawDriveRequest.FromString, + response_serializer=agv__calib__chassis__pb2.StandardResponse.SerializeToString, + ), + 'ExecuteRawSteerCommand': grpc.unary_unary_rpc_method_handler( + servicer.ExecuteRawSteerCommand, + request_deserializer=agv__calib__chassis__pb2.RawSteerRequest.FromString, + response_serializer=agv__calib__chassis__pb2.StandardResponse.SerializeToString, + ), + 'CommitKinematicParameters': grpc.unary_unary_rpc_method_handler( + servicer.CommitKinematicParameters, + request_deserializer=agv__calib__chassis__pb2.KinematicParams.FromString, + response_serializer=agv__calib__chassis__pb2.StandardResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'agv.calibration.chassis.AgvCalibChassisService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('agv.calibration.chassis.AgvCalibChassisService', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class AgvCalibChassisService(object): + """========================================================= + 核心服务:AGV 底盘底层硬件自诊与物理运动学标定代理服务 + [部署端 Server]:Windows 车端 (只负责听口令、转电机、报裸数据) + [调用端 Client]:Linux 车间服务器 (负责发口令、看雷达真值、算误差) + ========================================================= + """ + + @staticmethod + def SetDiagnosticMode(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.chassis.AgvCalibChassisService/SetDiagnosticMode', + agv__calib__chassis__pb2.DiagnosticModeRequest.SerializeToString, + agv__calib__chassis__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def HardwareEmergencyBrake(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.chassis.AgvCalibChassisService/HardwareEmergencyBrake', + agv__calib__chassis__pb2.Empty.SerializeToString, + agv__calib__chassis__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def StreamHardwareTelemetry(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/agv.calibration.chassis.AgvCalibChassisService/StreamHardwareTelemetry', + agv__calib__chassis__pb2.Empty.SerializeToString, + agv__calib__chassis__pb2.HardwareState.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExecuteRawDriveCommand(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.chassis.AgvCalibChassisService/ExecuteRawDriveCommand', + agv__calib__chassis__pb2.RawDriveRequest.SerializeToString, + agv__calib__chassis__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExecuteRawSteerCommand(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.chassis.AgvCalibChassisService/ExecuteRawSteerCommand', + agv__calib__chassis__pb2.RawSteerRequest.SerializeToString, + agv__calib__chassis__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CommitKinematicParameters(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.chassis.AgvCalibChassisService/CommitKinematicParameters', + agv__calib__chassis__pb2.KinematicParams.SerializeToString, + agv__calib__chassis__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/agv_calib_eu/scripts/agv_calib_control_pb2.py b/agv_calib_eu/scripts/agv_calib_control_pb2.py new file mode 100644 index 0000000..edaf732 --- /dev/null +++ b/agv_calib_eu/scripts/agv_calib_control_pb2.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agv_calib_control.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'agv_calib_control.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17\x61gv_calib_control.proto\x12\x17\x61gv.calibration.control\"\x07\n\x05\x45mpty\"4\n\x10StandardResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x8b\x01\n\x0bModeRequest\x12>\n\x0btarget_mode\x18\x01 \x01(\x0e\x32).agv.calibration.control.ModeRequest.Mode\"<\n\x04Mode\x12\x0f\n\x0bNORMAL_MODE\x10\x00\x12\x12\n\x0eOPEN_LOOP_MODE\x10\x01\x12\x0f\n\x0bTUNING_MODE\x10\x02\"p\n\x0fOpenLoopRequest\x12\x16\n\x0eleft_motor_cmd\x18\x01 \x01(\x01\x12\x17\n\x0fright_motor_cmd\x18\x02 \x01(\x01\x12\x16\n\x0esteering_angle\x18\x03 \x01(\x01\x12\x14\n\x0c\x64uration_sec\x18\x04 \x01(\x01\"h\n\x0fTrajectoryPoint\x12\x0b\n\x03x_m\x18\x01 \x01(\x01\x12\x0b\n\x03y_m\x18\x02 \x01(\x01\x12\x0f\n\x07yaw_rad\x18\x03 \x01(\x01\x12\x17\n\x0ftarget_speed_ms\x18\x04 \x01(\x01\x12\x11\n\tcurvature\x18\x05 \x01(\x01\"a\n\x11TrajectoryRequest\x12\x14\n\x0ctest_case_id\x18\x01 \x01(\t\x12\x36\n\x04path\x18\x02 \x03(\x0b\x32(.agv.calibration.control.TrajectoryPoint\"G\n\x13StepResponseRequest\x12\x1a\n\x12target_velocity_ms\x18\x01 \x01(\x01\x12\x14\n\x0c\x64uration_sec\x18\x02 \x01(\x01\"\xf9\x05\n\rControlParams\x12$\n\x17wheel_radius_left_ratio\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12%\n\x18wheel_radius_right_ratio\x18\x02 \x01(\x01H\x01\x88\x01\x01\x12$\n\x17\x65\x66\x66\x65\x63tive_track_width_m\x18\x03 \x01(\x01H\x02\x88\x01\x01\x12%\n\x18steering_zero_offset_deg\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x1b\n\x0epid_kp_lateral\x18\x05 \x01(\x01H\x04\x88\x01\x01\x12\x1b\n\x0epid_ki_lateral\x18\x06 \x01(\x01H\x05\x88\x01\x01\x12\x1b\n\x0epid_kd_lateral\x18\x07 \x01(\x01H\x06\x88\x01\x01\x12\x1b\n\x0epid_kp_heading\x18\x08 \x01(\x01H\x07\x88\x01\x01\x12\x1b\n\x0epid_ki_heading\x18\t \x01(\x01H\x08\x88\x01\x01\x12\x1b\n\x0epid_kd_heading\x18\n \x01(\x01H\t\x88\x01\x01\x12%\n\x18pure_pursuit_lookahead_m\x18\x0b \x01(\x01H\n\x88\x01\x01\x12!\n\x14mpc_weight_q_lateral\x18\x0c \x01(\x01H\x0b\x88\x01\x01\x12\"\n\x15mpc_weight_r_steering\x18\r \x01(\x01H\x0c\x88\x01\x01\x42\x1a\n\x18_wheel_radius_left_ratioB\x1b\n\x19_wheel_radius_right_ratioB\x1a\n\x18_effective_track_width_mB\x1b\n\x19_steering_zero_offset_degB\x11\n\x0f_pid_kp_lateralB\x11\n\x0f_pid_ki_lateralB\x11\n\x0f_pid_kd_lateralB\x11\n\x0f_pid_kp_headingB\x11\n\x0f_pid_ki_headingB\x11\n\x0f_pid_kd_headingB\x1b\n\x19_pure_pursuit_lookahead_mB\x17\n\x15_mpc_weight_q_lateralB\x18\n\x16_mpc_weight_r_steering\"\xad\x02\n\rTelemetryData\x12\x1d\n\x15hardware_timestamp_us\x18\x01 \x01(\x03\x12\x10\n\x08odom_x_m\x18\x02 \x01(\x01\x12\x10\n\x08odom_y_m\x18\x03 \x01(\x01\x12\x14\n\x0codom_yaw_rad\x18\x04 \x01(\x01\x12\x1e\n\x16\x66\x65\x65\x64\x62\x61\x63k_linear_vel_ms\x18\x05 \x01(\x01\x12!\n\x19\x66\x65\x65\x64\x62\x61\x63k_angular_vel_rads\x18\x06 \x01(\x01\x12\x1e\n\x16left_motor_current_amp\x18\x07 \x01(\x01\x12\x1f\n\x17right_motor_current_amp\x18\x08 \x01(\x01\x12\"\n\x1asteering_motor_current_amp\x18\t \x01(\x01\x12\x1b\n\x13\x63md_steering_output\x18\n \x01(\x01\x32\xd1\x06\n\x16\x41gvCalibControlService\x12\x61\n\x0eSetControlMode\x12$.agv.calibration.control.ModeRequest\x1a).agv.calibration.control.StandardResponse\x12Z\n\rEmergencyStop\x12\x1e.agv.calibration.control.Empty\x1a).agv.calibration.control.StandardResponse\x12i\n\x12\x45xecuteOpenLoopCmd\x12(.agv.calibration.control.OpenLoopRequest\x1a).agv.calibration.control.StandardResponse\x12m\n\x14\x46ollowTestTrajectory\x12*.agv.calibration.control.TrajectoryRequest\x1a).agv.calibration.control.StandardResponse\x12n\n\x13\x45xecuteStepResponse\x12,.agv.calibration.control.StepResponseRequest\x1a).agv.calibration.control.StandardResponse\x12k\n\x16InjectTuningParameters\x12&.agv.calibration.control.ControlParams\x1a).agv.calibration.control.StandardResponse\x12\x64\n\x17\x43ommitControlParameters\x12\x1e.agv.calibration.control.Empty\x1a).agv.calibration.control.StandardResponse\x12[\n\x0fStreamTelemetry\x12\x1e.agv.calibration.control.Empty\x1a&.agv.calibration.control.TelemetryData0\x01\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agv_calib_control_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_EMPTY']._serialized_start=52 + _globals['_EMPTY']._serialized_end=59 + _globals['_STANDARDRESPONSE']._serialized_start=61 + _globals['_STANDARDRESPONSE']._serialized_end=113 + _globals['_MODEREQUEST']._serialized_start=116 + _globals['_MODEREQUEST']._serialized_end=255 + _globals['_MODEREQUEST_MODE']._serialized_start=195 + _globals['_MODEREQUEST_MODE']._serialized_end=255 + _globals['_OPENLOOPREQUEST']._serialized_start=257 + _globals['_OPENLOOPREQUEST']._serialized_end=369 + _globals['_TRAJECTORYPOINT']._serialized_start=371 + _globals['_TRAJECTORYPOINT']._serialized_end=475 + _globals['_TRAJECTORYREQUEST']._serialized_start=477 + _globals['_TRAJECTORYREQUEST']._serialized_end=574 + _globals['_STEPRESPONSEREQUEST']._serialized_start=576 + _globals['_STEPRESPONSEREQUEST']._serialized_end=647 + _globals['_CONTROLPARAMS']._serialized_start=650 + _globals['_CONTROLPARAMS']._serialized_end=1411 + _globals['_TELEMETRYDATA']._serialized_start=1414 + _globals['_TELEMETRYDATA']._serialized_end=1715 + _globals['_AGVCALIBCONTROLSERVICE']._serialized_start=1718 + _globals['_AGVCALIBCONTROLSERVICE']._serialized_end=2567 +# @@protoc_insertion_point(module_scope) diff --git a/agv_calib_eu/scripts/agv_calib_control_pb2_grpc.py b/agv_calib_eu/scripts/agv_calib_control_pb2_grpc.py new file mode 100644 index 0000000..5dd44cb --- /dev/null +++ b/agv_calib_eu/scripts/agv_calib_control_pb2_grpc.py @@ -0,0 +1,444 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import agv_calib_control_pb2 as agv__calib__control__pb2 + +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in agv_calib_control_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class AgvCalibControlServiceStub(object): + """========================================================= + 核心服务:AGV 运控大脑(PID/MPC)参数自动化寻优调教代理 + [部署端 Server]:Windows车端 (满血保留自身算法,负责执行闭环追踪与高频汇报) + [调用端 Client]:Linux标定服务器 (上帝视角,负责发轨迹、看误差、AI打分与发新参数) + ========================================================= + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.SetControlMode = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/SetControlMode', + request_serializer=agv__calib__control__pb2.ModeRequest.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.EmergencyStop = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/EmergencyStop', + request_serializer=agv__calib__control__pb2.Empty.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.ExecuteOpenLoopCmd = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/ExecuteOpenLoopCmd', + request_serializer=agv__calib__control__pb2.OpenLoopRequest.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.FollowTestTrajectory = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/FollowTestTrajectory', + request_serializer=agv__calib__control__pb2.TrajectoryRequest.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.ExecuteStepResponse = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/ExecuteStepResponse', + request_serializer=agv__calib__control__pb2.StepResponseRequest.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.InjectTuningParameters = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/InjectTuningParameters', + request_serializer=agv__calib__control__pb2.ControlParams.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.CommitControlParameters = channel.unary_unary( + '/agv.calibration.control.AgvCalibControlService/CommitControlParameters', + request_serializer=agv__calib__control__pb2.Empty.SerializeToString, + response_deserializer=agv__calib__control__pb2.StandardResponse.FromString, + _registered_method=True) + self.StreamTelemetry = channel.unary_stream( + '/agv.calibration.control.AgvCalibControlService/StreamTelemetry', + request_serializer=agv__calib__control__pb2.Empty.SerializeToString, + response_deserializer=agv__calib__control__pb2.TelemetryData.FromString, + _registered_method=True) + + +class AgvCalibControlServiceServicer(object): + """========================================================= + 核心服务:AGV 运控大脑(PID/MPC)参数自动化寻优调教代理 + [部署端 Server]:Windows车端 (满血保留自身算法,负责执行闭环追踪与高频汇报) + [调用端 Client]:Linux标定服务器 (上帝视角,负责发轨迹、看误差、AI打分与发新参数) + ========================================================= + """ + + def SetControlMode(self, request, context): + """--------------------------------------------------------- + 第一步:权限接管与生命周期安全管控 + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:要求切断避障,但保留底层 PID/MPC 算法就绪 + 🚙 [Windows 返回 -> Linux]:回复模式切换成功,准备好接考题 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def EmergencyStop(self, request, context): + """💻 [Linux 发送 -> Windows]:断网或飞车时的最高级别急停,无视一切直接刹车 + 🚙 [Windows 返回 -> Linux]:返回底层抱死结果 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExecuteOpenLoopCmd(self, request, context): + """--------------------------------------------------------- + 第二步:运动考题下发 (开环排雷 / 闭环寻优 / 波峰对齐) + --------------------------------------------------------- + 【场景A: 纯物理开环备用】 + 💻 [Linux 发送 -> Windows]:要求切断算法盲跑,多用于摸底或辅助验证 + 🚙 [Windows 返回 -> Linux]:确认已按指定 RPM/PWM 运转 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def FollowTestTrajectory(self, request, context): + """【场景B: 算法闭环调优】 + 💻 [Linux 发送 -> Windows]:下发一条由几百个点组成的测试轨迹(如 S型贝塞尔曲线) + 🚙 [Windows 返回 -> Linux]:收到轨迹后,车端立刻使用它自带的 PID/MPC 算法努力贴合轨迹跑圈 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ExecuteStepResponse(self, request, context): + """【场景C: 波峰时序对齐】 + 💻 [Linux 发送 -> Windows]:下发极短促的阶跃加速指令,人为制造绝对速度波峰 + 🚙 [Windows 返回 -> Linux]:确认加速。(Linux 借此波峰算出网络的绝对 Time Offset) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def InjectTuningParameters(self, request, context): + """--------------------------------------------------------- + 第三步:运控参数 AI 寻优:动态热注入与最终固化 + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:Linux 发现上一圈跑得差,AI算出了新的 PID/前瞻距离,要求立即热注入 + 🚙 [Windows 返回 -> Linux]:车端将新参数瞬间覆写进运行内存(不重启系统),随时准备用新参数重跑 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CommitControlParameters(self, request, context): + """💻 [Linux 发送 -> Windows]:Linux 判定误差极小,调优结束,命令固化目前内存里的最高分参数 + 🚙 [Windows 返回 -> Linux]:车端将这组完美参数永久覆写进硬盘的 config.yaml 或系统注册表 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def StreamTelemetry(self, request, context): + """--------------------------------------------------------- + 第四步:高频数字孪生体感上报 (50Hz) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:空包触发,命令车端开始疯狂推流 + 🚙 [Windows 持续流式返回 -> Linux]:以 50Hz 频率,持续上报自己的里程计坐标、速度和单调时间戳 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_AgvCalibControlServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'SetControlMode': grpc.unary_unary_rpc_method_handler( + servicer.SetControlMode, + request_deserializer=agv__calib__control__pb2.ModeRequest.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'EmergencyStop': grpc.unary_unary_rpc_method_handler( + servicer.EmergencyStop, + request_deserializer=agv__calib__control__pb2.Empty.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'ExecuteOpenLoopCmd': grpc.unary_unary_rpc_method_handler( + servicer.ExecuteOpenLoopCmd, + request_deserializer=agv__calib__control__pb2.OpenLoopRequest.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'FollowTestTrajectory': grpc.unary_unary_rpc_method_handler( + servicer.FollowTestTrajectory, + request_deserializer=agv__calib__control__pb2.TrajectoryRequest.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'ExecuteStepResponse': grpc.unary_unary_rpc_method_handler( + servicer.ExecuteStepResponse, + request_deserializer=agv__calib__control__pb2.StepResponseRequest.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'InjectTuningParameters': grpc.unary_unary_rpc_method_handler( + servicer.InjectTuningParameters, + request_deserializer=agv__calib__control__pb2.ControlParams.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'CommitControlParameters': grpc.unary_unary_rpc_method_handler( + servicer.CommitControlParameters, + request_deserializer=agv__calib__control__pb2.Empty.FromString, + response_serializer=agv__calib__control__pb2.StandardResponse.SerializeToString, + ), + 'StreamTelemetry': grpc.unary_stream_rpc_method_handler( + servicer.StreamTelemetry, + request_deserializer=agv__calib__control__pb2.Empty.FromString, + response_serializer=agv__calib__control__pb2.TelemetryData.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'agv.calibration.control.AgvCalibControlService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('agv.calibration.control.AgvCalibControlService', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class AgvCalibControlService(object): + """========================================================= + 核心服务:AGV 运控大脑(PID/MPC)参数自动化寻优调教代理 + [部署端 Server]:Windows车端 (满血保留自身算法,负责执行闭环追踪与高频汇报) + [调用端 Client]:Linux标定服务器 (上帝视角,负责发轨迹、看误差、AI打分与发新参数) + ========================================================= + """ + + @staticmethod + def SetControlMode(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/SetControlMode', + agv__calib__control__pb2.ModeRequest.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def EmergencyStop(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/EmergencyStop', + agv__calib__control__pb2.Empty.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExecuteOpenLoopCmd(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/ExecuteOpenLoopCmd', + agv__calib__control__pb2.OpenLoopRequest.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def FollowTestTrajectory(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/FollowTestTrajectory', + agv__calib__control__pb2.TrajectoryRequest.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ExecuteStepResponse(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/ExecuteStepResponse', + agv__calib__control__pb2.StepResponseRequest.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def InjectTuningParameters(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/InjectTuningParameters', + agv__calib__control__pb2.ControlParams.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CommitControlParameters(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.control.AgvCalibControlService/CommitControlParameters', + agv__calib__control__pb2.Empty.SerializeToString, + agv__calib__control__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def StreamTelemetry(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/agv.calibration.control.AgvCalibControlService/StreamTelemetry', + agv__calib__control__pb2.Empty.SerializeToString, + agv__calib__control__pb2.TelemetryData.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/agv_calib_eu/scripts/agv_calib_sensor_pb2.py b/agv_calib_eu/scripts/agv_calib_sensor_pb2.py new file mode 100644 index 0000000..5c75e55 --- /dev/null +++ b/agv_calib_eu/scripts/agv_calib_sensor_pb2.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agv_calib_sensor.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'agv_calib_sensor.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61gv_calib_sensor.proto\x12\x16\x61gv.calibration.sensor\"4\n\x10StandardResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"b\n\x0bPoseRequest\x12\x12\n\ntarget_x_m\x18\x01 \x01(\x01\x12\x12\n\ntarget_y_m\x18\x02 \x01(\x01\x12\x16\n\x0etarget_yaw_deg\x18\x03 \x01(\x01\x12\x13\n\x0bis_relative\x18\x04 \x01(\x08\"$\n\x0e\x43\x61ptureRequest\x12\x12\n\nsensor_ids\x18\x01 \x03(\t\"W\n\x0f\x43\x61ptureResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x1c\n\x14\x63\x61pture_timestamp_us\x18\x02 \x01(\x03\x12\x15\n\rerror_message\x18\x03 \x01(\t\"C\n\x10\x44\x61taFetchRequest\x12\x1c\n\x14\x63\x61pture_timestamp_us\x18\x01 \x01(\x03\x12\x11\n\tsensor_id\x18\x02 \x01(\t\"J\n\tFileChunk\x12\x12\n\nchunk_data\x18\x01 \x01(\x0c\x12\x15\n\ris_last_chunk\x18\x02 \x01(\x08\x12\x12\n\nformat_ext\x18\x03 \x01(\t\"j\n\x10\x43\x61meraIntrinsics\x12\x11\n\tcamera_id\x18\x01 \x01(\t\x12\n\n\x02\x66x\x18\x02 \x01(\x01\x12\n\n\x02\x66y\x18\x03 \x01(\x01\x12\n\n\x02\x63x\x18\x04 \x01(\x01\x12\n\n\x02\x63y\x18\x05 \x01(\x01\x12\x13\n\x0b\x64ist_coeffs\x18\x06 \x03(\x01\"\xb0\x01\n\x10SensorExtrinsics\x12\x14\n\x0csource_frame\x18\x01 \x01(\t\x12\x14\n\x0ctarget_frame\x18\x02 \x01(\t\x12\x12\n\ntrans_x_mm\x18\x03 \x01(\x01\x12\x12\n\ntrans_y_mm\x18\x04 \x01(\x01\x12\x12\n\ntrans_z_mm\x18\x05 \x01(\x01\x12\x10\n\x08roll_deg\x18\x06 \x01(\x01\x12\x11\n\tpitch_deg\x18\x07 \x01(\x01\x12\x0f\n\x07yaw_deg\x18\x08 \x01(\x01\"\xb1\x01\n\x12\x43\x61librationPayload\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x44\n\x12updated_intrinsics\x18\x02 \x03(\x0b\x32(.agv.calibration.sensor.CameraIntrinsics\x12\x44\n\x12updated_extrinsics\x18\x03 \x03(\x0b\x32(.agv.calibration.sensor.SensorExtrinsics2\xa0\x04\n\x18SensorCalibrationService\x12\x66\n\x15MoveToObservationPose\x12#.agv.calibration.sensor.PoseRequest\x1a(.agv.calibration.sensor.StandardResponse\x12\x65\n\x12TriggerSyncCapture\x12&.agv.calibration.sensor.CaptureRequest\x1a\'.agv.calibration.sensor.CaptureResponse\x12^\n\rDownloadImage\x12(.agv.calibration.sensor.DataFetchRequest\x1a!.agv.calibration.sensor.FileChunk0\x01\x12\x63\n\x12\x44ownloadPointCloud\x12(.agv.calibration.sensor.DataFetchRequest\x1a!.agv.calibration.sensor.FileChunk0\x01\x12p\n\x18\x43ommitCalibrationResults\x12*.agv.calibration.sensor.CalibrationPayload\x1a(.agv.calibration.sensor.StandardResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agv_calib_sensor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_STANDARDRESPONSE']._serialized_start=50 + _globals['_STANDARDRESPONSE']._serialized_end=102 + _globals['_POSEREQUEST']._serialized_start=104 + _globals['_POSEREQUEST']._serialized_end=202 + _globals['_CAPTUREREQUEST']._serialized_start=204 + _globals['_CAPTUREREQUEST']._serialized_end=240 + _globals['_CAPTURERESPONSE']._serialized_start=242 + _globals['_CAPTURERESPONSE']._serialized_end=329 + _globals['_DATAFETCHREQUEST']._serialized_start=331 + _globals['_DATAFETCHREQUEST']._serialized_end=398 + _globals['_FILECHUNK']._serialized_start=400 + _globals['_FILECHUNK']._serialized_end=474 + _globals['_CAMERAINTRINSICS']._serialized_start=476 + _globals['_CAMERAINTRINSICS']._serialized_end=582 + _globals['_SENSOREXTRINSICS']._serialized_start=585 + _globals['_SENSOREXTRINSICS']._serialized_end=761 + _globals['_CALIBRATIONPAYLOAD']._serialized_start=764 + _globals['_CALIBRATIONPAYLOAD']._serialized_end=941 + _globals['_SENSORCALIBRATIONSERVICE']._serialized_start=944 + _globals['_SENSORCALIBRATIONSERVICE']._serialized_end=1488 +# @@protoc_insertion_point(module_scope) diff --git a/agv_calib_eu/scripts/agv_calib_sensor_pb2_grpc.py b/agv_calib_eu/scripts/agv_calib_sensor_pb2_grpc.py new file mode 100644 index 0000000..1b552fd --- /dev/null +++ b/agv_calib_eu/scripts/agv_calib_sensor_pb2_grpc.py @@ -0,0 +1,306 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import agv_calib_sensor_pb2 as agv__calib__sensor__pb2 + +GRPC_GENERATED_VERSION = '1.78.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in agv_calib_sensor_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class SensorCalibrationServiceStub(object): + """========================================================= + 核心服务:多传感器自动化外参标定代理服务 + [部署端 Server]:Windows车端 (充当带轮子的三脚架与文件下载服务器) + [调用端 Client]:Linux标定服务器 (掌控状态机、拉取大文件、算 Ceres 矩阵) + ========================================================= + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.MoveToObservationPose = channel.unary_unary( + '/agv.calibration.sensor.SensorCalibrationService/MoveToObservationPose', + request_serializer=agv__calib__sensor__pb2.PoseRequest.SerializeToString, + response_deserializer=agv__calib__sensor__pb2.StandardResponse.FromString, + _registered_method=True) + self.TriggerSyncCapture = channel.unary_unary( + '/agv.calibration.sensor.SensorCalibrationService/TriggerSyncCapture', + request_serializer=agv__calib__sensor__pb2.CaptureRequest.SerializeToString, + response_deserializer=agv__calib__sensor__pb2.CaptureResponse.FromString, + _registered_method=True) + self.DownloadImage = channel.unary_stream( + '/agv.calibration.sensor.SensorCalibrationService/DownloadImage', + request_serializer=agv__calib__sensor__pb2.DataFetchRequest.SerializeToString, + response_deserializer=agv__calib__sensor__pb2.FileChunk.FromString, + _registered_method=True) + self.DownloadPointCloud = channel.unary_stream( + '/agv.calibration.sensor.SensorCalibrationService/DownloadPointCloud', + request_serializer=agv__calib__sensor__pb2.DataFetchRequest.SerializeToString, + response_deserializer=agv__calib__sensor__pb2.FileChunk.FromString, + _registered_method=True) + self.CommitCalibrationResults = channel.unary_unary( + '/agv.calibration.sensor.SensorCalibrationService/CommitCalibrationResults', + request_serializer=agv__calib__sensor__pb2.CalibrationPayload.SerializeToString, + response_deserializer=agv__calib__sensor__pb2.StandardResponse.FromString, + _registered_method=True) + + +class SensorCalibrationServiceServicer(object): + """========================================================= + 核心服务:多传感器自动化外参标定代理服务 + [部署端 Server]:Windows车端 (充当带轮子的三脚架与文件下载服务器) + [调用端 Client]:Linux标定服务器 (掌控状态机、拉取大文件、算 Ceres 矩阵) + ========================================================= + """ + + def MoveToObservationPose(self, request, context): + """--------------------------------------------------------- + 第一步:物理走位 (走) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:调度车辆开到指定的标定观测点,到达后【绝对刹车静止】 + 🚙 [Windows 返回 -> Linux]:物理到位抱死刹车后,返回成功回执 + 🚨 业务潜台词:Linux 收到回执后,必须在代码里强制 sleep(0.5s) 等待避震悬挂平息,冻结物理空间! + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def TriggerSyncCapture(self, request, context): + """--------------------------------------------------------- + 第二步:防延迟同步锁存 (停与拍) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:命令车辆瞬间将底层相机的显存和雷达的点云冻结到后备内存池 + 🚙 [Windows 返回 -> Linux]:立刻锁存,并返回高精度硬件时间戳,作为后续拉取大文件的唯一“取件码” + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DownloadImage(self, request, context): + """--------------------------------------------------------- + 第三步:大文件流式下载 (传 —— 破解 Windows 网络延迟的绝杀) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:凭“取件码”请求下载巨大的图片/点云原文件 + 🚙 [Windows 持续流式返回 -> Linux]:将 5MB+ 的无损文件切成小块,像流水一样源源不断传回 Linux + 🚨 业务潜台词:必须使用 stream 关键字!否则 gRPC 会因为单包超过 4MB 瞬间崩溃! + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DownloadPointCloud(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CommitCalibrationResults(self, request, context): + """--------------------------------------------------------- + 第四步:标定闭环定稿 (写) + --------------------------------------------------------- + 💻 [Linux 发送 -> Windows]:Linux 攒够数据算完复杂的 4x4 外参矩阵后,下发给车端持久化保存 + 🚙 [Windows 返回 -> Linux]:车端收到后直接覆写 sensor_config.yaml 或注册表,返回成功 + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_SensorCalibrationServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'MoveToObservationPose': grpc.unary_unary_rpc_method_handler( + servicer.MoveToObservationPose, + request_deserializer=agv__calib__sensor__pb2.PoseRequest.FromString, + response_serializer=agv__calib__sensor__pb2.StandardResponse.SerializeToString, + ), + 'TriggerSyncCapture': grpc.unary_unary_rpc_method_handler( + servicer.TriggerSyncCapture, + request_deserializer=agv__calib__sensor__pb2.CaptureRequest.FromString, + response_serializer=agv__calib__sensor__pb2.CaptureResponse.SerializeToString, + ), + 'DownloadImage': grpc.unary_stream_rpc_method_handler( + servicer.DownloadImage, + request_deserializer=agv__calib__sensor__pb2.DataFetchRequest.FromString, + response_serializer=agv__calib__sensor__pb2.FileChunk.SerializeToString, + ), + 'DownloadPointCloud': grpc.unary_stream_rpc_method_handler( + servicer.DownloadPointCloud, + request_deserializer=agv__calib__sensor__pb2.DataFetchRequest.FromString, + response_serializer=agv__calib__sensor__pb2.FileChunk.SerializeToString, + ), + 'CommitCalibrationResults': grpc.unary_unary_rpc_method_handler( + servicer.CommitCalibrationResults, + request_deserializer=agv__calib__sensor__pb2.CalibrationPayload.FromString, + response_serializer=agv__calib__sensor__pb2.StandardResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'agv.calibration.sensor.SensorCalibrationService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('agv.calibration.sensor.SensorCalibrationService', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class SensorCalibrationService(object): + """========================================================= + 核心服务:多传感器自动化外参标定代理服务 + [部署端 Server]:Windows车端 (充当带轮子的三脚架与文件下载服务器) + [调用端 Client]:Linux标定服务器 (掌控状态机、拉取大文件、算 Ceres 矩阵) + ========================================================= + """ + + @staticmethod + def MoveToObservationPose(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.sensor.SensorCalibrationService/MoveToObservationPose', + agv__calib__sensor__pb2.PoseRequest.SerializeToString, + agv__calib__sensor__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def TriggerSyncCapture(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.sensor.SensorCalibrationService/TriggerSyncCapture', + agv__calib__sensor__pb2.CaptureRequest.SerializeToString, + agv__calib__sensor__pb2.CaptureResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DownloadImage(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/agv.calibration.sensor.SensorCalibrationService/DownloadImage', + agv__calib__sensor__pb2.DataFetchRequest.SerializeToString, + agv__calib__sensor__pb2.FileChunk.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DownloadPointCloud(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/agv.calibration.sensor.SensorCalibrationService/DownloadPointCloud', + agv__calib__sensor__pb2.DataFetchRequest.SerializeToString, + agv__calib__sensor__pb2.FileChunk.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CommitCalibrationResults(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/agv.calibration.sensor.SensorCalibrationService/CommitCalibrationResults', + agv__calib__sensor__pb2.CalibrationPayload.SerializeToString, + agv__calib__sensor__pb2.StandardResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/agv_calib_eu/scripts/mock_windows_agv.py b/agv_calib_eu/scripts/mock_windows_agv.py new file mode 100644 index 0000000..7fed2ff --- /dev/null +++ b/agv_calib_eu/scripts/mock_windows_agv.py @@ -0,0 +1,51 @@ +import grpc +from concurrent import futures +import time + +# 导入刚才生成的 3 个 Python 契约库 +import agv_calib_chassis_pb2 as chassis_pb2, agv_calib_chassis_pb2_grpc as chassis_grpc +import agv_calib_control_pb2 as control_pb2, agv_calib_control_pb2_grpc as control_grpc +import agv_calib_sensor_pb2 as sensor_pb2, agv_calib_sensor_pb2_grpc as sensor_grpc + +# 1. 伪装物理底盘 +class FakeChassis(chassis_grpc.AgvCalibChassisServiceServicer): + def SetDiagnosticMode(self, request, context): + mode = "纯物理直驱" if request.target_mode == 1 else "正常算法" + print(f"\n[⚙️ 底盘硬件] 收到 Linux 夺权指令!切换至: {mode}模式") + return chassis_pb2.StandardResponse(success=True, message="底盘已交出控制权") + + def StreamHardwareTelemetry(self, request, context): + print("[🌊 底盘硬件] 开始向 Linux 推送 50Hz 硬件裸数据流...") + while context.is_active(): + # 疯狂发送假脉冲数据 + yield chassis_pb2.HardwareState(hardware_timestamp_us=int(time.time()*1000000), encoder_ticks_fl=1024, current_fl_amp=2.5) + time.sleep(0.02) # 50Hz + +# 2. 伪装运控大脑 +class FakeControl(control_grpc.AgvCalibControlServiceServicer): + def InjectTuningParameters(self, request, context): + print(f"\n[🧠 运控大脑] 收到 AI 热注入参数!Kp 被修改为: {request.pid_kp_lateral}") + return control_pb2.StandardResponse(success=True, message="PID参数已瞬间写入内存") + +# 3. 伪装传感器代理 +class FakeSensor(sensor_grpc.SensorCalibrationServiceServicer): + def TriggerSyncCapture(self, request, context): + print(f"\n[📷 传感器代理] 收到冻结指令!要求拍摄: {request.sensor_ids}") + time.sleep(0.5) # 模拟硬件快门延迟 + print("[📷 传感器代理] 咔嚓!照片和点云已锁存。取件码: 999888777") + return sensor_pb2.CaptureResponse(success=True, capture_timestamp_us=999888777) + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + # 三大假硬件全挂载到同一个网络端口! + chassis_grpc.add_AgvCalibChassisServiceServicer_to_server(FakeChassis(), server) + control_grpc.add_AgvCalibControlServiceServicer_to_server(FakeControl(), server) + sensor_grpc.add_SensorCalibrationServiceServicer_to_server(FakeSensor(), server) + + server.add_insecure_port('[::]:50051') + print("🚀 [全能 Windows 假车端] 已启动,正在监听 50051 端口,等待 ROS 2 召唤...") + server.start() + server.wait_for_termination() + +if __name__ == '__main__': + serve() \ No newline at end of file diff --git a/agv_calib_eu/txt.txt b/agv_calib_eu/txt.txt new file mode 100644 index 0000000..6a297e7 --- /dev/null +++ b/agv_calib_eu/txt.txt @@ -0,0 +1 @@ +cd scripts && python3 -m grpc_tools.protoc -I../proto --python_out=. --grpc_python_out=. ../proto/*.proto