当前位置: 首页 > news >正文

2025.8.29机械臂实战项目

        好久没给大家更新了,上周末大学大四开学,所以停更了几天,回来后在做项目,接下来的几篇文章,给大家带来几个项目,第一个介绍的是机械臂操作,说是机械臂操作,简单来说,就是tcp网络通信,完成相应的指令。

        任务要求是:

        1)基于TCP服务器的机械臂,端口号可指定暂时为8888, ip是Windows的ip;

        查看Windows的IP:按住Windows+r 按键,输入cmd , 输入ipconfig

        2)点击软件中的开启监听;

        3)机械臂需要发送16进制数,共5个字节,协议如下

        0xff 0x02 x y 0xff

        0xff:起始结束协议,固定的;

        0x02:控制机械手臂协议,固定的;

        x:指定要操作的机械臂 0x00 红色摆臂 0x01 蓝色摆臂

        y:指定角度

        机械臂的小应用如下图,因为涉及版权问题,先不贴给大家了,可以私信

        首先我们来看一下完成这个任务需要怎么做,初始化阶段,核心是建立客户端与服务器的通信链路。先通过socket()函数创建 TCP 套接字(负责数据传输的 “通道”),若创建失败则报错退出;接着定义服务器的网络信息(IP、端口、协议族),并通过connect()函数与目标服务器建立连接,确保后续指令能准确送达;同时初始化红 / 蓝臂的初始角度(红 0°、蓝 90°),并向用户提示控制按键规则与角度范围,为后续操作铺垫。
然后是核心交互阶段,以 “用户输入 - 角度计算 - 指令封装 - 数据发送” 为循环逻辑。通过getchar()获取用户按键(w/s/d/a/q),先处理退出逻辑(按 q 则关闭连接并退出);若为控制按键,则先更新对应机械臂的角度(红臂 ±1°、蓝臂 ±1°),并强制将角度限制在规定范围(红 - 90°~90°、蓝 0°~180°);再按固定协议封装 5 字节指令(起始符 0xff + 类型 0x02 + 臂标识 + 角度 + 结束符 0xff),最后通过send()将指令发送给服务器,完成一次控制;若输入无效按键,则提示用户重新输入。
最后是收尾阶段,当用户按 q 退出循环后,通过close()关闭之前创建的 TCP 套接字,释放网络资源,确保程序优雅退出,避免资源泄漏。

