肥仔教程网

SEO 优化与 Web 开发技术学习分享平台

Ethnic costume fashion show staged in Beijing

Caption 1:Miao ethnic designer Yang Chunlin and his mother, Yang Shiying, an inheritor of Miao embroidery, pose for photosalongside modelsduring an ethnic costume fashion show in Beijing,Sept. 12, 2025. [Photo by Zhang Junmian/China.org.cn]

Upholding ideals and purpose of UN vital to realizing community with a shared future

Upholdingideals and purpose of UN vital to realizing community with a shared future: China Daily editorial - Opinion - Chinadaily.com.cn

This is an editorial from China Daily.

Beyond tourism: ROK-China visa shift sparks new dialogue

By Xiang Haoyu

Starting from Monday, the Republic of Korea (ROK) will implement a temporary visa-free policy for Chinese tour groups lasting nine months. This move is a positive response to China's unilateral visa exemption for South Korean visitors introduced last November. It is expected not only to deliver a strong boost to Seoul's economy, but also to serve as encouraging news for the development of China-ROK relations.

报告说“半月板2级损伤”,医生却说没事?

50多岁的王阿姨最近发现,上下楼梯时总感觉膝关节疼痛,过了2周时间症状也没有明显好转。身边的朋友都说,可能是膝关节半月板出了问题。

How common values of humanity address global challenges

By Zheng Haizhen

In September 2015, while attending the General Debate of the 70th Session of the United Nations General Assembly and delivering an important speech, Chinese President Xi Jinping emphasized that "peace, development, equity, justice, democracy and freedom are common values of all humanity and the lofty goals of the United Nations." Since then, President Xi has on many occasions stressed the need to carry forward the common values of humanity.

(五)如何一步一步实现两轮平衡车_两轮平衡车怎么调整平衡

通过串口调整KP,KI,KD,初步实现直立平衡

#include <Wire.h>
#include <MPU6050.h>


MPU6050 mpu;

// PID参数
float kp = 14.0;   // 比例系数
float ki = 0.6;    // 积分系数
float kd = 0.8;    // 微分系数
float ki_limit = 50.0; // 积分限幅

// 控制参数
float target_angle = -5.0;
const int motor_deadzone = 30;
const int motor_max = 255;
const float max_angle = 40.0;   // 最大允许角度
const float min_angle = -40.0;  // 最小允许角度

// PID计算变量
float error = 0, last_error = 0;
float integral = 0, derivative = 0;
float output = 0;

// 控制模式
enum ControlMode { MANUAL, AUTOMATIC };
ControlMode control_mode = AUTOMATIC;

// MPU变量
float angleY = 0;
float gyroY = 0;
unsigned long last_time = 0;

// 电机引脚定义
const int motor1_pwm = 15;
const int motor1_dir = 16;
const int motor2_pwm =17;
const int motor2_dir = 18;

// I2C引脚定义
const int i2c_sda = 41;
const int i2c_scl = 42;

bool mpu_connected = false;

// 串口参数调节相关变量
bool param_adjust_mode = false;
char adjust_param_type = ' ';
String inputString = "";

void initializeMPU6050() ;
void automaticControl();
void readMPU6050();
void stopMotors();
void setMotorSpeed(int pwm, bool forward);
void serialEvent() ;
void displayStatus();
void calculatePID();
void showHelp();


