• 无人机硬件
    # 第二章无人机硬件 本章的目标是 "**从零打造一个能稳定飞行的平台**"。 一个完整的智能无人机硬件系统也可以拆解为**四个核心子系统**:动力系统、飞行控制系统、定位系统、以及任务计算机(机载电脑)。 > 任务1:参考文章,选择一套合适的动力方案 > > 任务2:利用文中的公式进行可行性验算 > > 任务3:下载并安装 QGroundControl 地面站,熟悉基本界面。 ## 一、基本概念 ### 1.什么是无人机? 在专业领域,它通常被称为“无人机系统”(UAS),所谓的“系统”,意味着它不仅仅是一架能飞的飞机,而是一个由**飞行平台**、**地面控制**和**通信链路**组成的完整协同网络。 本教程将聚焦于“四旋翼无人机”,四旋翼拥有优秀的垂直起降能力和悬停能力,是深度开发最理想的平台,并对 PX4 飞控架构进行开发。 ### 2.无人机的动力系统 无人机的动力系统相当于肌肉提供飞行的力量。 主要包括电池、电机、桨叶、电调,对于从零开始手搓无人机,不同的硬件的选型组合会影响到四旋翼的响应速度、灵活度等等性能指标。对于电赛来说最主要考虑的就是无人机的微调能力、响应速度以及维持自稳的能力。 对于选型,不是单独的去选某一个硬件然后每个都选最好的,而是要选一个合适的硬件组合。对于如何选择一个合适的动力套件,主要就是**电池、电机、桨叶**,这三个进行选择。 当我们需要对这些动力选型时,首先需要了解的就是选型过程中这些东西的关键参数都有什么? - 电池 - S: s代表Series(串联),比如 `1S:3.7V`, - P: **Parallel**代表并联 - C: 放电倍率 (C-Rating), 放电倍率通常比较容易忽略,但是它代表电池瞬间放电的能力,是无人机电池选型中非常重要的参数,如果 C 数太低,在无人机油门非常大时会产生掉压,导致动力不足,在压降非常严重时,还可能会导致无人机的主控或机载电脑出现关机。 $$ 最大持续电流 (A) = 容量 (Ah) \times C数 $$ - 电池类型: 一般选择 LiPo 电池,优点 C 数高,缺点能量密度较低 - 电机 - KV: 代表电压每升高 1 伏特 ($1V$),电机空载的转速($RPM$)会增加多少 $$ {电机转速 (RPM)} = {KV值} \times {工作电压 (V)} $$ - 扭矩:通常高 KV 的电机匝数少,线径粗,扭矩较低 - 桨叶 - 直径: 通常以英寸为单位,大直径桨通常配合低 KV 电机,用于需要大拉力的重型机或追求航时的机型;小直径桨配合高 KV 电机,用于追求反应速度和敏捷性的赛机 - 螺距:螺旋桨在一种完全不滑动的固体介质中旋转一圈,理论上能够前进的距离。类似于汽车不同的挡位。 - 高螺距(相当于高速档): 桨叶倾角大,每转一圈前进距离长。适合高速飞行,但起步(低速)时负载大。 - 低螺距(相当于低速档): 桨叶倾角小,更易旋转,加速快,低速推力稳,但最高时速受限。 - 叶片数: 通常双叶桨的效率是最高的,三页桨效率略低于双叶,但是因为推力密度更高、接触面积更大通常会让飞行更平顺。 - 其他需要关注的参数:材质、动平衡、中心孔径 - 电调 - 四合一 vs 单体 - 四合一: 节省空间,有利于布线,但是如果某一路坏了可能整个电调都需要更换 - 单体: 虽然布线可能会比较乱,但是单路损坏更换成本低 - DShot vs PWM - DShot: 全数字信号 - PWM: 模拟信号,一般已经不用了 - 对于电流和电压等参数要考虑当电机满负载时最大会产生的电流进行考虑,要考虑电机参数,比如最大支持单路45A电流或者60A电流 ### 3.无人机的控制系统架构讲解 无人机控制系统中的核心硬件也就是飞行控制器,简称飞控,飞控内部集成部分传感器比如气压计、陀螺仪等,并且能够运行控制代码,能够将传感器融合出的**当前状态**,与遥控器或者机载电脑输入的**目标状态**进行对比,通过PID来计算每个电机的转速。 #### 3.1 主流固件 当前的主流并且开源固件有*PX4* / *ArduPilot* / *Betaflight*, 对于比赛和科研等二次开发的人来讲,PX4是最建议使用的固件,因此本篇主要以PX4的讲解和使用为主 #### 3.2 PX4大致介绍 PX4不仅仅是一个简单的飞控固件,他是一个非常完整的生态系统,涵盖了非常多的应用场景,比如各种多旋翼无人机、固定翼无人机以及地面机器人。 并且PX4代码完全开源,支持非常多的仿真工具, 例如Gazebo和SITL, ##### 硬件标准:Pixhawk Pixhawk 是开源的无人机飞行控制硬件标准和平台, PX4 通常运行在符合 Pixhawk 标准的硬件上。但是pixhawk因为是开源的,所以市面上的pixhawk的硬件就非常的多,而且部分型号已经非常久远了基本被淘汰 ##### 地面站:QGroundControl (QGC) PX4可以支持多种地面站,但是官方推荐是 QGroundControl,并且QGroundControl也是可以二次开发的 ##### 通信协议:MAVLink PX4 采用 MAVLink 的通信协议,用于飞控与地面站,或者飞控与机载电脑之间通信 #### 3.3 PX4接线框架 下面这个图非常好的展示了px4的基本的接线框架 <img src="/media/uploads_img/temp/pixhawk_infographic2.jpg" width="500" alt="接线图"> ## 二、选型方案 我应该如何进行选型呢?都需要考虑哪些方面 1. 首先是核心需求,比如对于无人机硬件选型前,通常需要一个最开始的需求分析,通常需要考虑的方面有**总重**、**续航**、**轴距** 2. 机架选择一般是碳纤维板,硬度高重量轻,初期可用F450这种塑料来学习(炸机成本低) 3. 对于动力选型这一部分比较重要,通常需要遵循原则:大桨配低 KV,小桨配高 KV。 此外需要关注推重比,一般要求推重比大于2,但是也不能太大,推重比的计算公式为 $${\text{推重比}} = {\text{总推力}} / {\text{总重量}}$$ 示例:F450 机架 + 2212 920KV 电机 + 20A/30A 电调 + 9450 桨叶 + 3S/4S 电池。 4. 飞控系统选择,一般选择 Pixhawk 系列 5. 供电系统,除了LiPo电池需要考虑外,由于普通电调或者飞控自带的5V BEC通常电流比较小,因此需要单独购买大功率的BEC专门给机载电脑供电。 ## 三、快速上手 首先请先阅读PX4的官网中关于PX4关于多旋翼的信息,以及如何接线 ```a https://docs.px4.io/v1.16/zh/frames_multicopter/ ``` 对硬件进行组装,飞控注意前后,其他的零件位置不重要,重心要尽量处于中间 <img src="/media/uploads_img/temp/fc_orientation_1.png" width="550" alt="方向"> 组装完成确保硬件接线正确,烧录PX4的固件,首先在室内拆掉螺旋桨进行基础配置,最后再去室外 GPS 定位条件下进行测试。 ### 1.电机转向测试与电调校准 ### 2.飞控陀螺仪等传感器校准 ### 3.遥控器配对与校准 ### 4.飞行模式设置 ### 5.失控保护配置 ### 6.电源设置 防止电池过放炸机,校准电压/电流计,确保地面站显示的电压和万用表测的一致。 ### 7.室外测试 了解基本的无人机操作,以安全第一 position 定点模式 <img src="/media/uploads_img/temp/position_mc.DC-hD8a2.png" width="600" alt="position"> altitude 定高模式 <img src="/media/uploads_img/temp/altitude_mc.0b-bzlDZ.png" width="600" alt="altitude">
    12 666 2849 2026-01-21
  • Editor-md使用
    # Editor-md使用 开源网址 ``` https://pandao.github.io/editor.md/ ``` ## TeX公式渲染 如果不配置的话默认是无法进行渲染的,就像下面这样 ![TeX公式不渲染的问题](image.png) ### 文章编写和展示页面配置 配置文章编写页面,首先下载Tex公式渲染需要的程序,建议用最新的网址,不要下载版本太低的,如果版本太低可能会不支持中文 cdn网址: ```0.16.9 https://cdn.bootcdn.net/ajax/libs/KaTeX/0.16.9/katex.min.js https://cdn.bootcdn.net/ajax/libs/KaTeX/0.16.9/katex.min.css ``` 由于cdn引用加载比较慢,建议保存到本地路径 `/static/editor.md-master/lib/KaTeX/0.16.9/` 下 js代码配置,启用tex,并引入刚刚下载的本地文件 ```javascript <script type="text/javascript"> $(function() { var editor = editormd("editor", { /*** 其他配置 ***/ tex : true, }); editormd.katexURL = { js : "/static/editor.md-master/lib/KaTeX/0.16.9/katex.min", // 注意:不需要写 .js 后缀 css : "/static/editor.md-master/lib/KaTeX/0.16.9/katex.min" // 注意:不需要写 .css 后缀 }; }); </script> ``` ## 代码一键复制 ![标签结构](/media/uploads/articles_image/202601/20260124_234941_屏幕截图_2026-01-24_234907.png "标签结构") 基本思想,给每个 pre 创建一个i标签,并创建一个点击事件,点击事件找到父亲并从父亲下的所有的 code 里面获取内容,最终添加到剪切板,遍历 `pre` 代码实现: ``` code_copy(){ $('pre').each(function (){ // 使用elementplus的图标库 let iconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" ><path fill="currentColor" d="巴拉巴拉巴拉巴拉..."></path></svg>' let copy_btn = $('<i title="copy" class="code_copy">' + iconSvg + '</i>'); $(this).append(copy_btn) // 给刚才创建的按钮绑定点击事件 copy_btn.on('click', function() { // 克隆一份当前的 pre 标签 let clone = $(this).parent().clone(); // 在克隆体中删掉“复制按钮”自己,防止把按钮的乱码也复制进去 clone.find('.code_copy').remove(); let codeText = ''; let $dom = clone.find('code'); $dom.each(function() { // 遍历每一行,获取文本并手动加上换行符 codeText += $(this).text().trimEnd() + '\n'; }); // 创建一个临时的 textarea 元素 let textarea = $('<textarea>' + codeText + '</textarea>') // 挂载 -> 选中 -> 执行命令 -> 移除 $('body').append(textarea) textarea[0].select() document.execCommand('Copy') textarea.remove() ElementPlus.ElMessage.success('复制成功!'); }); }) } ```
    4 666 0 2026-01-21
  • 博客上传文章配置
    # 博客上传文章配置 本地图片目录,图片上传方法 <img src="/media/uploads/articles_image/202601/本地文章.png" width="600" alt="本地文章"> 问题的核心在于:Markdown 里的图片链接是“本地相对路径”,而网页需要的是“服务器 URL 路径”。一般用户的上传文件都放在 `/media/` 路径下,目录一般的结构: ```Plaintext my_blog_project/ ├── ... ├── static/ 静态文件 (CSS, JS, 网站Logo) └── media/ (所有用户文件) ├── .../... <-- 其他用户文件 └── uploads/ <-- 上传的 ├── /articles_image <-- 文章的图片 │ ├── 202601/ <-- 按年月分文件夹(推荐,防止一个文件夹几千张图卡死) │ │ ├── drone-schematic.jpg │ │ └── pixhawk-wiring.png │ └── 202602/ └── ... ``` ## 一、setting.py 配置 首先django项目自身需要配置,这个一般是一个工程的前提 在settings.py中,提前配置路径,这是一个项目的基本前提,不仅仅是图片上传需要这个配置,包括头像、封面等都需要配置,否则将会找不到 ```python # 用户自己上传文件的话需要自己配置,添加以下两行 MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' ``` ## 二、手动管理图片 将图片复制到 `media/uploads_img/` 下面的目录下然后给 `.md` 文章中图片的目录前面加上图片的url路径,比如; 原来的内容: ```html <img src="position_mc.DC-hD8a2.png" width="600" alt="position"> ``` 修改后的内容 (文件路径为media下的实际路径): ```html <img src="/media/uploads/articles_image/202601/position_mc.DC-hD8a2.png" width="600" alt="position"> ``` ### img的html格式报错问题 这里用标准的markdown格式可以渲染图片,但是 html 格式的图片就无法进行。由于标准的markdown无法用宽度调节的代码,并且为了更好的适配不同的格式,因此还是需要兼容img标签,这里editor.md本身是支持html语法的 注:虽然此功能能极大地扩展 Markdown 语法,但也面临着安全上的风险,所以默认是不开启的。 <img src="/media/uploads/articles_image/202601/error-1.png" width="600" alt="error"> 这是由于 editor.md 默认不启用 HTML 语法, 因此需要配置editor.md的js代码引用部分 下面是官网的说明 { htmlDecode : true // Decode all html tags & attributes // Filter tags/attributes expression : tagName,tagName,...|attrName,attrName,... htmlDecode : "style,script,iframe,sub,sup|on*" // Filter tags, and all on* attributes //htmlDecode : "style,script,iframe,sub,sup|*" // Filter tags, and all attributes //htmlDecode : "style,script,iframe,sub,sup,embed|onclick,title,onmouseover,onmouseout,style" // Filter tags, and your custom attributes } 因此只需要在原本的JS代码中配置 `htmlDecode : "style,script,iframe,sub,sup|on*"` 即可 ## 三、图片自动上传配置 核心部分,通过editor.md的接口进行配置 ### 1.前端配置 ```javascript var editor = editormd("test-editormd", { width : "100%", height : 720, path : "/static/editor.md-master/lib/", // --- 核心配置开始 --- // 1. 开启图片上传功能 imageUpload : true, // 默认为关闭 imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"], // 允许的格式 // 2. 指向 Django 的上传接口,然后再django的urls.py里面配置 imageUploadURL : "/api/upload_image/", // --- 核心配置结束 --- // ... 其他配置 ... }); ``` 然后图片中就会有这个"本地上传"的按钮,点击可以发现后台会报错误,这是因为还没有配置后端的代码 <img src="/media/uploads/articles_image/202601/error-not-found.png" width="600" alt="error not found"> <img src="/media/uploads/articles_image/202601/本地上传.png" width="400px" alt="本地上传"> ### 2.后端配置 前提:django下有api这个app,并且所有以api开头的请求都分发到api下面 editor.md 官方图片上传示例 <img src="/media/uploads/articles_image/202601/editor.md图片上传示例.png" width="700px" alt="editor.md图片上传示例"> #### 2.1 创建视图函数 在api这个app下目录下,创建文件 `/api/views/uploads.py` ```python import os import uuid import datetime from django.conf import settings from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.clickjacking import xframe_options_exempt @csrf_exempt @xframe_options_exempt def upload_image(request): if request.method == 'POST': res={ 'success': 0, # 0表示失败,1表示成功 'message': "上传失败", 'url': "" } # 获取上传的文件 (注意这个 key 是 editor.md 固定的) img_file = request.FILES.get('editormd-image-file') if not img_file: res['message'] = "未获取到图片" return JsonResponse(res) # root: 文件名, ext: 文件后缀 root, ext = os.path.splitext(img_file.name) # 安全清洗: 把文件名里的空格替换成下划线,防止 Linux 路径报错 safe_root = root.replace(' ', '_') now = datetime.datetime.now() image_name = f"{now.strftime('%Y%m%d_%H%M%S')}_{safe_root}{ext}" relative_path = os.path.join('uploads', 'articles_image', now.strftime('%Y%m')) abs_image_path = os.path.join(settings.MEDIA_ROOT, relative_path) if not os.path.exists(abs_image_path): os.makedirs(abs_image_path) file_path = os.path.join(abs_image_path, image_name) with open(file_path, 'wb+') as f: for chunk in img_file.chunks(): f.write(chunk) url_path = os.path.join(settings.MEDIA_URL, relative_path, image_name) # 强制替换反斜杠为正斜杠 (兼容 Windows 开发环境) url_path = url_path.replace('\\', '/') # 测试 # print(img_file, root, ext, safe_root, image_name) # print('上传文件名字:' + root + ext) # print('文件保存名称:' + image_name) # print('相对路径:' + relative_path) # print('绝对路径:' + abs_image_path) # print('返回路径:' + url_path) res['success'] = 1 res['message'] = "上传成功" res['url'] = url_path return JsonResponse(res) ``` <img src="/media/uploads/articles_image/202601/print.png" alt="print"> 代码中用到的工具 - os.path.splitext(...): 这是一个 Python 工具,专门把文件名切成两半:(文件名, 后缀)。 - os.path.join(absolute_path, image_name) - os.path.join(...): 路径拼接 - 把‘文件夹路径’和‘文件名’拼在一起 - 'wb+': ##### Django 安全机制拦截允许 由于 editor.md 是一个第三方的 JavaScript 库,它内部写死了一套上传图片的逻辑,根据后面返回到前端的报错提示, 大概率是用的比较古老的hidden iframe表单提交,并且他默认也不知道项目是Django项目,因此不会带Token,就需要 Django 不再验证请求的来源是否绝对合法,直接放行,因此使用装饰器 `@csrf_exempt` 来免去验证请求。 但是这里发现仅仅免去验证请求还不够,在前端当后端返回一个URL时前端也报错 <img src="/media/uploads/articles_image/202601/error-2.png" width="800px" alt="error-2"> editor.md 的图片上传机制是创建一个看不见的 `<iframe>` 来提交图片的, Django 发现这个请求要在 iframe 里显示,直接拒绝了 (deny)。然后 Editor.md 的 JS 代码试图去读取 iframe 里的上传结果(JSON),结果发现 iframe 被浏览器封锁了,于是报错说“无法跨域访问”。 因此这里还需要再创建一个装饰器 `@xframe_options_exempt` 因此需要引入下面两个装饰器 ```python from django.views.decorators.csrf import csrf_exempt from django.views.decorators.clickjacking import xframe_options_exempt ``` #### 2.2 配置路由(urls.py) 前提在 setting.py 目录下的 urls.py 中以及将所有以/api开头的分发到api/urls.py中,再由他进行分发 在setting.py 目录下 `urls.py` 中 ```python from django.urls import include, re_path urlpatterns = [ # ... 其他路由 ... re_path(r'^api/', include('api.urls')), # 将所有api开头的api,都分发到api的urls.py中 ] ``` 在 `api/urls.py` 中 ```python from django.urls import path from api.views import uploads urlpatterns = [ # ... 其他路由 ... path('upload_image/', uploads.upload_editor_image, name='upload_editor_image'), ] ```
    6 666 0 2026-01-22
  • django的CDN配置
    # django的CDN配置 ## 修改 settings.py 在 `settings.py`中配置 ```python # 设为 True 则使用 CDN,False 则使用本地路径 USE_CDN = False # cdn CDN_LINKS = { 'vue': 'https://cdn.staticfile.org/vue/3.4.21/vue.global.prod.js', 'axios': 'https://cdn.staticfile.org/axios/1.6.8/axios.min.js', 'jquery': '/static/js/jquery/jquery-3.7.1.min.js', 'element_plus_index': 'https://cdn.staticfile.org/element-plus/2.3.0/index.full.min.js', 'element_plus_icons': 'https://cdn.staticfile.org/element-plus-icons-vue/2.3.1/index.iife.min.js', 'katex': 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min', # 不需要加后缀,js和css都通用 } # 本地 LOCAL_LINKS = { 'vue': '/static/vue/vue.global.js', 'axios': '/static/js/axios/axios.min.js', 'jquery': '/static/js/jquery/jquery-3.7.1.min.js', 'element_plus_index': '/static/element-plus/element-plus@2.13.0/dist/index.full.js', 'element_plus_icons': '/static/element-plus/icons-vue@2.3.2/dist/index.iife.min.js', 'katex': '/static/editor.md-master/lib/KaTeX/0.16.9/katex.min', # 不需要加后缀,js和css都通用 } ``` ## 创建自定义 Context Processor 在app下创建 `context_processors.py` ## 在ettings中注册 ``` TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ # 其他配置 ... # 用户自定义函数,加上这一行 # 格式:'应用名.文件名.函数名' 'app01.context_processors.js_libs', ], }, }, ] ``` ## 引用 ```html <script src="{{ libs.vue }}"></script> <script src="{{ libs.element_plus_index }}"></script> <script src="{{ libs.element_plus_icons }}"></script> <script src="{{ libs.jquery }}"></script> <script src="{{ libs.axios }}"></script> ```
    17 666 0 2026-01-22