麦克不能飞 发布的文章

开源硬件Arduino社群各种创意应用层出不穷,学习门槛越来越地,今天我们就尝试从小白视角,不求甚解动手构建一个自平衡机器人

请输入图片描述

材料收集

开源社区这类资料特别丰富,可以网络搜索从扫盲到实际动手操作都有很详细的讯息
从小白的角度,我们透过各种搜索,阅读,首先需要知道做这样的一个自平衡机器人,需要哪些材料? =》这样好从淘宝购买一些物美价廉的东西:)

经过若干次网络搜索,阅读,基本了解构建这样的一个自平衡机器人需要的材料如下:

  • Arduino控制板这里选用Uno or Nano,代码一模一样,唯一差异就是pin接口,以及连线;淘宝选国产的,不贵,才十几块
  • 陀螺仪MPU6050,物美价廉,淘宝才几块钱
  • 双轴减速电机,淘宝上一大堆,黄色的那个电机,我选择1:48减速比的电机
  • 轮子,一般和上面的双轴电机配套一起买,一两块钱
  • L298N电机驱动模块,可以驱动2个直流电机,同时可以输出5V电压给Arduino控制板供电;这样使用一路供电(比如2节或者3节16850电池),给电机与控制板同时供电
  • 16850锂电池(2节,3节,甚至4节也可以)
  • 16850电池盒(最好买带开关的盒子)
  • 框架材料,我通常选择雪弗板(一般购买20cm*20cm大小,厚度>5mm,一般选8mm或者10mm)便于美工到切割
  • 热熔胶
  • 电烙铁可能需要(焊接)
  • 美工刀

需要的软件

陀螺仪MPU6050
mpu6050
带有3轴加速度计以及三轴陀螺仪的模块,以及能够处理复杂的运动融合算法的数字运动处理器(DMP)

L298N电机驱动模块
电机驱动模块

双轴减速电机
电机

16850锂电池
电池

这个Arduino机器人DIY经常使用的电池,价格不贵,容量一般2000多毫安, 使用该电池可以非常方便满足3.7伏要求,一般我使用2节
可以考虑购买一个带开关的电池盒,方便重复使用,免得焊线

制作框架
对于这个项目,我使用雪弗板切割而成,加工起来也比较方便,强度对于这小车来说基本足够
车身
我看到网上很多教程建议将电池,电机等重物保持再底部,有一定道理,不过我把电池(最终的部分)放置在最上面,依然保持平衡很好(关键是控制参数调试的恰当)

电路图

截图 2023-12-15 16-35-00.png

该机器人的接线很简单, mpu6050测量运动方向上的倾斜,Arduino使用L298N电机驱动设置电机速度以克服倾斜

这里我们使用的mpu6050传感器,经济可靠。 其精度以及同时测量所有三轴旋转和运动的能力时其非常适合用于自平衡机器人。 L298N也是一款非常适合在此使用的低成本双H桥电机驱动器

MPU6050我放置在顶部,其Z轴朝向地球,当然我们可以将该传感器放置在框架中的任何位置。方向可以根据框架和设计进行修改。为此需要更改代码

由于我们使用L298N作为电机驱动器,同时也作为Arduino的电源,所以放置电源跳线,这样我们就可以使用+5v引脚作为电源。

Arduino代码

需要安装依赖的库

  • PID_v1
  • LMotorController
  • I2Cdev
  • MPU6050

完整代码:

/*
* Arduino Uno
* L298N
* HC-06 BT module
* MPU6050
* by 麦克不能飞, admin@dazuicheng.com
* www.dazuicheng.com
* 1. 如何可视化PID tuning??
* 2.勉强可以工作,但是抗扰动能力稍微有点弱?
*/
#include <PID_v1.h>
#include <LMotorController.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include <SoftwareSerial.h>


SoftwareSerial bluetoothSerial(4, 3); // RX, TX for bluetooth module control(HC-06 here)4和3分别表示蓝牙模块的rx,tx

#define MIN_ABS_SPEED 20

MPU6050 mpu;

// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q; // [w, x, y, z] quaternion container
VectorFloat gravity; // [x, y, z] gravity vector
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector

