128|1

3

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

《ROS2智能机器人开发与实践》——ROS核心(节点、话题、服务、DDS通信协议等) [复制链接]

 
# 《ROS2智能机器人开发与实践》——ROS核心(节点、话题、服务、DDS通信协议等)
今天主要是分享一下ROS2的核心包括节点、话题、服务和DDS通信协议的内容
其中会对官方和本书的相关细节进行比对
本文章都以C++为例

一、节点

1、ROS2中的节点具有以下几个特点

  1. 每个节点是一个进程
  2. 每个节点都是一个可以独立运行的可执行文件
  3. 每个节点可使用不同的编程语言
  4. 每个节点可分布式运行在不同的主机中
  5. 每个节点需要唯一的名称

2、节点编程方法(C++)

a.C++程序编写

  1. #include "rclcpp/rclcpp.hpp"
  2. /***
  3. 创建一个HelloWorld节点, 初始化时输出“hello world”日志
  4. ***/
  5. class HelloWorldNode : public rclcpp::Node
  6. {
  7. public:
  8. HelloWorldNode()
  9. : Node("node_helloworld_class") // ROS2节点父类初始化
  10. {
  11. while(rclcpp::ok()) // ROS2系统是否正常运行
  12. {
  13. RCLCPP_INFO(this->get_logger(), "Hello World");// ROS2日志输出
  14. sleep(1); // 休眠控制循环时间
  15. }
  16. }
  17. };
  18. // ROS2节点主入口main函数
  19. int main(int argc, char * argv[])
  20. {
  21. // ROS2 C++接口初始化
  22. rclcpp::init(argc, argv);
  23. // 创建ROS2节点对象并进行初始化
  24. rclcpp::spin(std::make_shared<HelloWorldNode>());
  25. // 关闭ROS2 C++接口
  26. rclcpp::shutdown();
  27. return 0;
  28. }

** 可以看的出来ROS2对节点的编程推荐面向对象编程的编程方法**
同时ROS2的编程难度更大、C++的语法更新、所以使用python也不失为一种方法

b.设置编译选项(编译接口)

  1. find_package(rclcpp REQUIRED)
  2. add_executable(node_helloworld_class src/node_helloworld_class.cpp)
  3. ament_target_dependencies(node_helloworld_class rclcpp)
  4. install(TARGETS
  5. node_helloworld_class
  6. DESTINATION lib/${PROJECT_NAME})

前三行相对于ROS1没什么太大区别,主要是多了最后这个

将编译生成的可执行文件拷贝到install安装空间的lib文件夹下
我们知道ROS2相对于ROS1的工作空间构建方式变了,其中多了install文件夹 所以要注意最后一行

c.编译运行

  1. #进入工作空间后
  2. colcon build #编译工作空间
  3. ros2 run learning_node_cpp node_helloworld_class # (功能包 可执行文件)
  4. ros2 node list # 查看节点列表
  5. ros2 node info <node_name> # 查看节点信息

二、话题

1.rqt_graph

  1. ros2 run turtlesim turtlesim_node
  2. ros2 run turtlesim turtle_teleop_key
  3. rqt_graph

我们将使用 rqt_graph 来可视化不断变化的节点和主题,以及它们之间的联系。

话题的特点:

  • 多对多通信
  • 异步通信
  • 消息接口

话题通信的一个特性,就是异步通信所谓异步,只要是指发布者发出数据后,并不知道订阅者什么时候可以收到。所以ROS2的后台会有一个轮询机制,当发现有数据进入消息队列时,就会触发回调函数

