如何用python在树莓派实现舵机缓慢转动?(模块封装可适配接口)

本文介绍了如何使用Python在RaspberryPi上控制舵机,通过`gradual_move`函数实现舵机从一个角度平滑地逐渐移动到另一个角度,通过调整步长和延迟以适应舵机特性和需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.简介

       使舵机缓慢从160度转向5度,然后再缓慢转回160度,需要在`set_servo_angle`函数中添加一些逻辑来逐步改变角度,而不是直接设置为目标角度。可以通过在两个角度之间分步改变角度来实现这一点。

2.示例

       代码中,添加了一个函数`gradual_move`,它接受起始角度`start_angle`,结束角度`end_angle`,步长`step`和延迟`delay`作为参数。函数会逐步地从起始角度改变到结束角度,每次改变`step`指定的角度,并在每次改变后暂停`delay`指定的时间。这样,舵机就会缓慢地从一个角度移动到另一个角度。

(注意:步长`step`和延迟`delay`的值需要根据实际的舵机和你的需求进行调整。步长太大或延迟太短可能会导致舵机移动得太快,而步长太小或延迟太长可能会使舵机移动得太慢。)

import RPi.GPIO as GPIO 
import time 

# 设置GPIO模式为BCM 
GPIO.setmode(GPIO.BCM) 

# 设置GPIO引脚 
GPIO_SERVO = 18 

# 设置GPIO为输出 
GPIO.setup(GPIO_SERVO, GPIO.OUT) 

# 设置PWM频率为50Hz 
pwm = GPIO.PWM(GPIO_SERVO, 50) 

# 初始化PWM信号为0 
pwm.start(0) 

def set_servo_angle(angle): 
 # 计算占空比 
 duty_cycle = (0.05 * 50) + (0.19 * 50 * angle / 180) 

 # 更新占空比 
 pwm.ChangeDut