//PID,
double originalSetpoint = 179.60;
double setpoint = originalSetpoint;
double movingAngleOffset = 0.1;
double input, output;

//my setting, cost a lot of time to fine tuning those parameters,adjust to fit your own design
double Kp = 39; //45; 地毯上可以工作,地板上有点jitter   
double Kd = 1.6; //1.8
double Ki = 400; //400;这貌似可以不用这么大,还可以优化?
PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);

double motorSpeedFactorLeft = 0.8;
double motorSpeedFactorRight = 0.7;
//MOTOR CONTROLLER,因为可能电机+-接线弄反了,所这里IN1,IN2定义与实际接线故意反了(实际是IN1->6, IN2->7)
int ENA = 5;
int IN1 = 7;
int IN2 = 6;
int IN3 = 9;
int IN4 = 8;
int ENB = 10;
LMotorController motorController(ENA, IN1, IN2, ENB, IN3, IN4, motorSpeedFactorLeft, motorSpeedFactorRight);

volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady()
{
  mpuInterrupt = true;
}


void setup()
{
  Serial.begin(115200);
  bluetoothSerial.begin(9600);

  // join I2C bus (I2Cdev library doesn't do this automatically)
  #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
  #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
  #endif

  mpu.initialize();

  devStatus = mpu.dmpInitialize();
  // 这部分偏移,只需要校正一次,使用mpu6050库里面的例子:IMU_Zero校正一下,记录这些偏移,靠不到这里
  mpu.setXAccelOffset(314);//-4259
  mpu.setYAccelOffset(1177);//749
  mpu.setZAccelOffset(931); // 1151,1688 factory default for my test chip
  mpu.setXGyroOffset(-12);//31
  mpu.setYGyroOffset(184);//557
  mpu.setZGyroOffset(-1);//34

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
    // turn on the DMP, now that it's ready
    mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    attachInterrupt(0, dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();

    //setup PID
    pid.SetMode(AUTOMATIC);
    pid.SetSampleTime(10);
    pid.SetOutputLimits(-255, 255); 
  } else {
    // ERROR!
    // 1 = initial memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
  }
}



void loop()
{
  //randomSeed(analogRead(0)); // 使用模拟引脚0的读取值作为随机数种子

  String received_from_bt = "";
  while (bluetoothSerial.available()) {
    char data = bluetoothSerial.read();
    received_from_bt += data;  
    // 在这里可以添加处理接收到的数据的代码
  }
  if (received_from_bt.length() > 0) {
    Serial.print("Received from Bluetooth: ");
    Serial.println(received_from_bt);
  }
  // if programming failed, don't try to do anything
  if (!dmpReady) return;
  //Serial.println(setpoint);

  // wait for MPU interrupt or extra packet(s) available
  while (!mpuInterrupt && fifoCount < packetSize) {
    //no mpu data - performing PID calculations and output to motors 
    pid.Compute();
    Serial.print(setpoint); Serial.print(",");Serial.print(input); Serial.print(","); Serial.println(output);
    motorController.move(output, MIN_ABS_SPEED);
}

  // reset interrupt flag and get INT_STATUS byte
  mpuInterrupt = false;
  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
    // reset so we can continue cleanly
    mpu.resetFIFO();
    Serial.println(F("FIFO overflow!"));
    // otherwise, check for DMP data ready interrupt (this should happen frequently)
  } else if (mpuIntStatus & 0x02) {
    // wait for correct available data length, should be a VERY short wait
    while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);
    // track FIFO count here in case there is > 1 packet available
    // (this lets us immediately read more without waiting for an interrupt)
    fifoCount -= packetSize;
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    input = ypr[1] * 180/M_PI + 180;
  }
}

平衡测试与调整

这种类型的自平衡机器人是倒立摆,类似于在手指上平衡一根棍子。在这里,车轮的旋转提供了防止跌落的平衡力。为了平衡这个机器人,你必须配置 PID 值,直到机器人达到稳定的位置和恢复能力。

为了使此任务变得简单,代码使用了 Arduino PID 的现成库。要根据您的设计调整 PID,您必须手动输入 P、I、D 值,并通过将代码上传到 Arduino 来检查稳定性(如上图中代码的以下部分)。

