news 2026/9/3 20:16:34

gRPC C++头文件与库配置详解:编译链接错误排查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
gRPC C++头文件与库配置详解:编译链接错误排查指南

简介:这份专为 Visual Studio 2015 准备的最新版 gRPC 库与头文件合集,面向需要在 VS2015 中搭建 gRPC 服务端和客户端的 C++ 开发者,可省去从源码编译 gRPC 及依赖组件的繁琐环节。包体共 458 个文件、34.55MB,以 346 个头文件和 51 个 lib 库文件为主,另有 cmake 配置、proto 定义、exe 工具、DLL 及少量源文件,分别用于构建、接口描述和运行时支撑。包内提供的 protobuf 生成示例代码,便于理解 Stub、Channel、Server 等核心组件用法。基于 HTTP/2 多路复用和 protobuf 二进制编码,可在 VS2015 下直接开发高吞吐、低延迟的分布式服务,支持 TLS 安全通道,适合微服务、物联网或数据同步场景。目前已有 620 人学习/下载,适合快速上手 gRPC 并绕开编译配置坑位的开发者和学习者。 很多刚接触gRPC的兄弟,经常在grpc库和头文件这一步卡住。明明代码照着文档写得一模一样,一编译就是fatal error: grpcpp/grpcpp.h: No such file or directory,或者一堆undefined reference。说实话,这跟代码水平没关系,纯粹是gRPC这套东西的头文件和库太“碎”了——它依赖protobuf、absl、c-ares、re2、zlib一堆底层库,每个库又有自己的头文件目录,而且版本之间还互相锁死。我这几年前前后后在十几个项目里折腾gRPC,从源码编译到vcpkg、conan都踩过,踩得多了就攒了不少经验,今天正好整理出来,给正准备上手或者已经被编译折磨得想摔键盘的朋友做个参考。这篇文章主要解决三件事:第一,怎么选一个相对稳的gRPC版本,而不是无脑追最新;第二,怎么把grpc的头文件路径和库文件正确配进工程里,CMake、Makefile、直接命令行编译我都会讲到;第三,把最常见的几个编译链接报错整理成排查手册,你对照着改就行。

1. 为什么gRPC的头文件和库是最容易翻车的环节

1.1 它不是一个“独立库”,而是一整套生态

很多人第一次用gRPC的时候,以为它像OpenSSL那样下载一个包、加个include路径、链接一个lib就完事了。实际上gRPC的C++实现是一个典型的“库的库”,它的核心库grpc++依赖Protobuf做序列化,依赖absl做基础工具库,依赖c-ares做DNS解析,依赖re2做正则匹配,还有OpenSSL、zlib等等。这些依赖各自都有独立的头文件目录和独立的.so/.a文件。

这就带来一个直接后果:头文件不是只有gRPC自己的,还有一堆间接头文件。你在代码里写了#include <grpcpp/grpcpp.h>,编译器不光要能找到grpcpp/grpcpp.h本身,还要找到它里面引用的grpc/grpc.hgrpc/impl/codegen/...,以及它背后Protobuf的google/protobuf/message.h。任何一个目录没加进去,编译就挂。

打个比方,这就像你请了一个大厨(gRPC主库),结果这个大厨非要自带一整个后厨团队(依赖库)才肯开工。你光把大厨叫来没用,整个团队都得安置好。很多人上网搜解决方案,看到别人的头文件路径配置能用,自己照抄却不行,多半就是因为版本不同,依赖的具体路径不一样。

1.2 版本选型:最新不等于最稳,要看你的项目“体质”

“最新grpc库和头文件”这个概念本身就有坑。我见过有人一上来就clone master分支,结果编译都过不去,因为master上的代码可能依赖了还没release的protobuf版本。我的建议是,优先选release版本,绝对不要碰master

