D - 继续畅通工程

题目描述:

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。

当N为0时输入结束。
Output
每个测试用例的输出占一行,输出全省畅通需要的最低成本。
Sample Input
3
1 2 1 0
1 3 2 0
2 3 4 0
3
1 2 1 0
1 3 2 0
2 3 4 1
3
1 2 1 0
1 3 2 1
2 3 4 1
0
Sample Output
3
1
0

分析:

本题的话也是一个最小生成树。只是要将输入的数据转化成邻接矩阵。然后同时需要注意那两个城镇之间是否有道路连接。如果有道路连接的话,那我们可以将其两个城镇之间的修路成本置为0。这样的话,就是一个模板题目了。只不过在实际操作做,我们需要将邻接矩阵里面i==j的位置置位-1。
AC代码:

#include"stdio.h"
#include"string.h"
#include"algorithm"
  using namespace std;
#define INF 1000000
void MinSpanTree(int Graph[][5000],int N)
{
    int i,j,k;
    int vexs[5000];
    int weight[5000];
    int sum=0;
    for(i=0;i<N;
我们将基于 **STM32CubeMX + HAL 库 + 蓝牙模块(如 HC-05/HC-06 或 ESP8266)** 实现一个完整的 **蓝牙遥控无人机飞行控制系统**,支持: - 通过手机 APP 发送指令 - 解析蓝牙串口数据 - 控制无人机的 **油门、偏航、俯仰、横滚** - 集成你已有的 **MPU6050 姿态解算** 和 **串级 PID 控制系统** --- ## ✅ 硬件与软件环境 | 项目 | 配置 | |------|------| | MCU | STM32F103C8T6 / F4系列等 | | 开发工具 | STM32CubeMX + Keil / STM32CubeIDE | | 蓝牙模块 | HC-05 / HC-06(SPP 协议),连接到 USART2 | | 通信方式 | UART 接收字符串或二进制协议 | | 手机端APP | 可使用“蓝牙串口助手”或自定义APP | --- ## ✅ 功能目标 实现以下功能: - 手机发送控制指令(例如:`R:50,P:30,Y:20,T:70`) - STM32 解析指令 → 提取 Roll, Pitch, Yaw, Throttle - 结合 MPU6050 姿态 → 运行串级PID → 输出PWM控制电机 --- ## ✅ 第一步:STM32CubeMX 配置 ### 1. 时钟配置(以 F103 为例) - SYS: 选择 `Serial Wire` 调试接口 - RCC: HSE 外部晶振使能 - Clock Configuration: 设置系统主频为 72MHz ### 2. UART 配置(用于蓝牙) - USART2: - Mode: Asynchronous - Baud Rate: 9600 或 115200(根据蓝牙模块设置) - NVIC: 使能中断(优先级高于主循环) > 📌 引脚连接示例: ``` HC-05 TX → PA3 (USART2_RX) HC-05 RX → PA2 (USART2_TX) ``` ### 3. 生成代码并添加外设句柄 ```c extern UART_HandleTypeDef huart2; ``` --- ## ✅ 第二步:蓝牙接收模块(非阻塞方式) 我们使用 **中断 + 缓冲区** 方式接收数据,避免阻塞主循环。 ### 文件:`bluetooth.h` ```c // bluetooth.h #ifndef __BLUETOOTH_H #define __BLUETOOTH_H #include <stdint.h> #include <string.h> // 最大命令长度 #define BT_BUFFER_SIZE 64 #define BT_CMD_TERMINATOR '\n' // 控制结构体 typedef struct { int16_t roll; // -100 ~ +100 int16_t pitch; // -100 ~ +100 int16_t yaw; // -100 ~ +100 int16_t throttle; // 0 ~ 100 uint8_t updated; // 标志位:是否接收到新指令 } BT_Control_t; // 函数声明 void Bluetooth_Init(void); void Bluetooth_Parse(void); // 在主循环中调用解析 extern BT_Control_t bt_cmd; #endif ``` --- ### 文件:`bluetooth.c` ```c // bluetooth.c #include "bluetooth.h" #include "usart.h" // 自动生成的头文件 #include "string.h" #include "stdio.h" BT_Control_t bt_cmd = {0}; uint8_t bt_rx_byte = 0; uint8_t bt_rx_buffer[BT_BUFFER_SIZE]; uint8_t bt_rx_index = 0; void Bluetooth_Init(void) { // 启动 UART 中断接收 HAL_UART_Receive_IT(&huart2, &bt_rx_byte, 1); } // UART 回调函数(需在 main.c 中重写) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == huart2.Instance) { if (bt_rx_byte == BT_CMD_TERMINATOR || bt_rx_index >= BT_BUFFER_SIZE - 1) { bt_rx_buffer[bt_rx_index] = '\0'; // 触发解析 Bluetooth_Parse(); // 重置缓冲区 bt_rx_index = 0; } else { bt_rx_buffer[bt_rx_index++] = bt_rx_byte; } // 继续开启下一次中断接收 HAL_UART_Receive_IT(&huart2, &bt_rx_byte, 1); } } void Bluetooth_Parse(void) { char *buffer = (char*)bt_rx_buffer; // 示例格式: R:50,P:30,Y:20,T:70\n int r = 0, p = 0, y = 0, t = 0; int parsed = sscanf(buffer, "R:%d,P:%d,Y:%d,T:%d", &r, &p, &y, &t); if (parsed == 4) { bt_cmd.roll = (int16_t)(r > 100 ? 100 : (r < -100 ? -100 : r)); bt_cmd.pitch = (int16_t)(p > 100 ? 100 : (p < -100 ? -100 : p)); bt_cmd.yaw = (int16_t)(y > 100 ? 100 : (y < -100 ? -100 : y)); bt_cmd.throttle = (int16_t)(t > 100 ? 100 : (t < 0 ? 0 : t)); bt_cmd.updated = 1; // 标记更新 } // 可选:打印调试信息 // printf("Parsed: R%d P%d Y%d T%d\r\n", r, p, y, t); } ``` > ⚠️ 注意:你需要在 `main.c` 的 `HAL_UART_RxCpltCallback` 中调用 `Bluetooth_Parse()` 或确保回调被正确注册。 --- ## ✅ 第三步:整合到主控逻辑(`main.c`) ```c // main.c #include "main.h" #include "mpu6050.h" #include "imu.h" #include "pid.h" #include "cascade_pid.h" #include "bluetooth.h" #include "motor.h" // 自定义电机混控 extern UART_HandleTypeDef huart2; CascadePID_Controller cpid; _st_AngE Angle = {0}; float base_throttle = 0.0f; int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); // 蓝牙串口 MPU_Init(); MPU_Calibrate(100); imu_rest(); CascadePID_Init(&cpid); Motor_Init(); // 初始化PWM输出 Bluetooth_Init(); // 启动蓝牙接收中断 float dt; uint32_t last_tick = HAL_GetTick(); while (1) { uint32_t now = HAL_GetTick(); dt = (now - last_tick) / 1000.0f; if (dt < 0.015f) { // 最小时间间隔保护 HAL_Delay(1); continue; } last_tick = now; // --- 1. 获取IMU数据并更新姿态 --- MpuGetData(); GetAngle((const _st_Mpu*)&MPU6050, &Angle, dt); // --- 2. 检查蓝牙是否有新命令 --- if (bt_cmd.updated) { bt_cmd.updated = 0; // 清除标志 // --- 3. 将遥控输入映射为目标角度 --- float target_roll = ((float)bt_cmd.roll) * 30.0f / 100.0f; // ±30° float target_pitch = ((float)bt_cmd.pitch) * 30.0f / 100.0f; float target_yaw = ((float)bt_cmd.yaw) * 90.0f / 100.0f; // ±90°快速转向 base_throttle = ((float)bt_cmd.throttle) * 1000.0f; // 映射到PWM范围 // --- 4. 运行串级PID --- float out_roll, out_pitch, out_yaw; CascadePID_Update(&cpid, target_roll, target_pitch, target_yaw, Angle.roll, Angle.pitch, Angle.yaw, MPU6050.gyroX, MPU6050.gyroY, MPU6050.gyroZ, dt, &out_roll, &out_pitch, &out_yaw); // --- 5. 混控输出到四个电机 --- Motor_Mixer(out_roll, out_pitch, out_yaw, base_throttle); } else { // 无指令时停机(安全机制) Motor_StopAll(); } HAL_Delay(20); // 控制频率 ~50Hz } } ``` --- ## ✅ 手机端操作建议 ### 使用任意蓝牙串口APP(如:Serial Bluetooth Terminal) 发送指令格式(每行一条): ``` R:0,P:0,Y:0,T:20 R:50,P:0,Y:0,T:40 R:0,P:-30,Y:60,T:50 ``` 含义: - `R`: Roll(左负右正) - `P`: Pitch(前负后正) - `Y`: Yaw(左转负,右转正) - `T`: Throttle(油门百分比) > 💡 你可以开发一个简单的 Android/iOS App 实现摇杆控制,自动拼接并发送这些命令。 --- ## ✅ 安全增强建议 | 功能 | 实现方法 | |------|----------| | **超时保护** | 如果超过1秒未收到指令 → 自动降落 | | **指令校验** | 添加校验和或固定起始符(如 `$`) | | **最小油门检测** | 防止意外启动 | | **状态反馈** | 通过蓝牙回传当前姿态 `printf("A:%.1f,%.1f,%.1f\r\n", Angle.roll, ...);` | --- ## ✅ 扩展功能设想 1. **双向通信**:手机显示当前姿态角、电池电压等 2. **模式切换**:通过蓝牙切换“定高模式”、“航向锁定”等 3. **参数调节**:手机发送 `KP:3.0,KI:0.02,KD:0.5` 修改PID参数 4. **日志记录**:通过蓝牙导出飞行数据用于分析 ---请问该蓝牙部分怎么在e调试软件上使用,请详细说明
最新发布
10-02
要在 **STM32 蓝牙遥控无人机系统** 中使用调试软件(如串口助手、蓝牙调试工具)进行开发和测试,你需要从 **硬件连接 → 软件配置 → 数据收发 → 实时监控与调试** 全流程掌握如何验证蓝牙通信是否正常。 下面我将详细说明:**如何在 PC 和手机端使用常见调试软件来测试和调试你的蓝牙控制系统**,包括推荐工具、操作步骤、数据格式、常见问题排查等。 --- ## ✅ 一、调试目标 你希望通过调试软件实现以下功能: | 目标 | 说明 | |------|------| | 🔹 发送控制指令 | 模拟手机 App 向 STM32 发送 `R:50,P:30,Y:20,T:70` 这类命令 | | 🔹 接收反馈信息 | 查看 STM32 返回的姿态角、状态、错误日志等 | | 🔹 验证通信稳定性 | 确保无丢包、乱码、延迟过高 | | 🔹 快速迭代PID参数 | 通过发送 `KP:2.0,KI:0.1` 动态修改参数 | --- ## ✅ 二、推荐的调试软件(PC + 手机) ### 🖥️ PC 端推荐工具(USB转TTL + HC-05直连) | 工具 | 特点 | 下载地址 | |------|------|---------| | **XCOM**(精研电子出品) | 中文界面,支持HEX/ASCII发送,自动保存指令 | [http://www.xkxy.com](http://www.xkxy.com) | | **SSCOM**(友善串口) | 多通道、定时发送、脚本功能强 | 百度搜索下载 | | **PuTTY / Tera Term** | 轻量级终端,适合纯文本通信 | 开源免费 | | **Arduino Serial Monitor** | 简单可用,但不支持蓝牙模块直接连接 | Arduino IDE 内置 | > ⚠️ 注意:PC 不能直接连蓝牙模块(除非自带蓝牙适配器),通常需要: > > ``` > STM32 USART TX → USB-TTL 模块 RX > USB-TTL 模块 → PC USB 口 > ``` 这样你可以用 XCOM 等软件监听所有蓝牙通信内容。 --- ### 📱 手机端推荐 App(直接连 HC-05) | App 名称 | 平台 | 特点 | |--------|------|------| | **Serial Bluetooth Terminal** | Android | 开源、稳定、支持 ASCII/HEX | | **BLE Scanner** | iOS/Android | 支持 BLE 设备扫描(仅限 BLE 模块) | | **Bluetooth Controller** | Android | 图形化按钮/滑块控制 | | **nRF Connect** | iOS/Android | Nordic 官方工具,专业级 BLE 调试 | > 💡 对于 **HC-05/HC-06**(经典蓝牙 SPP 协议),推荐使用 **Serial Bluetooth Terminal** --- ## ✅ 三、PC 端调试方法(使用 XCOM 示例) ### 步骤 1:硬件连接 ``` STM32 的 USART2_TX (PA3) → USB-TTL 模块的 RXD STM32 的 USART2_RX (PA2) ← USB-TTL 模块的 TXD GND ↔ GND ``` > ❗注意:此时 **不要接 HC-05 模块**!否则会冲突。这是为了直接监听 MCU 输出日志。 ### 步骤 2:打开 XCOM 1. 插入 USB-TTL 模块,设备管理器查看 COM 口(如 COM5) 2. 打开 XCOM: - 选择正确的 COM 口 - 波特率设为 `115200`(或你代码中设置的值) - 数据位:8,停止位:1,校验位:无 - 编码方式:ASCII 3. 勾选: - ✅ “新行”(发送时加 `\n`) - ✅ “显示时间戳” - ✅ “自动换行” ### 步骤 3:发送控制指令 在发送区输入: ``` R:50,P:0,Y:0,T:30 ``` 点击“手动发送”,观察 LED 或电机是否有反应。 > ✅ 成功标志:主循环解析成功,并调用 `Motor_Mixer()` 输出 PWM。 ### 步骤 4:接收 STM32 回传信息(可选增强) 修改 `main.c` 添加反馈: ```c // 在 Bluetooth_Parse 成功后添加 printf("CMD_OK: R%d P%d Y%d T%d\r\n", bt_cmd.roll, bt_cmd.pitch, bt_cmd.yaw, bt_cmd.throttle); // 或每帧回传姿态 printf("ATT: %.1f,%.1f,%.1f\r\n", Angle.roll, Angle.pitch, Angle.yaw); ``` 确保你在工程中重定向了 `printf` 到 UART2: ```c #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, HAL_MAX_DELAY); return ch; } ``` 这样你在 XCOM 上就能看到实时姿态输出! --- ## ✅ 四、手机端调试方法(使用 Serial Bluetooth Terminal) ### 步骤 1:配对蓝牙模块 1. 给 HC-05 上电(LED 慢闪表示等待连接) 2. 手机打开蓝牙,搜索设备 → 找到 `HC-05` 或 `linvor` 3. 配对密码一般是 `1234` 或 `0000` ### 步骤 2:打开 Serial Bluetooth Terminal 1. 点击右上角 `Connect a device` 2. 选择 `Paired Devices` 3. 找到并点击 `HC-05` 4. 连接成功后进入通信界面 ### 步骤 3:发送指令 - 输入框输入: ``` R:0,P:0,Y:0,T:20 ``` - 点击发送按钮(箭头图标) > ✅ 注意:必须以 `\n` 结尾!我们的协议依赖换行符作为结束标志。 ### 步骤 4:查看返回数据 如果 STM32 有 `printf` 回传,则你会在接收区看到类似: ``` CMD_OK: R0 P0 Y0 T20 ATT: 0.1,-0.3,0.0 ``` 这表明通信双向畅通--- ## ✅ 五、常见问题与解决方法 | 问题现象 | 原因 | 解决方案 | |--------|------|----------| | 收不到任何数据 | UART 波特率不匹配 | 检查 CubeMX 设置和蓝牙模块默认波特率(常用 9600 / 115200) | | 数据乱码 | 电平不匹配(5V vs 3.3V) | 使用电平转换或确保模块是 3.3V 逻辑 | | 发送无效 | 没有 `\n` 换行符 | 在调试软件中勾选“发送新行”或手动加 `\n` | | 控制无响应 | `bt_cmd.updated` 未被清除 | 检查是否在主循环中正确处理并清零 | | 手机无法连接 | HC-05 处于 AT 模式 | 断电,按住 KEY 引脚再上电,退出 AT 模式 | | 数据粘包 | 多条命令合并接收 | 改进解析逻辑,使用起始符 `$` 和校验机制 | --- ## ✅ 六、建议改进:增加调试友好性 ### 1. 添加命令回显 + ACK ```c void Bluetooth_Parse(void) { ... if (parsed == 4) { // 更新命令 bt_cmd.roll = ...; bt_cmd.updated = 1; // 回传确认 printf("ACK: R%d,P%d,Y%d,T%d\r\n", r, p, y, t); } else { printf("ERR: Invalid format\r\n"); } } ``` ### 2. 添加 HELP 命令 ```c if (strncmp((char*)bt_rx_buffer, "HELP", 4) == 0) { printf("Usage: R:x,P:y,Y:z,T:w\\n"); printf("Range: R/P: -100~100, Y: -100~100, T: 0~100\r\n"); } ``` ### 3. 添加实时姿态推送开关 ```c uint8_t streaming_mode = 0; if (strncmp((char*)bt_rx_buffer, "STREAM ON", 9) == 0) { streaming_mode = 1; } // 主循环中 if (streaming_mode && dt > 0.1f) { printf("ATT:%.2f,%.2f,%.2f\r\n", Angle.roll, Angle.pitch, Angle.yaw); } ``` --- ## ✅ 七、完整调试流程总结 | 步骤 | 操作 | |------|------| | 1 | 使用 USB-TTL + XCOM 测试 UART 发送/接收 | | 2 | 确保 `printf` 能打印姿态角 | | 3 | 加入蓝牙接收中断,测试字符串解析 | | 4 | 接入 HC-05,手机用 Serial Bluetooth Terminal 连接 | | 5 | 发送 `R:0,P:0,Y:0,T:10` 观察电机微动 | | 6 | 逐步提升油门,测试 PID 响应 | | 7 | 开启 STREAM 模式,实时监控飞行姿态 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值