markdowm完善了一下,删除无用的文件

master
45coll 2021-11-09 16:22:01 +08:00
parent 558777d2c6
commit 4737eb7517
40 changed files with 140 additions and 17891 deletions

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

12
.idea/foc.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
</module>

View File

@ -0,0 +1,14 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="PyQt5" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/foc.iml" filepath="$PROJECT_DIR$/.idea/foc.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,39 +1,75 @@
# FocProject # 自平衡莱洛三角形可充电版
#### 介绍 #### 介绍
{**以下是 Gitee 平台说明,您可以替换此简介** **在B站[“基于LQR控制器的自平衡莱洛三角形”](https://www.bilibili.com/video/BV19v411n7mN)基础上添加了充电模块**
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN。专为开发者提供稳定、高效、安全的云端软件开发协作平台 主控芯片使用ESP32并配置了调参上位机可以很方便的通过wifi无线调参。无刷控制使用灯哥开源FOC。制作出一个方便复刻的自平衡莱洛三角形在桌面上作为一个摆件还是非常不错的[展示视频]()
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
#### 软件架构 #### 1 软件架构
软件架构说明 在原作者的自平衡控制**电压**算法上进行修改,将电压控制改为**速度**控制。使对模型的控制在物理上更加容易理解。并且代码的调参都可以通过连接ESP32的wifi调整。具体特性如下
- **基于 Arduino**:运行在 ESP32 Arduino 上
- **控制模式丰富**:电压控制和速度控制
#### 安装教程 ![Image text](image/gui_main.jpg)
#### 2 硬件特性
1. xxxx | 说明 | 参数 |
2. xxxx | ---------------- |---------------------- |
3. xxxx | 莱洛三角形尺寸 | 100*100 mm |
|动量轮尺寸|80*80 mm|
| 输入电压 | 3.7v锂电池*3|
|充电电压| 5V 从Type-C口输入|
|充电芯片CS5095|5V输入,最大1.2A充电电流|
|串口芯片CH340C|需要打开开关才能下载|
| 主控 | ESP-WROOM-32 |
|电机驱动芯片L6234PD|引脚:32, 33, 25, 22; 22为enable|
| AS5600 编码器 |SDA-23 SCL-5 |
| MPU6050六轴传感器 | SDA-19 SCL-18 |
#### 使用说明 #### 3 使用说明
1. xxxx 1. 前往灯哥开源[FOCgit](https://gitee.com/ream_d/Deng-s-foc-controller)下载Arduino开发环境~~也可自行下载Arduino并安装SimpleFOC~~并打开本项目内的Arduino内的main烧录程序到ESP32。
2. xxxx 2. 打开本项目内的python_gui内的FOC_gui.exe并连接上WIFIESP32。点击设置开始调参。
3. xxxx
#### 参与贡献 比如让平衡角度为90度则输入TA90并且会存入eeprom的位置0中 注wifi发送**命令不能过快**因为每次都会保存进eepromK参数没有保存到EEPROM所以可以使用滑条调整。
1. Fork 本仓库 | 参数命令 | 说明 |
2. 新建 Feat_xxx 分支 | ---------------- |---------------------- |
3. 提交代码 | TA | target_angle平衡角度 例如TA89.3 设置平衡角度89.3|
4. 新建 Pull Request | SV | swing_up_voltage摇摆电压 左右摇摆的电压,越大越快到平衡态,但是过大会翻过头|
|SA|swing_up_angle摇摆角度 离平衡角度还有几度时候,切换到自平衡控制|
|VP1|速度环的PID的P1是稳定在平衡角度之前的P值|
|VI1|速度环的PID的I1是稳定在平衡角度之前的I值|
|VP2|速度环的PID的P2是稳定后的P值|
|VI2|速度环的PID的I2是稳定后的I值|
|K**1**1|**1和2**是电压控制时参数。LQR的参数1*角度差值|
|K**1**2|**1和2**是电压控制时参数。LQR的参数2*左右倾倒加速度|
|K**1**3|**1和2**是电压控制时参数,**3和4**是速度控制时参数。LQR的参数3*当前速度|
#### 4 硬件设计
使用立创EDA绘制电路原理图LaserMaker绘制莱洛三角形和动量轮有激光切割机可以事先切割结构作为参考。将绘制完的图形导入到立创EDA中可作为PCB的外框。丝印图案分别是**Gawr Gura**、**ouro kronii** ~~helicopter~~
#### 特技 LaserMaker绘制的plt在**莱洛三角结构**文件夹内
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 感谢嘉立创的PCB制板使DIY电路制作变得非常便利
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 [莱洛三角形PCB](https://lceda.cn/45coll/zi-ping-heng-di-lai-luo-san-jiao_10-10-ban-ben)
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 [动量轮]()
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) 具体需要购买的物品在**物料清单.xlsx**中
#### 5 Ctrl+C +V参考
Arduino上的控制算法抄的是原作者的LQR无刷电机控制是抄灯哥开源的。电机控制引脚定义与传感器定义和灯哥开源FOC控制板2.0版完全一样。
Python的GUI是抄SimpleFOC的SimpleFOCStudio。
充电电路完全抄的是立创广场开源的CS5095充电方案。
1. 原作者基于LQR控制器的自平衡莱洛三角形[BV19v411n7mN](https://www.bilibili.com/video/BV19v411n7mN)
2. 灯哥开源FOC [https://gitee.com/ream_d/Deng-s-foc-controller](https://gitee.com/ream_d/Deng-s-foc-controller)
3. 充电芯片电路[https://oshwhub.com/Aknice/cs5095e-san-jie-li-dian-chi-sheng-ya-chong-dian-dian-lu](https://oshwhub.com/Aknice/cs5095e-san-jie-li-dian-chi-sheng-ya-chong-dian-dian-lu)
#### 6 有用的地方
1. Arduino的程序中的command.h、command.cpp可以支持任意的字符串输入。在其他项目中一样可以用无论是wifi接收到的字符串数据或者是串口的字符串数据。
2. GUI上位机可以在其他wifi项目中可以继续使用用来调参还是很方便。

View File

@ -1,28 +0,0 @@
#include "Command.h"
void Command::run(char* str){
for(int i=0; i < call_count; i++){
if(isSentinel(call_ids[i],str)){ // case : call_ids = "T2" str = "T215.155"
call_list[i](str+strlen(call_ids[i])); // get 15.155 input function
break;
}
}
}
void Command::add(char* id, CommandCallback onCommand){
call_list[call_count] = onCommand;
call_ids[call_count] = id;
call_count++;
}
void Command::scalar(float* value, char* user_cmd){
*value = atof(user_cmd);
}
bool Command::isSentinel(char* ch,char* str)
{
char s[strlen(ch)+1];
strncpy(s,str,strlen(ch));
s[strlen(ch)] = '\0'; //strncpy need add end '\0'
if(strcmp(ch, s) == 0)
return true;
else
return false;
}

View File

@ -1,17 +0,0 @@
#include <Arduino.h>
// callback function pointer definiton
typedef void (* CommandCallback)(char*); //!< command callback function pointer
class Command
{
public:
void add(char* id , CommandCallback onCommand);
void run(char* str);
void scalar(float* value, char* user_cmd);
bool isSentinel(char* ch,char* str);
private:
// Subscribed command callback variables
CommandCallback call_list[20];//!< array of command callback pointers - 20 is an arbitrary number
char* call_ids[20]; //!< added callback commands
int call_count;//!< number callbacks that are subscribed
};

View File

@ -1,19 +0,0 @@
#include "Command.h"
//命令设置
float target_velocity = 0;
Command comm;//声明一个自己的该类的对象
void doTarget(char* cmd) { comm.scalar(&target_velocity, cmd); }
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
comm.add("T",doTarget);
}
void loop() {
// put your main code here, to run repeatedly:
comm.run("T233.2548");
Serial.println(target_velocity);
}

View File

@ -1,63 +0,0 @@
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com
e-mail : kristianl@tkjelectronics.com
*/
const uint8_t IMUAddress = 0x68; // AD0 is logic low on the PCB
const uint16_t I2C_TIMEOUT = 1000; // Used to check for errors in I2C communication
uint8_t i2cWrite(uint8_t registerAddress, uint8_t data, bool sendStop) {
return i2cWrite(registerAddress, &data, 1, sendStop); // Returns 0 on success
}
uint8_t i2cWrite(uint8_t registerAddress, uint8_t *data, uint8_t length, bool sendStop) {
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
Wire.write(data, length);
uint8_t rcode = Wire.endTransmission(sendStop); // Returns 0 on success
if (rcode) {
Serial.print(F("i2cWrite failed: "));
Serial.println(rcode);
}
return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
}
uint8_t i2cRead(uint8_t registerAddress, uint8_t *data, uint8_t nbytes) {
uint32_t timeOutTimer;
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
uint8_t rcode = Wire.endTransmission(false); // Don't release the bus
if (rcode) {
Serial.print(F("i2cRead failed: "));
Serial.println(rcode);
return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
}
Wire.requestFrom(IMUAddress, nbytes, (uint8_t)true); // Send a repeated start and then release the bus after reading
for (uint8_t i = 0; i < nbytes; i++) {
if (Wire.available())
data[i] = Wire.read();
else {
timeOutTimer = micros();
while (((micros() - timeOutTimer) < I2C_TIMEOUT) && !Wire.available());
if (Wire.available())
data[i] = Wire.read();
else {
Serial.println(F("i2cRead timeout"));
return 5; // This error value is not already taken by endTransmission
}
}
}
return 0; // Success
}

View File

@ -1,111 +0,0 @@
#include <Wire.h>
#include <Kalman.h> // Source: https://github.com/TKJElectronics/KalmanFilter
#define gyroZ_OFF -0.72
Kalman kalmanZ;
/* ----IMU Data---- */
double accX, accY, accZ;
double gyroX, gyroY, gyroZ;
int16_t tempRaw;
double gyroZangle; // Angle calculate using the gyro only
double compAngleZ; // Calculated angle using a complementary filter
double kalAngleZ; // Calculated angle using a Kalman filter
uint32_t timer;
uint8_t i2cData[14]; // Buffer for I2C data
/* ----FOC Data---- */
// driver instance
double acc2rotation(double x, double y);
void setup() {
Serial.begin(115200);
/* ----IMU init---- */
Wire.begin(19, 18,400000);// Set I2C frequency to 400kHz
i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
i2cData[2] = 0x00; // Set Gyro Full Scale Range to ±250deg/s
i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
while (i2cWrite(0x19, i2cData, 4, false))
; // Write to all four registers at once
while (i2cWrite(0x6B, 0x01, true))
; // PLL with X axis gyroscope reference and disable sleep mode
while (i2cRead(0x75, i2cData, 1))
;
if (i2cData[0] != 0x68)
{ // Read "WHO_AM_I" register
Serial.print(F("Error reading sensor"));
while (1)
;
}
delay(100); // Wait for sensor to stabilize
/* Set kalman and gyro starting angle */
while (i2cRead(0x3B, i2cData, 6))
;
accX = (int16_t)((i2cData[0] << 8) | i2cData[1]);
accY = (int16_t)((i2cData[2] << 8) | i2cData[3]);
accZ = (int16_t)((i2cData[4] << 8) | i2cData[5]);
double pitch = acc2rotation(accX, accY);
kalmanZ.setAngle(pitch);
gyroZangle = pitch;
timer = micros();
/* ----FOC init---- */
}
void loop() {
/*----IMU loop----*/
while (i2cRead(0x3B, i2cData, 14));
accX = (int16_t)((i2cData[0] << 8) | i2cData[1]);
accY = (int16_t)((i2cData[2] << 8) | i2cData[3]);
accZ = (int16_t)((i2cData[4] << 8) | i2cData[5]);
tempRaw = (int16_t)((i2cData[6] << 8) | i2cData[7]);
gyroX = (int16_t)((i2cData[8] << 8) | i2cData[9]);
gyroY = (int16_t)((i2cData[10] << 8) | i2cData[11]);
gyroZ = (int16_t)((i2cData[12] << 8) | i2cData[13]);
double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
timer = micros();
double pitch = acc2rotation(accX, accY);
double gyroZrate = gyroZ / 131.0; // Convert to deg/s
kalAngleZ = kalmanZ.getAngle(pitch, gyroZrate + gyroZ_OFF, dt);
gyroZangle += (gyroZrate + gyroZ_OFF) * dt;
compAngleZ = 0.93 * (compAngleZ + (gyroZrate + gyroZ_OFF) * dt) + 0.07 * pitch;
// Reset the gyro angle when it has drifted too much
if (gyroZangle < -180 || gyroZangle > 180)
gyroZangle = kalAngleZ;
#if 1 // Set to 1 to activate
Serial.print(pitch); Serial.print("\t");
Serial.print(gyroZangle); Serial.print("\t");
Serial.print(compAngleZ); Serial.print("\t");
Serial.print(kalAngleZ); Serial.print("\t");
#endif
Serial.print("\r\n");
delay(2);
}
/* mpu6050加速度转换为角度
acc2rotation(ax, ay)
acc2rotation(az, ay) */
double acc2rotation(double x, double y)
{
if (y < 0)
{
return atan(x / y) / 1.570796 * 90 + 180;
}
else if (x < 0)
{
return (atan(x / y) / 1.570796 * 90 + 360);
}
else
{
return (atan(x / y) / 1.570796 * 90);
}
}

View File

@ -13,10 +13,11 @@ AS5600霍尔传感器 SDA-23 SCL-5 MPU6050六轴传感器 SDA-19 SCL-18
#include "Command.h" #include "Command.h"
#include <WiFi.h> #include <WiFi.h>
#include <AsyncUDP.h> //引用以使用异步UDP #include <AsyncUDP.h> //引用以使用异步UDP
#include <Kalman.h> // Source: https://github.com/TKJElectronics/KalmanFilter #include "Kalman.h" // Source: https://github.com/TKJElectronics/KalmanFilter
#include "EEPROM.h" #include "EEPROM.h"
Kalman kalmanZ; Kalman kalmanZ;
#define gyroZ_OFF -0.19 #define gyroZ_OFF -0.19
#define FLAG_V 0
/* ----IMU Data---- */ /* ----IMU Data---- */
double accX, accY, accZ; double accX, accY, accZ;
@ -47,7 +48,7 @@ void wifi_print(char * s,double num);
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C); MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);
TwoWire I2Ctwo = TwoWire(1); TwoWire I2Ctwo = TwoWire(1);
LowPassFilter lpf_throttle{0.00}; LowPassFilter lpf_throttle{0.00};
#define FLAG_V 0
//倒立摆参数 //倒立摆参数
float LQR_K1_1 = 4; //摇摆到平衡 float LQR_K1_1 = 4; //摇摆到平衡
float LQR_K1_2 = 1.5; // float LQR_K1_2 = 1.5; //
@ -73,6 +74,10 @@ float target_angle = 89.3;
float target_voltage = 0; float target_voltage = 0;
float swing_up_voltage = 1.8; float swing_up_voltage = 1.8;
float swing_up_angle = 18; float swing_up_angle = 18;
float v_i_1 = 20;
float v_p_1 = 0.5;
float v_i_2 = 10;
float v_p_2 = 0.2;
//命令设置 //命令设置
Command comm; Command comm;
bool Motor_enable_flag = 0; bool Motor_enable_flag = 0;
@ -97,8 +102,10 @@ void do_K21(char* cmd) { comm.scalar(&LQR_K2_1, cmd); }
void do_K22(char* cmd) { comm.scalar(&LQR_K2_2, cmd); } void do_K22(char* cmd) { comm.scalar(&LQR_K2_2, cmd); }
void do_K23(char* cmd) { comm.scalar(&LQR_K2_3, cmd); } void do_K23(char* cmd) { comm.scalar(&LQR_K2_3, cmd); }
#else #else
void do_vp(char* cmd) { comm.scalar(&motor.PID_velocity.P, cmd); EEPROM.writeFloat(12, motor.PID_velocity.P);} void do_vp1(char* cmd) { comm.scalar(&v_p_1, cmd); EEPROM.writeFloat(12, v_p_1);}
void do_vi(char* cmd) { comm.scalar(&motor.PID_velocity.I, cmd);EEPROM.writeFloat(16, motor.PID_velocity.I); } void do_vi1(char* cmd) { comm.scalar(&v_i_1, cmd);EEPROM.writeFloat(16, v_p_1); }
void do_vp2(char* cmd) { comm.scalar(&v_p_2, cmd); EEPROM.writeFloat(20, v_p_2);}
void do_vi2(char* cmd) { comm.scalar(&v_i_2, cmd);EEPROM.writeFloat(24, v_i_2); }
void do_tv(char* cmd) { comm.scalar(&target_velocity, cmd); } void do_tv(char* cmd) { comm.scalar(&target_velocity, cmd); }
void do_K31(char* cmd) { comm.scalar(&LQR_K3_1, cmd); } void do_K31(char* cmd) { comm.scalar(&LQR_K3_1, cmd); }
void do_K32(char* cmd) { comm.scalar(&LQR_K3_2, cmd); } void do_K32(char* cmd) { comm.scalar(&LQR_K3_2, cmd); }
@ -145,8 +152,10 @@ swing_up_angle = EEPROM.readFloat(8);
comm.add("K22",do_K22); comm.add("K22",do_K22);
comm.add("K23",do_K23); comm.add("K23",do_K23);
#else #else
comm.add("VP",do_vp); comm.add("VP1",do_vp1);
comm.add("VI",do_vi); comm.add("VI1",do_vi1);
comm.add("VP2",do_vp2);
comm.add("VI2",do_vi2);
comm.add("TV",do_tv); comm.add("TV",do_tv);
comm.add("K31",do_K31); comm.add("K31",do_K31);
comm.add("K32",do_K32); comm.add("K32",do_K32);
@ -154,8 +163,12 @@ swing_up_angle = EEPROM.readFloat(8);
comm.add("K41",do_K41); comm.add("K41",do_K41);
comm.add("K42",do_K42); comm.add("K42",do_K42);
comm.add("K43",do_K43); comm.add("K43",do_K43);
motor.PID_velocity.P = EEPROM.readFloat(12); v_p_1 = EEPROM.readFloat(12);
motor.PID_velocity.I = EEPROM.readFloat(16); v_i_1 = EEPROM.readFloat(16);
v_p_2 = EEPROM.readFloat(20);
v_i_2 = EEPROM.readFloat(24);
motor.PID_velocity.P = v_p_1;
motor.PID_velocity.I = v_i_1;
#endif #endif
// kalman mpu6050 init // kalman mpu6050 init
Wire.begin(19, 18,400000);// Set I2C frequency to 400kHz Wire.begin(19, 18,400000);// Set I2C frequency to 400kHz
@ -420,10 +433,14 @@ float controllerLQR(float p_angle, float p_vel, float m_vel)
#else #else
if (!stable) if (!stable)
{ {
motor.PID_velocity.P = v_p_1;
motor.PID_velocity.I = v_i_1;
u = LQR_K3_1 * p_angle + LQR_K3_2 * p_vel + LQR_K3_3 * m_vel; u = LQR_K3_1 * p_angle + LQR_K3_2 * p_vel + LQR_K3_3 * m_vel;
} }
else else
{ {
motor.PID_velocity.P = v_p_2;
motor.PID_velocity.I = v_i_2;
//u = LQR_K1 * p_angle + LQR_K2 * p_vel + LQR_K3 * m_vel; //u = LQR_K1 * p_angle + LQR_K2 * p_vel + LQR_K3 * m_vel;
u = LQR_K4_1 * p_angle + LQR_K4_2 * p_vel + LQR_K4_3 * m_vel; u = LQR_K4_1 * p_angle + LQR_K4_2 * p_vel + LQR_K4_3 * m_vel;
} }

View File

@ -1,63 +0,0 @@
/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com
e-mail : kristianl@tkjelectronics.com
*/
const uint8_t IMUAddress = 0x68; // AD0 is logic low on the PCB
const uint16_t I2C_TIMEOUT = 1000; // Used to check for errors in I2C communication
uint8_t i2cWrite(uint8_t registerAddress, uint8_t data, bool sendStop) {
return i2cWrite(registerAddress, &data, 1, sendStop); // Returns 0 on success
}
uint8_t i2cWrite(uint8_t registerAddress, uint8_t *data, uint8_t length, bool sendStop) {
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
Wire.write(data, length);
uint8_t rcode = Wire.endTransmission(sendStop); // Returns 0 on success
if (rcode) {
Serial.print(F("i2cWrite failed: "));
Serial.println(rcode);
}
return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
}
uint8_t i2cRead(uint8_t registerAddress, uint8_t *data, uint8_t nbytes) {
uint32_t timeOutTimer;
Wire.beginTransmission(IMUAddress);
Wire.write(registerAddress);
uint8_t rcode = Wire.endTransmission(false); // Don't release the bus
if (rcode) {
Serial.print(F("i2cRead failed: "));
Serial.println(rcode);
return rcode; // See: http://arduino.cc/en/Reference/WireEndTransmission
}
Wire.requestFrom(IMUAddress, nbytes, (uint8_t)true); // Send a repeated start and then release the bus after reading
for (uint8_t i = 0; i < nbytes; i++) {
if (Wire.available())
data[i] = Wire.read();
else {
timeOutTimer = micros();
while (((micros() - timeOutTimer) < I2C_TIMEOUT) && !Wire.available());
if (Wire.available())
data[i] = Wire.read();
else {
Serial.println(F("i2cRead timeout"));
return 5; // This error value is not already taken by endTransmission
}
}
}
return 0; // Success
}

View File

@ -1,98 +0,0 @@
#include <Wire.h>
#include <Kalman.h> // Source: https://github.com/TKJElectronics/KalmanFilter
#define gyroZ_OFF -0.72
Kalman kalmanZ;
/* ----IMU Data---- */
double accX, accY, accZ;
double gyroX, gyroY, gyroZ;
int16_t tempRaw;
double gyroZangle; // Angle calculate using the gyro only
double compAngleZ; // Calculated angle using a complementary filter
double kalAngleZ; // Calculated angle using a Kalman filter
uint32_t timer;
uint8_t i2cData[14]; // Buffer for I2C data
/* ----FOC Data---- */
// driver instance
double acc2rotation(double x, double y);
void kalman_init(){
Wire.begin(19, 18,400000);// Set I2C frequency to 400kHz
i2cData[0] = 7; // Set the sample rate to 1000Hz - 8kHz/(7+1) = 1000Hz
i2cData[1] = 0x00; // Disable FSYNC and set 260 Hz Acc filtering, 256 Hz Gyro filtering, 8 KHz sampling
i2cData[2] = 0x00; // Set Gyro Full Scale Range to ±250deg/s
i2cData[3] = 0x00; // Set Accelerometer Full Scale Range to ±2g
while (i2cWrite(0x19, i2cData, 4, false))
; // Write to all four registers at once
while (i2cWrite(0x6B, 0x01, true))
; // PLL with X axis gyroscope reference and disable sleep mode
while (i2cRead(0x75, i2cData, 1))
;
if (i2cData[0] != 0x68)
{ // Read "WHO_AM_I" register
Serial.print(F("Error reading sensor"));
while (1)
;
}
delay(100); // Wait for sensor to stabilize
/* Set kalman and gyro starting angle */
while (i2cRead(0x3B, i2cData, 6))
;
accX = (int16_t)((i2cData[0] << 8) | i2cData[1]);
accY = (int16_t)((i2cData[2] << 8) | i2cData[3]);
accZ = (int16_t)((i2cData[4] << 8) | i2cData[5]);
double pitch = acc2rotation(accX, accY);
kalmanZ.setAngle(pitch);
gyroZangle = pitch;
timer = micros();
}
double kalman_read(){
while (i2cRead(0x3B, i2cData, 14));
accX = (int16_t)((i2cData[0] << 8) | i2cData[1]);
accY = (int16_t)((i2cData[2] << 8) | i2cData[3]);
accZ = (int16_t)((i2cData[4] << 8) | i2cData[5]);
tempRaw = (int16_t)((i2cData[6] << 8) | i2cData[7]);
gyroX = (int16_t)((i2cData[8] << 8) | i2cData[9]);
gyroY = (int16_t)((i2cData[10] << 8) | i2cData[11]);
gyroZ = (int16_t)((i2cData[12] << 8) | i2cData[13]);
double dt = (double)(micros() - timer) / 1000000; // Calculate delta time
timer = micros();
double pitch = acc2rotation(accX, accY);
double gyroZrate = gyroZ / 131.0; // Convert to deg/s
kalAngleZ = kalmanZ.getAngle(pitch, gyroZrate + gyroZ_OFF, dt);
gyroZangle += (gyroZrate + gyroZ_OFF) * dt;
compAngleZ = 0.93 * (compAngleZ + (gyroZrate + gyroZ_OFF) * dt) + 0.07 * pitch;
// Reset the gyro angle when it has drifted too much
if (gyroZangle < -180 || gyroZangle > 180)
gyroZangle = kalAngleZ;
return kalAngleZ;
}
/* mpu6050加速度转换为角度
acc2rotation(ax, ay)
acc2rotation(az, ay) */
double acc2rotation(double x, double y)
{
if (y < 0)
{
return atan(x / y) / 1.570796 * 90 + 180;
}
else if (x < 0)
{
return (atan(x / y) / 1.570796 * 90 + 360);
}
else
{
return (atan(x / y) / 1.570796 * 90);
}
}

View File

@ -1,68 +0,0 @@
#include <WiFi.h>
#include <AsyncUDP.h> //引用以使用异步UDP
#include<Wire.h>
const char *ssid = "esp32";
const char *password = "12345678";
double kalZ;
AsyncUDP udp; //创建UDP对象
unsigned int localUdpPort = 2333; //本地端口号
unsigned int broadcastPort = localUdpPort;
//const char *broadcastData = "broadcast data";
const uint8_t broadcastData[] = {"broadcast data"};
void onPacketCallBack(AsyncUDPPacket packet)
{
Serial.print("UDP数据包来源类型: ");
Serial.println(packet.isBroadcast() ? "广播数据" : (packet.isMulticast() ? "组播" : "单播"));
Serial.print("远端地址及端口号: ");
Serial.print(packet.remoteIP());
Serial.print(":");
Serial.println(packet.remotePort());
Serial.print("目标地址及端口号: ");
Serial.print(packet.localIP());
Serial.print(":");
Serial.println(packet.localPort());
Serial.print("数据长度: ");
Serial.println(packet.length());
Serial.print("数据内容: ");
Serial.write(packet.data(), packet.length());
Serial.println();
// packet.print("reply data");
// broadcastPort = packet.remotePort();
}
void setup()
{
Serial.begin(115200);
// kalman mpu6050 init
kalman_init();
//wifi初始化
WiFi.mode(WIFI_AP);
while(!WiFi.softAP(ssid, password)){}; //启动AP
Serial.println("AP启动成功");
while (!udp.listen(localUdpPort)) //等待udp监听设置成功
{
}
udp.onPacket(onPacketCallBack); //注册收到数据包事件
}
char s[255];
void loop()
{
kalZ = kalman_read();
Serial.print(kalZ);Serial.print("\t");
sprintf(s, "%.2f", kalZ); //将100转为16进制表示的字符串
// Serial.println(strlen(s));
delay(10);
// udp.broadcastTo(broadcastData, broadcastPort); //可以使用该方法广播信息
IPAddress broadcastAddr((~(uint32_t)WiFi.subnetMask())|((uint32_t)WiFi.localIP())); //计算广播地址
udp.writeTo((const unsigned char*)s, strlen(s), broadcastAddr, localUdpPort); //广播数据
}

View File

@ -1,92 +0,0 @@
#include <WiFi.h>
#include <AsyncUDP.h> //引用以使用异步UDP
#include<Wire.h>
const char *ssid = "esp32";
const char *password = "12345678";
const int MPU_addr=0x68; // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
AsyncUDP udp; //创建UDP对象
unsigned int localUdpPort = 2333; //本地端口号
unsigned int broadcastPort = localUdpPort;
//const char *broadcastData = "broadcast data";
const uint8_t broadcastData[] = {"broadcast data"};
void onPacketCallBack(AsyncUDPPacket packet)
{
Serial.print("UDP数据包来源类型: ");
Serial.println(packet.isBroadcast() ? "广播数据" : (packet.isMulticast() ? "组播" : "单播"));
Serial.print("远端地址及端口号: ");
Serial.print(packet.remoteIP());
Serial.print(":");
Serial.println(packet.remotePort());
Serial.print("目标地址及端口号: ");
Serial.print(packet.localIP());
Serial.print(":");
Serial.println(packet.localPort());
Serial.print("数据长度: ");
Serial.println(packet.length());
Serial.print("数据内容: ");
Serial.write(packet.data(), packet.length());
Serial.println();
// packet.print("reply data");
// broadcastPort = packet.remotePort();
}
void setup()
{
Serial.begin(115200);
// mpu6050初始化
Wire.begin(19, 18, 400000);
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
//wifi初始化
WiFi.mode(WIFI_AP);
while(!WiFi.softAP(ssid, password)){}; //启动AP
Serial.println("AP启动成功");
while (!udp.listen(localUdpPort)) //等待udp监听设置成功
{
}
udp.onPacket(onPacketCallBack); //注册收到数据包事件
}
char s[255];
void loop()
{
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr,14,true); // request a total of 14 registers
// AcX=Wire.read()<<8|Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY=Wire.read()<<8|Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
// AcZ=Wire.read()<<8|Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
// Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
// GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
// GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
// GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
// Serial.print("AcX = "); Serial.print(AcX);
Serial.print(" | AcY = "); Serial.println(AcY);
// itoa(AcY,s,10);
sprintf(s, "%d", AcY); //将100转为16进制表示的字符串
Serial.println(strlen(s));
// Serial.print(" | AcZ = "); Serial.print(AcZ);
// Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53); //equation for temperature in degrees C from datasheet
// Serial.print(" | GyX = "); Serial.print(GyX);
// Serial.print(" | GyY = "); Serial.print(GyY);
// Serial.print(" | GyZ = "); Serial.println(GyZ);
delay(10);
// udp.broadcastTo(broadcastData, broadcastPort); //可以使用该方法广播信息
IPAddress broadcastAddr((~(uint32_t)WiFi.subnetMask())|((uint32_t)WiFi.localIP())); //计算广播地址
udp.writeTo((const unsigned char*)s, strlen(s), broadcastAddr, localUdpPort); //广播数据
}

View File

@ -1,58 +0,0 @@
#include <WiFi.h>
#include <AsyncUDP.h> //引用以使用异步UDP
const char *ssid = "esp32";
const char *password = "12345678";
AsyncUDP udp; //创建UDP对象
unsigned int localUdpPort = 2333; //本地端口号
unsigned int broadcastPort = localUdpPort;
const char *broadcastData = "broadcast data";
// const uint8_t broadcastData[] = {"broadcast data"};
void onPacketCallBack(AsyncUDPPacket packet)
{
// Serial.print("UDP数据包来源类型: ");
// Serial.println(packet.isBroadcast() ? "广播数据" : (packet.isMulticast() ? "组播" : "单播"));
// Serial.print("远端地址及端口号: ");
// Serial.print(packet.remoteIP());
// Serial.print(":");
// Serial.println(packet.remotePort());
// Serial.print("目标地址及端口号: ");
// Serial.print(packet.localIP());
// Serial.print(":");
// Serial.println(packet.localPort());
// Serial.print("数据长度: ");
// Serial.println(packet.length());
Serial.print("数据内容: ");
// Serial.write(packet.data(), packet.length());
Serial.println(atoi( (char*)(packet.data())));
// packet.print("reply data");
// broadcastPort = packet.remotePort();
}
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_AP);
while(!WiFi.softAP(ssid, password)){}; //启动AP
Serial.println("AP启动成功");
while (!udp.listen(localUdpPort)) //等待udp监听设置成功
{
}
udp.onPacket(onPacketCallBack); //注册收到数据包事件
}
void loop()
{
// delay(5000);
//
// udp.broadcastTo(broadcastData, broadcastPort); //可以使用该方法广播信息
// IPAddress broadcastAddr((~(uint32_t)WiFi.subnetMask())|((uint32_t)WiFi.localIP())); //计算广播地址
// udp.writeTo(broadcastData, sizeof(broadcastData), broadcastAddr, localUdpPort); //广播数据
}

