睁扼妤 发表于 2025-10-6 14:33:44

与狼共舞——为Go2安装M416全自动水弹枪(二)

导语

  作为本系列的开篇,有必要先做些约定:

[*]以后的内容仅提供核心功能的代码,尽可能简洁,毕竟每个狗主都会有自己的想法,会添加自己的功能,写太多不容易整合,更何况我也没做什么复杂功能。
[*]提供的代码片段不进行过多解释,程序语言也是语言,“领会精神“”就好。
[*]不管是硬件设计还是软件编程部分,咱遵循三哥军工的设计思路,能依赖现有软件功能的就绝不自己编码,能购买的电子模块绝不自己动手制作。
[*]机器人操作系统采用Ros2 Foxy,软件编程使用python语言,硬件编程c语言,操作系统Ubuntu 20.04。
 
 
功能

  这里的功能仅指与架枪相关的功能。(水弹枪是儿子友情赞助的,请勿对人射击。)

[*]通过3D打印机打印支架和电器盒子,固定枪和电路板。
[*]由go2的扩展坞发送UDP广播消息给控制电路,击发开火。
[*]编程实现原厂遥控器的向上按钮作为开火键。
[*]用cv2实现一个简单的人脸识别系统。
[*]实现一个deepseek agent功能,让go2能理解你发出的指令,并按照你的指令执行任务(如,找到张三,立即开火。 或者 打个招呼 ...)

 
硬件

  先上图,实物接线图:

  
  图中的板子,都是淘宝上淘来的,其中控制板150元左右、DCDC模块2元左右,电子开关不到1元。
1.水弹枪

  《绝地求生》装备M416全自动步枪,许多年前买的,不记得多少钱了,原先还有很多附件,都丢光了。破是破了点,但还能打,玩起来很解压。靠2节18650电池驱动(3.7+3.7V=7.4V),时间久了,电池也已经失效,因此,从go2的12v输出取电,通过一个可调DC-DC降压模块给枪供电。
2.控制板

  以前买的一块开发板,STM32F103C8T6芯片+W5500以太网模块+2路CAN+2路485,价值150元左右,其实控制枪不用这么多功能,只要有W5500以太网模块和GPIO口输出的板子就行,GPIO口控制电子开关给枪供电。
3.DCDC模块

  把go2的12V直流电降为7.4V。
4.电子开关

  接收GPIO控制信号,打开或关闭枪的电源,枪有电就发射。(枪的扳机被我绑在击发位置)
 
控制程序


[*]STM32CubeMX


[*]移植W5500驱动库
-- 1. 下载W5500驱动库,gitee下载地址、github下载地址。将ioLibrary_Driver目录拷贝到你的工程下面。
-- 2. 将ioLibrary_Driver\Ethernet目录下的3个文件及其头文件——w5500.c,wizchip_conf.c,socket.c,添加到你的工程中。
[*]控制程序
  该部分代码使用了FreeRTOS操作系统,监听UDP 5001端口,当接收到消息含有fire字符串是,PB7口持续1秒的高电平。也就是说当收到UDP消息fire的时候,M416枪开火1秒。
/* USER CODE END Header_StartTaskSendMsg */
void StartTaskGetMsg(void *argument)
{
/* USER CODE BEGIN StartTaskSendMsg */
/* Infinite loop */
for(;;)
{
                do_udp();       
               
                if (fired == 1)
                {
                        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
                        osDelay(1000);
                        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7);
                        fired = 0;                       
                }
               
    osDelay(1000);
               
}
/* USER CODE END StartTaskSendMsg */
}myudp.c程序中定义了W5500的网络信息,监听端口、ip地址等,并处理接收到的消息。
myudp.h
#ifndef MYUDP_H
#define MYUDP_H

#include "myudp.h"
#include "main.h"
#include "w5500.h"
#include "socket.h"
#include "wizchip_conf.h"
#include "spi.h"
#include <string.h>

#define SOCK_UDPS 2

extern uint8_t buff;
extern uint8_t UDP_send_buff;
extern uint8_t remote_ip;
extern uint16_t remote_port;

void UDPinit(void);
void do_udp(void);
void udp_send(uint8_t* data, uint8_t len);
void Analysis(uint8_t *buf);
#endif // MYUDP_H在这里可以修改你的IP地址,监听端口,处理接收到的消息。
myudp.c
#include "myudp.h"
#include "main.h"
#include "w5500.h"
#include "socket.h"
#include "wizchip_conf.h"
#include "spi.h"
#include <string.h>

uint8_t buff;
uint8_t UDP_send_buff;

uint8_t remote_ip = {255,255,255,255};
uint16_t remote_port = 8080;
//片选
void W5500_Select(void) {
    HAL_GPIO_WritePin(SCSn_GPIO_Port, SCSn_Pin, GPIO_PIN_RESET);
}
void W5500_Unselect(void) {
    HAL_GPIO_WritePin(SCSn_GPIO_Port, SCSn_Pin, GPIO_PIN_SET);
}
void W5500_Restart(void) {
    HAL_GPIO_WritePin(RSTn_GPIO_Port, RSTn_Pin, GPIO_PIN_RESET);
    HAL_Delay(1);// delay 1ms
    HAL_GPIO_WritePin(RSTn_GPIO_Port, RSTn_Pin, GPIO_PIN_SET);
    HAL_Delay(1600);// delay 1600ms
}

