深色模式
第 4 章:控制整车四向移动、转弯与原地旋转
4.1 模块原理:麦克纳姆轮
普通车轮只能沿滚动方向前进或后退。麦克纳姆轮的轮毂上安装有一圈与轮轴呈约 45° 夹角的从动小滚轮,轮子转动时,滚轮会将部分驱动力分解到斜向。四个轮子按不同的转向组合,各轮的斜向分力相互抵消或叠加,整车即可在不改变车头方向的前提下实现左右平移、45° 斜移及原地旋转。

4.2 全向移动轮态速查表(八向平移 + 原地旋转)
下表为本章核心。"正转"指轮子向前滚动(前进管脚高电平、后退管脚低电平),"反转"与之相反,"停止"指两管脚电平相同。
| 整车动作 | 左前轮 | 右前轮 | 左后轮 | 右后轮 |
|---|---|---|---|---|
| 前进 | 正转 | 正转 | 正转 | 正转 |
| 后退 | 反转 | 反转 | 反转 | 反转 |
| 左平移 | 反转 | 正转 | 正转 | 反转 |
| 右平移 | 正转 | 反转 | 反转 | 正转 |
| 左前 45° 平移 | 停止 | 正转 | 正转 | 停止 |
| 右前 45° 平移 | 正转 | 停止 | 停止 | 正转 |
| 左后 45° 平移 | 反转 | 停止 | 停止 | 反转 |
| 右后 45° 平移 | 停止 | 反转 | 反转 | 停止 |
| 原地左转 | 反转 | 正转 | 反转 | 正转 |
| 原地右转 | 正转 | 反转 | 正转 | 反转 |

**比亚迪仰望U8的原地掉头,正是麦克纳姆轮四驱车的基本功能。**四轮全向小车共十种运动状态,上图中后四种斜移(左前、右前、左后、右后)因轮地摩擦导致定位精度不足,本教程不予采用。编程大赛的高精度场景下,建议以"平移+直行"组合替代,例如"左平移+前进"即可等效左前斜移,且定位更精准。
规律总结:
前进 / 后退:四轮同向转动,详细代码已在3.5代码实现中已示例;
**左右转弯:**前进时,左右轮转速一致则直行;左轮转速高于右轮则右转;右轮转速高于左轮则左转。
平移:两条对角线上的轮子各为一组(左前 + 右后为一组,右前 + 左后为一组),两组反向转动;左平移时"右前、左后"正转,右平移与之相反;
原地旋转:左侧两轮与右侧两轮反向转动;旋转方向一侧的轮子反转。
注意:个别小车因电机安装方向或接线顺序不同,表中"正转 / 反转"可能整体对调。处理方法:先运行 3.4 节的前进后退程序,对方向不正确的轮子,互换其两根信号线,或在程序中对调该轮两个管脚的电平。以实测结果为准。
4.3 代码实现:方向函数封装
4.3.1 刹车函数
任何车辆,先能刹停,方能安全调试。

python
def stop_all():
# 八个管脚全部输出 0:每个轮子两脚电势差都为 0,四轮同时停转
# 这里不用 move(0),因为 move 只走四个轮函数,stop_all 把 8 个脚显式清零,
# 即使程序其他部分改乱了脚位状态,也能一次刹干净
pwm(GP2, 0)
pwm(GP3, 0)
pwm(GP4, 0)
pwm(GP5, 0)
pwm(GP6, 0)
pwm(GP7, 0)
pwm(GP8, 0)
pwm(GP9, 0)cpp
void stop_all() {
// 八个管脚全部输出 0:每个轮子两脚电势差都为 0,四轮同时停转
// 这里不用 move(0),stop_all 把 8 个脚显式清零,刹得更彻底
analogWrite(2, 0);
analogWrite(3, 0);
analogWrite(4, 0);
analogWrite(5, 0);
analogWrite(6, 0);
analogWrite(7, 0);
analogWrite(8, 0);
analogWrite(9, 0);
}4.3.2 转弯封装函数
任何车辆,开起来的时候不能只走直线,要能左右转弯。

python
def turn1(e):
# 差速转弯:左、右两侧轮速不一样,车就向慢的一侧偏转
# e = 1 右转弯;e = -1 左转弯;e = 0 直行(两侧同速)
# e 也可以取小数,比如 0.5 就是"半弯",弯道更平缓
sp = 150 # 基础速度:两侧轮速都围绕它上下浮动
p = 80 # 差速幅度:e 每变化 1,两侧速度一增一减各 80
sp_left = sp + p * e # 左侧速度 = 基础速度 + 差速
sp_right = sp - p * e # 右侧速度 = 基础速度 - 差速
# 以 e = 1(右转)为例:左轮 230、右轮 70,
# 左快右慢 → 车头向右偏,符合直觉
wheel_left_1(sp_left)
wheel_left_2(sp_left)
wheel_right_1(sp_right)
wheel_right_2(sp_right)cpp
void turn1(float e) {
// 差速转弯:左、右两侧轮速不一样,车就向慢的一侧偏转
// e = 1 右转弯;e = -1 左转弯;e = 0 直行(两侧同速)
// e 也可以取小数,比如 0.5 就是"半弯",弯道更平缓
int sp = 150; // 基础速度:两侧轮速都围绕它上下浮动
int p = 80; // 差速幅度:e 每变化 1,两侧速度一增一减各 80
int sp_left = sp + p * e; // 左侧速度 = 基础速度 + 差速
int sp_right = sp - p * e; // 右侧速度 = 基础速度 - 差速
// 以 e = 1(右转)为例:左轮 230、右轮 70,左快右慢 → 车头向右偏
wheel_left_1(sp_left);
wheel_left_2(sp_left);
wheel_right_1(sp_right);
wheel_right_2(sp_right);
}以上函数封装了3.5中四个轮子的基础代码:wheel_left_1,wheel_left_2,wheel_right_1,wheel_right_2,该四个函数的详细代码请移步到3.5中查看。
4.3.3 四向移动封装函数

