new file: Legs_controller.py modified: README.md new file: __pycache__/Legs_controller.cpython-310.pyc new file: __pycache__/Legs_controller.cpython-313.pyc new file: __pycache__/balance.cpython-310.pyc new file: __pycache__/gradio.cpython-310.pyc new file: __pycache__/gradio.cpython-313.pyc new file: app_ui.py new file: balance.py new file: build/.cmake/api/v1/query/client-vscode/query.json new file: build/.cmake/api/v1/reply/cache-v2-ae4a9db768b4bbb36baa.json new file: build/.cmake/api/v1/reply/cmakeFiles-v1-389eb8769a8295e7571d.json new file: build/.cmake/api/v1/reply/codemodel-v2-5ea8cfc0b9263cbe5ae5.json new file: build/.cmake/api/v1/reply/directory-.-Debug-f5ebdc15457944623624.json new file: build/.cmake/api/v1/reply/index-2025-09-11T04-53-20-0515.json new file: build/.cmake/api/v1/reply/target-imu_py-Debug-3913d741f2156d7faae9.json new file: build/.cmake/api/v1/reply/toolchains-v1-3105704d088db7adbfb5.json new file: build/CMakeCache.txt new file: build/CMakeFiles/3.22.1/CMakeCXXCompiler.cmake new file: build/CMakeFiles/3.22.1/CMakeDetermineCompilerABI_CXX.bin new file: build/CMakeFiles/3.22.1/CMakeSystem.cmake new file: build/CMakeFiles/3.22.1/CompilerIdCXX/CMakeCXXCompilerId.cpp new file: build/CMakeFiles/3.22.1/CompilerIdCXX/a.out new file: build/CMakeFiles/CMakeDirectoryInformation.cmake new file: build/CMakeFiles/CMakeOutput.log new file: build/CMakeFiles/Makefile.cmake new file: build/CMakeFiles/Makefile2 new file: build/CMakeFiles/TargetDirectories.txt new file: build/CMakeFiles/cmake.check_cache new file: build/CMakeFiles/imu_py.dir/DependInfo.cmake new file: build/CMakeFiles/imu_py.dir/build.make new file: build/CMakeFiles/imu_py.dir/cmake_clean.cmake new file: build/CMakeFiles/imu_py.dir/compiler_depend.make new file: build/CMakeFiles/imu_py.dir/compiler_depend.ts new file: build/CMakeFiles/imu_py.dir/depend.make new file: build/CMakeFiles/imu_py.dir/flags.make new file: build/CMakeFiles/imu_py.dir/link.txt new file: build/CMakeFiles/imu_py.dir/progress.make new file: build/CMakeFiles/progress.marks new file: build/Makefile new file: build/cmake_install.cmake new file: build/compile_commands.json new file: dm_imu/bsp_crc.cpp new file: dm_imu/bsp_crc.h new file: dm_imu/imu_data.csv new file: dm_imu/imu_driver.cpp new file: dm_imu/imu_driver.h new file: dm_imu/imu_plot.png new file: dm_imu/plot_imu.py new file: dm_imu/test_imu.cpp new file: imu_data.csv new file: pybind_imu/CMakeLists.txt new file: pybind_imu/README.md new file: pybind_imu/build/CMakeCache.txt new file: pybind_imu/build/CMakeFiles/3.22.1/CMakeCXXCompiler.cmake new file: pybind_imu/build/CMakeFiles/3.22.1/CMakeDetermineCompilerABI_CXX.bin new file: pybind_imu/build/CMakeFiles/3.22.1/CMakeSystem.cmake new file: pybind_imu/build/CMakeFiles/3.22.1/CompilerIdCXX/CMakeCXXCompilerId.cpp new file: pybind_imu/build/CMakeFiles/3.22.1/CompilerIdCXX/a.out new file: pybind_imu/build/CMakeFiles/CMakeDirectoryInformation.cmake new file: pybind_imu/build/CMakeFiles/CMakeOutput.log new file: pybind_imu/build/CMakeFiles/Makefile.cmake new file: pybind_imu/build/CMakeFiles/Makefile2 new file: pybind_imu/build/CMakeFiles/TargetDirectories.txt new file: pybind_imu/build/CMakeFiles/cmake.check_cache new file: pybind_imu/build/CMakeFiles/imu_py.dir/DependInfo.cmake new file: pybind_imu/build/CMakeFiles/imu_py.dir/build.make new file: pybind_imu/build/CMakeFiles/imu_py.dir/cmake_clean.cmake new file: pybind_imu/build/CMakeFiles/imu_py.dir/compiler_depend.make new file: pybind_imu/build/CMakeFiles/imu_py.dir/compiler_depend.ts new file: pybind_imu/build/CMakeFiles/imu_py.dir/depend.make new file: pybind_imu/build/CMakeFiles/imu_py.dir/flags.make new file: pybind_imu/build/CMakeFiles/imu_py.dir/home/allenyuan/balance/dm_imu/bsp_crc.cpp.o new file: pybind_imu/build/CMakeFiles/imu_py.dir/home/allenyuan/balance/dm_imu/bsp_crc.cpp.o.d new file: pybind_imu/build/CMakeFiles/imu_py.dir/home/allenyuan/balance/dm_imu/imu_driver.cpp.o new file: pybind_imu/build/CMakeFiles/imu_py.dir/home/allenyuan/balance/dm_imu/imu_driver.cpp.o.d new file: pybind_imu/build/CMakeFiles/imu_py.dir/link.txt new file: pybind_imu/build/CMakeFiles/imu_py.dir/progress.make new file: pybind_imu/build/CMakeFiles/imu_py.dir/pybind_imu.cpp.o new file: pybind_imu/build/CMakeFiles/imu_py.dir/pybind_imu.cpp.o.d new file: pybind_imu/build/CMakeFiles/progress.marks new file: pybind_imu/build/Makefile new file: pybind_imu/build/cmake_install.cmake new file: pybind_imu/build/imu_py.cpython-310-aarch64-linux-gnu.so new file: pybind_imu/example.py new file: pybind_imu/pybind_imu.cpp new file: src/example.py new file: test_imu new file: u2can/DM_CAN.py new file: u2can/DM_Motor_Test.py new file: u2can/LICENSE new file: u2can/README.md new file: u2can/__pycache__/DM_CAN.cpython-310.pyc new file: u2can/__pycache__/DM_CAN.cpython-312.pyc new file: u2can/__pycache__/DM_CAN.cpython-313.pyc new file: u2can/__pycache__/gradio.cpython-313.pyc new file: u2can/__pycache__/motor_interface.cpython-313.pyc new file: u2can/motor_interface.py new file: u2can/requirements.txt new file: ui.log
175 lines
6.9 KiB
Python
175 lines
6.9 KiB
Python
import time
|
||
import numpy as np
|
||
import serial
|
||
import imu_py # Real IMU wrapper
|
||
from .DM_CAN import Motor, MotorControl, DM_Motor_Type
|
||
from cpg import CPGController
|
||
|
||
# -------------------------------------------------------------------------------
|
||
# 硬件初始化(串口、IMU、四条腿电机)
|
||
# -------------------------------------------------------------------------------
|
||
serial_dev = None
|
||
imu = None
|
||
motor_ctrl = None
|
||
motors = {}
|
||
|
||
# 全局滑动窗口,用于对 IMU 原始数据进行低通滤波(移动平均)
|
||
_pitch_window = []
|
||
_yaw_window = []
|
||
_roll_window = []
|
||
_WINDOW_SIZE = 5 # 窗口长度,可根据噪声水平调节
|
||
|
||
def _moving_average(window, new_value):
|
||
"""将 new_value 加入窗口并返回窗口均值"""
|
||
window.append(new_value)
|
||
if len(window) > _WINDOW_SIZE:
|
||
window.pop(0)
|
||
return float(np.mean(window))
|
||
|
||
def _init_hardware():
|
||
"""
|
||
初始化真实硬件:
|
||
- 串口用于 CAN 总线
|
||
- IMU(pybind_imu)提供陀螺仪角速度
|
||
- 四条腿电机对象并使能
|
||
"""
|
||
global serial_dev, imu, motor_ctrl, motors
|
||
|
||
# 1. 串口(根据实际设备路径修改)
|
||
serial_dev = serial.Serial('/dev/ttyACM1', 921600, timeout=0.5)
|
||
|
||
# 2. IMU(使用 pybind_imu 包)
|
||
imu = imu_py.DmImu("/dev/ttyACM0", 921600)
|
||
imu.start()
|
||
|
||
# 3. 电机控制对象
|
||
motor_ctrl = MotorControl(serial_dev)
|
||
|
||
# 4. 创建四条腿电机实例(DM4340 为示例型号,可根据实际更改)
|
||
Motor1 = Motor(DM_Motor_Type.DM4340, 0x01, 0x11)
|
||
Motor2 = Motor(DM_Motor_Type.DM4340, 0x02, 0x12)
|
||
Motor3 = Motor(DM_Motor_Type.DM4340, 0x03, 0x13)
|
||
Motor4 = Motor(DM_Motor_Type.DM4340, 0x04, 0x14)
|
||
|
||
# 5. 将电机加入控制器并使能
|
||
motor_ctrl.addMotor(Motor1)
|
||
motor_ctrl.addMotor(Motor2)
|
||
motor_ctrl.addMotor(Motor3)
|
||
motor_ctrl.addMotor(Motor4)
|
||
|
||
motor_ctrl.enable(Motor1)
|
||
motor_ctrl.enable(Motor2)
|
||
motor_ctrl.enable(Motor3)
|
||
motor_ctrl.enable(Motor4)
|
||
|
||
# 6. 保存到全局字典,便于后续索引
|
||
motors = {
|
||
'Motor1': Motor1,
|
||
'Motor2': Motor2,
|
||
'Motor3': Motor3,
|
||
'Motor4': Motor4
|
||
}
|
||
|
||
# -------------------------------------------------------------------------------
|
||
# 主循环:读取 IMU → 更新 CPG → 发送电机指令
|
||
# -------------------------------------------------------------------------------
|
||
def run(duration: float = 60.0, dt: float = 0.01):
|
||
"""
|
||
运行四足平衡控制,使用真实 IMU 数据。
|
||
:param duration: 运行时长(秒)
|
||
:param dt: 控制循环时间步长(秒)
|
||
"""
|
||
# 硬件初始化(只在第一次调用时执行)
|
||
if serial_dev is None or imu is None or motor_ctrl is None:
|
||
_init_hardware()
|
||
|
||
# 使用更长的基准周期以降低振荡频率(原 1.0 s → 2.0 s)
|
||
controller = CPGController(base_height=0.0,
|
||
base_period=2.0,
|
||
amplitude=0.85,
|
||
stable_range=6.0)
|
||
|
||
# 初始位置:Motor1、Motor4 在 -0.85,Motor2、Motor3 在 +0.85
|
||
h1 = -0.85
|
||
h2 = 0.85
|
||
h3 = 0.85
|
||
h4 = -0.85
|
||
t = 0.0
|
||
print(f"t={t:.2f}s deviation={0.0:.3f} "
|
||
f"h1={h1:.3f} h2={h2:.3f} h3={h3:.3f} h4={h4:.3f}")
|
||
motor_ctrl.control_Pos_Vel(motors['Motor1'], h1, 12)
|
||
motor_ctrl.control_Pos_Vel(motors['Motor2'], h2, 12)
|
||
motor_ctrl.control_Pos_Vel(motors['Motor3'], h3, 12)
|
||
motor_ctrl.control_Pos_Vel(motors['Motor4'], h4, 12)
|
||
|
||
# 保持初始位置一段时间后再开始 CPG 循环
|
||
time.sleep(0.5)
|
||
|
||
# 重置时间计数
|
||
start_time = time.time()
|
||
# 将 CPG 时间同步到最高点,以保持平滑运动
|
||
controller.t = controller.base_period / 4
|
||
|
||
while time.time() - start_time < duration:
|
||
# 1️⃣ 从真实 IMU 读取角速度(单位:度/秒)
|
||
data = imu.getData()
|
||
pitch_raw = data.get('gyroy', 0.0)
|
||
yaw_raw = data.get('gyroz', 0.0)
|
||
roll_raw = data.get('gyrox', 0.0)
|
||
|
||
# 对原始 IMU 数据进行移动平均滤波
|
||
pitch_rate = _moving_average(_pitch_window, pitch_raw)
|
||
yaw_rate = _moving_average(_yaw_window, yaw_raw)
|
||
roll_rate = _moving_average(_roll_window, roll_raw)
|
||
|
||
print(f"IMU data: pitch_rate={pitch_rate:.3f}, yaw_rate={yaw_rate:.3f}, roll_rate={roll_rate:.3f}")
|
||
|
||
# 2️⃣ CPG 步进,更新四条腿的目标高度,返回偏差用于调试
|
||
deviation = controller.step(pitch_rate, yaw_rate, dt)
|
||
|
||
# 3️⃣ 读取四条腿的目标高度(范围在 [-0.85, 0.85])
|
||
heights = controller.get_leg_heights() # {'FL':h1, 'FR':h2, 'RL':h3, 'RR':h4}
|
||
|
||
# 4️⃣ 将高度映射到电机指令范围(结合 CPG 输出和 IMU 角速度进行微调,保持最高点)
|
||
# - 基准高度:Motor1、Motor4 为 -0.85,Motor2、Motor3 为 +0.85
|
||
# - factor_cpg 控制 CPG 微调幅度(默认 0.2)
|
||
# - factor_imu 控制俯仰/偏航角速度反馈幅度(默认 0.05)
|
||
# - factor_roll 控制横滚(左右倾斜)反馈幅度(已增大至 0.12)
|
||
factor_cpg = 0.2
|
||
factor_imu = 0.05
|
||
factor_roll = 0.12
|
||
|
||
# 基于 IMU 角速度的额外微调
|
||
imu_correction_1 = -factor_imu * pitch_rate - factor_imu * yaw_rate - factor_roll * roll_rate # Motor1
|
||
imu_correction_2 = factor_imu * pitch_rate + factor_imu * yaw_rate + factor_roll * roll_rate # Motor2
|
||
imu_correction_3 = factor_imu * pitch_rate - factor_imu * yaw_rate + factor_roll * roll_rate # Motor3
|
||
imu_correction_4 = -factor_imu * pitch_rate + factor_imu * yaw_rate - factor_roll * roll_rate # Motor4
|
||
|
||
# 将 CPG 输出(heights)与基准高度、IMU 修正相结合后进行裁剪
|
||
h1 = np.clip(-0.85 + factor_cpg * heights['FL'] + imu_correction_1, -0.85, 0.0) # Motor1 负向
|
||
h2 = np.clip( 0.85 + factor_cpg * heights['FR'] + imu_correction_2, 0.0, 0.85) # Motor2 正向
|
||
h3 = np.clip( 0.85 + factor_cpg * heights['RL'] + imu_correction_3, 0.0, 0.85) # Motor3 正向
|
||
h4 = np.clip(-0.85 + factor_cpg * heights['RR'] + imu_correction_4, -0.85, 0.0) # Motor4 负向
|
||
|
||
# 5️⃣ 调试输出(显示实际发送给电机的高度,已裁剪到合法区间)
|
||
t = time.time() - start_time
|
||
print(f"t={t:.2f}s deviation={deviation:.3f} "
|
||
f"h1={h1:.3f} h2={h2:.3f} h3={h3:.3f} h4={h4:.3f}")
|
||
|
||
# 6️⃣ 发送位置指令
|
||
motor_ctrl.control_Pos_Vel(motors['Motor1'], h1, 12)
|
||
motor_ctrl.control_Pos_Vel(motors['Motor2'], h2, 12)
|
||
motor_ctrl.control_Pos_Vel(motors['Motor3'], h3, 12)
|
||
motor_ctrl.control_Pos_Vel(motors['Motor4'], h4, 12)
|
||
|
||
# 7️⃣ 控制循环频率
|
||
time.sleep(dt)
|
||
|
||
# ------------------- 安全停机 -------------------
|
||
for m in motors.values():
|
||
motor_ctrl.disable(m)
|
||
if serial_dev:
|
||
serial_dev.close()
|
||
if imu:
|
||
imu.stop()
|