当前位置: 首页 > news >正文

ROS1ROS2之CmakeList.txt和package.xml用法详解

前言:目前还在学习ROS+无人机框架中,,,
更多更新文章详见我的个人博客主页【前往】

文章目录

    • 1. CMakeLists.txt与package.xml的作用
    • 2. 生成CMakeLists.txt
      • 2.1 ROS1
      • 2.2 ROS2
    • 3. CMakeLists.txt编写
      • 3.1 ROS1
      • 3.2 ROS2
    • 4. package.xml编写
    • 5. 其他--Setup.py的修改

1. CMakeLists.txt与package.xml的作用

ROS系统的功能包中 要包含 CMakeLists.txtpackage.xml 文件来编译功能包的内容

CMakeLists.txt原本是Cmake编译系统的规则文件,而Catkin编译系统基本沿用了CMake的编译风格,只是针对ROS工程添加了一些宏定义。所以在写法上,catkinCMakeLists.txtCMake的基本一致。用cmake命令创建功能包时,会自动生成CMakeList.txt文件,已配置了多数编译选项,且包含详细的注释,只需稍作修改便可编译自己的文件。

package.xml文件是描述功能包清单的文件,包括功能包的名称版本号作者信息许可信息编译依赖运行依赖等。

所以 CMakeLists.txt 非常重要,它指定了由源码到目标文件的规则,catkin编译系统在工作时首先会找到每个package下的 CMakeLists.txt ,然后按照规则来编译构建

2. 生成CMakeLists.txt

2.1 ROS1

ROS1可通过用catkin_create_pkg命令创建功能包,这会自动生成CMakeLists.txtpackage.xml文件的。

# 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src# 注:std msgs rospy roscpp为创建功能时指定的铱赖,test为包名
# 也可以先不指定,后面在CMakeLists.txt和package.xmL中手动添加
catkin_create_pkg test std_msgs rospy roscpp# 查看一下在~/catkin_ws/src目录下自动生成了哪些文件夹和文件
tree

通过上面的命令生成的工作空间如下

image-20230726150753757

include:存放**.h**的头文件

src:可同时存放**.cpp.py的源文件,但一般.py**文件存放在scripts目录中

scripts:习惯存放**.pypython**文件,需要自己创建

注:python代码和c++代码不分家,可同时存放在同一功能包中

2.2 ROS2

ROS2可通过如下的命令来创建功能包以及必要的CMakeList.txtpackage.xml文件

# 创建工作空间
mkdir -p ~/dev_ws/src
cd ~/dev_ws/src# ament_cmake和ament_python分别代表c++和python两种代码的功能包
# test_c和test_python代表包名
ros2 pkg create --build-type ament_cmake test_c
ros2 pkg create --build-type ament_python test_python# 查看一下在~/dev_ws/src目录下自动生成了哪些文件夹和文件
tree

通过以上命令生成的工作空间如下:

image-20230726151618480

  • test_c/include:存放**.h**的头文件

  • test_c/src:存放**.cppC++**源文件

  • test_python/test_python:存放**.pypython**文件

注:与ROS1不同,同一功能包内只能同时包含python文件或者C++文件中的一种

3. CMakeLists.txt编写

3.1 ROS1

  1. 常用的ROS1CMakeLists.txt架构如下

视频解析参考:中科院软件所-机器人操作系统入门(ROS入门教程)

cmake_minimum_required()	#CMake的最低版本号project()					#项目名称find_package()				#找到编译需要的其他CMake/Catkin	package catkin_python_setup()		#catkin新加宏,打开catkin的Python Module的支持add_message_files()			#catkin新加宏,添加自定义Message文件 add_service_files()         #catkin新加宏,添加自定义Service文件 add_action_files()          #catkin新加宏,添加自定义Action文件 generate_message()			#catkin新加宏,生成不同语言版本的msg/srv/action接口 catkin_package()			#catkin新加宏,生成当前package的cmake配置,供依赖本包的其他软件包调用 add_library()				#生成库 add_executable()			#生成可执行二进制文件add_dependencies()			#定义目标文件依赖于其他目标文件,确保其他目标已被构建 target_link_libraries()		#链接catkin_add_gtest()			#catkin新加宏,生成测试install()					#生成可安装目标

