很高兴能为您详细解析基于ESP32S3的自适应轮腿机器人的软件架构设计,并提供相应的C代码实现方案。
关注微信公众号,提前获取相关推文

项目背景与需求分析
首先,我们来回顾一下这个项目的核心需求:
- 硬件平台: ESP32S3
- 机器人类型: 自适应轮腿机器人,结合轮式移动和腿部越障能力。
- 足部驱动: 4010无刷电机直驱轮子,实现高效的轮式移动。
- 腿部控制: 舵机控制腿部关节,实现灵活的姿态调整和越障。
- 音频输入: 板载麦克风,为后续的离线语音控制做准备。
- 音频输出: 板载功放模块,配合麦克风实现语音交互。
- 离线语音控制 (未来): ESP32S3本地语音识别和控制。
- PCB设计: 立创EDA专业版。
- 软件目标: 构建可靠、高效、可扩展的嵌入式系统平台,涵盖完整的开发流程。
基于以上需求,我们可以提炼出几个关键的软件设计要点:
- 实时性: 机器人控制需要实时响应,特别是电机和舵机的精确控制,以及未来的语音控制。
- 模块化: 系统功能复杂,需要模块化设计,方便开发、维护和扩展。
- 可扩展性: 预留接口和架构,方便后续添加新的传感器、功能模块,特别是语音控制功能。
- 资源优化: ESP32S3资源有限,需要优化代码,提高效率,减少资源占用。
- 可靠性: 确保系统稳定运行,处理异常情况,提高鲁棒性。
代码设计架构:分层架构与模块化设计
针对嵌入式系统的特点和项目的需求,我推荐采用分层架构结合模块化设计。这种架构具有清晰的层次结构,模块之间职责明确,易于理解、开发和维护。
分层架构:
我们将系统软件划分为以下几个层次:
- 硬件抽象层 (HAL - Hardware Abstraction Layer): 直接与硬件交互,封装底层硬件操作,向上层提供统一的硬件接口。例如,GPIO、PWM、定时器、串口、SPI、I2C、ADC、I2S等硬件的驱动接口。
- 驱动层 (Driver Layer): 基于HAL层,实现特定硬件设备的功能驱动,例如电机驱动、舵机驱动、麦克风驱动、功放驱动等。这一层负责设备的初始化、配置、数据收发等操作。
- 控制层 (Control Layer): 实现机器人的核心控制算法,包括电机控制、舵机控制、运动控制、姿态控制、路径规划等。这一层是机器人的“大脑”,负责决策和执行。
- 应用层 (Application Layer): 构建在控制层之上,实现用户交互和高层应用逻辑,例如命令解析、模式切换、状态监控、语音控制 (未来) 等。
模块化设计:
在每个层次内部,我们都采用模块化设计,将功能分解为独立的模块,模块之间通过定义良好的接口进行交互。例如:
- HAL层模块:
hal_gpio
: GPIO控制模块
hal_pwm
: PWM控制模块
hal_timer
: 定时器模块
hal_uart
: 串口通信模块
hal_i2c
: I2C通信模块
hal_spi
: SPI通信模块
hal_adc
: ADC模数转换模块
hal_i2s
: I2S音频接口模块
- 驱动层模块:
motor_driver
: 电机驱动模块 (4010无刷电机)
servo_driver
: 舵机驱动模块
microphone_driver
: 麦克风驱动模块
amplifier_driver
: 功放驱动模块
- 控制层模块:
wheel_control
: 轮式运动控制模块
leg_control
: 腿部运动控制模块
motion_control
: 综合运动控制模块 (轮腿协同)
posture_control
: 姿态控制模块
- 应用层模块:
command_parser
: 命令解析模块
robot_mode
: 机器人模式管理模块
status_monitor
: 状态监控模块
voice_control
: 语音控制模块 (未来)
代码实现 (C语言 - ESP-IDF框架)
接下来,我将逐步给出关键模块的C代码实现,并详细解释代码逻辑和设计思路。我将尽可能详细地编写代码,包括注释、错误处理、配置选项等。
1. 硬件抽象层 (HAL)
hal_gpio.h
(GPIO HAL头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #ifndef HAL_GPIO_H #define HAL_GPIO_H
#include <stdint.h> #include "driver/gpio.h" #include "esp_err.h"
typedef enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_INPUT_OUTPUT } hal_gpio_mode_t;
typedef enum { GPIO_PULLUP, GPIO_PULLDOWN, GPIO_PULLNONE } hal_gpio_pull_mode_t;
esp_err_t hal_gpio_init(gpio_num_t gpio_num, hal_gpio_mode_t mode, hal_gpio_pull_mode_t pull_mode);
esp_err_t hal_gpio_set_level(gpio_num_t gpio_num, uint32_t level);
esp_err_t hal_gpio_get_level(gpio_num_t gpio_num, uint32_t *level);
#endif
|
hal_gpio.c
(GPIO HAL实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include "hal_gpio.h"
esp_err_t hal_gpio_init(gpio_num_t gpio_num, hal_gpio_mode_t mode, hal_gpio_pull_mode_t pull_mode) { gpio_config_t io_conf; io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.pin_bit_mask = (1ULL << gpio_num);
if (mode == GPIO_MODE_INPUT) { io_conf.mode = GPIO_MODE_INPUT; } else if (mode == GPIO_MODE_OUTPUT) { io_conf.mode = GPIO_MODE_OUTPUT; } else if (mode == GPIO_MODE_INPUT_OUTPUT) { io_conf.mode = GPIO_MODE_INPUT_OUTPUT; } else { return ESP_ERR_INVALID_ARG; }
if (pull_mode == GPIO_PULLUP) { io_conf.pull_down_en = 0; io_conf.pull_up_en = 1; } else if (pull_mode == GPIO_PULLDOWN) { io_conf.pull_down_en = 1; io_conf.pull_up_en = 0; } else if (pull_mode == GPIO_PULLNONE) { io_conf.pull_down_en = 0; io_conf.pull_up_en = 0; } else { return ESP_ERR_INVALID_ARG; }
esp_err_t ret = gpio_config(&io_conf); if (ret != ESP_OK) { ESP_LOGE("HAL_GPIO", "GPIO %d init failed, error code: %d", gpio_num, ret); return ret; } ESP_LOGI("HAL_GPIO", "GPIO %d init success, mode: %d, pull mode: %d", gpio_num, mode, pull_mode); return ESP_OK; }
esp_err_t hal_gpio_set_level(gpio_num_t gpio_num, uint32_t level) { esp_err_t ret = gpio_set_level(gpio_num, level); if (ret != ESP_OK) { ESP_LOGE("HAL_GPIO", "GPIO %d set level failed, error code: %d", gpio_num, ret); return ret; } return ESP_OK; }
esp_err_t hal_gpio_get_level(gpio_num_t gpio_num, uint32_t *level) { if (level == NULL) { return ESP_ERR_INVALID_ARG; } *level = gpio_get_level(gpio_num); return ESP_OK; }
|
hal_pwm.h
(PWM HAL头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #ifndef HAL_PWM_H #define HAL_PWM_H
#include <stdint.h> #include "driver/ledc.h" #include "esp_err.h"
typedef enum { PWM_TIMER_0, PWM_TIMER_1, PWM_TIMER_2, PWM_TIMER_3 } hal_pwm_timer_t;
typedef enum { PWM_CHANNEL_0, PWM_CHANNEL_1, PWM_CHANNEL_2, PWM_CHANNEL_3, PWM_CHANNEL_4, PWM_CHANNEL_5, PWM_CHANNEL_6, PWM_CHANNEL_7 } hal_pwm_channel_t;
esp_err_t hal_pwm_init(hal_pwm_timer_t timer_num, uint32_t freq_hz, ledc_timer_bit_t duty_resolution);
esp_err_t hal_pwm_config_channel(hal_pwm_channel_t channel_num, gpio_num_t gpio_num, hal_pwm_timer_t timer_num);
esp_err_t hal_pwm_set_duty(hal_pwm_channel_t channel_num, uint32_t duty);
esp_err_t hal_pwm_update_duty(hal_pwm_channel_t channel_num);
#endif
|
hal_pwm.c
(PWM HAL实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include "hal_pwm.h" #include "esp_log.h"
#define PWM_DUTY_RESOLUTION LEDC_TIMER_13_BIT
esp_err_t hal_pwm_init(hal_pwm_timer_t timer_num, uint32_t freq_hz, ledc_timer_bit_t duty_resolution) { ledc_timer_config_t timer_config = { .speed_mode = LEDC_HIGH_SPEED_MODE, .duty_resolution = duty_resolution, .timer_num = timer_num, .freq_hz = freq_hz, .clk_cfg = LEDC_AUTO_CLK, }; esp_err_t ret = ledc_timer_config(&timer_config); if (ret != ESP_OK) { ESP_LOGE("HAL_PWM", "PWM timer %d init failed, error code: %d", timer_num, ret); return ret; } ESP_LOGI("HAL_PWM", "PWM timer %d init success, freq: %d Hz, resolution: %d bits", timer_num, freq_hz, duty_resolution); return ESP_OK; }
esp_err_t hal_pwm_config_channel(hal_pwm_channel_t channel_num, gpio_num_t gpio_num, hal_pwm_timer_t timer_num) { ledc_channel_config_t channel_config = { .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = channel_num, .timer_sel = timer_num, .intr_mask = 0, .duty = 0, .gpio_num = gpio_num, .hpoint = 0, }; esp_err_t ret = ledc_channel_config(&channel_config); if (ret != ESP_OK) { ESP_LOGE("HAL_PWM", "PWM channel %d config failed, error code: %d", channel_num, ret); return ret; } ESP_LOGI("HAL_PWM", "PWM channel %d config success, GPIO: %d, timer: %d", channel_num, gpio_num, timer_num); return ESP_OK; }
esp_err_t hal_pwm_set_duty(hal_pwm_channel_t channel_num, uint32_t duty) { esp_err_t ret = ledc_set_duty(LEDC_HIGH_SPEED_MODE, channel_num, duty); if (ret != ESP_OK) { ESP_LOGE("HAL_PWM", "PWM channel %d set duty failed, error code: %d", channel_num, ret); return ret; } return ESP_OK; }
esp_err_t hal_pwm_update_duty(hal_pwm_channel_t channel_num) { esp_err_t ret = ledc_update_duty(LEDC_HIGH_SPEED_MODE, channel_num); if (ret != ESP_OK) { ESP_LOGE("HAL_PWM", "PWM channel %d update duty failed, error code: %d", channel_num, ret); return ret; } return ESP_OK; }
|
(其他HAL模块类似,例如 hal_uart.h/c
, hal_timer.h/c
, hal_i2c.h/c
等,这里为了篇幅不再一一展开,但原理类似:定义接口头文件 .h
和实现文件 .c
,封装 ESP-IDF 提供的驱动函数。)
2. 驱动层 (Driver Layer)
motor_driver.h
(电机驱动头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #ifndef MOTOR_DRIVER_H #define MOTOR_DRIVER_H
#include <stdint.h> #include "esp_err.h"
typedef enum { MOTOR_FORWARD, MOTOR_BACKWARD, MOTOR_STOP } motor_direction_t;
typedef struct { uint32_t pwm_channel_forward; uint32_t pwm_channel_backward; gpio_num_t gpio_enable; } motor_config_t;
esp_err_t motor_driver_init(motor_config_t *config);
esp_err_t motor_driver_set_speed(motor_config_t *config, motor_direction_t direction, uint32_t speed);
esp_err_t motor_driver_stop(motor_config_t *config);
#endif
|
motor_driver.c
(电机驱动实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include "motor_driver.h" #include "hal_pwm.h" #include "hal_gpio.h" #include "esp_log.h"
#define PWM_MOTOR_FREQUENCY 20000 #define PWM_MOTOR_RESOLUTION LEDC_TIMER_13_BIT #define PWM_DUTY_MAX ((1 << PWM_MOTOR_RESOLUTION) - 1)
esp_err_t motor_driver_init(motor_config_t *config) { if (config == NULL) { return ESP_ERR_INVALID_ARG; }
ESP_ERROR_CHECK(hal_pwm_init(PWM_TIMER_0, PWM_MOTOR_FREQUENCY, PWM_MOTOR_RESOLUTION));
ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel_forward, -1, PWM_TIMER_0));
ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel_backward, -1, PWM_TIMER_0));
if (config->gpio_enable != -1) { ESP_ERROR_CHECK(hal_gpio_init(config->gpio_enable, GPIO_MODE_OUTPUT, GPIO_PULLNONE)); ESP_ERROR_CHECK(hal_gpio_set_level(config->gpio_enable, 1)); }
ESP_LOGI("MOTOR_DRIVER", "Motor driver initialized"); return ESP_OK; }
esp_err_t motor_driver_set_speed(motor_config_t *config, motor_direction_t direction, uint32_t speed) { if (config == NULL) { return ESP_ERR_INVALID_ARG; } if (speed > 100) { speed = 100; }
uint32_t duty = (uint32_t)(((float)speed / 100.0f) * PWM_DUTY_MAX);
if (direction == MOTOR_FORWARD) { ESP_LOGD("MOTOR_DRIVER", "Motor forward, speed: %d%%, duty: %d", speed, duty); ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel_forward, GPIO_NUM_21, PWM_TIMER_0)); ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel_backward, -1, PWM_TIMER_0)); ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel_forward, duty)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel_forward)); ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel_backward, 0)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel_backward)); } else if (direction == MOTOR_BACKWARD) { ESP_LOGD("MOTOR_DRIVER", "Motor backward, speed: %d%%, duty: %d", speed, duty); ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel_backward, GPIO_NUM_22, PWM_TIMER_0)); ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel_forward, -1, PWM_TIMER_0)); ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel_backward, duty)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel_backward)); ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel_forward, 0)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel_forward)); } else if (direction == MOTOR_STOP) { ESP_LOGD("MOTOR_DRIVER", "Motor stop"); ESP_ERROR_CHECK(motor_driver_stop(config)); } else { return ESP_ERR_INVALID_ARG; }
return ESP_OK; }
esp_err_t motor_driver_stop(motor_config_t *config) { if (config == NULL) { return ESP_ERR_INVALID_ARG; } ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel_forward, 0)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel_forward)); ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel_backward, 0)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel_backward)); return ESP_OK; }
|
servo_driver.h
(舵机驱动头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #ifndef SERVO_DRIVER_H #define SERVO_DRIVER_H
#include <stdint.h> #include "esp_err.h"
typedef struct { uint32_t pwm_channel; gpio_num_t gpio_servo; uint32_t angle_min_us; uint32_t angle_max_us; uint32_t angle_range_deg; } servo_config_t;
esp_err_t servo_driver_init(servo_config_t *config);
esp_err_t servo_driver_set_angle(servo_config_t *config, uint32_t angle);
#endif
|
servo_driver.c
(舵机驱动实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include "servo_driver.h" #include "hal_pwm.h" #include "esp_log.h"
#define PWM_SERVO_FREQUENCY 50 #define PWM_SERVO_RESOLUTION LEDC_TIMER_13_BIT #define PWM_DUTY_MAX ((1 << PWM_SERVO_RESOLUTION) - 1)
esp_err_t servo_driver_init(servo_config_t *config) { if (config == NULL) { return ESP_ERR_INVALID_ARG; }
ESP_ERROR_CHECK(hal_pwm_init(PWM_TIMER_1, PWM_SERVO_FREQUENCY, PWM_SERVO_RESOLUTION));
ESP_ERROR_CHECK(hal_pwm_config_channel(config->pwm_channel, config->gpio_servo, PWM_TIMER_1));
ESP_LOGI("SERVO_DRIVER", "Servo driver initialized for GPIO %d", config->gpio_servo); return ESP_OK; }
esp_err_t servo_driver_set_angle(servo_config_t *config, uint32_t angle) { if (config == NULL) { return ESP_ERR_INVALID_ARG; } if (angle > config->angle_range_deg) { angle = config->angle_range_deg; } if (angle < 0) { angle = 0; }
float angle_ratio = (float)angle / config->angle_range_deg; uint32_t pulse_width_us = config->angle_min_us + (uint32_t)(angle_ratio * (config->angle_max_us - config->angle_min_us));
float duty_cycle = (float)pulse_width_us / (1000000.0f / PWM_SERVO_FREQUENCY); uint32_t duty = (uint32_t)(duty_cycle * PWM_DUTY_MAX);
ESP_LOGD("SERVO_DRIVER", "Set servo angle: %d deg, pulse width: %d us, duty: %d", angle, pulse_width_us, duty);
ESP_ERROR_CHECK(hal_pwm_set_duty(config->pwm_channel, duty)); ESP_ERROR_CHECK(hal_pwm_update_duty(config->pwm_channel));
return ESP_OK; }
|
(其他驱动模块例如 microphone_driver.h/c
, amplifier_driver.h/c
的实现类似,需要根据具体的硬件接口和驱动芯片进行编写,这里为了篇幅不再详细展开,但基本思路是:初始化硬件,提供数据收发和控制接口。)
3. 控制层 (Control Layer)
wheel_control.h
(轮式运动控制头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #ifndef WHEEL_CONTROL_H #define WHEEL_CONTROL_H
#include <stdint.h> #include "esp_err.h" #include "motor_driver.h"
typedef struct { motor_config_t motor_left_config; motor_config_t motor_right_config; } wheel_control_config_t;
esp_err_t wheel_control_init(wheel_control_config_t *config);
esp_err_t wheel_control_set_speed(int32_t left_speed, int32_t right_speed);
esp_err_t wheel_control_stop();
#endif
|
wheel_control.c
(轮式运动控制实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include "wheel_control.h" #include "esp_log.h"
static wheel_control_config_t g_wheel_config;
esp_err_t wheel_control_init(wheel_control_config_t *config) { if (config == NULL) { return ESP_ERR_INVALID_ARG; } g_wheel_config = *config;
ESP_ERROR_CHECK(motor_driver_init(&g_wheel_config.motor_left_config)); ESP_ERROR_CHECK(motor_driver_init(&g_wheel_config.motor_right_config));
ESP_LOGI("WHEEL_CONTROL", "Wheel control initialized"); return ESP_OK; }
esp_err_t wheel_control_set_speed(int32_t left_speed, int32_t right_speed) { motor_direction_t left_dir = MOTOR_STOP; motor_direction_t right_dir = MOTOR_STOP; uint32_t left_speed_percent = 0; uint32_t right_speed_percent = 0;
if (left_speed > 0) { left_dir = MOTOR_FORWARD; left_speed_percent = (uint32_t)left_speed; } else if (left_speed < 0) { left_dir = MOTOR_BACKWARD; left_speed_percent = (uint32_t)(-left_speed); }
if (right_speed > 0) { right_dir = MOTOR_FORWARD; right_speed_percent = (uint32_t)right_speed; } else if (right_speed < 0) { right_dir = MOTOR_BACKWARD; right_speed_percent = (uint32_t)(-right_speed); }
ESP_LOGD("WHEEL_CONTROL", "Set speed: left %d, right %d", left_speed, right_speed);
ESP_ERROR_CHECK(motor_driver_set_speed(&g_wheel_config.motor_left_config, left_dir, left_speed_percent)); ESP_ERROR_CHECK(motor_driver_set_speed(&g_wheel_config.motor_right_config, right_dir, right_speed_percent));
return ESP_OK; }
esp_err_t wheel_control_stop() { ESP_LOGD("WHEEL_CONTROL", "Stop wheel control"); ESP_ERROR_CHECK(motor_driver_stop(&g_wheel_config.motor_left_config)); ESP_ERROR_CHECK(motor_driver_stop(&g_wheel_config.motor_right_config)); return ESP_OK; }
|
leg_control.h
(腿部运动控制头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #ifndef LEG_CONTROL_H #define LEG_CONTROL_H
#include <stdint.h> #include "esp_err.h" #include "servo_driver.h"
#define LEG_NUM 2
typedef struct { servo_config_t servo_hip_config; servo_config_t servo_knee_config; } leg_config_t;
typedef struct { leg_config_t legs[LEG_NUM]; } leg_control_config_t;
esp_err_t leg_control_init(leg_control_config_t *config);
esp_err_t leg_control_set_leg_angle(uint32_t leg_index, uint32_t hip_angle, uint32_t knee_angle);
esp_err_t leg_control_stand_pose();
esp_err_t leg_control_contract_pose();
#endif
|
leg_control.c
(腿部运动控制实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include "leg_control.h" #include "esp_log.h"
static leg_control_config_t g_leg_config;
esp_err_t leg_control_init(leg_control_config_t *config) { if (config == NULL) { return ESP_ERR_INVALID_ARG; } g_leg_config = *config;
for (int i = 0; i < LEG_NUM; i++) { ESP_ERROR_CHECK(servo_driver_init(&g_leg_config.legs[i].servo_hip_config)); ESP_ERROR_CHECK(servo_driver_init(&g_leg_config.legs[i].servo_knee_config)); }
ESP_LOGI("LEG_CONTROL", "Leg control initialized"); return ESP_OK; }
esp_err_t leg_control_set_leg_angle(uint32_t leg_index, uint32_t hip_angle, uint32_t knee_angle) { if (leg_index >= LEG_NUM) { return ESP_ERR_INVALID_ARG; } ESP_LOGD("LEG_CONTROL", "Set leg %d angle: hip %d, knee %d", leg_index, hip_angle, knee_angle);
ESP_ERROR_CHECK(servo_driver_set_angle(&g_leg_config.legs[leg_index].servo_hip_config, hip_angle)); ESP_ERROR_CHECK(servo_driver_set_angle(&g_leg_config.legs[leg_index].servo_knee_config, knee_angle));
return ESP_OK; }
esp_err_t leg_control_stand_pose() { ESP_LOGI("LEG_CONTROL", "Set stand pose"); for (int i = 0; i < LEG_NUM; i++) { ESP_ERROR_CHECK(leg_control_set_leg_angle(i, 90, 90)); } return ESP_OK; }
esp_err_t leg_control_contract_pose() { ESP_LOGI("LEG_CONTROL", "Set contract pose"); for (int i = 0; i < LEG_NUM; i++) { ESP_ERROR_CHECK(leg_control_set_leg_angle(i, 0, 0)); } return ESP_OK; }
|
( motion_control.h/c
, posture_control.h/c
等模块的实现类似,需要根据具体的运动控制算法和姿态控制策略进行编写,例如可以实现轮腿协同运动,姿态平衡控制等。)
4. 应用层 (Application Layer)
command_parser.h
(命令解析头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #ifndef COMMAND_PARSER_H #define COMMAND_PARSER_H
#include <stdint.h> #include "esp_err.h"
typedef enum { CMD_FORWARD, CMD_BACKWARD, CMD_TURN_LEFT, CMD_TURN_RIGHT, CMD_STOP, CMD_STAND, CMD_CONTRACT, CMD_UNKNOWN } robot_command_t;
esp_err_t command_parser_parse(const char *command_str, robot_command_t *command_out);
#endif
|
command_parser.c
(命令解析实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include "command_parser.h" #include <string.h> #include <stdio.h> #include "esp_log.h"
esp_err_t command_parser_parse(const char *command_str, robot_command_t *command_out) { if (command_str == NULL || command_out == NULL) { return ESP_ERR_INVALID_ARG; }
ESP_LOGD("COMMAND_PARSER", "Parsing command: %s", command_str);
if (strcmp(command_str, "forward") == 0) { *command_out = CMD_FORWARD; } else if (strcmp(command_str, "backward") == 0) { *command_out = CMD_BACKWARD; } else if (strcmp(command_str, "left") == 0 || strcmp(command_str, "turn_left") == 0) { *command_out = CMD_TURN_LEFT; } else if (strcmp(command_str, "right") == 0 || strcmp(command_str, "turn_right") == 0) { *command_out = CMD_TURN_RIGHT; } else if (strcmp(command_str, "stop") == 0) { *command_out = CMD_STOP; } else if (strcmp(command_str, "stand") == 0) { *command_out = CMD_STAND; } else if (strcmp(command_str, "contract") == 0) { *command_out = CMD_CONTRACT; } else { *command_out = CMD_UNKNOWN; ESP_LOGW("COMMAND_PARSER", "Unknown command: %s", command_str); }
return ESP_OK; }
|
robot_mode.h
(机器人模式管理头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #ifndef ROBOT_MODE_H #define ROBOT_MODE_H
#include <stdint.h> #include "esp_err.h" #include "command_parser.h"
typedef enum { ROBOT_MODE_IDLE, ROBOT_MODE_MANUAL_CONTROL, ROBOT_MODE_VOICE_CONTROL, ROBOT_MODE_AUTO_NAV, ROBOT_MODE_ERROR } robot_mode_t;
esp_err_t robot_mode_init();
esp_err_t robot_mode_set_mode(robot_mode_t mode);
robot_mode_t robot_mode_get_mode();
esp_err_t robot_mode_handle_command(robot_command_t command);
#endif
|
robot_mode.c
(机器人模式管理实现文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include "robot_mode.h" #include "wheel_control.h" #include "leg_control.h" #include "esp_log.h"
static robot_mode_t g_current_mode = ROBOT_MODE_IDLE;
esp_err_t robot_mode_init() { g_current_mode = ROBOT_MODE_IDLE; ESP_LOGI("ROBOT_MODE", "Robot mode control initialized, default mode: IDLE"); return ESP_OK; }
esp_err_t robot_mode_set_mode(robot_mode_t mode) { if (mode >= ROBOT_MODE_ERROR) { return ESP_ERR_INVALID_ARG; } g_current_mode = mode; ESP_LOGI("ROBOT_MODE", "Set robot mode to: %d", mode); return ESP_OK; }
robot_mode_t robot_mode_get_mode() { return g_current_mode; }
esp_err_t robot_mode_handle_command(robot_command_t command) { ESP_LOGD("ROBOT_MODE", "Handle command: %d, current mode: %d", command, g_current_mode);
if (g_current_mode == ROBOT_MODE_MANUAL_CONTROL) { switch (command) { case CMD_FORWARD: wheel_control_set_speed(50, 50); break; case CMD_BACKWARD: wheel_control_set_speed(-50, -50); break; case CMD_TURN_LEFT: wheel_control_set_speed(-30, 30); break; case CMD_TURN_RIGHT: wheel_control_set_speed(30, -30); break; case CMD_STOP: wheel_control_stop(); break; case CMD_STAND: leg_control_stand_pose(); break; case CMD_CONTRACT: leg_control_contract_pose(); break; case CMD_UNKNOWN: ESP_LOGW("ROBOT_MODE", "Unknown command in MANUAL_CONTROL mode"); break; default: break; } } else if (g_current_mode == ROBOT_MODE_IDLE) { if (command == CMD_STAND) { leg_control_stand_pose(); } else if (command == CMD_CONTRACT) { leg_control_contract_pose(); } else { ESP_LOGW("ROBOT_MODE", "Command ignored in IDLE mode"); } }
return ESP_OK; }
|
( status_monitor.h/c
, voice_control.h/c
等模块的实现类似,根据具体的功能需求进行编写。 voice_control
模块在未来实现离线语音控制功能时会比较复杂,需要集成语音识别算法和模型。)
5. main.c
(主程序)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "wheel_control.h" #include "leg_control.h" #include "command_parser.h" #include "robot_mode.h" #include "hal_uart.h"
#define TAG "MAIN"
wheel_control_config_t wheel_config = { .motor_left_config = { .pwm_channel_forward = PWM_CHANNEL_0, .pwm_channel_backward = PWM_CHANNEL_1, .gpio_enable = -1, }, .motor_right_config = { .pwm_channel_forward = PWM_CHANNEL_2, .pwm_channel_backward = PWM_CHANNEL_3, .gpio_enable = -1, }, };
leg_control_config_t leg_config = { .legs = { [0] = { .servo_hip_config = { .pwm_channel = PWM_CHANNEL_4, .gpio_servo = GPIO_NUM_18, .angle_min_us = 500, .angle_max_us = 2500, .angle_range_deg = 180, }, .servo_knee_config = { .pwm_channel = PWM_CHANNEL_5, .gpio_servo = GPIO_NUM_19, .angle_min_us = 500, .angle_max_us = 2500, .angle_range_deg = 180, }, }, [1] = { .servo_hip_config = { .pwm_channel = PWM_CHANNEL_6, .gpio_servo = GPIO_NUM_23, .angle_min_us = 500, .angle_max_us = 2500, .angle_range_deg = 180, }, .servo_knee_config = { .pwm_channel = PWM_CHANNEL_7, .gpio_servo = GPIO_NUM_5, .angle_min_us = 500, .angle_max_us = 2500, .angle_range_deg = 180, }, }, }, };
void app_main(void) { ESP_LOGI(TAG, "--- ESP32S3 Wheel-Leg Robot ---");
ESP_ERROR_CHECK(hal_uart_init(UART_NUM_0, 115200));
ESP_ERROR_CHECK(wheel_control_init(&wheel_config)); ESP_ERROR_CHECK(leg_control_init(&leg_config)); ESP_ERROR_CHECK(robot_mode_init());
ESP_ERROR_CHECK(leg_control_stand_pose());
ESP_ERROR_CHECK(robot_mode_set_mode(ROBOT_MODE_MANUAL_CONTROL));
char command_str[64]; robot_command_t command;
while (1) { printf("Enter command (forward, backward, left, right, stop, stand, contract): "); if (fgets(command_str, sizeof(command_str), stdin) != NULL) { command_str[strcspn(command_str, "\n")] = 0;
ESP_ERROR_CHECK(command_parser_parse(command_str, &command)); ESP_ERROR_CHECK(robot_mode_handle_command(command)); } vTaskDelay(pdMS_TO_TICKS(100)); } }
|
代码编译和运行
- 环境搭建: 确保已安装 ESP-IDF 开发环境,并配置好 ESP32S3 的工具链。
- 创建项目: 使用 ESP-IDF 创建新的项目,并将以上代码文件添加到项目中。
- 配置硬件: 根据实际硬件连接修改
main.c
中的电机和舵机配置 (GPIO 引脚、PWM 通道等)。
- 编译项目: 在 ESP-IDF 环境中编译项目 (
idf.py build
)。
- 烧录固件: 将编译生成的固件烧录到 ESP32S3 开发板 (
idf.py flash monitor
)。
- 串口监控: 通过串口终端 (例如
idf.py monitor
) 观察程序运行状态和输出信息,并通过串口输入命令控制机器人。
系统开发流程总结
- 需求分析: 明确项目的功能需求、性能指标、硬件平台等。
- 系统设计:
- 架构设计: 选择合适的分层架构和模块化设计。
- 模块划分: 将系统分解为独立的模块,定义模块接口。
- 硬件选型: 选择合适的硬件器件,例如电机、舵机、传感器等。
- PCB设计: 使用立创EDA等工具进行PCB设计。
- 代码实现:
- HAL层开发: 封装底层硬件操作,提供统一的硬件接口。
- 驱动层开发: 实现特定硬件设备的功能驱动。
- 控制层开发: 编写机器人的核心控制算法。
- 应用层开发: 构建用户交互和高层应用逻辑。
- 测试验证:
- 单元测试: 测试每个模块的功能是否正常。
- 集成测试: 测试模块之间的协同工作是否正常。
- 系统测试: 整体测试系统的功能和性能是否满足需求。
- 实机测试: 在实际机器人硬件平台上进行测试和调试。
- 维护升级:
- Bug修复: 及时修复测试和使用过程中发现的Bug。
- 功能扩展: 根据需求增加新的功能模块,例如语音控制、自主导航等。
- 性能优化: 持续优化代码,提高系统效率和性能。
- 固件升级: 提供方便的固件升级方式,方便用户更新系统。
技术和方法实践验证
本项目中采用的各项技术和方法都是经过实践验证的,例如:
- 分层架构和模块化设计: 是嵌入式系统开发中常用的成熟架构,可以提高代码的可读性、可维护性和可扩展性。
- HAL硬件抽象层: 可以屏蔽底层硬件差异,方便代码移植和重用。
- 驱动层封装: 将硬件驱动封装成独立的模块,方便驱动的更换和升级。
- FreeRTOS实时操作系统: ESP-IDF 默认集成了 FreeRTOS,可以方便地实现多任务并发执行,提高系统的实时性和响应速度。
- C语言编程: C语言是嵌入式系统开发中最常用的编程语言,具有高效、灵活、可移植等优点。
- ESP-IDF开发框架: ESP-IDF 是 Espressif 官方提供的 ESP32 系列芯片的开发框架,提供了丰富的API和组件,方便开发者快速构建应用。
- 立创EDA PCB设计: 立创EDA 是一款专业的在线PCB设计工具,功能强大,易于使用,适合嵌入式项目的PCB设计。
总结
以上代码和架构设计方案,旨在为您提供一个清晰、可靠、高效、可扩展的轮腿机器人嵌入式系统平台。实际项目开发中,还需要根据具体的需求和硬件平台进行调整和完善。希望这份详细的解答能够帮助您顺利开展项目开发!