You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改ROS-Arduino桥接编码器代码适配4轮麦克纳姆驱动?

4轮麦克纳姆轮ROS2-Arduino编码器扩展问题

我跟着Articulated Robotics的ROS系列教程,用ros_arduino_bridge实现ROS2与Arduino Mega 2560的串口电机控制。机器人采用4轮麦克纳姆驱动,其他部分代码修改都很顺利,但编码器部分涉及Arduino底层的引脚中断操作,我不太熟悉,现在需要扩展成4个编码器的支持,对位掩码、移位逻辑还有中断资源的问题搞不清,求帮忙。

原核心代码片段

1. Setup函数中的编码器初始化

void setup() {
...
    // 设置为输入模式
    DDRD &= ~(1<<LEFT_ENC_PIN_A);
    DDRD &= ~(1<<LEFT_ENC_PIN_B);
    DDRC &= ~(1<<RIGHT_ENC_PIN_A);
    DDRC &= ~(1<<RIGHT_ENC_PIN_B);
    
    // 启用内部上拉电阻
    PORTD |= (1<<LEFT_ENC_PIN_A);
    PORTD |= (1<<LEFT_ENC_PIN_B);
    PORTC |= (1<<RIGHT_ENC_PIN_A);
    PORTC |= (1<<RIGHT_ENC_PIN_B);
    
    // 配置引脚变化中断掩码,监听左编码器引脚
    PCMSK2 |= (1 << LEFT_ENC_PIN_A)|(1 << LEFT_ENC_PIN_B);
    // 配置引脚变化中断掩码,监听右编码器引脚
    PCMSK1 |= (1 << RIGHT_ENC_PIN_A)|(1 << RIGHT_ENC_PIN_B);
    
    // 启用PCINT1和PCINT2中断
    PCICR |= (1 << PCIE1) | (1 << PCIE2);
...
}

2. encoder_driver.h的引脚定义

...
  // 左编码器需用PORTD引脚,否则要改代码逻辑
  #define LEFT_ENC_PIN_A PD2  // 对应Arduino引脚2
  #define LEFT_ENC_PIN_B PD3  // 对应Arduino引脚3
  
  // 右编码器需用PORTC引脚,否则要改代码逻辑
  #define RIGHT_ENC_PIN_A PC4  // 对应Arduino引脚A4
  #define RIGHT_ENC_PIN_B PC5   // 对应Arduino引脚A5
...

3. encoder_driver.ino的中断与计数逻辑

volatile long left_enc_pos = 0L;
  volatile long right_enc_pos = 0L;
  // 编码器状态查找表,根据前后两次状态组合返回计数增量
  static const int8_t ENC_STATES [] = {0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0};
    
  /* 左编码器中断服务函数 */
  ISR (PCINT2_vect){
    static uint8_t enc_last=0;
        
    enc_last <<=2; // 将前一次状态左移2位,腾出低2位放当前状态
    // 读取PORTD的PD2、PD3引脚状态,右移2位后放到低2位
    enc_last |= (PIND & (3 << 2)) >> 2;
  
    // 取低4位作为索引,累加计数增量
    left_enc_pos += ENC_STATES[(enc_last & 0x0f)];
  }
  
  /* 右编码器中断服务函数 */
  ISR (PCINT1_vect){
        static uint8_t enc_last=0;
            
    enc_last <<=2; // 将前一次状态左移2位
    // 读取PORTC的PC4、PC5引脚状态,右移4位后放到低2位
    enc_last |= (PINC & (3 << 4)) >> 4;
  
    right_enc_pos += ENC_STATES[(enc_last & 0x0f)];
  }
  
  /* 编码器读数封装函数 */
  long readEncoder(int i) {
    if (i == LEFT) return left_enc_pos;
    else return right_enc_pos;
  }
...

扩展4编码器的实现步骤

1. 新增编码器引脚定义

Arduino Mega的引脚变化中断(PCINT)分为3组,每组对应一个端口:

  • PCINT0(PCIE0)→ PORTA(Arduino引脚22-29)
  • PCINT1(PCIE1)→ PORTC(Arduino引脚A0-A5)
  • PCINT2(PCIE2)→ PORTD(Arduino引脚0-7)

在encoder_driver.h中新增两个编码器的引脚定义,选PORTA的引脚(对应PCINT0组):

