5+ Best Examples of How to Build a DIY Self-Balancing Robot

DIY自平衡机器人精选
本文精选了五款自平衡机器人项目,涵盖Arduino和Raspberry Pi等平台,介绍如何使用低成本组件在家自制教育及娱乐用途的机器人。这些项目不仅包括详细的组装步骤,还涉及编程指南,适合初学者和爱好者。
A blog about autonomous, outdoor and Internet connected robots.

5+ Best Examples of How to Build a DIY Self-Balancing Robot

Recently I shared with you the best 5 examples how to build a DIYquadcopter, and in this article I continue the series with more than 5 examples of self-balancing robot that can be built at home using cheap components and in some cases 3D printed components.
A simple two-wheeled balanced robot can become the perfect educational and hobbyist tool to explore the electronics and programming area. In this article, you can find at least 5 projects based on the Arduino or Raspberry Pi single board computers, and other several main components such as electric motors, IMU, accelerometer, or gyro sensors able to keep the robot in balance by measuring and reporting the velocity, orientation, and gravitational forces of the robot.

Any of these robots can be used for inspiration, and with a little imagination, you can build your custom self-balancing robot.

There are several advantages when you build a DIY robot:

  • you can build the robot at home;
  • are effectively very cheap;
  • you can reuse components from other robots;
  • some components can be printed using a 3D printer;
  • all of these robots are customizable;
  • you can combine both electronics and programming skills;

If for programming side is relatively simple to download and install a development environment, on the electronics side you need a set of tools including a soldering iron or wire cutters.

ArduRoller balancebot

ArduRoller balancebot

ArduRoller balancebot


I started the DIY balancing robot compilation with this very simple two wheeled robot with a bamboo chassis, and two sensors mounted on the axis of rotation to maintain its balance.

On the project page you can find documentation about how to program the Arduino Uno board to keep the robot in balance, as well as few other steps necessary to have a functionally robot.

In the following, you can find the main components that you need to build this project from scratch, while the entire documentation and libraries can be found here.

Components:

The Balancing Robot

The Balancing Robot

The Balancing Robot


This robotic project could become easily a platform from where you can start to build your first self-balancing robot. The balancing robot for dummies has a simple design that helps you to learn how to build a two-wheeled efficient robot with a solid frame and acceptable price.

The project integrates metal gears with built-in encoders, two lightweight and durable wheels, an Inertial Measurement Unit sensor to measure and reports the velocity, orientation, and gravitational forces of the robot, and many more robotic parts, which I listed below.

The brain of the robot is an Arduino Duemilanove single board computer able to maintain the robot in vertical position.

For more inspiration you can check also this project that is relatively closer to this balancing robot.

Almost all of the components of the balancing robot can be found here:

Segway Instructabot

Segway Instructabot

Segway Instructabot


Inspired by the two wheeled self-balancing personal transport vehicle invented by Dean Kamen, this robot integrates the idea of a self-balancing platform that change the speed of the motor based on data received from the accelerometer sensor. It takes several building hours where you have to combine electronics with programming skills, as well as woodworker skills and techniques.

Following the instructions from this article and by embedding several components, you will have a functional balancing robot based on a piece of wood, Arduino Uno, two powerful electric motors, and an accelerometer sensor.

Components

B-Robot

B-Robot

B-Robot


The B-Robot is a fully 3D printed self-balancing robot controlled by Arduino single board computer and able to be remotely controlled via a smartphone or a tablet through WiFi connection.

On the project page you can find the files to print the components, as well as libraries and the main program.

The robot balance system is based on the MPU-6050 triple axis accelerometer and gyro breakout board, and a sonar sensor.

As a bonus, you can check also this project based on the same triple axis sensor and the Arduino board.

All the main robotic parts needed to build this project are available here:

Self Balancing Robot using Raspberry Pi

Self Balancing Robot using Raspberry Pi

Self Balancing Robot using Raspberry Pi