2.发布话题

  1. /***
  2. @作者: 古月居(www.guyuehome.com)
  3. @说明: ROS2话题示例-发布图像话题
  4. ***/
  5. #include <chrono>
  6. #include <functional>
  7. #include <memory>
  8. #include <string>
  9. #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
  10. #include "std_msgs/msg/string.hpp" // 字符串消息类型
  11. using namespace std::chrono_literals;
  12. class PublisherNode : public rclcpp::Node
  13. {
  14. public:
  15. PublisherNode()
  16. : Node("topic_helloworld_pub") // ROS2节点父类初始化
  17. {
  18. // 创建发布者对象(消息类型、话题名、队列长度)
  19. publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);
  20. // 创建一个定时器,定时执行回调函数
  21. timer_ = this->create_wall_timer(
  22. 500ms, std::bind(&PublisherNode::timer_callback, this));
  23. }
  24. private:
  25. // 创建定时器周期执行的回调函数
  26. void timer_callback()
  27. {
  28. // 创建一个String类型的消息对象
  29. auto msg = std_msgs::msg::String();
  30. // 填充消息对象中的消息数据
  31. msg.data = "Hello World";
  32. // 发布话题消息
  33. RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", msg.data.c_str());
  34. // 输出日志信息,提示已经完成话题发布
  35. publisher_->publish(msg);
  36. }
  37. rclcpp::TimerBase::SharedPtr timer_; // 定时器指针
  38. rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;// 发布者指针
  39. };
  40. // ROS2节点主入口main函数
  41. int main(int argc, char * argv[])
  42. {
  43. // ROS2 C++接口初始化
  44. rclcpp::init(argc, argv);
  45. // 创建ROS2节点对象并进行初始化
  46. rclcpp::spin(std::make_shared<PublisherNode>());
  47. // 关闭ROS2 C++接口
  48. rclcpp::shutdown();
  49. return 0;
  50. }

都是面向对象编程

3.订阅话题

  1. /***
  2. @作者: 古月居(www.guyuehome.com)
  3. @说明: ROS2话题示例-订阅“Hello World”话题消息
  4. ***/
  5. #include <memory>
  6. #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
  7. #include "std_msgs/msg/string.hpp" // 字符串消息类型
  8. using std::placeholders::_1;
  9. class SubscriberNode : public rclcpp::Node
  10. {
  11. public:
  12. SubscriberNode()
  13. : Node("topic_helloworld_sub") // ROS2节点父类初始化
  14. {
  15. subscription_ = this->create_subscription<std_msgs::msg::String>(
  16. "chatter", 10, std::bind(&SubscriberNode::topic_callback, this, _1)); // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
  17. }
  18. private:
  19. void topic_callback(const std_msgs::msg::String & msg) const // 创建回调函数,执行收到话题消息后对数据的处理
  20. {
  21. RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str()); // 输出日志信息,提示订阅收到的话题消息
  22. }
  23. rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_; // 订阅者指针
  24. };
  25. // ROS2节点主入口main函数
  26. int main(int argc, char * argv[])
  27. {
  28. // ROS2 C++接口初始化
  29. rclcpp::init(argc, argv);
  30. // 创建ROS2节点对象并进行初始化
  31. rclcpp::spin(std::make_shared<SubscriberNode>());
  32. // 关闭ROS2 C++接口
  33. rclcpp::shutdown();
  34. return 0;
  35. }

3.服务

服务可以实现类似你问我答的同步通信效果

a.服务的特点

服务的特点:
  • 同步通信
  • 一对多通信(服务端唯一)
  • 服务接口