void setup() {
  Serial.begin(115200);
  delay(1000);
  
  Serial.println("PID自平衡小车控制程序");
  
  // 初始化I2C
  Wire.begin(i2c_sda, i2c_scl, 400000);
  delay(100);
  
  // 初始化MPU6050
  initializeMPU6050();
  
  // 设置电机引脚
  pinMode(motor1_pwm, OUTPUT);
  pinMode(motor1_dir, OUTPUT);
  pinMode(motor2_pwm, OUTPUT);
  pinMode(motor2_dir, OUTPUT);
  
  // 初始停止电机
  stopMotors();
  
  last_time = micros();
  
  Serial.println("\n=== PID自平衡小车控制程序 ===");
  Serial.println("角度范围限制: -40° 到 +40°");
  Serial.println("目标角度: " + String(target_angle) + "°");
  Serial.println("当前PID参数: Kp=" + String(kp, 2) + " Ki=" + String(ki, 3) + " Kd=" + String(kd, 2));
  Serial.println("控制模式:");
  Serial.println("  a - 自动平衡模式");
  Serial.println("  m - 手动控制模式");
  Serial.println("手动控制命令:");
  Serial.println("  f - 正转");
  Serial.println("  b - 反转");
  Serial.println("  s - 停止");
  Serial.println("PID参数调节:");
  Serial.println("  p - 调节Kp (或直接输入 p数值,如 p14.0)");
  Serial.println("  i - 调节Ki (或直接输入 i数值,如 i0.1)");
  Serial.println("  d - 调节Kd (或直接输入 d数值,如 d0.8)");
  Serial.println("  r - 重置PID积分项");
  Serial.println("  t - 调整目标角度 (或直接输入 t数值,如 t6.0)");
  Serial.println("  ? - 显示帮助信息");
  Serial.println("当前模式: 自动平衡");
  Serial.println("----------------------------");
}

void initializeMPU6050() {
  mpu.initialize();
  mpu_connected = mpu.testConnection();
  if (mpu_connected) {
    Serial.println("MPU6050连接成功");
    
    // 配置MPU6050
    mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
    mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
    mpu.setDLPFMode(MPU6050_DLPF_BW_10);
  } else {
    Serial.println("MPU6050连接失败");
  }
}

void loop() {
  // 读取MPU6050数据(如果连接)
  if (mpu_connected) {
    readMPU6050();
  }
  
  // 根据当前模式执行相应控制
  if (control_mode == AUTOMATIC) {
    // 自动平衡模式 - 使用PID控制
    calculatePID();
    automaticControl();
  }
  
  // 显示状态
  static unsigned long last_display = 0;
  if (millis() - last_display > 500) {
    displayStatus();
    last_display = millis();
  }
  
  serialEvent();
  delay(10);
}

void readMPU6050() {
  int16_t ax, ay, az, gx, gy, gz;
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  
  // 计算角度
  angleY = atan2(ax, az) * 180.0 / PI;
  
  // 计算陀螺仪角速度 (度/秒)
  gyroY = gy / 131.0; // ±250°/s 量程,灵敏度 131 LSB/(°/s)
  
  // 互补滤波:融合加速度计和陀螺仪数据
  unsigned long current_time = micros();
  float dt = (current_time - last_time) / 1000000.0;
  last_time = current_time;
  
  float alpha = 0.96; // 互补滤波系数
  static float angle_filtered = 0;
  angle_filtered = alpha * (angle_filtered + gyroY * dt) + (1 - alpha) * angleY;
  angleY = angle_filtered;
}

void calculatePID() {
  // 计算误差
  error = target_angle - angleY;
  
  // 积分项 (防饱和)
  integral += error;
  if (integral > ki_limit) integral = ki_limit;
  if (integral < -ki_limit) integral = -ki_limit;
  
  // 微分项 (使用陀螺仪数据作为微分项)
  derivative = -gyroY; // 负号是因为方向
  
  // PID输出
  output = kp * error + ki * integral + kd * derivative;
  
  // 限制输出范围
  output = constrain(output, -motor_max, motor_max);
}

void automaticControl() {
  // 检查角度是否超出安全范围
  if (angleY > max_angle || angleY < min_angle) {
    // 角度超出安全范围,停止电机
    stopMotors();
    integral = 0; // 重置积分项
    return;
  }
  
  // 使用PID输出控制电机
  int motor_pwm = abs(output);
  
  // 处理死区
  if (motor_pwm < motor_deadzone) {
    motor_pwm = 0;
  }
  
  // 根据PID输出方向控制电机
  if (output > 0) {
    setMotorSpeed(motor_pwm, true); // 正转
  } else if (output < 0) {
    setMotorSpeed(motor_pwm, false); // 反转
  } else {
    stopMotors(); // 停止
  }
}