#include <myhead.h>  // 自定义头文件,通常包含标准库和项目通用定义// 宏定义服务器的端口号和IP地址
#define PORT 8888       // 服务器监听的端口号
#define IP "192.168.0.74"  // 服务器的IP地址int main(int argc, const char *argv[])
{char key;             // 存储用户输入的控制按键char buff[5];         // 用于发送数据的缓冲区,长度为5字节// 创建TCP套接字:AF_INET表示IPv4协议,SOCK_STREAM表示TCP协议,0表示自动选择协议int oldfd = socket(AF_INET, SOCK_STREAM, 0);// 检查套接字是否创建成功if (oldfd == -1){perror("socket");  // 打印错误信息return -1;         // 创建失败,退出程序}// 定义服务器的网络地址结构struct sockaddr_in server = {.sin_family = AF_INET,                // 使用IPv4地址族.sin_port = htons(PORT),              // 将端口号转换为网络字节序.sin_addr.s_addr = inet_addr(IP)      // 将字符串IP转换为网络字节序};// 连接到服务器if (connect(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1){perror("connect");  // 打印连接失败信息return -1;          // 连接失败,退出程序}// 连接成功后,打印服务器信息和操作提示printf("已成功连接了服务器%s-%d\n", inet_ntoa(server.sin_addr), PORT);printf("控制命令为:w--(红色臂顺时针+1°) s--(红色臂逆时针-1°) d--(蓝色臂顺时针+1°) a--(蓝色臂逆时针-1°)\n");printf("角度范围:红色臂(-90°~90°)  蓝色臂(0°~180°)\n");printf("按q键退出程序\n");// 初始化机械臂角度int red_angle = 0;    // 红色臂初始角度设为0°int blue_angle = 90;  // 蓝色臂初始角度设为90°// 主控制循环:持续接收用户输入并发送控制命令while (1){printf("\n请输入控制按键:");key = getchar();               // 获取用户输入的按键while(getchar()!='\n');        // 清空输入缓冲区,避免残留字符影响下次输入// 检查是否退出程序if (key == 'q'){printf("退出程序...\n");break;  // 跳出循环,结束程序}// 初始化数据缓冲区(通信协议格式)buff[0] = 0xff;  // 帧头标志buff[1] = 0x02;  // 数据类型或长度标识buff[4] = 0xff;  // 帧尾标志// 根据用户输入的按键执行相应操作switch (key){case 'w':  // 红色臂顺时针旋转+1°red_angle += 1;// 限制角度在有效范围内if(red_angle > 90){red_angle = 90;  // 超过最大角度,强制设为最大值}else if(red_angle < -90) {red_angle = -90; // 小于最小角度,强制设为最小值}buff[2] = 0x00;     // 0x00表示控制红色臂buff[3] = red_angle; // 存储当前角度值printf("红色臂角度更新:%d°\n", red_angle);break;case 's':  // 红色臂逆时针旋转-1°red_angle -= 1;// 限制角度在有效范围内if(red_angle > 90){red_angle = 90;}else if(red_angle < -90){red_angle = -90;}buff[2] = 0x00;     // 0x00表示控制红色臂buff[3] = red_angle; // 存储当前角度值printf("红色臂角度更新:%d°\n", red_angle);break;case 'd':  // 蓝色臂顺时针旋转+1°blue_angle += 1;// 限制角度在有效范围内if(blue_angle > 180){blue_angle = 180;}else if(blue_angle < 0){blue_angle = 0;}buff[2] = 0x01;     // 0x01表示控制蓝色臂buff[3] = blue_angle;// 存储当前角度值printf("蓝色臂角度更新:%d°\n", blue_angle);break;case 'a':  // 蓝色臂逆时针旋转-1°blue_angle -= 1;// 限制角度在有效范围内if(blue_angle > 180) {blue_angle = 180;}else if(blue_angle < 0){blue_angle = 0;}buff[2] = 0x01;     // 0x01表示控制蓝色臂buff[3] = blue_angle;// 存储当前角度值printf("蓝色臂角度更新:%d°\n", blue_angle);break;default:  // 处理无效输入printf("无效按键!请重新输入(w/s/d/a/q)\n");continue;  // 跳过本次循环,不发送数据}// 发送数据到服务器int res = send(oldfd, buff, 5, 0);if (res == -1)  // 检查发送是否成功{perror("send");    // 打印发送失败信息close(oldfd);      // 关闭套接字return -1;         // 退出程序}}// 关闭套接字,释放资源close(oldfd);return 0;
}

        我的这个代码主要以单次命令为主,即输入wads回车完成对机械臂红蓝色机械臂的控制,在完善代码的过程中,我发现红蓝色机械臂的角度受限红色机械臂的可调角度为-90°-90°,而蓝色机械臂可调角度为0-180°所以我加了个限制,使角度始终为这个区间内,来看一下效果。

        先看一下自己电脑现在的IP地址

        随后我发现这样一次次的操作再加上回车太过麻烦,效率非常低,我随后通过init_curses()函数初始化终端为无缓冲、无回显模式,支持实时按键响应(无需按回车确认)

