Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/opt/ros/noetic/share/catkin/cmake/toplevel.cmake
|
||||
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
project(head_servo_controller)
|
||||
|
||||
find_package(catkin REQUIRED COMPONENTS
|
||||
autoware_msgs
|
||||
roscpp
|
||||
sensor_msgs
|
||||
serial
|
||||
message_generation
|
||||
)
|
||||
|
||||
catkin_package(
|
||||
# INCLUDE_DIRS include
|
||||
# LIBRARIES head_servo_controller
|
||||
# CATKIN_DEPENDS autoware_msgs roscpp sensor_msgs
|
||||
# DEPENDS system_lib
|
||||
)
|
||||
|
||||
include_directories(
|
||||
include
|
||||
${catkin_INCLUDE_DIRS}
|
||||
${PROJECT_SOURCE_DIR}/msg
|
||||
)
|
||||
|
||||
add_executable(head_servo_controller_node
|
||||
src/head_servo_node.cpp
|
||||
src/head_servo_core.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(head_servo_controller_node
|
||||
${catkin_LIBRARIES}
|
||||
)
|
||||
|
||||
add_dependencies(head_servo_controller_node
|
||||
autoware_msgs_generate_messages_cpp
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef HEAD_SERVO_CORE_H
|
||||
#define HEAD_SERVO_CORE_H
|
||||
|
||||
#include <ros/ros.h>
|
||||
#include <serial/serial.h>
|
||||
#include <std_msgs/String.h>
|
||||
#include <std_msgs/Empty.h>
|
||||
#include <thread>
|
||||
// #include <head_servo_controller/HeadControlCmd.h>
|
||||
// #include <head_servo_controller/HeadAngleMsg.h>
|
||||
typedef struct
|
||||
{
|
||||
int32_t PU;
|
||||
} DispModbusData;
|
||||
|
||||
class Head_servo
|
||||
{
|
||||
private:
|
||||
ros::NodeHandle nh_;
|
||||
serial::Serial serial_;
|
||||
ros::Subscriber control_sub_; // 订阅控制指令
|
||||
ros::Publisher status_pub_; // 发布状态
|
||||
|
||||
// 串口相关配置
|
||||
std::string head_servo_com_;
|
||||
std::string __APP_NAME__;
|
||||
int baudrate_;
|
||||
|
||||
// // 串口线程控制
|
||||
// std::thread serial_thread_;
|
||||
// bool serial_thread_running = false;
|
||||
|
||||
// 控制标志位(对应原STM32代码中的flag)
|
||||
uint8_t flag_1 = 0; // 查询当前角度
|
||||
uint8_t flag_2 = 0; // 按速度转到指定角度
|
||||
uint8_t flag_3 = 0; // 进入巡航模式
|
||||
uint8_t flag_4 = 0; // 退出巡航模式
|
||||
uint8_t flag_5 = 0; // 一键回正(90°)
|
||||
uint8_t flag_6 = 0; // 重启(ROS中可忽略或调用节点重启逻辑)
|
||||
uint8_t flag_7 = 0; // 设置当前位置为零点
|
||||
|
||||
// 目标参数
|
||||
int16_t target_speed = 0xFFFF; // 0xFFFF表示速度不变
|
||||
int16_t target_angle = 0xFFFF; // 0xFFFF表示角度不变
|
||||
|
||||
// // 通讯数据缓冲区
|
||||
// uint8_t recvData_Uart3[100];
|
||||
// uint8_t recvData_Uart2[100];
|
||||
|
||||
// // 命令帧定义(根据原代码推测)
|
||||
// uint8_t ModusEn[8] = {0x01, 0x06, 0x00, 0x00, 0x00, 0x01, 0x48, 0x0A};
|
||||
// uint8_t param_save[8] = {0x01, 0x06, 0x00, 0x01, 0x00, 0x01, 0xF9, 0xCA};
|
||||
// uint8_t Motor_speed_target[11] = {0x01, 0x10, 0x00, 0x10, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00};
|
||||
// uint8_t Motor_speed_target_2[11] = {0x01, 0x10, 0x00, 0x10, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00};
|
||||
// uint8_t PosAngle_P135[13] = {0x01, 0x10, 0x00, 0x12, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00};
|
||||
// uint8_t PosAngle_P45[13] = {0x01, 0x10, 0x00, 0x12, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00};
|
||||
// uint8_t PosAngle_P90[13] = {0x01, 0x10, 0x00, 0x12, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00};
|
||||
// uint8_t PosAngle_N90[13] = {0x01, 0x10, 0x00, 0x12, 0x00, 0x02, 0x04, 0xFF, 0xFF, 0xFF, 0xA6, 0x00, 0x00};
|
||||
|
||||
// ROS话题相关
|
||||
ros::Subscriber control_cmd_sub_;
|
||||
ros::Publisher current_angle_pub_;
|
||||
|
||||
// CRC校验函数(原代码中使用)
|
||||
uint16_t usMBCRC16(uint8_t *pucFrame, uint16_t usLen);
|
||||
|
||||
// // 串口接收回调函数
|
||||
// void serialCallback(const ros::TimerEvent& e);
|
||||
|
||||
// 控制指令回调函数
|
||||
// void control_cmd_callback(const head_servo_controller::HeadControlCmd::ConstPtr& msg);
|
||||
|
||||
// 核心处理函数(移植原STM32 while(1)中的逻辑)
|
||||
void process(const ros::TimerEvent &e);
|
||||
|
||||
// 控制指令回调
|
||||
void controlCallback(const std_msgs::String::ConstPtr& msg);
|
||||
|
||||
// 辅助功能函数
|
||||
void position_stop();
|
||||
void Turn_angle(int16_t angle);
|
||||
int16_t read_current_angle();
|
||||
void setCurrentPositionZero();
|
||||
void fun_response(uint8_t cmd, uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4);
|
||||
void calculate_modbus_data(uint16_t modbus_data_11, uint16_t modbus_data_10, DispModbusData *disp_modbus_data);
|
||||
|
||||
public:
|
||||
Head_servo(ros::NodeHandle nh);
|
||||
~Head_servo();
|
||||
|
||||
// 串口初始化
|
||||
void serial_initial();
|
||||
|
||||
// 运行节点
|
||||
void run();
|
||||
};
|
||||
|
||||
#endif // HEAD_SERVO_CORE_H
|
||||
@@ -0,0 +1,13 @@
|
||||
<launch>
|
||||
<arg name="head_servo_com_" default="/dev/ttyACM0"/>
|
||||
<arg name="baudrate_" default="19200"/>
|
||||
<arg name="input_ctr_mode_topic_" default="/remote_ctrl"/>
|
||||
<arg name="input_battery_state_topic_" default="/sensor/battery_state"/>
|
||||
|
||||
<node name="head_servo_controller_node" pkg="head_servo_controller" type="head_servo_controller_node" output="screen">
|
||||
<param name="head_servo_com_" value="$(arg head_servo_com_)" type="string"/>
|
||||
<param name="baudrate_" value="$(arg baudrate_)" type="int"/>
|
||||
<param name="input_ctr_mode_topic_" value="$(arg input_ctr_mode_topic_)" type="string"/>
|
||||
<param name="input_battery_state_topic_" value="$(arg input_battery_state_topic_)" type="string"/>
|
||||
</node>
|
||||
</launch>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<package format="2">
|
||||
<name>head_servo_controller</name>
|
||||
<version>0.0.0</version>
|
||||
<description>The head_servo_controller package</description>
|
||||
|
||||
<maintainer email="zhangshu@todo.todo">zhangshu</maintainer>
|
||||
|
||||
|
||||
<license>TODO</license>
|
||||
|
||||
|
||||
<buildtool_depend>catkin</buildtool_depend>
|
||||
<build_depend>autoware_msgs</build_depend>
|
||||
<build_depend>roscpp</build_depend>
|
||||
<build_depend>sensor_msgs</build_depend>
|
||||
<build_depend>serial</build_depend>
|
||||
<build_export_depend>autoware_msgs</build_export_depend>
|
||||
<build_export_depend>roscpp</build_export_depend>
|
||||
<build_export_depend>sensor_msgs</build_export_depend>
|
||||
<build_export_depend>serial</build_export_depend>
|
||||
<exec_depend>autoware_msgs</exec_depend>
|
||||
<exec_depend>roscpp</exec_depend>
|
||||
<exec_depend>sensor_msgs</exec_depend>
|
||||
<exec_depend>serial</exec_depend>
|
||||
|
||||
|
||||
<!-- The export tag contains other, unspecified, tags -->
|
||||
<export>
|
||||
<!-- Other tools can request additional information be placed here -->
|
||||
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,149 @@
|
||||
#include "head_servo_core.h"
|
||||
#include <mqtt/async_client.h> // Paho MQTT C++库
|
||||
#include <thread> // 多线程支持
|
||||
#include <mutex> // 线程安全锁
|
||||
|
||||
// MQTT配置
|
||||
const std::string MQTT_ADDRESS("tcp://0.0.0.0:1883"); // 本地MQTT服务器地址
|
||||
const std::string MQTT_CLIENT_ID("HeadServo_MQTT_Server");
|
||||
const std::string MQTT_TOPIC("head_servo/control"); // 订阅的控制指令主题
|
||||
|
||||
// 线程安全锁(保护flag变量)
|
||||
std::mutex flag_mutex;
|
||||
|
||||
// MQTT回调类(处理连接和消息接收)
|
||||
class MQTTCallback : public virtual mqtt::callback {
|
||||
private:
|
||||
Head_servo* head_servo_ptr; // 指向Head_servo实例的指针
|
||||
|
||||
public:
|
||||
MQTTCallback(Head_servo* ptr) : head_servo_ptr(ptr) {}
|
||||
|
||||
// 连接丢失回调
|
||||
void connection_lost(const std::string& cause) override {
|
||||
ROS_WARN("MQTT连接丢失: %s", cause.c_str());
|
||||
}
|
||||
|
||||
// 消息到达回调(核心:解析MQTT消息并修改flag)
|
||||
void message_arrived(mqtt::const_message_ptr msg) override {
|
||||
ROS_INFO("收到MQTT消息: [%s] %s", msg->get_topic().c_str(), msg->to_string().c_str());
|
||||
|
||||
// 解析JSON格式的控制指令(示例格式:{"flag_1":1, "flag_2":1, "target_angle":90, ...})
|
||||
// 实际应用中可根据需求简化格式(如直接发送flag名称和值)
|
||||
std::string payload = msg->to_string();
|
||||
|
||||
// 线程安全地修改flag(根据消息内容设置对应flag)
|
||||
std::lock_guard<std::mutex> lock(flag_mutex);
|
||||
|
||||
// 示例:解析简单指令(实际需根据通信协议完善)
|
||||
if (payload.find("query_angle") != std::string::npos) {
|
||||
head_servo_ptr->flag_1 = 1; // 查询当前角度
|
||||
} else if (payload.find("set_target") != std::string::npos) {
|
||||
head_servo_ptr->flag_2 = 1; // 设置目标角度/速度
|
||||
// 提取目标角度(示例:假设消息中包含"angle:90")
|
||||
size_t angle_pos = payload.find("angle:");
|
||||
if (angle_pos != std::string::npos) {
|
||||
head_servo_ptr->target_angle = std::stoi(payload.substr(angle_pos + 6));
|
||||
}
|
||||
} else if (payload.find("enter_cruise") != std::string::npos) {
|
||||
head_servo_ptr->flag_3 = 1; // 进入巡航模式
|
||||
} else if (payload.find("exit_cruise") != std::string::npos) {
|
||||
head_servo_ptr->flag_4 = 1; // 退出巡航模式
|
||||
} else if (payload.find("home_position") != std::string::npos) {
|
||||
head_servo_ptr->flag_5 = 1; // 返回90°
|
||||
} else if (payload.find("set_zero") != std::string::npos) {
|
||||
head_servo_ptr->flag_7 = 1; // 设置当前位置为零点
|
||||
}
|
||||
}
|
||||
|
||||
// 消息发送完成回调
|
||||
void delivery_complete(mqtt::delivery_token_ptr token) override {
|
||||
ROS_DEBUG("MQTT消息发送完成");
|
||||
}
|
||||
};
|
||||
|
||||
// MQTT连接监听器
|
||||
class ActionListener : public virtual mqtt::iaction_listener {
|
||||
private:
|
||||
void on_failure(const mqtt::token& tok) override {
|
||||
ROS_WARN("MQTT操作失败");
|
||||
}
|
||||
void on_success(const mqtt::token& tok) override {
|
||||
ROS_INFO("MQTT操作成功");
|
||||
}
|
||||
};
|
||||
|
||||
// 扩展Head_servo类,添加MQTT相关成员
|
||||
class HeadServoWithMQTT : public Head_servo {
|
||||
public:
|
||||
mqtt::async_client mqtt_client; // MQTT客户端
|
||||
MQTTCallback mqtt_callback; // MQTT回调实例
|
||||
ActionListener mqtt_listener; // MQTT连接监听器
|
||||
|
||||
// 构造函数
|
||||
HeadServoWithMQTT(ros::NodeHandle nh)
|
||||
: Head_servo(nh),
|
||||
mqtt_client(MQTT_ADDRESS, MQTT_CLIENT_ID),
|
||||
mqtt_callback(this) {}
|
||||
|
||||
// 初始化MQTT服务器
|
||||
void mqtt_init() {
|
||||
// 设置MQTT回调
|
||||
mqtt_client.set_callback(mqtt_callback);
|
||||
|
||||
// 配置连接选项
|
||||
mqtt::connect_options conn_opts;
|
||||
conn_opts.set_keep_alive_interval(20); // 心跳间隔20秒
|
||||
conn_opts.set_clean_session(true); // 清理会话
|
||||
|
||||
// 连接MQTT服务器
|
||||
try {
|
||||
ROS_INFO("连接MQTT服务器: %s", MQTT_ADDRESS.c_str());
|
||||
mqtt::token_ptr conntok = mqtt_client.connect(conn_opts);
|
||||
conntok->wait(); // 等待连接完成
|
||||
ROS_INFO("MQTT服务器连接成功");
|
||||
|
||||
// 订阅控制指令主题
|
||||
mqtt_client.subscribe(MQTT_TOPIC, 1, nullptr, mqtt_listener);
|
||||
ROS_INFO("已订阅MQTT主题: %s", MQTT_TOPIC.c_str());
|
||||
} catch (const mqtt::exception& e) {
|
||||
ROS_ERROR("MQTT初始化失败: %s", e.what());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 重写run()函数,同时启动ROS和MQTT
|
||||
void run() override {
|
||||
serial_initial(); // 初始化串口
|
||||
mqtt_init(); // 初始化MQTT
|
||||
|
||||
// 创建线程运行MQTT循环(非阻塞)
|
||||
std::thread mqtt_thread([this]() {
|
||||
while (ros::ok()) {
|
||||
// 处理MQTT消息(非阻塞模式)
|
||||
mqtt_client.loop(100); // 超时100ms,避免阻塞
|
||||
ros::Duration(0.01).sleep(); // 短暂休眠
|
||||
}
|
||||
});
|
||||
|
||||
// 启动ROS定时器和主循环
|
||||
ros::Timer timer = nh_.createTimer(ros::Duration(0.01), &Head_servo::process, this);
|
||||
ros::spin();
|
||||
|
||||
// 退出时清理
|
||||
mqtt_client.disconnect()->wait(); // 断开MQTT连接
|
||||
mqtt_thread.join(); // 等待MQTT线程结束
|
||||
}
|
||||
};
|
||||
|
||||
// 主函数
|
||||
int main(int argc, char**argv) {
|
||||
ros::init(argc, argv, "head_servo_with_mqtt");
|
||||
ros::NodeHandle nh("~");
|
||||
|
||||
// 创建带MQTT功能的节点实例并运行
|
||||
HeadServoWithMQTT node(nh);
|
||||
node.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,733 @@
|
||||
#include "head_servo_core.h"
|
||||
#include <jsoncpp/json/json.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
/* 以下逆时针需要用到32768的负数那种,可以通过covertPU这个函数计算得到 */
|
||||
/* 位置模式: 转动到指定位置模板 */
|
||||
static uint8_t Angle_Target[] = {0x01, 0x10, 0x00, 0x16, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
/* 位置模式: 顺时针到180° */
|
||||
static uint8_t PosAngle_P180[] = {0x01, 0x10, 0x00, 0x16, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0xF3, 0x48};
|
||||
/* 位置模式:45° */
|
||||
static uint8_t PosAngle_P45[] = {0X01, 0X10, 0X00, 0X16, 0X00, 0X02, 0X04, 0X80, 0X00, 0X00, 0X00, 0X5B, 0X49};
|
||||
/* 位置模式:135° */
|
||||
static uint8_t PosAngle_P135[] = {0x01, 0x10, 0x00, 0x16, 0x00, 0x02, 0x04, 0x80, 0x00, 0x00, 0x01, 0x9A, 0x89};
|
||||
/* 位置模式: 1° */
|
||||
static uint8_t PosAngle_N1[] = {0x01, 0x10, 0x00, 0x16, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x00, 0x23, 0x49};
|
||||
/* 位置模式:顺时针转到90° */
|
||||
static uint8_t PosAngle_P90[] = {0x01, 0x10, 0x00, 0x16, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x01, 0xB3, 0x49};
|
||||
/* modbus使能 */
|
||||
static uint8_t ModusEn[] = {0x01, 0x06, 0x00, 0x00, 0x00, 0x01, 0x48, 0x0A};
|
||||
/* 发送目标速度 */
|
||||
static uint8_t Motor_speed_target[] = {0x01, 0x06, 0x00, 0x02, 0x00, 0xC8, 0x29, 0x9C};
|
||||
/* 发送目标速度 */
|
||||
static uint8_t Motor_speed_target_2[] = {0x01, 0x06, 0x00, 0x02, 0x00, 0x28, 0x28, 0x14};
|
||||
/* 发送参数保存标志 */
|
||||
static uint8_t param_save[] = {0x01, 0x06, 0x00, 0x14, 0x00, 0x01, 0x08, 0x0E};
|
||||
/* 电子齿轮发0 */
|
||||
static uint8_t Motor_mole_zero[] = {0x01, 0x06, 0x00, 0x0A, 0x00, 0x00, 0xA9, 0xC8};
|
||||
/* 电子齿轮发 60006 */
|
||||
static uint8_t Motor_mole_set_zero_1[] = {0x01, 0x06, 0x00, 0x0A, 0xEA, 0x66, 0x66, 0x82};
|
||||
/* 电子齿轮发 60016 */
|
||||
static uint8_t Motor_mole_set_zero_2[] = {0x01, 0x06, 0x00, 0x0A, 0xEA, 0x70, 0xE7, 0x4C};
|
||||
/* 增量位置发0 */
|
||||
static uint8_t Motor_incre_zero[] = {0x01, 0x10, 0x00, 0x0C, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xFA};
|
||||
|
||||
/* CRC16 数组 */
|
||||
static const uint8_t aucCRCHi[] = {0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00,
|
||||
0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
|
||||
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
|
||||
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80,
|
||||
0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
|
||||
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
|
||||
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
|
||||
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80,
|
||||
0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
|
||||
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00,
|
||||
0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40};
|
||||
static const uint8_t aucCRCLo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D,
|
||||
0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB,
|
||||
0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2,
|
||||
0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37,
|
||||
0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8,
|
||||
0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24,
|
||||
0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63,
|
||||
0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE,
|
||||
0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F,
|
||||
0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
|
||||
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C,
|
||||
0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
|
||||
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46,
|
||||
0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40};
|
||||
|
||||
|
||||
Head_servo::Head_servo(ros::NodeHandle nh) : nh_(nh)
|
||||
{
|
||||
// Get ROS params
|
||||
nh_.param<std::string>("head_servo_com", head_servo_com_, "/dev/ttyS0");
|
||||
nh_.param<std::string>("app_name", __APP_NAME__, "head_servo_controller");
|
||||
nh_.param<int>("baudrate_", baudrate_, 19200);
|
||||
|
||||
// 初始化控制标志
|
||||
flag_1 = 0;
|
||||
flag_2 = 0;
|
||||
flag_3 = 0;
|
||||
flag_4 = 0;
|
||||
flag_5 = 0;
|
||||
flag_7 = 0;
|
||||
|
||||
target_speed = 0xFFFF;
|
||||
target_angle = 0xFFFF;
|
||||
|
||||
// 初始化订阅者和发布者
|
||||
control_sub_ = nh_.subscribe("/head_servo/control_cmd", 10, &Head_servo::controlCallback, this);
|
||||
status_pub_ = nh_.advertise<std_msgs::String>("/head_servo/status", 10);
|
||||
}
|
||||
|
||||
// // 控制指令解析回调
|
||||
// void Head_servo::controlCallback(const std_msgs::String::ConstPtr& msg)
|
||||
// {
|
||||
// std::string cmd = msg->data;
|
||||
// ROS_INFO("[%s] 收到控制指令: %s", __APP_NAME__.c_str(), cmd.c_str());
|
||||
|
||||
// // 解析命令并设置对应标志
|
||||
// if (cmd == "query_angle") {
|
||||
// flag_1 = 1; // 查询当前角度
|
||||
// } else if (cmd == "enter_cruise") {
|
||||
// flag_3 = 1; // 进入巡航模式
|
||||
// } else if (cmd == "exit_cruise") {
|
||||
// flag_4 = 1; // 退出巡航模式
|
||||
// } else if (cmd == "home_position") {
|
||||
// flag_5 = 1; // 返回90°
|
||||
// } else if (cmd == "set_zero") {
|
||||
// flag_7 = 1; // 设置零点
|
||||
|
||||
// // 解析带参数的命令
|
||||
// } else if (cmd.find("set_target") != std::string::npos) {
|
||||
// flag_2 = 1; // 设置目标角度/速度
|
||||
|
||||
// // 提取目标角度和速度(格式示例:"set_target,angle:90,speed:100")
|
||||
// size_t angle_pos = cmd.find("angle:");
|
||||
// size_t speed_pos = cmd.find("speed:");
|
||||
|
||||
// if (angle_pos != std::string::npos) {
|
||||
// std::string angle_str = cmd.substr(angle_pos + 6);
|
||||
// angle_str = angle_str.substr(0, angle_str.find(','));
|
||||
// target_angle = std::stoi(angle_str);
|
||||
// }
|
||||
|
||||
// if (speed_pos != std::string::npos) {
|
||||
// std::string speed_str = cmd.substr(speed_pos + 6);
|
||||
// target_speed = std::stoi(speed_str);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 发布状态消息
|
||||
// std_msgs::String status_msg;
|
||||
// status_msg.data = "Command received: " + cmd;
|
||||
// status_pub_.publish(status_msg);
|
||||
// }
|
||||
// mosquitto_pub -t "head_servo/control" -m '{"command":"set_target","angle":0,"speed":40}'
|
||||
// mosquitto_pub -t "head_servo/control" -m '{"command":"query_angle"}'
|
||||
// mosquitto_pub -t "head_servo/control" -m '{"command":"reset_zero"}'
|
||||
// mosquitto_pub -t "head_servo/control" -m '{"command":"enter_cruise"}'
|
||||
// mosquitto_pub -t "head_servo/control" -m '{"command":"stop"}'
|
||||
|
||||
/*
|
||||
|
||||
主题: ‘robot/yuntai_control/{device_id}’
|
||||
Qos: 2
|
||||
消息内容:
|
||||
|
||||
绝对位置角度控制:
|
||||
{
|
||||
"uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d465",
|
||||
"task_type:": "yuntai_control",
|
||||
"command": "angle",
|
||||
"data":{
|
||||
"angle": 15, # 0-180度。0对应最左边,180对应最右边
|
||||
"speed": 30, # 060度。最⼤移动速度。不传值则为默认30度。
|
||||
}
|
||||
}
|
||||
|
||||
相对位置角度控制:
|
||||
{
|
||||
"uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d465",
|
||||
"task_type:": "yuntai_control",
|
||||
"command": "direction",
|
||||
"data":{
|
||||
"direction": "left", # "left"/"right", 向左/向右移动指定⻆度
|
||||
"angle": 15, # 5-30度。移动的度数。不传值则为默认15度
|
||||
}
|
||||
}
|
||||
|
||||
循环旋转控制:
|
||||
{
|
||||
"uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d465",
|
||||
"task_type:": "yuntai_control",
|
||||
"command": "loop",
|
||||
"data": {
|
||||
"speed": 30, # 0-60度。最⼤移动速度。不传值则为默认30度。
|
||||
}
|
||||
}
|
||||
|
||||
回中控制:
|
||||
{
|
||||
"uuid": "f47ac10b58cc4372a5670e02b2c3d465",
|
||||
"tasktype:": "yuntaicontrol",
|
||||
"command": "center",
|
||||
"data": {
|
||||
"speed": 30, # 060度。最⼤移动速度。不传值则为默认30度。
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// 修改 C++ 代码中的控制回调函数
|
||||
void Head_servo::controlCallback(const std_msgs::String::ConstPtr& msg)
|
||||
{
|
||||
std::string cmd = msg->data;
|
||||
ROS_INFO("[%s] 收到控制指令: %s", __APP_NAME__.c_str(), cmd.c_str());
|
||||
try {
|
||||
// 尝试解析 JSON 命令
|
||||
json root = json::parse(cmd);
|
||||
|
||||
// 处理 JSON 格式命令
|
||||
if (root.contains("command")) {
|
||||
std::string command = root["command"].get<std::string>();
|
||||
|
||||
if (command == "query_angle") {
|
||||
flag_1 = 1;
|
||||
ROS_INFO("1");
|
||||
} else if (command == "enter_cruise") {
|
||||
flag_3 = 1;
|
||||
ROS_INFO("3");
|
||||
} else if (command == "set_target") {
|
||||
flag_2 = 1;
|
||||
ROS_INFO("2");
|
||||
if (root.contains("angle"))
|
||||
target_angle = root["angle"].get<int>();
|
||||
if (root.contains("speed"))
|
||||
target_speed = root["speed"].get<int>();
|
||||
} else if (command == "reset_zero") {
|
||||
flag_7 = 1;
|
||||
ROS_INFO("7");
|
||||
} else if (command == "stop") {
|
||||
flag_4 = 1;
|
||||
ROS_INFO("4");
|
||||
}
|
||||
// 其他命令...
|
||||
}
|
||||
} catch (const json::parse_error& e) {
|
||||
// 处理JSON解析错误
|
||||
ROS_WARN("[%s] 解析JSON命令时出错: %s", __APP_NAME__.c_str(), e.what());
|
||||
} catch (const json::type_error& e) {
|
||||
// 处理类型错误
|
||||
ROS_WARN("[%s] JSON类型不匹配: %s", __APP_NAME__.c_str(), e.what());
|
||||
} catch (const std::exception& e) {
|
||||
// 处理其他异常
|
||||
ROS_WARN("[%s] 处理命令时出错: %s", __APP_NAME__.c_str(), e.what());
|
||||
}
|
||||
// try {
|
||||
// // 尝试解析 JSON 命令
|
||||
// Json::Value root;
|
||||
// Json::Reader reader;
|
||||
|
||||
// if (reader.parse(cmd, root)) {
|
||||
// // 处理 JSON 格式命令
|
||||
// if (root.isMember("command")) {
|
||||
// std::string command = root["command"].asString();
|
||||
|
||||
// if (command == "query_angle") {
|
||||
// flag_1 = 1;
|
||||
// } else if (command == "enter_cruise") {
|
||||
// flag_3 = 1;
|
||||
// } else if (command == "set_target") {
|
||||
// flag_2 = 1;
|
||||
// if (root.isMember("angle"))
|
||||
// target_angle = root["angle"].asInt();
|
||||
// if (root.isMember("speed"))
|
||||
// target_speed = root["speed"].asInt();
|
||||
// }
|
||||
// // 其他命令...
|
||||
// }
|
||||
// } else {
|
||||
// // 处理普通字符串命令(保持原有逻辑)
|
||||
// // ...
|
||||
// }
|
||||
// } catch (const std::exception& e) {
|
||||
// ROS_WARN("[%s] 解析命令时出错: %s", __APP_NAME__.c_str(), e.what());
|
||||
// }
|
||||
|
||||
// 发布状态消息
|
||||
std_msgs::String status_msg;
|
||||
status_msg.data = "Command received: " + cmd;
|
||||
status_pub_.publish(status_msg);
|
||||
}
|
||||
|
||||
|
||||
Head_servo::~Head_servo()
|
||||
{
|
||||
if (serial_.isOpen())
|
||||
{
|
||||
serial_.close();
|
||||
}
|
||||
}
|
||||
|
||||
// CRC16 check implementation
|
||||
uint16_t Head_servo::usMBCRC16(uint8_t *pucFrame, uint16_t usLen)
|
||||
{
|
||||
uint8_t ucCRCHi = 0xFF;
|
||||
uint8_t ucCRCLo = 0xFF;
|
||||
int iIndex;
|
||||
while (usLen--)
|
||||
{
|
||||
iIndex = ucCRCLo ^ *(pucFrame++);
|
||||
ucCRCLo = (uint8_t)(ucCRCHi ^ aucCRCHi[iIndex]);
|
||||
ucCRCHi = aucCRCLo[iIndex];
|
||||
}
|
||||
return (uint16_t)(ucCRCLo << 8 | ucCRCHi);
|
||||
}
|
||||
|
||||
// Serial init
|
||||
void Head_servo::serial_initial()
|
||||
{
|
||||
serial::Timeout timeOut = serial::Timeout::simpleTimeout(1000);
|
||||
|
||||
try
|
||||
{
|
||||
serial_.setPort(head_servo_com_);
|
||||
serial_.setBaudrate(baudrate_);
|
||||
serial_.setTimeout(timeOut);
|
||||
serial_.open();
|
||||
}
|
||||
catch (serial::IOException &e)
|
||||
{
|
||||
ROS_ERROR("[%s] Serial open failed: %s", __APP_NAME__.c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (serial_.isOpen())
|
||||
{
|
||||
ROS_INFO("[%s] Serial init success, starting init sequence.", __APP_NAME__.c_str());
|
||||
|
||||
// Send 485 enable
|
||||
serial_.write(ModusEn, sizeof(ModusEn));
|
||||
ros::Duration(0.01).sleep();
|
||||
serial_.write(param_save, sizeof(param_save));
|
||||
ros::Duration(0.01).sleep();
|
||||
|
||||
// Init speed
|
||||
serial_.write(Motor_speed_target_2, sizeof(Motor_speed_target_2));
|
||||
ros::Duration(0.005).sleep();
|
||||
serial_.write(param_save, sizeof(param_save));
|
||||
ros::Duration(0.005).sleep();
|
||||
|
||||
// Stop multiple times
|
||||
for (uint8_t i = 0; i < 100; i++)
|
||||
{
|
||||
position_stop();
|
||||
ros::Duration(0.02).sleep();
|
||||
}
|
||||
|
||||
// // Return to 0
|
||||
Turn_angle(0);
|
||||
ros::Duration(1.0).sleep();
|
||||
ROS_INFO("[%s] Init done, waiting for commands", __APP_NAME__.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// // Control cmd callback
|
||||
// void Head_servo::control_cmd_callback(const head_servo_controller::HeadControlCmd::ConstPtr& msg)
|
||||
// {
|
||||
// // Set flags per msg fields
|
||||
// if (msg->query_angle) flag_1 = 1;
|
||||
// if (msg->set_target) { flag_2 = 1; target_speed = msg->target_speed; target_angle = msg->target_angle; }
|
||||
// if (msg->enter_cruise) flag_3 = 1;
|
||||
// if (msg->exit_cruise) flag_4 = 1;
|
||||
// if (msg->home_position) flag_5 = 1;
|
||||
// if (msg->set_zero) flag_7 = 1;
|
||||
|
||||
// ROS_INFO("[%s] Recv cmd: query=%d, set=%d, enter_cruise=%d, exit_cruise=%d, home=%d, set_zero=%d",
|
||||
// __APP_NAME__.c_str(), msg->query_angle, msg->set_target, msg->enter_cruise,
|
||||
// msg->exit_cruise, msg->home_position, msg->set_zero);
|
||||
// }
|
||||
|
||||
// Position stop func
|
||||
void Head_servo::position_stop()
|
||||
{
|
||||
serial_.write(Motor_mole_zero, sizeof(Motor_mole_zero));
|
||||
ros::Duration(0.05).sleep();
|
||||
serial_.write(Motor_incre_zero, sizeof(Motor_incre_zero));
|
||||
ros::Duration(0.05).sleep();
|
||||
}
|
||||
|
||||
// Turn to specified angle
|
||||
void Head_servo::Turn_angle(int16_t target_angle)
|
||||
{
|
||||
ROS_INFO("[0", __APP_NAME__.c_str());
|
||||
// target_angle = target_angle > 135 ? 135 : target_angle;
|
||||
// target_angle = target_angle < 45 ? 45 : target_angle;
|
||||
|
||||
// 目标角度不能是0度,最多到1度
|
||||
// (对于脖子来说0度和1度无所谓,保证正确,0度就变成1度,,,其他而言,零度发1个脉冲,而不是1度)
|
||||
target_angle = target_angle == 0 ? 1 : target_angle;
|
||||
|
||||
// 读取当前角度
|
||||
// int16_t current_angle = read_current_angle();
|
||||
// while (current_angle == 0x7FFF)
|
||||
// {
|
||||
// current_angle = read_current_angle();
|
||||
// }
|
||||
// printf("current angle is %d\r\n", current_angle);
|
||||
|
||||
// 转换成要发送给电机的数据
|
||||
int32_t po_angle = 0xFFFF;
|
||||
if (target_angle<0)
|
||||
{
|
||||
po_angle = -(-target_angle * (32768 * 8) / 360);
|
||||
}
|
||||
else if(target_angle>0){
|
||||
po_angle = target_angle * (32768 * 8) / 360;
|
||||
}
|
||||
|
||||
// 构件要发送的帧数据
|
||||
Angle_Target[8] = po_angle & 0xFF;
|
||||
Angle_Target[7] = (po_angle >> 8) & 0xFF;
|
||||
Angle_Target[10] = (po_angle >> 16) & 0xFF;
|
||||
Angle_Target[9] = (po_angle >> 24) & 0xFF;
|
||||
uint16_t crc_temp = usMBCRC16(Angle_Target, 11);
|
||||
Angle_Target[11] = (crc_temp >> 8) & 0xFF;
|
||||
Angle_Target[12] = crc_temp & 0xFF;
|
||||
/* 转到指定角度 */
|
||||
serial_.write(Angle_Target, sizeof(Angle_Target));
|
||||
ros::Duration(0.001).sleep();
|
||||
serial_.write(Angle_Target, sizeof(Angle_Target));
|
||||
ROS_INFO("[1", __APP_NAME__.c_str());
|
||||
ros::Duration(7).sleep();
|
||||
/* 每隔1ms查询一次当前位置,到达目标位置为止 */
|
||||
/* 发送命令,读取当前位置以作验证 */
|
||||
// int16_t angle = 0x7FFF;
|
||||
// int16_t de_angle = 0x7FFF;
|
||||
// // flag_count_1s = 0;
|
||||
// while (1)
|
||||
// {
|
||||
// // if (flag_count_1s >= 20)
|
||||
// // {
|
||||
// // flag_count_1s = 0;
|
||||
// // break;
|
||||
// // }
|
||||
// angle = read_current_angle();
|
||||
// de_angle = (target_angle - angle);
|
||||
// de_angle = de_angle > 0 ? de_angle : -de_angle;
|
||||
// if (de_angle >= 0 && de_angle <= 3)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// ros::Duration(0.001).sleep();
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
将两个16位数据转换成一个32位数据
|
||||
11是高16位,10是低16位
|
||||
左移优先级很低,使用时记得加括号
|
||||
*/
|
||||
void Head_servo::calculate_modbus_data(uint16_t modbus_data_11, uint16_t modbus_data_10, DispModbusData *disp_modbus_data)
|
||||
{
|
||||
// printf("%d\t\t%d\r\n", modbus_data_11, modbus_data_10);
|
||||
if (modbus_data_11 > 32767)
|
||||
{ // If high byte exceeds 32767, interpret as negative value
|
||||
// Calculate PU for negative value
|
||||
disp_modbus_data->PU = ((modbus_data_11)-32768) * 65536 + modbus_data_10;
|
||||
// Convert to negative value (two's complement)
|
||||
disp_modbus_data->PU = -((0x7FFFFFFF - disp_modbus_data->PU) + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate PU for positive value
|
||||
disp_modbus_data->PU = modbus_data_11 * 65536 + modbus_data_10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read current angle
|
||||
int16_t Head_servo::read_current_angle()
|
||||
{
|
||||
// Send read angle cmd
|
||||
uint8_t read_cmd[] = {0x01, 0x03, 0x00, 0x16, 0x00, 0x02, 0x25, 0xCF};
|
||||
DispModbusData recvData = {0};
|
||||
serial_.write(read_cmd, sizeof(read_cmd));
|
||||
|
||||
// Wait for response
|
||||
// ros::Time start_time = ros::Time::now();
|
||||
// while ((ros::Time::now() - start_time).toSec() < 0.1) // 100ms timeout
|
||||
// {
|
||||
// ros::Duration(0.003).sleep();
|
||||
if (serial_.available() >= 7) // Min 7 bytes response
|
||||
{
|
||||
uint8_t buffer[100];
|
||||
// size_t bytes_read = serial_.read(buffer, serial_.available());
|
||||
size_t bytes_read = serial_.read(buffer, std::min(serial_.available(), (size_t)100));
|
||||
// 打印原始数据(以十六进制格式)
|
||||
ROS_INFO("[%s] 收到串口数据 (%zu bytes):", __APP_NAME__.c_str(), bytes_read);
|
||||
for (size_t i = 0; i < bytes_read; i++) {
|
||||
ROS_INFO(" [%02zu]: 0x%02X", i, buffer[i]);
|
||||
}
|
||||
|
||||
// Simple parse (assumed correct format)
|
||||
if (bytes_read >= 7 && buffer[0] == 0x01 && buffer[1] == 0x03)
|
||||
{
|
||||
// Extract angle (high byte first)
|
||||
int16_t angle_1 = (buffer[3] << 8) | buffer[4];
|
||||
int16_t angle_2 = (buffer[5] << 8) | buffer[6];
|
||||
calculate_modbus_data(angle_2, angle_1, &recvData);
|
||||
return (recvData.PU * 360 / (32768 * 8));
|
||||
}
|
||||
}
|
||||
return 0x7FFF;
|
||||
ros::Duration(0.001).sleep();
|
||||
// }
|
||||
|
||||
// return 0x7FFF; // Invalid on timeout
|
||||
}
|
||||
|
||||
// Set current pos as zero
|
||||
void Head_servo::setCurrentPositionZero()
|
||||
{
|
||||
serial_.write(Motor_mole_set_zero_1, sizeof(Motor_mole_set_zero_1));
|
||||
ros::Duration(0.050).sleep();
|
||||
serial_.write(Motor_mole_set_zero_2, sizeof(Motor_mole_set_zero_2));
|
||||
ros::Duration(0.050).sleep();
|
||||
serial_.write(param_save, sizeof(param_save));
|
||||
ros::Duration(0.050).sleep();
|
||||
}
|
||||
|
||||
// Response func (replace original fun_response)
|
||||
void Head_servo::fun_response(uint8_t cmd, uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4)
|
||||
{
|
||||
// Build response frame
|
||||
uint8_t response[] = {0x01, cmd, 0x04, data1, data2, data3, data4, 0x00, 0x00};
|
||||
|
||||
// Calc CRC
|
||||
uint16_t crc = usMBCRC16(response, 7);
|
||||
response[7] = (crc >> 8) & 0xFF;
|
||||
response[8] = crc & 0xFF;
|
||||
|
||||
// Send response
|
||||
serial_.write(response, 9);
|
||||
}
|
||||
|
||||
// Core process func
|
||||
void Head_servo::process(const ros::TimerEvent &e)
|
||||
{
|
||||
if (!serial_.isOpen()) return;
|
||||
|
||||
// Handle flag_6 (restart, log only)
|
||||
// 这里不是单片机,不需要重启
|
||||
// if (flag_6 == 1)
|
||||
// {
|
||||
// ROS_WARN("[%s] Recv restart cmd", __APP_NAME__.c_str());
|
||||
// flag_6 = 0;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Handle flag_1: query angle
|
||||
// ROS_INFO("process");
|
||||
if (flag_1 == 1)
|
||||
{
|
||||
ROS_INFO("1");
|
||||
int16_t angle = read_current_angle();
|
||||
while (angle == 0x7FFF) // Wait for valid
|
||||
{
|
||||
angle = read_current_angle();
|
||||
ROS_INFO("2");
|
||||
ros::Duration(0.01).sleep();
|
||||
}
|
||||
ROS_INFO("[%s] Current angle: %d°", __APP_NAME__.c_str(), angle);
|
||||
|
||||
flag_1 = 0;
|
||||
}
|
||||
|
||||
// Handle flag_2: turn to angle at speed
|
||||
if (flag_2 == 1)
|
||||
{
|
||||
ROS_INFO("[%s] Recv target: speed=%d, angle=%d", __APP_NAME__.c_str(), target_speed, target_angle);
|
||||
|
||||
// target_angle 是从外部获取得到
|
||||
if ((target_angle <-45 || target_angle > 45) && target_angle != 0xFFFF)
|
||||
{
|
||||
ROS_WARN("[%s] Angle out of range (-45 to 45)", __APP_NAME__.c_str());
|
||||
flag_2 = 0;
|
||||
return;
|
||||
}
|
||||
// Handle speed setting
|
||||
if (target_speed != 0xFFFF)
|
||||
{
|
||||
uint8_t Motor_speed_target_copy[8];
|
||||
memcpy(Motor_speed_target_copy, Motor_speed_target, sizeof(Motor_speed_target));
|
||||
// Update speed
|
||||
Motor_speed_target_copy[4] = (target_speed >> 8) & 0xFF;
|
||||
Motor_speed_target_copy[5] = target_speed & 0xFF;
|
||||
// Recalc CRC
|
||||
uint16_t crc = usMBCRC16(Motor_speed_target_copy, 6);
|
||||
Motor_speed_target_copy[6] = (crc >> 8) & 0xFF;
|
||||
Motor_speed_target_copy[7] = crc & 0xFF;
|
||||
// Send cmd
|
||||
serial_.write(Motor_speed_target_copy, sizeof(Motor_speed_target_copy));
|
||||
ros::Duration(0.005).sleep();
|
||||
serial_.write(param_save, sizeof(param_save));
|
||||
ros::Duration(0.005).sleep();
|
||||
ROS_INFO("[%s] Speed set: %d", __APP_NAME__.c_str(), target_speed);
|
||||
}
|
||||
|
||||
// Handle angle setting
|
||||
if (target_angle != 0xFFFF)
|
||||
{
|
||||
position_stop(); // Stop first
|
||||
ros::Duration(0.01).sleep();
|
||||
Turn_angle(target_angle); // Turn to target
|
||||
position_stop(); // Stop after arrival
|
||||
ROS_INFO("[%s] Turned to: %d°", __APP_NAME__.c_str(), target_angle);
|
||||
}
|
||||
|
||||
flag_2 = 0;
|
||||
}
|
||||
|
||||
// Handle flag_3: enter cruise (45°↔135°)
|
||||
if (flag_3 == 1)
|
||||
{
|
||||
ROS_INFO("[%s] Enter cruise mode", __APP_NAME__.c_str());
|
||||
|
||||
if(flag_4==1){
|
||||
flag_3 = 0;
|
||||
return ;
|
||||
}
|
||||
Turn_angle(30);
|
||||
if(flag_4==1){
|
||||
flag_3 = 0;
|
||||
return ;
|
||||
}
|
||||
Turn_angle(-30);
|
||||
if(flag_4==1) {
|
||||
flag_3 = 0;
|
||||
return ;
|
||||
}
|
||||
|
||||
// // Turn to 135°
|
||||
// position_stop();
|
||||
// ros::Duration(0.001).sleep();
|
||||
// serial_.write(PosAngle_P135, sizeof(PosAngle_P135));
|
||||
// ros::Duration(0.001).sleep();
|
||||
// serial_.write(PosAngle_P135, sizeof(PosAngle_P135));
|
||||
|
||||
// // Wait to reach 135° (≤3° error)
|
||||
// /* 每隔1ms查询一次当前位置(读取当前位置中有个20ms延时),到达目标位置为止 */
|
||||
// /* 发送命令,读取当前位置以作验证 */
|
||||
// int16_t angle = 0x7FFF;
|
||||
// int16_t de_angle = 0x7FFF; // 当前位置与目标位置的偏差
|
||||
// int16_t last_angle = 0x7FFF;
|
||||
// while (1)
|
||||
// {
|
||||
// // 终端中收到了停止指令,进入下一轮主循环
|
||||
// if (flag_4 == 1)
|
||||
// {
|
||||
// flag_3 = 0;
|
||||
// return;
|
||||
// }
|
||||
// angle = read_current_angle();
|
||||
// de_angle = (135 - angle);
|
||||
// de_angle = de_angle > 0 ? de_angle : -de_angle;
|
||||
// if (de_angle >= 0 && de_angle <= 3)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// ros::Duration(0.001).sleep();
|
||||
// }
|
||||
|
||||
// /* 逆时针转到45° */
|
||||
// ros::Duration(0.001).sleep();
|
||||
// serial_.write(PosAngle_P45, sizeof(PosAngle_P135));
|
||||
// ros::Duration(0.001).sleep();
|
||||
// serial_.write(PosAngle_P45, sizeof(PosAngle_P135));
|
||||
// /* 每隔1ms查询一次当前位置,到达目标位置为止 */
|
||||
// /* 发送命令,读取当前位置以作验证 */
|
||||
// angle = 0x7FFF;
|
||||
// de_angle = 0x7FFF;
|
||||
// last_angle = 0x7FFF;
|
||||
// while (1)
|
||||
// {
|
||||
// if (flag_4 == 1)
|
||||
// {
|
||||
// flag_3 = 0;
|
||||
// return;
|
||||
// }
|
||||
// angle = read_current_angle();
|
||||
// de_angle = (45 - angle);
|
||||
// de_angle = de_angle > 0 ? de_angle : -de_angle;
|
||||
// if (de_angle >= 0 && de_angle <= 3)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// ros::Duration(0.001).sleep();
|
||||
// }
|
||||
}
|
||||
|
||||
// Handle flag_4: exit cruise
|
||||
if (flag_4 == 1)
|
||||
{
|
||||
position_stop();
|
||||
ROS_INFO("[%s] Exited cruise mode", __APP_NAME__.c_str());
|
||||
flag_4 = 0;
|
||||
}
|
||||
|
||||
// Handle flag_5: return to 90°
|
||||
if (flag_5 == 1)
|
||||
{
|
||||
ROS_INFO("[%s] Return to 90°", __APP_NAME__.c_str());
|
||||
Turn_angle(0);
|
||||
flag_5 = 0;
|
||||
}
|
||||
|
||||
// Handle flag_7: set current as zero
|
||||
if (flag_7 == 1)
|
||||
{
|
||||
ROS_INFO("[%s] Set current pos as zero", __APP_NAME__.c_str());
|
||||
setCurrentPositionZero();
|
||||
flag_7 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Run node
|
||||
void Head_servo::run()
|
||||
{
|
||||
|
||||
serial_initial(); // Init serial and hardware
|
||||
|
||||
// Init topics
|
||||
// control_cmd_sub_ = nh_.subscribe("head_control_cmd", 1, &Head_servo::control_cmd_callback, this);
|
||||
// current_angle_pub_ = nh_.advertise<head_servo_controller::HeadAngleMsg>("current_angle", 1);
|
||||
|
||||
// Start timer (run process every 10ms)
|
||||
ros::Timer timer = nh_.createTimer(ros::Duration(0.01), &Head_servo::process, this);
|
||||
|
||||
// ROS spin
|
||||
ros::spin();
|
||||
}
|
||||
|
||||
// Main
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ros::init(argc, argv, "head_servo_controller");
|
||||
ros::NodeHandle nh("~");
|
||||
|
||||
Head_servo head_servo(nh);
|
||||
head_servo.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// #include "head_servo_core.h"
|
||||
|
||||
// int main(int argc, char **argv){
|
||||
// ros::init(argc, argv, "head_servo_node");
|
||||
// ros::NodeHandle nh;
|
||||
// Head_servo head_servo_contrl(nh);
|
||||
// ros::spin();
|
||||
// // head_servo_contrl.run();
|
||||
// return 0;
|
||||
// }
|
||||
Reference in New Issue
Block a user