#include <reg51.h> #include <intrins.h> sbit SDA = P2^0; // I2C数据线 sbit SCL = P2^1; // I2C时钟线 #define PCA9685_ADDR 0x40 /* 全局变量 - 舵机控制相关 */ volatile unsigned long sys_time = 0; // 系统时间计数器(单位:ms) volatile bit timer_flag = 0; // 定时器溢出标志 unsigned long servo_start_time[4]; // 舵机动作开始时间 volatile unsigned char servo_active = 0; // 舵机激活状态 volatile unsigned char servo_target_angle[4] = {0}; // 目标角度缓冲区 unsigned char UART_Data = 0; /************************ I2C通信协议 ************************/ void I2C_Delay() { _nop_(); _nop_(); _nop_(); _nop_(); } void I2C_Start() { SDA = 1; I2C_Delay(); SCL = 1; I2C_Delay(); SDA = 0; I2C_Delay(); SCL = 0; I2C_Delay(); } void I2C_Stop() { SDA = 0; I2C_Delay(); SCL = 1; I2C_Delay(); SDA = 1; I2C_Delay(); } void I2C_WriteByte(unsigned char dat) { unsigned char i; for(i = 0; i < 8; i++) { SDA = (dat & 0x80) ? 1 : 0; dat <<= 1; SCL = 1; I2C_Delay(); SCL = 0; I2C_Delay(); } SDA = 1; SCL = 1; I2C_Delay(); SCL = 0; I2C_Delay(); } /************************ PCA9685驱动 ************************/ void PCA9685_Write(unsigned char reg, unsigned char dat) { I2C_Start(); I2C_WriteByte(PCA9685_ADDR << 1); I2C_WriteByte(reg); I2C_WriteByte(dat); I2C_Stop(); } void PCA9685_Init() { unsigned char ch; PCA9685_Write(0x00, 0x20); // 进入睡眠模式 PCA9685_Write(0xFE, 121); // 预分频值(50Hz,25MHz时钟) PCA9685_Write(0x00, 0xA0); // 退出睡眠模式 for (ch = 0; ch < 4; ch++) { PCA9685_Write(0x06 + 4*ch, 0x00); PCA9685_Write(0x07 + 4*ch, 0x00); PCA9685_Write(0x08 + 4*ch, 0x00); PCA9685_Write(0x09 + 4*ch, 0x00); } } void Set_PWM(unsigned char ch, unsigned int on, unsigned int off) { I2C_Start(); I2C_WriteByte(PCA9685_ADDR << 1); I2C_WriteByte(0x06 + 4*ch); I2C_WriteByte(on & 0xFF); I2C_WriteByte(on >> 8); I2C_WriteByte(off & 0xFF); I2C_WriteByte(off >> 8); I2C_Stop(); } /************************ 舵机控制函数(修正后) ************************/ void Servo_SetAngle(unsigned char ch, unsigned char angle) { // 脉冲范围:0.5ms~2.5ms(适配常见180度舵机) float pulse_min = 0.5; // 0度对应0.5ms float pulse_max = 2.5; // 180度对应2.5ms float pulse = pulse_min + (angle / 180.0) * (pulse_max - pulse_min); // 计算OFF值(周期20ms,4096步长) unsigned int off = (unsigned int)(pulse * 4096 / 20); Set_PWM(ch, 0, off); } /************************ 定时器T0初始化 ************************/ void Timer0_Init() { TMOD |= 0x01; // 定时器0模式1 TH0 = 0xFC; // 1ms定时(11.0592MHz) TL0 = 0x67; ET0 = 1; TR0 = 1; } /************************ 定时器T0中断服务 ************************/ void Timer0_ISR() interrupt 1 { TH0 = 0xFC; TL0 = 0x67; sys_time++; timer_flag = 1; } /************************ 串口通信 ************************/ void UART_Init() { TMOD = 0x21; TH1 = 0xFD; TL1 = 0xFD; TR1 = 1; SCON = 0x50; ES = 1; EA = 1; } void UART_ISR() interrupt 4 { if (RI) { RI = 0; UART_Data = SBUF; if (UART_Data >= '1' && UART_Data <= '4') { unsigned char ch = UART_Data - '1'; servo_target_angle[ch] = 90; // 设置目标角度为90度 servo_start_time[ch] = sys_time; servo_active |= (1 << ch); } } } /************************ 主函数 ************************/ void main() { unsigned char i; // 初始化 servo_active = 0; for(i=0; i<4; i++) { servo_start_time[i] = 0; servo_target_angle[i] = 0; } UART_Init(); PCA9685_Init(); Timer0_Init(); while(1) { if(timer_flag) { timer_flag = 0; // 处理舵机角度设置 for(i=0; i<4; i++) { if(servo_target_angle[i] != 0) { Servo_SetAngle(i, servo_target_angle[i]); servo_target_angle[i] = 0; } } // 检查超时并回零 for(i=0; i<4; i++) { if(servo_active & (1 << i)) { if((sys_time - servo_start_time[i]) >= 3000) { Servo_SetAngle(i, 0); // 转回0度 servo_active &= ~(1 << i); } } } } } }根据这个代码修改
最新发布
05-19
### 修改与优化舵机控制程序 以下是对现有代码的改进方案,主要集中在以下几个方面: 1. 提升I2C通信效率; 2. 改善PCA9685驱动模块的初始化过程; 3. 增强串口通信的鲁棒性; 4. 扩展功能以支持更多舵机的同时控制。 --- #### 一、提升I2C通信效率 当前代码中,每次写入寄存器时都会单独调用 `i2c.writeto_mem()` 方法。这种逐字节操作的方式虽然简单易懂,但在频繁更新多个通道的状态时显得低效。可以通过批量写入的方式来减少I2C事务次数。 ##### 修改后的代码片段: ```python def batch_write_pca9685(data_list): """批量向PCA9685写入数据""" buffer = bytearray() for reg, value in data_list: buffer.extend([reg, value]) i2c.writeto(PCA9685_ADDRESS, buffer) ``` 此方法允许一次性将多个寄存器及其对应的数据打包发送给PCA9685芯片[^1]。 --- #### 二、改善PCA9685驱动模块的初始化过程 原代码仅设置了PWM频率,并未充分考虑其他重要参数(如重启模式)。为了确保更稳定的运行环境,可以在初始化阶段增加额外配置步骤。 ##### 完整的初始化函数如下所示: ```python def initialize_pca9685(): """全面初始化PCA9685模块""" # 复位设备进入正常工作状态 mode1_reg = i2c.readfrom_mem(PCA9685_ADDRESS, MODE1, 1)[0] mode1_reg &= ~0x10 # 清除睡眠标志 i2c.writeto_mem(PCA9685_ADDRESS, MODE1, bytes([mode1_reg | 0xA1])) # 自动增量模式 # 设置预分频值计算公式:CLK/(4096*freq)-1 desired_freq = 50 prescale_val = int((25_000_000 / (4096 * desired_freq)) - 1) # 更新PRESCALE寄存器前需暂停振荡器 old_mode = i2c.readfrom_mem(PCA9685_ADDRESS, MODE1, 1)[0] new_mode = (old_mode & 0x7F) | 0x10 # 开启休眠模式 i2c.writeto_mem(PCA9685_ADDRESS, MODE1, bytes([new_mode])) i2c.writeto_mem(PCA9685_ADDRESS, PRESCALE, bytes([prescale_val])) # 恢复原有模式并启动振荡器 i2c.writeto_mem(PCA9685_ADDRESS, MODE1, bytes([old_mode])) time.sleep(0.005) i2c.writeto_mem(PCA9685_ADDRESS, MODE1, bytes([old_mode | 0x80])) # 重新开启输出 ``` 上述改动不仅简化了原有的多次独立访问行为,还引入了一些高级特性以便更好地管理整个系统性能[^2]。 --- #### 三、增强串口通信的鲁棒性 原始版本缺乏足够的错误检测机制,在真实环境中可能导致不可预见的行为发生。为此我们添加了一个简单的校验流程以及超时保护措施。 ##### 新增辅助工具类定义: ```python class UARTCommandHandler: def __init__(self, uart_instance): self.uart = uart_instance self.timeout_ms = 100 def send_acknowledgement(self, success=True): ack_message = "ACK" if success else "NACK" self.uart.write(f"{ack_message}\n".encode()) def receive_command_with_timeout(self): start_time = time.ticks_ms() while not self.uart.any() and time.ticks_diff(time.ticks_ms(), start_time) < self.timeout_ms: pass raw_data = self.uart.readline().strip().decode('utf-8') if self.uart.any() else None return raw_data or "" ``` 通过此类封装可以使主循环更加清晰简洁,同时也便于后续扩展其它交互形式。 --- #### 四、扩展功能以支持多舵机同步操控 最后一步就是让我们的解决方案具备处理复杂场景的能力——即允许多个伺服电机在同一时刻接受不同的指令集。这通常涉及到矩阵运算或者队列调度算法的应用。 这里提供一种基础实现思路:创建一个二维数组用来存储各时间段内所有活动单元的目标位置信息;然后遍历每一行依次执行相应的动作变换直至完成全程轨迹规划为止。 ##### 示例伪代码结构框架: ```python servos_positions = [[0]*num_servos]*total_steps # 初始化为空闲姿态序列列表 current_step_index = 0 while current_step_index < total_steps: target_angles_for_this_tick = servos_positions[current_step_index] for idx, angle_degrees in enumerate(target_angles_for_this_tick): set_servo_angle(idx, angle_degrees) current_step_index += 1 time.sleep(step_duration_seconds) ``` 这样就可以轻松达成动态调整任意数量轴心方向的目的啦! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值