void stopMotors() {
  // 设置PWM为0
  analogWrite(motor1_pwm, 0);
  analogWrite(motor2_pwm, 0);
  
  // 设置方向引脚为确定状态
  digitalWrite(motor1_dir, LOW);
  digitalWrite(motor2_dir, LOW);
}

void setMotorSpeed(int pwm, bool forward) {
  // 如果PWM为0,停止电机
  if (pwm == 0) {
    stopMotors();
    return;
  }
  
  // 处理死区
  if (pwm < motor_deadzone) {
    stopMotors();
    return;
  }
  
  // 限制PWM值
  pwm = constrain(pwm, motor_deadzone, motor_max);
  
  // 控制电机
  if (forward) {
    digitalWrite(motor1_dir, HIGH);
    digitalWrite(motor2_dir, HIGH);
  } else {
    digitalWrite(motor1_dir, LOW);
    digitalWrite(motor2_dir, LOW);
  }
  
  analogWrite(motor1_pwm, pwm);
  analogWrite(motor2_pwm, pwm);
}

void displayStatus() {
  Serial.print("角度: ");
  Serial.print(angleY, 1);
  Serial.print("° | 目标: ");
  Serial.print(target_angle, 1);
  Serial.print("° | 误差: ");
  Serial.print(error, 2);
  Serial.print(" | PID输出: ");
  Serial.print(output, 2);
  
  Serial.print(" | 积分: ");
  Serial.print(integral, 2);
  Serial.print(" | 微分: ");
  Serial.print(derivative, 2);
  
  Serial.print(" | PID参数: Kp=");
  Serial.print(kp, 2);
  Serial.print(" Ki=");
  Serial.print(ki, 3);
  Serial.print(" Kd=");
  Serial.print(kd, 2);
  
  Serial.print(" | 模式: ");
  Serial.print(control_mode == AUTOMATIC ? "自动" : "手动");
  
  // 显示角度状态
  if (angleY > max_angle || angleY < min_angle) {
    Serial.print(" | 状态: 超出安全范围!");
  } else if (output > 0) {
    Serial.print(" | 状态: 正转");
  } else if (output < 0) {
    Serial.print(" | 状态: 反转");
  } else {
    Serial.print(" | 状态: 平衡");
  }
  
  Serial.println();
}

// 处理直接参数设置命令 (如 "p14.0")
bool processDirectCommand(String command) {
  if (command.length() < 2) return false;
  
  char param_type = command.charAt(0);
  String value_str = command.substring(1);
  float new_value = value_str.toFloat();
  
  switch(param_type) {
    case 'p': // kp
      kp = new_value;
      Serial.print("Kp设置为: ");
      break;
    case 'i': // ki  
      ki = new_value;
      Serial.print("Ki设置为: ");
      break;
    case 'd': // kd
      kd = new_value;
      Serial.print("Kd设置为: ");
      break;
    case 't': // 目标角度
      target_angle = new_value;
      Serial.print("目标角度设置为: ");
      break;
    default:
      return false; // 未知命令
  }
  
  Serial.println(new_value, 4);
  displayStatus();
  return true;
}