方法是先将Ki、Kd都设置为0,然后输入Kp的值,然后上传代码。经过几次尝试后,机器人在一段时间内保持平衡,但滚动时出现过度振荡,此时必须在不干扰 Kp 值的情况下设置 Kd 值。设置 Kd 以便发生最小振荡,这与其他两个一样手动调整 Ki。

此外,您还可以设置上图所示角度的设定值和偏移。但在许多情况下并不需要它,并且仅当陀螺仪有偏移时才进行设置。

经过这个反复试验阶段后,您将为您的机器人找到合适的值。这个过程有点棘手,如果您是控制系统的初学者和新手,有时需要几个小时才能找到完美的值。

请输入图片描述

前面展示了一个简单的面向对象的图书馆应用,定义了Library,Book两个类,缺少用户的管理,比如默认只有一个用户借书、还书
对于正常实际的图书馆应用比如涉及多个用户使用借书、还书的功能,这里我们就在前面程序的基础上增加用户管理的功能 - 简单的用户注册

这个程序首先新增加了一个类定义User

class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password
        self.borrowed_books = []

    def borrow_book(self, book):
        if not book.is_checked_out:
            book.check_out()
            self.borrowed_books.append(book)
            print(f"{self.username} has borrowed '{book.title}'.")
        else:
            print("Sorry, the book is already checked out.")

    def return_book(self, book):
        if book in self.borrowed_books:
            book.check_in()
            self.borrowed_books.remove(book)
            print(f"{self.username} has returned '{book.title}'.")
        else:
            print("This book is not borrowed by the user.")

User类包含用户基本信息,同时也维护所借阅book的信息

Library类内部需要增加一些数据定义,维护用户的信息

def __init__(self):
        self.books = []
        self.users = []

使用一个list保存注册到Library的用户

完整的演示

class User:
        def __init__(self, username, password):
            self.username = username
            self.password = password
            self.borrowed_books = []
    
        def borrow_book(self, book):
            if not book.is_checked_out:
                book.check_out()
                self.borrowed_books.append(book)
                print(f"{self.username} has borrowed '{book.title}'.")
            else:
                print("Sorry, the book is already checked out.")
    
        def return_book(self, book):
            if book in self.borrowed_books:
                book.check_in()
                self.borrowed_books.remove(book)
                print(f"{self.username} has returned '{book.title}'.")
            else:
                print("This book is not borrowed by the user.")
    
    class Library:
        def __init__(self):
            self.books = []
            self.users = []
    
        def add_book(self, book):
            self.books.append(book)
    
        def add_user(self, user):
            self.users.append(user)
    
        def list_books(self):
            print("Library Catalog:")
            for book in self.books:
                book.display_info()
    
        def search_book_by_name(self, book_name):
            for book in self.books:
                if book.title == book_name:
                    return book
            return None
    
        def check_book_status(self, book_name):
            book = self.search_book_by_name(book_name)
            if book:
                if book.is_checked_out:
                    print(f"The book '{book_name}' is checked out.")
                else:
                    print(f"The book '{book_name}' is available.")
            else:
                print(f"The book '{book_name}' is not found in the library.")
    
        def user_login(self, username, password):
            for user in self.users:
                if user.username == username and user.password == password:
                    return user
            return None
    
    # 创建图书馆对象
    library = Library()
    
    # 将图书加入图书馆
    library.add_book(Book("The Great Gatsby", "F. Scott Fitzgerald"))
    library.add_book(Book("To Kill a Mockingbird", "Harper Lee"))
    library.add_book(Book("1984", "George Orwell"))
    
    # 注册用户
    user1 = User("user1", "password1")
    user2 = User("user2", "password2")
    
    # 将用户加入图书馆
    library.add_user(user1)
    library.add_user(user2)
    
    # 用户登录
    logged_in_user = library.user_login("user1", "password1")
    
    if logged_in_user:
        # 用户借书
        library.check_book_status("The Great Gatsby")
        logged_in_user.borrow_book(library.search_book_by_name("The Great Gatsby"))
    
        # 用户还书
        logged_in_user.return_book(library.search_book_by_name("The Great Gatsby"))
    
        # 再次查询书籍状态
        library.check_book_status("The Great Gatsby")
    else:
        print("Login failed. Invalid username or password.")

