Skip to main content

SpeedControl Demo Project

This tutorial uses a small speed control function written in C to give you hands-on experience with CppModel. The function steers a vehicle with two driven wheels:

  • It computes the left wheel command, as a percentage, from the speed command and the direction angle.
  • It computes the right wheel command the same way.

When turning, the inner wheel slows down by the cosine of the direction angle, and the outer wheel keeps the commanded speed. The inputs are in these ranges:

  • Speed: [0, 100] %
  • Direction: [-90, 90] degrees. Negative angles slow the right wheel and turn the vehicle right. Positive angles slow the left wheel and turn it left.

Input validation is left out to keep the code short.

The controller​

speedControl.h​

#pragma once
#include <stdint.h>

typedef struct SpeedControlContext_s
{
/* Inputs */
int8_t directionAngleDeg;
int8_t controlCommandPercent;

/* Outputs */
int8_t leftWheelPercent;
int8_t rightWheelPercent;
} SpeedControlContext_ts;

void speedControlCyclic(SpeedControlContext_ts *const ctx);

speedControl.c​

#include <math.h>
#include "speedControl.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

void speedControlCyclic(SpeedControlContext_ts *const ctx)
{
float scale = cosf(((float)ctx->directionAngleDeg * (float)M_PI) / 180.0f);

ctx->leftWheelPercent = (ctx->directionAngleDeg <= 0)
? ctx->controlCommandPercent
: (int8_t)(scale * ctx->controlCommandPercent);
ctx->rightWheelPercent = (ctx->directionAngleDeg >= 0)
? ctx->controlCommandPercent
: (int8_t)(scale * ctx->controlCommandPercent);
}

The function has no dependency on CppModel, so it can run unchanged on an embedded target.


The simulation​

Before you continue, install the CppModel library.

The simulation calls the controller every 20 ms for 20 seconds. It sweeps the direction from -90 to +90 degrees and keeps the speed constant.

SimulateSpeedControl.c​

#include <cppmodel/CModel.h>
#include "speedControl.h"

void RunCyclic(CModelSimulation_ts *self, unsigned long currentTime_ms)
{
SpeedControlContext_ts ctx;

/* Sweep the direction from -90 to +90 degrees over the whole simulation */
float sweep = -90.0f + 180.0f * (float)currentTime_ms / (float)self->time_ms;

ctx.directionAngleDeg = CppModel_getInputI8(self, "Direction [deg]", (int8_t)sweep);
ctx.controlCommandPercent = CppModel_getInputI8(self, "Speed [%]",
CppModel_getParameterI8(self, "Speed [%]", 80));

speedControlCyclic(&ctx);

CppModel_setOutputI8(self, "LeftWheel [%]", ctx.leftWheelPercent);
CppModel_setOutputI8(self, "RightWheel [%]", ctx.rightWheelPercent);
}

int main(void)
{
CModelSimulation_ts *simulation = CppModel_create("SpeedControl", 20000, 20);

CppModel_setRunStepFunction(simulation, RunCyclic);
CppModel_Simulate(simulation);

int result = CppModel_getSimulationResult(simulation);
CppModel_destroy(simulation);

return result;
}

What the simulation does​

  1. CppModel_create("SpeedControl", 20000, 20) creates a simulation named SpeedControl that runs for 20 000 ms with a 20 ms step. The name identifies the simulation in the workspace.
  2. CppModel_setRunStepFunction() registers RunCyclic(), which is called at every step with the current simulation time.
  3. In RunCyclic():
    • CppModel_getInputI8() records the direction and speed that are fed to the controller, so they appear in the workspace.
    • The speed comes from the Speed [%] parameter, which defaults to 80. After the first run, you can change it in the workspace and run again without rebuilding.
    • speedControlCyclic() is the code under test.
    • CppModel_setOutputI8() records the wheel commands.

Building​

cmake_minimum_required(VERSION 3.12)

project(SpeedControl LANGUAGES C CXX)

add_definitions(-DCPPHTTPLIB_OPENSSL_SUPPORT)

include_directories(dependencies/include)
link_directories(dependencies/lib)

add_executable(SimulateSpeedControl speedControl.c SimulateSpeedControl.c)
set_target_properties(SimulateSpeedControl PROPERTIES LINKER_LANGUAGE CXX)

if (WIN32)
target_link_libraries(SimulateSpeedControl CppModelBase ixwebsocket ssl crypto z crypt32 ws2_32)
else()
target_link_libraries(SimulateSpeedControl CppModelBase ixwebsocket ssl crypto z m)
endif()
cmake -S . -B build
cmake --build build
./build/SimulateSpeedControl

LINKER_LANGUAGE CXX is required because the CppModel library is written in C++.


Viewing the results​

On the first run, open the printed login link to connect to your workspace. When the run finishes, open workspace.cppmodel.com and select SpeedControl. You should see:

  • RightWheel [%] rising from 0 at -90 degrees to 80% at 0 degrees, then staying at 80%.
  • LeftWheel [%] mirroring it: at 80% up to 0 degrees, then falling toward 0 as the direction approaches +90 degrees.

Next steps​

  • Add a verification that the outer wheel never runs slower than the inner wheel. See Adding verification.
  • Change the Speed [%] parameter in the workspace and run the simulation again.