void serialEvent() {
  while (Serial.available()) {
    char cmd = Serial.read();
    
    if (param_adjust_mode) {
      // 参数调节模式 (两步输入)
      if (cmd == '\n') {
        float new_value = inputString.toFloat();
        
        switch(adjust_param_type) {
          case 'p': // kp
            kp = new_value;
            Serial.print("Kp设置为: ");
            break;
          case 'i': // ki  
            ki = new_value;
            Serial.print("Ki设置为: ");
            break;
          case 'd': // kd
            kd = new_value;
            Serial.print("Kd设置为: ");
            break;
          case 't': // 目标角度
            target_angle = new_value;
            Serial.print("目标角度设置为: ");
            break;
        }
        
        Serial.println(new_value, 4);
        displayStatus();
        
        // 退出参数调节模式
        param_adjust_mode = false;
        adjust_param_type = ' ';
        inputString = "";
        
        Serial.println("输入指令继续调节 (p:Kp, i:Ki, d:Kd, t:目标角度, ?:帮助)");
      } else {
        inputString += cmd;
      }
    } else {
      // 命令模式
      switch(cmd) {
        case 'm': // 切换到手动模式
          control_mode = MANUAL;
          stopMotors();
          integral = 0; // 重置积分项
          Serial.println("切换到手动控制模式");
          break;
          
        case 'a': // 切换到自动模式
          control_mode = AUTOMATIC;
          integral = 0; // 重置积分项
          Serial.println("切换到自动平衡模式");
          break;
          
        case 'f': // 正转 (仅在手动模式下有效)
          if (control_mode == MANUAL) {
            setMotorSpeed(100, true);
            Serial.println("正转");
          } else {
            Serial.println("请在手动模式下使用此命令");
          }
          break;
          
        case 'b': // 反转 (仅在手动模式下有效)
          if (control_mode == MANUAL) {
            setMotorSpeed(100, false);
            Serial.println("反转");
          } else {
            Serial.println("请在手动模式下使用此命令");
          }
          break;
          
        case 's': // 停止 (在两种模式下都有效)
          stopMotors();
          integral = 0; // 重置积分项
          Serial.println("停止");
          break;
          
        case 'p': // 调节Kp (两步输入)
          adjust_param_type = 'p';
          param_adjust_mode = true;
          inputString = "";
          Serial.println("请输入Kp值 (当前值: " + String(kp, 2) + "): ");
          break;
          
        case 'i': // 调节Ki (两步输入)
          adjust_param_type = 'i';
          param_adjust_mode = true;
          inputString = "";
          Serial.println("请输入Ki值 (当前值: " + String(ki, 3) + "): ");
          break;
          
        case 'd': // 调节Kd (两步输入)
          adjust_param_type = 'd';
          param_adjust_mode = true;
          inputString = "";
          Serial.println("请输入Kd值 (当前值: " + String(kd, 2) + "): ");
          break;
          
        case 't': // 调节目标角度 (两步输入)
          adjust_param_type = 't';
          param_adjust_mode = true;
          inputString = "";
          Serial.println("请输入目标角度值 (当前值: " + String(target_angle, 1) + "): ");
          break;
          
        case 'r': // 重置积分项
          integral = 0;
          Serial.println("PID积分项已重置");
          break;
          
        case '?': // 显示帮助
          showHelp();
          break;
          
        case '\n': // 忽略换行符
          break;
          
        default:
          // 尝试处理直接命令 (如 "p14.0")
          String fullCommand = String(cmd);
          while (Serial.available()) {
            char nextChar = Serial.read();
            if (nextChar == '\n') break;
            fullCommand += nextChar;
          }
          
          if (!processDirectCommand(fullCommand)) {
            Serial.println("未知命令,输入 ? 查看帮助");
          }
          break;
      }
    }
  }
}

void showHelp() {
  Serial.println("\n=== 自平衡小车控制命令 ===");
  Serial.println("模式控制:");
  Serial.println("  a - 切换到自动平衡模式");
  Serial.println("  m - 切换到手动控制模式");
  Serial.println("手动控制:");
  Serial.println("  f - 正转");
  Serial.println("  b - 反转");
  Serial.println("  s - 停止");
  Serial.println("PID参数调节 (两种方式):");
  Serial.println("  方式1 - 分两步输入:");
  Serial.println("    p - 调节比例系数 Kp");
  Serial.println("    i - 调节积分系数 Ki");
  Serial.println("    d - 调节微分系数 Kd");
  Serial.println("    t - 调整目标角度");
  Serial.println("  方式2 - 直接输入:");
  Serial.println("    p14.0 - 设置Kp为14.0");
  Serial.println("    i0.1 - 设置Ki为0.1");
  Serial.println("    d0.8 - 设置Kd为0.8");
  Serial.println("    t6.0 - 设置目标角度为6.0°");
  Serial.println("其他:");
  Serial.println("  r - 重置PID积分项");
  Serial.println("  ? - 显示此帮助信息");
  Serial.println("----------------------------");
  Serial.println("当前PID参数: Kp=" + String(kp, 2) + " Ki=" + String(ki, 3) + " Kd=" + String(kd, 2));
  Serial.println("目标角度: " + String(target_angle) + "°");
  Serial.println("----------------------------");
}

