Files
balance/u2can/motor_interface.py
ydy0615 74a2d8c355 new file: .vscode/settings.json
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
2025-12-06 22:47:57 +08:00

175 lines
6.9 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 总线
- IMUpybind_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.0s → 2.0s
controller = CPGController(base_height=0.0,
base_period=2.0,
amplitude=0.85,
stable_range=6.0)
# 初始位置Motor1、Motor4 在 -0.85Motor2、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.85Motor2、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()