8-gUJHttA242VwOeZ.png

复习一些基础知识

当复习基础知识时,你可以通过编写一些简单的代码来回顾基本的Python语法、变量、运算符,以及列表、字典、集合等基本数据结构的使用。以下是一些例子:

Python语法、变量、运算符复习:

# 基本语法
print("Hello, World!")

# 变量和数据类型
name = "John"
age = 25
height = 1.75

# 运算符
result = age * height
print(result)

# 条件语句
if age >= 18:
    print(f"{name} is an adult.")
else:
    print(f"{name} is a minor.")

列表(List)的使用:

创建列表

fruits = ['apple', 'banana', 'orange']

访问列表元素

print(fruits[0])  # 输出: apple

# 添加元素
fruits.append('grape')

# 修改元素
fruits[1] = 'kiwi'

# 删除元素
del fruits[2]

# 遍历列表
for fruit in fruits:
    print(fruit)

字典(Dictionary)的使用:

# 创建字典
person = {'name': 'Alice', 'age': 30, 'city': 'New York'}

# 访问字典元素
print(person['name'])  # 输出: Alice

# 添加或修改元素
person['gender'] = 'female'
person['age'] = 31

# 删除元素
del person['city']

# 遍历字典
for key, value in person.items():
    print(f"{key}: {value}")

集合(Set)的使用:

# 创建集合
colors = {'red', 'green', 'blue'}

# 添加元素
colors.add('yellow')

# 删除元素
colors.remove('green')

# 遍历集合
for color in colors:
    print(color)

深入学习函数和模块

学习如何定义函数、传递参数、返回值等。 掌握模块的概念,了解如何创建和使用模块。

当深入学习函数和模块时,你可以通过以下例子来了解如何定义函数、传递参数、返回值,以及如何创建和使用模块。

函数的定义、参数和返回值:

定义一个简单的函数

def greet(name):
    return f"Hello, {name}!"

# 调用函数
result = greet("Alice")
print(result)  # 输出: Hello, Alice!

# 函数参数可以有默认值
def power(base, exponent=2):
    return base ** exponent

# 调用带默认参数的函数
result = power(3)
print(result)  # 输出: 9

# 函数可以返回多个值
def calculate_stats(numbers):
    total = sum(numbers)
    average = total / len(numbers)
    return total, average

# 调用返回多个值的函数
total_sum, avg = calculate_stats([1, 2, 3, 4, 5])
print(f"Total Sum: {total_sum}, Average: {avg}")

模块的创建和使用:

假设你有两个文件:math_operations.py 和 main_program.py。

math_operations.py(模块文件)

# math_operations.py

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

main_program.py:

# main_program.py

# 导入自定义模块
import math_operations

# 使用模块中的函数
result_add = math_operations.add(5, 3)
result_subtract = math_operations.subtract(8, 4)

print(f"Addition Result: {result_add}")
print(f"Subtraction Result: {result_subtract}")

在这个例子中,math_operations.py 文件包含了两个简单的数学运算函数。通过在 main_program.py
中导入这个模块,你可以在主程序中使用模块中定义的函数。

通过这些例子,你可以更深入地理解如何定义函数、传递参数、返回值,以及如何创建和使用模块。这是进一步学习Python编程的基础,为后续的学习打下坚实的基础。

学习类和对象的基本概念。

熟悉继承、封装和多态的使用

类和对象的基本概念:

类的定义:

学习如何定义一个类,类是对象的模板,包含属性(成员变量)和方法(成员函数)。

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        print(f"{self.name} is barking!")

对象的创建和使用:

学习如何创建类的实例(对象)并使用它们。

my_dog = Dog("Buddy", 3)
print(f"{my_dog.name} is {my_dog.age} years old.")
my_dog.bark()

继承、封装、多态的使用:

继承:

学习如何使用继承创建子类,子类可以继承父类的属性和方法,并可以新增或覆盖它们。

