ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【21届智能车赛轮腿穿越组】基于 CYT4B 的轮腿车开发笔记(七):遥控、菜单系统与实车调试心得

2026/8/21 9:05:36 拓冰建站 浏览量
【21届智能车赛轮腿穿越组】基于 CYT4B 的轮腿车开发笔记(七):遥控、菜单系统与实车调试心得 本期最终期核心人机交互怎么设计LoRa 摇杆如何映射成速度/转向菜单系统怎么组织以及整套系统在实车调试中沉淀下来的通用经验。1. 先想清楚人机交互的价值一辆小车只有算法是不够的——它需要被控制、被配置、被调试。这台轮腿车有两套交互手段LoRa 无线遥控器实车操控、路径录制IPS200 屏 按键菜单配置、状态显示。交互系统的设计目标就一句话让操作和调试都能在车边上完成不用反复烧程序。2. LoRa 遥控摇杆数据怎么变控制指令遥控器通过 LoRa 发来摇杆原始值joystick[2]两个摇杆和按键状态。代码在remote.c里做两件事2.1 摇杆线性映射/* 摇杆1前后推 → 目标速度 */int16_tremote_map_value(int16_tx,int16_tin_min,int16_tin_max,int16_tout_min,int16_tout_max){return(x-in_min)*(out_max-out_min)/(in_max-in_min)out_min;}把摇杆的模拟范围如 -2010~2000线性映射到速度输出范围如 -600~600car.target_speedremote_map_value(lora_driver_get_joystick(1),JOY1_IN_MIN,JOY1_IN_MAX,JOY1_OUT_MIN,JOY1_OUT_MAX);2.2 摇杆增量转向偏航摇杆2 不直接给目标偏航角而是给偏航增量——打杆就往那个方向慢慢转回杆保持当前方向floatremote_update_angle_incremental(int16_tjoy_raw,float*cur_angle){floatdelta0.0f;if(joy_rawJOY2_DEADZONE){delta(float)(joy_raw-JOY2_DEADZONE)/(JOY2_IN_MAX-JOY2_DEADZONE)*MAX_ANGLE_STEP;}elseif(joy_raw-JOY2_DEADZONE){delta(float)(joy_rawJOY2_DEADZONE)/(-JOY2_IN_MIN-JOY2_DEADZONE)*MAX_ANGLE_STEP;}*cur_angledelta;/* 累加 → 增量转向 */while(*cur_angle180.0f)*cur_angle-360.0f;/* 归一化 */return*cur_angle;}为什么转向用增量而速度用绝对这非常符合操控直觉速度摇杆推到哪速度就要到哪 → 绝对映射推杆即加速回中即停转向打杆只是给个转动的角速度松杆后希望车保持方向而不是回正 → 增量映射。一句话位置量用绝对映射速度/方向量用增量映射。这个原则做任何遥控/航模都适用。2.3 超时保护遥控链路断了车必须停不能继续开。isr 里做超时检测voidlora_driver_tick(CarState_t*s){s-lora_timeout_cnt;if(s-lora_timeout_cntLORA_TIMEOUT_MS/10){/* 500ms 没收到数据 */lora3a22_state_flag0;/* 标记失联 */s-lora_timeout_cnt0;}}主循环/中断里一旦发现失联if(!lora_driver_is_connected()){car.target_speed0;/* 失联即停 */}无线设备必须有超时保护失联必须有个安全默认动作这里是停车。这是遥控系统的安全底线。3. 按键系统消抖与单次触发车上有 4 个按键 K1~K4。按键最烦的是抖动和长按连发。代码用两招解决intkey_driver_scan(void){/* 读 GPIO */ints1hal_gpio_get(P20_1);...if(s11s21s31s41)return0;/* 全高 无按键 */hal_system_delay_ms(20);/* 20ms 软件消抖 *//* 再读一遍确认按下 */s1hal_gpio_get(P20_1);...if(s10)return1;.../* 返回按键号 */return0;}intkey_driver_get_single(void){staticintlast_key0;intcurkey_driver_scan();if(cur!0last_key0){/* 只在从未按下→按下沿触发 */last_keycur;returncur;}elseif(cur0){last_key0;}return0;}软件消抖读两次、间隔 20ms两次都低才算按下边沿触发last_key记住上次状态只在释放→按下的上升沿返回一次长按不会连发。消抖 边沿触发是按键系统的标配。尤其菜单导航里长按连发会让菜单乱跳边沿触发必须做。4. 菜单系统页面状态机菜单用页面管理本质上又是一个状态机voidmenu_process(CarState_t*s){intkeykey_driver_get_remote_or_key(s);/* 支持遥控或按键 */if(s-menu_pagePAGE_MAIN){if(key1)s-menu_pagePAGE_RUN;/* K1: 运行菜单 */if(key2)s-menu_pagePAGE_DEBUG;/* K2: 调试菜单 */if(key3){s-run_flag1;s-menu_pagePAGE_NONE;}/* K3: 开始复现 */if(key4){s-run_flag2;s-menu_pagePAGE_NONE;}/* K4: 遥控模式 */s-menu_need_redraw1;}...}voidmenu_display(CarState_t*s){if(!s-menu_need_redraw)return;/* 只在需要时重绘省屏寿命/CPU */...if(s-menu_pagePAGE_MAIN){display_driver_show_str(...);...}}菜单设计的两个要点页面状态机每个页面响应按键跳转run_flag切换菜单态/运行态——菜单和运行是互斥的模式用标志位切换脏标记重绘menu_need_redraw标志只有页面切换/参数变化才重绘平时不刷屏。菜单系统的本质就是状态机 脏标记。从单页菜单到多级菜单骨架都是这个。5. 参数配置哲学集中在一个头文件所有可调参数集中在config/car_config.h——这是全项目最好用的一个文件/* PID */#definePID_PITCH_KP210.0f#definePID_PITCH_KI0.1f#definePID_PITCH_KD50.0f/* 腿部机械常数 */#defineLEG_L16.0f#defineLEG_L53.7f/* 跳跃时序 */#defineJUMP_T_EXTEND100#defineJUMP_T_RETRACT60设计规则代码注释里明明白白写着所有可调参数集中定义不在 .c 里出现魔法数字每个参数配注释说明含义需要运行时改的参数曝光、阈值、路段时长存 Flash开机读回第4期。为什么集中调参 改一个头文件 全项目可见。散落在十几个 .c 文件里的魔法数字是嵌入式项目最大的维护噩梦。6. 实车调试心得踩坑总结最后分享这套系统实车调试沉淀下来的通用经验6.1 分模块验证别一次全开先只测平衡电机不动、只看俯仰再测转向再测遥控最后才测路径/视觉一次只调一个变量其他参数全部固定。6.2 传感器数据先打印再信任何算法上阵前先把 raw 数据打到屏上/串口人工检查一遍IMU 是否零漂、GPS 星数够不够、摄像头阈值合不合适——不验证数据就调算法等于盲调。6.3 时间驱动的开环动作先在地面做跳跃这类动作先架空轮子看腿部时序是否合理再上真车跳跃时序JUMP_T_*这种参数单独抽出单独调别和平衡耦合。6.4 保护机制越早加越好按键急停、遥控失联停车、腿长越界钳制——这些保护一开始就加后期省下无数麻烦出现 NAN、越界全部要有兜底第3期讲的ik_solveNAN 处理。6.5 参数可持久化调参才高效曝光、阈值、路段时长这些要存 Flash、菜单可改、开机读回不然每改一次参数烧一次程序调试效率极低。7. 系列总结回到第1期的一句话思路大于实现。回头看这 7 期其实核心思路就几条主题核心思路架构分层 状态体通信 分级调度平衡级联 PID越内层越快符号解耦腿部IK 逆解 时间状态机跳跃路径投影 → 骨架 → 样条 → 纯追踪跟踪三问在哪/往哪/多快 路段状态机视觉二值化 → 边线 → PD 修正叠加交互绝对/增量映射 状态机 集中配置代码会过时架构和思路不会。把这套骨架换成别的 MCU、别的传感器、别的小车照样能跑。8. 后记本项目代码已开源在 GitHubhttps://github.com/L-F-S-max/wheel-legged-robot欢迎 Star / Issue 交流。如有问题或想深入某个模块欢迎评论区留言。