b.客户端

  1. /***
  2. @作者: 古月居(www.guyuehome.com)
  3. @说明: ROS2服务示例-发送两个加数,请求加法器计算
  4. ***/
  5. #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
  6. #include "learning_interface/srv/add_two_ints.hpp" // 自定义的服务接口
  7. #include <chrono>
  8. #include <cstdlib>
  9. #include <memory>
  10. using namespace std::chrono_literals;
  11. int main(int argc, char **argv)
  12. {
  13. // ROS2 C++接口初始化
  14. rclcpp::init(argc, argv);
  15. if (argc != 3) {
  16. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: service_adder_client X Y");
  17. return 1;
  18. }
  19. // 创建ROS2节点对象并进行初始化
  20. std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("service_adder_client");
  21. // 创建服务客户端对象(服务接口类型,服务名)
  22. rclcpp::Client<learning_interface::srv::AddTwoInts>::SharedPtr client =
  23. node->create_client<learning_interface::srv::AddTwoInts>("add_two_ints");
  24. // 创建服务接口数据
  25. auto request = std::make_shared<learning_interface::srv::AddTwoInts::Request>();
  26. request->a = atoll(argv[1]);
  27. request->b = atoll(argv[2]);
  28. // 循环等待服务器端成功启动
  29. while (!client->wait_for_service(1s)) {
  30. if (!rclcpp::ok()) {
  31. RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
  32. return 0;
  33. }
  34. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  35. }
  36. // 异步方式发送服务请求
  37. auto result = client->async_send_request(request);
  38. // 接收服务器端的反馈数据
  39. if (rclcpp::spin_until_future_complete(node, result) ==
  40. rclcpp::FutureReturnCode::SUCCESS)
  41. {
  42. // 将收到的反馈信息打印输出
  43. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  44. } else {
  45. RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");
  46. }
  47. // 关闭ROS2 C++接口
  48. rclcpp::shutdown();
  49. return 0;
  50. }

c. 服务端

  1. /***
  2. @作者: 古月居(www.guyuehome.com)
  3. @说明: ROS2服务示例-提供加法器的服务器处理功能
  4. ***/
  5. #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
  6. #include "learning_interface/srv/add_two_ints.hpp" // 自定义的服务接口
  7. #include <memory>
  8. // 创建回调函数,执行收到请求后对数据的处理
  9. void adderServer(const std::shared_ptr<learning_interface::srv::AddTwoInts::Request> request,
  10. std::shared_ptr<learning_interface::srv::AddTwoInts::Response> response)
  11. {
  12. // 完成加法求和计算,将结果放到反馈的数据中
  13. response->sum = request->a + request->b;
  14. // 输出日志信息,提示已经完成加法求和计算
  15. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",
  16. request->a, request->b);
  17. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
  18. }
  19. // ROS2节点主入口main函数
  20. int main(int argc, char **argv)
  21. {
  22. // ROS2 C++接口初始化
  23. rclcpp::init(argc, argv);
  24. // 创建ROS2节点对象并进行初始化
  25. std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("service_adder_server");
  26. // 创建服务器对象(接口类型、服务名、服务器回调函数)
  27. rclcpp::Service<learning_interface::srv::AddTwoInts>::SharedPtr service =
  28. node->create_service<learning_interface::srv::AddTwoInts>("add_two_ints", &adderServer);
  29. RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");
  30. // 循环等待ROS2退出
  31. rclcpp::spin(node);
  32. // 关闭ROS2 C++接口
  33. rclcpp::shutdown();
  34. }

d.命令行操作

  1. ros2 service list # 查看服务列表
  2. ros2 service type <service_name> # 查看服务数据类型
  3. ros2 service call <service_name> <service_type> <service_data> # 发送服务请求

4.通信接口


同ROS1一样ROS2同样有的可自定义的通信接口,这些通信接口大大拓展了ROS的应用场景
具体的自定义接口的方法可以看官方文档或者

5.动作

动作是一种应用层的通信机制,其底层是基于话题和服务实现的