View File

@ -1,30 +0,0 @@
#include "Command.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Command::run(char* str){
for(int i=0; i < call_count; i++){
if(isSentinel(call_ids[i],str)){ // case : call_ids = "T2" str = "T215.15"
call_list[i](str+strlen(call_ids[i])); // get 15.15 input function
break;
}
}
}
void Command::add(char* id, CommandCallback onCommand){
call_list[call_count] = onCommand;
call_ids[call_count] = id;
call_count++;
}
void Command::scalar(float* value, char* user_cmd){
*value = atof(user_cmd);
}
bool Command::isSentinel(char* ch,char* str)
{
char s[strlen(ch)+1];
strncpy(s,str,strlen(ch));
s[strlen(ch)] = '\0'; //strncpy need add end '\0'
if(strcmp(ch, s) == 0)
return true;
else
return false;
}

View File

@ -1,16 +0,0 @@
// callback function pointer definiton
typedef void (* CommandCallback)(char*); //!< command callback function pointer
class Command
{
public:
void add(char* id , CommandCallback onCommand);
void run(char* str);
void scalar(float* value, char* user_cmd);
bool isSentinel(char* ch,char* str);
private:
// Subscribed command callback variables
CommandCallback call_list[20];//!< array of command callback pointers - 20 is an arbitrary number
char* call_ids[20]; //!< added callback commands
int call_count;//!< number callbacks that are subscribed
};

