webots 硬件在环仿真【通信基础】

本文webots仓库:gitee仓库

仿真平台:Webots R2021a
硬件平台:stm32F767


  • webots是一款开源易用的机器人仿真软件,支持Windows\Linux\Mac三平台,所以webots项目能具备很高的通用性,只需要具备PC机,就能去进行仿真。
  • 硬件在环仿真(又名:硬件在回路仿真、HIL),是一种半实物的仿真流程,其主要特点是通过实体的处理单元来控制虚拟的被控对象,和数字孪生的概念有一定的重合。

笔者希望通过webots来进行HIL,选中实体处理单元是STM32F767的板子,而虚拟的被控对象便是window11平台在webots 2021a中创建的模型,其中webots和STM32F767之间,使用虚拟串口通信(USB-OTG)。


由于笔者也是刚接触webots,对webots了解也不多,目前只是通过webots中内置的控制器,实现了最简单的串口通信,stm32上的虚拟串口是没有实现的。
代码主要参考了windows c编写串口通信,这位博主还提供了代码及所生成exe文件的下载链接,很感谢这位博主的分享。

值得一提的是,笔者曾在B站通过关键词webots STM32搜索到一位UP主的视频,里面出现了ta使用stm32的按钮来遥控webots内部机器人运动方向的视频,通信方式正是USB串口。而笔者通过QQ联系到这位UP主询问是否方便分享实现源码,UP主告诉笔者,假如同是校友可以免费分享,假如不是的话则收费,985学校有优惠,985优惠价是120元,由于笔者囊中羞涩,所以没办法承担此费用。好在后来通过视频逐帧分析,笔者发现UP主也是基于上面链接中的代码移植而得到的,所以再次感谢这位博主Genven_Liang的分享。


webot控制器有源文件和头文件组成,笔者在其中创建了一个四轮机器人,控制器名为auv_ctrl

  • 头文件auv_ctrl.hpp代码如下
#ifndef __AUV_CTRL__
#define __AUV_CTRL__

#include <stdio.h>
#include <windows.h>
#include <string.h>

#if 1   //开启DEBUG打印
#define LOGD(...) printf(__VA_ARGS__)
#else   //关闭DEBUG打印
#define LOGD(...)
#endif

#if 1   //开启ERROR打印
#define LOGE(...) printf(__VA_ARGS__)
#else   //关闭ERROR打印
#define LOGE(...)
#endif

//缓冲区大小
#define BUF_SIZE    2048
#define EXIT_STR    "exit"
#define I_EXIT      "I exit.\r\n"
#define I_RECEIVE   "I receive.\r\n"

HANDLE OpenSerial(const char ****,int baud,int byteSize,int parity,int stopBits);

#endif

  • 源文件auv_ctrl.cpp代码如下
#include <webots/DistanceSensor.hpp>
#include <webots/Motor.hpp>
#include <webots/Robot.hpp>
#include "auv_ctrl.hpp"

#define TIME_STEP 64
using namespace webots;

int main(int argc, char **argv) {
  BOOL b = FALSE;
  DWORD wRLen = 0;
  DWORD wWLen = 0;
  char buf[BUF_SIZE] = {0};
  HANDLE ***Handle = INVALID_HANDLE_VALUE;//串口句柄
  
   //打开串口
  const char **** = "***2";
  ***Handle = OpenSerial(***, CBR_9600, 8, NOPARITY, ONESTOPBIT);
  if (INVALID_HANDLE_VALUE == ***Handle) {
  	LOGE("OpenSerial ***2 fail!\r\n");
  	return -1;
  }
  else{
    LOGD("[auv_ctrl]Open ***2 Su***essfully!\r\n");  
   }	
	
  Robot *robot = new Robot();
  DistanceSensor *ds[2];
  char dsNames[2][10] = {"ds_right", "ds_left"};
  for (int i = 0; i < 2; i++) {
    ds[i] = robot->getDistanceSensor(dsNames[i]);
    ds[i]->enable(TIME_STEP);
  }
  Motor *wheels[4];
  char wheels_names[4][14] = {"wheel_motor_1", "wheel_motor_2", "wheel_motor_3", "wheel_motor_4"};
  for (int i = 0; i < 4; i++) {
    wheels[i] = robot->getMotor(wheels_names[i]);
    wheels[i]->setPosition(INFINITY);
    wheels[i]->setVelocity(0.0);
  }
  int avoidObstacleCounter = 0;
  
  while (robot->step(TIME_STEP) != -1) {
    double leftSpeed = -1.0;
    double rightSpeed = -1.0;
    if (avoidObstacleCounter > 0) {
      avoidObstacleCounter--;
      leftSpeed = -1.0;
      rightSpeed = 1.0;
    } else { // read sensors
      for (int i = 0; i < 2; i++) {
        if (ds[i]->getValue() < 300.0)
          avoidObstacleCounter = 100;
      }
    }
    wheels[0]->setVelocity(leftSpeed);
    wheels[1]->setVelocity(rightSpeed);
    wheels[2]->setVelocity(leftSpeed);
    wheels[3]->setVelocity(rightSpeed);
    
    /* 循环接收消息,收到消息后将消息内容打印并回复I_RECEIVE, 如果收到EXIT_STR就回复EXIT_STR并退出循环 */
    wRLen = 0;
    //读串口消息
    b = ReadFile(***Handle, buf, sizeof(buf)-1, &wRLen, NULL);
    if (b && 0 < wRLen) {//读成功并且数据大小大于0
      buf[wRLen] = '\0';
      LOGD("[RECV]%s\r\n", buf);//打印收到的数据
      if (0 == strncmp(buf, EXIT_STR, strlen(EXIT_STR))) {
          //回复
          b = WriteFile(***Handle, TEXT(I_EXIT), strlen(I_EXIT), &wWLen, NULL);
          if (!b) {
              LOGE("WriteFile fail\r\n");
          }
          break;
      }
      //回复
      b = WriteFile(***Handle, TEXT(I_RECEIVE), strlen(I_RECEIVE), &wWLen, NULL);
      if (!b) {
          LOGE("WriteFile fail\r\n");
      }
    }
  }
  
  /*关闭串口*/
  b = CloseHandle(***Handle);
  if (!b) {
      LOGE("CloseHandle fail\r\n");
  }

  LOGD("Program Exit.\r\n");  
  
  wheels[0]->setVelocity(0);
  wheels[1]->setVelocity(0);
  wheels[2]->setVelocity(0);
  wheels[3]->setVelocity(0);
  delete robot;
  return 0;  // EXIT_SU***ESS
}


