feat: 已添加win_ubuntu_bridge

This commit is contained in:
li-shihao-code
2026-03-06 17:16:50 +08:00
parent 1ea480eccd
commit 18d6500d04
21 changed files with 1673 additions and 196 deletions
@@ -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(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
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
)
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()
@@ -0,0 +1,9 @@
<root main_tree_to_execute="MainTree">
<BehaviorTree ID="MainTree">
<Sequence name="全自动标定总流程">
<SetChassisMode target_mode="1" />
<TriggerCapture sensor_id="cam_front" capture_code_out="{shared_code}" />
<DownloadData sensor_id="cam_front" capture_code_in="{shared_code}" save_dir="/tmp/calib_data" saved_path_out="{saved_image_path}" />
</Sequence>
</BehaviorTree>
</root>
@@ -0,0 +1,6 @@
/**:
ros__parameters:
# 动态指定要加载的 XML 剧本文件名
tree_xml_filename: "main_tree.xml"
# 行为树的 Tick 循环频率 (毫秒)
tick_rate_ms: 50
@@ -0,0 +1,25 @@
#pragma once
#include <rclcpp/rclcpp.hpp>
#include <behaviortree_cpp_v3/bt_factory.h>
#include <thread>
#include <atomic>
#include <string>
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<bool> is_running_;
};
} // namespace agv_calib_core
@@ -0,0 +1,109 @@
#pragma once
#include <behaviortree_cpp_v3/action_node.h>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_action/rclcpp_action.hpp>
// 引入底层 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<win_ubuntu_bridge::srv::SetDiagnosticMode>("/chassis_gateway/set_diagnostic_mode");
}
static BT::PortsList providedPorts() { return { BT::InputPort<int>("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<win_ubuntu_bridge::srv::SetDiagnosticMode::Request>(); 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<win_ubuntu_bridge::srv::SetDiagnosticMode>::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<win_ubuntu_bridge::srv::TriggerSyncCapture>("/sensor_gateway/trigger_sync_capture");
}
static BT::PortsList providedPorts() {
return { BT::InputPort<std::string>("sensor_id"), BT::OutputPort<int64_t>("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<win_ubuntu_bridge::srv::TriggerSyncCapture::Request>(); 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<win_ubuntu_bridge::srv::TriggerSyncCapture>::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<win_ubuntu_bridge::action::DownloadSensorData>(node_, "/sensor_gateway/download_sensor_data");
}
static BT::PortsList providedPorts() {
return { BT::InputPort<std::string>("sensor_id"), BT::InputPort<int64_t>("capture_code_in"),
BT::InputPort<std::string>("save_dir"), BT::OutputPort<std::string>("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<win_ubuntu_bridge::action::DownloadSensorData>::SendGoalOptions();
send_goal_options.result_callback = [this](const rclcpp_action::ClientGoalHandle<win_ubuntu_bridge::action::DownloadSensorData>::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<win_ubuntu_bridge::action::DownloadSensorData>::SharedPtr action_client_;
bool done_ = false; bool success_ = false;
};
} // namespace agv_calib_core
@@ -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])
@@ -2,19 +2,19 @@
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>agv_calib_core</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<version>1.0.0</version>
<description>行为树总控大脑</description>
<maintainer email="2469171725@qq.com">nvidia</maintainer>
<license>TODO: License declaration</license>
<license>Apache-2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>rclcpp_action</depend>
<depend>rclcpp_components</depend>
<depend>behaviortree_cpp_v3</depend>
<depend>ament_index_cpp</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<depend>win_ubuntu_bridge</depend>
<export>
<build_type>ament_cmake</build_type>
@@ -0,0 +1,87 @@
#include "agv_calib_core/brain_node.hpp"
#include "agv_calib_core/bt_ros2_nodes.hpp"
#include <behaviortree_cpp_v3/bt_factory.h>
#include <behaviortree_cpp_v3/loggers/bt_cout_logger.h>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <rclcpp_components/register_node_macro.hpp>
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<std::string>("tree_xml_filename", "main_tree.xml");
this->declare_parameter<int>("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<SetChassisModeNode>("SetChassisMode",
[this](const std::string& name, const BT::NodeConfiguration& config) {
return std::make_unique<SetChassisModeNode>(name, config, this);
});
factory.registerBuilder<TriggerCaptureNode>("TriggerCapture",
[this](const std::string& name, const BT::NodeConfiguration& config) {
return std::make_unique<TriggerCaptureNode>(name, config, this);
});
factory.registerBuilder<DownloadDataNode>("DownloadData",
[this](const std::string& name, const BT::NodeConfiguration& config) {
return std::make_unique<DownloadDataNode>(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)
@@ -1,82 +0,0 @@
#pragma once
#include <behaviortree_cpp_v3/action_node.h>
#include <iostream>
#include <thread>
#include <chrono>
// 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;
}
};
@@ -1,54 +0,0 @@
#pragma once
#include <behaviortree_cpp_v3/action_node.h>
#include <grpcpp/grpcpp.h>
// 引入 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<grpc::Channel> channel_;
std::unique_ptr<AgvCalibControlService::Stub> stub_;
};
@@ -1,44 +0,0 @@
#include <rclcpp/rclcpp.hpp>
#include <behaviortree_cpp_v3/bt_factory.h>
#include <ament_index_cpp/get_package_share_directory.hpp>
// 引入刚才写的假节点
#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>("MockConnectAGV");
factory.registerNodeType<MockCallChassisAlgo>("MockCallChassisAlgo");
factory.registerNodeType<MockTuneControl>("MockTuneControl");
factory.registerNodeType<MockCallControlAlgo>("MockCallControlAlgo");
factory.registerNodeType<MockMoveAndCapture>("MockMoveAndCapture");
factory.registerNodeType<MockCallSensorAlgo>("MockCallSensorAlgo");
factory.registerNodeType<MockCommitAllParams>("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;
}
@@ -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()
@@ -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])
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
+51
View File
@@ -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()
+1
View File
@@ -0,0 +1 @@
cd scripts && python3 -m grpc_tools.protoc -I../proto --python_out=. --grpc_python_out=. ../proto/*.proto