Binary file not shown.

View File

@ -1,31 +0,0 @@
# Project: ÏîÄ¿2
# Makefile created by Dev-C++ 5.11
CPP = g++.exe
CC = gcc.exe
WINDRES = windres.exe
OBJ = main.o Command.o
LINKOBJ = main.o Command.o
LIBS = -L"C:/Program Files (x86)/Dev-Cpp/MinGW64/lib" -L"C:/Program Files (x86)/Dev-Cpp/MinGW64/x86_64-w64-mingw32/lib" -static-libgcc
INCS = -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include"
CXXINCS = -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include" -I"C:/Program Files (x86)/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++"
BIN = ÏîÄ¿2.exe
CXXFLAGS = $(CXXINCS)
CFLAGS = $(INCS)
RM = rm.exe -f
.PHONY: all all-before all-after clean clean-custom
all: all-before $(BIN) all-after
clean: clean-custom
${RM} $(OBJ) $(BIN)
$(BIN): $(OBJ)
$(CPP) $(LINKOBJ) -o $(BIN) $(LIBS)
main.o: main.cpp
$(CPP) -c main.cpp -o main.o $(CXXFLAGS)
Command.o: Command.cpp
$(CPP) -c Command.cpp -o Command.o $(CXXFLAGS)

View File

@ -1,26 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Command.h"
Command command;
char buf[255];
void wifi_print(char * s,double num)
{
char str[255];
char n[255];
sprintf(n, "%.2f",num);
strcpy(str,s);
strcat(str, n);
strcat(buf+strlen(buf), str);
strcat(buf, ",");
}
float v = 0.1;
void re_command(float *num)
{
*num = *num + 1;
}
//void doTarget(char* cmd) { command.scalar(&v, cmd); }
main()
{
command.run();
}