Scratch编程学算法之模拟数字生命——律动的“小水母”

Scratch编程学算法之模拟数字生命——律动的“小水母”

来自网 上的一位大咖,他在账号上发布了很多生成数字生命的有趣的公式表达式,利用这些公式,能够画出很多有趣的“生物”图像,网上也有一些用MATLAB和python编程实现的,我们可以用Scratch编程来实现吗?

那我们就以下面这个基本上可以看作是海洋生物的小水母为例子,来探索一下吧。

(四)如何一步一步实现两轮平衡车_两轮平衡车怎么操作

验证出平衡小车直立时,angleY为5左右,轮子死区PWM35左右。编写程序,大于5,轮子正转,小于5轮子反转,轮子起始PWM为40。

#include <Arduino.h>
#include <Wire.h>
#include <MPU6050.h>

MPU6050 mpu;

// 控制参数
float target_angle = 5.0; // 目标平衡角度
int base_pwm = 40;        // 起始PWM值
const int motor_deadzone = 35; // 轮子死区

// MPU变量
float angleY = 0;

// 电机引脚定义
const int motor1_pwm = 15;
const int motor1_dir = 16;
const int motor2_pwm = 17;
const int motor2_dir = 18;

// I2C引脚定义
const int i2c_sda = 41;
const int i2c_scl = 42;

// MPU6050连接状态
bool mpu_connected = false;

void initializeMPU6050();
void readMPU6050();
void simpleControl();
void setMotorSpeed(int pwm, bool forward);
void displayStatus();
void serialEvent();

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  Serial.println("自平衡小车简单控制程序启动中...");
  
  // 初始化I2C总线
  Wire.begin(i2c_sda, i2c_scl, 400000);
  delay(100);
  
  // 初始化MPU6050
  initializeMPU6050();
  
  // 设置电机引脚
  pinMode(motor1_pwm, OUTPUT);
  pinMode(motor1_dir, OUTPUT);
  pinMode(motor2_pwm, OUTPUT);
  pinMode(motor2_dir, OUTPUT);
  
  // 停止电机
  digitalWrite(motor1_pwm, LOW);
  digitalWrite(motor2_pwm, LOW);
  
  Serial.println("\n=== 自平衡小车简单控制程序 ===");
  Serial.println("控制策略: angleY > 5 → 正转, angleY < 5 → 反转");
  Serial.println("起始PWM: " + String(base_pwm));
  Serial.println("轮子死区: " + String(motor_deadzone));
  Serial.println("----------------------------");
}

void initializeMPU6050() {
  Serial.println("正在初始化MPU6050...");
  
  mpu.initialize();
  delay(100);
  
  mpu_connected = mpu.testConnection();
  
  if (mpu_connected) {
    Serial.println("MPU6050连接成功!");
    
    // 设置MPU6050配置
    mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
    mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
    mpu.setDLPFMode(MPU6050_DLPF_BW_10);
    
    Serial.println("MPU6050配置完成");
  } else {
    Serial.println("MPU6050连接失败! 请检查连接:");
    Serial.println("SDA:41, SCL:42, 电源:3.3V");
  }
}

void loop() {
  // 读取MPU6050数据
  if (mpu_connected) {
    readMPU6050();
    
    // 简单控制逻辑
    simpleControl();
    
    // 显示状态
    static unsigned long last_display = 0;
    if (millis() - last_display > 500) {
      displayStatus();
      last_display = millis();
    }
  } else {
    // 如果MPU未连接,尝试重新初始化
    static unsigned long last_reconnect = 0;
    if (millis() - last_reconnect > 2000) {
      initializeMPU6050();
      last_reconnect = millis();
    }
  }
  
  // 处理串口输入
  serialEvent();
  
  delay(10); // 控制循环频率
}