a. 服务端

  1. /***
  2. @作者: 古月居(www.guyuehome.com)
  3. @说明: ROS2动作示例-负责执行圆周运动动作的服务端
  4. ***/
  5. #include <iostream>
  6. #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
  7. #include "rclcpp_action/rclcpp_action.hpp" // ROS2 动作类
  8. #include "learning_interface/action/move_circle.hpp"// 自定义的圆周运动接口
  9. using namespace std;
  10. class MoveCircleActionServer : public rclcpp::Node
  11. {
  12. public:
  13. // 定义一个自定义的动作接口类,便于后续使用
  14. using CustomAction = learning_interface::action::MoveCircle;
  15. // 定义一个处理动作请求、取消、执行的服务器端
  16. using GoalHandle = rclcpp_action::ServerGoalHandle<CustomAction>;
  17. explicit MoveCircleActionServer(const rclcpp::NodeOptions & action_server_options = rclcpp::NodeOptions())
  18. : Node("action_move_server", action_server_options) // ROS2节点父类初始化
  19. {
  20. using namespace std::placeholders;
  21. this->action_server_ = rclcpp_action::create_server<CustomAction>( // 创建动作服务器(接口类型、动作名、回调函数)
  22. this->get_node_base_interface(),
  23. this->get_node_clock_interface(),
  24. this->get_node_logging_interface(),
  25. this->get_node_waitables_interface(),
  26. "move_circle",
  27. std::bind(&MoveCircleActionServer::handle_goal, this, _1, _2),
  28. std::bind(&MoveCircleActionServer::handle_cancel, this, _1),
  29. std::bind(&MoveCircleActionServer::handle_accepted, this, _1));
  30. }
  31. private:
  32. rclcpp_action::Server<CustomAction>::SharedPtr action_server_;// 动作服务器
  33. // 响应动作目标的请求
  34. rclcpp_action::GoalResponse handle_goal(
  35. const rclcpp_action::GoalUUID & uuid,
  36. std::shared_ptr<const CustomAction::Goal> goal_request)
  37. {
  38. RCLCPP_INFO(this->get_logger(), "Server: Received goal request: %d", goal_request->enable);
  39. (void)uuid;
  40. // 如请求为enable则接受运动请求,否则就拒绝
  41. if (goal_request->enable)
  42. {
  43. return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
  44. }
  45. else
  46. {
  47. return rclcpp_action::GoalResponse::REJECT;
  48. }
  49. }
  50. // 响应动作取消的请求
  51. rclcpp_action::CancelResponse handle_cancel(
  52. const std::shared_ptr<GoalHandle> goal_handle_canceled_)
  53. {
  54. RCLCPP_INFO(this->get_logger(), "Server: Received request to cancel action");
  55. (void) goal_handle_canceled_;
  56. return rclcpp_action::CancelResponse::ACCEPT;
  57. }
  58. // 处理动作接受后具体执行的过程
  59. void handle_accepted(const std::shared_ptr<GoalHandle> goal_handle_accepted_)
  60. {
  61. using namespace std::placeholders;
  62. // 在线程中执行动作过程
  63. std::thread{std::bind(&MoveCircleActionServer::execute, this, _1), goal_handle_accepted_}.detach();
  64. }
  65. void execute(const std::shared_ptr<GoalHandle> goal_handle_)
  66. {
  67. const auto requested_goal = goal_handle_->get_goal(); // 动作目标
  68. auto feedback = std::make_shared<CustomAction::Feedback>(); // 动作反馈
  69. auto result = std::make_shared<CustomAction::Result>(); // 动作结果
  70. RCLCPP_INFO(this->get_logger(), "Server: Executing goal");
  71. rclcpp::Rate loop_rate(1);
  72. // 动作执行的过程
  73. for (int i = 0; (i < 361) && rclcpp::ok(); i=i+30)
  74. {
  75. // 检查是否取消动作
  76. if (goal_handle_->is_canceling())
  77. {
  78. result->finish = false;
  79. goal_handle_->canceled(result);
  80. RCLCPP_INFO(this->get_logger(), "Server: Goal canceled");
  81. return;
  82. }
  83. // 更新反馈状态
  84. feedback->state = i;
  85. // 发布反馈状态
  86. goal_handle_->publish_feedback(feedback);
  87. RCLCPP_INFO(this->get_logger(), "Server: Publish feedback");
  88. loop_rate.sleep();
  89. }
  90. // 动作执行完成
  91. if (rclcpp::ok())
  92. {
  93. result->finish = true;
  94. goal_handle_->succeed(result);
  95. RCLCPP_INFO(this->get_logger(), "Server: Goal succeeded");
  96. }
  97. }
  98. };
  99. // ROS2节点主入口main函数
  100. int main(int argc, char * argv[])
  101. {
  102. // ROS2 C++接口初始化
  103. rclcpp::init(argc, argv);
  104. // 创建ROS2节点对象并进行初始化
  105. rclcpp::spin(std::make_shared<MoveCircleActionServer>());
  106. // 关闭ROS2 C++接口
  107. rclcpp::shutdown();
  108. return 0;
  109. }