需要自己配置的项目一般就下面几种:

  • include_directories

    • 用于设置头文件的相对路径
    • 全局路径默认为功能包所在目录,功能包的头文件一般放在功能包根目录下的include文件夹,
      所以需要此处添加此文件夹
    • 还包含catkin编译器默认的其他头文件路径,如:ROS默认安装路径、Linux系统路径等
  • add_exectuable

    • 用于设置需要编译的代码和可执行文件
    • 第一个参数为期望生成的可执行文件(节点)名称
    • 后面的参数为参与编译的源文件(.cpp),若需要多个代码文件,可依次列出,空格分隔
  • target_link_libraries

    • 用于设置链接库
    • 有些功需要使用系统或第三方库函数,通过该选项可以配置执行文件链接的库文件
    • 第一个参数与add_exectuable相同,为可执行文件(节点)名称
    • 后面的参数为需要链接的库,依次列出,空格分隔
  • add_dependencies

    • 用于设置依赖
    • 有时候需要自定义消息类型,消息类型会在编译过程中产生相应语言的代码。若编译的可执行文
      件依赖这些动态生成的代码,则需要添加${PROJECT NAME}generate messages_cpp配置
  • 自定义数据类型:消息msg、服务srv、动作action

    • find_package(catkin REQUIRE COMPONENTS message_generation)
    • catkin_package(CATKIN_DEPENDS message_runtime)
    • add_message_files(FILES xxx.msg)
    • add_service_files(FILES xxx.srv)
    • add_action_files(FILES xxx.action)
    • generate_messages(DEPENDENCIES std_msgs)

CMakeLists.txt模板参考:CMakeLists.txt

更多教程参考:catkin/CMakeLists.txt - ROS Wiki

3.2 ROS2

常用的ROS2CMakeLists.txt架构如下

cmake_minimum_required()			#CMake的最低版本号project()				  		   #项目名称find_package()   				    #查找系统中的依赖项ament_target_dependencies()          #依赖于其他目标文件,确保其他目标已被构建 add_executable()				    #生成可执行二进制文件install()						   #生成可安装目标ament_package()						#生成功能包rosidl_generate_interfaces()		# 自定义消息类型接口

示例模板如下

cmake_minimum_required(VERSION 3.5)
project(test_c)# Default to C99
if(NOT CMAKE_C_STANDARD)set(CMAKE_C_STANDARD 99)
endif()# Default to C++14
if(NOT CMAKE_CXX_STANDARD)set(CMAKE_CXX_STANDARD 14)
endif()if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic)
endif()# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)add_executable(talker src/publisher_member_function.cpp)	# 修改2
ament_target_dependencies(talker rclcpp std_msgs)			# 修改3install(TARGETStalker												# 修改4DESTINATION lib/${PROJECT_NAME})if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)# the following line skips the linter which checks for copyrights# uncomment the line when a copyright and license is not present in all source files#set(ament_cmake_copyright_FOUND TRUE)# the following line skips cpplint (only works in a git repo)# uncomment the line when this package is not in a git repo#set(ament_cmake_cpplint_FOUND TRUE)ament_lint_auto_find_test_dependencies()
endif()# 添加自定义消息类型需要添加项
find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"msg/Num.msg"				# 自定义1"msg/Sphere.msg"			# 自定义2"srv/AddThreeInts.srv"     # 自定义3DEPENDENCIES geometry_msgs
)ament_package()

相较于基于catkinCMake文件,基于ament的明显简洁很多

假设你编写了~/dev_ws/src/test.cpp文件后,需要修改的步骤如下:

  1. 链接源代码
add_executable(test src/test.cpp)	# test为你输入节点后执行的名称,如ros2 run test_c test(前一个test_c为包名,后一个test则为这里的设置的名称,代表具体执行的代码)
ament_target_dependencies(test rclcpp)	# 添加依赖项
  1. 添加下面这两行代码的目的是让编译器编译~/dev_ws/src/test_c/test.cpp这个文件