具体怎么选,给你一个我用了很久的参考逻辑:

  • 如果是新项目,没有任何历史包袱,选最新的稳定release版本,但要注意它要求的protobuf最低版本。比如gRPC 1.60要求protobuf 3.21以上,你就不能拿一个系统自带的旧protobuf去配。
  • 如果项目里已经有固定版本的protobuf,那就去查这个protobuf版本对应的gRPC版本兼容性。gRPC官方在grpc-releases分支和CHANGELOG里都有说明,别拍脑袋决定。
  • 如果是在老系统上部署(比如CentOS 7这种),建议选稍微老一点的gRPC版本,因为新版gRPC可能要求更高的glibc和编译器版本,老系统的默认gcc压根不够格。

还有一个经常被忽略的点:gRPC的版本和头文件是强绑定的。你用什么版本的库编译出来的libgrpc++.so,就必须用什么版本的头文件去编译你的代码。混用版本是C++项目里最常见的“警告一堆但能编译过,运行起来偶尔崩”的神秘bug来源,这类问题最难排查,因为不是必现的,往往压测或者跑到特定逻辑才爆。

2. 获取最新gRPC库的几种主流方式

2.1 源码编译:最稳、也最费时间的一条路

如果你需要定制化(比如修改底层网络参数、裁剪部分模块),或者你用的操作系统比较小众、没有现成的二进制包,那源码编译是唯一靠谱的选择。你需要准备的东西有这些:git、cmake(3.13以上)、gcc/g++(支持C++14)、make、pkg-config,还有至少4GB的磁盘空间和比较稳的网络(因为要拉一堆submodule,网络不稳很容易中途失败)。

具体操作流程我整理如下:

# 1. 拉取源码,注意--recurse-submodules,这个千万别省 git clone --recurse-submodules -b v1.60.0 https://github.com/grpc/grpc.git cd grpc # 2. 创建build目录,out-of-source编译,避免污染源码 mkdir -p build && cd build # 3. 配置编译选项 cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DgRPC_INSTALL=ON \ -DgRPC_BUILD_TESTS=OFF \ .. # 4. 编译并安装,用nproc拿CPU核数,可以快不少 make -j$(nproc) sudo make install

几个参数的解释,新手容易忽略:

  • -DgRPC_BUILD_TESTS=OFF:默认gRPC的测试代码挺多的,不关掉编译时间会变得非常长。我们这里只要库和头文件,不需要跑测试。
  • -DgRPC_INSTALL=ON:让cmake帮我们把头文件、库文件、cmake配置文件一起装到CMAKE_INSTALL_PREFIX指定的目录,这一步直接决定了后面find_package能不能找到。
  • -DCMAKE_INSTALL_PREFIX=/usr/local:安装路径。我习惯装在/usr/local,因为这样CMake的默认搜索路径就能直接找到,不用额外设置环境变量。如果你想装到自定义目录,后面就要注意CMAKE_PREFIX_PATH的设置。

源码编译这套流程我第一次跑的时候花了快40分钟,当时没关测试,机器还在跑别的东西。后来关了测试、用全部核数,大概10分钟出头就能搞定,新机器会更快。编译完成后,你可以检查一下/usr/local/include/grpcpp/grpcpp.h这个文件是否存在,存在就说明头文件装好了。如果这个文件都不存在,后面所有编译报错都不用看,先回头查安装步骤。

2.2 包管理器:vcpkg和apt的取舍

如果你不想折腾源码编译,包管理器是更快的路。我主要推荐两个,各有利弊。

vcpkg(跨平台,Windows/Linux/macOS都能用):

git clone https://github.com/microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg install grpc

装完之后,vcpkg会帮你把头文件和库都放在一个统一的目录里,然后通过CMAKE_TOOLCHAIN_FILE变量集成进工程。这个方案的优势是依赖关系全部自动处理,你不用自己操心absl、c-ares那些间接依赖。有坑的地方也要提前知道:它编译出来的库默认使用它自己拉下来的protobuf版本,如果你项目里还要跟自己的protobuf混用,反而麻烦。建议的做法是全工程统一用vcpkg管理依赖,不要一半vcpkg一半自己找。

apt(Ubuntu/Debian):

sudo apt install libgrpc++-dev protobuf-compiler-grpc