b. 客户端

  1. /***
  2. @作者: 古月居(www.guyuehome.com)
  3. @说明: ROS2动作示例-请求执行圆周运动动作的客户端
  4. ***/
  5. #include <iostream>
  6. #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
  7. #include "rclcpp_action/rclcpp_action.hpp" // ROS2 动作类
  8. #include "learning_interface/action/move_circle.hpp"// 自定义的圆周运动接口
  9. using namespace std;
  10. class MoveCircleActionClient : public rclcpp::Node
  11. {
  12. public:
  13. // 定义一个自定义的动作接口类,便于后续使用
  14. using CustomAction = learning_interface::action::MoveCircle;
  15. // 定义一个处理动作请求、取消、执行的客户端类
  16. using GoalHandle = rclcpp_action::ClientGoalHandle<CustomAction>;
  17. explicit MoveCircleActionClient(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
  18. : Node("action_move_client", node_options) // ROS2节点父类初始化
  19. {
  20. this->client_ptr_ = rclcpp_action::create_client<CustomAction>( // 创建动作客户端(接口类型、动作名)
  21. this->get_node_base_interface(),
  22. this->get_node_graph_interface(),
  23. this->get_node_logging_interface(),
  24. this->get_node_waitables_interface(),
  25. "move_circle");
  26. }
  27. // 创建一个发送动作目标的函数
  28. void send_goal(bool enable)
  29. {
  30. // 检查动作服务器是否可以使用
  31. if (!this->client_ptr_->wait_for_action_server(std::chrono::seconds(10)))
  32. {
  33. RCLCPP_ERROR(this->get_logger(), "Client: Action server not available after waiting");
  34. rclcpp::shutdown();
  35. return;
  36. }
  37. // 绑定动作请求、取消、执行的回调函数
  38. auto send_goal_options = rclcpp_action::Client<CustomAction>::SendGoalOptions();
  39. using namespace std::placeholders;
  40. send_goal_options.goal_response_callback =
  41. std::bind(&MoveCircleActionClient::goal_response_callback, this, _1);
  42. send_goal_options.feedback_callback =
  43. std::bind(&MoveCircleActionClient::feedback_callback, this, _1, _2);
  44. send_goal_options.result_callback =
  45. std::bind(&MoveCircleActionClient::result_callback, this, _1);
  46. // 创建一个动作目标的消息
  47. auto goal_msg = CustomAction::Goal();
  48. goal_msg.enable = enable;
  49. // 异步方式发送动作的目标
  50. RCLCPP_INFO(this->get_logger(), "Client: Sending goal");
  51. this->client_ptr_->async_send_goal(goal_msg, send_goal_options);
  52. }
  53. private:
  54. rclcpp_action::Client<CustomAction>::SharedPtr client_ptr_;
  55. // 创建一个服务器收到目标之后反馈时的回调函数
  56. void goal_response_callback(GoalHandle::SharedPtr goal_message)
  57. {
  58. if (!goal_message)
  59. {
  60. RCLCPP_ERROR(this->get_logger(), "Client: Goal was rejected by server");
  61. rclcpp::shutdown(); // Shut down client node
  62. }
  63. else
  64. {
  65. RCLCPP_INFO(this->get_logger(), "Client: Goal accepted by server, waiting for result");
  66. }
  67. }
  68. // 创建处理周期反馈消息的回调函数
  69. void feedback_callback(
  70. GoalHandle::SharedPtr,
  71. const std::shared_ptr<const CustomAction::Feedback> feedback_message)
  72. {
  73. std::stringstream ss;
  74. ss << "Client: Received feedback: "<< feedback_message->state;
  75. RCLCPP_INFO(this->get_logger(), "%s", ss.str().c_str());
  76. }
  77. // 创建一个收到最终结果的回调函数
  78. void result_callback(const GoalHandle::WrappedResult & result_message)
  79. {
  80. switch (result_message.code)
  81. {
  82. case rclcpp_action::ResultCode::SUCCEEDED:
  83. break;
  84. case rclcpp_action::ResultCode::ABORTED:
  85. RCLCPP_ERROR(this->get_logger(), "Client: Goal was aborted");
  86. rclcpp::shutdown(); // 关闭客户端节点
  87. return;
  88. case rclcpp_action::ResultCode::CANCELED:
  89. RCLCPP_ERROR(this->get_logger(), "Client: Goal was canceled");
  90. rclcpp::shutdown(); // 关闭客户端节点
  91. return;
  92. default:
  93. RCLCPP_ERROR(this->get_logger(), "Client: Unknown result code");
  94. rclcpp::shutdown(); // 关闭客户端节点
  95. return;
  96. }
  97. RCLCPP_INFO(this->get_logger(), "Client: Result received: %s", (result_message.result->finish ? "true" : "false"));
  98. rclcpp::shutdown(); // 关闭客户端节点
  99. }
  100. };
  101. // ROS2节点主入口main函数
  102. int main(int argc, char * argv[])
  103. {
  104. // ROS2 C++接口初始化
  105. rclcpp::init(argc, argv);
  106. // 创建一个客户端指针
  107. auto action_client = std::make_shared<MoveCircleActionClient>();
  108. // 发送动作目标
  109. action_client->send_goal(true);
  110. // 创建ROS2节点对象并进行初始化
  111. rclcpp::spin(action_client);
  112. // 关闭ROS2 C++接口
  113. rclcpp::shutdown();
  114. return 0;
  115. }

c. 命令行操作

  1. $ ros2 action list # 查看服务列表
  2. $ ros2 action info <action_name> # 查看服务数据类型
  3. $ ros2 action send_goal <action_name> <action_type> <action_data> # 发送服务请求

6.参数

参数是ROS2模块化封装的重要功能,可以利用全局参数可视化显示以及动态调整
默认情况下,某一个参数被修改后,使用它的节点并不会立刻被同步,ROS2提供了动态配置的机制,需要在程序中进行设置,通过回调函数的方法,动态调整参数

关于参数,具体可以看https://docs.ros.org/en/jazzy/Concepts/Basic/About-Parameters.html#parameters

7.DDS

DS的全称是Data Distribution Service,也就是数据分发服务,2004年由对象管理组织OMG发布和维护,是一套专门为实时系统设计的数据分发/订阅标准,最早应用于美国海军, 解决舰船复杂网络环境中大量软件升级的兼容性问题,现在已经成为强制标准。

DDS强调以数据为中心,可以提供丰富的服务质量策略,以保障数据进行实时、高效、灵活地分发,可满足各种分布式实时通信应用需求。

在命令行中配置DDS

  1. $ ros2 topic pub /chatter std_msgs/msg/Int32 "data: 42" --qos-reliability best_effort
  2. $ ros2 topic echo /chatter --qos-reliability reliable
  3. $ ros2 topic echo /chatter --qos-reliability best_effort
  4. $ ros2 topic info /chatter --verbose #查看节点的Qos策略

8.分布式通信

分布式通信是解决使用一台计算机算力不足的情况
有几个实用的例子,也是我自己在使用的:
一台电脑运行执行器节点另一台电脑读取数据并使用QT可视化数据
一台电脑运行节点 另一台使用rviz等等

查看精华帖全部内容,请登录或者注册

最新回复

分布式通信是解决使用一台计算机算力不足的情况,这个可以   详情 回复 发表于 14 小时前
点赞 关注(1)
 
 

回复
举报

7002

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

分布式通信是解决使用一台计算机算力不足的情况,这个可以

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
【有奖直播】电机开发很复杂?MotorXpert™助您事半功倍!
直播时间:4月8日(周二)上午10:00
直播奖励:京东卡等您拿!

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 1

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表