//打开串口
HANDLE OpenSerial(const char ****, //串口名称,如***1,***2
    int baud,       //波特率:常用取值:CBR_9600、CBR_19200、CBR_38400、CBR_115200、CBR_230400、CBR_460800
    int byteSize,   //数位大小:可取值7、8;
    int parity,     //校验方式:可取值NOPARITY、ODDPARITY、EVENPARITY、MARKPARITY、SPACEPARITY
    int stopBits)   //停止位:ONESTOPBIT、ONE5STOPBITS、TWOSTOPBITS;
{    
    DCB dcb;
    BOOL b = FALSE;
	***MTIMEOUTS ***mTimeouts;	
    HANDLE ***Handle = INVALID_HANDLE_VALUE;

    //打开串口
	***Handle = CreateFile(***,            //串口名称
		GENERIC_READ | GENERIC_WRITE,      //可读、可写   				 
		0,            // No Sharing                               
		NULL,         // No Security                              
		OPEN_EXISTING,// Open existing port only                     
		FILE_ATTRIBUTE_NORMAL,            // Non Overlapped I/O                           
		NULL);        // Null for ***m Devices

	if (INVALID_HANDLE_VALUE == ***Handle) {
        LOGE("CreateFile fail\r\n");
        return ***Handle;
    }

    // 设置读写缓存大小
	b = Setup***m(***Handle, BUF_SIZE, BUF_SIZE);
    if (!b) {
        LOGE("Setup***m fail\r\n");
    }

    //设定读写超时
	***mTimeouts.ReadIntervalTimeout = MAXDWORD;//读间隔超时
	***mTimeouts.ReadTotalTimeoutMultiplier = 0;//读时间系数
	***mTimeouts.ReadTotalTimeoutConstant = 0;//读时间常量
	***mTimeouts.WriteTotalTimeoutMultiplier = 1;//写时间系数
	***mTimeouts.WriteTotalTimeoutConstant = 1;//写时间常量
	b = Set***mTimeouts(***Handle, &***mTimeouts); //设置超时
    if (!b) {
        LOGE("Set***mTimeouts fail\r\n");
    }

    //设置串口状态属性
    Get***mState(***Handle, &dcb);//获取当前
	dcb.BaudRate = baud; //波特率
	dcb.ByteSize = byteSize; //每个字节有位数
	dcb.Parity   = parity; //无奇偶校验位
	dcb.StopBits = stopBits; //一个停止位
	b = Set***mState(***Handle, &dcb);//设置
    if (!b) {
        LOGE("Set***mState fail\r\n");
    }

    return ***Handle;
}

最后使用Configure Virtual Serial Port Driver V6.9这个软件进行调试,这个软件可以生成虚拟端口,方便进行***口之间的通信测试。
测试效果:

从图片可以看到,虚拟的外部***口连接上webots所打开的***口,webots这边能收到外部发来的信息,也能给外部端口发去应答信息,由此实现了HIL的通信基础。


总结与展望

  1. 实现了HIL环节中的通信基础
  2. 后续会对串口代码进行进一步的封装,将参考以下链接:
    • windows c编写串口通信
    • ChatGPT win下C语言串口通信
  3. 后续会在STM32上部署好虚拟串口,并验证通信速率是否能满足webots的控制条件。

网上资料提到,虚拟串口可以跑到921600波特率,假如使用8N1的串口数据帧格式,那么速率相当于92.16kByte/s,照这个速率应该是能满足需求的。

  1. 3D模型的导入和使用
    • 参考链接: 【开源-教程】Webots运动学仿真,webots模型导入最好方法,SolidWorks导出wrl【四足机器人足端运动学仿真】
  2. Win11+VScode+MinGW+Cmake+Eigen环境搭建
    • 参考链接: Windows10下配置VSCode、Mingw、Cmake、Eigen、OpenCV环境
转载请说明出处内容投诉
CSS教程_站长资源网 » webots 硬件在环仿真【通信基础】

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买