View File

@ -1,32 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Command.h"
Command command;
char buf[255];
void wifi_print(char * s,double num)
{
char str[255];
char n[255];
sprintf(n, "%.2f",num);
strcpy(str,s);
strcat(str, n);
strcat(buf+strlen(buf), str);
strcat(buf, ",");
}
float v = 0;
float a = 0;
void re_command(float *num)
{
*num = *num + 1;
}
void doTarget_v(char* cmd) { command.scalar(&v, cmd); }
void doTarget_a(char* cmd) { command.scalar(&a, cmd); }
main()
{
command.add("T", doTarget_v);
command.add("A", doTarget_a);
command.run("A2233");
printf("%.2f",v);
printf("%.2f",a);
}

Binary file not shown.

View File

@ -1,8 +0,0 @@
[Editors]
Order=0
Focused=0
[Editor_0]
CursorCol=17
CursorRow=14
TopLine=8
LeftChar=1

View File

@ -1,82 +0,0 @@
[Project]
FileName=ÏîÄ¿2.dev
Name=ÏîÄ¿2
Type=1
Ver=2
ObjFiles=
Includes=
Libs=
PrivateResource=
ResourceIncludes=
MakeIncludes=
Compiler=
CppCompiler=
Linker=
IsCpp=1
Icon=
ExeOutput=
ObjectOutput=
LogOutput=
LogOutputEnabled=0
OverrideOutput=0
OverrideOutputName=
HostApplication=
UseCustomMakefile=0
CustomMakefile=
CommandLine=
Folders=
IncludeVersionInfo=0
SupportXPThemes=0
CompilerSet=0
CompilerSettings=0000000000000000000000000
UnitCount=3
[VersionInfo]
Major=1
Minor=0
Release=0
Build=0
LanguageID=1033
CharsetID=1252
CompanyName=
FileVersion=
FileDescription=Developed using the Dev-C++ IDE
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=
AutoIncBuildNr=0
SyncProduct=1
[Unit1]
FileName=main.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit2]
FileName=Command.cpp
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=
[Unit3]
FileName=Command.h
CompileCpp=1
Folder=
Compile=1
Link=1
Priority=1000
OverrideBuildCmd=0
BuildCmd=

