在物理模拟中防止编码器的错误“忘记按时间划分”

时间:2016-11-20 10:47:49

标签: c++ c++11

在物理逻辑中,我习惯于这样的代码: -

Position+=Velocity;
Velocity+=Acceleration;

//unit of Velocity = unit of Position / time-step      
//unit of Acceleration = unit of Position / (time-step^2)    

他们都是 Vector3D - 我的自定义类。 代码工作正常。

然后,我彻底改变了我的库,以匹配流行的物理引擎的标准,例如,子弹。
时间单位现在(或分钟等),而不再是时间步

现在有效代码变为:

Position+=Velocity*time_per_timestep;
Velocity+=Acceleration*time_per_timestep;

当语句有点复杂时,很难意识到我刚刚添加的以下代码是错误的:

Vector3D velocity=rigidBody->getPosition()-calibrator->getLastPosition();

在一些忙碌/晕眩的日子里,我忘了这样添加÷ time_per_timestep 结果,我糟糕的一天开始了。

问题:

如何防止此人为错误? (有断言?)

我的解决方案不好:

我会创建课程PositionVelocityAcceleration来强制执行。
 例如,Position minus Position仍会返回Position。我必须强制Position不能隐含地投放到Velocity 缺点:过于繁琐,降低可维护性(?)和过度杀伤(?)。

2 个答案:

答案 0 :(得分:1)

C ++ 11为User-defined literals为您提供了解决此问题的正确方法。 这有助于您通过在编译时检查它们来防止度量之间的错误操作。

答案 1 :(得分:1)

这是一个单位标签类型:

template<std::ptrdiff_t...Units>
struct unit_tags_t {
  template<std::ptrdiff_t...Lhs, std::ptrdiff_t...Rhs>
  friend unit_tags_t<(Lhs+Rhs)...>
  operator*( unit_tags_t<Lhs...>, unit_tags_t<Rhs...> ) {
    return {};
  }
  template<std::ptrdiff_t...Lhs, std::ptrdiff_t...Rhs>
  friend unit_tags_t<(Lhs-Rhs)...>
  operator/( unit_tags_t<Lhs...>, unit_tags_t<Rhs...> ) {
    return {};
  }
  friend unit_tags_t operator+( unit_tags_t, unit_tags_t ) { return {}; }
  friend unit_tags_t operator-( unit_tags_t, unit_tags_t ) { return {}; }
  friend unit_tags_t& operator+=( unit_tags_t& lhs, unit_tags_t ) { return lhs; }
  friend unit_tags_t& operator-=( unit_tags_t& lhs, unit_tags_t ) { return lhs; }
  friend unit_tags_t operator*( unit_tags_t ) { return {}; }
  friend unit_tags_t operator-( unit_tags_t ) { return {}; }
};

它是签名值的编译时列表。

我们挑选一些基本单位,比如时间和距离。

using base_time_t = unit_tags_t<1,0>;
using base_distance_t = unit_tags_t<0,1>;

现在,base_time_t{}+base_distance_t{}是编译时错误,但base_time_t{}+base_time_t{}不是。

同样地,base_time_t{}*base_time_t{}不是编译时错误,而是时间平方的新单位&#34;。

接下来,我们使用扩充运算符定义元组。它有一个标量作为一个字段,一些单位标记作为它的第二个字段。所有算术运算依次对每个单元执行,就像数学产品类型一样。

所以,如果我们有

{2.0, base_distance_t}/{3.14, base_time_t}

我们得到了

{0.636..., unit_tags_t<-1,1>{}}

只需将每个操作员转发到每个组成部分。

将此增强元组称为&#34;单位值&#34;。

下一步是处理标量。定义

的全局常量
using time = unit_value<double, base_time_t>;
using distance = unit_value<double, base_distance_t>;
const time second(1./time_per_timestep, {});
const distance meter(1., {});

或者其他什么是正确的。我们可以推导出新的类型:

const auto meter_per_second = meter/second;
const auto meter_per_second_squared = meter/second/second;
using speed = std::decay_t<decltype(meter_per_second)>;
using acceleration = std::decay_t<decltype(meter_per_second_squared)>;

基于这些。

现在,getPosition会返回distance值。

velocityspeed

类型的值

Velocity3D velocity=rigidBody->getPosition()-calibrator->getLastPosition();

是类型错误。

如果您使用的是压缩元组,doubleunit_value<double, blah>所需的空间应该相同(内存中的布局也应如此)。因此,您可以将Velocity3D设为unit_value<Vector3d, base_distance_t>Vector3d< base_distance_t >

像升压装置这样的图书馆会为你做这一切;我只想说明你是如何接近它的。