这次我们来看轮腿机器人开发中的三个核心技术:CAN总线通信、LQR控制和PID控制。这三个技术点构成了轮腿机器人从底层通信到上层控制的核心技术栈,对于机器人开发者来说,掌握这些技术意味着能够独立完成从硬件通信到运动控制的完整开发流程。
轮腿机器人结合了轮式移动的快速性和腿式运动的越障能力,在复杂地形中具有独特优势。而要实现稳定可靠的运动控制,需要CAN总线提供可靠的实时通信,LQR算法处理复杂的动力学平衡,PID控制器实现精确的电机控制。本文将详细解析这三个技术的原理、实现方法和在实际机器人中的应用要点。
1. 核心能力速览
| 技术组件 |
功能说明 |
适用场景 |
实现复杂度 |
| CAN总线 |
多节点实时通信,抗干扰强 |
电机控制器、传感器数据采集 |
中等 |
| LQR控制 |
最优状态反馈控制,多变量协调 |
机器人平衡控制、姿态稳定 |
较高 |
| PID控制 |
经典反馈控制,参数整定简单 |
电机转速控制、位置伺服 |
低到中等 |
这三个技术组合起来,可以构建一个完整的轮腿机器人控制系统:CAN总线负责各个执行器和传感器之间的可靠通信,LQR算法处理机器人的整体平衡和运动规划,PID控制器则确保每个关节电机的精确执行。
2. 适用场景与使用边界
轮腿机器人主要适用于需要兼顾移动速度和越障能力的场景,比如野外勘探、灾难救援、安防巡逻等。在这种应用中,CAN总线提供了分布式控制架构的通信基础,LQR算法保证了机器人在复杂地形中的稳定性,PID控制确保了执行精度。
CAN总线的适用边界:适合节点数量较多(通常2-128个)、通信距离适中(最长可达1000米)、需要强抗干扰能力的场景。但对于极高实时性要求(微秒级)或极高速率(超过1Mbps)的应用,可能需要考虑其他总线协议。
LQR控制的适用边界:LQR适合线性系统或可线性化的系统,对于高度非线性的复杂系统,可能需要结合其他控制方法。LQR的设计需要准确的系统模型,模型不准会导致控制性能下降。
PID控制的适用边界:PID适合单输入单输出系统,对于多变量耦合较强的系统,单纯的PID可能难以达到理想效果。PID参数整定相对简单,但面对时变系统或非线性系统时可能需要自适应机制。
3. 环境准备与前置条件
3.1 硬件环境要求
- 主控制器:STM32F4/F7系列或更高性能的MCU,具备CAN控制器
- 电机驱动:支持CAN通信的智能电机,如M2006、M3508等
- 传感器:IMU(惯性测量单元)、编码器、力矩传感器等
- CAN收发器:TJA1050或类似型号的CAN收发芯片
- 电源系统:稳定的电源供应,注意电机启动时的电流冲击
3.2 软件环境准备
BASH
2
STM32CubeIDE 或 Keil MDK
6
Webots 或 MATLAB/Simulink
7
Python + 控制算法库(如scipy、control)
3.3 基础知识储备
- C语言编程基础,特别是嵌入式开发经验
- 自动控制原理基础知识
- 矩阵运算和状态空间表示
- 实时操作系统基础(如FreeRTOS)
4. CAN总线通信实现
4.1 CAN总线基础配置
CAN总线的配置需要关注以下几个关键参数:
C
2
CAN_HandleTypeDef hcan;
4
hcan.Init.Prescaler = 6; // 总线时钟分频
5
hcan.Init.Mode = CAN_MODE_NORMAL; // 工作模式
6
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
7
hcan.Init.TimeSeg1 = CAN_BS1_13TQ; // 时间段1
8
hcan.Init.TimeSeg2 = CAN_BS2_2TQ; // 时间段2
9
hcan.Init.TimeTriggeredMode = DISABLE;
10
hcan.Init.AutoBusOff = DISABLE;
11
hcan.Init.AutoWakeUp = DISABLE;
12
hcan.Init.AutoRetransmission = ENABLE;
13
hcan.Init.ReceiveFifoLocked = DISABLE;
14
hcan.Init.TransmitFifoPriority = DISABLE;
16
// 计算波特率:APB1时钟 / Prescaler / (TimeSeg1 + TimeSeg2 + 1)
17
// 例如:42MHz / 6 / (13+2+1) = 437.5Kbps
4.2 CAN消息收发实现
C
2
HAL_StatusTypeDef CAN_Send_Motor_Cmd(uint16_t motor_id, int16_t current_cmd) {
3
CAN_TxHeaderTypeDef tx_header;
7
tx_header.StdId = 0x200 + motor_id; // 标准ID
8
tx_header.ExtId = 0x00; // 扩展ID
9
tx_header.RTR = CAN_RTR_DATA; // 数据帧
10
tx_header.IDE = CAN_ID_STD; // 标准ID格式
11
tx_header.DLC = 8; // 数据长度
12
tx_header.TransmitGlobalTime = DISABLE;
15
data[0] = current_cmd & 0xFF;
16
data[1] = (current_cmd >> 8) & 0xFF;
19
return HAL_CAN_AddTxMessage(&hcan, &tx_header, data, &mailbox);
23
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
24
CAN_RxHeaderTypeDef rx_header;
27
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, data);
30
if (rx_header.StdId >= 0x201 && rx_header.StdId <= 0x208) {
31
uint8_t motor_id = rx_header.StdId - 0x201;
32
int16_t angle = (data[0] | (data[1] << 8));
33
int16_t speed = (data[2] | (data[3] << 8));
34
int16_t current = (data[4] | (data[5] << 8));
37
update_motor_state(motor_id, angle, speed, current);
4.3 CAN总线拓扑结构设计
轮腿机器人通常采用总线型拓扑结构,所有节点并联在总线上:
TEXT
3
+--- 左前腿电机组 (CAN ID: 0x201-0x203)
5
+--- 右前腿电机组 (CAN ID: 0x204-0x206)
7
+--- 左后腿电机组 (CAN ID: 0x207-0x209)
9
+--- 右后腿电机组 (CAN ID: 0x20A-0x20C)
11
+--- IMU传感器 (CAN ID: 0x20D)
每个电机组包含3个电机(髋关节、膝关节、踝关节),通过CAN总线接收控制命令并反馈状态信息。
5. LQR控制器设计与实现
5.1 轮腿机器人状态空间模型
LQR控制需要建立系统的状态空间模型。对于轮腿机器人,可以简化为倒立摆模型:
TEXT
1
状态变量 x = [θ, θ̇, x, ẋ]ᵀ
5.2 LQR权重矩阵设计
LQR控制器的性能很大程度上取决于Q和R矩阵的选择:
PYTHON
2
from scipy.linalg import solve_continuous_are
5
A = np.array([[0, 1, 0, 0],
16
Q = np.diag([10, 1, 5, 1])
20
P = solve_continuous_are(A, B, Q, R)
21
K = np.linalg.inv(R) @ B.T @ P
5.3 LQR控制器实现
C
9
float lqr_control(LQR_Controller* ctrl, float* x_desired) {
14
for(int i = 0; i < 4; i++) {
15
error[i] = x_desired[i] - ctrl->x[i];
18
// LQR控制律:u = -K·(x_desired - x_actual)
19
for(int i = 0; i < 4; i++) {
20
u -= ctrl->K[i] * error[i];
24
ctrl->u = constrain(u, -MAX_TORQUE, MAX_TORQUE);
29
void update_state_estimate(LQR_Controller* ctrl,
30
float pitch_angle, float pitch_rate,
31
float position, float velocity) {
32
ctrl->x[0] = pitch_angle;
33
ctrl->x[1] = pitch_rate;
34
ctrl->x[2] = position;
35
ctrl->x[3] = velocity;
6. PID控制器设计与整定
6.1 PID控制器结构
C
3
float Kp, Ki, Kd; // PID参数
5
float prev_error; // 上一次误差
7
float integral_limit; // 积分限幅
8
float output_limit; // 输出限幅
12
float pid_calculate(PID_Controller* pid, float setpoint, float measurement) {
13
float error = setpoint - measurement;
16
float proportional = pid->Kp * error;
19
pid->integral += error;
20
if(pid->integral > pid->integral_limit) {
21
pid->integral = pid->integral_limit;
22
} else if(pid->integral < -pid->integral_limit) {
23
pid->integral = -pid->integral_limit;
25
float integral = pid->Ki * pid->integral;
28
float derivative = pid->Kd * (error - pid->prev_error);
29
pid->prev_error = error;
32
pid->output = proportional + integral + derivative;
33
if(pid->output > pid->output_limit) {
34
pid->output = pid->output_limit;
35
} else if(pid->output < -pid->output_limit) {
36
pid->output = -pid->output_limit;
6.2 PID参数整定方法
试凑法整定步骤:
- 先整定P参数:将Ki和Kd设为0,逐渐增大Kp直到系统出现等幅振荡
- 记录临界参数:记下临界增益Kc和振荡周期Tc
- 使用Ziegler-Nichols公式:
- P控制:Kp = 0.5Kc
- PI控制:Kp = 0.45Kc, Ki = 1.2Kp/Tc
- PID控制:Kp = 0.6Kc, Ki = 2Kp/Tc, Kd = Kp*Tc/8
轮腿机器人电机PID典型参数范围:
- 位置环:Kp=1.0-5.0, Ki=0.01-0.1, Kd=0.05-0.2
- 速度环:Kp=10-50, Ki=0.1-1.0, Kd=0.1-0.5
- 电流环:Kp=100-500, Ki=10-50, Kd=1-5
6.3 增量式PID实现
对于数字控制系统,增量式PID可以避免积分饱和问题:
C
2
float incremental_pid(PID_Controller* pid, float error) {
3
static float prev_error2 = 0; // 上上次误差
5
float delta_u = pid->Kp * (error - pid->prev_error) +
7
pid->Kd * (error - 2*pid->prev_error + prev_error2);
9
prev_error2 = pid->prev_error;
10
pid->prev_error = error;
7. 系统集成与协同控制
7.1 控制架构设计
轮腿机器人采用分层控制架构:
7.2 实时控制循环
C
3
static uint32_t last_time = 0;
4
uint32_t current_time = HAL_GetTick();
6
if(current_time - last_time >= 1) { // 1ms周期
12
estimate_robot_state();
15
float desired_torque = lqr_control(&lqr_ctrl, desired_state);
18
calculate_joint_angles(desired_torque);
21
for(int i = 0; i < MOTOR_COUNT; i++) {
22
float current_cmd = pid_calculate(&pid_ctrl[i],
26
CAN_Send_Motor_Cmd(i, current_cmd);
29
last_time = current_time;
7.3 模式切换与安全保护
C
5
MODE_EMERGENCY // 紧急停止模式
13
if(all_joints_ready()) {
14
current_mode = MODE_STAND;
35
if(imu_tilt_exceed() || motor_overcurrent()) {
36
current_mode = MODE_EMERGENCY;
8. 仿真验证与实验调试
8.1 Webots仿真环境搭建
PYTHON
2
from controller import Robot, Motor, PositionSensor
8
self.timestep = int(self.robot.getBasicTimeStep())
16
self.pid_controllers = []
17
self.setup_controllers()
19
def setup_devices(self):
21
motor_names = ['hip_yaw', 'hip_roll', 'hip_pitch',
22
'knee', 'ankle_pitch', 'ankle_roll'] * 2
23
for name in motor_names:
24
motor = self.robot.getDevice(name)
25
motor.setPosition(float('inf'))
27
self.motors.append(motor)
29
def balance_control(self):
31
current_state = self.get_robot_state()
32
torque = self.lqr_control(current_state)
33
self.apply_control(torque)
8.2 实物调试步骤
- 单关节测试:逐个测试每个关节的PID控制性能
- 通信验证:确保CAN总线通信稳定,无丢包现象
- 静态平衡:在支撑平台上测试LQR平衡算法
- 动态测试:缓慢增加运动幅度,观察系统稳定性
- 异常处理:测试急停、掉电等异常情况的处理能力
8.3 性能评估指标
- 平衡稳定性:车身俯仰角波动范围(目标:±2°以内)
- 响应速度:从扰动恢复到平衡的时间(目标:<1秒)
- 能耗效率:单位距离的能量消耗
- 越障能力:可跨越的障碍高度与车身高度比
9. 常见问题与排查方法
9.1 CAN总线通信问题
| 问题现象 |
可能原因 |
排查方法 |
解决方案 |
| 无法接收到数据 |
波特率不匹配 |
检查两端波特率设置 |
统一波特率配置 |
| 数据包丢失 |
总线负载过高 |
监控总线负载率 |
优化发送频率,增加重传 |
| 通信干扰 |
终端电阻缺失 |
检查120Ω终端电阻 |
总线两端添加终端电阻 |
9.2 LQR控制不稳定
| 问题现象 |
可能原因 |
排查方法 |
解决方案 |
| 系统发散 |
Q矩阵权重不合理 |
检查状态权重分配 |
重新调整Q矩阵,增大角度权重 |
| 振荡严重 |
R矩阵权重过小 |
检查控制权重 |
增大R矩阵值,限制控制量 |
| 响应迟钝 |
模型不准 |
验证系统模型 |
重新辨识系统参数 |
9.3 PID控制性能问题
| 问题现象 |
可能原因 |
排查方法 |
解决方案 |
| 静差大 |
Ki参数过小 |
检查积分项效果 |
适当增大Ki,注意积分饱和 |
| 超调大 |
Kp参数过大 |
观察阶跃响应 |
减小Kp,增加Kd |
| 振荡不止 |
微分增益过小 |
分析振荡频率 |
适当增大Kd,改善阻尼 |
10. 最佳实践与优化建议
10.1 参数整定顺序
- 从内环到外环:先整定电流环,再速度环,最后位置环
- 先P后I再D:每个环都按照P→PI→PID的顺序整定
- 小幅度渐进:每次只调整一个参数,小幅度变化观察效果
10.2 实时性保障措施
- 使用DMA进行CAN数据收发,减少CPU占用
- 关键控制任务设置为高优先级RTOS任务
- 控制周期与传感器更新周期匹配
- 避免在中断服务程序中复杂计算
10.3 安全保护机制
C
2
void safety_monitor() {
4
if(joint_angle > MAX_ANGLE) {
9
if(motor_current > MAX_CURRENT) {
10
reduce_torque_command();
14
if(last_can_time > TIMEOUT_THRESHOLD) {
19
if(body_tilt > TILT_LIMIT) {
10.4 调试与日志记录
建议实现详细的数据记录功能,便于离线分析:
- 关键状态变量(角度、速度、电流)
- 控制命令与实际输出
- 传感器原始数据
- 系统异常事件
通过SD卡或无线传输保存日志数据,用于后续的性能分析和问题诊断。
轮腿机器人的开发是一个系统工程,CAN总线、LQR控制和PID控制这三个技术点的熟练掌握是项目成功的关键。在实际开发中,建议先从仿真开始验证算法有效性,然后逐步过渡到实物调试。每个环节都要做好充分的测试和验证,特别是安全保护机制必须完善可靠。