apt装起来最快,但版本通常很老。我前阵子看了眼,Ubuntu 22.04默认源里的gRPC是1.30版本,跟当时最新版本差了好几个大版本。如果你的项目只是学习、测试,apt装装无所谓;如果是正经项目,我建议还是用源码编译或者vcpkg,别被apt的“省事”坑了版本。

还有一个值得提的是conan,在C++领域也越来越多项目用。不过conan的gRPC包有时候依赖项配置比较复杂,新手容易在profile设置上翻车。上手阶段,vcpkg比conan对新手友好得多,等你把CMake和依赖管理这套东西玩明白了,再去看conan会顺很多。

3. 头文件配置与工程集成实战

3.1 include路径和库链接:搞清楚这三件事就通透了

很多人在网上搜“grpc 头文件 配置”,搜到的答案五花八门,什么设置环境变量、改.vscode配置、在VS2015里加路径……看得人头晕。其实不管什么IDE、什么构建系统,底层就三件事:告诉编译器头文件在哪、告诉链接器库文件在哪、告诉运行时去哪找动态库

头文件路径方面,gRPC安装之后你的include目录里大概会长这样:

/usr/local/include/ ├── grpc/ ├── grpcpp/ ├── google/protobuf/ # 如果编译时也安装了protobuf └── absl/ # 如果编译时安装了absl

对应到CMake里,最朴素的写法是:

include_directories(/usr/local/include) link_directories(/usr/local/lib)

我强烈不建议长期这么干,因为link_directories是有坑的,它会全局影响后面所有target的链接搜索路径,项目一大容易出莫名其妙的问题。更规范的做法是用find_package,让CMake自己去找:

find_package(gRPC CONFIG REQUIRED) find_package(Protobuf REQUIRED)

只要gRPC是正常安装的,CMake会自动帮你在gRPC_INCLUDE_DIRProtobuf_INCLUDE_DIR里填好正确的头文件路径,不需要你手动写死。

库链接方面,你要链接的库至少包含这几个:

  • grpc++:C++封装的gRPC核心库
  • grpc:C语言底层库
  • protobuf:序列化库
  • gpr:一些基础工具函数,老版本需要显式链接,新版一般自动带上了

对应的CMake写法是:

target_link_libraries(your_target grpc++ grpc protobuf )

3.2 一个可以直接抄的CMakeLists.txt示例

光说理论容易飘,我给你一个可以直接抄的CMakeLists.txt。这个例子假设你的项目结构是:

my_grpc_demo/ ├── CMakeLists.txt ├── proto/ │ └── hello.proto └── src/ └── main.cpp
cmake_minimum_required(VERSION 3.13) project(my_grpc_demo CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 如果gRPC不是装在标准路径,取消注释下面这行,改成你自己的安装前缀 # set(CMAKE_PREFIX_PATH "/path/to/grpc/install") find_package(gRPC CONFIG REQUIRED) find_package(Protobuf REQUIRED) # 定义proto生成的方式 set(PROTO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/proto) set(PROTO_FILE ${PROTO_PATH}/hello.proto) set(GENERATED_SRC ${CMAKE_CURRENT_BINARY_DIR}/hello.pb.cc ${CMAKE_CURRENT_BINARY_DIR}/hello.grpc.pb.cc) add_custom_command( OUTPUT ${GENERATED_SRC} COMMAND protoc --cpp_out=${CMAKE_CURRENT_BINARY_DIR} --grpc_out=${CMAKE_CURRENT_BINARY_DIR} --plugin=protoc-gen-grpc=$<TARGET_FILE:gRPC::grpc_cpp_plugin> -I ${PROTO_PATH} ${PROTO_FILE} DEPENDS ${PROTO_FILE} ) add_custom_target(generate_proto ALL DEPENDS ${GENERATED_SRC}) add_executable(my_grpc_demo src/main.cpp ${GENERATED_SRC}) add_dependencies(my_grpc_demo generate_proto) target_include_directories(my_grpc_demo PRIVATE ${CMAKE_CURRENT_BINARY_DIR} # 生成的proto头文件在这里 ) target_link_libraries(my_grpc_demo PRIVATE gRPC::grpc++ protobuf::libprotobuf )

核心设计我解释一下:add_custom_command负责调用protoc生成四个文件,--plugin=protoc-gen-grpc=$<TARGET_FILE:gRPC::grpc_cpp_plugin>这个写法很关键,它直接拿到grpc_cpp_plugin的绝对路径,不管你库装在哪都能找到。然后target_include_directories必须加上${CMAKE_CURRENT_BINARY_DIR},因为生成的hello.pb.h在这个目录里。

这里有一个新手经常栽的地方:hello.pb.hhello.grpc.pb.h生成在当前二进制目录,不是源码目录。如果你没用target_include_directories${CMAKE_CURRENT_BINARY_DIR}加进去,编译的时候就会报hello.pb.h: No such file or directory。这个错误我见过太多次了,几乎每隔一段时间就有人在群里问一次,每次排查到最后都是这个问题。

4. 实操过程:从零到一跑通一个gRPC服务

4.1 编写proto文件与生成代码

不管你是做服务器还是客户端,gRPC的第一步都是写proto文件。这是定义接口和数据结构的文件,格式长这样:

syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }

然后用protoc生成C++代码:

protoc -I ./proto --cpp_out=./generated --grpc_out=./generated \ --plugin=protoc-gen-grpc=$(which grpc_cpp_plugin) ./proto/hello.proto

生成之后你会得到四个文件:hello.pb.hhello.pb.cchello.grpc.pb.hhello.grpc.pb.cc。前两个是protobuf序列化相关的类,后两个是gRPC服务接口相关的类。注意grpc_cpp_plugin的路径一定要对,它通常在/usr/local/bin/grpc_cpp_plugin。用vcpkg的话,路径在vcpkg的tools/grpc目录里。如果你命令行执行which grpc_cpp_plugin什么都查不到,大概率是安装时候的bin目录没加进PATH。

4.2 编写服务端与客户端代码,编译运行验证

服务端代码我写得尽量精简,让你看清楚核心流程:

#include <grpcpp/grpcpp.h> #include "hello.grpc.pb.h" class GreeterServiceImpl final : public helloworld::Greeter::Service { grpc::Status SayHello(grpc::ServerContext* context, const helloworld::HelloRequest* request, helloworld::HelloReply* reply) override { std::string prefix("Hello "); reply->set_message(prefix + request->name()); return grpc::Status::OK; } }; int main(int argc, char** argv) { std::string server_address("0.0.0.0:50051"); GreeterServiceImpl service; grpc::ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service); std::unique_ptr<grpc::Server> server(builder.BuildAndStart()); std::cout << "Server listening on " << server_address << std::endl; server->Wait(); return 0; }

客户端代码:

#include <grpcpp/grpcpp.h> #include "hello.grpc.pb.h" int main(int argc, char** argv) { auto channel = grpc::CreateChannel( "localhost:50051", grpc::InsecureChannelCredentials()); auto stub = helloworld::Greeter::NewStub(channel); helloworld::HelloRequest request; request.set_name("grpc"); helloworld::HelloReply reply; grpc::ClientContext context; grpc::Status status = stub->SayHello(&context, request, &reply); if (status.ok()) { std::cout << "Received: " << reply.message() << std::endl; } else { std::cout << "RPC failed: " << status.error_message() << std::endl; } return 0; }

编译运行:

mkdir build && cd build cmake .. make -j$(nproc) # 终端A运行服务端 ./my_grpc_demo # 终端B运行客户端 ./my_grpc_client

如果一切正常,客户端应该打印出Received: Hello grpc。到这步,你的gRPC环境就算是真正跑通了。我第一次在CentOS上跑通这个流程的时候,光环境就折腾了两天,现在想想大部分时间都花在头文件和库的路径上,代码本身反而很简单。所以如果你环境还没通,别怀疑自己写代码的能力,先把库和头文件理顺。

5. 常见问题与排查技巧实录

5.1 头文件找不到:先分清是哪一层找不到