void W5500_ReadBuff(uint8_t* buff, uint16_t len) {
    HAL_SPI_Receive(&hspi2, buff, len, HAL_MAX_DELAY);
}

void W5500_WriteBuff(uint8_t* buff, uint16_t len) {
    HAL_SPI_Transmit(&hspi2, buff, len, HAL_MAX_DELAY);
}

uint8_t W5500_ReadByte(void) {
    uint8_t byte;
    W5500_ReadBuff(&byte, sizeof(byte));
    return byte;
}

void W5500_WriteByte(uint8_t byte) {
    W5500_WriteBuff(&byte, sizeof(byte));
}

//配置W5500网络信息
wiz_NetInfo gSetNetInfo = {
.mac= {0x00, 0x08, 0xdc, 0x11, 0x11, 0x11},
.ip   = {192, 168, 123, 55},
.sn   = {255, 255, 255, 0},
.gw   = {192, 168, 123, 1},
.dns= {144, 144, 144, 144},
.dhcp = NETINFO_STATIC};

wiz_NetInfo gGetNetInfo;

enum Status
{
Failed = 0,
Success = 1
};

/**
* @brief valid the result of set net info
* @return 1: Success
*         0: Failed
*/
uint8_t validSetNetInfoResult(wiz_NetInfo* _set, wiz_NetInfo* _get)
{
return (!memcmp(_set, _get, sizeof(wiz_NetInfo)));// if same, memcmp return 0
}


void UDPinit(void)
{
reg_wizchip_cs_cbfunc(W5500_Select, W5500_Unselect);
reg_wizchip_spi_cbfunc(W5500_ReadByte, W5500_WriteByte);

W5500_Restart();// hardware restart through RESET pin

ctlnetwork(CN_SET_NETINFO, (void*)&gSetNetInfo);// set net info
// maybe need delay
ctlnetwork(CN_GET_NETINFO, (void*)&gGetNetInfo);// get net info

// W5500 has 8 channel, 32k buffer, 2 means 2KBytes
uint8_t buffer_size_8channel_tx_rx = {2, 2, 2, 2, 2, 2, 2, 2,// 8 channel tx
                                          2, 2, 2, 2, 2, 2, 2, 2}; // 8 channel rx
if(ctlwizchip(CW_INIT_WIZCHIP,(void*)buffer_size_8channel_tx_rx))
{
    // failed
   
}

uint8_t sta = getSn_SR(SOCK_UDPS);
if(sta == SOCK_CLOSED)
{
    socket(SOCK_UDPS, Sn_MR_UDP, 5001, 0);
}
HAL_Delay(100);

}


void do_udp(void)
{                                                            
        uint16_t len=0;       
        switch(getSn_SR(SOCK_UDPS))       /*获取socket的状态*/
        {
                case SOCK_CLOSED:               /*socket处于关闭状态*/
                        socket(SOCK_UDPS,Sn_MR_UDP,5001,0);    /*初始化socket*/
                  break;
               
                case SOCK_UDP:      /*socket初始化完成*/
                        HAL_Delay(10);
                        if(getSn_IR(SOCK_UDPS) & Sn_IR_RECV) //检查是否有接收中断
                        {
                                setSn_IR(SOCK_UDPS, Sn_IR_RECV);/*清接收中断*/
                        }
                        if((len=getSn_RX_RSR(SOCK_UDPS))>0)/*接收到数据*/
                        {
                                recvfrom(SOCK_UDPS,buff, len, remote_ip,&remote_port);               /*W5500接收计算机发送来的数据W5500接收计算机发送来的数据,并获取发送方的IP地址和端口号*/
                                sendto(SOCK_UDPS,buff,len-8, remote_ip, remote_port);                /*W5500把接收到的数据发送*/
      Analysis(buff);//分析数据
      memset(buff, 0, sizeof(buff));
                        }
                        break;
        }
}


void udp_send(uint8_t* data,uint8_t len)
{
sendto(SOCK_UDPS, data, len, remote_ip, remote_port);
memset(data, 0, len);
}

void SystemReset() {
    __HAL_RCC_BACKUPRESET_FORCE(); // 强制备份域复位
    __HAL_RCC_BACKUPRESET_RELEASE(); // 释放备份域复位
    NVIC_SystemReset(); // 发起系统复位
}

void Analysis(uint8_t *buf)
{
        char * gun = "fire";
        char * result = strstr((char*)buf, gun);
        if (result != NULL)
        {
                fired =1;               
                return;
        }
       
        char * devreset ="reset";
        result = strstr((char*)buf, devreset);
        if (result != NULL)
        {
                        SystemReset();
                        return;
        }
} 
待续

  预祝您,成功将以上代码烧写进你的板子中。接下来,我们开始给go2扩展坞写ROS2程序,让它能够通过原厂遥控器击发开火功能。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 与狼共舞——为Go2安装M416全自动水弹枪(二)