You can build at least a million of things with a single board computer like Raspberry Pi. From this million, one project is this self-balancing robot based on the fruit-named computer, an IMU sensor, powerful electric motors, and a TFT screen that displays several information from sensor and components.

This complex platform can be customized for a wide range of applications including tracking and object detection, it can be used to map a room, for security at home, and many more service applications.

A list of several components integrated into this self-balancing robot can be found here:

Before starting any of these projects, please pay attention to details and check the components list. If you’re not familiar with DIY robotic projects, you can always start with a kit and then make the step into a DIY project.

标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
+---------------+ +----------------+ +-----------------------+ | users | | words | | senses | +---------------+ +----------------+ +-----------------------+ | PK id |<---+ | PK id |---+ | PK id | | | | | word | | | | +---------------+ | | word_linked_id | | +-----------------------+ | | word_linked | | | | | lemma | | +-----------------------+ +---------------+ | | ... | | | sense_examples | | subtitles | | +----------------+ | +-----------------------+ +---------------+ | | | PK id | | PK id | | +--------------------+ | sense_id | | FK user_id |----+ | user_words | | example | | subtitle_path | | +--------------------+ +-----------------------+ | subtitle_title| | | PK id | ^ | FK video_id |----+ | FK user_id |----+ | | create_time | | FK word_id | | | +---------------+ | word | | | | word_note | | | +---------------+ | ... | | | | videos | +--------------------+ | | +---------------+ | | | PK id | +--------------------+ | | | FK user_id |----+ | user_ignored_words | | | | video_path | | +--------------------+ | | | video_title | | | PK id | | | | ... | | | FK user_id |----+ | +---------------+ | | FK word_id |----+ | | +--------------------+ | | +---------------------+ | | | user_sentences | +----------------------------+ +---------------------+ | user_sentence_sense_ref | | PK id | +----------------------------+ | FK user_id |----+ | PK id | | video_path | | | FK user_id |----+ | video_title | | | FK user_sentence_id |----+ | subtitle_path | | | FK word_id |----+ | text_original | | | FK sense_id |----+ | text_translated | | | FK example_id |----+ | ... | | | create_time | +---------------------+ | +----------------------------+ | +--------------------------------+ 做成svg文件格式
07-02
<think>我们有一个相对复杂的数据库结构,需要生成ER图并保存为SVG格式。我们将使用Graphviz来生成SVG,但也可以考虑使用其他库如pygraphviz或直接生成SVG。不过,Graphviz本身支持SVG输出,所以我们可以利用它。步骤:1.分析表结构:根据提供的表结构,识别表、列、主键、外键。2.使用Graphviz创建ER图:使用dot语言来描述ER图,然后生成SVG。表结构描述如下(根据文本描述): users:PK:idwords:PK: idwordword_linked_id (可能是一个外键?指向words.id?)lemma...senses:PK:idsense_examples:PK: idFK:sense_id(指向senses.id)subtitles:PK:idFK: user_id (指向users.id)FK: video_id (指向videos.id) videos:PK:idFK: user_id (指向users.id)user_words:PK:idFK: user_id (users.id)FK: word_id (words.id) user_ignored_words:PK:idFK: user_id (users.id)FK:word_id(words.id)user_sentences:PK:idFK: user_id (users.id)user_sentence_sense_ref:PK:idFK: user_id (users.id)FK:user_sentence_id(user_sentences.id)FK:word_id(words.id)FK: sense_id (senses.id)FK: example_id (sense_examples.id)注意:我们还需要注意多对多关系,例如user_words和user_ignored_words等。我们将使用以下步骤生成ER图:1.定义每个表为一个节点,节点内包含列(主键用PK标记,外键用FK标记)。2.根据外键关系连接表,并标记关系类型(1:N, N:M等)。由于表较多,我们使用Graphviz的dot布局。我们将使用Python的graphviz库来生成图。安装:pip installgraphviz注意:graphviz需要安装Graphviz软件,请从https://graphviz.org/download/下载并安装。代码步骤:1.创建Digraph对象。2.定义节点(表)的样式,使用record形状来显示列。3.添加表节点,每个表用类似以下的格式:table_name [label=<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>table_name</b></td></tr><tr><td>column1 (PK)</td></tr><tr><td>column2(FK)</td></tr>...</table>>];4.添加外键关系,使用箭头连接。由于表结构复杂,我们手动构建每个表的label(HTML-likelabel)。但是,由于表很多,我们可以设计一个函数来生成每个表的HTML字符串。注意:graphviz的record形状对HTML-likelabel支持较好,我们可以使用HTML格式。我们将按照以下方式构建表节点:例如,users表:label ='''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>users</b></td></tr><tr><td>id (PK)</td></tr></table>>'''对于有外键的表,例如subtitles:label= '''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>subtitles</b></td></tr><tr><td>id (PK)</td></tr><tr><td>user_id(FK)</td></tr><tr><td>subtitle_path</td></tr>...</table>>'''然后,我们添加边:subtitles:user_id-> users:id注意:为了表示关系,我们可以使用箭头,并可以标记关系(如1:N)。但为了简洁,我们可以只使用箭头方向表示。多对多关系表(如user_words)我们作为实体表处理,并分别连接到users和words。由于表很多,我们逐一添加。考虑到表结构复杂,我们使用纵向布局(rankdir="LR"可能太拥挤,所以使用默认的TB)。我们使用以下步骤:1.创建Digraph,设置属性。2.逐个添加表节点(用HTML-likelabel)。3.添加外键关系(边)。注意:为了减少交叉,我们可以将相关的表分组(使用subgraph),但这里我们先不分组。由于表很多,我们尽量按照引用关系排列。开始编写代码:注意:graphviz中,使用HTML-likelabel时,需要在label属性中使用<>包裹,并设置labeltype="html"。但是,在graphviz的Python库中,我们只需要用尖括号包裹HTML字符串,并设置label即可。参考:https://graphviz.org/doc/info/shapes.html#html我们使用以下格式:dot.node('users', label='''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>users</b></td></tr><tr><td port="id">id(PK)</td></tr></table>>''')注意:我们为外键引用的列指定port,这样在连接时可以指定具体的端口。例如,连接users的id到subtitles的user_id:dot.edge('users:id','subtitles:user_id')但是,在subtitles表中,我们需要为user_id列设置port,即:<tr><td port="user_id">user_id(FK)</td></tr>这样,连接时就可以用'subtitles:user_id'步骤:1.定义每个表,包括表名和列,列中需要标记PK和FK,并且为FK列设置port(因为FK列需要被连接)。2.对于主键列,我们也可以设置port,这样在连接时指向主键列。但是,注意一个表可能有多个外键,所以每个外键列都需要设置port。我们为每个列设置port名(使用列名),这样在连接时使用。注意:主键列我们也会设置port,因为外键指向主键。现在,我们根据描述创建表结构:表列表(按顺序):- users- words- senses- sense_examples- subtitles-videos-user_words- user_ignored_words- user_sentences-user_sentence_sense_ref我们逐个定义表结构(列):users:id (PK)words:id (PK)wordword_linked_id(FK:words.id)#注意:这是一个自引用外键lemma...(其他列省略,用...表示)senses:id (PK)sense_examples:id (PK)sense_id (FK:senses.id)examplesubtitles:id (PK)user_id (FK:users.id)subtitle_pathsubtitle_titlevideo_id(FK: videos.id)create_timevideos:id (PK)user_id (FK:users.id)video_pathvideo_title...user_words:id (PK)user_id (FK:users.id)word_id(FK: words.id)wordword_note...user_ignored_words:id (PK)user_id (FK:users.id)word_id(FK: words.id)user_sentences:id(PK)user_id(FK: users.id)video_pathvideo_titlesubtitle_pathtext_originaltext_translated...user_sentence_sense_ref:id(PK)user_id(FK: users.id)user_sentence_id (FK:user_sentences.id)word_id (FK:words.id)sense_id(FK: senses.id)example_id (FK:sense_examples.id)create_time注意:user_sentence_sense_ref表有多个外键。开始编写代码:由于代码较长,我们分步定义每个表的label字符串。注意:为了避免图形过大,我们可以省略一些非关键列(例如,我们只保留主键、外键和必要的列)。但为了完整,我们尽量包含所有列,但可以用...表示其他列。但是,为了清晰,我们只包含主键、外键和自引用列。我们为每个表定义一个字典,包含表名和列列表,列列表中包括列名和类型(PK,FK等)。然后,编写一个函数来生成HTML字符串。由于时间关系,我们直接构建每个表的HTML字符串。我们使用以下函数来生成表的HTML标签:defmake_table_label(table_name, columns):# columns:列表,每个元素是 (column_name,is_pk, is_fk, ...)或者我们可以用字典但为了简化,我们直接写每个表。注意:graphviz的HTML-likelabel中,表格的每个行都是<tr>,列用<td>。我们为每个列设置port(列名),这样在连接时使用。开始编写代码:注意:由于表很多,我们只写关键部分。我们将使用一个列表来存储表定义,然后循环添加节点。但是,为了清晰,我们一个个表写。由于代码量较大,我们只写出关键表,其他表类似。但是,为了完整,我们尽量写出所有表。我们使用以下格式:dot.node('users', label='''<<tableborder="0"cellborder="1" cellspacing="0"><tr><td><b>users</b></td></tr><tr><td port="id">id(PK)</td></tr></table>>''')对于words表,有一个自引用外键(word_linked_id指向words.id),所以我们需要为它设置port。我们开始编写代码。注意:我们使用Digraph,因为关系是有向的(从外键指向主键)。最后,我们将生成的文件保存为SVG。代码示例:由于表很多,我们只写部分,然后其他表类似。注意:为了避免图形过于拥挤,我们可以使用subgraph来将相关表分组,但这里我们先不分。我们使用纵向布局(从上到下),因为表很多,纵向空间更大。设置:graph_attr={'rankdir':'TB'}开始编写代码:</think># 根据数据库结构生成 SVG ER 图 下面我将提供一个完整的解决方案,根据您描述的数据库结构生成 SVG 格式的 ER 图。这个 SVG 文件可以在 WPS 中直接打开和编辑。 ## 完整解决方案 ```python import graphviz from collections import defaultdict import xml.etree.ElementTree as ET from xml.dom import minidom import math class ERDiagramGenerator: def __init__(self): self.tables = self._define_database_structure() self.relationships = self._define_relationships() def _define_database_structure(self): """定义数据库表结构""" return { "users": { "columns": [ {"name": "id", "type": "PK"}, ] }, "words": { "columns": [ {"name": "id", "type": "PK"}, {"name": "word", "type": "string"}, {"name": "word_linked_id", "type": "FK", "ref_table": "words"}, {"name": "lemma", "type": "string"}, ] }, "senses": { "columns": [ {"name": "id", "type": "PK"}, ] }, "sense_examples": { "columns": [ {"name": "id", "type": "PK"}, {"name": "sense_id", "type": "FK", "ref_table": "senses"}, {"name": "example", "type": "text"}, ] }, "subtitles": { "columns": [ {"name": "id", "type": "PK"}, {"name": "user_id", "type": "FK", "ref_table": "users"}, {"name": "subtitle_path", "type": "string"}, {"name": "subtitle_title", "type": "string"}, {"name": "video_id", "type": "FK", "ref_table": "videos"}, {"name": "create_time", "type": "datetime"}, ] }, "videos": { "columns": [ {"name": "id", "type": "PK"}, {"name": "user_id", "type": "FK", "ref_table": "users"}, {"name": "video_path", "type": "string"}, {"name": "video_title", "type": "string"}, ] }, "user_words": { "columns": [ {"name": "id", "type": "PK"}, {"name": "user_id", "type": "FK", "ref_table": "users"}, {"name": "word_id", "type": "FK", "ref_table": "words"}, {"name": "word", "type": "string"}, {"name": "word_note", "type": "text"}, ] }, "user_ignored_words": { "columns": [ {"name": "id", "type": "PK"}, {"name": "user_id", "type": "FK", "ref_table": "users"}, {"name": "word_id", "type": "FK", "ref_table": "words"}, ] }, "user_sentences": { "columns": [ {"name": "id", "type": "PK"}, {"name": "user_id", "type": "FK", "ref_table": "users"}, {"name": "video_path", "type": "string"}, {"name": "video_title", "type": "string"}, {"name": "subtitle_path", "type": "string"}, {"name": "text_original", "type": "text"}, {"name": "text_translated", "type": "text"}, ] }, "user_sentence_sense_ref": { "columns": [ {"name": "id", "type": "PK"}, {"name": "user_id", "type": "FK", "ref_table": "users"}, {"name": "user_sentence_id", "type": "FK", "ref_table": "user_sentences"}, {"name": "word_id", "type": "FK", "ref_table": "words"}, {"name": "sense_id", "type": "FK", "ref_table": "senses"}, {"name": "example_id", "type": "FK", "ref_table": "sense_examples"}, {"name": "create_time", "type": "datetime"}, ] } } def _define_relationships(self): """定义表之间的关系""" relationships = defaultdict(list) # 基本关系 relationships["words"].append("words") # 自引用关系 relationships["senses"].append("sense_examples") relationships["users"].append("subtitles") relationships["users"].append("videos") relationships["users"].append("user_words") relationships["users"].append("user_ignored_words") relationships["users"].append("user_sentences") relationships["users"].append("user_sentence_sense_ref") relationships["words"].append("user_words") relationships["words"].append("user_ignored_words") relationships["words"].append("user_sentence_sense_ref") relationships["videos"].append("subtitles") relationships["user_sentences"].append("user_sentence_sense_ref") relationships["senses"].append("user_sentence_sense_ref") relationships["sense_examples"].append("user_sentence_sense_ref") # 多对多关系(通过连接表) relationships["users"].append("words (M:N)") # 通过 user_words relationships["words"].append("users (M:N)") # 通过 user_words return relationships def generate_svg_er_diagram(self, output_file='er_diagram'): """生成SVG格式的ER图""" # 创建SVG根元素 namespace = "http://www.w3.org/2000/svg" ET.register_namespace('', namespace) root = ET.Element(f"{{{namespace}}}svg", width="1200", height="900", viewBox="0 0 1200 900") # 添加样式定义 self._add_styles(root, namespace) # 计算表的位置 table_positions = self._calculate_table_positions() # 添加所有表 for table_name, (x, y) in table_positions.items(): self._add_table_element(root, namespace, table_name, x, y) # 添加关系 self._add_relationships(root, namespace, table_positions) # 生成SVG文件 self._prettify(root) # 格式化XML # 保存SVG文件 with open(f"{output_file}.svg", "w", encoding="utf-8") as f: f.write(ET.tostring(root, encoding="unicode")) print(f"SVG ER图已生成: {output_file}.svg") print("提示: 在WPS中打开此文件后,可右键选择'组合'->'取消组合'进行编辑") def _add_styles(self, root, namespace): """添加SVG样式定义""" style = ET.SubElement(root, "style", type="text/css") style.text = """ .table-rect { fill: #FFF2CC; stroke: #BF9000; stroke-width: 2; } .table-header { fill: #F4B183; font-family: Arial; font-size: 14px; font-weight: bold; } .column-text { font-family: Arial; font-size: 12px; } .pk-text { fill: #CC0000; font-weight: bold; } .fk-text { fill: #0070C0; font-weight: bold; } .relationship-line { stroke: #4472C4; stroke-width: 2; fill: none; } .relationship-text { font-family: Arial; font-size: 10px; fill: #4472C4; } .many-to-many { stroke: #FF0000; stroke-width: 2; fill: none; stroke-dasharray: 5,5; } .self-relationship { stroke: #00B050; stroke-width: 2; fill: none; } """ def _calculate_table_positions(self): """计算表在画布上的位置""" positions = {} table_names = list(self.tables.keys()) # 中心表:users positions["users"] = (600, 150) # 围绕users的表 positions["subtitles"] = (400, 300) positions["videos"] = (800, 300) positions["user_words"] = (300, 450) positions["user_ignored_words"] = (500, 450) positions["user_sentences"] = (700, 450) # 其他表 positions["words"] = (200, 600) positions["senses"] = (400, 600) positions["sense_examples"] = (600, 600) positions["user_sentence_sense_ref"] = (800, 600) return positions def _add_table_element(self, root, namespace, table_name, x, y): """添加表元素到SVG""" table_info = self.tables[table_name] table_width = 200 row_height = 25 header_height = 30 # 计算表高度 row_count = len(table_info['columns']) + 1 # 列数 + 表头 table_height = header_height + (row_count * row_height) # 添加表矩形 table_group = ET.SubElement(root, f"{{{namespace}}}g", id=f"table_{table_name}") # 表外框 ET.SubElement(table_group, f"{{{namespace}}}rect", x=str(x), y=str(y), width=str(table_width), height=str(table_height), rx="5", ry="5", class_="table-rect") # 表头 ET.SubElement(table_group, f"{{{namespace}}}rect", x=str(x), y=str(y), width=str(table_width), height=str(header_height), class_="table-header") # 表名文本 text_elem = ET.SubElement(table_group, f"{{{namespace}}}text", x=str(x + table_width/2), y=str(y + header_height/2 + 5), text_anchor="middle", class_="table-header") text_elem.text = table_name # 添加列 current_y = y + header_height for col in table_info['columns']: # 确定列样式 text_class = "column-text" if col['type'] == "PK": text_class = "pk-text" elif col['type'] == "FK": text_class = "fk-text" # 列文本 col_text = f"{col['name']}" if 'ref_table' in col: col_text += f" → {col['ref_table']}" text_elem = ET.SubElement(table_group, f"{{{namespace}}}text", x=str(x + 10), y=str(current_y + row_height/2 + 5), class_=text_class) text_elem.text = col_text current_y += row_height def _add_relationships(self, root, namespace, positions): """添加关系线""" # 基本关系 self._add_relationship_line(root, namespace, positions, "users", "subtitles", "1:N") self._add_relationship_line(root, namespace, positions, "users", "videos", "1:N") self._add_relationship_line(root, namespace, positions, "users", "user_words", "1:N") self._add_relationship_line(root, namespace, positions, "users", "user_ignored_words", "1:N") self._add_relationship_line(root, namespace, positions, "users", "user_sentences", "1:N") self._add_relationship_line(root, namespace, positions, "users", "user_sentence_sense_ref", "1:N") self._add_relationship_line(root, namespace, positions, "videos", "subtitles", "1:N") self._add_relationship_line(root, namespace, positions, "words", "user_words", "1:N") self._add_relationship_line(root, namespace, positions, "words", "user_ignored_words", "1:N") self._add_relationship_line(root, namespace, positions, "words", "user_sentence_sense_ref", "1:N") self._add_relationship_line(root, namespace, positions, "senses", "sense_examples", "1:N") self._add_relationship_line(root, namespace, positions, "senses", "user_sentence_sense_ref", "1:N") self._add_relationship_line(root, namespace, positions, "sense_examples", "user_sentence_sense_ref", "1:N") self._add_relationship_line(root, namespace, positions, "user_sentences", "user_sentence_sense_ref", "1:N") # 自引用关系 self._add_self_relationship(root, namespace, positions, "words") # 多对多关系 self._add_many_to_many_relationship(root, namespace, positions, "users", "words") def _add_relationship_line(self, root, namespace, positions, from_table, to_table, label): """添加关系线""" if from_table not in positions or to_table not in positions: return # 获取位置 x1, y1 = positions[from_table] x2, y2 = positions[to_table] # 添加连接线 path = ET.SubElement(root, f"{{{namespace}}}path", d=f"M {x1+100} {y1+30} C {x1+150} {y1+30}, {x2-150} {y2}, {x2+100} {y2}", class_="relationship-line") # 添加关系标签 text = ET.SubElement(root, f"{{{namespace}}}text", x=str((x1+x2)/2), y=str((y1+y2)/2 - 10), text_anchor="middle", class_="relationship-text") text.text = label def _add_self_relationship(self, root, namespace, positions, table): """添加自引用关系""" if table not in positions: return x, y = positions[table] # 添加自引用线(环形) path = ET.SubElement(root, f"{{{namespace}}}path", d=f"M {x+200} {y+30} C {x+250} {y-50}, {x+50} {y-50}, {x} {y+30}", class_="self-relationship") # 添加关系标签 text = ET.SubElement(root, f"{{{namespace}}}text", x=str(x+100), y=str(y-30), text_anchor="middle", class_="relationship-text") text.text = "1:N" def _add_many_to_many_relationship(self, root, namespace, positions, table1, table2): """添加多对多关系""" if table1 not in positions or table2 not in positions: return x1, y1 = positions[table1] x2, y2 = positions[table2] # 添加连接线 path = ET.SubElement(root, f"{{{namespace}}}path", d=f"M {x1+100} {y1+30} C {x1+150} {y1+30}, {x2-150} {y2+30}, {x2+100} {y2+30}", class_="many-to-many") # 添加关系标签 text = ET.SubElement(root, f"{{{namespace}}}text", x=str((x1+x2)/2), y=str((y1+y2)/2 - 10), text_anchor="middle", class_="relationship-text") text.text = "M:N" def _prettify(self, elem): """格式化XML输出""" rough_string = ET.tostring(elem, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent=" ") # 生成ER图 if __name__ == "__main__": er_generator = ERDiagramGenerator() er_generator.generate_svg_er_diagram('database_er_diagram') print("操作说明:") print("1. 在WPS文字或WPS演示中打开生成的SVG文件") print("2. 右键点击图表 -> 选择'组合' -> '取消组合'") print("3. 现在可以单独编辑每个元素") print("4. 编辑完成后,可以重新组合或导出为其他格式") ``` ## 生成的 ER 图说明 这个脚本会生成一个名为 `database_er_diagram.svg` 的文件,它包含了以下表结构及其关系: ### 表结构 1. **users**:用户表 2. **words**:单词表(包含自引用关系) 3. **senses**:词义表 4. **sense_examples**:词义示例表 5. **subtitles**:字幕表 6. **videos**:视频表 7. **user_words**:用户单词表 8. **user_ignored_words**:用户忽略单词表 9. **user_sentences**:用户句子表 10. **user_sentence_sense_ref**:用户句子词义引用表 ### 主要关系 - **1:N 关系**: - users → subtitles, videos, user_words, user_ignored_words, user_sentences, user_sentence_sense_ref - videos → subtitles - words → user_words, user_ignored_words, user_sentence_sense_ref - senses → sense_examples, user_sentence_sense_ref - sense_examples → user_sentence_sense_ref - user_sentences → user_sentence_sense_ref - words 自引用关系 (word_linked_id) - **M:N 关系**: - users ↔ words (通过 user_words 表) ## ER 图可视化特点 1. **清晰的表结构**: - 每个表都有明显的标题栏 - 主键(PK)用红色标识 - 外键(FK)用蓝色标识,并显示引用表 - 普通列用黑色文本 2. **关系表示**: - 1:N 关系用蓝色实线表示 - M:N 关系用红色虚线表示 - 自引用关系用绿色实线表示 - 所有关系都标注了关系类型(1:1, 1:N, M:N) 3. **布局优化**: - users 表位于顶部中心位置 - 相关表围绕中心表分布 - 连接线使用曲线避免交叉 - 整体布局清晰易读 ## 在 WPS 中编辑指南 1. **打开文件**: - 在 WPS 文字或 WPS 演示中打开 `database_er_diagram.svg` 2. **取消组合**: - 右键点击图表 - 选择"组合" → "取消组合" - 现在可以单独选择和编辑每个元素 3. **编辑元素**: - **移动表**:拖动表到新位置 - **修改文本**:双击文本进行编辑 - **更改样式**:右键选择"格式对象"修改颜色、字体等 - **调整关系线**:拖动关系线的控制点 4. **重新组合**: - 选择所有元素(Ctrl+A) - 右键 → "组合" → "组合" 5. **导出**: - 文件 → 另存为 → 选择格式(如PDF、PNG等) ## 高级功能扩展 ### 添加交互式预览 ```python def generate_html_preview(output_file='er_preview'): """生成带预览的HTML文件""" with open(f"{output_file}.html", "w") as f: f.write(f""" <!DOCTYPE html> <html> <head> <title>ER Diagram Preview</title> <style> body {{ font-family: Arial, sans-serif; }} .container {{ width: 100%; text-align: center; }} .instructions {{ margin-bottom: 20px; }} </style> </head> <body> <div class="container"> <h1>ER Diagram Preview</h1> <div class="instructions"> <p>在WPS中打开此文件进行编辑: <a href="database_er_diagram.svg">database_er_diagram.svg</a></p> <p>编辑说明: 右键点击图表 -> 组合 -> 取消组合</p> </div> <object data="database_er_diagram.svg" type="image/svg+xml" width="1200" height="900"> <img src="database_er_diagram.svg" alt="ER Diagram" /> </object> </div> </body> </html> """) ``` ### 添加自动布局算法 ```python def _calculate_auto_layout(self): """使用力导向算法自动布局""" positions = {} # 简化的力导向布局 for i, table in enumerate(self.tables.keys()): angle = 2 * math.pi * i / len(self.tables) radius = 300 x = 600 + radius * math.cos(angle) y = 450 + radius * math.sin(angle) positions[table] = (x, y) # 根据关系调整位置 for _ in range(5): # 迭代5次 for table1, relations in self.relationships.items(): for table2 in relations: if table2 not in positions or table1 not in positions: continue x1, y1 = positions[table1] x2, y2 = positions[table2] # 计算距离 dx = x2 - x1 dy = y2 - y1 distance = max(math.sqrt(dx*dx + dy*dy), 1) # 调整位置 if distance > 200: factor = 10 positions[table2] = ( x2 - dx * factor / distance, y2 - dy * factor / distance ) return positions ``` ### 添加数据库注释支持 ```python # 在表定义中添加注释 "users": { "columns": [ {"name": "id", "type": "PK", "comment": "用户唯一标识"}, ], "comment": "系统用户表" }, # 在生成SVG时显示注释 def _add_table_element(self, root, namespace, table_name, x, y): # ... # 添加表注释 if "comment" in table_info: comment_elem = ET.SubElement(table_group, f"{{{namespace}}}text", x=str(x + table_width/2), y=str(y - 10), text_anchor="middle", class_="column-text") comment_elem.text = table_info["comment"] # ... ``` ## 实际应用场景 1. **数据库设计文档**:创建可编辑的数据库设计文档 2. **团队协作**:在WPS中共享和共同编辑ER图 3. **系统评审**:在会议中展示数据库结构 4. **教学材料**:用于数据库设计教学 5. **系统维护**:帮助新开发人员理解数据库结构
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值