“fatal error: grpcpp/grpcpp.h: No such file or directory”这类报错,我建议你按下面这个顺序排查:

症状可能原因快速验证方法
找不到grpcpp/grpcpp.hgRPC头文件没装,或include路径没加ls /usr/local/include/grpcpp/grpcpp.h
能找到grpcpp/grpcpp.h,但找不到google/protobuf/message.hprotobuf头文件路径缺失在CMake里单独find_package(Protobuf)并加上include路径
VSCode里能编译过但编辑器画红线VSCode的C/C++插件用了自己的一套includePath.vscode/c_cpp_properties.json里配置compileCommands或手动加includePath
hello.pb.h找不到生成的代码路径没加到include检查target_include_directories是否包含${CMAKE_CURRENT_BINARY_DIR}

这里面最容易被误导的是VSCode的“假报错”——CMake编译完全OK,但编辑器里全是红色波浪线。这是因为VSCode的IntelliSense和CMake的编译配置是两套体系,它默认没有加载你的编译命令。解决办法是用CMake Tools插件,然后在c_cpp_properties.json里把"configurationProvider": "ms-vscode.cmake-tools"配置好,让插件自动读取compile_commands.json。如果你用的是Visual Studio而不是VSCode,道理一样,配置好include目录和库目录就行,只是入口在项目属性页里。

5.2 链接阶段报错:undefined reference的真相

编译通过了,但链接时报一堆undefined reference to 'grpc::...',这个问题的本质是链接器找不到对应符号,或者找到了但符号不一致。符号不一致通常是版本混用导致的,比如你用1.60的头文件去链接1.50的库,C++名字修饰规则变了,自然就找不到。

几个实用的排查命令:

# 查看动态库导出了哪些符号,确认grpc::CreateChannel符号在不在 nm -D /usr/local/lib/libgrpc++.so | grep "grpc::CreateChannel" # 查看库文件是32位还是64位 file /usr/local/lib/libgrpc.a # 查看可执行文件链接了哪些库 ldd your_app

对于“如何查看.a库是32位还是64位”这个问题,file命令最直接,它会输出类似ELF 64-bit LSB relocatable, x86-64的信息。必要时还可以用objdump -f libxxx.a看文件头。很多时候你从网上下了一个旧库,或者手滑装了32位版本,链接阶段就会报一堆奇奇怪怪的错误,拿file一验就能定位。

5.3 运行时崩溃或连接失败:检查动态库路径

编译链接都过了,结果一运行报error while loading shared libraries: libgrpc++.so.1: cannot open shared object file,这是运行时动态库搜索路径的问题。程序运行时用的库路径和编译时的库路径不是一回事,这一点很多人会忽略。

解决方法:

# 方式一:临时设置LD_LIBRARY_PATH export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH # 方式二:用ldconfig把路径注册进系统缓存(推荐) sudo sh -c "echo /usr/local/lib > /etc/ld.so.conf.d/grpc.conf" sudo ldconfig # 验证 ldconfig -p | grep grpc

如果你是在Windows上用vcpkg,那动态库的路径问题通常表现为grpc.dll找不到,把vcpkg的bin目录加到系统PATH里就行。Windows下还有个常见问题:Debug版本和Release版本的库混用,也会导致运行时行为异常,所以用vcpkg安装的时候要注意--tripletx64-windows还是x64-windows-debug

5.4 一个隐藏很深的坑:protoc版本与protobuf版本不匹配

这个坑我必须要单独说。gRPC生成代码依赖protoc,而protoc是和protobuf绑定的。如果你系统里的protoc是3.6版本,但你链接的protobuf库是3.21版本,生成的代码极有可能编译不过,或者编译过了运行时给你抛一个GPB_WARN_INVALID_UTF8之类的诡异错误。

应对思路:使用gRPC官方推荐的protoc版本。源码编译gRPC时,make install会把它自己拉下来的protobuf装好,这时你用的protoc就是配套版本。如果你用apt装protoc,系统的protoc版本可能和vcpkg装的gRPC不一致,就要小心了。最稳的做法是:哪个gRPC装出来的protoc,就用哪个protoc来生成代码。我们在CMake里用$<TARGET_FILE:gRPC::grpc_cpp_plugin>拿插件路径,其实protoc本身也可以从gRPC的安装目录里指定,别直接用系统的/usr/bin/protoc