void readMPU6050() {
  // 读取加速度和陀螺仪数据
  int16_t ax, ay, az, gx, gy, gz;
  
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  
  // 计算角度
  angleY = atan2(ax, az) * 180.0 / PI;
}

void simpleControl() {
  int motor_pwm = 0;
  
  // 简单控制逻辑
  if (angleY > target_angle) {
    // 角度大于5度,轮子正转
    motor_pwm = base_pwm;
    setMotorSpeed(motor_pwm, true); // 正转
  } else if (angleY < target_angle) {
    // 角度小于5度,轮子反转
    motor_pwm = base_pwm;
    setMotorSpeed(motor_pwm, false); // 反转
  } else {
    // 角度等于5度,停止
    setMotorSpeed(0, true);
  }
}

void setMotorSpeed(int pwm, bool forward) {
  // 处理死区
  if (abs(pwm) < motor_deadzone) {
    pwm = 0;
  }
  
  // 限制PWM值
  pwm = constrain(pwm, 0, 255);
  
  // 控制电机
  if (pwm > 0) {
    if (forward) {
      // 正转
      digitalWrite(motor1_dir, HIGH);
      digitalWrite(motor2_dir, HIGH);
    } else {
      // 反转
      digitalWrite(motor1_dir, LOW);
      digitalWrite(motor2_dir, LOW);
    }
    analogWrite(motor1_pwm, pwm);
    analogWrite(motor2_pwm, pwm);
  } else {
    // 停止
    analogWrite(motor1_pwm, 0);
    analogWrite(motor2_pwm, 0);
  }
}

void displayStatus() {
  Serial.print("角度: ");
  Serial.print(angleY, 2);
  Serial.print("° | 目标: ");
  Serial.print(target_angle, 1);
  Serial.print("° | 动作: ");
  
  if (angleY > target_angle) {
    Serial.print("正转");
  } else if (angleY < target_angle) {
    Serial.print("反转");
  } else {
    Serial.print("停止");
  }
  
  Serial.print(" | PWM: ");
  Serial.println(base_pwm);
}

void serialEvent(){
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    
    switch(inChar) {
      case '+': // 增加PWM
        base_pwm += 5;
        if (base_pwm > 255) base_pwm = 255;
        Serial.println("PWM增加到: " + String(base_pwm));
        break;
        
      case '-': // 减少PWM
        base_pwm -= 5;
        if (base_pwm < motor_deadzone) base_pwm = motor_deadzone;
        Serial.println("PWM减少到: " + String(base_pwm));
        break;
        
      case 's': // 显示状态
        displayStatus();
        break;
        
      case ' ': // 停止电机
        setMotorSpeed(0, true);
        Serial.println("电机已停止");
        break;
        
      case 't': // 调整目标角度
        target_angle += 1.0;
        if (target_angle > 15.0) target_angle = 0.0;
        Serial.println("目标角度设置为: " + String(target_angle, 1) + "°");
        break;
        
      case '\n':
        break;
        
      default:
        Serial.println("可用命令:");
        Serial.println("+ : 增加PWM");
        Serial.println("- : 减少PWM");
        Serial.println("s : 显示状态");
        Serial.println("空格 : 停止电机");
        Serial.println("t : 切换目标角度");
        break;
    }
  }
}

一键解锁坐标点的隐藏角度——【Excel神技】ATAN2公式

公式简介

ATAN2公式 返回给定的 X 轴坐标值和 Y 轴坐标值所对应的反正切值。反正切值就是从 X 轴到经过原点(也就是 0,0)以及坐标点(x_num,y_num)的那条直线之间形成的夹角。这个角度用弧度来表示,弧度的值是在-π到π之间(不包含-π,也就是-π<弧度值<=π)。

Sqlite - 数学函数 - ATAN_sqlite数据库下载

在 SQLite 中,atan 函数用于计算一个数的反正切值,它是正切函数的反函数。反正切函数返回的是一个角度,该角度的正切值等于输入的数值,角度以弧度为单位。以下是关于 atan 函数的详细介绍及示例。

基本语法

<< < 10 11 12 13 14 15 16 17 18 19 > >>
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言