install(TARGETStestDESTINATION lib/${PROJECT_NAME}
)

当你自定义消息类型时还需要消息类型文件

# 添加自定义消息类型需要添加项
# 下面两项是固定的,geometry_msgs是用于生成消息依赖包
find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"msg/Num.msg"				# 自定义1"msg/Sphere.msg"			# 自定义2"srv/AddThreeInts.srv"     # 自定义3DEPENDENCIES geometry_msgs
)

4. package.xml编写

package.xml的框架如下

<package format="3"><name>..</name><version>0.0.0</version><description>...</description><maintainer email="lanhanba@todo.todo">...</maintainer><license>...</license><buildtool_depend>...</buildtool_depend><exec_depend>...</exec_depend><exec_depend>...</exec_depend><export><build_type>...</build_type></export>
</package>

相较于CMakeList.txtpackage.xml的语法就简单许多,且ROS1ROS2的标签语法通用

  • <build_depend></build_depend>: 标签定义了功能包中代码编译时所依赖的其他功能包
  • <exec_depend><exec_depend>: 标签定义了功能包中可执行程序运行时所依赖的其他功能包
  • 自定义数据类型:话题消息msg、服务数据srv、动作数据action

模板如下

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3"><name>test_c</name><version>0.0.0</version><description>TODO: Package description</description><maintainer email="lanhanba@todo.todo">lanhanba</maintainer><license>TODO: License declaration</license><!-- ROS1 --><buildtool_depend>catkin</buildtool_depend><build_depend>roscpp</build_depend><build_depend>rospy</build_depend><build_depend>std_msgs</build_depend><build_export_depend>roscpp</build_export_depend><build_export_depend>rospy</build_export_depend><build_export_depend>std_msgs</build_export_depend><exec_depend>roscpp</exec_depend><exec_depend>rospy</exec_depend><exec_depend>std_msgs</exec_depend><!-- ROS2_C++ --><buildtool_depend>ament_cmake</buildtool_depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export><!-- ROS2_python --><test_depend>ament_copyright</test_depend><test_depend>ament_flake8</test_depend><test_depend>ament_pep257</test_depend><test_depend>python3-pytest</test_depend><export><build_type>ament_python</build_type></export><!-- ROS2中C++功能包添加自定义消息类型还需添加下面三行(固定形式,和上面的CMake修改搭配使用,python不需要) --><depend>rosidl_default_generators</depend><depend>geometry_msgs</depend><member_of_group>rosidl_interface_packages</member_of_group></package>

5. 其他–Setup.py的修改

ROS2-Python功能包中,如果添加类似~/dev_ws/src/test_python/test.pypython文件,则需要对功能包中的Setup.py进行如下的修改