6. 写在最后:关于版本选择的一些个人体会

每次看到有人追求“最新grpc库和头文件”,我都想说一句:新版本确实会带来性能优化和新特性,但对大多数项目来说,稳定可复现比版本数字重要得多。我现在的习惯是,一个项目一旦确定了gRPC版本,就把它写进依赖锁定文件里(比如vcpkg的vcpkg.json的version约束、Conan的lockfile),绝不随手升级。真要升级,也是单独拉一个分支,把编译、测试、集成全套跑完再合并,绝不直接在主干上升级依赖。

最后再分享一个小技巧:grpc_cpp_plugin这个插件在生成代码时非常关键,但它不在PATH里是常态。你可以在CMake里用$<TARGET_FILE:gRPC::grpc_cpp_plugin>拿它的绝对路径,这样不管库装在哪,CMake都能自己找到。这个写法在上面那个CMakeLists.txt里已经用了,你直接抄就行。祝各位都能一次编译通过,少踩我当年踩过的坑。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 20:14:54

Linux进程生命周期详解:从fork到僵尸与孤儿进程的完整过程

刚接触 Linux 的时候&#xff0c;我一直有个困惑&#xff1a;执行./app之后&#xff0c;这个程序到底经历了什么&#xff1f;为什么有些进程能一直跑在后台&#xff0c;有些进程退出了却还占着进程表&#xff0c;有些进程甚至杀不掉&#xff1f;后来排查线上问题&#xff0c;遇…

作者头像 李华
网站建设 2026/9/3 20:14:29

EY投入1亿美元奖励员工适应AI:企业AI落地瓶颈与激励体系设计

EY 这次直接拿 1 亿美元做员工 AI 适应奖励&#xff0c;在四大和专业服务机构里算动作非常大的。很多人第一反应是“财大气粗”&#xff0c;但真正值得技术人员关注的是背后的逻辑&#xff1a;企业已经意识到&#xff0c;AI 落地最大的瓶颈不是模型不够强&#xff0c;而是员工不…

作者头像 李华
网站建设 2026/9/3 20:09:54

从零实现ST-GCN骨骼动作识别:原理、PyTorch实战与避坑指南

简介&#xff1a;这是一套面向计算机科学、电子信息工程等专业高年级学生及科研初学者的ST-GCN骨骼动作识别实践资源&#xff0c;聚焦人体动作识别这一典型时序图学习任务&#xff0c;提供从理论建模到代码落地的完整技术闭环。资源含109个文件&#xff0c;涵盖29个核心Python模…

作者头像 李华
网站建设 2026/9/3 20:09:19

单片机毕设选题推荐:基于 STM32 的 OLED 显示超声测距语音报警系统设计 基于 STM32 单片机的近距离探测无线监测系统设计(014206)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/9/3 20:05:18

Curvelet工具箱在Matlab中的完整使用指南:从编译到图像去噪

简介&#xff1a;Curvelet MATLAB工具箱是一套基于Curvelet变换的MATLAB实现库&#xff0c;面向图像处理与信号分析领域的科研人员和工程师&#xff0c;用于图像去噪、压缩、增强及边缘特征提取等任务&#xff0c;相比传统小波变换更能捕捉图像中的曲线与边缘结构&#xff0c;适…

作者头像 李华
网站建设 2026/9/3 20:02:50

树莓派+LinuxCNC:打造开源运动控制硬件框架完整指南

这次我们来看一个树莓派生态里很硬核的方向&#xff1a;用树莓派作为主控&#xff0c;把 LinuxCNC 数控系统的核心框架硬件部分搭起来。如果你一直在关注小型桌面级 CNC、雕刻机、写字机器人&#xff0c;或者想在树莓派上跑一个真正能控制电机和 IO 的开源运动控制系统&#xf…

作者头像 李华