#include <myhead.h>
#include <curses.h>  // 包含curses库
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#define PORT 8888
#define IP "192.168.0.74"// 初始化curses模式
void init_curses() {initscr();          // 初始化屏幕cbreak();           // 关闭行缓冲,按键直接生效noecho();           // 关闭输入回显keypad(stdscr, TRUE); // 启用特殊按键支持refresh();          // 刷新屏幕
}// 显示程序信息和控制说明
void show_info(int red_angle, int blue_angle) {clear();            // 清空屏幕// 显示标题和连接信息mvprintw(1, 2, "===== 机械臂控制系统 =====");mvprintw(3, 2, "已连接服务器: %s:%d", IP, PORT);// 显示当前角度信息mvprintw(5, 2, "当前角度:");mvprintw(6, 4, "红色臂: %d° (范围: -90° ~ 90°)", red_angle);mvprintw(7, 4, "蓝色臂: %d° (范围: 0° ~ 180°)", blue_angle);// 显示控制说明mvprintw(9, 2, "控制命令:");mvprintw(10, 4, "w: 红色臂顺时针 (+1°)");mvprintw(11, 4, "s: 红色臂逆时针 (-1°)");mvprintw(12, 4, "d: 蓝色臂顺时针 (+1°)");mvprintw(13, 4, "a: 蓝色臂逆时针 (-1°)");mvprintw(14, 4, "q: 退出程序");// 显示状态提示mvprintw(16, 2, "状态: 就绪 (按任意控制键操作)");mvprintw(17, 2, "----------------------------------------");refresh();          // 刷新屏幕显示
}// 显示操作结果提示
void show_status(const char *msg) {mvprintw(16, 2, "状态: %s", msg);  // 在固定位置显示状态mvprintw(18, 2, "按任意键继续...");refresh();getch();            // 等待按键继续
}int main(int argc, const char *argv[]) {char key;char buff[5]; int oldfd = socket(AF_INET, SOCK_STREAM, 0);int red_angle = 0;    // 红色臂初始角度int blue_angle = 90;  // 蓝色臂初始角度// 初始化cursesinit_curses();// 创建socketif (oldfd == -1) {endwin();  // 退出curses模式perror("socket创建失败");return -1;}// 设置服务器地址struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};// 连接服务器if (connect(oldfd, (struct sockaddr *)&server, sizeof(server)) == -1) {endwin();  // 退出curses模式perror("连接服务器失败");close(oldfd);return -1;}// 显示初始界面show_info(red_angle, blue_angle);// 主控制循环while (1) {key = getch();  // 无缓冲读取按键,无需回车// 退出程序if (key == 'q') {break;}buff[0] = 0xff;  buff[1] = 0x02;  buff[4] = 0xff;char status_msg[100] = {0};int send_flag = 0;// 处理按键逻辑switch (key) {case 'w':  // 红色臂+1°if (red_angle < 90) {red_angle++;buff[2] = 0x00;buff[3] = red_angle;sprintf(status_msg, "红色臂已更新至 %d° (发送成功)", red_angle);send_flag = 1;} else {sprintf(status_msg, "红色臂已达最大角度 90° (无法继续增加)");}break;case 's':  // 红色臂-1°if (red_angle > -90) {red_angle--;buff[2] = 0x00;buff[3] = red_angle;sprintf(status_msg, "红色臂已更新至 %d° (发送成功)", red_angle);send_flag = 1;} else {sprintf(status_msg, "红色臂已达最小角度 -90° (无法继续减小)");}break;case 'd':  // 蓝色臂+1°if (blue_angle < 180) {blue_angle++;buff[2] = 0x01;buff[3] = blue_angle;sprintf(status_msg, "蓝色臂已更新至 %d° (发送成功)", blue_angle);send_flag = 1;} else {sprintf(status_msg, "蓝色臂已达最大角度 180° (无法继续增加)");}break;case 'a':  // 蓝色臂-1°if (blue_angle > 0) {blue_angle--;buff[2] = 0x01;buff[3] = blue_angle;sprintf(status_msg, "蓝色臂已更新至 %d° (发送成功)", blue_angle);send_flag = 1;} else {sprintf(status_msg, "蓝色臂已达最小角度 0° (无法继续减小)");}break;default:sprintf(status_msg, "无效按键! 请使用 w/s/d/a/q");break;}// 发送数据if (send_flag) {int res = send(oldfd, buff, 5, 0);if (res == -1) {sprintf(status_msg, "发送失败: %s", strerror(errno));}}// 更新界面和显示状态show_info(red_angle, blue_angle);show_status(status_msg);}// 清理资源close(oldfd);endwin();  // 退出curses模式,恢复终端printf("程序已退出\n");return 0;
}

        单击wads键无需缓冲直接执行,长按wads多次执行,就实现了无极控制机械臂的摆动幅度,基本完成了项目需求。

http://www.xdnf.cn/news/1392265.html

相关文章:

  • Windows 下 MSYS2 + MinGW-w64 配置 Fyne GUI 编译环境全流程
  • Redis-分布式缓存
  • Java深拷贝与浅拷贝核心解析
  • 设计模式:装饰模式(Decorator Pattern)
  • Kubernetes 与 GitOps 的深度融合实践指南
  • 【3D入门-指标篇上】3D 网格重建评估指标详解与通俗比喻
  • 3D 数字孪生可视化技术在学校项目中的应用
  • “破译”的密钥/算法类型
  • 【工具】开源大屏设计器 自用整理
  • LeetCode第二题知识点2 ---- 栈、堆、地址
  • LeetCode - 128. 最长连续序列
  • Vue3+Ant-design-vue 实现树形穿梭框
  • BlueKing-ci
  • 币安创始人赵长鹏:香港需要更广泛的加密货币产品来与美国和阿联酋竞争
  • docker-相关笔记
  • Cesium 入门教程(十三):粒子系统实例
  • 2025年03月 Scratch 图形化(一级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • springboot中循环依赖的解决方法-使用反射
  • mysql双机热备(主主模式)
  • Java项目实现【记录系统操作日志】功能
  • 基于FPGA的DDR3读写实验学习
  • 《ArkUI 记账本开发:状态管理与数据持久化实现》
  • el-table合并列实例
  • 光谱相机多层镀膜技术如何提高透过率
  • (二)Python语法基础(下)
  • 响应式编程框架Reactor【2】
  • Redis开发06:使用stackexchange.redis库结合WebAPI对redis进行增删改查
  • Vue3 全面介绍
  • 技术SEO修复ROI最大化:有限资源下的优先排序策略
  • 【笔记】Linux高性能网络详解之DPDK