Binary file not shown.

View File

@ -1,8 +0,0 @@
[Editors]
Order=0
Focused=1
[Editor_0]
CursorCol=2
CursorRow=20
TopLine=12
LeftChar=1

View File

@ -1,47 +0,0 @@
# -*- coding: utf-8 -*-
"""
Demonstrates basic use of LegendItem
"""
# import initExample ## Add path to library (just for examples; you do not need this)
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
win = pg.plot()
win.setWindowTitle('pyqtgraph example: BarGraphItem')
# # option1: only for .plot(), following c1,c2 for example-----------------------
# win.addLegend(frame=False, rowCount=1, colCount=2)
# bar graph
x = np.arange(10)
y = np.sin(x+2) * 3
bg1 = pg.BarGraphItem(x=x, height=y, width=0.3, brush='b', pen='w', name='bar')
win.addItem(bg1)
# curve
c1 = win.plot([np.random.randint(0,8) for i in range(10)], pen='r', symbol='t', symbolPen='r', symbolBrush='g', name='curve1')
c2 = win.plot([2,1,4,3,1,3,2,4,3,2], pen='g', fillLevel=0, fillBrush=(255,255,255,30), name='curve2')
# scatter plot
s1 = pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120), name='scatter')
spots = [{'pos': [i, np.random.randint(-3, 3)], 'data': 1} for i in range(10)]
s1.addPoints(spots)
win.addItem(s1)
# # option2: generic method------------------------------------------------
legend = pg.LegendItem((80,60), offset=(70,20))
legend.setParentItem(win.graphicsItem())
legend.addItem(bg1, 'bar')
legend.addItem(c1, 'curve1')
legend.addItem(c2, 'curve2')
legend.addItem(s1, 'scatter')
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,36 +0,0 @@
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import sys
'''pyqt5动态添加删除控件'''
class DynAddObject(QDialog):
def __init__(self, parent=None):
super(DynAddObject, self).__init__(parent)
self.widgetList = []
addButton = QPushButton(u"添加控件")
delBUtton = QPushButton(u"删除控件")
self.layout = QGridLayout()
self.layout.addWidget(addButton, 1, 0)
self.layout.addWidget(delBUtton, 2, 0)
self.setLayout(self.layout)
addButton.clicked.connect(self.add)
delBUtton.clicked.connect(self.delete)
def add(self):
btncont = self.layout.count()
widget = QPushButton(str(btncont - 1), self)
self.layout.addWidget(widget)
def delete(self):
for i in range(self.layout.count())[2:]:
self.layout.itemAt(i).widget().deleteLater()
if __name__ == "__main__":
app = QApplication(sys.argv)
form = DynAddObject()
form.show()
app.exec_()

View File

@ -1,34 +0,0 @@
import socket #引入套接字
import threading #引入并行
udp_data = None
def udp_send(udp_socket):
while True:
num1 = '192.168.4.1'
num2 = 2333
send_data = input('请输入要发送的数据:')
send_data = send_data.encode('utf-8')
udp_socket.sendto(send_data,(num1,num2)) #sendto发送数据发送地址
def udp_recv(udp_socket):
global udp_data
while True:
recv_data = udp_socket.recv(1024)
recv_data = recv_data.decode('utf-8')
udp_data = recv_data
print('收到信息为:%s'%recv_data)
def main():
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #创建套接字
ip = '192.168.4.2' #服务器ip和端口
port = 2333
udp_socket.bind(("192.168.4.2",2333)) #服务器绑定ip和端口
#发送数据
t=threading.Thread(target=udp_send,args=(udp_socket,)) # Thread函数用于并行
#接收数据
t1=threading.Thread(target=udp_recv,args=(udp_socket,))
t.start() #并行开始
t1.start()
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.