python
def go_sp(sp, dir):
if dir == 1:
# dir = 1:前后移动。四轮同速同向
# sp > 0 前进;sp < 0 后退
wheel_left_1(sp)
wheel_left_2(sp)
wheel_right_1(sp)
wheel_right_2(sp)
elif dir == 2:
# dir = 2:左右平移(麦克纳姆轮独有的动作)
# 左前轮与右后轮一组、左后轮与右前轮一组,两组反向转动:
# 每个轮子产生的都是斜向力,对角两组斜向力相互抵消前后分量,
# 只合成出纯横向的力,车身不转、车头不偏,整体横着走
# sp > 0 向左平移;sp < 0 向右平移
wheel_left_1(-1 * sp)
wheel_left_2(sp)
wheel_right_1(sp)
wheel_right_2(-1 * sp)cpp
void go_sp(int sp, int dir) {
if (dir == 1) {
// dir = 1:前后移动。四轮同速同向
// sp > 0 前进;sp < 0 后退
wheel_left_1(sp);
wheel_left_2(sp);
wheel_right_1(sp);
wheel_right_2(sp);
} else if (dir == 2) {
// dir = 2:左右平移(麦克纳姆轮独有的动作)
// 左前轮与右后轮一组、左后轮与右前轮一组,两组反向转动:
// 对角两组斜向力相互抵消前后分量,只合成纯横向的力,车身整体横移
// sp > 0 向左平移;sp < 0 向右平移
wheel_left_1(-1 * sp);
wheel_left_2(sp);
wheel_right_1(sp);
wheel_right_2(-1 * sp);
}
}4.3.4 原地左右旋转封装函数

python
def turn_angle(angle):
# 原地旋转指定角度:angle > 0 原地左转;angle < 0 原地右转
# 思路:左、右两侧轮速大小相等、方向相反,车就绕自身中心打转
angle_n = angle / abs(angle) # 取符号:只保留方向(+1 或 -1),数值大小后面单独算
start_time = time.ticks_ms() # 记下开始转动的系统时间(毫秒)
# 时间换角度:实测标定,速度 200 时小车约每 10 毫秒转过 1°
# 所以要转 |angle| 度,就需要 |angle| × 10 毫秒
# 用 ticks_diff 相减而不是直接相减,可以正确处理毫秒计数器回绕
while time.ticks_diff(time.ticks_ms(), start_time) < abs(angle) * 10:
# 以 angle > 0(左转)为例:angle_n = 1
# 左侧两轮 = -200(反转向后),右侧两轮 = +200(正转向前)
# 右前左后 → 车身俯视逆时针原地打转 = 左转
wheel_left_1(angle_n * -200)
wheel_left_2(angle_n * -200)
wheel_right_1(angle_n * 200)
wheel_right_2(angle_n * 200)
# 时间到,四轮归零停车
wheel_left_1(0)
wheel_left_2(0)
wheel_right_1(0)
wheel_right_2(0)cpp
void turn_angle(int angle) {
// 原地旋转指定角度:angle > 0 原地左转;angle < 0 原地右转
// 思路:左、右两侧轮速大小相等、方向相反,车就绕自身中心打转
int angle_n = angle / abs(angle); // 取符号:只保留方向(+1 或 -1)
unsigned long start_time = millis(); // 记下开始转动的系统时间(毫秒)
// 时间换角度:实测标定,速度 200 时约每 10 毫秒转过 1°
// 所以要转 |angle| 度,就需要 |angle| × 10 毫秒
while (millis() - start_time < (unsigned long)abs(angle) * 10) {
// 以 angle > 0(左转)为例:左侧两轮反转向后,右侧两轮正转向前
wheel_left_1(angle_n * -200);
wheel_left_2(angle_n * -200);
wheel_right_1(angle_n * 200);
wheel_right_2(angle_n * 200);
}
// 时间到,四轮归零停车
wheel_left_1(0);
wheel_left_2(0);
wheel_right_1(0);
wheel_right_2(0);
}掌握本章各向运动控制,即可根据传感器数据,让车身在任意任务中自由移动。
