【21届智能车赛轮腿穿越组】基于 CYT4B 的轮腿车开发笔记(一):项目总览与整体架构
系列导读:本系列不讲流水账,只讲思路。AI 时代,思路大于实现——真正的难点在于"为什么这么分层、为什么这么调度、为什么这么算",代码只是思路的载体。每一期围绕一个模块展开,说明逻辑架构与实现思路,关键代码片段作为佐证。
1. 项目是什么
这是一台轮腿式平衡小车,主控是英飞凌 Traveo II 系列 CYT4BB7 双核 Cortex-M7 单片机,搭配逐飞科技 CYT4B 核心板。
它的特别之处在于既有轮又有腿:
- 轮:左右两个轮毂电机,负责驱动与差速转向;
- 腿:每侧前、后两个舵机组成的四连杆机构,可以调整车身高度,甚至可以跳起来越障。
所以它不只是"会跑的平衡车",而是一个能实现自平衡 + 自主巡航 + 跳跃越障 + 视觉循线的组合体。
轮腿车 = 轮式(速度/转向) × 腿式(姿态/越障)
= 平衡控制算法 + 运动学逆解 + 路径规划 + 视觉感知
2. 硬件平台一览
| 模块 | 型号 | 作用 |
|---|---|---|
| 主控 | CYT4BB7 (Traveo II, 双 M7) | 全部算法与调度 |
| IMU | 逐飞 IMU660RC(6 轴) | 姿态角 pitch/roll/yaw、角速度 |
| 摄像头 | 逐飞 MT9V03X(120×188) | 视觉循线、跳跃触发识别 |
| 显示屏 | IPS200(SPI) | 菜单与调试信息 |
| 电机 | 轮毂电机 ×2(UART) | 驱动与差速,带转速回读 |
| 舵机 | 四连杆腿部舵机 ×4(PWM) | 腿部姿态控制 |
| GPS | GNSS 模块(≥16 颗卫星有效) | 全局定位、路径打点 |
| 无线 | LoRa 遥控 | 手遥 / 路径录制 |
3. 整体软件架构
这是整个项目最值得讲的地方。代码被清晰地分成了 四层:
┌─────────────────────────────────────────────┐
│ 中断层 isr.c (1ms / 10ms 定时调度) │
├─────────────────────────────────────────────┤
│ 应用层 app/ (balance/ik/jump/path/vision) │
├─────────────────────────────────────────────┤
│ 驱动层 drivers/ (imu/camera/motor/servo) │
├─────────────────────────────────────────────┤
│ 抽象层 hal/ (uart/pwm/spi/flash) │
└─────────────────────────────────────────────┘
- 中断层:把实时性要求高的任务放进 1ms 定时中断里跑(平衡控制必须卡着节拍来);
- 应用层:纯算法,不碰寄存器,只操作
CarState_t结构体; - 驱动层:封装逐飞库,向上暴露简洁接口(如
imu_driver_read()); - 抽象层:把寄存器操作包成
hal_uart_init()这类通用函数。
这样分层的好处:算法和硬件解耦。想换摄像头、换电机,只改驱动层;想改平衡逻辑,只动 app 层。
4. 数据中枢 CarState_t:一辆车的"共享内存"
这是架构里最核心的设计——所有模块之间不直接互调函数传参,而是通过一个全局结构体 CarState_t 交换数据。
typedef struct {
float pitch, roll, yaw; /* IMU 姿态 */
float gyro_y, gyro_z; /* 角速度 */
float target_speed; /* 控制目标:速度 */
float target_yaw; /* 控制目标:偏航 */
int left_motor_out; /* 电机输出 */
int right_motor_out;
Pid_t pid_as, pid_pitch; /* 各级 PID */
Servo_t servo_l1, servo_r1; /* 四个腿部舵机 */
PathData_t path; /* 路径数据 */
PathRecord_t record; /* 打点记录 */
uint8_t run_flag, task_flag; /* 运行/任务状态 */
...
} CarState_t;
设计思路:
- 一个状态体,处处可见:
main.c里定义CarState_t car,中断、算法、驱动全部读写它; - 天然是"控制环"的接口:传感器把数据写进去,算法从里面读目标、写输出,驱动从里面取输出下发;
- 方便调试:所有中间量都在一个结构体里,屏显和串口打印想要什么直接取。
一句话:
CarState_t就是这辆车的"总线"。模块间只通过它说话,谁也不用知道谁存在。
5. 双线程式调度:主循环 vs 定时中断
这是全项目调度设计的关键。系统没有用 RTOS,而是靠 两个 1ms 中断 + 一个主循环 分工:
5.1 1ms 中断:控制实时部分
void pit0_ch0_isr(void) {
pit_isr_flag_clear(PIT_CH0);
if (car.imu_init_cnt >= 401) { /* IMU 稳定后再开控 */
/* 1. 更新里程/融合定位 */
fusion_update(&car, ...);
/* 2. 按 run_flag 执行对应控制 */
if (car.run_flag == 1) { /* 自主复现 */
balance_update(&car);
motor_driver_set_duty(...);
servo_driver_control(...); /* 腿部跟随 */
}
else if (car.run_flag == 2) { /* 遥控模式 */
...
}
}
}
为什么放中断?平衡控制要求严格的 1ms 周期,中断天然满足定时精度,不依赖主循环忙闲。
5.2 另一个 1ms 中断:任务状态机
void pit0_ch2_isr(void) {
pit_isr_flag_clear(PIT_CH2);
if (car.run_flag == 1) {
path_follow_update(&car); /* 路径跟踪 → 设置目标速度/目标偏航 */
if (car.task_flag == 1 && car.jump_flag == 1) {
jump_manager(&car); /* 跳跃状态机 */
}
}
}
设计意图:把"路径决策"和"实时平衡"分开。平衡环 1ms 一次,路径决策也 1ms 一次但逻辑上更"慢节奏"——它们互不阻塞,实时性都有保证。
5.3 10ms 中断:LoRa 遥控心跳
void pit0_ch1_isr(void) {
pit_isr_flag_clear(PIT_CH1);
lora_driver_tick(&car); /* 遥控超时检测 */
}
遥控数据频率不需要 1ms,10ms 足够,还能省 CPU。
5.4 主循环:非实时任务
int main(void) {
system_init(&car);
while (1) {
imu_driver_read(&car); /* 读 IMU */
path_record_gps_update(&car); /* 收 GPS */
if (camera_driver_frame_ready()) { /* 处理一帧图像 */
image_binarize(image_bin, raw, car.img_threshold);
image_noise_filter(image_bin);
track_detect_process(&car, image_bin);
}
if (car.run_flag != 1) { /* 待机时显示菜单 */
menu_process(&car);
menu_display(&car);
}
}
}
主循环只做慢任务:图像处理(一帧 120×188 的遍历)、GPS 解析、菜单刷新。它们允许几百微秒级的抖动,放主循环最合适。
6. 调度哲学总结
实时性要求: 1ms平衡 > 1ms路径 > 10ms遥控 > 主循环图像
放在哪: 中断 1 → 中断 2 → 中断 3 → main
这就是嵌入式控制里最常见的"分级调度"思想:
- 越紧急、周期越严格的,越靠近中断;
- 越耗时、越不紧急的,越往主循环沉;
- 层与层之间用状态体通信,互不依赖。
这套思路其实通用性很强——任何"传感器采集 + 控制 + 人机交互"的小车/机器人项目都可以照搬这个骨架,换成 STM32、ESP32 只是换驱动层而已。
7. 下期预告
架构搭好了,接下来进入第一个核心算法:两轮自平衡的级联 PID 控制。
我们会讲清楚:为什么平衡要用"角速度环 + 俯仰角环 + 速度环"三环嵌套,而不是一个 PID 一把梭,以及横滚环、偏航环是怎么在平衡之上叠加的。
欢迎关注,持续连载中。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)