class GoldenRetriever(Dog):
    def fetch(self):
        print(f"{self.name} is fetching the ball!")

my_golden = GoldenRetriever("Max", 2)
my_golden.bark()
my_golden.fetch()

封装:

理解封装的概念,即将类的实现细节隐藏起来,只向外部提供必要的接口。

class Circle:
    def __init__(self, radius):
        self.__radius = radius  # 使用双下划线进行私有属性的封装

    def get_radius(self):
        return self.__radius

    def set_radius(self, new_radius):
        if new_radius > 0:
            self.__radius = new_radius

my_circle = Circle(5)
print(f"Radius: {my_circle.get_radius()}")
my_circle.set_radius(8)
print(f"New Radius: {my_circle.get_radius()}")

多态:

了解多态的概念,即不同类的对象可以对相同的方法做出不同的响应。

class Cat:
    def make_sound(self):
        print("Meow!")

def animal_sound(animal):
    animal.make_sound()

my_dog = Dog("Buddy", 3)
my_cat = Cat()

animal_sound(my_dog)  # 输出: Buddy is barking!
animal_sound(my_cat)  # 输出: Meow!

通过理解和实践这些概念,你将更深入地了解面向对象编程在Python中的应用。这些概念是编写复杂程序和更容易维护的关键。

完整的应用展示面向对象的一些概念

当你理解了面向对象编程的基本概念后,可以尝试实现一个简单的应用来展示这些概念的完整应用。以下是一个简单的图书管理系统的示例,其中涉及类的定义、对象的创建、继承、封装和多态。

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.is_checked_out = False

    def check_out(self):
        if not self.is_checked_out:
            self.is_checked_out = True
            print(f"The book '{self.title}' by {self.author} has been checked out.")
        else:
            print("Sorry, the book is already checked out.")

    def check_in(self):
        if self.is_checked_out:
            self.is_checked_out = False
            print(f"Thank you for returning '{self.title}' by {self.author}.")
        else:
            print("The book is not checked out.")

    def display_info(self):
        status = "Available" if not self.is_checked_out else "Checked Out"
        print(f"Title: {self.title}, Author: {self.author}, Status: {status}")


class Library:
    def __init__(self):
        self.books = []

    def add_book(self, book):
        self.books.append(book)

    def list_books(self):
        print("Library Catalog:")
        for book in self.books:
            book.display_info()


# 创建图书对象
book1 = Book("The Great Gatsby", "F. Scott Fitzgerald")
book2 = Book("To Kill a Mockingbird", "Harper Lee")
book3 = Book("1984", "George Orwell")

# 创建图书馆对象
library = Library()

# 将图书加入图书馆
library.add_book(book1)
library.add_book(book2)
library.add_book(book3)

# 列出图书馆的所有图书
library.list_books()

# 借出和归还图书
book1.check_out()
book2.check_out()
book3.check_out()

# 再次列出图书馆的所有图书
library.list_books()

# 归还图书
book2.check_in()

# 最终列出图书馆的所有图书
library.list_books()

这个简单的图书管理系统包含了两个类:Book 和 Library。Book 类表示一本书,具有检出、归还和显示信息等方法。Library
类表示一个图书馆,包含了图书的集合,可以添加书籍并列出所有书籍。在这个示例中,你可以看到类的实例化、方法的调用、继承(没有显式的继承,但是通过对象的使用体现了类和对象的继承关系)、封装和多态的概念的应用。

通过这个示例,你可以更好地理解如何将面向对象编程的概念应用到实际的应用中。你还可以尝试扩展这个应用,例如添加用户类、图书馆管理员类等,以更全面地体现面向对象编程的特性。

8-BT7Tz6YA20nvp0M.png

