xyg@xyg-T6AD:~$ cd ~/ros2_ws
colcon build --packages-select pca9685_hardware
source install/setup.bash
Starting >>> pca9685_hardware
--- stderr: pca9685_hardware
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:9:1: error: ‘CallbackReturn’ does not name a type
9 | CallbackReturn Pca9685SystemHardware::on_init(const hardware_interface::HardwareInfo& info)
| ^~~~~~~~~~~~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp: In member function ‘virtual std::vector<hardware_interface::StateInterface> pca9685_hardware::Pca9685SystemHardware::export_state_interfaces()’:
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:44:37: error: ‘HW_IF_POSITION’ is not a member of ‘hardware_interface’
44 | hardware_interface::HW_IF_POSITION,
| ^~~~~~~~~~~~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp: In member function ‘virtual std::vector<hardware_interface::CommandInterface> pca9685_hardware::Pca9685SystemHardware::export_command_interfaces()’:
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:59:37: error: ‘HW_IF_POSITION’ is not a member of ‘hardware_interface’
59 | hardware_interface::HW_IF_POSITION,
| ^~~~~~~~~~~~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp: At global scope:
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:67:1: error: ‘CallbackReturn’ does not name a type
67 | CallbackReturn Pca9685SystemHardware::on_activate(const rclcpp_lifecycle::State& previous_state)
| ^~~~~~~~~~~~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:126:1: error: ‘CallbackReturn’ does not name a type
126 | CallbackReturn Pca9685SystemHardware::on_deactivate(const rclcpp_lifecycle::State& previous_state)
| ^~~~~~~~~~~~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp: In member function ‘virtual hardware_interface::return_type pca9685_hardware::Pca9685SystemHardware::read(const rclcpp::Time&, const rclcpp::Duration&)’:
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:152:81: warning: unused parameter ‘time’ [-Wunused-parameter]
152 | return_type Pca9685SystemHardware::read(const rclcpp::Time& time, const rclcpp::Duration& period)
| ~~~~~~~~~~~~~~~~~~~~^~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:152:111: warning: unused parameter ‘period’ [-Wunused-parameter]
152 | ardware::read(const rclcpp::Time& time, const rclcpp::Duration& period)
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp: In member function ‘virtual hardware_interface::return_type pca9685_hardware::Pca9685SystemHardware::write(const rclcpp::Time&, const rclcpp::Duration&)’:
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:159:82: warning: unused parameter ‘time’ [-Wunused-parameter]
159 | eturn_type Pca9685SystemHardware::write(const rclcpp::Time& time, const rclcpp::Duration& period)
| ~~~~~~~~~~~~~~~~~~~~^~~~
/home/xyg/ros2_ws/src/pca9685_hardware/src/pca9685_system_hardware.cpp:159:112: warning: unused parameter ‘period’ [-Wunused-parameter]
159 | rdware::write(const rclcpp::Time& time, const rclcpp::Duration& period)
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
gmake[2]: *** [CMakeFiles/pca9685_hardware.dir/build.make:76:CMakeFiles/pca9685_hardware.dir/src/pca9685_system_hardware.cpp.o] 错误 1
gmake[1]: *** [CMakeFiles/Makefile2:137:CMakeFiles/pca9685_hardware.dir/all] 错误 2
gmake: *** [Makefile:146:all] 错误 2
---
Failed <<< pca9685_hardware [2.25s, exited with code 2]
Summary: 0 packages finished [2.39s]
1 package failed: pca9685_hardware
1 package had stderr output: pca9685_hardware
not found: "/home/xyg/ros2_ws/install/pca9685_hardware/share/pca9685_hardware/local_setup.bash"
xyg@xyg-T6AD:~/ros2_ws$ ros2 control list_hardware_components | grep PCA9685
# 应输出: pca9685_hardware/Pca9685SystemHardware
[INFO] [1750735736.192545351] [_ros2cli_16699]: waiting for service /controller_manager/list_hardware_components to become available...
[WARN] [1750735746.210548769] [_ros2cli_16699]: Could not contact service /controller_manager/list_hardware_components
[INFO] [1750735746.210767288] [_ros2cli_16699]: waiting for service /controller_manager/list_hardware_components to become available...
^C
以下分别是pca9685_params.yaml:pca9685_hardware:
ros__parameters:
hardware:
plugin: "pca9685_hardware/Pca9685SystemHardware"
i2c_bus: "i2c-1" # 移除/dev/前缀,代码中会自动添加
i2c_address: "0x33" # I2C地址保持不变
channel_mapping: "5,7,9,11,13,15" # 使用逗号分隔的字符串格式
joints:
- joint1 # 关节名称与机器人结构对应
- joint2
- joint3
- joint4
- joint5
- joint6然后是pca9685_system_hardware.hpp:#ifndef PCA9685_HARDWARE__PCA9685_SYSTEM_HARDWARE_HPP_
#define PCA9685_HARDWARE__PCA9685_SYSTEM_HARDWARE_HPP_
#include <hardware_interface/types/hardware_interface_return_values.hpp>
#include <hardware_interface/system_interface.hpp>
#include <rclcpp_lifecycle/state.hpp>
#include <rclcpp/logger.hpp>
#include <memory>
#include <vector>
#include <string>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <unistd.h>
namespace pca9685_hardware
{
class Pca9685SystemHardware : public hardware_interface::SystemInterface
{
public:
using CallbackReturn = hardware_interface::CallbackReturn;
using ReturnType = hardware_interface::return_type;
CallbackReturn on_init(const hardware_interface::HardwareInfo& info) override;
std::vector<hardware_interface::StateInterface> export_state_interfaces() override;
std::vector<hardware_interface::CommandInterface> export_command_interfaces() override;
CallbackReturn on_activate(const rclcpp_lifecycle::State& previous_state) override;
CallbackReturn on_deactivate(const rclcpp_lifecycle::State& previous_state) override;
ReturnType read(const rclcpp::Time& time, const rclcpp::Duration& period) override;
ReturnType write(const rclcpp::Time& time, const rclcpp::Duration& period) override;
private:
int i2c_fd_;
std::string i2c_bus_;
int i2c_address_;
std::vector<int> channel_mapping_;
std::vector<double> hw_commands_;
std::vector<double> hw_states_;
bool write_byte_data(int reg, uint8_t value);
bool write_word_data(int reg, uint16_t value);
uint8_t read_byte_data(int reg);
};
} // namespace pca9685_hardware
#endif // PCA9685_HARDWARE__PCA9685_SYSTEM_HARDWARE_HPP_
然后是:pca9685_hardware_plugin.xml:<library path="libpca9685_hardware">
<class name="pca9685_hardware/Pca9685SystemHardware"
type="pca9685_hardware::Pca9685SystemHardware"
base_class_type="hardware_interface::SystemInterface">
<description>PCA9685 PWM控制器硬件接口</description>
</class>
</library>
还有:pca9685_system_hardware.cpp:#include "pca9685_hardware/pca9685_system_hardware.hpp"
#include <hardware_interface/types/hardware_interface_return_values.hpp>
#include <rclcpp/rclcpp.hpp>
#include <cmath>
namespace pca9685_hardware
{
CallbackReturn Pca9685SystemHardware::on_init(const hardware_interface::HardwareInfo& info)
{
if (SystemInterface::on_init(info) != CallbackReturn::SUCCESS) {
return CallbackReturn::ERROR;
}
try {
i2c_bus_ = info.hardware_parameters.at("i2c_bus");
i2c_address_ = std::stoi(info.hardware_parameters.at("i2c_address"), nullptr, 16);
std::string channel_str = info.hardware_parameters.at("channel_mapping");
std::istringstream iss(channel_str);
std::string token;
while (std::getline(iss, token, ',')) {
channel_mapping_.push_back(std::stoi(token));
}
hw_commands_.resize(info.joints.size(), 0.0);
hw_states_.resize(info.joints.size(), 0.0);
return CallbackReturn::SUCCESS;
} catch (const std::exception& e) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "初始化失败: %s", e.what());
return CallbackReturn::ERROR;
}
}
std::vector<hardware_interface::StateInterface> Pca9685SystemHardware::export_state_interfaces()
{
std::vector<hardware_interface::StateInterface> state_interfaces;
for (size_t i = 0; i < hw_states_.size(); i++) {
state_interfaces.emplace_back(
hardware_interface::StateInterface(
info_.joints[i].name,
hardware_interface::HW_IF_POSITION,
&hw_states_[i]
)
);
}
return state_interfaces;
}
std::vector<hardware_interface::CommandInterface> Pca9685SystemHardware::export_command_interfaces()
{
std::vector<hardware_interface::CommandInterface> command_interfaces;
for (size_t i = 0; i < hw_commands_.size(); i++) {
command_interfaces.emplace_back(
hardware_interface::CommandInterface(
info_.joints[i].name,
hardware_interface::HW_IF_POSITION,
&hw_commands_[i]
)
);
}
return command_interfaces;
}
CallbackReturn Pca9685SystemHardware::on_activate(const rclcpp_lifecycle::State& previous_state)
{
RCLCPP_INFO(rclcpp::get_logger("pca9685_hardware"), "激活硬件接口");
try {
std::string device_path = "/dev/" + i2c_bus_;
i2c_fd_ = open(device_path.c_str(), O_RDWR);
if (i2c_fd_ < 0) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "无法打开I2C设备: %s", device_path.c_str());
return CallbackReturn::ERROR;
}
if (ioctl(i2c_fd_, I2C_SLAVE, i2c_address_) < 0) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "无法设置I2C地址: 0x%02X", i2c_address_);
close(i2c_fd_);
return CallbackReturn::ERROR;
}
// 初始化PCA9685
if (!write_byte_data(0x00, 0x01)) { // 复位
return CallbackReturn::ERROR;
}
usleep(50000);
// 设置为普通模式
if (!write_byte_data(0x00, 0x00)) {
return CallbackReturn::ERROR;
}
// 设置PWM频率为50Hz (20ms周期)
uint8_t prescale = static_cast<uint8_t>(25000000.0 / (4096.0 * 50.0) - 1);
uint8_t old_mode = read_byte_data(0x00);
uint8_t new_mode = (old_mode & 0x7F) | 0x10; // 进入睡眠模式
if (!write_byte_data(0x00, new_mode)) {
return CallbackReturn::ERROR;
}
if (!write_byte_data(0xFE, prescale)) {
return CallbackReturn::ERROR;
}
if (!write_byte_data(0x00, old_mode)) {
return CallbackReturn::ERROR;
}
usleep(50000);
if (!write_byte_data(0x00, old_mode | 0x80)) { // 启用自动递增
return CallbackReturn::ERROR;
}
RCLCPP_INFO(rclcpp::get_logger("pca9685_hardware"), "硬件接口激活成功");
return CallbackReturn::SUCCESS;
} catch (const std::exception& e) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "激活失败: %s", e.what());
if (i2c_fd_ >= 0) {
close(i2c_fd_);
}
return CallbackReturn::ERROR;
}
}
CallbackReturn Pca9685SystemHardware::on_deactivate(const rclcpp_lifecycle::State& previous_state)
{
RCLCPP_INFO(rclcpp::get_logger("pca9685_hardware"), "停用硬件接口");
try {
// 关闭所有PWM通道
for (size_t i = 0; i < 16; i++) {
if (!write_word_data(0x06 + 4 * i, 0)) {
RCLCPP_WARN(rclcpp::get_logger("pca9685_hardware"), "关闭通道 %zu 失败", i);
}
}
if (i2c_fd_ >= 0) {
close(i2c_fd_);
i2c_fd_ = -1;
}
RCLCPP_INFO(rclcpp::get_logger("pca9685_hardware"), "硬件接口停用成功");
return CallbackReturn::SUCCESS;
} catch (const std::exception& e) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "停用失败: %s", e.what());
return CallbackReturn::ERROR;
}
}
hardware_interface::return_type Pca9685SystemHardware::read(const rclcpp::Time& time, const rclcpp::Duration& period)
{
// 舵机通常没有位置反馈,所以我们将命令值作为状态返回
hw_states_ = hw_commands_;
return ReturnType::OK;
}
hardware_interface::return_type Pca9685SystemHardware::write(const rclcpp::Time& time, const rclcpp::Duration& period)
{
try {
for (size_t i = 0; i < hw_commands_.size(); i++) {
if (i >= channel_mapping_.size()) {
RCLCPP_WARN(rclcpp::get_logger("pca9685_hardware"), "通道映射越界: %zu", i);
continue;
}
// 将弧度转换为角度 (0-180度)
double angle_rad = hw_commands_[i];
double angle_deg = std::fmod(std::fmod(angle_rad, 2 * M_PI) + 2 * M_PI, 2 * M_PI);
angle_deg = std::clamp(angle_deg * 180.0 / M_PI, 0.0, 180.0);
// 计算PWM脉冲宽度 (500-2500us对应0-180度)
uint16_t pulse_width = static_cast<uint16_t>(500 + (angle_deg / 180.0) * (2500 - 500));
uint16_t pwm_value = static_cast<uint16_t>(pulse_width * 4096.0 / 20000.0);
// 设置PWM输出
int channel = channel_mapping_[i];
if (channel < 0 || channel > 15) {
RCLCPP_WARN(rclcpp::get_logger("pca9685_hardware"), "无效通道: %d", channel);
continue;
}
// 设置PWM值 (0为关闭时间,pwm_value为开启时间)
if (!write_word_data(0x06 + 4 * channel, 0)) {
RCLCPP_WARN(rclcpp::get_logger("pca9685_hardware"), "设置通道 %d 关闭时间失败", channel);
}
if (!write_word_data(0x08 + 4 * channel, pwm_value)) {
RCLCPP_WARN(rclcpp::get_logger("pca9685_hardware"), "设置通道 %d 开启时间失败", channel);
}
}
return ReturnType::OK;
} catch (const std::exception& e) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "写入失败: %s", e.what());
return ReturnType::ERROR;
}
}
bool Pca9685SystemHardware::write_byte_data(int reg, uint8_t value)
{
uint8_t buf[2] = {static_cast<uint8_t>(reg), value};
if (::write(i2c_fd_, buf, 2) != 2) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "I2C写入失败: reg=0x%02X, value=0x%02X", reg, value);
return false;
}
return true;
}
bool Pca9685SystemHardware::write_word_data(int reg, uint16_t value)
{
uint8_t buf[3] = {
static_cast<uint8_t>(reg),
static_cast<uint8_t>(value & 0xFF),
static_cast<uint8_t>((value >> 8) & 0xFF)
};
if (::write(i2c_fd_, buf, 3) != 3) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "I2C写入失败: reg=0x%02X, value=0x%04X", reg, value);
return false;
}
return true;
}
uint8_t Pca9685SystemHardware::read_byte_data(int reg)
{
uint8_t buf = static_cast<uint8_t>(reg);
if (::write(i2c_fd_, &buf, 1) != 1) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "I2C写入寄存器失败: reg=0x%02X", reg);
return 0;
}
if (::read(i2c_fd_, &buf, 1) != 1) {
RCLCPP_ERROR(rclcpp::get_logger("pca9685_hardware"), "I2C读取失败: reg=0x%02X", reg);
return 0;
}
return buf;
}
} // namespace pca9685_hardware
#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(pca9685_hardware::Pca9685SystemHardware, hardware_interface::SystemInterface)
CMakeLists.txt:cmake_minimum_required(VERSION 3.8)
project(pca9685_hardware)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
find_package(hardware_interface REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_lifecycle REQUIRED)
find_package(pluginlib REQUIRED)
add_library(
${PROJECT_NAME}
SHARED
src/pca9685_system_hardware.cpp
)
target_include_directories(
${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
ament_target_dependencies(
${PROJECT_NAME}
"hardware_interface"
"rclcpp"
"rclcpp_lifecycle"
"pluginlib"
)
# 导出插件描述文件(确保resource目录存在)
pluginlib_export_plugin_description_file(hardware_interface resource/pca9685_hardware_plugin.xml)
install(
TARGETS ${PROJECT_NAME}
DESTINATION lib
)
install(
DIRECTORY include/
DESTINATION include
)
# 安装插件描述文件(明确指定resource目录)
install(
FILES resource/pca9685_hardware_plugin.xml
DESTINATION share/${PROJECT_NAME}
)
ament_package()
package.xml:<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema-instance"?>
<package format="3">
<name>pca9685_hardware</name>
<version>0.0.1</version>
<description>PCA9685 PWM Servo Driver Hardware Interface for ROS 2</description>
<maintainer email="xyg@example.com">xyg</maintainer>
<license>Apache License 2.0</license>
<!-- 核心依赖 -->
<depend>hardware_interface</depend>
<depend>rclcpp</depend>
<depend>pluginlib</depend>
<depend>controller_manager</depend>
<depend>rclcpp_lifecycle</depend>
<!-- 测试依赖 -->
<test_depend>ament_cmake</test_depend>
<test_depend>ament_cmake_copyright</test_depend>
<test_depend>ament_cmake_clang_format</test_depend>
<test_depend>ament_cmake_cppcheck</test_depend>
<!-- 移除所有Python相关依赖 -->
<!-- 原test_depend已删除: ament_flake8, ament_pep257, python3-pytest -->
<export>
<!-- 改为ament_cmake编译类型 -->
<build_type>ament_cmake</build_type>
<!-- 硬件接口插件描述文件 -->
<hardware_interface plugin="${prefix}/resource/plugin_description.xml" />
</export>
</package>
以上是各个文件内容以及启动日志报错内容
最新发布