from setuptools import setuppackage_name = 'test_python'setup(name=package_name,version='0.0.0',packages=[package_name],data_files=[('share/ament_index/resource_index/packages',['resource/' + package_name]),('share/' + package_name, ['package.xml']),],install_requires=['setuptools'],zip_safe=True,maintainer='lanhanba',maintainer_email='lanhanba@todo.todo',description='TODO: Package description',license='TODO: License declaration',tests_require=['pytest'],entry_points={'console_scripts': [# 结构如下:节点名 = 功能包名.代码文件.执行入口函数名(一般执行入口函数名都是main,也就是说python代码中一般要有main函数)# 通过ros2 run test_python test 来执行test_python.test:main下的函数# 如果有多个python代码文件都需要一一添加,名称不能一样"test = test_python.test:main","test2 = test_python.test2:main"],},
)

参考:

  • catkin/CMakeLists.txt - ROS Wiki

  • CMakeLists.txt - ROS Humble Wiki

  • 【ROS 学习笔记】CMakeLists.txt 与 package.xml - 知乎 (zhihu.com)

  • 详细分析一个ROS2 CMakeLists.txt文件_首飞爱玩机器人的博客-CSDN博客

相关文章:

ROS1ROS2之CmakeList.txt和package.xml用法详解

前言&#xff1a;目前还在学习ROS无人机框架中&#xff0c;&#xff0c;&#xff0c; 更多更新文章详见我的个人博客主页【前往】 文章目录 1. CMakeLists.txt与package.xml的作用2. 生成CMakeLists.txt2.1 ROS12.2 ROS2 3. CMakeLists.txt编写3.1 ROS13.2 ROS2 4. package.xml…...

C#设计模式之---适配器模式

适配器模式&#xff08;Adapter Pattern&#xff09; 适配器模式&#xff08;Adapter Pattern&#xff09;也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。适配器模式是一种结构型模式&#xff0c;一个适配使得因接口不兼容而不能在一起工作的类工作在一起…...

串口设备驱动

文章目录 一、串口简介二、Linux下串口驱动框架uart_driver 结构体uart_port 的添加与移除三、Linux下串口驱动工作流程四、Linux下串口应用开发终端工作模式多线程例程一、串口简介 串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线…...

Nginx实现反向代理和负载均衡

Nginx安装 本文章主要介绍下&#xff0c;如何使用Nginx来实现反向代理和负载均衡&#xff0c;Nginx安装和基础知识&#xff0c;可参考我的这篇文章 Nginx安装。 Nginx实现反向代理 实现反向代理需要准备两台Nginx服务器。一台Nginx服务器A&#xff0c;ip为 192.168.206.140&…...

小米手机MIUI优化的影响

1. 小/红米手机的MIUI优化选项 2. MIUI优化选项的影响 2.1 MIUI优化会影响应用信息展示 MIUI优化选项会影响到应用信息的内容展示&#xff0c;具体如下图所示&#xff1a; 如果我们需要在应用信息里展示自启动入口&#xff0c;那我们就需要开启MIUI优化。 2.2 MIUI优化会影…...

【图论】kruskal算法

一.介绍 Kruskal&#xff08;克鲁斯卡尔&#xff09;算法是一种用于解决最小生成树问题的贪心算法。最小生成树是指在一个连通无向图中&#xff0c;选择一棵包含所有顶点且边权重之和最小的树。 下面是Kruskal算法的基本步骤&#xff1a; 将图中的所有边按照权重从小到大进行…...

Django框架:使用channels实现websocket,配置和项目实际使用

一、基本配置 依赖包&#xff1a; Django3.2 django-cors-headers3.5.0 redis4.6.0 #操作redis数据库的 channels3.0.0 #websocket channels-redis4.1.0 #通道层需要&#xff0c;依赖redis包项目目录结构&#xff1a; study_websocket --study_websocket --__init__.py --s…...

基于RK3588+FPGA+AI算法定制的智慧交通与智能安防解决方案

随着物联网、大数据、人工智能等技术的快速发展&#xff0c;边缘计算已成为当前信息技术领域的一个热门话题。在物联网领域&#xff0c;边缘计算被广泛应用于智慧交通、智能安防、工业等多个领域。因此&#xff0c;基于边缘计算技术的工业主板设计方案也受到越来越多人的关注。…...

AI面试官:LINQ和Lambda表达式(一)

AI面试官&#xff1a;LINQ和Lambda表达式&#xff08;一&#xff09; 当面试官面对C#中关于LINQ和Lambda表达式的面试题时&#xff0c;通常会涉及这两个主题的基本概念、用法、实际应用以及与其他相关技术的对比等。以下是一些可能的面试题目&#xff0c;附带简要解答和相关案…...

FPGA学习——FPGA利用状态机实现电子锁模拟

文章目录 一、本次实验简介二、源码及分析三、总结 一、本次实验简介 本次是实验是为了利用状态机模拟电子锁&#xff0c;相关要求如下&#xff1a; 顺序输入4位密码&#xff0c;密码为1234&#xff0c;用按键来键入密码用led灯指示键入第几位密码&#xff0c;&#xff08;博…...

Bert经典变体学习

ALBert ALBERT就是为了解决模型参数量大以及训练时间过长的问题。ALBERT最小的参数只有十几M, 效果要比BERT低1-2个点&#xff0c;最大的xxlarge也就200多M。可以看到在模型参数量上减少的还是非常明显的&#xff0c;但是在速度上似乎没有那么明显。最大的问题就是这种方式其实…...

uniapp checkbox radio 样式修改

文章目录 通过查看代码&#xff0c;发现 before部分是设置样式的主要属性 我们要设置的话&#xff0c;就要设置checkbox::before的属性。 其中的content表示内容&#xff0c;比如内部的对勾 那么我们设置的时候&#xff0c;比如设置disabletrue的时候或者checkedtrue的时候&…...

电脑重启后VScode快捷方式失效,找不到Code.exe

问题描述 下班回家关了部分程序就直接关机了&#xff0c;回家后重启电脑发现vscode的快捷方式就失效了&#xff0c;提示Code.exe已被移动或删除。 解决方法 查看你的vscode安装目录&#xff0c;Microsoft VS Code目录下大概率会存在一个名为_的文件夹&#xff0c;然后会发现…...

C语言实现扫雷游戏

test.c源文件 - 扫雷游戏测试 game.h头文件 - 扫雷游戏函数的声明 game.c源文件 - 扫雷游戏函数的实现 1.布置雷 -- 存放雷的雷盘 9*9 数组设计成11*11 上下左右方各多一行&#xff0c;保证周围8的范围 雷 - 1 不是雷 - 0 2.排查雷 主题测试源文件代码 &…...

蓝图节点编辑器

打印字符串 第02章 蓝图结构 03 -注释和重新路由_哔哩哔哩_bilibili 第02章 蓝图结构 04 - 变量_哔哩哔哩_bilibili 第03章 蓝图简易门 01 - 箱子碰撞_哔哩哔哩_bilibili 第03章 蓝图简易门 02 - 静态Mesh和箭头_哔哩哔哩_bilibili 第03章 蓝图简易门 03 - 设置相对旋转节点_哔…...

MySql 知识大汇总

数据库索引 数据库索引是一种数据结构&#xff0c;用于提高数据库查询的速度和效率。索引可以看作是表中一列或多列的值的快速查找方式&#xff0c;类似于书籍的目录。通过创建索引&#xff0c;可以减少数据库的扫描量&#xff0c;加快数据的检索速度。 常见的索引类型 常见…...

深入浅出Pytorch函数——torch.sum

分类目录&#xff1a;《深入浅出Pytorch函数》总目录 相关文章&#xff1a; 深入浅出Pytorch函数——torch.Tensor 函数torch.sum有两种形式&#xff1a; torch.sum(input, *, dtypeNone)&#xff1a;返回输入张量input所有元素的和。torch.sum(input, dim, keepdimFalse, *,…...

Git克隆文件不显示绿色勾、红色感叹号等图标

1、问题 Git和TorToiseGit安装后&#xff0c;Git克隆的文件不会显示绿色勾、红色感叹号等图标。 2、检查注册表 2.1、打开注册表 (1)WinR打开运行窗口&#xff0c;输入regedit&#xff0c;点击确定&#xff0c;打开注册表编辑器。 2.2、找如下路径 (1)找到路径 计算机\HKEY_…...

SOC FPGA之HPS模型设计(一)

目录 一、建立HPS硬件系统模型 1.1 GHRD 1.2 从0开始搭建HPS 1.2.1 FPGA Interfaces 1.2.1.1 General 1.2.1.2 AXI Bridge 1.2.1.3 FPGA-to-HPS SDRAM Interface 1.2.1.4 DMA Peripheral Request 1.2.1.5 Interrupts 1.2.1.6 EMAC ptp interface 1.2.2 Peripheral P…...

解决openstack重启swift服务后报错

swift重启报错 问题描述解决办法 问题描述 swift服务正常状态如下 [rootcontroller ~]# swift statAccount: AUTH_8bde12ff804e42498661b7454994c446Containers: 0Objects: 0Bytes: 0X-Put-Timestamp: 1690507907.67931X-Timestamp: 1690507907.67931X-Trans-Id: tx56d22fa13…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...