> Bubble Sort

    #include <iostream>
    
    void swap(int &a, int &b) {
        int temp = a;
        a = b;
        b = temp;
    }
    
    void bubbleSort(int arr[], int size) {
        for (int i = 0; i < size - 1; ++i) {
            for (int j = 0; j < size - i - 1; ++j) {
                // 如果相邻元素逆序,则交换它们
                if (arr[j] > arr[j + 1]) {
                    swap(arr[j], arr[j + 1]);
                }
            }
        }
    }
    
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; ++i) {
            std::cout << arr[i] << " ";
        }
        std::cout << std::endl;
    }
    
    int main() {
        const int size = 6;
        int arr[size] = {64, 25, 12, 22, 11, 1};
    
        std::cout << "原始数组: ";
        printArray(arr, size);
    
        bubbleSort(arr, size);
    
        std::cout << "排序后数组: ";
        printArray(arr, size);
    
        return 0;
    }

## > Insert Sort

#include <iostream>

void insertionSort(int arr[], int size) {
    for (int i = 1; i < size; ++i) {
        int key = arr[i];
        int j = i - 1;

        // 将 arr[0..i-1] 中大于 key 的元素向右移动
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            --j;
        }

        // 将 key 插入到正确的位置
        arr[j + 1] = key;
    }
}

void printArray(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

int main() {
    const int size = 6;
    int arr[size] = {64, 25, 12, 22, 11, 1};

    std::cout << "原始数组: ";
    printArray(arr, size);

    insertionSort(arr, size);

    std::cout << "排序后数组: ";
    printArray(arr, size);

    return 0;
}

> Quick Sort

#include <iostream>

void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

int partition(int arr[], int low, int high) {
    // 选择最右边的元素作为基准
    int pivot = arr[high];
    int i = low - 1;  // 小于等于基准的元素的最右索引

    for (int j = low; j < high; ++j) {
        // 如果当前元素小于或等于基准,则将其交换到最左边
        if (arr[j] <= pivot) {
            ++i;
            swap(arr[i], arr[j]);
        }
    }

    // 将基准元素放到正确的位置
    swap(arr[i + 1], arr[high]);
    return i + 1;  // 返回基准元素的索引
}

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        // 对数组进行分割,获取基准元素的索引
        int pivotIndex = partition(arr, low, high);

        // 递归地对左右子数组进行排序
        quickSort(arr, low, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, high);
    }
}

void printArray(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

int main() {
    const int size = 6;
    int arr[size] = {64, 25, 12, 22, 11, 1};

    std::cout << "原始数组: ";
    printArray(arr, size);

    quickSort(arr, 0, size - 1);

    std::cout << "排序后数组: ";
    printArray(arr, size);

    return 0;
}

8-0u1CMzUE8CXOTIK.png

> 逆序输出:

编写一个程序,要求用户输入一个整数,然后将该整数的各位数字逆序输出
 #include <iostream>
 
 int main() {
     int number;
     
     // 用户输入一个整数
     std::cout << "输入一个整数: ";
     std::cin >> number;
 
     // 逆序输出整数的各位数字
     while (number > 0) {
         int digit = number % 10;
         std::cout << digit;
         number /= 10;
     }
 
     std::cout << std::endl;
 
     return 0;
 }

> 字符串反转:

编写一个程序,用户输入一个字符串,程序将字符串反转后输出
    #include <iostream>
    #include <string>
    
    int main() {
        std::string input;
    
        // 用户输入一个字符串
        std::cout << "输入一个字符串: ";
        std::cin >> input;
    
        // 反转字符串
        std::string reversed = "";
        for (int i = input.length() - 1; i >= 0; --i) {
            reversed += input[i];
        }
    
        // 输出反转后的字符串
        std::cout << "反转后的字符串: " << reversed << std::endl;
    
        return 0;
    }

> 找元素:

编写一个程序,用户输入一个整数和一个数组,程序判断该整数是否在数组中,如果在则输出索引位置,否则输出"未找到"。
#include <iostream>
#include <vector>

int main() {
    int target;
    std::vector<int> numbers = {1, 3, 5, 7, 9};

    // 用户输入一个整数
    std::cout << "输入一个整数: ";
    std::cin >> target;

    // 查找元素并输出结果
    bool found = false;
    for (int i = 0; i < numbers.size(); ++i) {
        if (numbers[i] == target) {
            std::cout << "元素 " << target << " 在数组中的索引位置是 " << i << std::endl;
            found = true;
            break;
        }
    }

    if (!found) {
        std::cout << "未找到元素 " << target << std::endl;
    }

    return 0;
}