// 新增前右、后左编码器引脚(PORTA对应PCINT0中断组)
#define FRONT_RIGHT_ENC_PIN_A PA0  // Arduino引脚22
#define FRONT_RIGHT_ENC_PIN_B PA1  // Arduino引脚23
#define BACK_LEFT_ENC_PIN_A PA2    // Arduino引脚24
#define BACK_LEFT_ENC_PIN_B PA3    // Arduino引脚25

2. 初始化新增编码器引脚

在Setup函数的编码器初始化部分后添加:

// 设置新增编码器引脚为输入模式
DDRA &= ~(1<<FRONT_RIGHT_ENC_PIN_A);
DDRA &= ~(1<<FRONT_RIGHT_ENC_PIN_B);
DDRA &= ~(1<<BACK_LEFT_ENC_PIN_A);
DDRA &= ~(1<<BACK_LEFT_ENC_PIN_B);

// 启用新增引脚的内部上拉电阻
PORTA |= (1<<FRONT_RIGHT_ENC_PIN_A);
PORTA |= (1<<FRONT_RIGHT_ENC_PIN_B);
PORTA |= (1<<BACK_LEFT_ENC_PIN_A);
PORTA |= (1<<BACK_LEFT_ENC_PIN_B);

// 配置PCMSK0掩码,监听新增的4个编码器引脚
PCMSK0 |= (1 << FRONT_RIGHT_ENC_PIN_A)|(1 << FRONT_RIGHT_ENC_PIN_B)|
          (1 << BACK_LEFT_ENC_PIN_A)|(1 << BACK_LEFT_ENC_PIN_B);

// 启用PCINT0中断组
PCICR |= (1 << PCIE0);

3. 扩展中断服务与计数变量

在encoder_driver.ino中添加新增编码器的计数变量和中断服务函数:

// 新增编码器计数变量,volatile确保中断中读写的原子性
volatile long front_right_enc_pos = 0L;
volatile long back_left_enc_pos = 0L;

/* PCINT0中断服务函数,处理PORTA上的两个编码器 */
ISR (PCINT0_vect){
    static uint8_t fr_enc_last=0;  // 前右编码器的前一次状态
    static uint8_t bl_enc_last=0;  // 后左编码器的前一次状态
    
    // 处理前右编码器(PA0、PA1)
    fr_enc_last <<=2;
    // 直接读取PA0、PA1的状态(掩码0x03对应低2位)
    fr_enc_last |= (PINA & 0x03);
    front_right_enc_pos += ENC_STATES[(fr_enc_last & 0x0f)];
    
    // 处理后左编码器(PA2、PA3)
    bl_enc_last <<=2;
    // 读取PA2、PA3的状态,右移2位后放到低2位
    bl_enc_last |= (PINA & 0x0C) >> 2;
    back_left_enc_pos += ENC_STATES[(bl_enc_last & 0x0f)];
}

// 修改readEncoder函数,支持4个编码器
#define LEFT 0
#define RIGHT 1
#define FRONT_RIGHT 2
#define BACK_LEFT 3

long readEncoder(int i) {
    switch(i){
        case LEFT: return left_enc_pos;
        case RIGHT: return right_enc_pos;
        case FRONT_RIGHT: return front_right_enc_pos;
        case BACK_LEFT: return back_left_enc_pos;
        default: return 0;
    }
}

4. 关键逻辑解释

  • 引脚模式与上拉电阻:DDRx &= ~(1<<PIN)将引脚设为输入;PORTx |= (1<<PIN)启用内部上拉,避免编码器信号悬空。
  • 引脚变化中断:PCINT监听端口内任意引脚的电平变化,通过PCMSKx掩码选择要监听的引脚,PCICR启用对应中断组。
  • 编码器计数逻辑:每次中断时,把前一次的2位状态左移,拼接当前的2位状态,形成4位的状态组合,用这个组合作为索引查ENC_STATES数组,得到计数增量(+1正转、-1反转、0防抖)。

5. Arduino Mega中断资源说明

Mega的3组PCINT每组支持最多8个引脚,完全能覆盖4个编码器的8个引脚,不存在资源不足的问题。如果用外部中断(INT0-INT5)才会有限制,但这里用的PCINT足够满足需求。

内容的提问来源于stack exchange,提问作者Bravish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 19:35:00